From d5b2e58dae31f9c0a8ed42973d7a32da8ab8ac69 Mon Sep 17 00:00:00 2001 From: Amos <48657826+Mauler125@users.noreply.github.com> Date: Sat, 25 Dec 2021 22:36:38 +0100 Subject: [PATCH] Code base refactor + major performance and readability improvement. Read description for details. * Codebase restructured to SourceSDK codebase style and .cpp/.h assertion paths in the game executable. * Document most functions with valve style 'Purpose' blocks. * Rename variables to match the rest of the codebase and Valve's naming convention. * Dedicated DLL and the SDKLauncher now share the same codebase as the DevSDK. * Obtain globals or pointers directly instead of waiting for runtime initialized data. * Dynamically search for all functions and globals (this doesn't count for dedicated yet!). * Initialize most in-SDK variables. * Move certain prints and other utilities under ConVars to reduce verbosity and increase performance. * Print all pattern scan results through a virtual function to make it easier to add and debug new patterns in the future. * Type global var pointers appropriately if class or type is known and implemented. * Forward declare 'CClient' class to avoid having 2 'g_pClient' copies. * Add IDA's pseudo definitions for easier prototyping with decompiled assembly code. * RPAK decompress Command callback implementation. * Load decompressed RPaks from 'paks\Win32\' overriding the ones in 'paks\Win64\' (the decompress callback will automatically fix the header and write it to 'paks\Win32\'). * VPK decompress Command callback implementation. * Move CRC32 ands Adler32 to implementation files. * Server will print out more details about the connecting client. * Upgrade ImGui lib to v1.86. * Don't compile id3dx.h for dedicated. * Don't compile id3dx.cpp for dedicated * Implement DevMsg print function allowing to print information to the in-game VGUI/RUI console overlay, ImGui console overlay and the external windows console * Fixed bug where the Error function would not properly terminate the process when an error is called. This caused access violations for critical/non-recoverable errors. * Fixed bug where the game would crash if the console or server browser was enabled while the game was still starting up. * Several bug fixes for the dedicated server (warning: dedicated is still considered work-in-progress!). --- apex.sln | 71 - detours.sln | 51 + external/detours/libs/detours.lib | Bin 722364 -> 0 bytes external/detours/libs/detours.pdb | Bin 184320 -> 0 bytes external/detours/libs/detours_trace.lib | Bin 702732 -> 0 bytes external/detours/libs/syelog.lib | Bin 51394 -> 0 bytes external/minhook/include/MinHook.h | 185 - external/minhook/libMinHook.vcxproj | 173 - external/minhook/libMinHook.vcxproj.filters | 57 - external/minhook/src/buffer.c | 312 - external/minhook/src/buffer.h | 42 - external/minhook/src/hde/hde32.c | 324 - external/minhook/src/hde/hde32.h | 105 - external/minhook/src/hde/hde64.c | 333 - external/minhook/src/hde/hde64.h | 112 - external/minhook/src/hde/pstdint.h | 39 - external/minhook/src/hde/table32.h | 73 - external/minhook/src/hde/table64.h | 74 - external/minhook/src/hook.c | 906 -- external/minhook/src/trampoline.c | 320 - external/minhook/src/trampoline.h | 105 - license/lzham/LICENSE.txt | 19 + license/minhook/AUTHORS.txt | 8 - license/minhook/LICENSE.txt | 81 - r5dedicated/console.cpp | 97 - r5dedicated/console.h | 5 - r5dedicated/csourceappsystemgroup.cpp | 36 - r5dedicated/cvengineserver.cpp | 36 - r5dedicated/dllmain.cpp | 50 - r5dedicated/dllmain.h | 5 - r5dedicated/enums.h | 422 - r5dedicated/gameclasses.cpp | 470 - r5dedicated/gameclasses.h | 487 - r5dedicated/hooks.cpp | 165 - r5dedicated/hooks.h | 169 - r5dedicated/iconvar.cpp | 70 - r5dedicated/msgbox.cpp | 19 - r5dedicated/net.cpp | 69 - r5dedicated/opcodes.h | 117 - r5dedicated/pch.cpp | 1 - r5dedicated/pch.h | 41 - r5dedicated/r5dedicated.def | 4 - r5dedicated/r5dedicated.vcxproj | 318 - r5dedicated/r5dedicated.vcxproj.filters | 450 - r5dedicated/sqvm.cpp | 114 - r5dev/bsplib/bsplib.cpp | 339 + r5dev/bsplib/bsplib.h | 45 + r5dev/client/IVEngineClient.cpp | 13 + r5dev/client/IVEngineClient.h | 25 + r5dev/client/cdll_engine_int.cpp | 133 + r5dev/client/cdll_engine_int.h | 74 + r5dev/client/client.cpp | 5 + r5dev/client/client.h | 69 + {r5dedicated => r5dev/common}/opcodes.cpp | 118 +- r5dev/common/opcodes.h | 119 + r5dev/common/protocol.h | 19 + r5dev/common/psuedodefs.h | 462 + r5dev/{src => core}/dllmain.cpp | 52 +- r5dev/core/init.cpp | 182 + r5dev/core/init.h | 10 + r5dev/core/logdef.h | 23 + r5dev/{include => core}/r5dev.h | 0 r5dev/{ => core}/resource.h | 7 +- r5dev/core/stdafx.cpp | 1 + r5dev/core/stdafx.h | 56 + r5dev/cpp.hint | 9 + r5dev/dedicated.vcxproj | 377 + r5dev/dedicated.vcxproj.filters | 687 ++ r5dev/ebisusdk/EbisuSDK.cpp | 26 + r5dev/ebisusdk/EbisuSDK.h | 64 + r5dev/ebisusdk/EbisuTypes.h | 137 + r5dev/engine/baseclient.cpp | 20 + r5dev/engine/baseclient.h | 27 + r5dev/engine/baseclientstate.cpp | 9 + r5dev/engine/baseclientstate.h | 31 + r5dev/engine/host_state.cpp | 244 + r5dev/engine/host_state.h | 65 + r5dev/engine/net_chan.cpp | 176 + r5dev/engine/net_chan.h | 95 + r5dev/engine/sys_dll.cpp | 26 + r5dev/engine/sys_dll.h | 27 + r5dev/engine/sys_dll2.cpp | 3 + r5dev/engine/sys_dll2.h | 40 + r5dev/engine/sys_utils.cpp | 123 + r5dev/engine/sys_utils.h | 53 + r5dev/gameui/IBrowser.cpp | 712 ++ r5dev/gameui/IBrowser.h | 173 + r5dev/gameui/IConsole.cpp | 408 + .../CGameConsole.h => gameui/IConsole.h} | 41 +- r5dev/include/CCompanion.h | 163 - r5dev/include/console.h | 5 - r5dev/include/enums.h | 790 -- r5dev/include/gameclasses.h | 521 -- r5dev/include/gui_utility.h | 88 - r5dev/include/hooks.h | 155 - r5dev/include/id3dx.h | 27 - r5dev/include/input.h | 1 - r5dev/include/logsystem.h | 36 - r5dev/include/opcptc.h | 60 - r5dev/include/patterns.h | 210 - r5dev/include/pch.h | 43 - r5dev/include/squirrel.h | 17 - r5dev/include/structs.h | 28 - r5dev/inputsystem/ButtonCode.h | 215 + r5dev/inputsystem/inputsystem.cpp | 6 + r5dev/inputsystem/inputsystem.h | 47 + r5dev/launcher/IApplication.cpp | 32 + r5dev/launcher/IApplication.h | 37 + r5dev/mathlib/IceKey.H | 62 + r5dev/mathlib/IceKey.cpp | 392 + r5dev/mathlib/adler32.cpp | 47 + r5dev/mathlib/adler32.h | 7 + r5dev/mathlib/crc32.cpp | 22 + r5dev/mathlib/crc32.h | 16 + r5dev/mathlib/parallel_for.h | 57 + r5dev/mathlib/vector.h | 26 + r5dev/networksystem/r5net.cpp | 328 + r5dev/networksystem/r5net.h | 27 + r5dev/networksystem/serverlisting.h | 14 + r5dev/networksystem/sm_protocol.h | 39 + r5dev/public/bansystem.cpp | 193 + r5dev/public/binstream.cpp | 171 + r5dev/public/include/bansystem.h | 24 + r5dev/public/include/binstream.h | 68 + {shared => r5dev/public}/include/httplib.h | 0 {shared => r5dev/public}/include/json.hpp | 0 .../public/include/memaddr.h | 186 +- r5dev/public/include/stb_image.h | 7897 +++++++++++++++++ r5dev/public/include/utility.h | 20 + r5dev/public/utility.cpp | 298 + r5dev/r5dev.def | 4 +- r5launcher/Apex.props => r5dev/r5dev.props | 0 r5dev/r5dev.vcxproj | 586 +- r5dev/r5dev.vcxproj.filters | 1070 ++- r5dev/resource/ico/sdklauncher_dbg.ico | Bin 0 -> 274208 bytes r5dev/resource/ico/sdklauncher_rel.ico | Bin 0 -> 269547 bytes r5dev/resource/png/lockedserver.png | Bin 0 -> 938 bytes r5dev/{BuildVersion.rc => resource/r5dev.rc} | 22 +- .../resource/sdklauncher.rc | 26 +- r5dev/rtech/rtech.cpp | 497 ++ r5dev/rtech/rtech.h | 176 + r5dev/rtech/stryder.cpp | 3 + r5dev/rtech/stryder.h | 27 + .../sdklauncher.vcxproj | 75 +- .../sdklauncher.vcxproj.filters | 33 +- .../sdklauncher/sdklauncher.cpp | 23 +- .../main.h => r5dev/sdklauncher/sdklauncher.h | 0 .../sdklauncher/sdklauncher_res.h | 2 +- r5dev/server/IVEngineServer.cpp | 70 + r5dev/server/IVEngineServer.h | 30 + r5dev/server/server.cpp | 121 + r5dev/server/server.h | 53 + r5dev/squirrel/sqapi.cpp | 79 + r5dev/squirrel/sqapi.h | 68 + r5dev/squirrel/sqinit.cpp | 8 + r5dev/squirrel/sqinit.h | 23 + r5dev/squirrel/sqvm.cpp | 306 + r5dev/squirrel/sqvm.h | 85 + r5dev/src/CCompanion.cpp | 679 -- r5dev/src/CGameConsole.cpp | 403 - r5dev/src/cenginevgui.cpp | 28 - r5dev/src/gameclasses.cpp | 532 -- r5dev/src/gui_utility.cpp | 44 - r5dev/src/hooks/chlclient.cpp | 52 - r5dev/src/hooks/connectclient.cpp | 90 - r5dev/src/hooks/cvengineserver.cpp | 27 - r5dev/src/hooks/hooks.cpp | 223 - r5dev/src/hooks/hoststate.cpp | 198 - r5dev/src/hooks/iconvar.cpp | 84 - r5dev/src/hooks/loadplaylist.cpp | 35 - r5dev/src/hooks/lockcursor.cpp | 18 - r5dev/src/hooks/msgbox.cpp | 15 - r5dev/src/hooks/net.cpp | 90 - r5dev/src/hooks/netchannel.cpp | 13 - r5dev/src/hooks/originsdk.cpp | 43 - r5dev/src/hooks/sqvm.cpp | 221 - r5dev/src/hooks/winapi.cpp | 59 - r5dev/src/id3dx.cpp | 415 - r5dev/src/input.cpp | 2 - r5dev/src/logsystem.cpp | 75 - r5dev/src/opcptc.cpp | 58 - r5dev/src/pch.cpp | 1 - r5dev/src/squirrel.cpp | 56 - .../thirdparty}/detours/include/detours.h | 0 .../thirdparty}/detours/include/detver.h | 0 r5dev/thirdparty/detours/include/idetour.h | 39 + .../thirdparty}/detours/include/syelog.h | 0 r5dev/thirdparty/detours/libs/detours.lib | Bin 0 -> 852394 bytes r5dev/thirdparty/detours/libs/syelog.lib | Bin 0 -> 69934 bytes .../thirdparty}/imgui/include/imconfig.h | 11 +- .../thirdparty}/imgui/include/imgui.h | 363 +- .../imgui/include/imgui_impl_dx11.h | 3 +- .../imgui/include/imgui_impl_win32.h | 3 +- .../imgui/include/imgui_internal.h | 785 +- .../imgui}/include/imgui_stdlib.h | 0 .../thirdparty/imgui/include/imgui_utility.h | 31 + .../imgui/include/imstb_rectpack.h | 52 +- .../imgui/include/imstb_textedit.h | 82 +- .../imgui/include/imstb_truetype.h | 108 +- .../thirdparty}/imgui/src/imgui.cpp | 3373 ++++--- .../thirdparty}/imgui/src/imgui_demo.cpp | 506 +- .../thirdparty}/imgui/src/imgui_draw.cpp | 242 +- .../thirdparty}/imgui/src/imgui_impl_dx11.cpp | 213 +- .../imgui/src/imgui_impl_win32.cpp | 285 +- .../thirdparty}/imgui/src/imgui_stdlib.cpp | 4 +- .../thirdparty}/imgui/src/imgui_tables.cpp | 247 +- r5dev/thirdparty/imgui/src/imgui_utility.cpp | 103 + .../thirdparty}/imgui/src/imgui_widgets.cpp | 1161 ++- r5dev/thirdparty/lzham/include/lzham.h | 765 ++ .../lzham/include/lzham_static_lib.h | 57 + r5dev/thirdparty/lzham/libs/lzhamcomp_x64.lib | Bin 0 -> 556410 bytes .../thirdparty/lzham/libs/lzhamcomp_x64D.lib | Bin 0 -> 824648 bytes .../thirdparty/lzham/libs/lzhamdecomp_x64.lib | Bin 0 -> 599642 bytes .../lzham/libs/lzhamdecomp_x64D.lib | Bin 0 -> 588216 bytes r5dev/thirdparty/lzham/libs/lzhamlib_x64.lib | Bin 0 -> 26576 bytes r5dev/thirdparty/lzham/libs/lzhamlib_x64D.lib | Bin 0 -> 24960 bytes .../thirdparty}/spdlog/include/async.h | 6 +- .../spdlog/include/async_logger-inl.h | 6 +- .../thirdparty}/spdlog/include/async_logger.h | 2 +- .../thirdparty}/spdlog/include/cfg/argv.h | 4 +- .../thirdparty}/spdlog/include/cfg/env.h | 6 +- .../spdlog/include/cfg/helpers-inl.h | 8 +- .../thirdparty}/spdlog/include/cfg/helpers.h | 2 +- .../thirdparty}/spdlog/include/common-inl.h | 2 +- .../thirdparty}/spdlog/include/common.h | 6 +- .../spdlog/include/details/backtracer-inl.h | 2 +- .../spdlog/include/details/backtracer.h | 4 +- .../spdlog/include/details/circular_q.h | 0 .../spdlog/include/details/console_globals.h | 2 +- .../spdlog/include/details/file_helper-inl.h | 6 +- .../spdlog/include/details/file_helper.h | 2 +- .../spdlog/include/details/fmt_helper.h | 4 +- .../spdlog/include/details/log_msg-inl.h | 4 +- .../spdlog/include/details/log_msg.h | 2 +- .../include/details/log_msg_buffer-inl.h | 2 +- .../spdlog/include/details/log_msg_buffer.h | 2 +- .../spdlog/include/details/mpmc_blocking_q.h | 2 +- .../spdlog/include/details/null_mutex.h | 0 .../spdlog/include/details/os-inl.h | 6 +- .../thirdparty}/spdlog/include/details/os.h | 2 +- .../include/details/periodic_worker-inl.h | 2 +- .../spdlog/include/details/periodic_worker.h | 0 .../spdlog/include/details/registry-inl.h | 14 +- .../spdlog/include/details/registry.h | 2 +- .../include/details/synchronous_factory.h | 0 .../include/details/tcp_client-windows.h | 4 +- .../spdlog/include/details/tcp_client.h | 4 +- .../spdlog/include/details/thread_pool-inl.h | 4 +- .../spdlog/include/details/thread_pool.h | 6 +- .../spdlog/include/details/windows_include.h | 0 .../spdlog/include/fmt/bin_to_hex.h | 0 .../spdlog/include/fmt/bundled/args.h | 0 .../spdlog/include/fmt/bundled/chrono.h | 0 .../spdlog/include/fmt/bundled/color.h | 0 .../spdlog/include/fmt/bundled/compile.h | 0 .../spdlog/include/fmt/bundled/core.h | 0 .../spdlog/include/fmt/bundled/format-inl.h | 0 .../spdlog/include/fmt/bundled/format.h | 0 .../spdlog/include/fmt/bundled/locale.h | 0 .../spdlog/include/fmt/bundled/os.h | 0 .../spdlog/include/fmt/bundled/ostream.h | 0 .../spdlog/include/fmt/bundled/printf.h | 0 .../spdlog/include/fmt/bundled/ranges.h | 0 .../spdlog/include/fmt/bundled/xchar.h | 0 .../thirdparty}/spdlog/include/fmt/chrono.h | 0 .../thirdparty}/spdlog/include/fmt/fmt.h | 8 +- .../thirdparty}/spdlog/include/fmt/ostr.h | 0 .../thirdparty}/spdlog/include/fmt/xchar.h | 0 .../thirdparty}/spdlog/include/formatter.h | 4 +- .../thirdparty}/spdlog/include/fwd.h | 0 .../thirdparty}/spdlog/include/logger-inl.h | 6 +- .../thirdparty}/spdlog/include/logger.h | 6 +- .../spdlog/include/pattern_formatter-inl.h | 10 +- .../spdlog/include/pattern_formatter.h | 8 +- .../spdlog/include/sinks/android_sink.h | 10 +- .../spdlog/include/sinks/ansicolor_sink-inl.h | 6 +- .../spdlog/include/sinks/ansicolor_sink.h | 7 +- .../spdlog/include/sinks/base_sink-inl.h | 6 +- .../spdlog/include/sinks/base_sink.h | 6 +- .../include/sinks/basic_file_sink-inl.h | 6 +- .../spdlog/include/sinks/basic_file_sink.h | 8 +- .../spdlog/include/sinks/daily_file_sink.h | 18 +- .../spdlog/include/sinks/dist_sink.h | 8 +- .../spdlog/include/sinks/dup_filter_sink.h | 6 +- .../spdlog/include/sinks/hourly_file_sink.h | 16 +- .../spdlog/include/sinks/mongo_sink.h | 8 +- .../spdlog/include/sinks/msvc_sink.h | 4 +- .../spdlog/include/sinks/null_sink.h | 6 +- .../spdlog/include/sinks/ostream_sink.h | 4 +- .../spdlog/include/sinks/qt_sinks.h | 8 +- .../spdlog/include/sinks/ringbuffer_sink.h | 8 +- .../include/sinks/rotating_file_sink-inl.h | 10 +- .../spdlog/include/sinks/rotating_file_sink.h | 8 +- .../spdlog/include/sinks/sink-inl.h | 4 +- .../thirdparty}/spdlog/include/sinks/sink.h | 4 +- .../include/sinks/stdout_color_sinks-inl.h | 2 +- .../spdlog/include/sinks/stdout_color_sinks.h | 8 +- .../spdlog/include/sinks/stdout_sinks-inl.h | 8 +- .../spdlog/include/sinks/stdout_sinks.h | 8 +- .../spdlog/include/sinks/syslog_sink.h | 6 +- .../spdlog/include/sinks/systemd_sink.h | 6 +- .../spdlog/include/sinks/tcp_sink.h | 10 +- .../spdlog/include/sinks/win_eventlog_sink.h | 8 +- .../spdlog/include/sinks/wincolor_sink-inl.h | 8 +- .../spdlog/include/sinks/wincolor_sink.h | 8 +- .../thirdparty}/spdlog/include/spdlog-inl.h | 6 +- .../thirdparty}/spdlog/include/spdlog.h | 10 +- .../thirdparty}/spdlog/include/stopwatch.h | 2 +- .../thirdparty}/spdlog/include/tweakme.h | 0 .../thirdparty}/spdlog/include/version.h | 0 r5dev/tier0/ConCommand.cpp | 118 + r5dev/tier0/ConCommand.h | 133 + r5dev/tier0/IConVar.cpp | 144 + r5dev/tier0/IConVar.h | 173 + r5dev/tier0/basetypes.h | 19 + r5dev/tier0/completion.cpp | 588 ++ r5dev/tier0/completion.h | 49 + r5dev/tier0/cvar.cpp | 40 + r5dev/tier0/cvar.h | 66 + r5dev/tier0/interface.h | 4 + r5dev/vgui/CEngineVGui.cpp | 118 + r5dev/vgui/CEngineVGui.h | 76 + r5dev/vgui/vgui.h | 19 + r5dev/vgui/vgui_fpspanel.cpp | 20 + r5dev/vgui/vgui_fpspanel.h | 24 + r5dev/vguimatsurface/MatSystemSurface.cpp | 3 + r5dev/vguimatsurface/MatSystemSurface.h | 31 + r5dev/vpc/IAppSystem.cpp | 3 + r5dev/vpc/IAppSystem.h | 22 + r5dev/vpc/basefilesystem.cpp | 63 + r5dev/vpc/basefilesystem.h | 37 + r5dev/vpc/interfaces.h | 15 + r5dev/vpc/keyvalues.cpp | 100 + r5dev/vpc/keyvalues.h | 263 + r5dev/vphysics/QHull.cpp | 28 + r5dev/vphysics/QHull.h | 31 + r5dev/vpklib/packedstore.cpp | 365 + r5dev/vpklib/packedstore.h | 74 + r5dev/{src => windows}/console.cpp | 117 +- r5dev/windows/console.h | 3 + r5dev/windows/id3dx.cpp | 563 ++ r5dev/windows/id3dx.h | 121 + r5dev/windows/input.cpp | 119 + r5dev/windows/input.h | 12 + r5launcher/pch.cpp | 1 - r5launcher/pch.h | 12 - r5launcher/r5reloaded.ico | Bin 64463 -> 0 bytes r5net/include/netpch.h | 28 - r5net/include/r5/r5net.h | 33 - r5net/include/r5/serverlisting.h | 15 - r5net/r5net.vcxproj | 178 - r5net/r5net.vcxproj.filters | 33 - r5net/src/netpch.cpp | 2 - r5net/src/r5net.cpp | 264 - r5net/src/serverlisting.cpp | 2 - shared/include/banlist.h | 155 - shared/include/utility.h | 22 - shared/utility.cpp | 193 - 358 files changed, 27549 insertions(+), 16877 deletions(-) delete mode 100644 apex.sln create mode 100644 detours.sln delete mode 100644 external/detours/libs/detours.lib delete mode 100644 external/detours/libs/detours.pdb delete mode 100644 external/detours/libs/detours_trace.lib delete mode 100644 external/detours/libs/syelog.lib delete mode 100644 external/minhook/include/MinHook.h delete mode 100644 external/minhook/libMinHook.vcxproj delete mode 100644 external/minhook/libMinHook.vcxproj.filters delete mode 100644 external/minhook/src/buffer.c delete mode 100644 external/minhook/src/buffer.h delete mode 100644 external/minhook/src/hde/hde32.c delete mode 100644 external/minhook/src/hde/hde32.h delete mode 100644 external/minhook/src/hde/hde64.c delete mode 100644 external/minhook/src/hde/hde64.h delete mode 100644 external/minhook/src/hde/pstdint.h delete mode 100644 external/minhook/src/hde/table32.h delete mode 100644 external/minhook/src/hde/table64.h delete mode 100644 external/minhook/src/hook.c delete mode 100644 external/minhook/src/trampoline.c delete mode 100644 external/minhook/src/trampoline.h create mode 100644 license/lzham/LICENSE.txt delete mode 100644 license/minhook/AUTHORS.txt delete mode 100644 license/minhook/LICENSE.txt delete mode 100644 r5dedicated/console.cpp delete mode 100644 r5dedicated/console.h delete mode 100644 r5dedicated/csourceappsystemgroup.cpp delete mode 100644 r5dedicated/cvengineserver.cpp delete mode 100644 r5dedicated/dllmain.cpp delete mode 100644 r5dedicated/dllmain.h delete mode 100644 r5dedicated/enums.h delete mode 100644 r5dedicated/gameclasses.cpp delete mode 100644 r5dedicated/gameclasses.h delete mode 100644 r5dedicated/hooks.cpp delete mode 100644 r5dedicated/hooks.h delete mode 100644 r5dedicated/iconvar.cpp delete mode 100644 r5dedicated/msgbox.cpp delete mode 100644 r5dedicated/net.cpp delete mode 100644 r5dedicated/opcodes.h delete mode 100644 r5dedicated/pch.cpp delete mode 100644 r5dedicated/pch.h delete mode 100644 r5dedicated/r5dedicated.def delete mode 100644 r5dedicated/r5dedicated.vcxproj delete mode 100644 r5dedicated/r5dedicated.vcxproj.filters delete mode 100644 r5dedicated/sqvm.cpp create mode 100644 r5dev/bsplib/bsplib.cpp create mode 100644 r5dev/bsplib/bsplib.h create mode 100644 r5dev/client/IVEngineClient.cpp create mode 100644 r5dev/client/IVEngineClient.h create mode 100644 r5dev/client/cdll_engine_int.cpp create mode 100644 r5dev/client/cdll_engine_int.h create mode 100644 r5dev/client/client.cpp create mode 100644 r5dev/client/client.h rename {r5dedicated => r5dev/common}/opcodes.cpp (75%) create mode 100644 r5dev/common/opcodes.h create mode 100644 r5dev/common/protocol.h create mode 100644 r5dev/common/psuedodefs.h rename r5dev/{src => core}/dllmain.cpp (52%) create mode 100644 r5dev/core/init.cpp create mode 100644 r5dev/core/init.h create mode 100644 r5dev/core/logdef.h rename r5dev/{include => core}/r5dev.h (100%) rename r5dev/{ => core}/resource.h (67%) create mode 100644 r5dev/core/stdafx.cpp create mode 100644 r5dev/core/stdafx.h create mode 100644 r5dev/cpp.hint create mode 100644 r5dev/dedicated.vcxproj create mode 100644 r5dev/dedicated.vcxproj.filters create mode 100644 r5dev/ebisusdk/EbisuSDK.cpp create mode 100644 r5dev/ebisusdk/EbisuSDK.h create mode 100644 r5dev/ebisusdk/EbisuTypes.h create mode 100644 r5dev/engine/baseclient.cpp create mode 100644 r5dev/engine/baseclient.h create mode 100644 r5dev/engine/baseclientstate.cpp create mode 100644 r5dev/engine/baseclientstate.h create mode 100644 r5dev/engine/host_state.cpp create mode 100644 r5dev/engine/host_state.h create mode 100644 r5dev/engine/net_chan.cpp create mode 100644 r5dev/engine/net_chan.h create mode 100644 r5dev/engine/sys_dll.cpp create mode 100644 r5dev/engine/sys_dll.h create mode 100644 r5dev/engine/sys_dll2.cpp create mode 100644 r5dev/engine/sys_dll2.h create mode 100644 r5dev/engine/sys_utils.cpp create mode 100644 r5dev/engine/sys_utils.h create mode 100644 r5dev/gameui/IBrowser.cpp create mode 100644 r5dev/gameui/IBrowser.h create mode 100644 r5dev/gameui/IConsole.cpp rename r5dev/{include/CGameConsole.h => gameui/IConsole.h} (84%) delete mode 100644 r5dev/include/CCompanion.h delete mode 100644 r5dev/include/console.h delete mode 100644 r5dev/include/enums.h delete mode 100644 r5dev/include/gameclasses.h delete mode 100644 r5dev/include/gui_utility.h delete mode 100644 r5dev/include/hooks.h delete mode 100644 r5dev/include/id3dx.h delete mode 100644 r5dev/include/input.h delete mode 100644 r5dev/include/logsystem.h delete mode 100644 r5dev/include/opcptc.h delete mode 100644 r5dev/include/patterns.h delete mode 100644 r5dev/include/pch.h delete mode 100644 r5dev/include/squirrel.h delete mode 100644 r5dev/include/structs.h create mode 100644 r5dev/inputsystem/ButtonCode.h create mode 100644 r5dev/inputsystem/inputsystem.cpp create mode 100644 r5dev/inputsystem/inputsystem.h create mode 100644 r5dev/launcher/IApplication.cpp create mode 100644 r5dev/launcher/IApplication.h create mode 100644 r5dev/mathlib/IceKey.H create mode 100644 r5dev/mathlib/IceKey.cpp create mode 100644 r5dev/mathlib/adler32.cpp create mode 100644 r5dev/mathlib/adler32.h create mode 100644 r5dev/mathlib/crc32.cpp create mode 100644 r5dev/mathlib/crc32.h create mode 100644 r5dev/mathlib/parallel_for.h create mode 100644 r5dev/mathlib/vector.h create mode 100644 r5dev/networksystem/r5net.cpp create mode 100644 r5dev/networksystem/r5net.h create mode 100644 r5dev/networksystem/serverlisting.h create mode 100644 r5dev/networksystem/sm_protocol.h create mode 100644 r5dev/public/bansystem.cpp create mode 100644 r5dev/public/binstream.cpp create mode 100644 r5dev/public/include/bansystem.h create mode 100644 r5dev/public/include/binstream.h rename {shared => r5dev/public}/include/httplib.h (100%) rename {shared => r5dev/public}/include/json.hpp (100%) rename shared/include/address.h => r5dev/public/include/memaddr.h (76%) create mode 100644 r5dev/public/include/stb_image.h create mode 100644 r5dev/public/include/utility.h create mode 100644 r5dev/public/utility.cpp rename r5launcher/Apex.props => r5dev/r5dev.props (100%) create mode 100644 r5dev/resource/ico/sdklauncher_dbg.ico create mode 100644 r5dev/resource/ico/sdklauncher_rel.ico create mode 100644 r5dev/resource/png/lockedserver.png rename r5dev/{BuildVersion.rc => resource/r5dev.rc} (77%) rename r5launcher/r5launcher.rc => r5dev/resource/sdklauncher.rc (69%) create mode 100644 r5dev/rtech/rtech.cpp create mode 100644 r5dev/rtech/rtech.h create mode 100644 r5dev/rtech/stryder.cpp create mode 100644 r5dev/rtech/stryder.h rename r5launcher/r5launcher.vcxproj => r5dev/sdklauncher.vcxproj (79%) rename r5launcher/r5launcher.vcxproj.filters => r5dev/sdklauncher.vcxproj.filters (74%) rename r5launcher/main.cpp => r5dev/sdklauncher/sdklauncher.cpp (93%) rename r5launcher/main.h => r5dev/sdklauncher/sdklauncher.h (100%) rename r5launcher/resource.h => r5dev/sdklauncher/sdklauncher_res.h (94%) create mode 100644 r5dev/server/IVEngineServer.cpp create mode 100644 r5dev/server/IVEngineServer.h create mode 100644 r5dev/server/server.cpp create mode 100644 r5dev/server/server.h create mode 100644 r5dev/squirrel/sqapi.cpp create mode 100644 r5dev/squirrel/sqapi.h create mode 100644 r5dev/squirrel/sqinit.cpp create mode 100644 r5dev/squirrel/sqinit.h create mode 100644 r5dev/squirrel/sqvm.cpp create mode 100644 r5dev/squirrel/sqvm.h delete mode 100644 r5dev/src/CCompanion.cpp delete mode 100644 r5dev/src/CGameConsole.cpp delete mode 100644 r5dev/src/cenginevgui.cpp delete mode 100644 r5dev/src/gameclasses.cpp delete mode 100644 r5dev/src/gui_utility.cpp delete mode 100644 r5dev/src/hooks/chlclient.cpp delete mode 100644 r5dev/src/hooks/connectclient.cpp delete mode 100644 r5dev/src/hooks/cvengineserver.cpp delete mode 100644 r5dev/src/hooks/hooks.cpp delete mode 100644 r5dev/src/hooks/hoststate.cpp delete mode 100644 r5dev/src/hooks/iconvar.cpp delete mode 100644 r5dev/src/hooks/loadplaylist.cpp delete mode 100644 r5dev/src/hooks/lockcursor.cpp delete mode 100644 r5dev/src/hooks/msgbox.cpp delete mode 100644 r5dev/src/hooks/net.cpp delete mode 100644 r5dev/src/hooks/netchannel.cpp delete mode 100644 r5dev/src/hooks/originsdk.cpp delete mode 100644 r5dev/src/hooks/sqvm.cpp delete mode 100644 r5dev/src/hooks/winapi.cpp delete mode 100644 r5dev/src/id3dx.cpp delete mode 100644 r5dev/src/input.cpp delete mode 100644 r5dev/src/logsystem.cpp delete mode 100644 r5dev/src/opcptc.cpp delete mode 100644 r5dev/src/pch.cpp delete mode 100644 r5dev/src/squirrel.cpp rename {external => r5dev/thirdparty}/detours/include/detours.h (100%) rename {external => r5dev/thirdparty}/detours/include/detver.h (100%) create mode 100644 r5dev/thirdparty/detours/include/idetour.h rename {external => r5dev/thirdparty}/detours/include/syelog.h (100%) create mode 100644 r5dev/thirdparty/detours/libs/detours.lib create mode 100644 r5dev/thirdparty/detours/libs/syelog.lib rename {external => r5dev/thirdparty}/imgui/include/imconfig.h (88%) rename {external => r5dev/thirdparty}/imgui/include/imgui.h (93%) rename {external => r5dev/thirdparty}/imgui/include/imgui_impl_dx11.h (81%) rename {external => r5dev/thirdparty}/imgui/include/imgui_impl_win32.h (91%) rename {external => r5dev/thirdparty}/imgui/include/imgui_internal.h (84%) rename r5dev/{ => thirdparty/imgui}/include/imgui_stdlib.h (100%) create mode 100644 r5dev/thirdparty/imgui/include/imgui_utility.h rename {external => r5dev/thirdparty}/imgui/include/imstb_rectpack.h (98%) rename {external => r5dev/thirdparty}/imgui/include/imstb_textedit.h (98%) rename {external => r5dev/thirdparty}/imgui/include/imstb_truetype.h (99%) rename {external => r5dev/thirdparty}/imgui/src/imgui.cpp (81%) rename {external => r5dev/thirdparty}/imgui/src/imgui_demo.cpp (95%) rename {external => r5dev/thirdparty}/imgui/src/imgui_draw.cpp (95%) rename {external => r5dev/thirdparty}/imgui/src/imgui_impl_dx11.cpp (72%) rename {external => r5dev/thirdparty}/imgui/src/imgui_impl_win32.cpp (67%) rename {external => r5dev/thirdparty}/imgui/src/imgui_stdlib.cpp (97%) rename {external => r5dev/thirdparty}/imgui/src/imgui_tables.cpp (95%) create mode 100644 r5dev/thirdparty/imgui/src/imgui_utility.cpp rename {external => r5dev/thirdparty}/imgui/src/imgui_widgets.cpp (89%) create mode 100644 r5dev/thirdparty/lzham/include/lzham.h create mode 100644 r5dev/thirdparty/lzham/include/lzham_static_lib.h create mode 100644 r5dev/thirdparty/lzham/libs/lzhamcomp_x64.lib create mode 100644 r5dev/thirdparty/lzham/libs/lzhamcomp_x64D.lib create mode 100644 r5dev/thirdparty/lzham/libs/lzhamdecomp_x64.lib create mode 100644 r5dev/thirdparty/lzham/libs/lzhamdecomp_x64D.lib create mode 100644 r5dev/thirdparty/lzham/libs/lzhamlib_x64.lib create mode 100644 r5dev/thirdparty/lzham/libs/lzhamlib_x64D.lib rename {external => r5dev/thirdparty}/spdlog/include/async.h (95%) rename {external => r5dev/thirdparty}/spdlog/include/async_logger-inl.h (93%) rename {external => r5dev/thirdparty}/spdlog/include/async_logger.h (97%) rename {external => r5dev/thirdparty}/spdlog/include/cfg/argv.h (91%) rename {external => r5dev/thirdparty}/spdlog/include/cfg/env.h (84%) rename {external => r5dev/thirdparty}/spdlog/include/cfg/helpers-inl.h (93%) rename {external => r5dev/thirdparty}/spdlog/include/cfg/helpers.h (93%) rename {external => r5dev/thirdparty}/spdlog/include/common-inl.h (97%) rename {external => r5dev/thirdparty}/spdlog/include/common.h (98%) rename {external => r5dev/thirdparty}/spdlog/include/details/backtracer-inl.h (96%) rename {external => r5dev/thirdparty}/spdlog/include/details/backtracer.h (89%) rename {external => r5dev/thirdparty}/spdlog/include/details/circular_q.h (100%) rename {external => r5dev/thirdparty}/spdlog/include/details/console_globals.h (90%) rename {external => r5dev/thirdparty}/spdlog/include/details/file_helper-inl.h (95%) rename {external => r5dev/thirdparty}/spdlog/include/details/file_helper.h (97%) rename {external => r5dev/thirdparty}/spdlog/include/details/fmt_helper.h (97%) rename {external => r5dev/thirdparty}/spdlog/include/details/log_msg-inl.h (90%) rename {external => r5dev/thirdparty}/spdlog/include/details/log_msg.h (96%) rename {external => r5dev/thirdparty}/spdlog/include/details/log_msg_buffer-inl.h (96%) rename {external => r5dev/thirdparty}/spdlog/include/details/log_msg_buffer.h (94%) rename {external => r5dev/thirdparty}/spdlog/include/details/mpmc_blocking_q.h (98%) rename {external => r5dev/thirdparty}/spdlog/include/details/null_mutex.h (100%) rename {external => r5dev/thirdparty}/spdlog/include/details/os-inl.h (99%) rename {external => r5dev/thirdparty}/spdlog/include/details/os.h (98%) rename {external => r5dev/thirdparty}/spdlog/include/details/periodic_worker-inl.h (94%) rename {external => r5dev/thirdparty}/spdlog/include/details/periodic_worker.h (100%) rename {external => r5dev/thirdparty}/spdlog/include/details/registry-inl.h (95%) rename {external => r5dev/thirdparty}/spdlog/include/details/registry.h (98%) rename {external => r5dev/thirdparty}/spdlog/include/details/synchronous_factory.h (100%) rename {external => r5dev/thirdparty}/spdlog/include/details/tcp_client-windows.h (97%) rename {external => r5dev/thirdparty}/spdlog/include/details/tcp_client.h (97%) rename {external => r5dev/thirdparty}/spdlog/include/details/thread_pool-inl.h (96%) rename {external => r5dev/thirdparty}/spdlog/include/details/thread_pool.h (94%) rename {external => r5dev/thirdparty}/spdlog/include/details/windows_include.h (100%) rename {external => r5dev/thirdparty}/spdlog/include/fmt/bin_to_hex.h (100%) rename {external => r5dev/thirdparty}/spdlog/include/fmt/bundled/args.h (100%) rename {external => r5dev/thirdparty}/spdlog/include/fmt/bundled/chrono.h (100%) rename {external => r5dev/thirdparty}/spdlog/include/fmt/bundled/color.h (100%) rename {external => r5dev/thirdparty}/spdlog/include/fmt/bundled/compile.h (100%) rename {external => r5dev/thirdparty}/spdlog/include/fmt/bundled/core.h (100%) rename {external => r5dev/thirdparty}/spdlog/include/fmt/bundled/format-inl.h (100%) rename {external => r5dev/thirdparty}/spdlog/include/fmt/bundled/format.h (100%) rename {external => r5dev/thirdparty}/spdlog/include/fmt/bundled/locale.h (100%) rename {external => r5dev/thirdparty}/spdlog/include/fmt/bundled/os.h (100%) rename {external => r5dev/thirdparty}/spdlog/include/fmt/bundled/ostream.h (100%) rename {external => r5dev/thirdparty}/spdlog/include/fmt/bundled/printf.h (100%) rename {external => r5dev/thirdparty}/spdlog/include/fmt/bundled/ranges.h (100%) rename {external => r5dev/thirdparty}/spdlog/include/fmt/bundled/xchar.h (100%) rename {external => r5dev/thirdparty}/spdlog/include/fmt/chrono.h (100%) rename {external => r5dev/thirdparty}/spdlog/include/fmt/fmt.h (74%) rename {external => r5dev/thirdparty}/spdlog/include/fmt/ostr.h (100%) rename {external => r5dev/thirdparty}/spdlog/include/fmt/xchar.h (100%) rename {external => r5dev/thirdparty}/spdlog/include/formatter.h (79%) rename {external => r5dev/thirdparty}/spdlog/include/fwd.h (100%) rename {external => r5dev/thirdparty}/spdlog/include/logger-inl.h (97%) rename {external => r5dev/thirdparty}/spdlog/include/logger.h (98%) rename {external => r5dev/thirdparty}/spdlog/include/pattern_formatter-inl.h (99%) rename {external => r5dev/thirdparty}/spdlog/include/pattern_formatter.h (94%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/android_sink.h (91%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/ansicolor_sink-inl.h (96%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/ansicolor_sink.h (95%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/base_sink-inl.h (91%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/base_sink.h (90%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/basic_file_sink-inl.h (85%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/basic_file_sink.h (86%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/daily_file_sink.h (94%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/dist_sink.h (91%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/dup_filter_sink.h (94%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/hourly_file_sink.h (92%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/mongo_sink.h (92%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/msvc_sink.h (90%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/null_sink.h (86%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/ostream_sink.h (91%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/qt_sinks.h (95%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/ringbuffer_sink.h (88%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/rotating_file_sink-inl.h (93%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/rotating_file_sink.h (90%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/sink-inl.h (87%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/sink.h (88%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/stdout_color_sinks-inl.h (94%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/stdout_color_sinks.h (85%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/stdout_sinks-inl.h (94%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/stdout_sinks.h (90%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/syslog_sink.h (95%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/systemd_sink.h (94%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/tcp_sink.h (87%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/win_eventlog_sink.h (97%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/wincolor_sink-inl.h (96%) rename {external => r5dev/thirdparty}/spdlog/include/sinks/wincolor_sink.h (92%) rename {external => r5dev/thirdparty}/spdlog/include/spdlog-inl.h (95%) rename {external => r5dev/thirdparty}/spdlog/include/spdlog.h (97%) rename {external => r5dev/thirdparty}/spdlog/include/stopwatch.h (97%) rename {external => r5dev/thirdparty}/spdlog/include/tweakme.h (100%) rename {external => r5dev/thirdparty}/spdlog/include/version.h (100%) create mode 100644 r5dev/tier0/ConCommand.cpp create mode 100644 r5dev/tier0/ConCommand.h create mode 100644 r5dev/tier0/IConVar.cpp create mode 100644 r5dev/tier0/IConVar.h create mode 100644 r5dev/tier0/basetypes.h create mode 100644 r5dev/tier0/completion.cpp create mode 100644 r5dev/tier0/completion.h create mode 100644 r5dev/tier0/cvar.cpp create mode 100644 r5dev/tier0/cvar.h create mode 100644 r5dev/tier0/interface.h create mode 100644 r5dev/vgui/CEngineVGui.cpp create mode 100644 r5dev/vgui/CEngineVGui.h create mode 100644 r5dev/vgui/vgui.h create mode 100644 r5dev/vgui/vgui_fpspanel.cpp create mode 100644 r5dev/vgui/vgui_fpspanel.h create mode 100644 r5dev/vguimatsurface/MatSystemSurface.cpp create mode 100644 r5dev/vguimatsurface/MatSystemSurface.h create mode 100644 r5dev/vpc/IAppSystem.cpp create mode 100644 r5dev/vpc/IAppSystem.h create mode 100644 r5dev/vpc/basefilesystem.cpp create mode 100644 r5dev/vpc/basefilesystem.h create mode 100644 r5dev/vpc/interfaces.h create mode 100644 r5dev/vpc/keyvalues.cpp create mode 100644 r5dev/vpc/keyvalues.h create mode 100644 r5dev/vphysics/QHull.cpp create mode 100644 r5dev/vphysics/QHull.h create mode 100644 r5dev/vpklib/packedstore.cpp create mode 100644 r5dev/vpklib/packedstore.h rename r5dev/{src => windows}/console.cpp (62%) create mode 100644 r5dev/windows/console.h create mode 100644 r5dev/windows/id3dx.cpp create mode 100644 r5dev/windows/id3dx.h create mode 100644 r5dev/windows/input.cpp create mode 100644 r5dev/windows/input.h delete mode 100644 r5launcher/pch.cpp delete mode 100644 r5launcher/pch.h delete mode 100644 r5launcher/r5reloaded.ico delete mode 100644 r5net/include/netpch.h delete mode 100644 r5net/include/r5/r5net.h delete mode 100644 r5net/include/r5/serverlisting.h delete mode 100644 r5net/r5net.vcxproj delete mode 100644 r5net/r5net.vcxproj.filters delete mode 100644 r5net/src/netpch.cpp delete mode 100644 r5net/src/r5net.cpp delete mode 100644 r5net/src/serverlisting.cpp delete mode 100644 shared/include/banlist.h delete mode 100644 shared/include/utility.h delete mode 100644 shared/utility.cpp diff --git a/apex.sln b/apex.sln deleted file mode 100644 index 6f04a266..00000000 --- a/apex.sln +++ /dev/null @@ -1,71 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30621.155 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "r5launcher", "r5launcher\r5launcher.vcxproj", "{18F8C75E-3844-4AA6-AB93-980A08253519}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "r5dev", "r5dev\r5dev.vcxproj", "{28CC6B4F-7A95-4933-ADA9-65E38D48516D}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "r5dedicated", "r5dedicated\r5dedicated.vcxproj", "{71988D92-343C-49AB-B52B-0AE0E83B0401}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Minhook", "external\minhook\libMinHook.vcxproj", "{F142A341-5EE0-442D-A15F-98AE9B48DBAE}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "r5net", "r5net\r5net.vcxproj", "{F04BE619-0326-4FF1-B06B-FBE882E04D5E}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {18F8C75E-3844-4AA6-AB93-980A08253519}.Debug|x64.ActiveCfg = Debug|x64 - {18F8C75E-3844-4AA6-AB93-980A08253519}.Debug|x64.Build.0 = Debug|x64 - {18F8C75E-3844-4AA6-AB93-980A08253519}.Debug|x86.ActiveCfg = Debug|Win32 - {18F8C75E-3844-4AA6-AB93-980A08253519}.Debug|x86.Build.0 = Debug|Win32 - {18F8C75E-3844-4AA6-AB93-980A08253519}.Release|x64.ActiveCfg = Release|x64 - {18F8C75E-3844-4AA6-AB93-980A08253519}.Release|x64.Build.0 = Release|x64 - {18F8C75E-3844-4AA6-AB93-980A08253519}.Release|x86.ActiveCfg = Release|Win32 - {18F8C75E-3844-4AA6-AB93-980A08253519}.Release|x86.Build.0 = Release|Win32 - {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Debug|x64.ActiveCfg = Debug|x64 - {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Debug|x64.Build.0 = Debug|x64 - {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Debug|x86.ActiveCfg = Debug|Win32 - {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Debug|x86.Build.0 = Debug|Win32 - {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Release|x64.ActiveCfg = Release|x64 - {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Release|x64.Build.0 = Release|x64 - {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Release|x86.ActiveCfg = Release|Win32 - {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Release|x86.Build.0 = Release|Win32 - {71988D92-343C-49AB-B52B-0AE0E83B0401}.Debug|x64.ActiveCfg = Debug|x64 - {71988D92-343C-49AB-B52B-0AE0E83B0401}.Debug|x64.Build.0 = Debug|x64 - {71988D92-343C-49AB-B52B-0AE0E83B0401}.Debug|x86.ActiveCfg = Debug|Win32 - {71988D92-343C-49AB-B52B-0AE0E83B0401}.Debug|x86.Build.0 = Debug|Win32 - {71988D92-343C-49AB-B52B-0AE0E83B0401}.Release|x64.ActiveCfg = Release|x64 - {71988D92-343C-49AB-B52B-0AE0E83B0401}.Release|x64.Build.0 = Release|x64 - {71988D92-343C-49AB-B52B-0AE0E83B0401}.Release|x86.ActiveCfg = Release|Win32 - {71988D92-343C-49AB-B52B-0AE0E83B0401}.Release|x86.Build.0 = Release|Win32 - {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|x64.ActiveCfg = Debug|x64 - {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|x64.Build.0 = Debug|x64 - {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|x86.ActiveCfg = Debug|Win32 - {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|x86.Build.0 = Debug|Win32 - {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|x64.ActiveCfg = Release|x64 - {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|x64.Build.0 = Release|x64 - {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|x86.ActiveCfg = Release|Win32 - {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|x86.Build.0 = Release|Win32 - {F04BE619-0326-4FF1-B06B-FBE882E04D5E}.Debug|x64.ActiveCfg = Debug|x64 - {F04BE619-0326-4FF1-B06B-FBE882E04D5E}.Debug|x64.Build.0 = Debug|x64 - {F04BE619-0326-4FF1-B06B-FBE882E04D5E}.Debug|x86.ActiveCfg = Debug|Win32 - {F04BE619-0326-4FF1-B06B-FBE882E04D5E}.Debug|x86.Build.0 = Debug|Win32 - {F04BE619-0326-4FF1-B06B-FBE882E04D5E}.Release|x64.ActiveCfg = Release|x64 - {F04BE619-0326-4FF1-B06B-FBE882E04D5E}.Release|x64.Build.0 = Release|x64 - {F04BE619-0326-4FF1-B06B-FBE882E04D5E}.Release|x86.ActiveCfg = Release|Win32 - {F04BE619-0326-4FF1-B06B-FBE882E04D5E}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {5E5FE02E-6BCE-4BAF-9948-C56476039C3C} - EndGlobalSection -EndGlobal diff --git a/detours.sln b/detours.sln new file mode 100644 index 00000000..4aed7e75 --- /dev/null +++ b/detours.sln @@ -0,0 +1,51 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31808.319 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "r5devdll", "r5dev\r5dev.vcxproj", "{28CC6B4F-7A95-4933-ADA9-65E38D48516D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sdklauncher", "r5dev\sdklauncher.vcxproj", "{18F8C75E-3844-4AA6-AB93-980A08253519}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dedicated", "r5dev\dedicated.vcxproj", "{ED2C50B3-7C2C-4E44-988E-DAA059F72B9C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Debug|x64.ActiveCfg = Debug|x64 + {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Debug|x64.Build.0 = Debug|x64 + {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Debug|x86.ActiveCfg = Debug|Win32 + {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Debug|x86.Build.0 = Debug|Win32 + {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Release|x64.ActiveCfg = Release|x64 + {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Release|x64.Build.0 = Release|x64 + {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Release|x86.ActiveCfg = Release|Win32 + {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Release|x86.Build.0 = Release|Win32 + {18F8C75E-3844-4AA6-AB93-980A08253519}.Debug|x64.ActiveCfg = Debug|x64 + {18F8C75E-3844-4AA6-AB93-980A08253519}.Debug|x64.Build.0 = Debug|x64 + {18F8C75E-3844-4AA6-AB93-980A08253519}.Debug|x86.ActiveCfg = Debug|Win32 + {18F8C75E-3844-4AA6-AB93-980A08253519}.Debug|x86.Build.0 = Debug|Win32 + {18F8C75E-3844-4AA6-AB93-980A08253519}.Release|x64.ActiveCfg = Release|x64 + {18F8C75E-3844-4AA6-AB93-980A08253519}.Release|x64.Build.0 = Release|x64 + {18F8C75E-3844-4AA6-AB93-980A08253519}.Release|x86.ActiveCfg = Release|Win32 + {18F8C75E-3844-4AA6-AB93-980A08253519}.Release|x86.Build.0 = Release|Win32 + {ED2C50B3-7C2C-4E44-988E-DAA059F72B9C}.Debug|x64.ActiveCfg = Debug|x64 + {ED2C50B3-7C2C-4E44-988E-DAA059F72B9C}.Debug|x64.Build.0 = Debug|x64 + {ED2C50B3-7C2C-4E44-988E-DAA059F72B9C}.Debug|x86.ActiveCfg = Debug|Win32 + {ED2C50B3-7C2C-4E44-988E-DAA059F72B9C}.Debug|x86.Build.0 = Debug|Win32 + {ED2C50B3-7C2C-4E44-988E-DAA059F72B9C}.Release|x64.ActiveCfg = Release|x64 + {ED2C50B3-7C2C-4E44-988E-DAA059F72B9C}.Release|x64.Build.0 = Release|x64 + {ED2C50B3-7C2C-4E44-988E-DAA059F72B9C}.Release|x86.ActiveCfg = Release|Win32 + {ED2C50B3-7C2C-4E44-988E-DAA059F72B9C}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {5E5FE02E-6BCE-4BAF-9948-C56476039C3C} + EndGlobalSection +EndGlobal diff --git a/external/detours/libs/detours.lib b/external/detours/libs/detours.lib deleted file mode 100644 index a813633cd317785749019913bcbb9a02e84d9b72..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 722364 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

1x|g_Rvhh?5tsia1&k@=>?wTemvv67eIO?smJ^ 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�v3~r#WnR+!-`~ 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 ZomLaZD_x~xD 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 diff --git a/external/detours/libs/detours.pdb b/external/detours/libs/detours.pdb deleted file mode 100644 index 7e39e0c0a50c0999862fec03635f61f6e6413e0c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 184320 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 diff --git a/external/detours/libs/detours_trace.lib b/external/detours/libs/detours_trace.lib deleted file mode 100644 index 1bda1142b22f91835a63997edb3b2873805da546..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 702732 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�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

TvhcSWkyX|QFUp_ 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

  • o;!klURm@#J)B*iQgtmVnJ>7zmv+uz9Lx? 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�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 M07*qoM6N<$g3>CWY5)KL literal 0 HcmV?d00001 diff --git a/r5dev/BuildVersion.rc b/r5dev/resource/r5dev.rc similarity index 77% rename from r5dev/BuildVersion.rc rename to r5dev/resource/r5dev.rc index de49eb04..bd627999 100644 --- a/r5dev/BuildVersion.rc +++ b/r5dev/resource/r5dev.rc @@ -1,6 +1,6 @@ // Microsoft Visual C++ generated resource script. // -#include "resource.h" +#include "core/resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// @@ -16,7 +16,8 @@ // English (United States) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE 9, 1 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// @@ -24,18 +25,18 @@ LANGUAGE 9, 1 // TEXTINCLUDE // -1 TEXTINCLUDE +1 TEXTINCLUDE BEGIN - "resource.h\0" + "core/resource.h\0" END -2 TEXTINCLUDE +2 TEXTINCLUDE BEGIN "#include ""winres.h""\r\n" "\0" END -3 TEXTINCLUDE +3 TEXTINCLUDE BEGIN "\r\n" "\0" @@ -43,6 +44,14 @@ END #endif // APSTUDIO_INVOKED + +///////////////////////////////////////////////////////////////////////////// +// +// PNG +// + +IDB_PNG1 PNG "resource\\png\\lockedserver.png" + #endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// @@ -57,3 +66,4 @@ END ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED + diff --git a/r5launcher/r5launcher.rc b/r5dev/resource/sdklauncher.rc similarity index 69% rename from r5launcher/r5launcher.rc rename to r5dev/resource/sdklauncher.rc index 170bbdd4..50453c05 100644 --- a/r5launcher/r5launcher.rc +++ b/r5dev/resource/sdklauncher.rc @@ -1,6 +1,9 @@ // Microsoft Visual C++ generated resource script. // -#include "resource.h" +#define USE_RES + +#ifdef USE_RES +#include "sdklauncher/sdklauncher_res.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// @@ -13,10 +16,10 @@ #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// -// German (Germany) resources +// English (United States) resources -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU) -LANGUAGE LANG_GERMAN, SUBLANG_GERMAN +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) #ifdef APSTUDIO_INVOKED @@ -27,7 +30,7 @@ LANGUAGE LANG_GERMAN, SUBLANG_GERMAN 1 TEXTINCLUDE BEGIN - "resource.h\0" + "sdklauncher/sdklauncher_res.h\0" END 2 TEXTINCLUDE @@ -52,9 +55,14 @@ END // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. -IDI_ICON1 ICON "r5reloaded.ico" -#endif // German (Germany) resources +#ifdef RELEASE +IDI_ICON1 ICON "resource\\ico\\sdklauncher_rel.ico" +#else +IDI_ICON1 ICON "resource\\ico\\sdklauncher_dbg.ico" +#endif + +#endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// @@ -64,8 +72,8 @@ IDI_ICON1 ICON "r5reloaded.ico" // // Generated from the TEXTINCLUDE 3 resource. // - +#endif // !APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED +#endif // !USE_RES diff --git a/r5dev/rtech/rtech.cpp b/r5dev/rtech/rtech.cpp new file mode 100644 index 00000000..a1d50172 --- /dev/null +++ b/r5dev/rtech/rtech.cpp @@ -0,0 +1,497 @@ +#include "core/stdafx.h" +#include "rtech/rtech.h" +#include "engine/sys_utils.h" + +/****************************************************************************** +------------------------------------------------------------------------------- +File : rtech.cpp +Date : 18:07:2021 +Author : Kawe Mazidjatari +Purpose: Implements the 'rtech_game' core utilities +------------------------------------------------------------------------------- +History: +- 18:07:2021 | 13:02 : Created by Kawe Mazidjatari +- 10:09:2021 | 18:22 : Implement 'StringToGuid' method +- 12:11:2021 | 14:41 : Add decompression method to ConCommand callback + +******************************************************************************/ + +//----------------------------------------------------------------------------- +// Purpose: calculate 'GUID' from input data +//----------------------------------------------------------------------------- +std::uint64_t __fastcall RTech::StringToGuid(const char* pData) +{ + std::uint32_t* v1; // r8 + std::uint64_t v2; // r10 + int v3; // er11 + std::uint32_t v4; // er9 + std::uint32_t i; // edx + std::uint64_t v6; // rcx + int v7; // er9 + int v8; // edx + int v9; // eax + std::uint32_t v10; // er8 + int v12; // ecx + std::uint32_t* a1 = (std::uint32_t*)pData; + + v1 = a1; + v2 = 0i64; + v3 = 0; + v4 = (*a1 - 45 * ((~(*a1 ^ 0x5C5C5C5Cu) >> 7) & (((*a1 ^ 0x5C5C5C5Cu) - 0x1010101) >> 7) & 0x1010101)) & 0xDFDFDFDF; + for (i = ~*a1 & (*a1 - 0x1010101) & 0x80808080; !i; i = v8 & 0x80808080) + { + v6 = v4; + v7 = v1[1]; + ++v1; + v3 += 4; + v2 = ((((std::uint64_t)(0xFB8C4D96501i64 * v6) >> 24) + 0x633D5F1 * v2) >> 61) ^ (((std::uint64_t)(0xFB8C4D96501i64 * v6) >> 24) + + 0x633D5F1 * v2); + v8 = ~v7 & (v7 - 0x1010101); + v4 = (v7 - 45 * ((~(v7 ^ 0x5C5C5C5Cu) >> 7) & (((v7 ^ 0x5C5C5C5Cu) - 0x1010101) >> 7) & 0x1010101)) & 0xDFDFDFDF; + } + v9 = -1; + v10 = (i & -(signed)i) - 1; + if (_BitScanReverse((unsigned long*)&v12, v10)) + { + v9 = v12; + } + return 0x633D5F1 * v2 + ((0xFB8C4D96501i64 * (std::uint64_t)(v4 & v10)) >> 24) - 0xAE502812AA7333i64 * (std::uint32_t)(v3 + v9 / 8); +} + +//----------------------------------------------------------------------------- +// Purpose: calculate 'decompressed' size and commit parameters +//----------------------------------------------------------------------------- +std::uint32_t __fastcall RTech::DecompressedSize(std::int64_t param_buf, std::uint8_t* file_buf, std::int64_t file_size, std::int64_t off_no_header, std::int64_t header_size) +{ + std::int64_t v8; // r9 + std::uint64_t v9; // r11 + char v10; // r8 + int v11; // er8 + std::int64_t v12; // rbx + unsigned int v13; // ebp + std::uint64_t v14; // rbx + std::int64_t v15; // rax + unsigned int v16; // er9 + std::uint64_t v17; // r12 + std::uint64_t v18; // r11 + std::uint64_t v19; // r10 + std::uint64_t v20; // rax + int v21; // ebp + std::uint64_t v22; // r10 + unsigned int v23; // er9 + std::int64_t v24; // rax + std::int64_t v25; // rsi + std::int64_t v26; // rdx + std::int64_t v28; // rdx + std::int64_t v29; // [rsp+48h] [rbp+18h] + std::int64_t result; // rax + + v29 = 0xFFFFFFi64; + *(std::uint64_t*)param_buf = (std::uint64_t)file_buf; + *(std::uint64_t*)(param_buf + 32) = off_no_header + file_size; + *(std::uint64_t*)(param_buf + 8) = 0i64; + *(std::uint64_t*)(param_buf + 24) = 0i64; + *(std::uint32_t*)(param_buf + 68) = 0; + *(std::uint64_t*)(param_buf + 16) = -1i64; + v8 = off_no_header + header_size + 8; + v9 = *(std::uint64_t*)((0xFFFFFFi64 & (off_no_header + header_size)) + file_buf); + *(std::uint64_t*)(param_buf + 80) = header_size; + *(std::uint64_t*)(param_buf + 72) = v8; + v10 = v9; + v9 >>= 6; + v11 = v10 & 0x3F; + *(std::uint64_t*)(param_buf + 40) = (1i64 << v11) | v9 & ((1i64 << v11) - 1); + v12 = *(std::uint64_t*)((0xFFFFFFi64 & v8) + file_buf) << (64 - ((std::uint8_t)v11 + 6)); + *(std::uint64_t*)(param_buf + 72) = v8 + ((std::uint8_t)(unsigned int)(v11 + 6) >> 3); + v13 = ((v11 + 6) & 7) + 13; + v14 = (0xFFFFFFFFFFFFFFFFui64 >> ((v11 + 6) & 7)) & ((v9 >> v11) | v12); + v15 = v29 & *(std::uint64_t*)(param_buf + 72); + v16 = (((std::uint8_t)v14 - 1) & 0x3F) + 1; + v17 = 0xFFFFFFFFFFFFFFFFui64 >> (64 - (std::uint8_t)v16); + *(uint64_t*)(param_buf + 48) = v17; + v18 = 0xFFFFFFFFFFFFFFFFui64 >> (64 - ((((v14 >> 6) - 1) & 0x3F) + 1)); + *(uint64_t*)(param_buf + 56) = v18; + v19 = (v14 >> 13) | (*(std::uint64_t*)(v15 + file_buf) << (64 - (std::uint8_t)v13)); + v20 = v13; + v21 = v13 & 7; + *(std::uint64_t*)(param_buf + 72) += v20 >> 3; + v22 = (0xFFFFFFFFFFFFFFFFui64 >> v21) & v19; + if (v17 == -1i64) + { + *(std::uint32_t*)(param_buf + 64) = 0; + *(std::uint64_t*)(param_buf + 88) = file_size; + } + else + { + v23 = v16 >> 3; + v24 = v29 & *(std::uint64_t*)(param_buf + 72); + *(std::uint32_t*)(param_buf + 64) = v23 + 1; + v25 = *(std::uint64_t*)(v24 + file_buf) & ((1i64 << (8 * ((std::uint8_t)v23 + 1))) - 1); + *(std::uint64_t*)(param_buf + 72) += v23 + 1; + *(std::uint64_t*)(param_buf + 88) = v25; + } + *(std::uint64_t*)(param_buf + 88) += off_no_header; + v26 = *(std::uint64_t*)(param_buf + 88); + *(std::uint64_t*)(param_buf + 96) = v22; + *(std::uint32_t*)(param_buf + 104) = v21; + *(std::uint64_t*)(param_buf + 112) = v17 + off_no_header - 6; + result = *(std::uint64_t*)(param_buf + 40); + *(std::uint32_t*)(param_buf + 108) = 0; + *(std::uint64_t*)(param_buf + 120) = v26; + *(std::uint64_t*)(param_buf + 128) = result; + if ((((std::uint8_t)(v14 >> 6) - 1) & 0x3F) != -1i64 && result - 1 > v18) + { + v28 = v26 - *(unsigned int*)(param_buf + 64); + *(std::uint64_t*)(param_buf + 128) = v18 + 1; + *(std::uint64_t*)(param_buf + 120) = v28; + } + return result; +} + +//----------------------------------------------------------------------------- +// Purpose: decompress input data +//----------------------------------------------------------------------------- +std::uint8_t __fastcall RTech::Decompress(std::int64_t* param_buffer, std::uint64_t file_size, std::uint64_t buf_size) +{ + char result; // al + std::int64_t v5; // r15 + std::int64_t v6; // r11 + std::uint32_t v7; // ebp + std::uint64_t v8; // rsi + std::uint64_t v9; // rdi + std::uint64_t v10; // r12 + std::int64_t v11; // r13 + std::uint32_t v12; // ecx + std::uint64_t v13; // rsi + std::uint64_t i; // rax + std::uint64_t v15; // r8 + std::int64_t v16; // r9 + int v17; // ecx + std::uint64_t v18; // rax + std::uint64_t v19; // rsi + std::int64_t v20; // r14 + int v21; // ecx + std::uint64_t v22; // r11 + int v23; // edx + std::int64_t v24; // rax + int v25; // er8 + std::uint32_t v26; // er13 + std::int64_t v27; // r10 + std::int64_t v28; // rax + std::uint64_t* v29; // r10 + std::uint64_t v30; // r9 + std::uint64_t v31; // r10 + std::uint64_t v32; // r8 + std::uint64_t v33; // rax + std::uint64_t v34; // rax + std::uint64_t v35; // rax + std::uint64_t v36; // rcx + std::uint64_t v37; // rdx + std::uint64_t v38; // r14 + std::uint64_t v39; // r11 + char v40; // cl + std::uint64_t v41; // rsi + std::uint64_t v42; // rcx + std::uint64_t v43; // r8 + int v44; // er11 + std::uint8_t v45; // r9 + std::uint64_t v46; // rcx + std::uint64_t v47; // rcx + std::uint64_t v48; // r9 + std::uint64_t l; // r8 + std::uint32_t v50; // er9 + std::uint64_t v51; // r8 + std::uint64_t v52; // rdx + std::uint64_t k; // r8 + char* v54; // r10 + std::uint64_t v55; // rdx + std::uint32_t v56; // er14 + std::uint64_t* v57; // rdx + std::uint64_t* v58; // r8 + char v59; // al + std::uint64_t v60; // rsi + std::uint64_t v61; // rax + std::uint64_t v62; // r9 + int v63; // er10 + std::uint8_t v64; // cl + std::uint64_t v65; // rax + std::uint32_t v66; // er14 + std::uint32_t j; // ecx + std::uint64_t v68; // rax + std::uint64_t v69; // rcx + std::uint64_t v70; // [rsp+0h] [rbp-58h] + int v71; // [rsp+60h] [rbp+8h] + std::uint64_t v74; // [rsp+78h] [rbp+20h] + + if (file_size < param_buffer[11]) + { + return 0; + } + v5 = param_buffer[10]; + if (buf_size < param_buffer[7] + (v5 & (std::uint64_t)~param_buffer[7]) + 1 && buf_size < param_buffer[5]) + { + return 0; + } + v6 = param_buffer[1]; + v7 = *((std::uint32_t*)param_buffer + 26); + v8 = param_buffer[12]; + v9 = param_buffer[9]; + v10 = param_buffer[14]; + v11 = *param_buffer; + if (param_buffer[15] < v10) + { + v10 = param_buffer[15]; + } + v12 = *((std::uint32_t*)param_buffer + 27); + v74 = v11; + v70 = v6; + v71 = v12; + if (!v7) + { + goto LABEL_11; + } + v13 = (*(std::uint64_t*)((v9 & param_buffer[2]) + v11) << (64 - (std::uint8_t)v7)) | v8; + for (i = v7; ; i = v7) + { + v7 &= 7u; + v9 += i >> 3; + v12 = v71; + v8 = (0xFFFFFFFFFFFFFFFFui64 >> v7) & v13; + LABEL_11: + v15 = (std::uint64_t)v12 << 8; + v16 = v12; + v17 = *((std::uint8_t*)&LUT_0 + (std::uint8_t)v8 + v15 + 512); + v18 = (std::uint8_t)v8 + v15; + v7 += v17; + v19 = v8 >> v17; + v20 = (std::uint32_t)*((char*)&LUT_0 + v18); + if (*((char*)&LUT_0 + v18) < 0) + { + v56 = -(int)v20; + v57 = (std::uint64_t*)(v11 + (v9 & param_buffer[2])); + v71 = 1; + v58 = (std::uint64_t*)(v6 + (v5 & param_buffer[3])); + if (v56 == *((std::uint8_t*)&LUT_0 + v16 + 1248)) + { + if ((~v9 & param_buffer[6]) < 0xF || (param_buffer[7] & (std::uint64_t)~v5) < 0xF || (std::uint64_t)(param_buffer[5] - v5) < 0x10) + { + v56 = 1; + } + v59 = v19; + v60 = v19 >> 3; + v61 = v59 & 7; + v62 = v60; + if (v61) + { + v63 = *((std::uint8_t*)&LUT_0 + v61 + 1232); + v64 = *((std::uint8_t*)&LUT_0 + v61 + 1240); + } + else + { + v62 = v60 >> 4; + v65 = v60 & 0xF; + v7 += 4; + v63 = *((std::uint32_t*)&LUT_0 + v65 + 288); + v64 = *((std::uint8_t*)&LUT_0 + v65 + 1216); + } + v7 += v64 + 3; + v19 = v62 >> v64; + v66 = v63 + (v62 & ((1 << v64) - 1)) + v56; + for (j = v66 >> 3; j; --j) + { + v68 = *v57++; + *v58++ = v68; + } + if ((v66 & 4) != 0) + { + *(std::uint32_t*)v58 = *(std::uint32_t*)v57; + v58 = (std::uint64_t*)((char*)v58 + 4); + v57 = (std::uint64_t*)((char*)v57 + 4); + } + if ((v66 & 2) != 0) + { + *(std::uint16_t*)v58 = *(std::uint16_t*)v57; + v58 = (std::uint64_t*)((char*)v58 + 2); + v57 = (std::uint64_t*)((char*)v57 + 2); + } + if ((v66 & 1) != 0) + { + *(std::uint8_t*)v58 = *(std::uint8_t*)v57; + } + v9 += v66; + v5 += v66; + } + else + { + *v58 = *v57; + v58[1] = v57[1]; + v9 += v56; + v5 += v56; + } + } + else + { + v21 = v19 & 0xF; + v71 = 0; + v22 = ((std::uint64_t)(std::uint32_t)v19 >> (((std::uint32_t)(v21 - 31) >> 3) & 6)) & 0x3F; + v23 = 1 << (v21 + ((v19 >> 4) & ((24 * (((std::uint32_t)(v21 - 31) >> 3) & 2)) >> 4))); + v7 += (((std::uint32_t)(v21 - 31) >> 3) & 6) + *((std::uint8_t*)&LUT_0 + v22 + 1088) + v21 + ((v19 >> 4) & ((24 * (((std::uint32_t)(v21 - 31) >> 3) & 2)) >> 4)); + v24 = param_buffer[3]; + v25 = 16 * (v23 + ((v23 - 1) & (v19 >> ((((std::uint32_t)(v21 - 31) >> 3) & 6) + *((std::uint8_t*)&LUT_0 + v22 + 1088))))); + v19 >>= (((std::uint32_t)(v21 - 31) >> 3) & 6) + *((std::uint8_t*)&LUT_0 + v22 + 1088) + v21 + ((v19 >> 4) & ((24 * (((std::uint32_t)(v21 - 31) >> 3) & 2)) >> 4)); + v26 = v25 + *((std::uint8_t*)&LUT_0 + v22 + 1024) - 16; + v27 = v24 & (v5 - v26); + v28 = v70 + (v5 & v24); + v29 = (std::uint64_t*)(v70 + v27); + if ((std::uint32_t)v20 == 17) + { + v40 = v19; + v41 = v19 >> 3; + v42 = v40 & 7; + v43 = v41; + if (v42) + { + v44 = *((std::uint8_t*)&LUT_0 + v42 + 1232); + v45 = *((std::uint8_t*)&LUT_0 + v42 + 1240); + } + else + { + v7 += 4; + v46 = v41 & 0xF; + v43 = v41 >> 4; + v44 = *((std::uint32_t*)&LUT_0 + v46 + 288); + v45 = *((std::uint8_t*)&LUT_0 + v46 + 1216); + if (v74 && v7 + v45 >= 0x3D) + { + v47 = v9++ & param_buffer[2]; + v43 |= (std::uint64_t)*(std::uint8_t*)(v47 + v74) << (61 - (std::uint8_t)v7); + v7 -= 8; + } + } + v7 += v45 + 3; + v19 = v43 >> v45; + v48 = ((std::uint32_t)v43 & ((1 << v45) - 1)) + v44 + 17; + v5 += v48; + if (v26 < 8) + { + v50 = v48 - 13; + v5 -= 13i64; + if (v26 == 1) + { + v51 = *(std::uint8_t*)v29; + v52 = 0i64; + for (k = 0x101010101010101i64 * v51; (std::uint32_t)v52 < v50; v52 = (std::uint32_t)(v52 + 8)) + { + *(std::uint64_t*)(v52 + v28) = k; + } + } + else + { + if (v50) + { + v54 = (char*)v29 - v28; + v55 = v50; + do + { + *(std::uint8_t*)v28 = v54[v28]; + ++v28; + --v55; + } while (v55); + } + } + } + else + { + for (l = 0i64; (std::uint32_t)l < (std::uint32_t)v48; l = (std::uint32_t)(l + 8)) + { + *(std::uint64_t*)(l + v28) = *(std::uint64_t*)((char*)v29 + l); + } + } + } + else + { + v5 += v20; + *(std::uint64_t*)v28 = *v29; + *(std::uint64_t*)(v28 + 8) = v29[1]; + } + v11 = v74; + } + if (v9 >= v10) + { + break; + } + LABEL_29: + v6 = v70; + v13 = (*(std::uint64_t*)((v9 & param_buffer[2]) + v11) << (64 - (std::uint8_t)v7)) | v19; + } + if (v5 != param_buffer[16]) + { + goto LABEL_25; + } + v30 = param_buffer[5]; + if (v5 == v30) + { + result = 1; + goto LABEL_69; + } + v31 = param_buffer[6]; + v32 = *((std::uint32_t*)param_buffer + 16); + v33 = v31 & -(std::int64_t)v9; + v19 >>= 1; + ++v7; + if (v32 > v33) + { + v9 += v33; + v34 = param_buffer[14]; + if (v9 > v34) + { + param_buffer[14] = v31 + v34 + 1; + } + } + v35 = v9 & param_buffer[2]; + v9 += v32; + v36 = v5 + param_buffer[7] + 1; + v37 = *(std::uint32_t*)(v35 + v11) & ((1i64 << (8 * (std::uint8_t)v32)) - 1); + v38 = v37 + param_buffer[11]; + v39 = v37 + param_buffer[15]; + param_buffer[11] = v38; + param_buffer[15] = v39; + if (v36 >= v30) + { + v36 = v30; + param_buffer[15] = v32 + v39; + } + param_buffer[16] = v36; + if (file_size >= v38 && buf_size >= v36) + { + LABEL_25: + v10 = param_buffer[14]; + if (v9 >= v10) + { + v9 = ~param_buffer[6] & (v9 + 7); + v10 += param_buffer[6] + 1; + param_buffer[14] = v10; + } + if (param_buffer[15] < v10) + { + v10 = param_buffer[15]; + } + goto LABEL_29; + } + v69 = param_buffer[14]; + if (v9 >= v69) + { + v9 = ~v31 & (v9 + 7); + param_buffer[14] = v69 + v31 + 1; + } + *((std::uint32_t*)param_buffer + 27) = v71; + result = 0; + param_buffer[12] = v19; + *((std::uint32_t*)param_buffer + 26) = v7; +LABEL_69: + param_buffer[10] = v5; + param_buffer[9] = v9; + return result; +} +/////////////////////////////////////////////////////////////////////////////// +RTech* g_pRtech = new RTech(); diff --git a/r5dev/rtech/rtech.h b/r5dev/rtech/rtech.h new file mode 100644 index 00000000..534223a8 --- /dev/null +++ b/r5dev/rtech/rtech.h @@ -0,0 +1,176 @@ +#pragma once + +#define PAK_HEADER_SIZE 0x80 +#define PAK_PARAM_SIZE 0xB0 +#define DCMP_BUF_SIZE 0x400000 + +namespace +{ + /*unk_141313180*/ + const unsigned char LUT_0[0x720] = + { + 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF9, 0x04, 0xFD, 0xFC, 0x07, 0x04, 0x05, 0xFF, 0xF4, + 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF6, 0x04, 0xFD, 0xFC, 0xFB, 0x04, 0x06, 0xFF, 0x0B, + 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF8, 0x04, 0xFD, 0xFC, 0x0C, 0x04, 0x05, 0xFF, 0xF7, + 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF5, 0x04, 0xFD, 0xFC, 0xFA, 0x04, 0x06, 0xFF, 0xF3, + 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF9, 0x04, 0xFD, 0xFC, 0x07, 0x04, 0x05, 0xFF, 0xF4, + 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF6, 0x04, 0xFD, 0xFC, 0xFB, 0x04, 0x06, 0xFF, 0x0E, + 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF8, 0x04, 0xFD, 0xFC, 0x0C, 0x04, 0x05, 0xFF, 0x09, + 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF5, 0x04, 0xFD, 0xFC, 0xFA, 0x04, 0x06, 0xFF, 0xF1, + 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF9, 0x04, 0xFD, 0xFC, 0x07, 0x04, 0x05, 0xFF, 0xF4, + 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF6, 0x04, 0xFD, 0xFC, 0xFB, 0x04, 0x06, 0xFF, 0x0D, + 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF8, 0x04, 0xFD, 0xFC, 0x0C, 0x04, 0x05, 0xFF, 0xF7, + 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF5, 0x04, 0xFD, 0xFC, 0xFA, 0x04, 0x06, 0xFF, 0xF2, + 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF9, 0x04, 0xFD, 0xFC, 0x07, 0x04, 0x05, 0xFF, 0xF4, + 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF6, 0x04, 0xFD, 0xFC, 0xFB, 0x04, 0x06, 0xFF, 0x0F, + 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF8, 0x04, 0xFD, 0xFC, 0x0C, 0x04, 0x05, 0xFF, 0x0A, + 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF5, 0x04, 0xFD, 0xFC, 0xFA, 0x04, 0x06, 0xFF, 0xF0, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x11, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0C, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x09, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0E, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x11, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0B, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0A, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x10, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x11, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0C, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x09, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0F, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x11, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0D, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0A, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0xFF, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x06, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x07, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x06, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x06, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x07, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x06, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x06, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x07, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x07, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x08, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x06, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x08, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x07, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x08, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x06, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x07, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x07, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x08, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x06, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x08, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x07, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x08, + 0x00, 0x08, 0x00, 0x04, 0x00, 0x08, 0x00, 0x06, 0x00, 0x08, 0x00, 0x01, 0x00, 0x08, 0x00, 0x0B, + 0x00, 0x08, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x09, 0x00, 0x08, 0x00, 0x03, 0x00, 0x08, 0x00, 0x0E, + 0x00, 0x08, 0x00, 0x04, 0x00, 0x08, 0x00, 0x07, 0x00, 0x08, 0x00, 0x02, 0x00, 0x08, 0x00, 0x0D, + 0x00, 0x08, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x05, 0x00, 0x08, 0x00, 0x0F, + 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, + 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, + 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, + 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, + 0x4A, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, + 0xCA, 0x00, 0x00, 0x00, 0xEA, 0x00, 0x00, 0x00, 0x0A, 0x01, 0x00, 0x00, 0x2A, 0x01, 0x00, 0x00, + 0x4A, 0x01, 0x00, 0x00, 0x6A, 0x01, 0x00, 0x00, 0x8A, 0x01, 0x00, 0x00, 0xAA, 0x01, 0x00, 0x00, + 0xAA, 0x03, 0x00, 0x00, 0xAA, 0x05, 0x00, 0x00, 0xAA, 0x25, 0x00, 0x00, 0xAA, 0x25, 0x02, 0x00, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x09, 0x09, 0x0D, 0x11, 0x15, + 0x00, 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x2A, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x05, 0x05, + 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xBF, + 0xA8, 0xAA, 0x2A, 0xBE, 0xA8, 0xAA, 0x2A, 0xBE, 0xA8, 0xAA, 0x2A, 0xBE, 0xA8, 0xAA, 0x2A, 0xBE, + 0xD2, 0x85, 0x08, 0x3C, 0xD2, 0x85, 0x08, 0x3C, 0xD2, 0x85, 0x08, 0x3C, 0xD2, 0x85, 0x08, 0x3C, + 0x83, 0xF9, 0x22, 0x3F, 0x83, 0xF9, 0x22, 0x3F, 0x83, 0xF9, 0x22, 0x3F, 0x83, 0xF9, 0x22, 0x3F, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x10, 0xC9, 0x3F, 0x00, 0x10, 0xC9, 0x3F, 0x00, 0x10, 0xC9, 0x3F, 0x00, 0x10, 0xC9, 0x3F, + 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3F, + 0x02, 0x61, 0x4D, 0xB9, 0x02, 0x61, 0x4D, 0xB9, 0x02, 0x61, 0x4D, 0xB9, 0x02, 0x61, 0x4D, 0xB9, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xC2, 0x14, 0xCF, 0x37, 0xC2, 0x14, 0xCF, 0x37, 0xC2, 0x14, 0xCF, 0x37, 0xC2, 0x14, 0xCF, 0x37, + 0x9E, 0x4B, 0x6F, 0xB0, 0x9E, 0x4B, 0x6F, 0xB0, 0x9E, 0x4B, 0x6F, 0xB0, 0x9E, 0x4B, 0x6F, 0xB0, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xF1, 0x1D, 0xC1, 0xF6, 0x7F, 0x00, 0x00, + 0x22, 0x0B, 0xB6, 0xBA, 0x22, 0x0B, 0xB6, 0xBA, 0x22, 0x0B, 0xB6, 0xBA, 0x22, 0x0B, 0xB6, 0xBA, + 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3F, + 0x02, 0x61, 0x4D, 0xB9, 0x02, 0x61, 0x4D, 0xB9, 0x02, 0x61, 0x4D, 0xB9, 0x02, 0x61, 0x4D, 0xB9, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xC2, 0x14, 0xCF, 0x37, 0xC2, 0x14, 0xCF, 0x37, 0xC2, 0x14, 0xCF, 0x37, 0xC2, 0x14, 0xCF, 0x37, + 0x9E, 0x4B, 0x6F, 0xB0, 0x9E, 0x4B, 0x6F, 0xB0, 0x9E, 0x4B, 0x6F, 0xB0, 0x9E, 0x4B, 0x6F, 0xB0, + 0x22, 0x0B, 0xB6, 0xBA, 0x22, 0x0B, 0xB6, 0xBA, 0x22, 0x0B, 0xB6, 0xBA, 0x22, 0x0B, 0xB6, 0xBA, + 0x00, 0x70, 0x95, 0xB6, 0x00, 0x70, 0x95, 0xB6, 0x00, 0x70, 0x95, 0xB6, 0x00, 0x70, 0x95, 0xB6, + 0xA9, 0xAA, 0x2A, 0x3D, 0xA9, 0xAA, 0x2A, 0x3D, 0xA9, 0xAA, 0x2A, 0x3D, 0xA9, 0xAA, 0x2A, 0x3D, + 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x80, 0x3F, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xBF, + 0xA8, 0xAA, 0x2A, 0xBE, 0xA8, 0xAA, 0x2A, 0xBE, 0xA8, 0xAA, 0x2A, 0xBE, 0xA8, 0xAA, 0x2A, 0xBE, + 0xD2, 0x85, 0x08, 0x3C, 0xD2, 0x85, 0x08, 0x3C, 0xD2, 0x85, 0x08, 0x3C, 0xD2, 0x85, 0x08, 0x3C, + 0x83, 0xF9, 0x22, 0x3F, 0x83, 0xF9, 0x22, 0x3F, 0x83, 0xF9, 0x22, 0x3F, 0x83, 0xF9, 0x22, 0x3F, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x10, 0xC9, 0x3F, 0x00, 0x10, 0xC9, 0x3F, 0x00, 0x10, 0xC9, 0x3F, 0x00, 0x10, 0xC9, 0x3F, + 0x4C, 0x39, 0x56, 0x75, 0x42, 0x52, 0x65, 0x75, 0x70, 0x35, 0x31, 0x77, 0x4C, 0x51, 0x64, 0x61, + }; +} + +struct rpak_h +{ + std::uint32_t m_nMagic; // 'RPak' + std::uint16_t m_nVersion; // R2 = '7' R5 = '8' + std::uint8_t m_nFlags[0x2]; // + std::uint8_t m_nHash[0x10]; // + std::uint64_t m_nSizeDisk; // Compressed size + std::uint64_t m_nEmbeddedStarpakOffset; // + std::uint8_t unk0[0x8]; // + std::uint64_t m_nSizeMemory; // Decompressed size + std::uint64_t m_nEmbeddedStarpakSize; // + std::uint8_t unk1[0x8]; // + + std::uint16_t m_nStarpakReferenceSize; // + std::uint16_t m_nStarpakOptReferenceSize; // + std::uint16_t m_nVirtualSegmentCount; // * 0x10 + std::uint16_t m_nVirtualSegmentBlockCount; // * 0xC + + std::uint32_t m_nPatchIndex; // + + std::uint32_t m_nUnknownThirdBlockCount; // + std::uint32_t m_nAssetEntryCount; // File entry count + std::uint32_t m_nUnknownFifthBlockCount; // + std::uint32_t m_nUnknownSixedBlockCount; // + + std::uint8_t unk2[0x1C]; // +}; + +namespace +{ + /* ==== RTECH =========================================================================================================================================================== */ + //DWORD64 p_RTech_Decompress = FindPatternV2("r5apex.exe", (const unsigned char*)"\x4C\x89\x44\x24\x18\x48\x89\x54\x24\x10\x53\x48\x83\xEC\x50\x48", "xxxxxxxxxxxxxxxx"); + //char (*RTech_Decompress)(int64_t* parameter, std::uint64_t input, std::uint64_t output) = (char (*)(std::int64_t*, std::uint64_t, std::uint64_t))p_RTech_Decompress; /*4C 89 44 24 18 48 89 54 24 10 53 48 83 EC 50 48*/ + + //DWORD64 p_RTech_DecompressedSize = FindPatternV2("r5apex.exe", (const unsigned char*)"\x48\x89\x5C\x24\x08\x48\x89\x6C\x24\x18\x48\x89\x74\x24\x20\x48\x89\x54\x24\x10\x57\x41\x54\x41\x55\x41\x56\x41\x57\x4C\x8B\x74", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + //std::int64_t (*RTech_DecompressedSize)(std::int64_t parameter, std::uint8_t* input, std::int64_t magic, std::int64_t a4, std::int64_t a5) = (std::int64_t (*)(std::int64_t, std::uint8_t*, std::int64_t, std::int64_t, std::int64_t))p_RTech_DecompressedSize; /*48 89 5C 24 08 48 89 6C 24 18 48 89 74 24 20 48 89 54 24 10 57 41 54 41 55 41 56 41 57 4C 8B 74*/ +} + +class RTech +{ +public: + std::uint64_t __fastcall StringToGuid(const char* pData); + std::uint8_t __fastcall Decompress(std::int64_t* params, std::uint64_t file_size, std::uint64_t buffer_size); + std::uint32_t __fastcall DecompressedSize(std::int64_t param_buf, std::uint8_t* file_buf, std::int64_t file_size, std::int64_t off_no_header, std::int64_t header_size); +}; + +/////////////////////////////////////////////////////////////////////////////// +extern RTech* g_pRtech; diff --git a/r5dev/rtech/stryder.cpp b/r5dev/rtech/stryder.cpp new file mode 100644 index 00000000..d8101c70 --- /dev/null +++ b/r5dev/rtech/stryder.cpp @@ -0,0 +1,3 @@ +#include "core/stdafx.h" +#include "rtech/stryder.h" +//TODO diff --git a/r5dev/rtech/stryder.h b/r5dev/rtech/stryder.h new file mode 100644 index 00000000..ad1e7400 --- /dev/null +++ b/r5dev/rtech/stryder.h @@ -0,0 +1,27 @@ +#pragma once +#include "tier0/basetypes.h" + +namespace +{ + /* ==== STRYDER ================================================================================================================================================ */ +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) + ADDRESS p_Stryder_StitchRequest = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x8B\xC4\x53\x57\x41\x56\x48\x81\xEC\x20", "xxxxxxxxxxx"); + void* (*Stryder_StitchRequest)(void* a1) = (void* (*)(void*))p_Stryder_StitchRequest.GetPtr(); /*48 8B C4 53 57 41 56 48 81 EC 20*/ +#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) + ADDRESS p_Stryder_StitchRequest = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x08\x48\x89\x6C\x24\x10\x48\x89\x74\x24\x18\x57\x48\x83\xEC\x20\x48\x8B\xF9\xE8\xB4", "xxxxxxxxxxxxxxxxxxxxxxxxx"); + void* (*Stryder_StitchRequest)(void* a1) = (void* (*)(void*))p_Stryder_StitchRequest.GetPtr(); /*48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 48 83 EC 20 48 8B F9 E8 B4*/ +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +class HStryder : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: Stryder_StitchRequest : 0x" << std::hex << std::uppercase << p_Stryder_StitchRequest.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HStryder); \ No newline at end of file diff --git a/r5launcher/r5launcher.vcxproj b/r5dev/sdklauncher.vcxproj similarity index 79% rename from r5launcher/r5launcher.vcxproj rename to r5dev/sdklauncher.vcxproj index 197a31a7..bbd178d4 100644 --- a/r5launcher/r5launcher.vcxproj +++ b/r5dev/sdklauncher.vcxproj @@ -22,36 +22,38 @@ 16.0 Win32Proj {18f8c75e-3844-4aa6-ab93-980a08253519} - r5launcher + sdklauncher 10.0 + sdklauncher Application true - v142 + v143 Unicode Application false - v142 + v143 true Unicode Application true - v142 + v143 MultiByte + Static Application false - v142 + v143 true MultiByte - false + Static @@ -66,11 +68,10 @@ - + - @@ -81,19 +82,19 @@ true - $(SolutionDir)external\detours\include;$(SolutionDir)external\spdlog\include;$(IncludePath) - $(SolutionDir)external\detours\libs;$(LibraryPath) - $(SolutionDir)build\$(ProjectName)\$(Configuration)\ + $(SolutionDir)r5dev\;$(IncludePath); + $(SolutionDir)r5dev\thirdparty\detours\libs;$(LibraryPath); + r5reloaded $(SolutionDir)bin\$(Configuration)\ - launcher + $(SolutionDir)build\$(ProjectName)\$(Configuration)\ false - $(SolutionDir)external\detours\include;$(SolutionDir)external\spdlog\include;$(IncludePath) - $(SolutionDir)external\detours\libs;$(LibraryPath) + $(SolutionDir)r5dev\;$(IncludePath); + $(SolutionDir)r5dev\thirdparty\detours\libs;$(LibraryPath); + r5reloaded $(SolutionDir)bin\$(Configuration)\ $(SolutionDir)build\$(ProjectName)\$(Configuration)\ - launcher @@ -129,11 +130,12 @@ true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true - stdcpp17 - stdc17 + + Use - pch.h - MultiThreadedDebug + stdcpp17 + core\stdafx.h + /D SDKLAUNCHER %(AdditionalOptions) Console @@ -141,8 +143,7 @@ detours.lib;%(AdditionalDependencies) - - + del "..\..\..\bin\r5reloaded.exe" && copy /Y "$(TargetPath)" "..\..\..\bin\$(TargetFileName)" @@ -153,14 +154,20 @@ true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true - stdcpp17 - stdc17 - AnySuitable + + Speed + true true true + true + + + AnySuitable Use - pch.h + stdcpp17 + core\stdafx.h + /D SDKLAUNCHER %(AdditionalOptions) Console @@ -170,27 +177,27 @@ detours.lib;%(AdditionalDependencies) - del "$(SolutionDir)bin\$(Configuration)\r5reloaded.exe" -rename "$(TargetPath)" "r5reloaded.exe" + del "..\..\..\r5reloaded.exe" && copy /Y "$(TargetPath)" "..\..\..\$(TargetFileName)" - - + Create Create + - - - + - + + - + + + diff --git a/r5launcher/r5launcher.vcxproj.filters b/r5dev/sdklauncher.vcxproj.filters similarity index 74% rename from r5launcher/r5launcher.vcxproj.filters rename to r5dev/sdklauncher.vcxproj.filters index 38adabbd..5a7f2de0 100644 --- a/r5launcher/r5launcher.vcxproj.filters +++ b/r5dev/sdklauncher.vcxproj.filters @@ -15,31 +15,34 @@ - + Source Files - + Source Files - - Header Files - - - Header Files - - - Header Files - - - - + Resource Files - + + Header Files + + + Header Files + + + Header Files + + + + + Resource Files + + Resource Files diff --git a/r5launcher/main.cpp b/r5dev/sdklauncher/sdklauncher.cpp similarity index 93% rename from r5launcher/main.cpp rename to r5dev/sdklauncher/sdklauncher.cpp index 0677f381..7e36fb69 100644 --- a/r5launcher/main.cpp +++ b/r5dev/sdklauncher/sdklauncher.cpp @@ -1,5 +1,5 @@ -#include "pch.h" -#include "main.h" +#include "core/stdafx.h" +#include "sdklauncher.h" //----------------------------------------------------------------------------- // Print the error message to the console if any. @@ -13,7 +13,7 @@ void PrintLastError() size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); - spdlog::error("{}", messageBuffer); + spdlog::error("{}\n", messageBuffer); LocalFree(messageBuffer); } } @@ -56,7 +56,7 @@ bool LaunchR5Apex(LAUNCHMODE lMode, LAUNCHSTATE lState) } cfgFile.close(); // Close cfg file. - WorkerDll = currentDirectory + "\\r5dev.dll"; // Get path to worker dll. + WorkerDll = currentDirectory + "\\bin\\r5apexvtxd.dll"; // Get path to worker dll. GameDirectory = currentDirectory + "\\r5apex.exe"; // Get path to game executeable. StartupCommandLine = currentDirectory + "\\r5apex.exe " + CommandLineArguments; // Setup startup command line string. @@ -81,7 +81,7 @@ bool LaunchR5Apex(LAUNCHMODE lMode, LAUNCHSTATE lState) } cfgFile.close(); // Close cfg file. - WorkerDll = currentDirectory + "\\r5detours.dll"; // Get path to worker dll. + WorkerDll = currentDirectory + "\\r5apexsdkd64.dll"; // Get path to worker dll. GameDirectory = currentDirectory + "\\r5apex.exe"; // Get path to game executeable. StartupCommandLine = currentDirectory + "\\r5apex.exe " + CommandLineArguments; // Setup startup command line string. @@ -95,8 +95,8 @@ bool LaunchR5Apex(LAUNCHMODE lMode, LAUNCHSTATE lState) if (cfgFile.good() && cfgFile) // Does the cfg file exist? { std::stringstream ss; - ss << cfgFile.rdbuf(); // Read ifstream buffer into stringstream. - CommandLineArguments = ss.str(); // Get all the contents of the cfg file. + ss << cfgFile.rdbuf(); // Read ifstream buffer into stringstream. + CommandLineArguments = ss.str(); // Get all the contents of the cfg file. } else { @@ -219,9 +219,9 @@ int main(int argc, char* argv[], char* envp[]) spdlog::warn("All FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY ConVar's/ConCommand's will be enabled.\n"); spdlog::warn("Connected clients will be able to set and execute anything flagged FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY.\n"); std::cout << "--------------------------------------------------------------------------------------------------------" << std::endl; - spdlog::warn("Use DEBUG [1] for development and debugging purposes.\n"); - spdlog::warn("Use RELEASE [2] for normal playing and server hosting.\n"); - spdlog::warn("Use DEDICATED [3] for running a dedicated server.\n"); + spdlog::warn("Use DEBUG [1] for research and development purposes.\n"); + spdlog::warn("Use RELEASE [2] for playing and server hosting purposes.\n"); + spdlog::warn("Use DEDICATED [3] for running and hosting a dedicated server.\n"); std::cout << "--------------------------------------------------------------------------------------------------------" << std::endl; spdlog::info("Enter 1 for DEBUG. Enter 2 for RELEASE. Enter 3 for DEDICATED: "); @@ -266,9 +266,8 @@ int main(int argc, char* argv[], char* envp[]) return EXIT_FAILURE; } } - spdlog::error("R5Reloaded requires numerical input to launch.\n"); Sleep(5000); return EXIT_FAILURE; -} \ No newline at end of file +} diff --git a/r5launcher/main.h b/r5dev/sdklauncher/sdklauncher.h similarity index 100% rename from r5launcher/main.h rename to r5dev/sdklauncher/sdklauncher.h diff --git a/r5launcher/resource.h b/r5dev/sdklauncher/sdklauncher_res.h similarity index 94% rename from r5launcher/resource.h rename to r5dev/sdklauncher/sdklauncher_res.h index 13375343..cd449a78 100644 --- a/r5launcher/resource.h +++ b/r5dev/sdklauncher/sdklauncher_res.h @@ -1,6 +1,6 @@ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. -// Used by r5launcher.rc +// Used by r5launch.rc // #define IDI_ICON1 101 diff --git a/r5dev/server/IVEngineServer.cpp b/r5dev/server/IVEngineServer.cpp new file mode 100644 index 00000000..a46279bb --- /dev/null +++ b/r5dev/server/IVEngineServer.cpp @@ -0,0 +1,70 @@ +//=============================================================================// +// +// Purpose: Interface the engine exposes to the game DLL +// +//=============================================================================// + +#include "core/stdafx.h" +#include "tier0/cvar.h" +#include "engine/sys_utils.h" +#include "server/IVEngineServer.h" +#include "client/client.h" + +//----------------------------------------------------------------------------- +// Purpose: sets the persistence var in the CClient instance to 'ready' +//----------------------------------------------------------------------------- +bool HIVEngineServer_PersistenceAvailable(void* entidx, int clientidx) +{ + CClient* pClient = g_pClient->GetClientInstance(clientidx); // Get client instance. + std::uintptr_t targetInstance = (std::uintptr_t)pClient; + *(char*)(targetInstance + g_dwPersistenceVar) = (char)0x5; // Set the client instance to 'ready'. + + if (!g_bIsPersistenceVarSet[clientidx] && sv_showconnecting->m_pParent->m_iValue > 0) + { + void* clientNamePtr = (void**)(((std::uintptr_t)pClient->GetNetChan()) + 0x1A8D); // Get client name from netchan. + std::string clientName((char*)clientNamePtr, 32); // Get full name. + std::int64_t originID = pClient->m_iOriginID; + std::int64_t clientID = static_cast(pClient->m_iUserID + 1); + + std::string ipAddress = "null"; // If this stays null they modified the packet somehow. + ADDRESS ipAddressField = ADDRESS(((std::uintptr_t)pClient->GetNetChan()) + 0x1AC0); // Get client ip from netchan. + if (ipAddressField) + { + std::stringstream ss; + ss << std::to_string(ipAddressField.GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x1).GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x2).GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x3).GetValue()); + + ipAddress = ss.str(); + } + + DevMsg(eDLL_T::SERVER, "\n"); + DevMsg(eDLL_T::SERVER, "______________________________________________________________\n"); + DevMsg(eDLL_T::SERVER, "] CLIENT_INSTANCE_DETAILS ------------------------------------\n"); + DevMsg(eDLL_T::SERVER, "] INDEX: | '#%d'\n", clientidx); + DevMsg(eDLL_T::SERVER, "] NAME : | '%s'\n", clientName.c_str()); + DevMsg(eDLL_T::SERVER, "] OID : | '%lld'\n", originID); + DevMsg(eDLL_T::SERVER, "] UID : | '%lld'\n", clientID); + DevMsg(eDLL_T::SERVER, "] IPADR: | '%s'\n", ipAddress.c_str()); + DevMsg(eDLL_T::SERVER, "--------------------------------------------------------------\n"); + DevMsg(eDLL_T::SERVER, "\n"); + + g_bIsPersistenceVarSet[clientidx] = true; + } + /////////////////////////////////////////////////////////////////////////// + return IVEngineServer_PersistenceAvailable(entidx, clientidx); +} + +void IVEngineServer_Attach() +{ + DetourAttach((LPVOID*)&IVEngineServer_PersistenceAvailable, &HIVEngineServer_PersistenceAvailable); +} + +void IVEngineServer_Detach() +{ + DetourDetach((LPVOID*)&IVEngineServer_PersistenceAvailable, &HIVEngineServer_PersistenceAvailable); +} + +/////////////////////////////////////////////////////////////////////////////// +bool g_bIsPersistenceVarSet[128]; diff --git a/r5dev/server/IVEngineServer.h b/r5dev/server/IVEngineServer.h new file mode 100644 index 00000000..c7d7ed26 --- /dev/null +++ b/r5dev/server/IVEngineServer.h @@ -0,0 +1,30 @@ +#pragma once + +namespace +{ + /* ==== CVENGINESERVER ================================================================================================================================================== */ + ADDRESS p_IVEngineServer_PersistenceAvailable = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x3B\x15\x00\x00\x00\x00\x7D\x33", "xx????xx"); + bool (*IVEngineServer_PersistenceAvailable)(void* entidx, int clientidx) = (bool (*)(void*, int))p_IVEngineServer_PersistenceAvailable.GetPtr(); /*3B 15 ?? ?? ?? ?? 7D 33*/ +} + +/////////////////////////////////////////////////////////////////////////////// +bool HIVEngineServer_PersistenceAvailable(void* entidx, int clientidx); + +void IVEngineServer_Attach(); +void IVEngineServer_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +extern bool g_bIsPersistenceVarSet[128]; + +/////////////////////////////////////////////////////////////////////////////// +class HVEngineServer : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: IVEngineServer::PersistenceAvailable : 0x" << std::hex << std::uppercase << p_IVEngineServer_PersistenceAvailable.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HVEngineServer); diff --git a/r5dev/server/server.cpp b/r5dev/server/server.cpp new file mode 100644 index 00000000..682db81c --- /dev/null +++ b/r5dev/server/server.cpp @@ -0,0 +1,121 @@ +#include "core/stdafx.h" +#include "tier0/cvar.h" +#include "engine/sys_utils.h" +#include "server/server.h" +#include "client/client.h" +#include "networksystem/r5net.h" +#include "public/include/bansystem.h" + +//----------------------------------------------------------------------------- +// Purpose: checks is particular client is banned on the comp server +//----------------------------------------------------------------------------- +void IsClientBanned(R5Net::Client* pR5net, const std::string svIPAddr, std::int64_t nNucleusID) +{ + std::string svError = std::string(); + bool bCompBanned = pR5net && pR5net->GetClientIsBanned(svIPAddr, nNucleusID, svError); + if (bCompBanned) + { + while (bCompBanned) + { + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. + { + CClient* pClient = g_pClient->GetClientInstance(i); // Get client instance. + if (!pClient) // Client instance valid? + { + continue; + } + + if (!pClient->GetNetChan()) // Netchan valid? + { + continue; + } + + std::int64_t nOriginID = pClient->m_iOriginID; // Get originID. + if (nOriginID != nNucleusID) // See if they match. + { + continue; + } + + g_pBanSystem->AddConnectionRefuse(svError, pClient->m_iUserID + 1); // Add to the vector. + bCompBanned = false; + break; + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: client to server authentication +//----------------------------------------------------------------------------- +void* HCServer_Authenticate(void* pServer, user_creds* pInpacket) +{ + std::string svIpAddress = "null"; + ADDRESS ipAddressField = ADDRESS(&pInpacket->m_nIpAddr); + + if (ipAddressField && ipAddressField.GetValue() != 0x0) + { + std::stringstream ss; + ss << std::to_string(ipAddressField.GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x1).GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x2).GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x3).GetValue()); + + svIpAddress = ss.str(); + } + + if (sv_showconnecting->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::SERVER, "\n"); + DevMsg(eDLL_T::SERVER, "______________________________________________________________\n"); + DevMsg(eDLL_T::SERVER, "] AUTHENTICATION_DETAILS -------------------------------------\n"); + DevMsg(eDLL_T::SERVER, "] UserID : | '%s'\n", pInpacket->m_nUserID); + DevMsg(eDLL_T::SERVER, "] OriginID : | '%lld'\n", pInpacket->m_nNucleusID); + DevMsg(eDLL_T::SERVER, "] IP-ADDR : | '%s'\n", svIpAddress.c_str()); + DevMsg(eDLL_T::SERVER, "--------------------------------------------------------------\n"); + } + + if (g_pBanSystem->IsBanListValid()) // Is the banlist vector valid? + { + if (g_pBanSystem->IsBanned(svIpAddress, pInpacket->m_nNucleusID)) // Is the client trying to connect banned? + { + CServer_RejectConnection(pServer, *(unsigned int*)((std::uintptr_t)pServer + 0xC), pInpacket, "You have been banned from this Server."); // RejectConnection for the client. + + if (sv_showconnecting->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::SERVER, "] NOTICE : | THIS CLIENT IS BANNED!\n"); + DevMsg(eDLL_T::SERVER, "--------------------------------------------------------------\n\n"); + } + return nullptr; + } + } + if (sv_showconnecting->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::SERVER, "\n"); + } + + if (g_bCheckCompBanDB) + { + if (g_pR5net) + { + std::thread th(IsClientBanned, g_pR5net, svIpAddress, pInpacket->m_nNucleusID); + th.detach(); + } + } + + return CServer_Authenticate(pServer, pInpacket); +} + +/////////////////////////////////////////////////////////////////////////////// +void CServer_Attach() +{ + DetourAttach((LPVOID*)&CServer_Authenticate, &HCServer_Authenticate); +} + +void CServer_Detach() +{ + DetourDetach((LPVOID*)&CServer_Authenticate, &HCServer_Authenticate); +} + +/////////////////////////////////////////////////////////////////////////////// +bool g_bCheckCompBanDB = true; \ No newline at end of file diff --git a/r5dev/server/server.h b/r5dev/server/server.h new file mode 100644 index 00000000..bd3c78d0 --- /dev/null +++ b/r5dev/server/server.h @@ -0,0 +1,53 @@ +#pragma once +#include "tier0/basetypes.h" +#include "networksystem/r5net.h" + +struct user_creds +{ + std::uint8_t gap0[16]; + std::uint32_t m_nIpAddr; + std::uint8_t gap1[4]; + std::int32_t m_nProtocolVer; + std::uint8_t gap2[12]; + std::int64_t m_nNucleusID; + std::int64_t m_nUserID; +}; + +namespace +{ + /* ==== CSERVER ========================================================================================================================================================= */ +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) + ADDRESS p_CServer_Authenticate = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x44\x89\x44\x24\x00\x55\x56\x57\x48\x8D\xAC\x24\x00\x00\x00\x00", "xxxx?xxxxxxx????"); + void* (*CServer_Authenticate)(void* cserver, user_creds* creds) = (void* (*)(void* cserver, user_creds * creds))p_CServer_Authenticate.GetPtr(); /*44 89 44 24 ?? 55 56 57 48 8D AC 24 ?? ?? ?? ??*/ +#elif defined (GAMEDLL_S2) + ADDRESS p_CServer_Authenticate = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x44\x89\x44\x24\x00\x56\x57\x48\x81\xEC\x00\x00\x00\x00", "xxxx?xxxxx????"); + void* (*CServer_Authenticate)(void* cserver, user_creds* creds) = (void* (*)(void* cserver, user_creds * creds))p_CServer_Authenticate.GetPtr(); /*44 89 44 24 ?? 56 57 48 81 EC ?? ?? ?? ??*/ +#else + ADDRESS p_CServer_Authenticate = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x55\x57\x41\x55\x41\x57\x48\x8D\xAC\x24\x00\x00\x00\x00", "xxxxxxxxxxx????"); + void* (*CServer_Authenticate)(void* cserver, user_creds* creds) = (void* (*)(void* cserver, user_creds * creds))p_CServer_Authenticate.GetPtr(); /*40 55 57 41 55 41 57 48 8D AC 24 ?? ?? ?? ??*/ +#endif + ADDRESS p_CServer_RejectConnection = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x4C\x89\x4C\x24\x00\x53\x55\x56\x57\x48\x81\xEC\x00\x00\x00\x00\x49\x8B\xD9", "xxxx?xxxxxxx????xxx"); + void* (*CServer_RejectConnection)(void* pServer, unsigned int a2, user_creds* pCreds, const char* szMessage) = (void* (*)(void*, unsigned int, user_creds*, const char*))p_CServer_RejectConnection.GetPtr(); /*4C 89 4C 24 ?? 53 55 56 57 48 81 EC ?? ?? ?? ?? 49 8B D9*/ +} + +void CServer_Attach(); +void CServer_Detach(); + +void IsClientBanned(R5Net::Client* r5net, const std::string ipaddr, std::int64_t nucleus_id); +void* HCServer_Authenticate(void* cserver, user_creds* inpacket); + +extern bool g_bCheckCompBanDB; + +/////////////////////////////////////////////////////////////////////////////// +class HServer : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: CServer::Authenticate : 0x" << std::hex << std::uppercase << p_CServer_Authenticate.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: CServer::RejectConnection : 0x" << std::hex << std::uppercase << p_CServer_RejectConnection.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HServer); diff --git a/r5dev/squirrel/sqapi.cpp b/r5dev/squirrel/sqapi.cpp new file mode 100644 index 00000000..4fa21a48 --- /dev/null +++ b/r5dev/squirrel/sqapi.cpp @@ -0,0 +1,79 @@ +//=============================================================================// +// +// Purpose: Squirrel API +// +//=============================================================================// + +#include "core/stdafx.h" +#include "squirrel/sqapi.h" + +char* hsq_getstring(void* sqvm, int i) +{ + std::uintptr_t thisptr = reinterpret_cast(sqvm); + + return *(char**)(*(std::int64_t*)(thisptr + 0x58) + 0x10 * i + 0x8) + 0x40; +} + +int hsq_getinteger(void* sqvm, int i) +{ + std::uintptr_t thisptr = reinterpret_cast(sqvm); + + return *(int*)(*(std::int64_t*)(thisptr + 0x58) + 0x10 * i + 0x8); +} + +void hsq_pushbool(void* sqvm, int val) +{ + sq_pushbool(sqvm, val); +} + +void hsq_pushstring(void* sqvm, const char* string, int len) +{ + sq_pushstring(sqvm, const_cast(string), len); +} + +void hsq_pushinteger(void* sqvm, int val) +{ + sq_pushinteger(sqvm, val); +} + +void hsq_newarray(void* sqvm, int size) +{ + sq_newarray(sqvm, size); +} + +void hsq_arrayappend(void* sqvm, int idx) +{ + sq_arrayappend(sqvm, idx); +} + +void hsq_newtable(void* sqvm) +{ + sq_newtable(sqvm); +} + +void hsq_newslot(void* sqvm, int idx) +{ + sq_newslot(sqvm, idx); +} + +void SQAPI_Attach() +{ + DetourAttach((LPVOID*)&sq_pushbool, &hsq_pushbool); + DetourAttach((LPVOID*)&sq_pushstring, &hsq_pushstring); + DetourAttach((LPVOID*)&sq_pushinteger, &hsq_pushinteger); + DetourAttach((LPVOID*)&sq_newarray, &hsq_newarray); + DetourAttach((LPVOID*)&sq_arrayappend, &hsq_arrayappend); + DetourAttach((LPVOID*)&sq_newtable, &hsq_newtable); + DetourAttach((LPVOID*)&sq_newslot, &hsq_newslot); +} + +void SQAPI_Detach() +{ + DetourDetach((LPVOID*)&sq_pushbool, &hsq_pushbool); + DetourDetach((LPVOID*)&sq_pushstring, &hsq_pushstring); + DetourDetach((LPVOID*)&sq_pushinteger, &hsq_pushinteger); + DetourDetach((LPVOID*)&sq_newarray, &hsq_newarray); + DetourDetach((LPVOID*)&sq_arrayappend, &hsq_arrayappend); + DetourDetach((LPVOID*)&sq_newtable, &hsq_newtable); + DetourDetach((LPVOID*)&sq_newslot, &hsq_newslot); +} \ No newline at end of file diff --git a/r5dev/squirrel/sqapi.h b/r5dev/squirrel/sqapi.h new file mode 100644 index 00000000..8dce4f0d --- /dev/null +++ b/r5dev/squirrel/sqapi.h @@ -0,0 +1,68 @@ +#pragma once +#include "tier0/basetypes.h" + +namespace +{ + /* ==== SQUIRREL ======================================================================================================================================================== */ + ADDRESS p_sq_pushbool = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x83\xEC\x38\x33\xC0\x48\xC7\x44\x24\x20\x08\x00\x00\x01\x48", "xxxxxxxxxxxxxxxx"); + void (*sq_pushbool)(void* sqvm, int val) = (void (*)(void*, int))p_sq_pushbool.GetPtr(); /*48 83 EC 38 33 C0 48 C7 44 24 20 08 00 00 01 48*/ +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) || defined (GAMEDLL_S2) + ADDRESS p_sq_pushstring = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x56\x48\x83\xEC\x30\x48\x8B\xF1\x48\x85\xD2\x0F\x84\x8C\x00", "xxxxxxxxxxxxxxxx"); + void (*sq_pushstring)(void* sqvm, const char* string, int len) = (void (*)(void*, const char*, int))p_sq_pushstring.GetPtr(); /*40 56 48 83 EC 30 48 8B F1 48 85 D2 0F 84 8C 00*/ +#elif defined (GAMEDLL_S3) + ADDRESS p_sq_pushstring = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x56\x48\x83\xEC\x30\x48\x8B\xF1\x48\x85\xD2\x0F\x84\x8F\x00", "xxxxxxxxxxxxxxxx"); + void (*sq_pushstring)(void* sqvm, const char* string, int len) = (void (*)(void*, const char*, int))p_sq_pushstring.GetPtr(); /*40 56 48 83 EC 30 48 8B F1 48 85 D2 0F 84 8F 00*/ +#endif + ADDRESS p_sq_pushinteger = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x83\xEC\x38\x33\xC0\x48\xC7\x44\x24\x20\x02\x00\x00\x05\x48", "xxxxxxxxxxxxxxxx"); + void (*sq_pushinteger)(void* sqvm, int val) = (void (*)(void*, int))p_sq_pushinteger.GetPtr(); /*48 83 EC 38 33 C0 48 C7 44 24 20 02 00 00 05 48*/ + + ADDRESS p_sq_newarray = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x08\x57\x48\x83\xEC\x30\x48\x8B\xD9\x48\xC7\x44\x24\x20\x40", "xxxxxxxxxxxxxxxxxxx"); + void (*sq_newarray)(void* sqvm, int size) = (void (*)(void*, int))p_sq_newarray.GetPtr(); /*48 89 5C 24 08 57 48 83 EC 30 48 8B D9 48 C7 44 24 20 40*/ + + ADDRESS p_sq_arrayappend = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x53\x48\x83\xEC\x20\x8B\x41\x00\x48\x8B\xD9\x2B\x41\x00\x83\xF8\x02\x7D", "xxxxxxxx?xxxxx?xxxx"); + void (*sq_arrayappend)(void* sqvm, int idx) = (void (*)(void*, int))p_sq_arrayappend.GetPtr(); /*40 53 48 83 EC 20 8B 41 ?? 48 8B D9 2B 41 ?? 83 F8 02 7D*/ + + ADDRESS p_sq_newtable = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x08\x57\x48\x83\xEC\x30\x48\x8B\xD9\x48\xC7\x44\x24\x20\x20", "xxxxxxxxxxxxxxxxxxx"); + void (*sq_newtable)(void* sqvm) = (void (*)(void*))p_sq_newtable.GetPtr(); /*48 89 5C 24 08 57 48 83 EC 30 48 8B D9 48 C7 44 24 20 20*/ + + ADDRESS p_sq_newslot = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x53\x48\x83\xEC\x30\x44\x8B\x49\x00\x48\x8B\xD9\x41\x8B\xC1", "xxxxxxxxx?xxxxxx"); + void (*sq_newslot)(void* sqvm, int idx) = (void (*)(void*, int))p_sq_newslot.GetPtr(); /*40 53 48 83 EC 20 8B 41 ?? 48 8B D9 2B 41 ?? 83 F8 02 7D*/ +} + +/////////////////////////////////////////////////////////////////////////////// +char* hsq_getstring(void* sqvm, int i); +int hsq_getinteger(void* sqvm, int i); + +void hsq_pushbool(void* sqvm, int val); + +void hsq_pushstring(void* sqvm, const char* string, int len); + +void hsq_pushinteger(void* sqvm, int val); + +void hsq_newarray(void* sqvm, int size); +void hsq_arrayappend(void* sqvm, int idx); + +void hsq_newtable(void* sqvm); +void hsq_newslot(void* sqvm, int idx); + +void SQAPI_Attach(); +void SQAPI_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +class HSqapi : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: sq_pushbool : 0x" << std::hex << std::uppercase << p_sq_pushbool.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: sq_pushstring : 0x" << std::hex << std::uppercase << p_sq_pushstring.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: sq_pushinteger : 0x" << std::hex << std::uppercase << p_sq_pushinteger.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: sq_newarray : 0x" << std::hex << std::uppercase << p_sq_newarray.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: sq_arrayappend : 0x" << std::hex << std::uppercase << p_sq_arrayappend.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: sq_newtable : 0x" << std::hex << std::uppercase << p_sq_newtable.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: sq_newslot : 0x" << std::hex << std::uppercase << p_sq_newslot.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HSqapi); diff --git a/r5dev/squirrel/sqinit.cpp b/r5dev/squirrel/sqinit.cpp new file mode 100644 index 00000000..7248ee24 --- /dev/null +++ b/r5dev/squirrel/sqinit.cpp @@ -0,0 +1,8 @@ +//=============================================================================// +// +// Create new functions here +// +//=============================================================================// + +#include "core/stdafx.h" +#include "sqinit.h" diff --git a/r5dev/squirrel/sqinit.h b/r5dev/squirrel/sqinit.h new file mode 100644 index 00000000..98e790f6 --- /dev/null +++ b/r5dev/squirrel/sqinit.h @@ -0,0 +1,23 @@ +#pragma once + +namespace +{ + ADDRESS p_Script_Remote_BeginRegisteringFunctions = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x83\xEC\x28\x83\x3D\x00\x00\x00\x00\x00\x74\x10", "xxxxxx?????xx"); + void* Script_Remote_BeginRegisteringFunctions = (void*)p_Script_Remote_BeginRegisteringFunctions.GetPtr(); /*48 83 EC 28 83 3D ?? ?? ?? ?? ?? 74 10*/ + + std::uint32_t* g_nRemoteFunctionCallsChecksum = reinterpret_cast(p_Script_Remote_BeginRegisteringFunctions.FindPatternSelf("89 05", ADDRESS::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x6).GetPtr()); +} + +/////////////////////////////////////////////////////////////////////////////// +class HSqInit : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: Remote_BeginRegisteringFunctions : 0x" << std::hex << std::uppercase << p_Script_Remote_BeginRegisteringFunctions.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: g_nRemoteFunctionCallsChecksum : 0x" << std::hex << std::uppercase << g_nRemoteFunctionCallsChecksum << std::setw(0) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HSqInit); diff --git a/r5dev/squirrel/sqvm.cpp b/r5dev/squirrel/sqvm.cpp new file mode 100644 index 00000000..4eb69115 --- /dev/null +++ b/r5dev/squirrel/sqvm.cpp @@ -0,0 +1,306 @@ +//=============================================================================// +// +// Purpose: Squirrel VM +// +//=============================================================================// + +#include "core/stdafx.h" +#include "core/logdef.h" +#include "tier0/basetypes.h" +#include "tier0/cvar.h" +#include "tier0/IConVar.h" +#include "engine/sys_utils.h" +#include "squirrel/sqvm.h" +#include "vgui/CEngineVGui.h" +#include "gameui/IConsole.h" + +//--------------------------------------------------------------------------------- +// Purpose: prints the output of each VM to the console +//--------------------------------------------------------------------------------- +void* HSQVM_PrintFunc(void* sqvm, char* fmt, ...) +{ +#ifdef GAMEDLL_S3 + int vmIdx = *(int*)((std::uintptr_t)sqvm + 0x18); +#else // TODO [ AMOS ]: nothing equal to 'rdx + 18h' exist in the vm pointers for anything below S3. + int vmIdx = 3; +#endif + static bool initialized = false; + + static char buf[1024]; + static std::string vmType[4] = { "Script(S):", "Script(C):", "Script(U):", "Script(X):" }; + + static auto iconsole = spdlog::stdout_logger_mt("sqvm_print_iconsole"); // in-game console. + static auto wconsole = spdlog::stdout_logger_mt("sqvm_print_wconsole"); // windows console. + static auto sqlogger = spdlog::basic_logger_mt("sqvm_print_logger", "platform\\logs\\sqvm_print.log"); // file logger. + + std::string vmStr = vmType[vmIdx].c_str(); + + g_spd_sqvm_p_oss.str(""); + g_spd_sqvm_p_oss.clear(); + + if (!initialized) + { + iconsole = std::make_shared("sqvm_print_ostream", g_spd_sqvm_p_ostream_sink); + iconsole->set_pattern("[%S.%e] %v"); + iconsole->set_level(spdlog::level::debug); + wconsole->set_pattern("[%S.%e] %v"); + wconsole->set_level(spdlog::level::debug); + sqlogger->set_pattern("[%S.%e] %v"); + sqlogger->set_level(spdlog::level::debug); + initialized = true; + } + + va_list args; + va_start(args, fmt); + + vsnprintf(buf, sizeof(buf), fmt, args); + + buf[sizeof(buf) - 1] = 0; + va_end(args); + + vmStr.append(buf); + + if (sq_showvmoutput->m_pParent->m_iValue > 0) + { + sqlogger->debug(vmStr); + } + if (sq_showvmoutput->m_pParent->m_iValue > 1) + { + iconsole->debug(vmStr); + wconsole->debug(vmStr); +#ifndef DEDICATED + std::string s = g_spd_sqvm_p_oss.str(); + const char* c = s.c_str(); + Items.push_back(Strdup((const char*)c)); +#endif // !DEDICATED + } +#ifndef DEDICATED + if (sq_showvmoutput->m_pParent->m_iValue > 2) + { + std::string s = g_spd_sqvm_p_oss.str(); + g_pLogSystem.AddLog((LogType_t)vmIdx, s); + } +#endif // !DEDICATED + return NULL; +} + +//--------------------------------------------------------------------------------- +// Purpose: prints the warning output of each VM to the console +//--------------------------------------------------------------------------------- +void* HSQVM_WarningFunc(void* sqvm, int a2, int a3, int* nStringSize, void** ppString) +{ + void* result = SQVM_WarningFunc(sqvm, a2, a3, nStringSize, ppString); + if (g_bSQVM_WarnFuncCalled) // Check if its SQVM_Warning calling. + { + return result; // If not return. + } + + static bool initialized = false; + static std::string vmType[4] = { "Script(S): WARNING: ", "Script(C): WARNING: ", "Script(U): WARNING: ", "Script(X): WARNING: " }; + + static auto iconsole = spdlog::stdout_logger_mt("sqvm_warn_iconsole"); // in-game console. + static auto wconsole = spdlog::stdout_logger_mt("sqvm_warn_wconsole"); // windows console. + static auto sqlogger = spdlog::basic_logger_mt("sqvm_warn_logger", "platform\\logs\\sqvm_warn.log"); // file logger. + +#ifdef GAMEDLL_S3 + int vmIdx = *(int*)((std::uintptr_t)sqvm + 0x18); +#else // TODO [ AMOS ]: nothing equal to 'rdx + 18h' exist in the vm pointers for anything below S3. + int vmIdx = 3; +#endif + std::string vmStr = vmType[vmIdx].c_str(); + + g_spd_sqvm_w_oss.str(""); + g_spd_sqvm_w_oss.clear(); + + if (!initialized) + { + iconsole = std::make_shared("sqvm_warn_ostream", g_spd_sqvm_p_ostream_sink); + iconsole->set_pattern("[%S.%e] %v\n"); + iconsole->set_level(spdlog::level::debug); + wconsole->set_pattern("[%S.%e] %v\n"); + wconsole->set_level(spdlog::level::debug); + sqlogger->set_pattern("[%S.%e] %v\n"); + sqlogger->set_level(spdlog::level::debug); + initialized = true; + } + + std::string stringConstructor((char*)*ppString, *nStringSize); // Get string from memory via std::string constructor. + vmStr.append(stringConstructor); + + std::string s = g_spd_sqvm_w_oss.str(); + const char* c = s.c_str(); + + if (sq_showvmwarning->m_pParent->m_iValue > 0) + { + sqlogger->debug(vmStr); // Emit to file. + } + if (sq_showvmwarning->m_pParent->m_iValue > 1) + { + iconsole->debug(vmStr); // Emit to in-game console. + wconsole->debug(vmStr); // Emit to windows console. +#ifndef DEDICATED + std::string s = g_spd_sqvm_w_oss.str(); + const char* c = s.c_str(); + Items.push_back(Strdup(c)); +#endif // !DEDICATED + } +#ifndef DEDICATED + if (sq_showvmwarning->m_pParent->m_iValue > 2) + { + g_pLogSystem.AddLog((LogType_t)vmIdx, s); + const char* c = s.c_str(); + Items.push_back(Strdup(c)); + } +#endif // !DEDICATED + g_bSQVM_WarnFuncCalled = false; + + return result; +} + +//--------------------------------------------------------------------------------- +// Purpose: +//--------------------------------------------------------------------------------- +void* HSQVM_WarningCmd(int a1, int a2) +{ + g_bSQVM_WarnFuncCalled = true; + return SQVM_WarningCmd(a1, a2); +} + +//--------------------------------------------------------------------------------- +// Purpose: loads the include file from the mods directory +//--------------------------------------------------------------------------------- +void* HSQVM_LoadRson(const char* szRsonName) +{ + char szFilePath[MAX_PATH] = { 0 }; + sprintf_s(szFilePath, MAX_PATH, "platform\\%s", szRsonName); + + // Flip forward slashes in filepath to windows-style backslash + for (int i = 0; i < strlen(szFilePath); i++) + { + if (szFilePath[i] == '/') + { + szFilePath[i] = '\\'; + } + } + + // Returns the new path if the rson exists on the disk + if (FileExists(szFilePath) && SQVM_LoadRson(szRsonName)) + { + if (sq_showrsonloading->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::ENGINE, "\n"); + DevMsg(eDLL_T::ENGINE, "______________________________________________________________\n"); + DevMsg(eDLL_T::ENGINE, "] RSON_DISK --------------------------------------------------\n"); + DevMsg(eDLL_T::ENGINE, "] PATH: '%s'\n", szFilePath); + DevMsg(eDLL_T::ENGINE, "--------------------------------------------------------------\n"); + DevMsg(eDLL_T::ENGINE, "\n"); + } + return SQVM_LoadRson(szFilePath); + } + else + { + if (sq_showrsonloading->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::ENGINE, "\n"); + DevMsg(eDLL_T::ENGINE, "______________________________________________________________\n"); + DevMsg(eDLL_T::ENGINE, "] RSON_VPK ---------------------------------------------------\n"); + DevMsg(eDLL_T::ENGINE, "] PATH: '%s'\n", szRsonName); + DevMsg(eDLL_T::ENGINE, "--------------------------------------------------------------\n"); + DevMsg(eDLL_T::ENGINE, "\n"); + } + } + return SQVM_LoadRson(szRsonName); +} + +//--------------------------------------------------------------------------------- +// Purpose: loads the script file from the mods directory +//--------------------------------------------------------------------------------- +bool HSQVM_LoadScript(void* sqvm, const char* szScriptPath, const char* szScriptName, int nFlag) +{ + char filepath[MAX_PATH] = { 0 }; + sprintf_s(filepath, MAX_PATH, "platform\\%s", szScriptPath); + + // Flip forward slashes in filepath to windows-style backslash + for (int i = 0; i < strlen(filepath); i++) + { + if (filepath[i] == '/') + { + filepath[i] = '\\'; + } + } + + if (sq_showscriptloading->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::ENGINE, "Loading SQVM Script '%s'\n", filepath); + } + + // Returns true if the script exists on the disk + if (FileExists(filepath) && SQVM_LoadScript(sqvm, filepath, szScriptName, nFlag)) + { + return true; + } + + if (sq_showscriptloading->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::ENGINE, "FAILED. Try SP / VPK for '%s'\n", filepath); + } + + /////////////////////////////////////////////////////////////////////////////// + return SQVM_LoadScript(sqvm, szScriptPath, szScriptName, nFlag); +} + +void HSQVM_RegisterFunction(void* sqvm, const char* szName, const char* szHelpString, const char* szRetValType, const char* szArgTypes, void* pFunction) +{ + SQFuncRegistration* sqFunc = new SQFuncRegistration(); + + sqFunc->m_szScriptName = szName; + sqFunc->m_szNativeName = szName; + sqFunc->m_szHelpString = szHelpString; + sqFunc->m_szRetValType = szRetValType; + sqFunc->m_szArgTypes = szArgTypes; + sqFunc->m_pFunction = pFunction; + + SQVM_RegisterFunc(sqvm, sqFunc, 1); +} + +int HSQVM_NativeTest(void* sqvm) +{ + // Function code goes here. + return 1; +} + +void RegisterUIScriptFunctions(void* sqvm) +{ + HSQVM_RegisterFunction(sqvm, "UINativeTest", "native ui function", "void", "", &HSQVM_NativeTest); +} + +void RegisterClientScriptFunctions(void* sqvm) +{ + HSQVM_RegisterFunction(sqvm, "ClientNativeTest", "native client function", "void", "", &HSQVM_NativeTest); +} + +void RegisterServerScriptFunctions(void* sqvm) +{ + HSQVM_RegisterFunction(sqvm, "ServerNativeTest", "native server function", "void", "", &HSQVM_NativeTest); +} + +void SQVM_Attach() +{ + DetourAttach((LPVOID*)&SQVM_PrintFunc, &HSQVM_PrintFunc); + DetourAttach((LPVOID*)&SQVM_WarningFunc, &HSQVM_WarningFunc); + DetourAttach((LPVOID*)&SQVM_WarningCmd, &HSQVM_WarningCmd); + DetourAttach((LPVOID*)&SQVM_LoadRson, &HSQVM_LoadRson); + DetourAttach((LPVOID*)&SQVM_LoadScript, &HSQVM_LoadScript); +} + +void SQVM_Detach() +{ + DetourDetach((LPVOID*)&SQVM_PrintFunc, &HSQVM_PrintFunc); + DetourDetach((LPVOID*)&SQVM_WarningFunc, &HSQVM_WarningFunc); + DetourDetach((LPVOID*)&SQVM_WarningCmd, &HSQVM_WarningCmd); + DetourDetach((LPVOID*)&SQVM_LoadRson, &HSQVM_LoadRson); + DetourDetach((LPVOID*)&SQVM_LoadScript, &HSQVM_LoadScript); +} + +/////////////////////////////////////////////////////////////////////////////// +bool g_bSQVM_WarnFuncCalled = false; diff --git a/r5dev/squirrel/sqvm.h b/r5dev/squirrel/sqvm.h new file mode 100644 index 00000000..5016e8fe --- /dev/null +++ b/r5dev/squirrel/sqvm.h @@ -0,0 +1,85 @@ +#pragma once + +struct SQFuncRegistration +{ + const char* m_szScriptName; // 00 + const char* m_szNativeName; // 08 + const char* m_szHelpString; // 10 + const char* m_szRetValType; // 18 + const char* m_szArgTypes; // 20 + std::int16_t unk28; // 28 + std::int16_t padding1; // 2A + std::int32_t unk2c; // 2C + std::int64_t unk30; // 30 + std::int32_t unk38; // 38 + std::int32_t padding2; // 3C + std::int64_t unk40; // 40 + std::int64_t unk48; // 48 + std::int64_t unk50; // 50 + std::int32_t unk58; // 58 + std::int32_t padding3; // 5C + void* m_pFunction; // 60 + + SQFuncRegistration() + { + memset(this, 0, sizeof(SQFuncRegistration)); + this->padding2 = 6; + } +}; + +namespace +{ + /* ==== SQUIRREL ======================================================================================================================================================== */ + ADDRESS p_SQVM_PrintFunc = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x8B\xC4\x48\x89\x50\x10\x4C\x89\x40\x18\x4C\x89\x48\x20\x53\x56\x57\x48\x81\xEC\x30\x08\x00\x00\x48\x8B\xDA\x48\x8D\x70\x18\x48\x8B\xF9\xE8\x00\x00\x00\xFF\x48\x89\x74\x24\x28\x48\x8D\x54\x24\x30\x33", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx???xxxxxxxxxxxx"); + void* SQVM_PrintFunc = (void*)p_SQVM_PrintFunc.GetPtr(); /*48 8B C4 48 89 50 10 4C 89 40 18 4C 89 48 20 53 56 57 48 81 EC 30 08 00 00 48 8B DA 48 8D 70 18 48 8B F9 E8 ?? ?? ?? FF 48 89 74 24 28 48 8D 54 24 30 33*/ + + ADDRESS p_SQVM_WarningFunc = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x4C\x89\x4C\x24\x20\x44\x89\x44\x24\x18\x89\x54\x24\x10\x53\x55\x56\x57\x41\x54\x41\x55\x41\x56\x41\x57\x48\x83\xEC\x00\x48\x8B", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx?xx"); + void* (*SQVM_WarningFunc)(void* sqvm, int a2, int a3, int* nStringSize, void** ppString) = (void* (*)(void*, int, int, int*, void**))p_SQVM_WarningFunc.GetPtr(); /*4C 89 4C 24 20 44 89 44 24 18 89 54 24 10 53 55 56 57 41 54 41 55 41 56 41 57 48 83 EC ?? 48 8B*/ + + ADDRESS p_SQVM_WarningCmd = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x53\x48\x83\xEC\x30\x33\xDB\x48\x8D\x44\x24\x00\x4C\x8D\x4C\x24\x00", "xxxxxxxxxxxx?xxxx?"); + void* (*SQVM_WarningCmd)(int a1, int a2) = (void* (*)(int, int))p_SQVM_WarningCmd.GetPtr(); /*40 53 48 83 EC 30 33 DB 48 8D 44 24 ?? 4C 8D 4C 24 ??*/ +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) + ADDRESS p_SQVM_LoadScript = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x10\x48\x89\x74\x24\x18\x48\x89\x7C\x24\x20\x48\x89\x4C\x24\x08\x55\x41\x54\x41\x55\x41\x56\x41\x57\x48\x8D\x6C", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + bool (*SQVM_LoadScript)(void* sqvm, const char* szScriptPath, const char* szScriptName, int nFlag) = (bool (*)(void*, const char*, const char*, int))p_SQVM_LoadScript.GetPtr(); /*48 89 5C 24 10 48 89 74 24 18 48 89 7C 24 20 48 89 4C 24 08 55 41 54 41 55 41 56 41 57 48 8D 6C*/ +#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) + ADDRESS p_SQVM_LoadScript = FindPatternSIMD("r5apex.exe", (const unsigned char*)"\x48\x8B\xC4\x48\x89\x48\x08\x55\x41\x56\x48\x8D\x68", "xxxxxxxxxxxxx"); /*48 8B C4 48 89 48 08 55 41 56 48 8D 68*/ + bool (*SQVM_LoadScript)(void* sqvm, const char* szScriptPath, const char* szScriptName, int nFlag) = (bool (*)(void*, const char*, const char*, int))p_SQVM_LoadScript.GetPtr(); +#endif + ADDRESS p_SQVM_LoadRson = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x4C\x8B\xDC\x49\x89\x5B\x08\x57\x48\x81\xEC\xA0\x00\x00\x00\x33", "xxxxxxxxxxxxxxxx"); + void* (*SQVM_LoadRson)(const char* szRsonName) = (void* (*)(const char*))p_SQVM_LoadRson.GetPtr(); /*4C 8B DC 49 89 5B 08 57 48 81 EC A0 00 00 00 33*/ + + ADDRESS p_SQVM_RegisterFunc = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x83\xEC\x38\x45\x0F\xB6\xC8", "xxxxxxxx"); /*48 83 EC 38 45 0F B6 C8*/ + void* (*SQVM_RegisterFunc)(void* sqvm, SQFuncRegistration* sqFunc, int a1) = (void* (*)(void*, SQFuncRegistration*, int))p_SQVM_RegisterFunc.GetPtr(); +} + +void* HSQVM_PrintFunc(void* sqvm, char* fmt, ...); +void* HSQVM_LoadRson(const char* szRsonName); +bool HSQVM_LoadScript(void* sqvm, const char* szScriptPath, const char* szScriptName, int nFlags); + +void HSQVM_RegisterFunction(void* sqvm, const char* szName, const char* szHelpString, const char* szRetValType, const char* szArgTypes, void* pFunction); +int HSQVM_NativeTest(void* sqvm); + + +void SQVM_Attach(); +void SQVM_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +extern bool g_bSQVM_WarnFuncCalled; + +/////////////////////////////////////////////////////////////////////////////// +class HSQVM : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: SQVM_PrintFunc : 0x" << std::hex << std::uppercase << p_SQVM_PrintFunc.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: SQVM_WarningFunc : 0x" << std::hex << std::uppercase << p_SQVM_WarningFunc.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: SQVM_WarningCmd : 0x" << std::hex << std::uppercase << p_SQVM_WarningCmd.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: SQVM_LoadScript : 0x" << std::hex << std::uppercase << p_SQVM_LoadScript.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: SQVM_LoadRson : 0x" << std::hex << std::uppercase << p_SQVM_LoadRson.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: SQVM_RegisterFunc : 0x" << std::hex << std::uppercase << p_SQVM_RegisterFunc.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HSQVM); diff --git a/r5dev/src/CCompanion.cpp b/r5dev/src/CCompanion.cpp deleted file mode 100644 index fbeb8970..00000000 --- a/r5dev/src/CCompanion.cpp +++ /dev/null @@ -1,679 +0,0 @@ -#include "pch.h" -#include "hooks.h" -#include "id3dx.h" -#include "console.h" -#include "patterns.h" -#include "gameclasses.h" -#include "CCompanion.h" -#include "r5net.h" - -CCompanion* g_ServerBrowser = nullptr; -bool g_CheckCompBanDB = true; - -std::map mapArray = -{ - { "mp_rr_canyonlands_64k_x_64k", "King's Canyon Season 0" }, - { "mp_rr_desertlands_64k_x_64k", "World's Edge Season 3" }, - { "mp_rr_canyonlands_mu1", "King's Canyon Season 2" }, - { "mp_rr_canyonlands_mu1_night", "King's Canyon Season 2 After Dark" }, - { "mp_rr_desertlands_64k_x_64k_nx", "World's Edge Season 3 After Dark" }, - { "mp_lobby", "Lobby Season 3" }, - { "mp_rr_canyonlands_staging", "King's Canyon Firing Range" } -}; - -/*----------------------------------------------------------------------------- - * _ccompanion.cpp - *-----------------------------------------------------------------------------*/ -CCompanion::CCompanion() : MatchmakingServerStringBuffer("r5a-comp-sv.herokuapp.com"), r5net(new R5Net::Client("r5a-comp-sv.herokuapp.com")) -{ - memset(ServerConnStringBuffer, 0, sizeof(ServerConnStringBuffer)); - - std::string path = "stbsp"; - for (const auto& entry : std::filesystem::directory_iterator(path)) - { - std::string filename = entry.path().string(); - int slashPos = filename.rfind("\\", std::string::npos); - filename = filename.substr((INT8)slashPos + 1, std::string::npos); - filename = filename.substr(0, filename.size() - 6); - - auto it = mapArray.find(filename); // Find MapName in mapArray. - if (it != mapArray.end()) - { - MapsList.push_back(it->second); - } - else - { - MapsList.push_back(filename); - } - } - - // copy assignment kjek - MyServer.map = MapsList[0]; - - static std::thread HostingServerRequestThread([this]() - { - while (true) - { - UpdateHostingStatus(); - std::this_thread::sleep_for(std::chrono::milliseconds(5000)); - } - }); - - HostingServerRequestThread.detach(); -} - -CCompanion::~CCompanion() -{ - delete r5net; -} - -void CCompanion::UpdateHostingStatus() -{ - if (!GameGlobals::HostState || !GameGlobals::Cvar) // Is HostState and Cvar valid? - return; - - HostingStatus = GameGlobals::HostState->m_bActiveGame ? EHostStatus::Hosting : EHostStatus::NotHosting; // Are we hosting a server? - - switch (HostingStatus) - { - case EHostStatus::NotHosting: - { - HostRequestMessage = ""; - HostRequestMessageColor = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - break; - } - case EHostStatus::Hosting: - { - if (ServerVisibility == EServerVisibility::Offline) // Do we wanna broadcast server to the browser? - break; - - if (*reinterpret_cast(0x1656057E0) == NULL) // Check if script checksum is valid yet. - break; - - switch (ServerVisibility) - { - - case EServerVisibility::Hidden: - MyServer.hidden = true; - break; - case EServerVisibility::Public: - MyServer.hidden = false; - break; - default: - break; - } - - SendHostingPostRequest(); - break; - } - default: - break; - } -} - -void CCompanion::RefreshServerList() -{ - ServerList.clear(); - - static bool bThreadLocked = false; - - if (!bThreadLocked) - { - std::thread t([this]() - { - spdlog::debug("[+CCompanion+] Refreshing server list with string {}\n", MatchmakingServerStringBuffer); - bThreadLocked = true; - ServerList = r5net->GetServersList(ServerListMessage); - bThreadLocked = false; - }); - - t.detach(); - } -} - -void CCompanion::SendHostingPostRequest() -{ - HostToken = std::string(); - spdlog::debug("[+CCompanion+] Sending PostServerHost request now..\n"); - bool result = r5net->PostServerHost(HostRequestMessage,HostToken, - ServerListing{ - MyServer.name, - std::string(GameGlobals::HostState->m_levelName), - "", - GameGlobals::Cvar->FindVar("hostport")->m_pzsCurrentValue, - GameGlobals::Cvar->FindVar("mp_gamemode")->m_pzsCurrentValue, - MyServer.hidden, - std::to_string(*reinterpret_cast(0x1656057E0)), - std::string(), - addr_NetChan_EncKey - } - ); - - if (result) - { - HostRequestMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f); - std::stringstream msg; - msg << "Broadcasting! "; - if (!HostToken.empty()) - { - msg << "Share the following token for people to connect: "; - } - HostRequestMessage = msg.str().c_str(); - } - else - { - HostRequestMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); - } -} - - -void CCompanion::CompMenu() -{ - ImGui::BeginTabBar("CompMenu"); - if (ImGui::TabItemButton("Server Browser")) - { - SetSection(ESection::ServerBrowser); - } - if (ImGui::TabItemButton("Host Server")) - { - SetSection(ESection::HostServer); - } - if (ImGui::TabItemButton("Settings")) - { - SetSection(ESection::Settings); - } - ImGui::EndTabBar(); -} - -void CCompanion::ServerBrowserSection() -{ - ImGui::BeginGroup(); - ServerBrowserFilter.Draw(); - ImGui::SameLine(); - if (ImGui::Button("Refresh List")) - { - RefreshServerList(); - } - ImGui::EndGroup(); - ImGui::TextColored(ImVec4(1.00f, 0.00f, 0.00f, 1.00f), ServerListMessage.c_str()); - ImGui::Separator(); - - const float FooterHeight = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); - ImGui::BeginChild("ServerListChild", { 0, -FooterHeight }, true, ImGuiWindowFlags_AlwaysVerticalScrollbar); - if (ImGui::BeginTable("##ServerBrowser_ServerList", 5, ImGuiTableFlags_Resizable)) - { - ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch, 20); - ImGui::TableSetupColumn("Map", ImGuiTableColumnFlags_WidthStretch, 25); - ImGui::TableSetupColumn("Port", ImGuiTableColumnFlags_WidthStretch, 5); - ImGui::TableSetupColumn("Playlist", ImGuiTableColumnFlags_WidthStretch, 5); - ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 5); - ImGui::TableHeadersRow(); - - for (ServerListing& server : ServerList) - { - const char* name = server.name.c_str(); - const char* map = server.map.c_str(); - const char* port = server.port.c_str(); - const char* playlist = server.playlist.c_str(); - - if (ServerBrowserFilter.PassFilter(name) - || ServerBrowserFilter.PassFilter(map) - || ServerBrowserFilter.PassFilter(port)) - { - ImGui::TableNextColumn(); - ImGui::Text(name); - - ImGui::TableNextColumn(); - ImGui::Text(map); - - ImGui::TableNextColumn(); - ImGui::Text(port); - - ImGui::TableNextColumn(); - ImGui::Text(playlist); - - ImGui::TableNextColumn(); - std::string selectButtonText = "Connect##"; - selectButtonText += (server.name + server.ip + server.map); - - if (ImGui::Button(selectButtonText.c_str())) - { - ConnectToServer(server.ip, server.port, server.netchanEncryptionKey); - } - } - - } - ImGui::EndTable(); - } - ImGui::EndChild(); - - ImGui::Separator(); - - ImGui::PushItemWidth(ImGui::GetWindowContentRegionWidth() / 4); - { - ImGui::InputTextWithHint("##ServerBrowser_ServerConnString", "Enter IP address or \"localhost\"", ServerConnStringBuffer, IM_ARRAYSIZE(ServerConnStringBuffer)); - - ImGui::SameLine(); - ImGui::InputTextWithHint("##ServerBrowser_ServerEncKey", "Enter the encryption key", ServerEncKeyBuffer, IM_ARRAYSIZE(ServerEncKeyBuffer)); - ImGui::SameLine(); - - if (ImGui::Button("Connect##ServerBrowser_ConnectByIp", ImVec2(ImGui::GetWindowContentRegionWidth() / 4, 18.5))) - { - ConnectToServer(ServerConnStringBuffer, ServerEncKeyBuffer); - } - - ImGui::SameLine(); - - if (ImGui::Button("Private Servers##ServerBrowser_HiddenServersButton", ImVec2(ImGui::GetWindowContentRegionWidth() / 4, 18.5))) - { - ImGui::OpenPopup("Connect to Private Server##HiddenServersConnectModal"); - } - HiddenServersModal(); - // TODO: Still not in a seperate class... - - } - ImGui::PopItemWidth(); -} - -void CCompanion::HiddenServersModal() -{ - bool modalOpen = true; - if (ImGui::BeginPopupModal("Connect to Private Server##HiddenServersConnectModal", &modalOpen)) - { - // I *WILL* move this in a separate class - - ImGui::SetWindowSize(ImVec2(400.f, 200.f), ImGuiCond_Always); - - /* When removing this and adding the resource instead. Please initialize the texture in the CCompanion class constructor. - Pixie*/ - static unsigned char lockedserver[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x83, 0xff, 0xff, 0xff, 0x40, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x28, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa8, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x47, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xff, 0x70, 0xff, 0xff, 0xff, 0x1b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xa7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x33, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x08, 0xff, 0xff, 0xff, 0x88, 0xff, 0xff, 0xff, 0xe8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbc, 0xff, 0xff, 0xff, 0x47, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x44, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x2c, 0xff, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x14, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8b, 0xff, 0xff, 0xff, 0x0b, 0xff, 0xff, 0xff, 0x27, 0xff, 0xff, 0xff, 0xd7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x70, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xff, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xbc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xec, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x43, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x33, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x0c, 0xff, 0xff, 0xff, 0xeb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x34, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x6c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb4, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd3, 0xff, 0xff, 0xff, 0x57, 0xff, 0xff, 0xff, 0x73, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa3, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xa7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xb8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x43, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xa7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x50, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf4, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xd3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x60, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x48, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc4, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0x0b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x4c, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x1b, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbc, 0xff, 0xff, 0xff, 0xdc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x2c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb8, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x9b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf4, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x73, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x27, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x54, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x87, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x13, 0xff, 0xff, 0xff, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa8, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa0, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0x0b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x74, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x4b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x54, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x5f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x64, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xd7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd3, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x1c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x03, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x50, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x0c, 0xff, 0xff, 0xff, 0xe8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x7c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x44, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xec, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x17, 0xff, 0xff, 0xff, 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb7, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xe8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xab, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x2c, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x9c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x34, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x48, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x93, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x1c, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb4, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x37, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xc4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0x10, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x4f, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x10, 0xff, 0xff, 0xff, 0xa4, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x63, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x0b, 0xff, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd8, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x0c, 0xff, 0xff, 0xff, 0xe8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdc, 0xff, 0xff, 0xff, 0x03, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x27, 0xff, 0xff, 0xff, 0xd3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x4f, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0x1b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x43, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x24, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x0b, 0xff, 0xff, 0xff, 0x27, 0xff, 0xff, 0xff, 0x04, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x1b, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x43, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x97, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x33, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xb0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x67, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x17, 0xff, 0xff, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xdc, 0xff, 0xff, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x28, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x4f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x63, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x14, 0xff, 0xff, 0xff, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc4, 0xff, 0xff, 0xff, 0x0b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x43, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa8, 0xff, 0xff, 0xff, 0x04, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x4f, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x73, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x2b, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x48, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0x53, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x0b, 0xff, 0xff, 0xff, 0xb7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x08, 0xff, 0xff, 0xff, 0xd7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x60, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x0c, 0xff, 0xff, 0xff, 0xcb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0xff, 0xff, 0xff, 0x14, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x67, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x64, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff, 0xd7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x9b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x67, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x13, 0xff, 0xff, 0xff, 0xc8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf4, 0xff, 0xff, 0xff, 0x34, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x4c, 0xff, 0xff, 0xff, 0x8f, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x80, 0xff, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0x2b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x93, 0xff, 0xff, 0xff, 0xdc, 0xff, 0xff, 0xff, 0x18, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - }; - // ^^^^ - // this definitely wont reach the final commit lmao - // Nope it will for now -Pixie - if (!ApexLockIcon) - { - bool ret = LoadTextureFromByteArray(lockedserver, ApexLockIconWidth, ApexLockIconHeight, &ApexLockIcon); // Load texture from byte array. - } - - ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.00f, 0.00f, 0.00f, 0.00f)); // Override the style color for child bg. - ImGui::BeginChild("##HiddenServersConnectModal_IconParent", ImVec2(ApexLockIconWidth, ApexLockIconHeight)); - { - ImGui::Image(ApexLockIcon, ImVec2(ApexLockIconWidth, ApexLockIconHeight)); // Display texture. - } - ImGui::EndChild(); - ImGui::PopStyleColor(); // Pop the override for the child bg. - - ImGui::SameLine(); - - ImGui::Text("Enter the following details to continue"); - - ImGui::PushItemWidth(ImGui::GetWindowContentRegionWidth()); // Override item width. - ImGui::InputTextWithHint("##HiddenServersConnectModal_TokenInput", "Token", &HiddenServerToken); - ImGui::PopItemWidth(); // Pop item width. - - ImGui::Dummy(ImVec2(ImGui::GetWindowContentRegionWidth(), 19.f)); // Place a dummy, basically making space inserting a blank element. - - ImGui::TextColored(HiddenServerMessageColor, HiddenServerRequestMessage.c_str()); - - ImGui::Separator(); - - if (ImGui::Button("Connect##HiddenServersConnectModal_ConnectButton", ImVec2(ImGui::GetWindowContentRegionWidth() / 2, 24))) - { - HiddenServerRequestMessage = ""; - ServerListing server; - bool result = r5net->GetServerByToken(server, HiddenServerRequestMessage, HiddenServerToken); // Send token connect request. - if (!server.name.empty()) - { - ConnectToServer(server.ip, server.port, server.netchanEncryptionKey); // Connect to the server - HiddenServerRequestMessage = "Found Server: " + server.name; - HiddenServerMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f); - ImGui::CloseCurrentPopup(); - } - else - { - HiddenServerRequestMessage = "Error: " + HiddenServerRequestMessage; - HiddenServerMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); - } - } - - ImGui::SameLine(); - - if (ImGui::Button("Close##HiddenServersConnectModal_CloseButton", ImVec2(ImGui::GetWindowContentRegionWidth() / 2, 24))) - { - ImGui::CloseCurrentPopup(); - } - - ImGui::EndPopup(); - } -} - -void CCompanion::HostServerSection() -{ - static std::string ServerNameErr = ""; - static std::string ServerMap = std::string(); - - ImGui::InputTextWithHint("##ServerHost_ServerName", "Server Name (Required)", &MyServer.name); - ImGui::Spacing(); - - if (ImGui::BeginCombo("Playlist##ServerHost_PlaylistBox", MyServer.playlist.c_str())) - { - for (auto& item : GameGlobals::allPlaylists) - { - if (ImGui::Selectable(item.c_str(), item == MyServer.playlist)) - { - MyServer.playlist = item; - } - } - ImGui::EndCombo(); - } - - if (ImGui::BeginCombo("Map##ServerHost_MapListBox", MyServer.map.c_str())) - { - for (auto& item : MapsList) - { - if (ImGui::Selectable(item.c_str(), item == MyServer.map)) - { - MyServer.map = item; - ServerMap = item; - for (auto it = mapArray.begin(); it != mapArray.end(); ++it) - { - if (it->second.compare(MyServer.map) == NULL) - ServerMap = it->first; - } - } - } - ImGui::EndCombo(); - } - - // ImGui::Checkbox("Start as Dedicated Server (HACK)##ServerHost_DediCheckbox", &StartAsDedi); - ImGui::Checkbox("Load Global Ban List##ServerHost_CheckCompBanDBCheckbox", &g_CheckCompBanDB); - - ImGui::Spacing(); - ImGui::SameLine(); - ImGui::Text("Server Visiblity"); - - if (ImGui::SameLine(); ImGui::RadioButton("Offline##ServerHost_ServerChoice1", ServerVisibility == EServerVisibility::Offline)) - { - ServerVisibility = EServerVisibility::Offline; - } - if (ImGui::SameLine(); ImGui::RadioButton("Hidden##ServerHost_ServerChoice2", ServerVisibility == EServerVisibility::Hidden)) - { - ServerVisibility = EServerVisibility::Hidden; - } - if (ImGui::SameLine(); ImGui::RadioButton("Public##ServerHost_ServerChoice2", ServerVisibility == EServerVisibility::Public)) - { - ServerVisibility = EServerVisibility::Public; - } - - ImGui::Spacing(); - ImGui::Separator(); - - if (!GameGlobals::HostState->m_bActiveGame) - { - if (ImGui::Button("Start Server##ServerHost_StartServerButton", ImVec2(ImGui::GetWindowSize().x, 32))) - { - if (!MyServer.name.empty() && !MyServer.playlist.empty() && !ServerMap.empty()) - { - spdlog::debug("[+CCompanion+] Starting Server with name {}, map {} and playlist {}..\n", MyServer.name, ServerMap, MyServer.playlist); - ServerNameErr = std::string(); - UpdateHostingStatus(); - - /* - * Playlist gets parsed in two instances, first in LoadPlaylist all the neccessary values. - * Then when you would normally call launchplaylist which calls StartPlaylist it would cmd call mp_gamemode which parses the gamemode specific part of the playlist.. - */ - addr_LoadPlaylist(MyServer.playlist.c_str()); - std::stringstream cgmd; - cgmd << "mp_gamemode " << MyServer.playlist; - ProcessCommand(cgmd.str().c_str()); - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Avoid race condition. - - std::stringstream cmd; - cmd << "map " << ServerMap; - ProcessCommand(cmd.str().c_str()); - } - else - { - ServerNameErr = "No Server Name assigned / Playlist assigned / Map didn't apply properly."; - } - } - } - - if (ImGui::Button("Force Start##ServerHost_ForceStart", ImVec2(ImGui::GetWindowSize().x, 32))) - { - ServerNameErr = std::string(); - if (!ServerMap.empty()) - { - strncpy_s(GameGlobals::HostState->m_levelName, ServerMap.c_str(), 64); // Copy new map into hoststate levelname. 64 is size of m_levelname. - GameGlobals::HostState->m_iNextState = HostStates_t::HS_NEW_GAME; // Force CHostState::FrameUpdate to start a server. - } - else - { - ServerNameErr = "Failed to force start. Map didn't apply properly."; - } - } - - ImGui::TextColored(ImVec4(1.00f, 0.00f, 0.00f, 1.00f), ServerNameErr.c_str()); - ImGui::TextColored(HostRequestMessageColor, HostRequestMessage.c_str()); - if (!HostToken.empty()) - { - ImGui::InputText("##ServerHost_HostToken", &HostToken, ImGuiInputTextFlags_ReadOnly); - } - - if (GameGlobals::HostState->m_bActiveGame) - { - if (ImGui::Button("Reload Scripts##ServerHost_ReloadServerButton", ImVec2(ImGui::GetWindowSize().x, 32))) - { - spdlog::debug("[+CCompanion+] Reloading scripts..\n"); - ProcessCommand("reparse_weapons"); - ProcessCommand("reload"); - } - - if (ImGui::Button("Change Level##ServerHost_ChangeLevel", ImVec2(ImGui::GetWindowSize().x, 32))) - { - if (!ServerMap.empty()) - { - spdlog::debug("[+CCompanion+] Changing level to {}..\n", ServerMap); - strncpy_s(GameGlobals::HostState->m_levelName, ServerMap.c_str(), 64); // Copy new map into hoststate levelname. 64 is size of m_levelname. - GameGlobals::HostState->m_iNextState = HostStates_t::HS_CHANGE_LEVEL_MP; // Force CHostState::FrameUpdate to change the level. - } - else - { - ServerNameErr = "Failed to change level. Map didn't apply properly."; - } - } - - if (ImGui::Button("Stop Server##ServerHost_StopServerButton", ImVec2(ImGui::GetWindowSize().x, 32))) - { - spdlog::debug("[+CCompanion+] Stopping server..\n"); - ProcessCommand("LeaveMatch"); // Use script callback instead. - GameGlobals::HostState->m_iNextState = HostStates_t::HS_GAME_SHUTDOWN; // Force CHostState::FrameUpdate to shutdown the server for dedicated. - } - } - else - { - if (ImGui::Button("Reload Playlist from Disk##ServerHost_ReloadPlaylist", ImVec2(ImGui::GetWindowSize().x, 32))) - { - spdlog::debug("[+CCompanion+] Reloading playlist..\n"); - addr_downloadPlaylists_Callback(); - GameGlobals::InitPlaylist(); // Re-Init playlist. - } - } -} - -void CCompanion::SettingsSection() -{ - // Matchmaking Server String - ImGui::InputTextWithHint("##MatchmakingServerString", "Matchmaking Server String", &MatchmakingServerStringBuffer); - if (ImGui::Button("Update Settings")) - { - if (r5net) - { - delete r5net; - r5net = new R5Net::Client(MatchmakingServerStringBuffer); - } - } - // Encryption Key - if (ImGui::Button("Regenerate Encryption Key##SettingsSection_RegenEncKey")) - { - RegenerateEncryptionKey(); - } - ImGui::InputText("Encryption Key##SettingsSection_EncKey", addr_NetChan_EncKey, ImGuiInputTextFlags_ReadOnly); -} - -void CCompanion::Draw(const char* title) -{ - if (!ThemeSet) - { - SetStyleVar(); - ThemeSet = true; - } - - ImGui::SetNextWindowSize(ImVec2(840, 600), ImGuiCond_FirstUseEver); - ImGui::SetWindowPos(ImVec2(-500, 50), ImGuiCond_FirstUseEver); - - ImGui::Begin(title, NULL, ImGuiWindowFlags_NoScrollbar); - { - CompMenu(); - - switch (CurrentSection) - { - case ESection::ServerBrowser: - ServerBrowserSection(); - break; - case ESection::HostServer: - HostServerSection(); - break; - case ESection::Settings: - SettingsSection(); - break; - default: - break; - } - } - ImGui::End(); -} - -void CCompanion::ProcessCommand(const char* command_line) -{ - spdlog::debug("[+CCompanion+] Processing command: {} creating thread now.\n", command_line); - std::thread t(&CCompanion::ExecCommand, this, command_line); - spdlog::debug("[+CCompanion+] Thread created.\n"); - t.detach(); - spdlog::debug("[+CCompanion+] Detached from Thread.\n"); - - // HACK: This is to avoid a race condition. - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - - // TODO: - //ScrollToBottom = true; -} - -void CCompanion::ExecCommand(const char* command_line) -{ - spdlog::debug("[+CCompanion+] Executing command {}\n", command_line); - addr_CommandExecute(NULL, command_line); -} - -void CCompanion::ConnectToServer(const std::string ip, const std::string port, const std::string encKey) -{ - if (!encKey.empty()) - { - ChangeEncryptionKeyTo(encKey); - } - - std::stringstream cmd; - cmd << "connect " << ip << ":" << port; - ProcessCommand(cmd.str().c_str()); -} - -void CCompanion::ConnectToServer(const std::string connString, const std::string encKey) -{ - if (!encKey.empty()) - { - ChangeEncryptionKeyTo(encKey); - } - - std::stringstream cmd; - cmd << "connect " << connString; - ProcessCommand(cmd.str().c_str()); -} - -void CCompanion::RegenerateEncryptionKey() -{ - spdlog::debug("[+CCompanion+] Regenerating Encryption Key..\n"); - BCRYPT_ALG_HANDLE hAlgorithm; - if (BCryptOpenAlgorithmProvider(&hAlgorithm, L"RNG", 0, 0) < 0) - { - spdlog::critical("[+CCompanion+] Failed to open rng algorithm\n"); - } - unsigned char pBuffer[0x10u]; - if (BCryptGenRandom(hAlgorithm, pBuffer, 0x10u, 0) < 0) - { - spdlog::critical("[+CCompanion+] Failed to generate random data\n"); - } - std::string fin = std::string(); - - for (int i = 0; i < 0x10u; i++) - { - fin += pBuffer[i]; - } - - fin = base64_encode(fin); - - addr_NetChan_SetEncKey(addr_NetChan_EncKeyPtr.GetPtr(), fin.c_str()); -} - -void CCompanion::ChangeEncryptionKeyTo(const std::string str) -{ - addr_NetChan_SetEncKey(addr_NetChan_EncKeyPtr.GetPtr(), str.c_str()); -} \ No newline at end of file diff --git a/r5dev/src/CGameConsole.cpp b/r5dev/src/CGameConsole.cpp deleted file mode 100644 index 1ae16a13..00000000 --- a/r5dev/src/CGameConsole.cpp +++ /dev/null @@ -1,403 +0,0 @@ -#include "pch.h" -#include "hooks.h" -#include "id3dx.h" -#include "console.h" -#include "patterns.h" -#include "gameclasses.h" -#include "CGameConsole.h" - -CGameConsole* g_GameConsole = nullptr; -ImVector Items; - -/*----------------------------------------------------------------------------- - * _cgameconsole.cpp - *-----------------------------------------------------------------------------*/ - -CGameConsole::CGameConsole() -{ - ClearLog(); - memset(InputBuf, 0, sizeof(InputBuf)); - - HistoryPos = -1; - AutoScroll = true; - ScrollToBottom = false; - ThemeSet = false; - - Commands.push_back("HELP"); - Commands.push_back("HISTORY"); - Commands.push_back("CLEAR"); - Commands.push_back("CLASSIFY"); - - AddLog("[DEBUG] THREAD ID: %ld\n", g_dThreadId); -} - -CGameConsole::~CGameConsole() -{ - ClearLog(); - History.clear(); -} - -/////////////////////////////////////////////////////////////////////////// -// Draw -void CGameConsole::Draw(const char* title) -{ - bool CopyToClipboard = false; - static auto cgameconsoleConfig = &g_GuiConfig.CGameConsoleConfig; - static auto ccompanionConfig = &g_GuiConfig.CCompanionConfig; - - if (!ThemeSet) - { - SetStyleVar(); - ThemeSet = true; - } - - if (cgameconsoleConfig->autoClear && Items.Size > cgameconsoleConfig->autoClearLimit) // Check if Auto-Clear is enabled and if its above our limit. If yes then clear. - { - ClearLog(); - History.clear(); - } - - //ImGui::ShowStyleEditor(); - - ImGui::SetNextWindowSize(ImVec2(1000, 600), ImGuiCond_FirstUseEver); - ImGui::SetWindowPos(ImVec2(-1000, 50), ImGuiCond_FirstUseEver); - - ImGui::Begin(title, NULL); // ImGui::Begin should never fail, if it does we got another problem. - { - // Reserve enough left-over height and width for 1 separator + 1 input text - const float FooterHeightToReserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); - const float FooterWidthtoReserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetWindowWidth(); - - /////////////////////////////////////////////////////////////////////// - ImGui::Separator(); - if (ImGui::BeginPopup("Options")) - { - if (ImGui::Checkbox("Print to Cmd", &cgameconsoleConfig->printCmd)) - g_GuiConfig.Save(); - - ImGui::Checkbox("Auto-Scroll", &AutoScroll); - - if (ImGui::Checkbox("Auto-Clear", &cgameconsoleConfig->autoClear)) - g_GuiConfig.Save(); - - ImGui::SameLine(); - - ImGui::PushItemWidth(100); - if (ImGui::InputInt("Auto Clear Limit##AutoClearAfterCertainIndexCGameConsole", &cgameconsoleConfig->autoClearLimit)) - g_GuiConfig.Save(); - - ImGui::PopItemWidth(); - - if (ImGui::SmallButton("Clear")) - ClearLog(); - - ImGui::SameLine(); - CopyToClipboard = ImGui::SmallButton("Copy"); - ImGui::Text("CG Hotkey:"); - ImGui::SameLine(); - if (ImGui::Hotkey("##OpenCGameConsoleBind1", &cgameconsoleConfig->bind1, ImVec2(80, 80))) - g_GuiConfig.Save(); - - ImGui::Text("CC Hotkey:"); - ImGui::SameLine(); - - if (ImGui::Hotkey("##OpenCCompanionBind1", &ccompanionConfig->bind1, ImVec2(80, 80))) - g_GuiConfig.Save(); - - ImGui::EndPopup(); - } - if (ImGui::Button("Options")) - { - ImGui::OpenPopup("Options"); - } - ImGui::SameLine(); - if (ImGui::BeginPopup("Tools")) - { - Hooks::bToggledDevFlags ? ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0, 255, 0, 255)) : ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(255, 0, 0, 255)); - if (ImGui::SmallButton("Developer Mode")) - { - Hooks::ToggleDevCommands(); - AddLog("+--------------------------------------------------------+\n"); - AddLog("|>>>>>>>>>>>>>>| DEVONLY COMMANDS TOGGLED |<<<<<<<<<<<<<<|\n"); - AddLog("+--------------------------------------------------------+\n"); - ProcessCommand("exec autoexec"); - } - ImGui::PopStyleColor(); // Pop color override. - Hooks::bToggledNetTrace ? ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0, 255, 0, 255)) : ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(255, 0, 0, 255)); - if (ImGui::SmallButton("Netchannel Trace")) - { - Hooks::ToggleNetTrace(); - AddLog("+--------------------------------------------------------+\n"); - AddLog("|>>>>>>>>>>>>>>| NETCHANNEL TRACE TOGGLED |<<<<<<<<<<<<<<|\n"); - AddLog("+--------------------------------------------------------+\n"); - ProcessCommand("exec netchan"); - } - ImGui::PopStyleColor(); // Pop color override. - if (ImGui::SmallButton("Commands/Convars to Console")) - { - if (GameGlobals::Cvar) - { - for (auto map : GameGlobals::Cvar->DumpToMap()) - { - AddLog("%s\n", map.first.c_str()); - } - } - } - ImGui::EndPopup(); - } - if (ImGui::Button("Tools")) - { - ImGui::OpenPopup("Tools"); - } - ImGui::SameLine(); - Filter.Draw("Filter [\"-incl,-excl\"] [\"error\"]", FooterWidthtoReserve - 500); - ImGui::Separator(); - - /////////////////////////////////////////////////////////////////////// - ImGui::BeginChild("ScrollingRegion", ImVec2(0, -FooterHeightToReserve), true, ImGuiWindowFlags_AlwaysVerticalScrollbar); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 4.f, 6.f }); - if (CopyToClipboard) - { - ImGui::LogToClipboard(); - } - - for (int i = 0; i < Items.Size; i++) - { - const char* item = Items[i]; - if (!Filter.PassFilter(item)) - continue; - - /////////////////////////////////////////////////////////////////// - ImVec4 color; - bool has_color = false; - - /////////////////////////////////////////////////////////////////// - // General - if (strstr(item, "[INFO]")) { color = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); has_color = true; } - if (strstr(item, "[ERROR]")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } - if (strstr(item, "[DEBUG]")) { color = ImVec4(0.00f, 0.30f, 1.00f, 1.00f); has_color = true; } - if (strstr(item, "[WARNING]")) { color = ImVec4(1.00f, 1.00f, 0.00f, 0.80f); has_color = true; } - if (strncmp(item, "# ", 2) == 0) { color = ImVec4(1.00f, 0.80f, 0.60f, 1.00f); has_color = true; } - - /////////////////////////////////////////////////////////////////// - // Virtual machines - if (strstr(item, "Script(S):")) { color = ImVec4(0.59f, 0.58f, 0.73f, 1.00f); has_color = true; } - if (strstr(item, "Script(C):")) { color = ImVec4(0.59f, 0.58f, 0.63f, 1.00f); has_color = true; } - if (strstr(item, "Script(U):")) { color = ImVec4(0.59f, 0.48f, 0.53f, 1.00f); has_color = true; } - - if (strstr(item, "Script(S) Warning:")) { color = ImVec4(0.80f, 0.80f, 0.73f, 1.00f); has_color = true; } - if (strstr(item, "Script(C) Warning:")) { color = ImVec4(0.80f, 0.80f, 0.63f, 1.00f); has_color = true; } - if (strstr(item, "Script(U) Warning:")) { color = ImVec4(0.80f, 0.80f, 0.53f, 1.00f); has_color = true; } - - /////////////////////////////////////////////////////////////////// - // Callbacks - //if (strstr(item, "CodeCallback_")) { color = ImVec4(0.00f, 0.30f, 1.00f, 1.00f); has_color = true; } - - /////////////////////////////////////////////////////////////////// - // Script errors - if (strstr(item, ".gnut")) { color = ImVec4(1.00f, 1.00f, 1.00f, 0.60f); has_color = true; } - if (strstr(item, ".nut")) { color = ImVec4(1.00f, 1.00f, 1.00f, 0.60f); has_color = true; } - if (strstr(item, "[CLIENT]")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } - if (strstr(item, "[SERVER]")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } - if (strstr(item, "[UI]")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } - if (strstr(item, "SCRIPT ERROR")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } - if (strstr(item, "SCRIPT COMPILE")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } - if (strstr(item, ".gnut #")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } - if (strstr(item, ".nut #")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } - if (strstr(item, "): -> ")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } - - /////////////////////////////////////////////////////////////////// - // Script debug - if (strstr(item, "CALLSTACK")) { color = ImVec4(1.00f, 1.00f, 0.00f, 0.80f); has_color = true; } - if (strstr(item, "LOCALS")) { color = ImVec4(1.00f, 1.00f, 0.00f, 0.80f); has_color = true; } - if (strstr(item, "*FUNCTION")) { color = ImVec4(1.00f, 1.00f, 0.00f, 0.80f); has_color = true; } - if (strstr(item, "DIAGPRINTS")) { color = ImVec4(1.00f, 1.00f, 0.00f, 0.80f); has_color = true; } - if (strstr(item, " File : ")) { color = ImVec4(0.00f, 0.30f, 1.00f, 1.00f); has_color = true; } - if (strstr(item, "<><>GRX<><>")) { color = ImVec4(0.00f, 0.30f, 1.00f, 1.00f); has_color = true; } - - /////////////////////////////////////////////////////////////////// - // Filters - //if (strstr(item, ") -> ")) { color = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); has_color = true; } - - if (has_color) - { - ImGui::PushStyleColor(ImGuiCol_Text, color); - } - - ImGui::TextWrapped(item); - - if (has_color) - { - ImGui::PopStyleColor(); - } - } - - if (CopyToClipboard) - { - ImGui::LogFinish(); - } - - if (ScrollToBottom || (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())) - { - ImGui::SetScrollHereY(1.0f); - } - ScrollToBottom = false; - - /////////////////////////////////////////////////////////////////////// - ImGui::PopStyleVar(); - ImGui::EndChild(); - ImGui::Separator(); - - /////////////////////////////////////////////////////////////////////// - // Console - bool ShouldReclaimFocus = false; - ImGui::PushItemWidth(FooterWidthtoReserve - 80); - if (ImGui::IsWindowAppearing()) { ImGui::SetKeyboardFocusHere(); } - ImGuiInputTextFlags input_text_flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory; - - std::function CommandExec = [&](char* InputBuf) - { - char* s = InputBuf; - const char* replace = ""; - if (strstr(InputBuf, "`")) - { - strcpy_s(s, sizeof(replace), replace); - } - - Strtrim(s); - - if (s[0]) - { - ProcessCommand(s); - } - - strcpy_s(s, sizeof(replace), replace); - ShouldReclaimFocus = true; - }; - - if (ImGui::InputText("##input", InputBuf, IM_ARRAYSIZE(InputBuf), input_text_flags, &TextEditCallbackStub, (void*)this)) - { - CommandExec(InputBuf); - } - - ImGui::SameLine(); - - if (ImGui::Button("Submit")) - { - CommandExec(InputBuf); - } - - // Auto-focus on window apparition - ImGui::SetItemDefaultFocus(); - - // Auto focus previous widget - if (ShouldReclaimFocus) - { - ImGui::SetKeyboardFocusHere(-1); - } - } - ImGui::End(); -} - -/////////////////////////////////////////////////////////////////////////// -// Exec -void CGameConsole::ProcessCommand(const char* command_line) -{ - std::thread t(&CGameConsole::ExecCommand, this, command_line); - t.detach(); - - // HACK: This is to avoid a race condition. - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - AddLog("# %s\n", command_line); - - HistoryPos = -1; - for (int i = History.Size - 1; i >= 0; i--) - { - if (Stricmp(History[i], command_line) == 0) - { - delete History[i]; - History.erase(History.begin() + i); - break; - } - } - - History.push_back(Strdup(command_line)); - if (Stricmp(command_line, "CLEAR") == 0) - { - ClearLog(); - } - else if (Stricmp(command_line, "HELP") == 0) - { - AddLog("Commands:"); - for (int i = 0; i < Commands.Size; i++) - { - AddLog("- %s", Commands[i]); - } - } - else if (Stricmp(command_line, "HISTORY") == 0) - { - int first = History.Size - 10; - for (int i = first > 0 ? first : 0; i < History.Size; i++) - { - AddLog("%3d: %s\n", i, History[i]); - } - } - - ScrollToBottom = true; -} - -void CGameConsole::ExecCommand(const char* command_line) -{ - addr_CommandExecute(NULL, command_line); -} - -/////////////////////////////////////////////////////////////////////////// -// Edit -int CGameConsole::TextEditCallback(ImGuiInputTextCallbackData* data) -{ - switch (data->EventFlag) - { - case ImGuiInputTextFlags_CallbackCompletion: - { - // Locate beginning of current word - const char* word_end = data->Buf + data->CursorPos; - const char* word_start = word_end; - while (word_start > data->Buf) - { - const char c = word_start[-1]; - if (c == ' ' || c == '\t' || c == ',' || c == ';') - { - break; - } - word_start--; - } - break; - } - case ImGuiInputTextFlags_CallbackHistory: - { - const int prev_history_pos = HistoryPos; - if (data->EventKey == ImGuiKey_UpArrow) - { - if (HistoryPos == -1) { HistoryPos = History.Size - 1; } - else if (HistoryPos > 0) { HistoryPos--; } - } - else if (data->EventKey == ImGuiKey_DownArrow) - { - if (HistoryPos != -1) - { - if (++HistoryPos >= History.Size) - { - HistoryPos = -1; - } - } - } - if (prev_history_pos != HistoryPos) - { - const char* history_str = (HistoryPos >= 0) ? History[HistoryPos] : ""; - data->DeleteChars(0, data->BufTextLen); - data->InsertChars(0, history_str); - } - } - } - return 0; -} \ No newline at end of file diff --git a/r5dev/src/cenginevgui.cpp b/r5dev/src/cenginevgui.cpp deleted file mode 100644 index cec33ebe..00000000 --- a/r5dev/src/cenginevgui.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "pch.h" -#include "hooks.h" -#include "logsystem.h" - -namespace Hooks -{ - CEngineVGui_PaintFn originalCEngineVGui_Paint = nullptr; -} - -int Hooks::CEngineVGui_Paint(void* thisptr, int mode) -{ - int result = originalCEngineVGui_Paint(thisptr, mode); - - static void* pCMatSystemSurface = MemoryAddress(0x14D40B3B0).RCast(); - static auto fnRenderStart = MemoryAddress(0x14053EFC0).RCast(); - static auto fnRenderEnd = MemoryAddress(0x14053F1B0).RCast(); - - if (mode == 1 || mode == 2) // Render in main menu and ingame. - { - fnRenderStart(pCMatSystemSurface); - - g_LogSystem.Update(); - - fnRenderEnd(); - } - - return result; -} \ No newline at end of file diff --git a/r5dev/src/gameclasses.cpp b/r5dev/src/gameclasses.cpp deleted file mode 100644 index 5c4ae395..00000000 --- a/r5dev/src/gameclasses.cpp +++ /dev/null @@ -1,532 +0,0 @@ -#include "pch.h" -#include "gameclasses.h" -#include "id3dx.h" -#include "cgameconsole.h" -#include "squirrel.h" -#include - -// Need this for a re-factor later. -// Interface* interfaces = *reinterpret_cast(0x167F4FA48); - -// for (Interface* current = interfaces; current; current = reinterpret_cast(current->NextInterfacePtr)) -// { -// printf("%s: %p\n", current->InterfaceName, current->InterfacePtr); -// } - -namespace GameGlobals -{ - bool IsInitialized = false; - CHostState* HostState = nullptr; - CInputSystem* InputSystem = nullptr; - CCVar* Cvar = nullptr; - CClient* Client = nullptr; - BanList* BanSystem = new BanList(); - - CKeyValuesSystem* KeyValuesSystem = nullptr; - KeyValues** PlaylistKeyValues = nullptr; - - std::vector allPlaylists = { "none" }; - - namespace CustomCommandVariations - { - void CGameConsole_Callback(const CCommand& cmd) - { - g_bShowConsole = !g_bShowConsole; - } - - void CCompanion_Callback(const CCommand& cmd) - { - g_bShowBrowser = !g_bShowBrowser; - } - - void Kick_Callback(CCommand* cmd) - { - std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); - if (argSize < 2) // Do we atleast have 2 arguments? - return; - - CCommand& cmdReference = *cmd; // Get reference. - const char* firstArg = cmdReference[1]; // Get first arg. - - for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. - { - CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance. - if (!client) - continue; - - if (!client->GetNetChan()) // Netchan valid? - continue; - - void* clientNamePtr = (void**)(((std::uintptr_t)client->GetNetChan()) + 0x1A8D); // Get client name from netchan. - std::string clientName((char*)clientNamePtr, 32); // Get full name. - - if (clientName.empty()) // Empty name? - continue; - - if (strcmp(firstArg, clientName.c_str()) != 0) // Our wanted name? - continue; - - DisconnectClient(client, "Kicked from Server", 0, 1); // Disconnect client. - } - } - - void KickID_Callback(CCommand* cmd) - { - static auto HasOnlyDigits = [](const std::string& string) - { - for (const char& character : string) - { - if (std::isdigit(character) == 0) - return false; - } - return true; - }; - - std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); - if (argSize < 2) // Do we atleast have 2 arguments? - return; - - CCommand& cmdReference = *cmd; // Get reference. - std::string firstArg = cmdReference[1]; // Get first arg. - - try - { - bool onlyDigits = HasOnlyDigits(firstArg); // Only has digits? - for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. - { - CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance. - if (!client) - continue; - - if (!client->GetNetChan()) // Netchan valid? - continue; - - std::string finalIPAddress = "null"; // If this stays null they modified the packet somehow. - MemoryAddress ipAddressField = MemoryAddress(((std::uintptr_t)client->GetNetChan()) + 0x1AC0); // Get client ip from netchan. - if (ipAddressField) - { - std::stringstream ss; - ss << std::to_string(ipAddressField.GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x1).GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x2).GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x3).GetValue()); - - finalIPAddress = ss.str(); - } - - if (onlyDigits) - { - std::int64_t ID = static_cast(std::stoll(firstArg)); - if (ID > MAX_PLAYERS) // Is it a possible originID? - { - std::int64_t originID = client->m_iOriginID; - if (originID != ID) // See if they match. - continue; - } - else // If its not try by userID. - { - std::int64_t clientID = static_cast(client->m_iUserID + 1); // Get UserID + 1. - if (clientID != ID) // See if they match. - continue; - } - - DisconnectClient(client, "Kicked from Server", 0, 1); // Disconnect client. - } - else - { - if (firstArg.compare(finalIPAddress) != NULL) // Do the string equal? - continue; - - DisconnectClient(client, "Kicked from Server", 0, 1); // Disconnect client. - } - } - } - catch (std::exception& e) - { - spdlog::critical("Kick UID asked for a userID or originID :( You can get the userid with the 'status' command. Error: {}\n", e.what()); - g_GameConsole->AddLog("Kick UID asked for a userID or originID :( You can get the userid with the 'status' command. Error: %s", e.what()); - return; - } - } - - void Unban_Callback(CCommand* cmd) - { - static auto HasOnlyDigits = [](const std::string& string) - { - for (const char& character : string) - { - if (std::isdigit(character) == 0) - return false; - } - return true; - }; - - std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); - if (argSize < 2) // Do we atleast have 2 arguments? - return; - - CCommand& cmdReference = *cmd; // Get reference. - - try - { - const char* firstArg = cmdReference[1]; - if (HasOnlyDigits(firstArg)) // Check if we have an ip address or origin ID. - { - GameGlobals::BanSystem->DeleteEntry("noIP", std::stoll(firstArg)); // Delete ban entry. - GameGlobals::BanSystem->Save(); // Save modified vector to file. - } - else - { - GameGlobals::BanSystem->DeleteEntry(firstArg, 1); // Delete ban entry. - GameGlobals::BanSystem->Save(); // Save modified vector to file. - } - } - catch (std::exception& e) - { - spdlog::critical("Unban Error: {}\n", e.what()); - g_GameConsole->AddLog("Unban Error: %s", e.what()); - return; - } - } - - void ReloadBanList_Callback(CCommand* cmd) - { - GameGlobals::BanSystem->Load(); // Reload banlist. - } - - void Ban_Callback(CCommand* cmd) - { - std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); - if (argSize < 2) // Do we atleast have 2 arguments? - return; - - CCommand& cmdReference = *cmd; // Get reference. - const char* firstArg = cmdReference[1]; // Get first arg. - - for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. - { - CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance. - if (!client) - continue; - - if (!client->GetNetChan()) // Netchan valid? - continue; - - void* clientNamePtr = (void**)(((std::uintptr_t)client->GetNetChan()) + 0x1A8D); // Get client name from netchan. - std::string clientName((char*)clientNamePtr, 32); // Get full name. - - if (clientName.empty()) // Empty name? - continue; - - if (strcmp(firstArg, clientName.c_str()) != 0) // Our wanted name? - continue; - - std::string finalIPAddress = "null"; // If this stays null they modified the packet somehow. - MemoryAddress ipAddressField = MemoryAddress(((std::uintptr_t)client->GetNetChan()) + 0x1AC0); // Get client ip from netchan. - if (ipAddressField && ipAddressField.GetValue() != 0x0) - { - std::stringstream ss; - ss << std::to_string(ipAddressField.GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x1).GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x2).GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x3).GetValue()); - - finalIPAddress = ss.str(); - } - - GameGlobals::BanSystem->AddEntry(finalIPAddress, client->m_iOriginID); // Add ban entry. - GameGlobals::BanSystem->Save(); // Save ban list. - DisconnectClient(client, "Banned from Server", 0, 1); // Disconnect client. - } - } - - void BanID_Callback(CCommand* cmd) - { - static auto HasOnlyDigits = [](const std::string& string) - { - for (const char& character : string) - { - if (std::isdigit(character) == 0) - return false; - } - return true; - }; - - std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); - if (argSize < 2) // Do we atleast have 2 arguments? - return; - - CCommand& cmdReference = *cmd; // Get reference. - std::string firstArg = cmdReference[1]; - - try - { - bool onlyDigits = HasOnlyDigits(firstArg); // Only has digits? - for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. - { - CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance. - if (!client) - continue; - - if (!client->GetNetChan()) // Netchan valid? - continue; - - std::string finalIPAddress = "null"; // If this stays null they modified the packet somehow. - MemoryAddress ipAddressField = MemoryAddress(((std::uintptr_t)client->GetNetChan()) + 0x1AC0); // Get client ip from netchan. - if (ipAddressField) - { - std::stringstream ss; - ss << std::to_string(ipAddressField.GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x1).GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x2).GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x3).GetValue()); - - finalIPAddress = ss.str(); - } - - if (onlyDigits) - { - std::int64_t ID = static_cast(std::stoll(firstArg)); - if (ID > MAX_PLAYERS) // Is it a possible originID? - { - std::int64_t originID = client->m_iOriginID; - if (originID != ID) // See if they match. - continue; - } - else // If its not try by userID. - { - std::int64_t clientID = static_cast(client->m_iUserID + 1); // Get UserID + 1. - if (clientID != ID) // See if they match. - continue; - } - - GameGlobals::BanSystem->AddEntry(finalIPAddress, client->m_iOriginID); // Add ban entry. - GameGlobals::BanSystem->Save(); // Save ban list. - DisconnectClient(client, "Banned from Server", 0, 1); // Disconnect client. - } - else - { - if (firstArg.compare(finalIPAddress) != NULL) // Do the string equal? - continue; - - GameGlobals::BanSystem->AddEntry(finalIPAddress, client->m_iOriginID); // Add ban entry. - GameGlobals::BanSystem->Save(); // Save ban list. - DisconnectClient(client, "Banned from Server", 0, 1); // Disconnect client. - } - } - } - catch (std::exception& e) - { - spdlog::critical("Banid Error: {}\n", e.what()); - g_GameConsole->AddLog("Banid Error: %s", e.what()); - return; - } - } - } - - void NullHostNames() - { - spdlog::debug("Nulling host names..\n"); - const char* hostnameArray[] = - { - "pin_telemetry_hostname", - "assetdownloads_hostname", - "users_hostname", - "persistence_hostname", - "speechtotexttoken_hostname", - "communities_hostname", - "persistenceDef_hostname", - "party_hostname", - "speechtotext_hostname", - "serverReports_hostname", - "subscription_hostname", - "steamlink_hostname", - "staticfile_hostname", - "matchmaking_hostname", - "skill_hostname", - "publication_hostname", - "stats_hostname" - }; - - for (int i = 0; i < 17; i++) - { - const char* name = hostnameArray[i]; - Cvar->FindVar(name)->m_pzsCurrentValue = "0.0.0.0"; - } - } - - void InitGameGlobals() - { - spdlog::debug("Initializing Game Globals..\n"); - HostState = reinterpret_cast(0x141736120); // Get CHostState from memory. - InputSystem = *reinterpret_cast(0x14D40B380); // Get IInputSystem from memory. - Cvar = *reinterpret_cast(0x14D40B348); // Get CCVar from memory. - KeyValuesSystem = reinterpret_cast(0x141F105C0); // Get CKeyValuesSystem from memory. - PlaylistKeyValues = reinterpret_cast(0x16705B980); // Get the KeyValue for the playlist file. - Client = reinterpret_cast(0x16073B200); - - NullHostNames(); // Null all hostnames. - InitAllCommandVariations(); // Initialize our custom ConVars. - *(char*)addr_m_bRestrictServerCommands = true; // Restrict commands. - void* disconnect = Cvar->FindCommand("disconnect"); - *(std::int32_t*)((std::uintptr_t)disconnect + 0x38) |= FCVAR_SERVER_CAN_EXECUTE; // Make sure server is not restricted to this. - - std::thread t1(InitPlaylist); // Start thread to grab playlists. - t1.detach(); // Detach thread from current one. - - IsInitialized = true; - } - - void InitPlaylist() - { - spdlog::debug("Parsing Playlist..\n"); - while (true) - { - if ((*PlaylistKeyValues)) - { - KeyValues* playlists = (*PlaylistKeyValues)->FindKey("Playlists", false); // Find playlists key. - if (playlists) - { - allPlaylists.clear(); - for (KeyValues* dat = playlists->m_pSub; dat != nullptr; dat = dat->m_pPeer) // Parse through all sub keys. - { - allPlaylists.push_back(dat->GetName()); // Get all playlist names. - } - - break; // Break if playlist got filled. - } - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - } - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - } - } - - void InitAllCommandVariations() - { - spdlog::debug("Initializing all Custom ConVars and Commands..\n"); - void* CGameConsoleConCommand = CreateCustomConCommand("cgameconsole", "Opens the R5 Reloaded Console.", 0, CustomCommandVariations::CGameConsole_Callback, nullptr); - void* CCompanionConCommand = CreateCustomConCommand("ccompanion", "Opens the R5 Reloaded Server Browser.", 0, CustomCommandVariations::CCompanion_Callback, nullptr); - void* KickConCommand = CreateCustomConCommand("kick", "Kick a client from the Server via name. | Usage: kick (name).", 0, CustomCommandVariations::Kick_Callback, nullptr); - void* KickIDConCommand = CreateCustomConCommand("kickid", "Kick a client from the Server via userID or originID | Usage: kickid (originID/userID)", 0, CustomCommandVariations::KickID_Callback, nullptr); - void* UnbanConCommand = CreateCustomConCommand("unban", "Unbans a client from the Server via IP or originID | Usage: unban (originID/ipAddress)", 0, CustomCommandVariations::Unban_Callback, nullptr); - void* ReloadBanListConCommand = CreateCustomConCommand("reloadbanlist", "Reloads the ban list from disk.", 0, CustomCommandVariations::ReloadBanList_Callback, nullptr); - void* BanConCommand = CreateCustomConCommand("ban", "Bans a client from the Server via name. | Usage: ban (name)", 0, CustomCommandVariations::Ban_Callback, nullptr); - void* BanIDConCommand = CreateCustomConCommand("banid", "Bans a client from the Server via originID, userID or IP | Usage: banid (originID/ipAddress/userID)", 0, CustomCommandVariations::BanID_Callback, nullptr); - - ConVar* DrawConsoleOverlayConVar = CreateCustomConVar("cl_drawconsoleoverlay", "0", 0, "Draw the console overlay at the top of the screen", false, 0.f, false, 0.f, nullptr, nullptr); - ConVar* ConsoleOverlayLinesConVar = CreateCustomConVar("cl_consoleoverlay_lines", "3", 0, "Number of lines of console output to draw", false, 1.f, false, 50.f, nullptr, nullptr); - } - - void* CreateCustomConCommand(const char* name, const char* helpString, int flags, void* callback, void* callbackAfterExecution) - { - static MemoryAddress ConCommandVtable = MemoryAddress(0x14136BD70); - static MemoryAddress NullSub = MemoryAddress(0x1401B3280); - static MemoryAddress CallbackCompletion = MemoryAddress(0x1401E3990); - static MemoryAddress RegisterConCommand = MemoryAddress(0x14046F470); - - void* command = reinterpret_cast(addr_MemAlloc_Wrapper(0x68)); // Allocate new memory with StdMemAlloc else we crash. - memset(command, 0, 0x68); // Set all to null. - std::uintptr_t commandPtr = reinterpret_cast(command); // To ptr. - - *(void**)commandPtr = ConCommandVtable.RCast(); // 0x0 to ConCommand vtable. - *(const char**)(commandPtr + 0x18) = name; // 0x18 to ConCommand Name. - *(const char**)(commandPtr + 0x20) = helpString; // 0x20 to ConCommand help string. - *(std::int32_t*)(commandPtr + 0x38) = flags; // 0x38 to ConCommand Flags. - *(void**)(commandPtr + 0x40) = NullSub.RCast(); // 0x40 Nullsub since every concommand has it. - *(void**)(commandPtr + 0x50) = callback; // 0x50 has function callback. - *(std::int32_t*)(commandPtr + 0x60) = 2; // 0x60 Set to use callback and newcommand callback. - - if (callbackAfterExecution) // Do we wanna have a callback after execution? - { - *(void**)(commandPtr + 0x58) = callbackAfterExecution; // 0x58 to our callback after execution. - } - else - { - *(void**)(commandPtr + 0x58) = CallbackCompletion.RCast(); // 0x58 nullsub. - } - - RegisterConCommand.RCast()((void*)commandPtr); // Register command in ConVarAccessor. - - return command; - } - - ConVar* CreateCustomConVar(const char* name, const char* defaultValue, int flags, const char* helpString, bool bMin, float fMin, bool bMax, float fMax, void* callback, void* unk) - { - static MemoryAddress ConVarVtable = MemoryAddress(0x14046FB50).Offset(0x12).ResolveRelativeAddress(); // Get vtable ptr for ConVar table. - static MemoryAddress ICvarVtable = MemoryAddress(0x14046FB50).Offset(0x29).ResolveRelativeAddress(); // Get vtable ptr for ICvar table. - static MemoryAddress CreateConVar = MemoryAddress(0x140470540); // Get CreateConvar address. - - ConVar* allocatedConvar = reinterpret_cast(addr_MemAlloc_Wrapper(0xA0)); // Allocate new memory with StdMemAlloc else we crash. - memset(allocatedConvar, 0, 0xA0); // Set all to null. - std::uintptr_t cvarPtr = reinterpret_cast(allocatedConvar); // To ptr. - - *(void**)(cvarPtr + 0x40) = ICvarVtable.RCast(); // 0x40 to ICvar table. - *(void**)cvarPtr = ConVarVtable.RCast(); // 0x0 to ConVar vtable. - - CreateConVar.RCast() // Call to create ConVar. - (allocatedConvar, name, defaultValue, flags, helpString, bMin, fMin, bMax, fMax, callback, unk); - - return allocatedConvar; // Return allocated ConVar. - } - - void Script_RegisterFunction(void* sqvm, const char* name, const char* helpString, const char* retValType, const char* argTypes, void* funcPtr) - { - static MemoryAddress Script_RegisterFunction = MemoryAddress(0x141056040); - - SQFuncRegistration* func = new SQFuncRegistration(); - - func->scriptName = name; - func->nativeName = name; - func->helpString = helpString; - func->retValType = retValType; - func->argTypes = argTypes; - func->funcPtr = funcPtr; - - Script_RegisterFunction.RCast()(sqvm, func, 1); - } - - int Script_NativeTest(void* sqvm) - { - // function code goes here - - return 1; - } - - void RegisterUIScriptFunctions(void* sqvm) - { - //Script_RegisterFunction(sqvm, "UINativeTest", "native ui function", "void", "", &Script_NativeTest); - } - - void RegisterClientScriptFunctions(void* sqvm) - { - //Script_RegisterFunction(sqvm, "ClientNativeTest", "native client function", "void", "", &Script_NativeTest); - } - - void RegisterServerScriptFunctions(void* sqvm) - { - //Script_RegisterFunction(sqvm, "ServerNativeTest", "native server function", "void", "", &Script_NativeTest); - } - - void DisconnectClient(CClient* client, const char* reason, unsigned __int8 unk1, char unk2) - { - if (!client) // Client valid? - return; - - if (std::strlen(reason) == NULL) // Is reason null? - return; - - if (!client->GetNetChan()) - return; - - addr_NetChan_Shutdown(client->GetNetChan(), reason, unk1, unk2); // Shutdown netchan. - client->GetNetChan() = nullptr; // Null netchan. - MemoryAddress(0x140302FD0).RCast()(client); // Reset CClient instance for client. - } -} - -#pragma region KeyValues -const char* KeyValues::GetName() -{ - return GameGlobals::KeyValuesSystem->GetStringForSymbol(MAKE_3_BYTES_FROM_1_AND_2(m_iKeyNameCaseSensitive, m_iKeyNameCaseSensitive2)); -} -#pragma endregion diff --git a/r5dev/src/gui_utility.cpp b/r5dev/src/gui_utility.cpp deleted file mode 100644 index e991c749..00000000 --- a/r5dev/src/gui_utility.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "pch.h" -#include "gui_utility.h" - -GuiConfig g_GuiConfig; - -/*----------------------------------------------------------------------------- - * _gui_utility.cpp - *-----------------------------------------------------------------------------*/ - -int Stricmp(const char* s1, const char* s2) -{ - int d; - while ((d = toupper(*s2) - toupper(*s1)) == 0 && *s1) - { - s1++; s2++; - } - return d; -} - -int Strnicmp(const char* s1, const char* s2, int n) -{ - int d = 0; while (n > 0 && (d = toupper(*s2) - toupper(*s1)) == 0 && *s1) - { - s1++; s2++; n--; - } - return d; -} - -char* Strdup(const char* s) -{ - IM_ASSERT(s); size_t len = strlen(s) + 1; void* buf = malloc(len); IM_ASSERT(buf); if (buf != NULL) - { - return (char*)memcpy(buf, (const void*)s, len); - } - return NULL; -} - -void Strtrim(char* s) -{ - char* str_end = s + strlen(s); - - while (str_end > s && str_end[-1] == ' ') - str_end--; *str_end = 0; -} \ No newline at end of file diff --git a/r5dev/src/hooks/chlclient.cpp b/r5dev/src/hooks/chlclient.cpp deleted file mode 100644 index 63a83f4b..00000000 --- a/r5dev/src/hooks/chlclient.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -namespace Hooks -{ - FrameStageNotifyFn originalFrameStageNotify = nullptr; -} - -void __fastcall Hooks::FrameStageNotify(CHLClient* rcx, ClientFrameStage_t curStage) -{ - switch (curStage) - { - case FRAME_START: // FrameStageNotify gets called every frame by CEngine::Frame with the stage being FRAME_START. We can use this to check/set global variables. - { - if (!GameGlobals::IsInitialized) - GameGlobals::InitGameGlobals(); - - break; - } - case FRAME_NET_UPDATE_POSTDATAUPDATE_END: - { - if (GameGlobals::BanSystem->IsRefuseListValid()) - { - for (int i = 0; i < GameGlobals::BanSystem->refuseList.size(); i++) // Loop through vector. - { - for (int c = 0; c < MAX_PLAYERS; c++) // Loop through all possible client instances. - { - CClient* client = GameGlobals::Client->GetClientInstance(c); // Get client instance. - if (!client) - continue; - - if (!client->GetNetChan()) // Netchan valid? - continue; - - int clientID = client->m_iUserID + 1; // Get UserID + 1. - if (clientID != GameGlobals::BanSystem->refuseList[i].second) // See if they match. - continue; - - GameGlobals::DisconnectClient(client, GameGlobals::BanSystem->refuseList[i].first.c_str(), 0, 1); - GameGlobals::BanSystem->DeleteConnectionRefuse(clientID); - break; - } - } - } - PatchNetVarConVar(); - break; - } - default: - break; - } - originalFrameStageNotify(rcx, curStage); -} \ No newline at end of file diff --git a/r5dev/src/hooks/connectclient.cpp b/r5dev/src/hooks/connectclient.cpp deleted file mode 100644 index 6b2a7360..00000000 --- a/r5dev/src/hooks/connectclient.cpp +++ /dev/null @@ -1,90 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -namespace Hooks -{ - ConnectClientFn originalConnectClient = nullptr;; -} - -void IsClientBanned(R5Net::Client* r5net, const std::string ip, std::int64_t orid) -{ - std::string err = std::string(); - bool compBanned = r5net && r5net->GetClientIsBanned(ip, orid, err); - if (compBanned) - { - while (compBanned) - { - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. - { - CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance. - if (!client) // Client instance valid? - continue; - - if (!client->GetNetChan()) // Netchan valid? - continue; - - std::int64_t originID = client->m_iOriginID; // Get originID. - if (originID != orid) // See if they match. - continue; - - GameGlobals::BanSystem->AddConnectionRefuse(err, client->m_iUserID + 1); // Add to the vector. - compBanned = false; - break; - } - } - } -} - -void* Hooks::ConnectClient(void* thisptr, void* packet) -{ - if (!GameGlobals::BanSystem) - return originalConnectClient(thisptr, packet); - - std::string finalIPAddress = "null"; - MemoryAddress ipAddressField = MemoryAddress(((std::uintptr_t)packet + 0x10)); - if (ipAddressField && ipAddressField.GetValue() != 0x0) // The api they use to store the IP is pretty eh and idk any other way to do it rn. - { - std::stringstream ss; - ss << std::to_string(ipAddressField.GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x1).GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x2).GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x3).GetValue()); - - finalIPAddress = ss.str(); - } - const char* name = *(const char**)((std::uintptr_t)packet + 0x30); // Get player name. - std::int64_t originID = *(std::int64_t*)((std::uintptr_t)packet + 0x28); // Get origin ID. - - if (std::strlen(name) == 0 || originID == 0) // Yeah that should not happen lol. - { - addr_CServer_RejectConnection(thisptr, *(unsigned int*)((std::uintptr_t)thisptr + 0xC), packet, "Invalid connection credentials."); // RejectConnection for the client. - } - - g_GameConsole->AddLog("[CServer::ConnectClient] %s is trying to connect. OriginID: %lld", name, originID); - - if (GameGlobals::BanSystem->IsBanListValid()) // Is the banlist vector valid? - { - if (GameGlobals::BanSystem->IsBanned(finalIPAddress, originID)) // Is the client trying to connect banned? - { - addr_CServer_RejectConnection(thisptr, *(unsigned int*)((std::uintptr_t)thisptr + 0xC), packet, "You have been banned from this Server."); // RejectConnection for the client. - g_GameConsole->AddLog("[CServer::ConnectClient] %s is banned. OriginID: %lld", name, originID); - return nullptr; - } - } - - if (g_CheckCompBanDB) - { - if (g_ServerBrowser) - { - R5Net::Client* r5net = g_ServerBrowser->GetR5Net(); - if (r5net) - { - std::thread t1(IsClientBanned, r5net, finalIPAddress, originID); - t1.detach(); - } - } - } - - return originalConnectClient(thisptr, packet); -} \ No newline at end of file diff --git a/r5dev/src/hooks/cvengineserver.cpp b/r5dev/src/hooks/cvengineserver.cpp deleted file mode 100644 index 73b84f1f..00000000 --- a/r5dev/src/hooks/cvengineserver.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -namespace Hooks -{ - IsPersistenceDataAvailableFn originalIsPersistenceDataAvailable = nullptr; -} - -// TODO: turn this into a playerstruct constructor if it ever becomes necessary -bool Hooks::IsPersistenceDataAvailable(__int64 thisptr, int client) -{ - static bool isPersistenceVarSet[256]; - - // TODO: Maybe not hardcode - std::uintptr_t playerStructBase = 0x16073B200; - std::uintptr_t playerStructSize = 0x4A4C0; - std::uintptr_t persistenceVar = 0x5BC; - - std::uintptr_t targetPlayerStruct = playerStructBase + client * playerStructSize; - - *(char*)(targetPlayerStruct + persistenceVar) = (char)0x5; - - if (!isPersistenceVarSet[client]) - isPersistenceVarSet[client] = true; - - return originalIsPersistenceDataAvailable(thisptr, client); -} \ No newline at end of file diff --git a/r5dev/src/hooks/hooks.cpp b/r5dev/src/hooks/hooks.cpp deleted file mode 100644 index 515e928f..00000000 --- a/r5dev/src/hooks/hooks.cpp +++ /dev/null @@ -1,223 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -bool g_bBlockInput = false; - -namespace Hooks -{ - bool bToggledDevFlags = true; - bool bToggledNetTrace = false; -} - -void Hooks::InstallHooks() -{ - /////////////////////////////////////////////////////////////////////////////// - // Initialize Minhook - spdlog::debug("Hooking game functions now..\n"); - MH_Initialize(); - - /////////////////////////////////////////////////////////////////////////////// - // Hook Squirrel functions - MH_CreateHook(addr_SQVM_Print, &Hooks::SQVM_Print, NULL); - MH_CreateHook(addr_SQVM_Warning, &Hooks::SQVM_Warning, reinterpret_cast(&originalSQVM_Warning)); - MH_CreateHook(addr_SQVM_LoadRson, &Hooks::SQVM_LoadRson, reinterpret_cast(&originalSQVM_LoadRson)); - MH_CreateHook(addr_SQVM_LoadScript, &Hooks::SQVM_LoadScript, reinterpret_cast(&originalSQVM_LoadScript)); - MH_CreateHook(addr_SQVM_RegisterOriginFuncs, &Hooks::SQVM_RegisterOriginFuncs, reinterpret_cast(&originalSQVM_RegisterOriginFuncs)); - MH_CreateHook(addr_SQVM_RegisterCreatePlayerTasklist, &Hooks::SQVM_RegisterCreatePlayerTasklist, reinterpret_cast(&originalSQVM_RegisterCreatePlayerTasklist)); - - /////////////////////////////////////////////////////////////////////////////// - // Hook various Game functions - MH_CreateHook(addr_CHLClient_FrameStageNotify, &Hooks::FrameStageNotify, reinterpret_cast(&originalFrameStageNotify)); - MH_CreateHook(addr_CVEngineServer_IsPersistenceDataAvailable, &Hooks::IsPersistenceDataAvailable, reinterpret_cast(&originalIsPersistenceDataAvailable)); - MH_CreateHook(addr_CServer_ConnectClient, &Hooks::ConnectClient, reinterpret_cast(&originalConnectClient)); - - /////////////////////////////////////////////////////////////////////////////// - // Hook Netchan functions - MH_CreateHook(addr_NET_PrintFunc, &Hooks::NET_PrintFunc, reinterpret_cast(&originalNET_PrintFunc)); - MH_CreateHook(addr_NET_ReceiveDatagram, &Hooks::NET_ReceiveDatagram, reinterpret_cast(&originalNET_ReceiveDatagram)); - MH_CreateHook(addr_NET_SendDatagram, &Hooks::NET_SendDatagram, reinterpret_cast(&originalNET_SendDatagram)); - MH_CreateHook(addr_NetChan_Shutdown, &Hooks::NetChan_Shutdown, reinterpret_cast(&originalNetChan_ShutDown)); - - /////////////////////////////////////////////////////////////////////////////// - // Hook ConVar | ConCommand functions - MH_CreateHook(addr_ConVar_IsFlagSet, &Hooks::ConVar_IsFlagSet, reinterpret_cast(&originalConVar_IsFlagSet)); - MH_CreateHook(addr_ConCommand_IsFlagSet, &Hooks::ConCommand_IsFlagSet, reinterpret_cast(&originalConCommand_IsFlagSet)); - - /////////////////////////////////////////////////////////////////////////////// - // Hook CMatSystemSurface functions - MH_CreateHook(addr_CMatSystemSurface_LockCursor, &LockCursor, reinterpret_cast(&originalLockCursor)); - - /////////////////////////////////////////////////////////////////////////////// - // Hook Utility functions - MH_CreateHook(addr_MSG_EngineError, &Hooks::MSG_EngineError, reinterpret_cast(&originalMSG_EngineError)); - MH_CreateHook(addr_LoadPlaylist, &Hooks::LoadPlaylist, reinterpret_cast(&originalLoadPlaylist)); - MH_CreateHook(addr_CEngineVGui_Paint, &Hooks::CEngineVGui_Paint, reinterpret_cast(&originalCEngineVGui_Paint)); - MH_CreateHook(addr_OriginGetErrorDescription, &Hooks::OriginGetErrorDescription, reinterpret_cast(&originalOriginGetErrorDescriptionWrapper)); - - /////////////////////////////////////////////////////////////////////////////// - // Hook WinAPI - if (Module user32dll = Module("user32.dll"); user32dll.GetModuleBase()) // Is user32.dll valid? - { - void* SetCursorPosPtr = user32dll.GetExportedFunction("SetCursorPos"); - void* ClipCursorPtr = user32dll.GetExportedFunction("ClipCursor"); - void* GetCursorPosPtr = user32dll.GetExportedFunction("GetCursorPos"); - void* ShowCursorPtr = user32dll.GetExportedFunction("ShowCursor"); - MH_CreateHook(SetCursorPosPtr, &Hooks::SetCursorPos, reinterpret_cast(&originalSetCursorPos)); - MH_CreateHook(ClipCursorPtr, &Hooks::ClipCursor, reinterpret_cast(&originalClipCursor)); - MH_CreateHook(GetCursorPosPtr, &Hooks::GetCursorPos, reinterpret_cast(&originalGetCursorPos)); - MH_CreateHook(ShowCursorPtr, &Hooks::ShowCursor, reinterpret_cast(&originalShowCursor)); - - /////////////////////////////////////////////////////////////////////////// - // Enable WinAPI hooks - MH_EnableHook(SetCursorPosPtr); - MH_EnableHook(ClipCursorPtr); - MH_EnableHook(GetCursorPosPtr); - MH_EnableHook(ShowCursorPtr); - } - - /////////////////////////////////////////////////////////////////////////////// - // Enable Squirrel hooks - MH_EnableHook(addr_SQVM_Print); - MH_EnableHook(addr_SQVM_Warning); - MH_EnableHook(addr_SQVM_LoadRson); - MH_EnableHook(addr_SQVM_LoadScript); - MH_EnableHook(addr_SQVM_RegisterOriginFuncs); - MH_EnableHook(addr_SQVM_RegisterCreatePlayerTasklist); - - /////////////////////////////////////////////////////////////////////////////// - // Enable various Game hooks - MH_EnableHook(addr_CHLClient_FrameStageNotify); - MH_EnableHook(addr_CVEngineServer_IsPersistenceDataAvailable); - MH_EnableHook(addr_CServer_ConnectClient); - - /////////////////////////////////////////////////////////////////////////////// - // Enable Netchan hooks - MH_EnableHook(addr_NET_PrintFunc); - MH_EnableHook(addr_NetChan_Shutdown); - - /////////////////////////////////////////////////////////////////////////////// - // Enable ConVar | ConCommand hooks - MH_EnableHook(addr_ConVar_IsFlagSet); - MH_EnableHook(addr_ConCommand_IsFlagSet); - - /////////////////////////////////////////////////////////////////////////////// - // Enable CMatSystemSurface hooks - MH_EnableHook(addr_CMatSystemSurface_LockCursor); - - /////////////////////////////////////////////////////////////////////////////// - // Enabled Utility hooks - MH_EnableHook(addr_MSG_EngineError); - MH_EnableHook(addr_LoadPlaylist); - MH_EnableHook(addr_CEngineVGui_Paint); - MH_EnableHook(addr_OriginGetErrorDescription); -} - -void Hooks::RemoveHooks() -{ - spdlog::debug("Unhooking game functions..\n"); - /////////////////////////////////////////////////////////////////////////////// - // Unhook Squirrel functions - MH_RemoveHook(addr_SQVM_Print); - MH_RemoveHook(addr_SQVM_LoadRson); - MH_RemoveHook(addr_SQVM_LoadScript); - MH_RemoveHook(addr_SQVM_RegisterOriginFuncs); - MH_RemoveHook(addr_SQVM_RegisterCreatePlayerTasklist); - - /////////////////////////////////////////////////////////////////////////////// - // Unhook various Game Functions - MH_RemoveHook(addr_CHLClient_FrameStageNotify); - MH_RemoveHook(addr_CVEngineServer_IsPersistenceDataAvailable); - MH_RemoveHook(addr_CServer_ConnectClient); - - /////////////////////////////////////////////////////////////////////////////// - // Unhook Netchan functions - MH_RemoveHook(addr_NET_PrintFunc); - MH_RemoveHook(addr_NET_ReceiveDatagram); - MH_RemoveHook(addr_NET_SendDatagram); - MH_RemoveHook(addr_NetChan_Shutdown); - - /////////////////////////////////////////////////////////////////////////////// - // Unhook ConVar | ConCommand functions - MH_RemoveHook(addr_ConVar_IsFlagSet); - MH_RemoveHook(addr_ConCommand_IsFlagSet); - - /////////////////////////////////////////////////////////////////////////////// - // Unhook CMatSystemSurface functions - MH_RemoveHook(addr_CMatSystemSurface_LockCursor); - - /////////////////////////////////////////////////////////////////////////////// - // Unhook WinAPI - if (Module user32dll = Module("user32.dll"); user32dll.GetModuleBase()) // Is user32.dll valid? - { - void* SetCursorPosPtr = user32dll.GetExportedFunction("SetCursorPos"); - void* ClipCursorPtr = user32dll.GetExportedFunction("ClipCursor"); - void* GetCursorPosPtr = user32dll.GetExportedFunction("GetCursorPos"); - void* ShowCursorPtr = user32dll.GetExportedFunction("ShowCursor"); - - MH_RemoveHook(SetCursorPosPtr); - MH_RemoveHook(ClipCursorPtr); - MH_RemoveHook(GetCursorPosPtr); - MH_RemoveHook(ShowCursorPtr); - } - - /////////////////////////////////////////////////////////////////////////////// - // Unhook Utility functions - MH_RemoveHook(addr_MSG_EngineError); - MH_RemoveHook(addr_LoadPlaylist); - MH_RemoveHook(addr_CEngineVGui_Paint); - MH_RemoveHook(addr_OriginGetErrorDescription); - - /////////////////////////////////////////////////////////////////////////////// - // Reset Minhook - MH_Uninitialize(); -} - -void Hooks::ToggleNetTrace() -{ - if (!bToggledNetTrace) - { - MH_EnableHook(addr_NET_ReceiveDatagram); - MH_EnableHook(addr_NET_SendDatagram); - spdlog::info("\n"); - spdlog::info("+--------------------------------------------------------+\n"); - spdlog::info("|>>>>>>>>>>>>>| NETCHANNEL TRACE ACTIVATED |<<<<<<<<<<<<<|\n"); - spdlog::info("+--------------------------------------------------------+\n"); - spdlog::info("\n"); - } - else - { - MH_DisableHook(addr_NET_ReceiveDatagram); - MH_DisableHook(addr_NET_SendDatagram); - spdlog::info("\n"); - spdlog::info("+--------------------------------------------------------+\n"); - spdlog::info("|>>>>>>>>>>>>| NETCHANNEL TRACE DEACTIVATED |<<<<<<<<<<<<|\n"); - spdlog::info("+--------------------------------------------------------+\n"); - spdlog::info("\n"); - } - bToggledNetTrace = !bToggledNetTrace; -} - -void Hooks::ToggleDevCommands() -{ - if (!bToggledDevFlags) - { - MH_EnableHook(addr_ConVar_IsFlagSet); - MH_EnableHook(addr_ConCommand_IsFlagSet); - spdlog::info("\n"); - spdlog::info("+--------------------------------------------------------+\n"); - spdlog::info("|>>>>>>>>>>>>>| DEVONLY COMMANDS ACTIVATED |<<<<<<<<<<<<<|\n"); - spdlog::info("+--------------------------------------------------------+\n"); - spdlog::info("\n"); - } - else - { - MH_DisableHook(addr_ConVar_IsFlagSet); - MH_DisableHook(addr_ConCommand_IsFlagSet); - spdlog::info("\n"); - spdlog::info("+--------------------------------------------------------+\n"); - spdlog::info("|>>>>>>>>>>>>| DEVONLY COMMANDS DEACTIVATED |<<<<<<<<<<<<|\n"); - spdlog::info("+--------------------------------------------------------+\n"); - spdlog::info("\n"); - } - bToggledDevFlags = !bToggledDevFlags; -} diff --git a/r5dev/src/hooks/hoststate.cpp b/r5dev/src/hooks/hoststate.cpp deleted file mode 100644 index 513debe6..00000000 --- a/r5dev/src/hooks/hoststate.cpp +++ /dev/null @@ -1,198 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -namespace Hooks -{ - FrameUpdateFn originalFrameUpdate = nullptr; -} - -void Hooks::FrameUpdate(void* rcx, void* rdx, float time) -{ - static auto setjmpFn = MemoryAddress(0x141205460).RCast<__int64(*)(jmp_buf, void*)>(); - static auto host_abortserver = MemoryAddress(0x14B37C700).RCast(); - static auto CHostState_InitFn = MemoryAddress(0x14023E7D0).RCast(); - static auto g_ServerAbortServer = MemoryAddress(0x14B37CA22).RCast(); - static auto State_RunFn = MemoryAddress(0x14023E870).RCast(); - static auto Cbuf_ExecuteFn = MemoryAddress(0x14020D5C0).RCast(); - static auto g_ServerGameClients = MemoryAddress(0x14B383428).RCast<__int64*>(); - static auto SV_InitGameDLLFn = MemoryAddress(0x140308B90).RCast(); - static auto g_CModelLoader = MemoryAddress(0x14173B210).RCast(); - static auto CModelLoader_Map_IsValidFn = MemoryAddress(0x1402562F0).RCast(); - static auto Host_NewGameFn = MemoryAddress(0x140238DA0).RCast(); - static auto Host_Game_ShutdownFn = MemoryAddress(0x14023EDA0).RCast(); - static auto src_drawloading = MemoryAddress(0x14B37D96B).RCast(); - static auto scr_engineevent_loadingstarted = MemoryAddress(0x1666ED024).RCast(); - static auto gfExtendedError = MemoryAddress(0x14B383391).RCast(); - static auto g_CEngineVGui = MemoryAddress(0x141741310).RCast(); - static auto g_ServerDLL = MemoryAddress(0x141732048).RCast(); - static auto Host_ChangelevelFn = MemoryAddress(0x1402387B0).RCast(); - static auto CL_EndMovieFn = MemoryAddress(0x1402C03D0).RCast(); - static auto SendOfflineRequestToStryderFn = MemoryAddress(0x14033D380).RCast(); - static auto CEngine = MemoryAddress(0X141741BA0).RCast(); - - HostStates_t oldState; - void* placeHolder = nullptr; - if (setjmpFn(*host_abortserver, placeHolder)) - { - CHostState_InitFn(GameGlobals::HostState); - return; - } - else - { - *g_ServerAbortServer = true; - - do - { - Cbuf_ExecuteFn(); - oldState = GameGlobals::HostState->m_iCurrentState; - switch (GameGlobals::HostState->m_iCurrentState) - { - case HostStates_t::HS_NEW_GAME: - { - spdlog::debug("[+CHostState::FrameUpdate+] Starting new game now with level: {}\n", GameGlobals::HostState->m_levelName); - // Inlined CHostState::State_NewGame - GameGlobals::HostState->m_bSplitScreenConnect = false; - if (!g_ServerGameClients) // Init Game if it ain't valid. - { - SV_InitGameDLLFn(); - } - - if ( !CModelLoader_Map_IsValidFn(g_CModelLoader, GameGlobals::HostState->m_levelName) // Check if map is valid and if we can start a new game. - || !Host_NewGameFn(GameGlobals::HostState->m_levelName, nullptr, GameGlobals::HostState->m_bBackgroundLevel, GameGlobals::HostState->m_bSplitScreenConnect, nullptr) || !g_ServerGameClients) - { - spdlog::info("[+CHostState::FrameUpdate+] Fatal map error 1.\n"); - // Inlined SCR_EndLoadingPlaque - if (*src_drawloading) - { - *scr_engineevent_loadingstarted = 0; - using HideLoadingPlaqueFn = void(*)(void*); - (*reinterpret_cast(g_CEngineVGui))[36](g_CEngineVGui); // (*((void(__fastcall**)(void**))g_CEngineVGui + 36))(&g_CEngineVGui);// HideLoadingPlaque - } - else if (*gfExtendedError) - { - using ShowErrorMessageFn = void(*)(void*); - (*reinterpret_cast(g_CEngineVGui))[35](g_CEngineVGui); // (*((void(__fastcall**)(void**))g_CEngineVGui + 35))(&g_CEngineVGui);// ShowErrorMessage - } - // End Inline SCR_EndLoadingPlaque - - // Inlined CHostState::GameShutdown - if (GameGlobals::HostState->m_bActiveGame) - { - using GameShutdownFn = void(*)(void*); - (*reinterpret_cast(g_ServerDLL))[9](g_ServerDLL); // (*(void(__fastcall**)(void*))(*(_QWORD*)g_ServerDLL + 72i64))(g_ServerDLL);// GameShutdown - GameGlobals::HostState->m_bActiveGame = 0; - } - // End Inline CHostState::GameShutdown - } - - // Seems useless so nope. - // if (g_CHLClient) - // (*(void(__fastcall**)(__int64, _QWORD))(*(_QWORD*)g_CHLClient + 1000i64))(g_CHLClient, 0i64); - - GameGlobals::HostState->m_iCurrentState = HostStates_t::HS_RUN; // Set current state to run. - - // If our next state isn't a shutdown or its a forced shutdown then set next state to run. - if (GameGlobals::HostState->m_iNextState != HostStates_t::HS_SHUTDOWN || !GameGlobals::Cvar->FindVar("host_hasIrreversibleShutdown")->m_iValue) - GameGlobals::HostState->m_iNextState = HostStates_t::HS_RUN; - - // End Inline CHostState::State_NewGame - break; - } - case HostStates_t::HS_CHANGE_LEVEL_SP: - { - GameGlobals::HostState->m_flShortFrameTime = 1.5; // Set frame time. - - spdlog::debug("[+CHostState::FrameUpdate+] Changing singleplayer level to: {}.\n", GameGlobals::HostState->m_levelName); - - if (CModelLoader_Map_IsValidFn(g_CModelLoader, GameGlobals::HostState->m_levelName)) // Check if map is valid and if we can start a new game. - { - Host_ChangelevelFn(true, GameGlobals::HostState->m_levelName, GameGlobals::HostState->m_mapGroupName); // Call change level as singleplayer level. - } - else - { - spdlog::info("[+CHostState::FrameUpdate+] Server unable to change level, because unable to find map {}.\n", GameGlobals::HostState->m_levelName); - } - - // Seems useless so nope. - // if (g_CHLClient) - // (*(void(__fastcall**)(__int64, _QWORD))(*(_QWORD*)g_CHLClient + 1000i64))(g_CHLClient, 0i64); - - GameGlobals::HostState->m_iCurrentState = HostStates_t::HS_RUN; // Set current state to run. - - // If our next state isn't a shutdown or its a forced shutdown then set next state to run. - if (GameGlobals::HostState->m_iNextState != HostStates_t::HS_SHUTDOWN || !GameGlobals::Cvar->FindVar("host_hasIrreversibleShutdown")->m_iValue) - GameGlobals::HostState->m_iNextState = HostStates_t::HS_RUN; - - break; - } - case HostStates_t::HS_CHANGE_LEVEL_MP: - { - GameGlobals::HostState->m_flShortFrameTime = 0.5; // Set frame time. - using LevelShutdownFn = void(__thiscall*)(void*); - (*reinterpret_cast(*g_ServerDLL))[8](g_ServerDLL); // (*(void (__fastcall **)(void *))(*(_QWORD *)server_dll_var + 64i64))(server_dll_var);// LevelShutdown - - spdlog::debug("[+CHostState::FrameUpdate+] Changing multiplayer level to: {}.\n", GameGlobals::HostState->m_levelName); - - if (CModelLoader_Map_IsValidFn(g_CModelLoader, GameGlobals::HostState->m_levelName)) // Check if map is valid and if we can start a new game. - { - using EnabledProgressBarForNextLoadFn = void(*)(void*); - (*reinterpret_cast(g_CEngineVGui))[31](g_CEngineVGui); // (*((void(__fastcall**)(void**))g_CEngineVGUI + 31))(&g_CEngineVGUI);// EnabledProgressBarForNextLoad - Host_ChangelevelFn(false, GameGlobals::HostState->m_levelName, GameGlobals::HostState->m_mapGroupName); // Call change level as multiplayer level. - } - else - { - spdlog::info("[+CHostState::FrameUpdate+] Server unable to change level, because unable to find map {}.\n", GameGlobals::HostState->m_levelName); - } - - // Seems useless so nope. - // // if (g_CHLClient) - // (*(void(__fastcall**)(__int64, _QWORD))(*(_QWORD*)g_CHLClient + 1000i64))(g_CHLClient, 0i64); - - GameGlobals::HostState->m_iCurrentState = HostStates_t::HS_RUN; // Set current state to run. - - // If our next state isn't a shutdown or its a forced shutdown then set next state to run. - if (GameGlobals::HostState->m_iNextState != HostStates_t::HS_SHUTDOWN || !GameGlobals::Cvar->FindVar("host_hasIrreversibleShutdown")->m_iValue) - GameGlobals::HostState->m_iNextState = HostStates_t::HS_RUN; - - break; - } - case HostStates_t::HS_RUN: - { - State_RunFn(&GameGlobals::HostState->m_iCurrentState, nullptr, time); - break; - } - case HostStates_t::HS_GAME_SHUTDOWN: - { - spdlog::debug("[+CHostState::FrameUpdate+] Shutting game down now.\n"); - Host_Game_ShutdownFn(GameGlobals::HostState); - break; - } - case HostStates_t::HS_RESTART: - { - spdlog::debug("[+CHostState::FrameUpdate+] Restarting client.\n"); - CL_EndMovieFn(); - SendOfflineRequestToStryderFn(); // We have hostnames nulled anyway. - *(std::int32_t*)((std::uintptr_t)CEngine + 0xC) = 3; //g_CEngine.vtable->SetNextState(&g_CEngine, DLL_RESTART); - break; - } - case HostStates_t::HS_SHUTDOWN: - { - spdlog::debug("[+CHostState::FrameUpdate+] Shutting client down.\n"); - CL_EndMovieFn(); - SendOfflineRequestToStryderFn(); // We have hostnames nulled anyway. - *(std::int32_t*)((std::uintptr_t)CEngine + 0xC) = 2; //g_CEngine.vtable->SetNextState(&g_CEngine, DLL_CLOSE); - break; - } - default: - { - break; - } - } - - } while ((oldState != HostStates_t::HS_RUN || GameGlobals::HostState->m_iNextState == HostStates_t::HS_LOAD_GAME && GameGlobals::Cvar->FindVar("g_single_frame_shutdown_for_reload_cvar")->m_pParent->m_iValue) - && oldState != HostStates_t::HS_SHUTDOWN - && oldState != HostStates_t::HS_RESTART); - - } -// originalFrameUpdate(rcx, rdx, time); -} \ No newline at end of file diff --git a/r5dev/src/hooks/iconvar.cpp b/r5dev/src/hooks/iconvar.cpp deleted file mode 100644 index ef384a0f..00000000 --- a/r5dev/src/hooks/iconvar.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -#ifdef _DEBUG -#define MaskOffCheats -#endif - -namespace Hooks -{ - ConVar_IsFlagSetFn originalConVar_IsFlagSet = nullptr; - ConCommand_IsFlagSetFn originalConCommand_IsFlagSet = nullptr; -} - -bool Hooks::ConVar_IsFlagSet(ConVar* cvar, int flag) -{ -#ifdef MaskOffCheats - if (g_bDebugConsole) - { - std::cout << "--------------------------------------------------\n"; - std::cout << cvar->m_ConCommandBase.m_pszName << " Flags: " << std::hex << std::uppercase << cvar->m_ConCommandBase.m_nFlags << "\n"; - } - // Mask off FCVAR_DEVELOPMENTONLY and FCVAR_CHEAT. - cvar->m_ConCommandBase.m_nFlags &= ~(FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT); - if (g_bDebugConsole) - { - std::cout << cvar->m_ConCommandBase.m_pszName << " Flags: " << std::hex << std::uppercase << cvar->m_ConCommandBase.m_nFlags << "\n"; - std::cout << cvar->m_ConCommandBase.m_pszName << " Verify: " << std::hex << std::uppercase << flag << "\n"; - std::cout << "--------------------------------------------------\n"; - } - - if (flag & FCVAR_RELEASE) - { - return true; - } - - if (g_bReturnAllFalse) - { - return false; - } - - return (cvar->m_ConCommandBase.m_nFlags & flag) != 0; -#else - // Mask off FCVAR_DEVELOPMENTONLY if existing. - cvar->m_ConCommandBase.m_nFlags &= ~FCVAR_DEVELOPMENTONLY; - - return originalConVar_IsFlagSet(cvar, flag); -#endif -} - -bool Hooks::ConCommand_IsFlagSet(ConCommandBase* cmd, int flag) -{ -#ifdef MaskOffCheats - if (g_bDebugConsole) - { - std::cout << "--------------------------------------------------\n"; - std::cout << cmd->m_pszName << " Flags: " << std::hex << std::uppercase << cmd->m_nFlags << "\n"; - } - // Mask off FCVAR_DEVELOPMENTONLY and FCVAR_CHEAT. - cmd->m_nFlags &= ~(FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT); - if (g_bDebugConsole) - { - std::cout << cmd->m_pszName << " Flags: " << std::hex << std::uppercase << cmd->m_nFlags << "\n"; - std::cout << cmd->m_pszName << " Verify: " << std::hex << std::uppercase << flag << "\n"; - std::cout << "--------------------------------------------------\n"; - } - - if (flag & FCVAR_RELEASE) - { - return true; - } - - if (g_bReturnAllFalse) - { - return false; - } - - return (cmd->m_nFlags & flag) != 0; -#else - // Mask off FCVAR_DEVELOPMENTONLY if existing. - cmd->m_nFlags &= ~FCVAR_DEVELOPMENTONLY; - - return originalConCommand_IsFlagSet(cmd, flag); -#endif -} \ No newline at end of file diff --git a/r5dev/src/hooks/loadplaylist.cpp b/r5dev/src/hooks/loadplaylist.cpp deleted file mode 100644 index c362daec..00000000 --- a/r5dev/src/hooks/loadplaylist.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -namespace Hooks -{ - LoadPlaylistFn originalLoadPlaylist = nullptr; -} - -bool Hooks::LoadPlaylist(const char* playlist) -{ - memset(addr_MapVPKCache, 0, 0x40); // Bye bye vpk cache, you only make us crash >:(. - - CHAR playlistPath[] = "\x77\x27\x35\x2b\x2c\x6c\x2b\x2c\x2b"; - PCHAR curr = playlistPath; - while (*curr) { - *curr ^= 'B'; - ++curr; - } - - if (FileExists(playlistPath)) - { - std::uint8_t verifyPlaylistIntegrity[] = // Very hacky way for alternative inline assembly for x64.. - { - 0x48, 0x8B, 0x45, 0x58, // mov rcx, playlist - 0xC7, 0x00, 0x00, 0x00, 0x00, // test playlist, playlist - 0x00 - }; - void* verifyPlaylistIntergrityFn = nullptr; - VirtualAlloc(verifyPlaylistIntergrityFn, 10, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); - memcpy(&verifyPlaylistIntergrityFn, (const void*)verifyPlaylistIntegrity, 9); - reinterpret_cast(verifyPlaylistIntergrityFn)(); - } - - return originalLoadPlaylist(playlist); // Parse playlist like normally.. -} \ No newline at end of file diff --git a/r5dev/src/hooks/lockcursor.cpp b/r5dev/src/hooks/lockcursor.cpp deleted file mode 100644 index 676fb70d..00000000 --- a/r5dev/src/hooks/lockcursor.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "pch.h" -#include "hooks.h" -#include "id3dx.h" - -namespace Hooks -{ - LockCursorFn originalLockCursor = nullptr; -} - -void Hooks::LockCursor(void* thisptr) -{ - if (g_bShowConsole || g_bShowBrowser) - { - addr_CMatSystemSurface_UnlockCursor(thisptr); // Unlock cursor if our gui is shown. - return; - } - return originalLockCursor(thisptr); -} \ No newline at end of file diff --git a/r5dev/src/hooks/msgbox.cpp b/r5dev/src/hooks/msgbox.cpp deleted file mode 100644 index 4846a4f6..00000000 --- a/r5dev/src/hooks/msgbox.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -namespace Hooks -{ - MSG_EngineErrorFn originalMSG_EngineError = nullptr; -} - -int Hooks::MSG_EngineError(char* fmt, va_list args) -{ - std::cout << "\nENGINE ERROR #####################################\n"; - vprintf(fmt, args); - - return originalMSG_EngineError(fmt, args); -} \ No newline at end of file diff --git a/r5dev/src/hooks/net.cpp b/r5dev/src/hooks/net.cpp deleted file mode 100644 index 096d33d4..00000000 --- a/r5dev/src/hooks/net.cpp +++ /dev/null @@ -1,90 +0,0 @@ -#include "pch.h" -#include "hooks.h" -#include "logsystem.h" - -namespace Hooks -{ - NET_PrintFuncFn originalNET_PrintFunc = nullptr; - NET_ReceiveDatagramFn originalNET_ReceiveDatagram = nullptr; - NET_SendDatagramFn originalNET_SendDatagram = nullptr; -} - -static std::ostringstream oss_print; -static auto ostream_sink_print = std::make_shared(oss_print); -static auto log_sink_print = std::make_shared("logs/NET_Print.txt", true); - -//----------------------------------------------------------------------------- -// Purpose: log the clients signonstate to the console -//----------------------------------------------------------------------------- -void Hooks::NET_PrintFunc(const char* fmt, ...) -{ - static bool initialized = false; - static char buf[1024]; - - oss_print.str(""); - oss_print.clear(); - - static spdlog::logger logger("sqvm_print", { log_sink_print, ostream_sink_print }); - - if (!initialized) - { - log_sink_print->set_level(spdlog::level::debug); - ostream_sink_print->set_level(spdlog::level::debug); - logger.set_level(spdlog::level::debug); - logger.set_pattern("[%S.%e] %v"); - - initialized = true; - } - - va_list args; - va_start(args, fmt); - - vsnprintf(buf, sizeof(buf), fmt, args); - - buf[sizeof(buf) - 1] = 0; - va_end(args); - - logger.debug(buf); - - std::string s = oss_print.str(); - const char* c = s.c_str(); - - g_LogSystem.AddLog(LogType_t::NATIVE, s); - - Items.push_back(Strdup((const char*)c)); -} - -//----------------------------------------------------------------------------- -// Purpose: hook and log the receive datagram -//----------------------------------------------------------------------------- -bool Hooks::NET_ReceiveDatagram(int sock, void* inpacket, bool raw) -{ - bool result = originalNET_ReceiveDatagram(sock, inpacket, raw); - if (result) - { - int i = NULL; - netpacket_t* pkt = (netpacket_t*)inpacket; - - /////////////////////////////////////////////////////////////////////////// - // Log received packet data - HexDump("[+] NET_ReceiveDatagram", 0, &pkt->data[i], pkt->wiresize); - } - - return result; -} - -//----------------------------------------------------------------------------- -// Purpose: hook and log the send datagram -//----------------------------------------------------------------------------- -unsigned int Hooks::NET_SendDatagram(SOCKET s, const char* buf, int len, int flags) -{ - unsigned int result = originalNET_SendDatagram(s, buf, len, flags); - if (result) - { - /////////////////////////////////////////////////////////////////////////// - // Log transmitted packet data - HexDump("[+] NET_SendDatagram", 0, buf, len); - } - - return result; -} \ No newline at end of file diff --git a/r5dev/src/hooks/netchannel.cpp b/r5dev/src/hooks/netchannel.cpp deleted file mode 100644 index 6ad9ffa3..00000000 --- a/r5dev/src/hooks/netchannel.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -namespace Hooks -{ - NetChan_ShutDown originalNetChan_ShutDown = nullptr; -} - -void Hooks::NetChan_Shutdown(void* rcx, const char* reason, unsigned __int8 unk1, char unk2) -{ - addr_downloadPlaylists_Callback(); // Re-Load playlist from disk after getting dropped or disconnecting off a server. - originalNetChan_ShutDown(rcx, reason, unk1, unk2); -} \ No newline at end of file diff --git a/r5dev/src/hooks/originsdk.cpp b/r5dev/src/hooks/originsdk.cpp deleted file mode 100644 index 6233208c..00000000 --- a/r5dev/src/hooks/originsdk.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -namespace Hooks -{ - OriginGetErrorDescriptionWrapperFn originalOriginGetErrorDescriptionWrapper = nullptr; -} - -const char* Hooks::OriginGetErrorDescription(std::uint32_t originCode) -{ - switch (originCode) - { - case ORIGIN_ERROR_CORE_AUTHENTICATION_FAILED: - { - MessageBoxA(NULL, "Origin LSX Authentication challenge failed.\nAre you perhaps not logged into Origin?", "R5 Reloaded", MB_OK); - break; - } - case ORIGIN_ERROR_SDK_NOT_INITIALIZED: - { - MessageBoxA(NULL, "Origin SDK was not initialized.", "R5 Reloaded", MB_OK); - break; - } - case ORIGIN_ERROR_CORE_NOTLOADED: - { - MessageBoxA(NULL, "Origin Desktop Application is not loaded.", "R5 Reloaded", MB_OK); - break; - } - case ORIGIN_ERROR_CORE_LOGIN_FAILED: - { - MessageBoxA(NULL, "Origin couldn't authenticate with the Origin Servers.", "R5 Reloaded", MB_OK); - break; - } - case ORIGIN_ERROR_CORE_NOT_INSTALLED: - { - MessageBoxA(NULL, "Origin is not installed on this machine or could not be found.\nOrigin is needed to run R5 Reloaded.", "R5 Reloaded", MB_OK); - break; - } - default: - break; - } - - return originalOriginGetErrorDescriptionWrapper(originCode); -} \ No newline at end of file diff --git a/r5dev/src/hooks/sqvm.cpp b/r5dev/src/hooks/sqvm.cpp deleted file mode 100644 index 34a02f36..00000000 --- a/r5dev/src/hooks/sqvm.cpp +++ /dev/null @@ -1,221 +0,0 @@ -#include "pch.h" -#include "hooks.h" -#include "logsystem.h" - -namespace Hooks -{ - SQVM_WarningFn originalSQVM_Warning = nullptr; - SQVM_LoadRsonFn originalSQVM_LoadRson = nullptr; - SQVM_LoadScriptFn originalSQVM_LoadScript = nullptr; - SQVM_RegisterOriginFuncsFn originalSQVM_RegisterOriginFuncs = nullptr; - SQVM_RegisterCreatePlayerTasklistFn originalSQVM_RegisterCreatePlayerTasklist = nullptr; -} - -static std::ostringstream oss_print; -static auto ostream_sink_print = std::make_shared(oss_print); -static auto log_sink_print = std::make_shared("logs/SQVM_Print.txt", true); - -//--------------------------------------------------------------------------------- -// Purpose: prints the output of each VM to the console -//--------------------------------------------------------------------------------- -void* Hooks::SQVM_Print(void* sqvm, char* fmt, ...) -{ - static bool initialized = false; - static char buf[1024]; - static std::string vmType[3] = { "Script(S):", "Script(C):", "Script(U):" }; - - int vmIdx = *(int*)((std::uintptr_t)sqvm + 0x18); - std::string vmStr = vmType[vmIdx].c_str(); - - oss_print.str(""); - oss_print.clear(); - - static spdlog::logger logger("sqvm_print", { log_sink_print, ostream_sink_print }); - - if (!initialized) - { - log_sink_print->set_level(spdlog::level::debug); - ostream_sink_print->set_level(spdlog::level::debug); - logger.set_level(spdlog::level::debug); - logger.set_pattern("[%S.%e] %v"); - - initialized = true; - } - - va_list args; - va_start(args, fmt); - - vsnprintf(buf, sizeof(buf), fmt, args); - - buf[sizeof(buf) - 1] = 0; - va_end(args); - - vmStr.append(buf); - - if (g_GameConsole && g_GameConsole->ShouldPrintToCommandPrompt()) - { - spdlog::info(vmStr); - } - - logger.debug(vmStr); - - - std::string s = oss_print.str(); - const char* c = s.c_str(); - - g_LogSystem.AddLog((LogType_t)vmIdx, s); - - Items.push_back(Strdup(c)); - - return NULL; -} - -static std::ostringstream oss_warning; -static auto ostream_sink_warning = std::make_shared(oss_warning); -static auto log_sink_warning = std::make_shared("logs/SQVM_Warning.txt", true); - -__int64 Hooks::SQVM_Warning(void* sqvm, int a2, int a3, int* stringSize, void** string) -{ - __int64 result = originalSQVM_Warning(sqvm, a2, a3, stringSize, string); - - void* retaddr = _ReturnAddress(); // Get return address. - - if (retaddr != addr_SQVM_Warning_ReturnAddr) // Check if its SQVM_Warning calling. - return result; // If not return. - - static bool initialized = false; - static std::string vmType[3] = { "Script(S) Warning:", "Script(C) Warning:", "Script(U) Warning:" }; - - int vmIdx = *(int*)((std::uintptr_t)sqvm + 0x18); - std::string vmStr = vmType[vmIdx].c_str(); - - oss_print.str(""); - oss_print.clear(); - - static spdlog::logger logger("sqvm_warning", { log_sink_warning, ostream_sink_warning }); - - if (!initialized) - { - log_sink_warning->set_level(spdlog::level::debug); - ostream_sink_warning->set_level(spdlog::level::debug); - logger.set_level(spdlog::level::debug); - logger.set_pattern("[%S.%e] %v"); - - initialized = true; - } - - std::string stringConstructor((char*)*string, *stringSize); // Get string from memory via std::string constructor. - vmStr.append(stringConstructor); - - if (g_GameConsole && g_GameConsole->ShouldPrintToCommandPrompt()) - { - spdlog::info(vmStr); - } - - logger.debug(vmStr); - - std::string s = oss_warning.str(); - const char* c = s.c_str(); - - g_LogSystem.AddLog((LogType_t)vmIdx, s); - - Items.push_back(Strdup(c)); - - return result; -} - -//--------------------------------------------------------------------------------- -// Purpose: loads the include file from the mods directory -//--------------------------------------------------------------------------------- -__int64 Hooks::SQVM_LoadRson(const char* rson_name) -{ - char filepath[MAX_PATH] = { 0 }; - sprintf_s(filepath, MAX_PATH, "platform\\%s", rson_name); - - /////////////////////////////////////////////////////////////////////////////// - // Flip forward slashes in filepath to windows-style backslash - for (int i = 0; i < strlen(filepath); i++) - { - if (filepath[i] == '/') - { - filepath[i] = '\\'; - } - } - - /////////////////////////////////////////////////////////////////////////////// - // Returns the new path if the rson exists on the disk - if (FileExists(filepath) && originalSQVM_LoadRson(rson_name)) - { - spdlog::info("\n"); - spdlog::info("##################################################\n"); - spdlog::info("] '{}'\n", filepath); - spdlog::info("##################################################\n"); - spdlog::info("\n"); - - return originalSQVM_LoadRson(filepath); - } - - spdlog::info("\n"); - spdlog::info("##################################################\n"); - spdlog::info("] '{}'\n", rson_name); - spdlog::info("##################################################\n"); - spdlog::info("\n"); - - return originalSQVM_LoadRson(rson_name); -} - -//--------------------------------------------------------------------------------- -// Purpose: loads the script file from the mods directory -//--------------------------------------------------------------------------------- -bool Hooks::SQVM_LoadScript(void* sqvm, const char* script_path, const char* script_name, int flag) -{ - char filepath[MAX_PATH] = { 0 }; - sprintf_s(filepath, MAX_PATH, "platform\\%s", script_path); - - /////////////////////////////////////////////////////////////////////////////// - // Flip forward slashes in filepath to windows-style backslash - for (int i = 0; i < strlen(filepath); i++) - { - if (filepath[i] == '/') - { - filepath[i] = '\\'; - } - } - if (g_bDebugLoading) - { - spdlog::info(" [+] Loading SQVM Script '{}' ...\n", filepath); - } - /////////////////////////////////////////////////////////////////////////////// - // Returns true if the script exists on the disk - if (FileExists(filepath) && originalSQVM_LoadScript(sqvm, filepath, script_name, flag)) - { - return true; - } - if (g_bDebugLoading) - { - spdlog::info(" [!] FAILED. Try SP / VPK for '%s'\n", filepath); - } - - return originalSQVM_LoadScript(sqvm, script_path, script_name, flag); -} - -void Hooks::SQVM_RegisterOriginFuncs(void* sqvm) -{ - static MemoryAddress UIVM = MemoryAddress(0x14D4151F0); - - originalSQVM_RegisterOriginFuncs(sqvm); - - if (sqvm == *(UIVM.RCast())) { - GameGlobals::RegisterUIScriptFunctions(sqvm); - } - else { - GameGlobals::RegisterClientScriptFunctions(sqvm); - } -} - -void Hooks::SQVM_RegisterCreatePlayerTasklist(void* sqvm) -{ - GameGlobals::RegisterServerScriptFunctions(sqvm); - - originalSQVM_RegisterCreatePlayerTasklist(sqvm); -} \ No newline at end of file diff --git a/r5dev/src/hooks/winapi.cpp b/r5dev/src/hooks/winapi.cpp deleted file mode 100644 index a9a96905..00000000 --- a/r5dev/src/hooks/winapi.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -namespace Hooks -{ - namespace - { - static POINT g_pLastCursorPos{ 0 }; - } - - GetCursorPosFn originalGetCursorPos = nullptr; - SetCursorPosFn originalSetCursorPos = nullptr; - ClipCursorFn originalClipCursor = nullptr; - ShowCursorFn originalShowCursor = nullptr; -} - -BOOL WINAPI Hooks::GetCursorPos(LPPOINT lpPoint) -{ - if (g_bBlockInput) - { - assert(lpPoint != nullptr); - *lpPoint = g_pLastCursorPos; - } - - return originalGetCursorPos(lpPoint); -} - -BOOL WINAPI Hooks::SetCursorPos(int X, int Y) -{ - g_pLastCursorPos.x = X; - g_pLastCursorPos.y = Y; - - if (g_bBlockInput) - { - return TRUE; - } - - return originalSetCursorPos(X, Y); -} - -BOOL WINAPI Hooks::ClipCursor(const RECT* lpRect) -{ - if (g_bBlockInput) - { - lpRect = nullptr; - } - - return originalClipCursor(lpRect); -} - -BOOL WINAPI Hooks::ShowCursor(BOOL bShow) -{ - if (g_bBlockInput) - { - bShow = TRUE; - } - - return originalShowCursor(bShow); -} \ No newline at end of file diff --git a/r5dev/src/id3dx.cpp b/r5dev/src/id3dx.cpp deleted file mode 100644 index 6adce1a2..00000000 --- a/r5dev/src/id3dx.cpp +++ /dev/null @@ -1,415 +0,0 @@ -#include "pch.h" - -#include "id3dx.h" -#include "hooks.h" -#include "console.h" -#include "patterns.h" -#include "gameclasses.h" - -#include "CCompanion.h" -#include "CGameConsole.h" - -#pragma comment(lib, "d3d11.lib") -/////////////////////////////////////////////////////////////////////////////////// -// Type definitions. -typedef HRESULT(__stdcall* IDXGISwapChainPresent)(IDXGISwapChain* pSwapChain, UINT nSyncInterval, UINT nFlags); -typedef HRESULT(__stdcall* IDXGIResizeBuffers) (IDXGISwapChain* pSwapChain, UINT nBufferCount, UINT nWidth, UINT nHeight, DXGI_FORMAT dxFormat, UINT nSwapChainFlags); - -/////////////////////////////////////////////////////////////////////////////////// -// Variables. -bool g_bShowConsole = false; -bool g_bShowBrowser = false; -IDXGISwapChainPresent g_fnIDXGISwapChainPresent = nullptr; -IDXGIResizeBuffers g_fnIDXGIResizeBuffers = nullptr; -ID3D11Device* g_pDevice = nullptr; -ID3D11RenderTargetView* g_pMainRenderTargetView = nullptr; -ID3D11DeviceContext* g_pDeviceContext = nullptr; -WNDPROC originalWndProc = NULL; -DWORD g_dThreadId = NULL; - -LRESULT CALLBACK DXGIMsgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - return DefWindowProc(hWnd, uMsg, wParam, lParam); -} - -LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - if (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN) - { - if (wParam == g_GuiConfig.CGameConsoleConfig.bind1 || wParam == g_GuiConfig.CGameConsoleConfig.bind2) - { - g_bShowConsole = !g_bShowConsole; - } - - if (wParam == g_GuiConfig.CCompanionConfig.bind1 || wParam == g_GuiConfig.CCompanionConfig.bind2) - { - g_bShowBrowser = !g_bShowBrowser; - } - } - - if (g_bShowConsole || g_bShowBrowser) - { - g_bBlockInput = true; // Prevent mouse cursor from being modified if console is open. - if (ImGui_ImplWin32_WndProcHandler(hwnd, uMsg, wParam, lParam) > 0) - return 1L; - } - else - { - g_bBlockInput = false; // Allow mouse input. - } - - return CallWindowProc(originalWndProc, hwnd, uMsg, wParam, lParam); -} - -void InitRenderer() -{ - spdlog::debug("Registering temporary Window for DirectX..\n"); - // Register temporary window instance to get DirectX 11 relevant virtual function ptr. - WNDCLASSEX ws; - ws.cbSize = sizeof(WNDCLASSEX); - ws.style = CS_HREDRAW | CS_VREDRAW; - ws.lpfnWndProc = DXGIMsgProc; - ws.cbClsExtra = 0; - ws.cbWndExtra = 0; - ws.hInstance = GetModuleHandle(NULL); - ws.hIcon = NULL; - ws.hCursor = NULL; - ws.hbrBackground = NULL; - ws.lpszMenuName = NULL; - ws.lpszClassName = "R5 Reloaded"; - ws.hIconSm = NULL; - - RegisterClassEx(&ws); - - // Create temporary window. - HWND window = CreateWindowA(ws.lpszClassName, "R5 Reloaded Window", WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, NULL, NULL, ws.hInstance, NULL); - - DXGI_RATIONAL refreshRate; - refreshRate.Numerator = 60; - refreshRate.Denominator = 1; - - D3D_FEATURE_LEVEL featureLevel; - const D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_0 }; - - // Setup buffer description. - DXGI_MODE_DESC bufferDescription; - bufferDescription.Width = 100; - bufferDescription.Height = 100; - bufferDescription.RefreshRate = refreshRate; - bufferDescription.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - bufferDescription.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; - bufferDescription.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; - - DXGI_SAMPLE_DESC sampleDescription; - sampleDescription.Count = 1; - sampleDescription.Quality = 0; - - // Setup swap chain description. - DXGI_SWAP_CHAIN_DESC swapChainDescription; - swapChainDescription.BufferDesc = bufferDescription; - swapChainDescription.SampleDesc = sampleDescription; - swapChainDescription.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swapChainDescription.BufferCount = 1; - swapChainDescription.OutputWindow = window; - swapChainDescription.Windowed = TRUE; - swapChainDescription.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; - swapChainDescription.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; - - IDXGISwapChain* swapChain; - ID3D11Device* device; - ID3D11DeviceContext* context; - - // Create temporary fake device and swap chain. - if (FAILED(D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, featureLevels, 1, D3D11_SDK_VERSION, &swapChainDescription, &swapChain, &device, &featureLevel, &context))) - { - std::cout << "Creating Device and Swap Chain failed." << std::endl; - return; - } - - DWORD_PTR* swapChainVTable = nullptr; - DWORD_PTR* contextVTable = nullptr; - DWORD_PTR* deviceVTable = nullptr; - - // Get vtable by dereferencing once. - swapChainVTable = (DWORD_PTR*)swapChain; - swapChainVTable = (DWORD_PTR*)swapChainVTable[0]; - contextVTable = (DWORD_PTR*)context; - contextVTable = (DWORD_PTR*)contextVTable[0]; - deviceVTable = (DWORD_PTR*)device; - deviceVTable = (DWORD_PTR*)deviceVTable[0]; - - // Get virtual functions addresses. - g_fnIDXGISwapChainPresent = (IDXGISwapChainPresent)(DWORD_PTR)swapChainVTable[(int)DXGISwapChainVTbl::Present]; - g_fnIDXGIResizeBuffers = (IDXGIResizeBuffers)(DWORD_PTR)swapChainVTable[(int)DXGISwapChainVTbl::ResizeBuffers]; - - // Safe release all relevant ptrs. - swapChain->Release(); - swapChain = nullptr; - - device->Release(); - device = nullptr; - - context->Release(); - context = nullptr; - - // Destroy Window used for getting the virtual functions addresses and unregister its class. - DestroyWindow(swapChainDescription.OutputWindow); - UnregisterClass(ws.lpszClassName, ws.hInstance); -} - -bool LoadTextureFromByteArray(unsigned char* image_data, const int& image_width, const int& image_height, ID3D11ShaderResourceView** out_srv) -{ - // Load from disk into a raw RGBA buffer - //int image_width = 0; - //int image_height = 0; - //unsigned char* image_data = stbi_load(filename, &image_width, &image_height, NULL, 4); - if (image_data == NULL) - { - return false; - } - - // Create texture - D3D11_TEXTURE2D_DESC desc; - ID3D11Texture2D* pTexture = NULL; - D3D11_SUBRESOURCE_DATA subResource; - D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; - - ZeroMemory(&desc, sizeof(desc)); - desc.Width = image_width; - desc.Height = image_height; - desc.MipLevels = 1; - desc.ArraySize = 1; - desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - desc.SampleDesc.Count = 1; - desc.Usage = D3D11_USAGE_DEFAULT; - desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; - desc.CPUAccessFlags = 0; - - subResource.pSysMem = image_data; - subResource.SysMemPitch = desc.Width * 4; - subResource.SysMemSlicePitch = 0; - - g_pDevice->CreateTexture2D(&desc, &subResource, &pTexture); - - // Create texture view - ZeroMemory(&srvDesc, sizeof(srvDesc)); - srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; - srvDesc.Texture2D.MipLevels = desc.MipLevels; - srvDesc.Texture2D.MostDetailedMip = 0; - - if (pTexture) - { - g_pDevice->CreateShaderResourceView(pTexture, &srvDesc, out_srv); - } - pTexture->Release(); - - //stbi_image_free(image_data); - - return true; -} - -void DrawMenu() -{ - if (!GameGlobals::IsInitialized || !GameGlobals::InputSystem) // Check if GameGlobals initialized and if InputSystem is valid. - return; - - // Handle new ImGui frame. - ImGui_ImplDX11_NewFrame(); - ImGui_ImplWin32_NewFrame(); - ImGui::NewFrame(); - - // Init class instances. - static CGameConsole console; - static bool AssignConsolePtr = []() { - g_GameConsole = &console; - spdlog::debug("[+CGameConsole+] Created CGameConsole Class instance.\n"); - return true; - } (); - - static CCompanion companion; - static bool AssignCompanionPtr = []() { - g_ServerBrowser = &companion; - spdlog::debug("[+CCompanion+] Created CCompanion Class instance.\n"); - return true; - } (); - - // Handle game input if one of the menus is open. - if (g_bShowConsole || g_bShowBrowser) - { - GameGlobals::InputSystem->EnableInput(false); - } - else - { - GameGlobals::InputSystem->EnableInput(true); - } - - if (g_bShowConsole) - { - console.Draw("Console"); - } - - if (g_bShowBrowser) - { - companion.Draw("Companion"); - } - - // Handle end of frame and prepare rendering. - ImGui::EndFrame(); - ImGui::Render(); - - // Set new render target. - // This breaks 4:3 in main menu and load screen if not applying the games DepthStencilView. Applying the games DepthStencilView makes ImGui not render tho. - g_pDeviceContext->OMSetRenderTargets(1, &g_pMainRenderTargetView, NULL); - ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); // Tell ImGui to render all the draw data. -} - -IDXGIResizeBuffers originalResizeBuffers = nullptr; -HRESULT __stdcall GetResizeBuffers(IDXGISwapChain* pSwapChain, UINT nBufferCount, UINT nWidth, UINT nHeight, DXGI_FORMAT dxFormat, UINT nSwapChainFlags) -{ - spdlog::debug("Resizing IDXGIResizeBuffers..\n"); - // Re-create render target if our window got resized. - if (g_pMainRenderTargetView) - { - g_pDeviceContext->OMSetRenderTargets(0, 0, 0); // Set render target to null. - - // Safe release the render target. - g_pMainRenderTargetView->Release(); - g_pMainRenderTargetView = nullptr; - } - - ImGui_ImplDX11_InvalidateDeviceObjects(); // Invalidate all ImGui DirectX objects. - - HRESULT hr = originalResizeBuffers(pSwapChain, nBufferCount, nWidth, nHeight, dxFormat, nSwapChainFlags); // Let DirectX resize all the buffers. - - if (!g_pDevice) // Valid device? - return hr; - - ID3D11Texture2D* pBuffer; - pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pBuffer); // Grab the swapchains buffer. - - if (!pBuffer) // Valid buffer? - return hr; - - g_pDevice->CreateRenderTargetView(pBuffer, NULL, &g_pMainRenderTargetView); // Create render target again with the new swapchain buffer. - - // Safe release the buffer. - pBuffer->Release(); - pBuffer = nullptr; - - if (!g_pMainRenderTargetView) // Valid render target? - return hr; - - g_pDeviceContext->OMSetRenderTargets(1, &g_pMainRenderTargetView, NULL); // Set new render target. - - // Set up the viewport. - D3D11_VIEWPORT vp; - vp.Width = static_cast(nWidth); - vp.Height = static_cast(nHeight); - vp.MinDepth = 0.0f; - vp.MaxDepth = 1.0f; - vp.TopLeftX = 0; - vp.TopLeftY = 0; - g_pDeviceContext->RSSetViewports(1, &vp); - - ImGui_ImplDX11_CreateDeviceObjects(); // Create new DirectX objects for ImGui. - - return hr; -} - -IDXGISwapChainPresent originalPresent = nullptr; -HRESULT __stdcall Present(IDXGISwapChain* pSwapChain, UINT nSyncInterval, UINT nFlags) -{ - static bool InitializedPresent = false; - if (!InitializedPresent) - { - spdlog::debug("Initializing IDXGISwapChainPresent hook..\n"); - if (SUCCEEDED(pSwapChain->GetDevice(__uuidof(ID3D11Device), (void**)&g_pDevice))) // Get device via swap chain. - { - g_pDevice->GetImmediateContext(&g_pDeviceContext); // Get device context via device. - DXGI_SWAP_CHAIN_DESC sd; - pSwapChain->GetDesc(&sd); // Get the swap chain description. - ID3D11Texture2D* pBuffer; - pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBuffer); // Get swap chain buffer. - - if (!pBuffer) // Valid buffer? - return originalPresent(pSwapChain, nSyncInterval, nFlags); - - g_pDevice->CreateRenderTargetView(pBuffer, NULL, &g_pMainRenderTargetView); // Create new render target. - - // Safe release the buffer. - pBuffer->Release(); - pBuffer = nullptr; - - originalWndProc = (WNDPROC)SetWindowLongPtr(sd.OutputWindow, GWLP_WNDPROC, (LONG_PTR)WindowProc); // Hook current output window. - - // Initialize ImGui. - ImGui::CreateContext(); - ImGui_ImplWin32_Init(sd.OutputWindow); - ImGui_ImplDX11_Init(g_pDevice, g_pDeviceContext); - - InitializedPresent = true; - } - else - { - return originalPresent(pSwapChain, nSyncInterval, nFlags); - } - } - - DrawMenu(); - return originalPresent(pSwapChain, nSyncInterval, nFlags); -} - -void InstallDXHooks() -{ - spdlog::debug("Initializing DirectX hooks..\n"); - MH_CreateHook(g_fnIDXGISwapChainPresent, &Present, reinterpret_cast(&originalPresent)); - MH_CreateHook(g_fnIDXGIResizeBuffers, &GetResizeBuffers, reinterpret_cast(&originalResizeBuffers)); - - MH_EnableHook(g_fnIDXGISwapChainPresent); - MH_EnableHook(g_fnIDXGIResizeBuffers); -} - -void RemoveDXHooks() -{ - spdlog::debug("Removing DirectX hooks..\n"); - MH_RemoveHook(g_fnIDXGISwapChainPresent); - MH_RemoveHook(g_fnIDXGIResizeBuffers); - - ImGui_ImplWin32_Shutdown(); - ImGui_ImplDX11_Shutdown(); -} - -void PrintDXAddress() -{ - std::cout << "+--------------------------------------------------------+" << std::endl; - std::cout << "| ID3D11DeviceContext : " << std::hex << std::uppercase << g_pDeviceContext << std::setw(13) << " |" << std::endl; - std::cout << "| ID3D11Device : " << std::hex << std::uppercase << g_pDevice << std::setw(13) << " |" << std::endl; - std::cout << "| ID3D11RenderTargetView : " << std::hex << std::uppercase << g_pMainRenderTargetView << std::setw(13) << " |" << std::endl; - std::cout << "| IDXGISwapChainPresent : " << std::hex << std::uppercase << g_fnIDXGISwapChainPresent << std::setw(13) << " |" << std::endl; - std::cout << "+--------------------------------------------------------+" << std::endl; -} - -//################################################################################# -// ENTRYPOINT -//################################################################################# - -DWORD __stdcall DXSwapChainWorker(LPVOID) -{ - InitRenderer(); - InstallDXHooks(); - return true; -} - -void SetupDXSwapChain() -{ - spdlog::debug("Setting up DirectX thread..\n"); - // Create a worker thread for the console overlay - DWORD __stdcall DXSwapChainWorker(LPVOID); - HANDLE hThread = CreateThread(NULL, 0, DXSwapChainWorker, NULL, 0, &g_dThreadId); - - if (hThread) - { - CloseHandle(hThread); - } -} \ No newline at end of file diff --git a/r5dev/src/input.cpp b/r5dev/src/input.cpp deleted file mode 100644 index e85ccc19..00000000 --- a/r5dev/src/input.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "pch.h" -#include "input.h" \ No newline at end of file diff --git a/r5dev/src/logsystem.cpp b/r5dev/src/logsystem.cpp deleted file mode 100644 index 78b7e8dc..00000000 --- a/r5dev/src/logsystem.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "pch.h" -#include "hooks.h" -#include "logsystem.h" - -LogSystem g_LogSystem; - -void LogSystem::Update() -{ - if (GameGlobals::Cvar->FindVar("cl_drawconsoleoverlay")->m_iValue < 1) - return; - - if (m_vLogs.empty()) - return; - - static void* pCMatSystemSurface = MemoryAddress(0x14D40B360).RCast(); - if (!pCMatSystemSurface) - return; - - static int fontHeight = 16; - - for (int i = 0; i < m_vLogs.size(); ++i) - { - if (m_vLogs[i].Ticks >= 0) - { - if (i < GameGlobals::Cvar->FindVar("cl_consoleoverlay_lines")->m_iValue) - { - float fadepct = fminf(static_cast(m_vLogs[i].Ticks) / 64.f, 1.0); - float ptc = static_cast(ceilf( fadepct * 255.f)); - int alpha = static_cast(ptc); - int y = (10 + (fontHeight * i)); - - std::array color = GetLogColorForType(m_vLogs[i].Type); - addr_CMatSystemSurface_DrawColoredText(pCMatSystemSurface, 0x13, fontHeight, 10, y, color[0], color[1], color[2], alpha, m_vLogs[i].Message.c_str()); - } - else - { - m_vLogs.erase(m_vLogs.begin()); - continue; - } - - m_vLogs[i].Ticks--; - } - else - { - m_vLogs.erase(m_vLogs.begin() + i); - } - } -} - -void LogSystem::AddLog(LogType_t type, std::string message) -{ - if (message.length() > 0) - { - m_vLogs.push_back(Log{ message, 1024, type }); - } -} - -std::array LogSystem::GetLogColorForType(LogType_t type) -{ - switch (type) - { - case LogType_t::NATIVE: - return { 255, 255, 255 }; - case LogType_t::SCRIPT_SERVER: - return { 190, 183, 240 }; - case LogType_t::SCRIPT_CLIENT: - return { 117, 116, 139 }; - case LogType_t::SCRIPT_UI: - return { 197, 160, 177 }; - default: - return { 255, 255, 255 }; - } - - return { 255, 255, 255 }; -} diff --git a/r5dev/src/opcptc.cpp b/r5dev/src/opcptc.cpp deleted file mode 100644 index 3fd0b8fa..00000000 --- a/r5dev/src/opcptc.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "pch.h" -#include "opcptc.h" - -/*----------------------------------------------------------------------------- - * _opcptc.cpp - *-----------------------------------------------------------------------------*/ - -void InstallOpcodes() /* .TEXT */ -{ - spdlog::debug("Patching the game executeable..\n"); - //------------------------------------------------------------------------- - // JNZ --> JMP | Prevent OriginSDK from initializing - //Origin_Init.Offset(0x0B).Patch({ 0xE9, 0x63, 0x02, 0x00, 0x00, 0x00 }); - //Origin_SetState.Offset(0x0E).Patch({ 0xE9, 0xCB, 0x03, 0x00, 0x00 }); - //------------------------------------------------------------------------- - // JNE --> JMP | Allow games to be loaded without the optional texture streaming file - dst002.Offset(0x8E5).Patch({ 0xEB, 0x19 }); - //------------------------------------------------------------------------- - // JNE --> JMP | Prevent connect command from crashing by invalid call to UI function - dst004.Offset(0x1D6).Patch({ 0xEB, 0x27 }); - //------------------------------------------------------------------------- - // JNE --> JMP | Prevent connect localhost from being executed after listenserver init - //Host_NewGame.Offset(0x637).Patch({ 0xE9, 0xC1, 0x00, 0x00, 0x00}); - //------------------------------------------------------------------------- - // JA --> JMP | Disable server-side verification for duplicate accounts on the server - CServer_Auth.Offset(0x284).Patch({ 0x90, 0x90 }); - //------------------------------------------------------------------------- - // JA --> JMP | Prevent FairFight anti-cheat from initializing on the server - FairFight_Init.Offset(0x61).Patch({ 0xE9, 0xED, 0x00, 0x00, 0x00, 0x00 }); - //------------------------------------------------------------------------- - // CALL --> NOP | Prevent squirrel compiler errors from calling Error - Squirrel_CompileError.Offset(0x12C).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 }); - //------------------------------------------------------------------------- - // CALL --> NOP | Prevent random netchan encryption key from being overriden by default key - NetChan_EncKey_DefaultAssign.Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 }); - //------------------------------------------------------------------------- - // INLINE CALL --> VTABLE CALL | Call LockCursor VTable class function instead of doing it inlined. - //------------------------------------------------------------------------- - // .text:0000000140548E2C 80 3D 5C 2E 1D 01 00 cmp cs:byte_14171BC8F, 0 - // .text:0000000140548E33 48 8B 0D 16 25 EC 0C mov rcx, cs:g_InputStackSystem - // .text:0000000140548E3A 48 8B 97 18 01 00 00 mov rdx, [rdi+118h] - // .text:0000000140548E41 C6 05 91 7B EC 0C 01 mov cs:byte_14D4109D9, 1 - // .text:0000000140548E48 48 8B 01 mov rax, [rcx] - // .text:0000000140548E4B 74 10 jz short loc_140548E5D - // .text:0000000140548E4D 4C 8B 05 8C 7B EC 0C mov r8, cs:qword_14D4109E0 - // .text:0000000140548E54 48 83 C4 30 add rsp, 30h - // .text:0000000140548E58 5F pop rdi - // .text:0000000140548E59 48 FF 60 60 jmp qword ptr[rax+60h] - //------------------------------------------------------------------------- - // TURNS INTO: - //------------------------------------------------------------------------- - // .text:0000000140548E2C 48 8B 07 mov rax, [rdi] - // .text:0000000140548E2F 48 89 F9 mov rcx, rdi - // .text:0000000140548E32 FF 90 90 02 00 00 call qword ptr[rax+290h] - // .text:0000000140548E38 EB 2F jmp short loc_140548E69 - //------------------------------------------------------------------------- - MemoryAddress(0x140548E2C).Patch({ 0x48, 0x8B, 0x07, 0x48, 0x89, 0xF9, 0xFF, 0x90, 0x90, 0x02, 0x00, 0x00, 0xEB, 0x2F }); -} diff --git a/r5dev/src/pch.cpp b/r5dev/src/pch.cpp deleted file mode 100644 index 17305716..00000000 --- a/r5dev/src/pch.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "pch.h" \ No newline at end of file diff --git a/r5dev/src/squirrel.cpp b/r5dev/src/squirrel.cpp deleted file mode 100644 index 584af969..00000000 --- a/r5dev/src/squirrel.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "pch.h" -#include "patterns.h" - -char* sq_getstring(void* sqvm, int i) -{ - std::uintptr_t thisptr = reinterpret_cast(sqvm); - - return *(char**)(*(__int64*)(thisptr + 0x58) + 0x10 * i + 0x8) + 0x40; -} - -int sq_getinteger(void* sqvm, int i) -{ - std::uintptr_t thisptr = reinterpret_cast(sqvm); - - return *(int*)(*(__int64*)(thisptr + 0x58) + 0x10 * i + 0x8); -} - -void sq_pushbool(void* sqvm, int val) -{ - addr_sq_pushbool(sqvm, val); -} - -void sq_pushstring(void* sqvm, char* string, int len) -{ - addr_sq_pushstring(sqvm, string, len); -} - -void sq_pushstring(void* sqvm, const char* string, int len) -{ - addr_sq_pushstring(sqvm, const_cast(string), len); -} - -void sq_pushinteger(void* sqvm, int val) -{ - addr_sq_pushinteger(sqvm, val); -} - -void sq_newarray(void* sqvm, int size) -{ - addr_sq_newarray(sqvm, size); -} - -void sq_arrayappend(void* sqvm, int idx) -{ - addr_sq_arrayappend(sqvm, idx); -} - -void sq_newtable(void* sqvm) -{ - addr_sq_newtable(sqvm); -} - -void sq_newslot(void* sqvm, int idx) -{ - addr_sq_newslot(sqvm, idx); -} \ No newline at end of file diff --git a/external/detours/include/detours.h b/r5dev/thirdparty/detours/include/detours.h similarity index 100% rename from external/detours/include/detours.h rename to r5dev/thirdparty/detours/include/detours.h diff --git a/external/detours/include/detver.h b/r5dev/thirdparty/detours/include/detver.h similarity index 100% rename from external/detours/include/detver.h rename to r5dev/thirdparty/detours/include/detver.h diff --git a/r5dev/thirdparty/detours/include/idetour.h b/r5dev/thirdparty/detours/include/idetour.h new file mode 100644 index 00000000..63474cfe --- /dev/null +++ b/r5dev/thirdparty/detours/include/idetour.h @@ -0,0 +1,39 @@ +#include + +#ifndef IDETOUR_H +#define IDETOUR_H + +#define ADDDETOUR(x,y) static size_t dummy_reg_##y = AddDetour( new x() ); +#define XREGISTER(x,y) ADDDETOUR(x, y) +#define REGISTER(x) XREGISTER(x, __COUNTER__) + +class IDetour +{ +public: + virtual ~IDetour() { ; } + //virtual void attach() = 0; + //virtual void detach() = 0; + virtual void debugp() = 0; +}; + +namespace +{ + std::int32_t npad = 9; + std::vector vdetour; + size_t AddDetour(IDetour* idtr) + { + vdetour.push_back(idtr); + return vdetour.size(); + } +} + +class H : public IDetour +{ + virtual void debugp() + { + // + } +}; + +REGISTER(H); +#endif // IDETOUR_H diff --git a/external/detours/include/syelog.h b/r5dev/thirdparty/detours/include/syelog.h similarity index 100% rename from external/detours/include/syelog.h rename to r5dev/thirdparty/detours/include/syelog.h diff --git a/r5dev/thirdparty/detours/libs/detours.lib b/r5dev/thirdparty/detours/libs/detours.lib new file mode 100644 index 0000000000000000000000000000000000000000..f85304f423743a04eae4f6e2e9cdfbb0e6fc7edf GIT binary patch literal 852394 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%0D8dtxTai2FTQqDY)>9 znc6a?t4OIUQ*fykGmT}+Fp<(!rhFt)I>?kcBBi-ZSte3C$rKvfk?A5+eibRDWePTa zh(=1WM)@c>_{mIJ_~O=Bkzy}X7Kju_nQ}^`sANhtvC!9&DeXmyTBh_9DGgZ{XeFOUgIv25FB;cz3dM_BP?>^@)VPs-NrG9s$bC_~XbqjlY$iDrLbXI; za!8+vLh&La`@+pne07_~HcSrg4B|1RQ3|I>DKAsxl$U);S<2dJ?u$|^9zT-DDHJaX zA<5$u3JqD|(?HMReMWK)%?F54IuS zEq-S5HA?iwcm#7@n9Nw{;xiYJh2^3z#-q^X#e7M+*7zT(Yw~qg^u>4-y1bb$DPC)$ zI!))Z8<2%JqA$jy(AAUql5}-lnO)W7%admbo3-01bonTBCEfe>HJ>4puHm9D#-q^X ztI)Nu`4`1ipF-E)(U%_cSuN@MMfAmZ6uSHsy6%6L{=nqxwdjlSD0KD8 zr;E?TNmnBW`pZ5cjef_xY-m6=m5n7?=XaWX1&O{Gk3v@f^W}`cHu!l@$lk+eE_e`( zqA$jy&_yFdldhv(Z+vL-^{MEK@d&N3s%DI)y2L(n4PW-mj`s4b`aSMT3mz=IG1CWs zmAM>96dN`EQt>&^t!D}!^W#BSDe7W8iVz0D)fB=>ZOfc8`8p>0Vmyk}2xh*d();qK z#GNKz_e5WeN1-c3p^IvY$yXIo7~@gs3T3|3_)GEntN)4-y26+*^LUwjeJGL{k3v_tLKnr$`2NdkS3?FO#o@qA$jy&=tvisqvTc>&#bEtMD}4-x}upcHU3f>j_#F_&DX5(AWRT_F&>4k7`U2r zt*+7Tg~`{qqA$jy&_xZUDPC0OO}-9@z8H@}mrkLJ%7)2Tp6HA5D0Ia!UuyiNczy4% zt2$q6!h=wert~n3N1#Wy9plPxQrj6uJ@=x+q>I zUvZ)@#-q^nfkIcU9KTn5?F(dKlIV-^D0C$%bj{qo^rXqx644jqQRt$+&(v;IIdnUP zueXq{y`nG1qtKPC&~<2f=f@^rmqcHTN1-c)`7%#Kldo5zFUF(Lm8#H1X=w7*keV(S z#-q@c2CAtXwm8&&DPQ*^?f}sj<5B1u&U}%x9e$Vpxvn+&iV=M=9)&JF^CiX0W8CD9 zomhzkSr{$)Vmu062IfoB)##5QJ50V7ioO_+LYGmY>&UWyM)370(zR3c#ds9D(iOTE zy+1q4$S>b9q#PfP2GtZVY7b4mw4yJ@qtG>m`I2-|nK$`bC;DPM zLaS&46%uB~;;)^JMttIcX*4shL|=?Y;cFb+O(EpmlCSPg{F(76d}T6U(r8)fYoh3j z@i3=@B847orS)eTW(ni*v$LU*iPE%+iBB|Oi#VAH_}SSwE0uxv8>IUBMTGPV)A|kc z(}wwlM}&m=X?yhx@D*?a2VMH>r`Mdr+x|<_iuh*p*L-naKIwW8J1idjmDhZE9 zyiu1P{c(YiWbd1O()E7*GaDjD#B_x$dr8w%yCQaOn-!Sccz?&6n{o~+a3kN{6! zgLs`OJeW?nn`ov}sUl7GE`5;CLaO)~084K9>j0vI*@bucDL6JE$3l6OPU1DYdx~@B zJ2_>Y49PvX3zeUMN14*SOAUHh2JJ`ohtM&3C zfBa)>UOn7<{6`U9?KIwEjO7rE1wlIHXgxlr&q7$Zi=|NM+pKgb9jp}Vd<>>yoX^Qr zn)5j+%bDb?)%s`+X}Xw%xP%yOJZp*cf>zrroVQAPpLktNA_xYQ1$}~Xls1M4+L+YT z!~~t-8WoLiR=VPBwrhIINL=ygTEuqAl`Z-enOiBm(AG>Txp3A(#1`HF7NX=|JStTd zp#@c{DvAdS6$LiE7Lp6*%P-JsQ=MO)bv98PuO{Y~Obem}Yc2{;Rw{|ouT_SB6CqUz zMJTLDlKB(@<<>&1@5%t>cP-Sjtb$jQ!dn!n>Txav*wj8K17YE$2!X=Qs(R}jmO@Mo zP(Iz}P6c}_NVmzi%%Kn>r?Av*E3z@aYoVTH6;{*VqCZfkzpP0bCs+@M>}ow6rqM*9 z-qG6^uHDot{fitq#)|NmbyLdhR5%pmMV4uvie^4#_ksja^a}8sFXkwo${tSX)rzx1 zYd&{5aOMFhm`fpyhWXOXp~?uDJ1D&58{R4W<##RAvt_zjdN)hFcO5K>!Fp$JVt7S= zmX)N?!Jty*yLpw=BIEwccv4dRU%gA}OJ-Oqg{EYr^}rV!0`M&euO1#f-Z4A9+VyMW zw(4pqL zrHPMDB&H@~Jv)V-_`WkQXXrKCbx!mS_}vzH=$VW)d0RlQlZvl8<41;;Q3{gxLgZnL z_*0O))>t~dPRpk-1<8wsTUlBxH8a-axgx&F;G0EDq%f48*1|{fPKZ3jN5+~wI@{YC zYhv%wGAK+z^6Gl5gnTDZLS;GYp_#T7j_Zgh8pz?y|MQHjd zZh*j#47^#5?>*47z>_+YxC+i+O5XY4sWpqSv2}23>DLa5g1|F=Hs>oS{o2BLrO3lM zt%vUl94~@z&m7KGki7Tdd_&~noYv*hp1YoNapCg3!sVfg3n?N`Ve;rq#8vR!|EzF% zYmuQ<=1Y<|T1!7VtMS8^xODXE!sR`Hb2NBD7jr&%vzos%Q9lNQ=k5~D=L|5_4|?ap z4YdcKd@DV1WZ|>u7q-Faur)rjvtZL}+^0j!0uA?P22XO+kY@mA&C0DT{LmK=ixrl9bVyrbZ`FY*;8&*nJp2f>d_LGqfy zt^5g$@9-m2ki1uL>kS_NpBY=B@>0MvU*s!H-fHkHKUuguKV;}h@Kip{`N{*VG z`V2g)&T_tj>TepH8=x@ErUC&&8*Qw~qx`rBo>RXPSHbB=?dNmwIR3`igo4T+njoPf zcy@_=1?9(lI9vnIs0&2B6 z)S-yOIO1omJo|v244w@lUqSin40#8^^UF<=R&e@Ff!izaOuWU|Sh=zmz6i)$3mzMa z5R5&3*1|{iu&KyHd}OT2TLF4o@LjN!XSOey{5s-imrFL2@p8s|i!Jq-Xq5hC!B zMfMa`*-zKHVDz&H9|$T10tkz_+6a@_s9#yg$Hqw+Qka?-eaSoWWOH zMI$fNfDj>rQmGX=?e1in{AkXQ4* zB&l$D4ZugIOUV?Let|`l7XiMZMUXePi1H?ZZ*~#nZ78C=9pF1y1bNqrDDN)#o)tk} zsD)<_RyoKe5R}tlDz!y;jdHN#C`w)DSiXd-k5#_A`-;N^4`>lxb z{s7qtdjoI(!z9GZ^(A zJf8O}`+7k8ik7s5Zj87YhIaxO-RUodFn_$B8Nsj1<~QAtTc9ZUBxsv#+o*m`hgY0g9#v?-yw zlvvcxhF@4J+;;$do7} zveAJ10vue%gT+n=r7Z1d(CLGsG7^&0ljQZ8)EvD;Z;PovKS>a2#vJ|BB*Az>+Hg}Z>T%tX*j zTVb~nmL_8Im&t6@utD~XMNf|Lr1TH&uT71M!)-X)=qN*iK~Rf~s?5!_DuthkF-@P4 ztV5x8aGA~$%_k&G+utuNoT_7xcW-)XgU$%Y;E~bQC6Cx!ZbYHh8b_t+w8*CfqoHdY z5m7d@L1OaisVsN$~{wv z8zI}?zMJRdC_^GkH1cbY3owi3KR8v722z_WuDO$q$>hMj8{DOMRg0P%X zr1*r>#(V@QB`U=tP(4LicA2ORnMElR;glJ=SS>YY5Qg;eHw#bZxOb5ul}j#1%HZ~e z#VIy5JvvF3zivsM%NEVETs&CCl&ZyByFx;5u9Xya`vBL!DEDkT)vGMP+X;nmJeV7_|S$0B5p3afkh1?i;z zg=Ih^2-Bn6vlAjhF=P%5?CI^(M;jUz5E2#;F$jme*n9yqFGrVaU>g|j-QQ1(@p_PV z1n%B+WaC5(5!$xV8iks8rf>I#@`GH#5#l+naGauaf?GNwo-Q$$6$$+Uf+NtYI=awj zF-4!IBVZUSI~tGNk_ONeIwQ33X!!>O`iT+4tx}FIci~q&x#k_=t@RBE z!#ogzSn@PPl=TLDOc2TA3}$-v9S{=cs|}3^!-biSE{9MgLi-H}$0LQiEgoSH_xAVG zhWZ2#9^j)9+(4&^W?|DzKB1!=xI%HUk^>YAtGUDSrV;{%d0Q_QE-9q_WC~^9F*&4K zAE6uwP$VGtaHkbL(MPjFeKg)->?s(`{2XjOxZN^^DI}EA)H_g2LKF=u3H2e0l7%H2 zQ{=o_Hrfr-r6on-2(nMAUK6Virh~5uF}}LEgcK~8P_8zBA68&l zEuiq_51u>t)=_?&*HORV{sCbaLHGCQ+_`IdN(#Ad(7>sq*yWwTH67h4MLE6A~7IFLm)qA*|vcpT$#+c#HiIX(VTJCCW(R zMOILnk#{Nj6sR;jvs@=t4Tzc7evtpdZ!-AN(g|w2-IG#ZUYLL2TC1)87pfid@AOyO zjQGuq6BSjP9{%B<|#-QTpXKJK>N;-}HaZ$%uPaBEt-4huG<92&Q9 zaVKo+WPFE$r@u%oHSUi+_Qt&@;EC+ywtw8y9wuZoP2fi`D$&IcmEL$hy8M?(&kSGe*5w3(5&yD&MJe|Y3zGa z+YOvOsQkW_$up}Z{+`xG+#?Bb_UA609{r)oJ`0MsJ%47Q?ko>o= z-{zRU7j3RBJpN$JfV=8XM?R~$^oNbJu+5wCEeyHW*4~)da9v=_haMGnmvt<^@<@=d z+)?w`V4u-r&SMO`8Q;z4wolxa-dKNrljGc=+^&5(-#itTS8sXCPluj&X!zs(bGTm4 zj>0!%-QT-wOnjPs$2}q@z^C1-eye^>UU2g(gMX8*_treGIT%|z8Q<(?N52_-_q%r2 z4*5R3+atT2gKo9=w`Jm@a_>L$UgEH~3oa^Qe8D%WZaNd}ck;)xkvBJM+HUL9?)4X) z=8cZ~U%xWX&hB`j$CZ3|IiR+iaM>&Jf6EWf-sHHzH9$Y}#FDEOo1VEb`rgF!gZB>| zdW&N&dd9aQ%>LQe*anM`WSrPiSdOCQk?>1B@1eY`2F%F{pF?$7hf zY_87MzQI;j#%D7z@t>Df`zG7|9Nq9rP3OiAm#a@!pZk2mydCPgf37L>3h_m)QQK7= zzAUP8(=HY7y+~Z8?jMlj-pxLG&h=8Ck5Z34wRzi#quAcY_}n8ZY#y>|)Zt~v7L=On zepypt($;|O3v7G2O<%Djv*x8bIC8@Hdej^H;GZ*Tvp)_v@ z{cOy-d&Vsg+sqtD@6K(@?l&BNd$9kuN-JA_Ix_6!)`0Tu1D2g%zp!uid$F}qKVlf) z#nUUCxBleSb=9T)l~*RNsFhKvd6lXSI+kA8y=%&y$M3Hagf)zBTKg|szMT>jk=p&| zGjHa`TwXe%&)Y*s4%a>L>4QhD#;(Hv=WoWht-+?iy(@ZtV@UY;&uL%gg^svc^HKE} z$DZFb*|&F=y+aDx1MH(v+hr{sIz95@fsWoAE*)X5QJZiUnTG-up9%#T{Q-T&^jsa|heWNp25zV>@~xAlVY`IV}aSAE~w zGuvx#U)kk@&%En5nc(!>8UL{Mhiu;_*4~O&%>k&JYP)6SD?B}!n;g^I`@t9Ujf*M| z*FD!IUA{l$X_p@YKe-byQqx^SYtQdmeWr1QeU-bvdu#k!uR7NBoXePx zM}<#BJH+@FY-{%8%bv47t6k&X)BT%nd2i|X!d@Nmcv;Hc*X8qu-}?sJ!RghQ-Gb{K z)i3US`o}rP)pH-a>}Wfr?2^|F{Fkj=-FbD3EB)+#$1w=TSE_Ez=ij8QvwL5acy;~I z$)LoN)=tZk-sbcFH`92ce6=z)8rJCbOZfWMy>>k*6%=EW>3H<<*WcD@i4TY} zzQO00?i#y$^`BLqoyamQU6cKtdf!*g%Gi81V_xFlzcuyw7M}uPd~+YZSH0RVN1ui6 z-ZSpp@Q)4ietnWy>G0{5$DJij&Ud6(VqeZB9^2B+DZ z^m9MCdT!3#GM}u&t7c6(3ZM4wOa061pJu$d-@H?;o?q4Tsk$zH$)_=Y_0;uotGBCn zJ$yck@!1djyq2NW%tdyC-#AtM=5EWY2j*;<)%TAC$3XYA8UdQCSf4|is^+s>(`n~JEYO5ct`s76Y8zDo+yqVqr!-onKzTfjs z#XNHDcDlj6wlk|nonBWhe9+q6HeZ(?uia^T>shrMIDW?XR-Sa+m+kP)XU^WPzplOJ zK0_Vc>ujUuCz_op?^M5IcDHD>yNvI^)38TRMhI&^uldW4&0jXK-}YYbSC1;Tw|~?< zql*8dM<~z1PNerxg-T<`J)bz??^dC2j59+97(!b%_&TFu@QTj68&7_AXC2Bt<4aSG z+BiOLVyE3dy!mp;=x%j7T>sd zUmJp&*Q(WL+mD~v-0X7r$?$mFPq2j+y47|g>ot0Oa7m9T-M&A3bm~_1xel}b*LbAn z`}2*?W)Cd)_C+Hc3t)V27e1JAbNj0CcmMvP?&=D2_PUR-xih9q=JFn{3xXHTs~v^c z;fya}quYWl%}<8keEu^1)`glwdc6Gb>rTO;m)rKeG9jq<)SkHJ86zLH-3Ply+}(XI zdhGcN%T_ma5BjS0UwL1CqkBDmWtqr{^}Gh#;KQ?wZ_(emSAW_XIl6t_kKYbna6Nk4 z+Y5bbw=J`J(O-$t8_xd>c{do}=dZ`V@b?aVnhFVzT$o1 z?$;QvP_dvw0~RA z>R;UO&WP&a=elcK@Yc>>e>c5WLa7J8ua9cqYE9@pd`ynOq#c> z^Y=ggG4b$n&s9Nl8hw?ZZ9H9#Yp*dDQ`^1lyzXI}K0}0`RUcffW_$6#{y%j8sGN_k z+R`?0{@JyE%|Uz5_~zHC;o#rtScgh|wm6J9)_VNt@MEWj7^jB$e>$K>o889)P~R9| zeE;&@Tj{s`5dUYx^=}7_KcstEdqwF{ISx+W#+B|I-{SNc9FDnr6{znN{gf97K2#Z@l< zFLK`8&c{aiJ^33SN@sj?Tb9kt8&f?cxBaU-r2|i#xZ62k^TOkHPs1Fy9~kR4e-zqX z#@BQ5wf>VvIJJA)WZ}3{yK3#Pm2q)b;;0*Y+9vPyf1OzNLwuCF3h9kMlRaHn*Ve7| zCf#JG@o`VKUL1Pj!^RhMuA$4e1o=Kd-cDeArz@n^S@Edh$^Ji{`}^qM&UZ%lYUI1I zzvi2|ExOh2d%EfdLHL>R4GEw9Md@+VSG{@vd6`ukM@3z0A8UwnpWM#o^O{F4W?ou} z{oEOd!5 zcHMFMOZQvTw_SYI=koUr?JBqLurcn|u6oF~HH`0niMPwyp8GkVafR$jt-F8K$@YHz zg_EmpJ~=ITz|Pn8vn~n3Q^wbNO2W6DS9)x&u;Eqq)Gqzs9!dS`qeUY+`(E+eU^i^f zw5fP!P>sUZ>(BYkD!N=>wx-R-PuJC)5VYari(`I_J{^{OeD%Vn#LLef@A`9!?{(kCx{fQ} z@7&EPh^)t7(#T|4&vTWkxfL3-0iHMT!tzs!E+7Y%Jz^!~o%vzp7d+&|Y~ z^2eulZMyLOW^7Ajd`)Hs&n)9D%7xe~qp2$$%xjT=!o(n{?K_f$QbQIHH8H ztlDn)*fT5BZFfxDWK(_l@4a80ZryTevrli`bv^sX$sx-;s$&d0gz>HZ`tGXiu47i5 z9&@C^`=hhQeRQ>{@8QP-OFPFuSklGu?QA>=>Fc$R!jdk zsq5>wn106gO-_D({}8s-F}`bwXR8?Oe#$*{c8SmQ%Jseu-)`IT!yFgeIo-NGsh9Y2 zC$4d?MdACV?Dzh|Hm^GGsY=YeJh8{udoOl9H0$U9&xKuv4SHPTCd&MD#<%>c5bdL0 zaiyZ?%pZlR#~O^^-=ypEpWoYfX8wzhU+){W7{`ei-%k$BHl+SJxAC}Fk`y`z#(4W7Mq^{9G5nSX9M{bc&cPijWGB!%?*_C?#Z zJDY0IZj5Jqzr>x}T>rIm^7?=FrM_~X>lWI+Lc+lT^D{FhU2A(r2thx1g7KAe^zX4} zaKjaoS`A3}spJIJ^#c_RL+khG z)whGkTHnJzymG4Dd~(m=#PPr5m>=V7^XB3Q+gf|=4V&6yk=A?h=Of?txc6krfnmq@ zkIw$5SQV^|9M+=K*E)FwKha|9E-U(;|ttTyJ_66xbRKeHs-!~I4*qbVDEmFszqjx zaR~hG%3gg0jwms{Yd)J64V=IC$TH#g`d>GWpBQwlbI5}62e-txyWzEI@n0CL^~D%V zZKpeaD&bAL_BEE@E4|?O`oXDolOM0IUUuHX3BzYLU(j{K=Qz^A_-u}RmGV=$dda8S zXD(jcZ(XyUixN9#-CUBp_M>R~i^+MIZ@poBdzU$!4r%3O+pS&TPM>4HfAS>x&eCyR zoV05ag4B(M*L;i>1@v9$7h)fcXsqjfergwet&4*PJ04n6F|1K&gV>2je#{L#`oU%%f=>s@162K zfBRgUyZ`*qDe~$1Iq~ym#>EUeSN~QIPh4tTpY(oeycRKK=-h872PdSzcHJD{>EG8& z_t%)lahJnS-1XaW7vmDf*Yss_pJmrwuEdWVx=8)ao`6FqUK*VGUvgR${+(-Wuho&* z=FRv%c{KjxX65Y$wRkw@#r#8?*WJH;+xGFM$>$FYy!mk1k%yZw?`}YP!|#ObSh(q7 z_`sCPKkmPpQ!nCY|3z)+kW5I=8JEhZTDUKOV)|F`>q+bxm||r`QXFL z7(?`KNP6ci3i9gkqxPRRTdS8D5SBY*Qq>uwa%VsPdDHlh4%hbBl8x~$p)Q9zF z9PB!({T7dnQU0%-x;6c!%jwyVn+_^fC;H&@0f;Z-OL3aw@ny&6LwBgWJr~E;>3y-W z=hySfm994bW!0$6$MZkKc4k-7J7n#g{Vyy0I-qKq@9&NI`OCZ~)5={)8lJgl{tq@M zrv~pHEC}B+zPc-yTyK{8WWm}|_EF^yJG&iSxl!Ngivf#Psw$SRRr4O=Q4V88wVk?b zzz+}4BpzPb>dSw+WRE^naZ|_3PY*Y(^YzR+owh8y*$PL{7~iJ#BNIcKJWjf`pr%Xv zq&?MRrcRnUcJ!*_b<1t~Idx?S+N&JKmzU@@{pf!JqzQDwB{YP&bB^xMCxZL@lucZc%lJ`VrzSiqdu8TKD(`)us`$D~=;(4Kz4 z_%f5JO*1oLI-@Z~OP`aLkYKjY@3LICK6?hbh@R zYecjhKkc^n%rh=!UA~_4!}0@@n-95(`AieiTm5L5eboW8vi5XXq?*xhP~{7e=_4K9 zn=`0dk3)O>2K5^%2!9F~V}X~x1dNM6v9T>#?T_7$2DMv;XNRVd18|>S>PUmS5B8u$ zdbmdhq{Jkp$Le5h<=)DplY2XlR_>AM$&q+JF)TKrRebT}#ORHYF{$_z0iVk6TG@o7;pi5|rd7!o~IKdQt620_?9 z6@#6UBZ?U|F{aqsmZD3dyCRESE|Mb+qf%nxiyv2%{^SI@c(Me_h8RtfWHiJUGtGFl zOi3z%q)Lv-D1KnD16_w*);jA+9%$G^cidM-=xJyb^242?2>f(yu z=0Og9VZE5GXL4kkJ{9+J6_CL1)S@d|V(iyVjj(&l_#gB6tC_ghxK~anWLd8vCK(JO{4DIO8nm?r_uUj&xlTss+*{b zEq>tA71>=WI2zjUj~Ak5|tE*4JE0jtd7J5^oB_O4&D$+2Yq67#?*AZp;b&; zT5!-47t(PHMdY!c%?cL?1+>V^@;%^Y!}t22%)fk6`{V)V%m9P}%x<(ZKwDaDPU znEc{GPn%*EW~7&CoLvIKcrTiUd3lmpt0w>bi;_CVU!^O7ipJBVxb47Gw#JtFpLXQN~(p#M)@Qh8!K+0 z*es?b=Bj4Xp<;$fS-CxnDGg--1D}N0^kiBtD1mxt7EyeGCrTd^ZzY5!)%nv>5L)2k zPV3k}tOV);#XUL7Xf4L?R6otruq5OqN1EE)lF4OiB6ZrBks;!IURP2tnug&MbHyKqQ@<>Jvy5Kplt6=IK5{F5G3IYb zN~U(9NkR+qA+8;eqxsGYdih^~lvKY$J-&FuLcYGGq;!nJ{VxX8Hhl@Sd9@!WcsHMQlsS} z|DX3zn39p!{_(}k|MR5eDQP9I{zFN@PP+k`?LiQo2wac;&J-&MK*3Vt9nlisDPkF{7-9 zxTF+~z^YQ5wI=-?lLS+J+$UyCO-oHm9aaLHbxgyj5|t^9eDGojNlce}6?;t^A;nAn zBykf-iA@O#sJZWyE#|{+zKaTFUl6cWm0k5-S_pI*>4HxV*y8dI{K^VvMVm_a8#WcA zy%cRKVJY_dm1ndSqD>|2g{=ak?H6q-;aAw480~k_rV^gQ=FDg>M4L*ejJ@J6j7Gc0 z$)*z8!&Z^eI*B%w5DpuiK`ko`5N#@UrjkB`P*xZL8rf9BY}l$W+GnCog_x3=65;AR zCD7GTlXb89R9{zhBb){hCgvDRAF_9~$+O2_!D*Yl>ttV7TR6K(&JKrsU2SagN)dno zf5Bl#7F&33L>9Y=Iq9{@Pu3Nu{a5U#2D-Y9zbfFum$Lh(fST7zD~#fnmxlbe}Sux_N}R2{yM;6i`wW(~24gC|KF zxget@7xPP2gI=z;USRH=n=35kaU*CZIaxk67I;fdooQU6%)<)(XMKcGYu{pDl!@b z?)f;)6nob^N6ldr7~%O_%~X3=MQ)|RLkFRkBDXq#Rt7&0{Fa+?i|-fEwiyhY#nptPh|QJf@0`Y0tS^$|*D;t&rC15!Mb!jS}| z6u}`IF-6Fx60&95WYMM)EK}t9xDjHCw6shSN8z1Q%hkbY zTTU0$@AT?QhnzchuJz!SXP>vbZH}Tm?Sszl7PTKC#-$55WrN?re5uFQqqVg6L2JZ& zd7ZJ4R1{FIsGh1JiLRyyu}O=-UTbvW=oWEPhl`6FTq@!hVv`yj!NpY(?BwDd3?~W- zOa*Wga_$tA%+joM^12pEPNJdkQ~AWqVPN_(i6jlWCLxQw~ZQ<+Kg0e3$*{6_xqu>OeWzMwx?$ zptENoIe4JZ95m%d!L38#xk&9grKlP|mWw8l|0g*)8aYEbIR;32#{zo+#{nrP#{(%R zCjcoYvw)P7kMQF;Nj8;$V`(C7yJ%Cf+Q<2>$TW?4WsIkmIz!WvuJX`KJ#q(JXuhcN z20cyF2{xxmH;6bD1rR_KJzPysBP;UO9x?<+<~Q#L?;Y-VYI{aAJc^axjX z4OMWi2}6NWxf8*Pt|OGfkp4dPA{w&l!@YNU!O43EHP*X$2xXr%RM2FNc6AesWk|nU zo==`L6WVQLY1|QI-!>=DLk<`sN0G{_P83fo(akM~0SIeK;t$k;(%bmi%(E9L zLuhbkFX&<0$y`6iUn;Ej!hZZcz--jqA*i6ym;SgEdpuKR!}&d2WUL&R(04y8fL%UCg0U}LRuLPwu8SGGE@tIIjCt*| zUKgo${-?V52|SiSUDV{Oiye4fM8KrFScfPYY7E>pc1JW*_bb|;TnAHytc*ZWYS^<; z8_m1y(Z@>_G66PTA;-eTE94H?c!fL*8?TU#NNg^punI{Z&ZR;~A7b5HgbJCI`v0Xu zQt6=v7Uth8k{-O_|acWmr z!B|;?u`BDxG|gGjmAXu|VlX-fDtcM8_}xG&gP$jU8w)C6bhEq`Zw^wl##FKW|Cdpz zwHBRd(1IKOs72OcyL=QMVN#-M$VD_1Cdz!Y+(K88hWRub?*9m^4%#7LJ>X$rTi{PX zio;PLB|f#JJn_k<5?08xHKNUYVuY%%)>tHJL)2O{4$YCWNKQvec>F<+FE$U>(|A9m z`&p!MAZV1vz3|&qXd0)dm3Ru9_oI~ZlxQ%OkQt6Zo*7ZGAEe`INz<60O|?TAC;vln zpgeH^`ik@uNNbAMNg(CPDIkeI4Wx>B2G|q$3y|XeE0FSrnmwL3WK#(%W!hTNrV`G{ zG#bNjKB*E644m$ZRvLphPs)&?^x7d3@?B0-`%WiOy7 zaqyUx;Vd|1PcoxGwx_0(##wKacv$ZM%E)M$;)R(c4gOF(zzmzd9NFEOeS|~XoZNM& z#WFi49-65E8qE}qv-ggnP`2wh(+E0`2K>6^eq`o_hdB?!S|0eRUg$~{@i6*GF*0_P zxF|NprqYuflWJxZ1Q9VQmFt17E$fsGFv zXl%|UKbKu?P#!oRbq?HCNwk#`*dT$0s)_7MUu5Tey<}SqY&0sg7iPdlV@!Ktwd}e{ zwtX+#ewJ;&!p1{&MW)@BX@ASKeh3HGYY+Jp7y9;fH;WcA1zgl}BmlP;&?2IHkXABi z?mQ6eRt)AC)r3;4UxBM@9P6t16ooB-3U#Bi)q?I72;dO9)|ipFQ6C{f4z>@XH2ZL5Mz(T zfT4vLHPDD(h)qJid5asD2v>aL9bTwaZU8&Y1;~uV@PpEJ46qJx9I!EP0Dytg5nN}wSj4cE#F!$q4)D2->%rxI-{!9%7|UE+QtW!g}g z7Ko;#x?;lQNqvzT!qL;v8g#lFts;2g*=q!wW_Zz*U+~gQJw;8GhBy4wl8OASG&{&0 z?ebgdM!XE7FKQv-E5bEYF**4+Ez}Sy7E^er{zVH5n^(TDVXVpISqnFFL)g%on)#!K z8skvU13m~o%`feRQTUt7Y%68kW!SjuOW9Tpsm%GL)?_c-dRw$6&A?R&^hQ9syIC}f zRSQ#VqGih%sOnlAu3^Jis@ql!nig6@^@5W{3qlP7RUuP@K$K7u#cWMOYGr6mgTFap zW2*&VmJEfTYB2?Unn>)9(o1pb0jv%52etGAKzrzm1X5oz1gHiM1ycG&0b78NdIw%>$i^38IqjV2DvqCbz>|PpSDCBDY_1!O&MBzZpqmfZdLBXOA$DeHM-qrn=vl`+y9xr5Qq z8o9f%l{Ip#v6?lqF1@2fHj6NeNL|UnR!#|uhyx?%*|yD*Uucd-e0(fkkv%{XQR8$U zxHex6j7`GHHS$RXrd0XBCKMi<8Yfk#4Fzv`j)_ zBXihB$fWQb2QCC+9Fw^WcoMh@cnWwCcp7*Mcm_zN_AIa#Pu60lp6YLU zkfcWxZ?2~l8>7p5+_)ZbW`iJzdQ1ew+1GU->2}vl!5qNXbtpY}(nHok4I6}0M%m+2 z6Z~zby+BENiP0!ax$7&LR-Uqpxk_CIE$();D6rI}Q}gN$T$8WB;v)mG+7FV`w9z!2 zG_aDY0e5~QmDEr;DM~86?i~o?-x!)=`8kb!qK~}<9%@EqALL_8)zk+y#4p4KYwEOA z<}MSkre{7>41z-xelV7+EEvh6ij>aB{*#HFl2Hn%#BSCtZQz{jFQpMxEJ~-PA{_%c z)-5dqHURBApa*ajkOGD21nZ900%zcP9q>ya27Z}e12+QK0k;52*ES%9Zaa`V|DC{R zz+J#n(7zi(*ld=*5SO7N6v zU1gfI%z(uOjJ-6Cu=4ko)gL}#$7>{%P;r36IH)RNQjdiI4XQM&XySa54SSj(1bk{I zmB^W(bhQ}7go0KEzdrcwaK;~Q#6e6PCJeOG*nCDrMaGIj+ey5dOhiFlYPj0EKb6YdmXu4xB^u^yFnT;wmO>P}Q<&)dR zz&;i^nGG7bkHc@5^_)yk!A%LVW*I83s`93$;Ec7NebOspn~J=x;knu~%3$g+GZ7Za zQ1~ck%L8eWN#-(gjKX{cNI|{|tPQ*lbOqi3wgKJ*`T%bO`vLC&hXQkf(ZD=l8t^`F z4DbPv(%~VHO2k9_c!?mJN}vfRr|l4JG^~_g?jz@%sl#$KVBrdINI1?=^EQVv(=qQ7 zdz8l}SrF3BqN#7o6yZG@a8SCMlr8U6b8%6 zl!&HF1$q*Y-a94(sWPPiTLM#o6y`J_CEXSLc+!zgB`m;SvXvDUi8e*jVWXT>@X;${ z5u0kkAt9F&dHSK%A_rP6pfp1>43Qs-}DYrkQ7Sf_}ME}L#dF94HH)57nlB#B+Oj3Q@R`?bJg`s z6BLu`tmkGSQs|aLR7x2l8usLVf+UetD^lEP;%vA}PUfMiX9}z-RNXScogOXmo5F>3 z50t2iV9Cj+2$me!(#pV6ZOOfaLL$ks2<)1I0_!w!mYI`TfU_yU8Im|ffaPc?0xU;E z5nwqQiU7;ea8O3Wk)lzxfM~=yO+0KCR`WD>a++u!LsK+R36wE3MWmZDhKjhzF;v7w zj-ethatsx5kzEy)3LFjEyI6GaYtdj)E4l_qUpy3zIDJeI*WYlPSdvcvUk1C2gn`yTRoA|nq_YCy|vwfSK zyo?>xVq)zbI}WgbHZ|L3GV^Zo)hXMRNP7Q&V%R$I0~I^#eCa2&$VUY-m;$CIbUWcUGSGzEg{ZlVGDrm$uB%jE^)+hT=_igW^6~Bz710K-LRL`g#IAfIdLb z1Yck;Aby!cfdRl|U~k}XUShQu|?TzZZ(N zw4D|#?3MoUHbnZOV1AowF7+u|2_NYZ}0xB@vI8Uf(nLTYR3GRyg3dT<0eECI0 zYxxz-cKJ%}k6O|Sq$MXxV>Ay~S>*=vG*(|P;E~r?vZ>fnC0ZITOGjq}vZ;iFuyMX4 zqD{s2NpqSrc(~s^u>A>+vO*N{wi(R@8>3Ov;!HMYI`YK#x3aYWcTE=GMdO(7%**($t}78fLl8R= zA?~5U68vZzjbQAcB%+1Dq6{|H=xgM2sLmZ|ySqc)65gj}9dwfyXlSj+a^-UyXl3x* zh~G|YO@z4PT$h|?9L36^8#t{PG!wKY;|?d~`X;T1(qeHb$u8({P2U34$x5VY}&VhqjJAkJ8*y?3a=hjtaE zk2^TC=)L$`T-cI(Ohve?Y&mPAEq!m~VY*a<k2_LQjTB=c2t#iA=|vtm%DXx)j*6n~Q+kE4@x$*ZCm&{Ajx?wd-W z8?4F_X#N7HHC$<74@P={(*Sny&?b9_w_Ac~_GlbwPsOLPYv7 zNrN&Pvf_fGfhr~*Ny--uII(D$Y0vYO)R;wtMgZx}B?mDQ`CihRNuk(;vRXJDUa8Bo z7b1{t+$PmH+Dp;iV$yL4G%9iXfvfY?I74DW8ofnjO}6#wC9NigLWtDR$^(OeAkukT zdVSg}3D=<{DJxwt+@tV=v0NIqa@4UJ*cLEmR_i=rl{A{(gDPL0QemKC43$N0q<45NhGyg;BNVU2;9z}(M0TUZaTO*KqgmfVF2qS@wfEhp! z;AkMF0=C?-H?QM>6wgfHGT;Q@df>-El06yt0Qd>;IdD3VLjEZ*0Eo#}CKkGdSwOs+ z5as|!0OtXrQ#TSmVC(g5j8AeDo!fGr^RDt^3mA)8A0N~SFlZ7LR5&UZ?t zQT5?8I~#6O3C%=XS)r9oirs+LBaXF4>JyBG^+=u)zY%ASW#2Dr(`I(ZvP8`oL) zfELtzzm52MIUQSHum^>AOBlC0%a1g8p@u#S8&T>)^)>wLS=UXSg(>TAM$? zs=m?qZ8K%PFZ;-VwAq-CLkA=n;}cSP<7&;G|F+#k5Qig_F%2LUO`~+In|3u1Bywp+ zj)|6(#D?R3dx4zQaF(_sqRq41^h`M@j)TQb&+aA)Ha)X#B2+Af;wKr3Clw1Cz(VY)GGXN>Qj6fv zitJ~z9Q14$aZWAM$TvfpVUnnT%zt@DvfX>v)1i`099 z1*2Iy{qKmKDe#f_6G#|n-~_qKk+^%9akIWw$qkY@w~HA!8v-b~K{DsQ4vkVs*wUtw z8zggXswNUQi>s0wBy;YM&A8bDsFE8b6L726IqK zQcQ8}U4g<>yz4Z-IAsE89W81V_UW-&bpm(-%ArKK_lcb9?vgVK~04M1ahQ*7o^4SoaDuQGv3{>LnKwtp}R#ov5UkylMYY3zg zd>%hug2~3;*>f7bv*$EL2{vsV_iOu;A37i;nT86XxZjAjQByEa;%91rvhmc$BB^L> zK$Vb!aX4R68I4gf@hp`J?qz7n%L~4xF<59S`}7OzW;CFSO$kqY);dt*V4hSI2Fs-S z$$=-;Q6Pown20~a&XbC4JgGR1l8Vy;@wQfNxsP)Ey%k`P&kJSkam~TAU_`T?&8*^# zLj#v1CKs}rxtub&kk!m(2f5%K>;OP64k7SW+)l7SiE76sq;2jOc#;mAmE{AF+)1F3 z&JDx}|7e$m+~Gt-xQM99@)XjWQNYOwJ&o5CCRBFkkK~X`>SQ{f2ZYio1D1Pa83++{ z!-1lQ3njudSBk+Ma@S1BW)IXPVk0PjNUtP?yma2!ywMo=q~s-;5)0j7HcK}qn(fQn z<4Q>+7%PzzStTWK-RX{JS=mhHnC;3CGpB)kD(E!v030ahjzF`RW1{FO7+aDmPcB4` zV15(v!W@vmAubDpA zHIrNrG}DR?)N_wSju$rx#o#sojUCubuO?XFLd}>U+8ttj>82I;%&V;F_e4Kx>*e}f zwI9NKsb%w=?5pzPF(kEIJ~_J9%elh3H~0xuc|m~ch|@NuKN64odhnNQbYYL)mTuIT z#$w8n5i0<;d8KmmaHtPFEKv+SStyw^{2GyCMj_ocuQ{C(#5qrjrri-b{ts6~pDGF? zvJFxct-LLgEIpk;WSU3Rp)98{kW`l(a$CQ~vC2}pbpe!$EOC)7nh`c^4;xk^CTqGwwmWmsWbIe`(s@jr`(fcG7x);uiw32d=BG7Nk7~N?A5osdz*?DA zuULec_fRo9q(U>ro8=UA^Hu>nRuMfELZ@wB&`vCJt$x6eLGi}?R(e` zGTRZ@b}<`G3g}`zd%;7tK^$93O$FU@&twm9)q;CR;1Oq7xDm%$Z1CWh!G;f%uvQEP zS>|u-;-V6g6a)h z>6C?&IBKCqd+HTxRHTt0KiKJs=lt&j=sb!qTpHjPVw2)cpN(K^J)t02to0CYrWrXD zA2DGsz=ndz1;j+tgil^9l8EAB5lzm$z>82p8M_;u-QuUgv#iS$cVf?g6PLp7{$m5$ zcr$j|6KTF;WsBn3j>fR43GhDIj5Q>U1vU*N9)Kj=xvi(m8kFl}USRN}V#Jpo=*0); zkMZaG49G(T;eD4C&_&gxI-KQMb3> zFTg*67lD5PZvmeIp8}r)-vD0%>8;-@U^U=tpc+`t5esy{@<0u+B5(k(5^yN6GB6fc z1sDgc4*URE8;Hd|p$>2&upW?hK&XM=0viHx3_@@P{tRpc{0-Oycm>!Dcn8=V_ypJj z_!8I>SlSl%3<0Sv@Br2UwgGkpwgvhD+XLx*Z3mzh=n4D)*a(-9AGctDPSNl7Z?Kk6Br774U7O*K;HKU(k8G0z`DSJz{bD~ zAQjkAKyToOzyRP_pdL5@_ysTvxDJ>Nq>D@^0?VV6P6Ac~P6pNoP60LqP6M_BP6u`X zehTaYoDHPXJqJh^n9c13w2&1bzYh7`Ps|5V#5WEpRh%4R8x^BXBDa z3u3|!AYR!Bdw_?5dx1xQIlwEx{lNRcAAk<18$SZ;0S^P40Dl6u0GHO1*`=871#`jZT~`Z;1ggAAnLEs5?BG)3Rn}^ z8rT%*4r~u>1EhYUEs!R3G&SWDIkKsQSp21WQdXemh-@k$9e-(|w=6peOg0tm+!6iK zyLIl@U#9hzX^Aq8dM?gK{VLZ>C6m(>^J)wb#EUE3??4;oUU(^81D31!nI_UR@MO8O zssU&f@T-I0ZfE@AhIb(JvceyCHA-`1+Qi}}u_#eox^o9r?ARnvG|{F`N9qTqmmRFB z%(wnP;jx@lJD28@Y8N1dvm%i4;#d55UXYC+z~QtNqN~NOrFabC)NtZ+Cgmg-TchOR ztu)Ck1sctIGUOTZpJ-}`k|xi2uM!1gwKO*2Z9*DQTxgJ}<|8!jC{L}_x$EJ>d?G2& zerWtn!rg`Rz9h#{DTlcj8<0F4C&bWQoCeCPzVJioF9ydf7|=@*4^sv|EV5a7vY z)EP1%gIH*^+#RmCDU|-OVKd$7BHz8_g@E8%rn1~U>v>%^KTs>@2f=jhN~zp#G({w# zBnQ{3X+n)OnXq7{i6oev0dRC~3+V{DphxQSzIxR{Tzk;-T(kaRhyQ1J(DC)a>C^`}0T>9RW;_@;AJ`YT0T>3{ z0SpJ`0Q&)t0tW)m00#l-wa#GRU%*J9fUpk%mI2NH)&b50(p$)Rz%Ibgfb_O}K9GiS z3xFxW&w+SXD|`VQ4O|GsJ6ho@U>0x*5bsunZ-EPeOM$dmVj1xNm3Ad?I#%EReR~IE zh(VSRlLkYK!B|FN2E$Ba8H^06G0V#gW-*H;%AUP!DHRopl1P*yCO?IKEs9Y6T1aI} z(!TKff6qDhKI_bwe*gPnmz=M^Wi3HMuFHGVcQ*yKM68I5B3+vkIup8bCPm%2WR8m zAJ@1xK<8Zu6wqu2Z$uX?PgO~);wI+%CuHRo$@{yx`A5@H6(~K_{HbeKF?8t0xi1)%T3@4ZF=^XnDakMZn}sQNQFA#kYf^}( zuz?l|F@@p|M2AoU)FGTnScR4ya-a|DsblFgryw=G5O30nNy8Hq+=XO9x`e<}PxF=`x>1) z41RkO60vAd5zB%1D1&4Z@U&~FZ*a4!NWyV)(Gj%gdQ(k;6x!AzyW#j*mSV9)L`6)0 zhQPdS?kf@Fe<~m9!)P8)rw4@uoI+G)eL<-=><3DD9SBO9NdmnGbSP*RXfkLK=rGV( zpu<6zfZhX2Q<4-=s=bk*#h|I6Z-J(P9tQP;QVuggDGOsje*+x{8i4d=gOb~r1KJ!k z7qkUv9%v-!c+hU31)x;Vg`m_0kZUSk0L6m%26Ev|RY!yP+J*4e8N5>}R)ufx6)d^0 zB6q!t1$oG}@H!hjx_!>Le#IBI&kyC(2`5rW++v>iwJ*j(O30<^FV(=swMrt?&T@*C z92G5mZmaQj7H(vyh_upKPyvlWpNAoe7r-waq4O}1o`+HA%^<{Rx)?~$ym-FB7T`(6 zzf2cL8!>7DKVln+&3RVkfk(&3Vk-{Dp9Jw1EoT*O_0N)*RCU%ec8aq~Kb1l{b)Y!4 zO6pNE6tg{Qm27qdrDDW-84m!rgSNwdSI{WXXwY7uF`xrLdw^0e6btGH?E^~bjsvAu zatNEW5{d<}dn&w1>L?EsVeJob4e+X0B^-Gdo4M*mCFu02sbDC@l#6|3lv2;!Y6uaS-(<-qLFry%CVOAYCE%ses%K{blcDXu=7`W;`y!@tBjc z&KV2QPL-)hCnM2>IFeCpR*G_`9@X%WveI!3i+N{f<4@`tMTg2W>IC&I&5qm}ZQF48 zqS=D!@FC%jhQ^|Nw*fc9#Cn7TE2kz&TgV+{n3p0ttDvHZ`@!wlif1CPo?#<4EIl z!Cf){mf|p$*?XwsM+I|7)Bau2Ib%QdME|Oe5~zbgD}WaRWVQ2l@Wk=#ftWa+?i+O(|CfLsCvG+6YF2we6Y&v4%BJV;>9NrHRlOg;R zVzdfT!E+9=9~kiiV&XRcikPId2KrWUn>!&UTJAth+~y?2#BI(;OiFSJVp2j25&M-j zBf|Nhdv?1!Cg_jgAi4O{K;OFNz^3kUpn5DJ^sIrAs7np1^Z{4}qdOF6SP5enH!m-* zH*S0IXXq7O@_<92Mf=*?_tqXUIn^|%6k@~56W;ii!S@HmVP9tcB|dvb4dWa(1iJ5l zoRtuGRM^ls3hplP7PKpzIZ2lOLQKPcTYlnHtcbS&r(pgEv_ zfaZcy#pHwD4q5=30$Kz*0dxZBY|x3IOF<`rt^%C`O1Csl1>FmJALt>_`$69aod!x@ z1Dy$a7IYTqMbNpRKY=a)t%Ukn1X>&PLC_|k4}sF<^d+ElIejT;AJAo>gFu&q(&hAr zK}UnG1f2}J3Y5;#$3V%|C6`qkK#B$N`7OL()X^Z%%_vXdHC3@7dMlLTjWu}GABx=b z2Jf)JJ8JOsB9`%a1%27`ZJ4A+!5<(?5`PMh0lGR98vO)eB;thbO78YtH)pzeQEpE>wlkw;Xr`#9>^+ z3sieNeS`O?#z34#>bhOrzp5ZgeP{)&-RjAKpj$xi z1>Fid3v?UkV$hdC9|he3x(0M7=w{H@K&h5?fl@cU2b3D@UeJ$0i$PCpZrI*+Hk~`O@(YVn z$EnZW(qkg}Yqm6V?F>i>ftStXDylS_JpFzO8mg)B-GU7_1M(AAbeAEuuqMpQn20B= z^jCK4<4`gkl5yGgabBZ2QB%-?^5UEs2jiI%%3lpo%403{jL9d6OSMrfh@C&-oyNZK zdX@Cx#H@4Ju>?4U{}QC;Opt>K;mdapw#-1|EZnsFdWRp%mggFn~k!M^%# zX9u3{5NHPAYuExj(fB{ev0?bX%(3bCKfp0v(Z6D^kE4&64;~f7Y;6B7cSS2tE)$>< zf~x8~9CEo@w(IcL7BC&s&K5u=Q%Vb1LWfFd0px{I<(n_3k@XTPlQ?WTP`;^AKm{+3 z5&v&owWYG~qA|1t^?_1f5&+r{v?3^#Q)SRrpeL|NW1*P5?nHRO81;n5qaM!5z<=pl z$bf8wqZWd|qcNm1w)SQ%Bo^i;*BgjqO@M{3%H1mEm`Xwi zvO(9peW0O=iQZ;9*+q}Ty(>l-&KdPbk~50Q0+H~hVqbV(tjGqWVHY7!2><_ve;?W; zZZ75+rHhvBeR%SiAp5Y7K#3#+q%Dc6a0OHl5+elNBIWaV@nWn7-r6ijckLR1k8)BuMu?uK4ON14V~7qu10gX& z#0vuR+Q23vN9UIgw4N@hk{BTdl!>TdAS6bJA%dW?QZ`y>1E3WRgv1De_rg-nOF>jJ z5E3IqsvxLzdx&ynig#$*F8PocA;?XI4MNNm#AqS>57P=EF+$LDszB89dg|>HQR*Oc z2r>{7BSe9TNE5l9S{$^Bfshy>CJTa6r7}QIKk7i!AjyZs2!YoRfLECir$mkb&9Qwg zyG%*)XKX%9LFGjgK6X7nV_rS{|HQF|#!*E0GLVy1|`4UP^s7zl|G;yFP;MJ&#a8v4^%qWEa;wn-_J~VeZgD5o@lgZOoJQpA8h5n{Mh8{-x&5&r8iz_ zHuY$ye=Up2kAHSl{Z;jwR$Z{%8@l@Wm|h$ErfmH!`Q!Y>`?fTQy`xQo9@oA3%b!oD zuf5?+gP`|{>;CaqdQ!snlTSbO@%DM0_cfaFTv1s6TOu1SD8966^Zr$D^=pwk{Hwqx zlK*>QV)L(m$)0pmaN~g$k9WSY(Nnj4(;{Knjjs)j%wLeT?(n(UcUSMS?y(+q3O#4$ zoLd|6-GPu5D{fdaVer*8_ecCuv+jG|Z6l7f8`I{x9icTr+*cmSo7vOTZPp9tr+n6N@3tIIe&&xsbv7kF zvhL%lb9#@To$R}5sc&xG^HkuL+dnUiJ=bXTfv5$Y>uu`PxBmJjU6ys7Tl_+e z6TKdcsF)o6T<4}g&UyFps%H}y*8aU?(#{({+x5mfk4;$g*{1ZfUFNSE);eTx%I|fK z*WSFZ!=>E?i??j}^$&>IS5wxWn|!(6hu^+C^YQ6@c6B?qwQu+M-MweM z+I-f$vk$&l;m^wvcb`uF{e1A!4aJQ!SI?f*bk!G;AKbm6`A~n%m8$<)mOK8*^)u(j zKhx}u3%At!^tJY>J1!>%>|68qXH{>xE3W2Sp;b@)`f%Xw-@Mf8`S=}a_r#Tj+j`ADx;A+D`f49| zt4ts1-}1=Dx!G@59dvk9hX%p7)g1HZ`B(lutxe^-cCH_AvC2yu5Be7k3_sSb(HnPs z@KW9XOw5d%SA71ZX`PZI8(-KOw5rwv*Zt7?;Kv&mww^coQrJ&xeu@3H`05iQH>9@l zT$hq_KBwQwxXT~(et+QBo44N<_jRMqSMR!GdSL6kw~i0kb#Q8{d0EwKWi9>am))LC z>y~`7cJ?>MJYdjv7$@OlxMfJoEzM2;lfvDJwE0~&&9mC zJ74=KXV7zNnl-pEW%i!*`?K~{$$Mh={r8QTd9M4u%s&IOAN}a;#K{AV`tf8o0!zQzN($9G8^ zkbmF#JEy+Vr08Vre!6~!Rtt&xzQO&?J{kAwQ->3N`KVu?mR{nU?kbvnPd?)d=|GPiF} ze||%i^>e<=xGz5>^tV0d{`%Z=YyT}zWzFbvCa^)Z2hwU~pIma+>8MF-x9apQ_-SS2 zlp8CzUB02;{8R7ki|W?2`H9c7Hvi$=-<$OK-a!p>8(+Whd}8A*Ll&R9^VJCrdp~Tr z7gwUUy?o|{gyqy{Q;1H_&CUzY%D6PKf|ovo?ej#B@~0MtM?@^~dZ#PSgxqoA*%^HG zKsezU^l5_$e$qfaywA%c0__?hJj0(>G&*!JF)(C6<}?P?I>vE|gy&-wGF4cl!RNJ3 zgM=SCxKI12$jDB#$1)z_bZkpwj&Q;Y@d@nE>>QF|u2+v@qvy=~TGD7u4%FE+4=vmKyxQ~qdCrkX+J#|@T zHe{%FDb#W4p&6XdOFl2z+MoOL&2Bw6vdlQh=zP_4>8Z~$7a>F8?>28B6Lel$>#G6w zeKZX=^wf9hp?Rdw+W|6^p3IdSu;%F0a}&#)fsC&AhAusgSO#4J*HX8pf9-VVt1-)v zpRV)O$fYNQWoF}_&g!nCzfE@QxtV1mE%^#@>1o0;?eR~cXy|v@?tI;XBR=m}_}A%a z;?fh!GJ0OxWo^ZiZaqy|hE`s*o=}&bW-L>He+tFB+9He2a;CKapO;n)w4P=zJz*@f znJNpRP0d*c-FjN!h|fF8q9@FyrzOi=#6QG5KOXxQjgDg0_H!%CEI~5qtftV?qGu9w zTH)X4)#vHw`InDbWuy;p%`&gBJTB8da~nDUr>$_7p~rz#);z5(wnR@GmdV6FmCsEz zK0znp)DyunabWAV+Qy})Ez8h40M)>XwR^9)%dj2GL_&aiSPE@jdZ=~zym!(+w(7r~ z-snzGB+Eot($n6hrvuB3#y_R!>Z0-aZap+|`Me{bN4Jd*Eh9gErnvQV zW*IaZLr*7{o+y@~Qi3_p_m7-Ll~ByuHt5Qe&ui5a<;3dldh=e7d^ybY~e_X{FJn*0ii;ZavW~ zQxz4e`|$2AJuxgpy$97-(j$L-;?~oHWlrN?rzggxrzgvlt*4huPfwSgSeBu)p3-xC z{p^$O^z>#KTIJU1iFN7e!!l*-iF4`c3dtmT~G($kM+s1GCbg|KiW%c%#$Ioi)g{Oj`R=h8EPW$3B{ z_4wN>oj73sN3^Jx1K>PvjXZ-cpL`0^bBShYDHA< zBlf?IQ`VWDB$lC7TAi=KESDC+^+NTJ6Q!a1Vbwsg=OI$J0VWtnV$c6#1q;Rp(x0)L^ELvb2iBxpU< zH`zJVO6(jx>YM2quSJX$PIGYbHT;=!^NSi91^rG zx`0O^!LUW+6NPw#(?@ao860}`F@*sJhpvH87-(=7DNdBZS*19)8JvxZ)7{|EYB+@$ zgG1}e6nYw*(~1*oZ~~~&BlIyip^DSh;B-+Ovj!6tr-LC=q&Pzi4!wbj!eE2*s^WAq zIENIcB{(?Q5$KjP3W0`9ML7VEMwfgO#R)Yy35wIq;7nGWCI;tG#c6AB_9#w6gL6f3 z>KmNq>Vyw7IPr?p9vl%bRGc;j=Sjt>X>blJ&J70VzlsxVaBi(2d9P-0hAK`ygR@?7 z>KGh+T9rd1gHs270EN~D2hVeG2r)Pl6sM}e!HtLs(8}Q8c0(0( z?Tm17vTdBuzWvEn<3M9mMGrV_+bIr>ddwlWkOTE7!a>K(fkqH;=)SAaDE=boK;iW5 zPmUP}gJb9+e@vqy+aB}Mn^>4b?@b7eZ1Dxb&JYWh+hHbTtDh7xAALwCJyNGv8 z4sxN*E1ACi4eK$2vT%C3II%8H9~XzbI4%u%ZxX05?Hn43m}6KUtT{%>lb6df_|%bH zJ8fxjDDUJev@$qUJLEeX977L{tE|W1P@}P14|Q>xX^v6vlnP0YE_s?$O9PyP_Cw=A z9p;2jGS|n3f#dV)vAylId7Z>>0h@00TsSdS)rNzQ86y2-Te39q1rvpv-8is} zCmgGMg=yoMa~J-7UNjO8#}4iKOi(G1!9pMhmho6qI0Ae#g#(*hx+oqRi62q6SjLvZ zyBVcZ*!4ir0TYGyhH+pSn=Kk?O3g%%;QMq5eoqcUeC@qG%#GB#UzjM8Oz-{h!oP1HGsVi}vQ@h)33uOwTz zuag7I*lgv4Zt}^x z8Jn$1Hd{ZgDeP;a8qow5fn{vACWC99UoWIaEtYvDN%v7Gma*Bomr=THRC}xD1{0O5 zP%LA!HN|G@vwmMLk~J%`wNjy2#%61(&DO{F4mf3^wkj0M*lgX$s1W>Y_?|MsM$8i zmRv_8)M15U8Jn%yjI!D?Q9mmb%h+tqvDu=wA#3kstM2vmkA4xGeKdc7IL2uH^T`*Y zO;nshv5d{uTt?|KBwMmR54LBLLa~f3U-N9X3RZsE#6-QQP%LA!HQ#27`VCosCR<-C z6wBCbEwI@Nh}~1)MAfK9SUiq|4W(i6MHdps@I`-1`}{|_zC+U86pCeRwiYo;*VpDd zhmSQ;a})~YU-wMStHd_zcY{mQ;zuZJ!RVbFR*?P!kYw4CTYuXsC zH=;UO#6wuv>}_KSag4T6ZC5p)iAq-}ma*Ad$|&77sLji@NU%K*D-_Gv^0mxni_T>e z^}a%}jLp_^mo2$oNwz8m(?1>_!)DLd3gQ^~T6?_R{U)lbLa~g^*29d_`653`uGNC= zDNrbuvE^%}%~t<5hhj_=z0QvU%h+r^0#hzmQLSqd7LROVv*&9yag2Pmdb`7VxyDM;xeCQHHd|{L zrStXnPtAsys23HAWo-F+(q`-3>64z2Yj$MofpI-9MHzt7wv_e+qicNB_c zY_`_hY`wkV{beR9_y)q_RfgE?`Fe&pM!v{akkLmCQ7D$N*?N{yI$vbVL_MNVEMv>p z2Ai$PE8_9e6M1 zydaW~QYe;T1$To(eT;Cz)9?{d6`lw6c{}S}8=ax_unZr0M(Hs*2f?9Kn4GV`q0E|` z-@vJ7;{?~i+s192X5fU_IK99LvvG33iLi0jfTP28`ub@SaW-M|d6V@)gNCPPjxFdI z$@?#`%t&2DhRjxI?cTjz^st!3*qG#$gy`YeZI9h#W8dQ;H{E&M(=S& z1)1?V1%>%V{7$Z(csH!yqaHitPqFD8{ZODf)*H|H>q7&J{P~mhj*`Q3Yf6%jVz-nx z6}i~*S}+$|y|JehzMgvUfQNlfj8g<8K?1FrB}YH2j97$crXY zk_N@}OB~QQzJF|VbXyBNBiCP$l8%?Gr{K&?8C8_SCl_gnBP|w5lrGERbM@o=`6Q9x zkSODOn=GDe)0yMDa%1-1L-u%3VrE?!=sw!d&%TX_BNQIFexn6y=py94{lw z%}E)BSIwu4%T=1WAgxuRR9j>-v&xpmXFam>a>r%m_){2+#*>oc=LU#m>zcY|HHl$U znKD`;&QTono|66j z-vB2C$+l|6_g$pC?TMr6EHQD`#+XW9hf!$$1`Vmdqcr3lluGG&5>rR}RmUHbF{TIu z+9ZE^k(|*92|Na(fft5GM0bzquKSJBI+p0jxEN1LO3xIGYw1~|veHvBQ*$zKc7WMu zaGb<@X8O~|f>8iF_#o}%6pUNxV{tC!j?MDpA|5wvDeBO+9Xz2$ITLY8he~pj%$HEg z>+(YRGES*SO9RVLRT@|wEe)&&w9?S}aM^J5>}4w;0hRqXR|0%uzV`*ji*$Fj&mT!;^x@%X+5A)aINl2 zC*j(1TH!zJ3RMcb|5KPYmmz*!Np6}}U6x1*YMdTj32Iy-4mE~PSDN$Z(0Oh7(hQX8 zG?r$iY>>lL*)FBS#5IH+s{cnn)M4{~)#6A@IXqE^;qo9HM#|yCI*gPD;V>eOUpc&A zhpF=8$}m`l_gsd#vQQ-(v-s0)-@9Z>WkarM#_hIW(@Y7F63v+2?EmEPmza+Kug+^E zv>daEYb2{Y8XSrEhw~Cg0{$u5VH_TPIcGNxyZ->_Fj>Cok;7a$K@L;a*^x2-q$(I?gc4%CA-kk(d{t@O^%K6IAu(~ z*e)YJZGkYIlWV0%WXcjzU(wND;DIP6rbxml+kC-b=OkjWCo5;Ptgyys;J_U$uMhAl wuffgBQ}UXPJX*48Q)c9Z%U%pbN2`n|$pLx({8aj`f?e2Pv%Gb}EK}wG12~hsrvLx| literal 0 HcmV?d00001 diff --git a/r5dev/thirdparty/detours/libs/syelog.lib b/r5dev/thirdparty/detours/libs/syelog.lib new file mode 100644 index 0000000000000000000000000000000000000000..35e7e5b7fa1298d73fcac0b6b82d2a116f94b00d GIT binary patch literal 69934 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

    ~MsP7ua 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

    aX|#7kx{X{>Ch-O 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 literal 0 HcmV?d00001 diff --git a/external/imgui/include/imconfig.h b/r5dev/thirdparty/imgui/include/imconfig.h similarity index 88% rename from external/imgui/include/imconfig.h rename to r5dev/thirdparty/imgui/include/imconfig.h index 39de21c6..7082c550 100644 --- a/external/imgui/include/imconfig.h +++ b/r5dev/thirdparty/imgui/include/imconfig.h @@ -33,17 +33,20 @@ // 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 metrics/debugger window: ShowMetricsWindow() will be empty. +//#define IMGUI_DISABLE_METRICS_WINDOW // Disable metrics/debugger and other debug tools: ShowMetricsWindow() and ShowStackToolWindow() 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. (user32.lib/.a, kernel32.lib/.a) -//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] Don't implement default IME handler. Won't use and link with ImmGetContext/ImmSetCompositionWindow. (imm32.lib/.a) +//#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW) +//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a) //#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime). //#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default). //#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf) //#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself. -//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function. +//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies) +//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function. //#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions(). +//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available //---- Include imgui_user.h at the end of imgui.h as a convenience //#define IMGUI_INCLUDE_IMGUI_USER_H @@ -67,7 +70,7 @@ //---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui) // Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided). -// On Windows you may use vcpkg with 'vcpkg install freetype' + 'vcpkg integrate install'. +// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'. //#define IMGUI_ENABLE_FREETYPE //---- Use stb_truetype to build and rasterize the font atlas (default) diff --git a/external/imgui/include/imgui.h b/r5dev/thirdparty/imgui/include/imgui.h similarity index 93% rename from external/imgui/include/imgui.h rename to r5dev/thirdparty/imgui/include/imgui.h index 72c197e5..ff0a44be 100644 --- a/external/imgui/include/imgui.h +++ b/r5dev/thirdparty/imgui/include/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.83 WIP +// dear imgui, v1.86 // (headers) // Help: @@ -11,11 +11,14 @@ // - FAQ http://dearimgui.org/faq // - Homepage & latest https://github.com/ocornut/imgui // - Releases & changelog https://github.com/ocornut/imgui/releases -// - Gallery https://github.com/ocornut/imgui/issues/3793 (please post your screenshots/video there!) +// - Gallery https://github.com/ocornut/imgui/issues/4451 (please post your screenshots/video there!) // - Wiki https://github.com/ocornut/imgui/wiki (lots of good stuff there) // - Glossary https://github.com/ocornut/imgui/wiki/Glossary // - Issues & support https://github.com/ocornut/imgui/issues -// - Discussions https://github.com/ocornut/imgui/discussions + +// Getting Started? +// - For first-time users having issues compiling/linking/running or issues loading fonts: +// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above. /* @@ -60,8 +63,8 @@ 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.83 WIP" -#define IMGUI_VERSION_NUM 18204 +#define IMGUI_VERSION "1.86" +#define IMGUI_VERSION_NUM 18600 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -89,18 +92,31 @@ Index of this file: #endif // Helper Macros - IM_FMTARGS, IM_FMTLIST: Apply printf-style warnings to our formatting functions. -#if !defined(IMGUI_USE_STB_SPRINTF) && defined(__clang__) -#define IM_FMTARGS(FMT) __attribute__((format(printf, FMT, FMT+1))) -#define IM_FMTLIST(FMT) __attribute__((format(printf, FMT, 0))) -#elif !defined(IMGUI_USE_STB_SPRINTF) && defined(__GNUC__) && defined(__MINGW32__) +#if !defined(IMGUI_USE_STB_SPRINTF) && defined(__MINGW32__) && !defined(__clang__) #define IM_FMTARGS(FMT) __attribute__((format(gnu_printf, FMT, FMT+1))) #define IM_FMTLIST(FMT) __attribute__((format(gnu_printf, FMT, 0))) +#elif !defined(IMGUI_USE_STB_SPRINTF) && (defined(__clang__) || defined(__GNUC__)) +#define IM_FMTARGS(FMT) __attribute__((format(printf, FMT, FMT+1))) +#define IM_FMTLIST(FMT) __attribute__((format(printf, FMT, 0))) #else #define IM_FMTARGS(FMT) #define IM_FMTLIST(FMT) #endif +// Disable some of MSVC most aggressive Debug runtime checks in function header/footer (used in some simple/low-level functions) +#if defined(_MSC_VER) && !defined(__clang__) && !defined(IMGUI_DEBUG_PARANOID) +#define IM_MSVC_RUNTIME_CHECKS_OFF __pragma(runtime_checks("",off)) __pragma(check_stack(off)) __pragma(strict_gs_check(push,off)) +#define IM_MSVC_RUNTIME_CHECKS_RESTORE __pragma(runtime_checks("",restore)) __pragma(check_stack()) __pragma(strict_gs_check(pop)) +#else +#define IM_MSVC_RUNTIME_CHECKS_OFF +#define IM_MSVC_RUNTIME_CHECKS_RESTORE +#endif + // Warnings +#ifdef _MSC_VER +#pragma warning (push) +#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6). +#endif #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wold-style-cast" @@ -136,7 +152,7 @@ struct ImGuiContext; // Dear ImGui context (opaque structure, unl struct ImGuiIO; // Main configuration and I/O between your application and ImGui struct ImGuiInputTextCallbackData; // Shared state of InputText() when using custom ImGuiInputTextCallback (rare/advanced use) struct ImGuiListClipper; // Helper to manually clip large list of items -struct ImGuiOnceUponAFrame; // Helper for running a block of code not more than once a frame, used by IMGUI_ONCE_UPON_A_FRAME macro +struct ImGuiOnceUponAFrame; // Helper for running a block of code not more than once a frame struct ImGuiPayload; // User data payload for drag and drop operations struct ImGuiSizeCallbackData; // Callback data when using SetNextWindowSizeConstraints() (rare/advanced use) struct ImGuiStorage; // Helper for key->value storage @@ -147,7 +163,7 @@ struct ImGuiTextBuffer; // Helper to hold and append into a text buf struct ImGuiTextFilter; // Helper to parse and apply text filters (e.g. "aaaaa[,bbbbb][,ccccc]") struct ImGuiViewport; // A Platform Window (always only one in 'master' branch), in the future may represent Platform Monitor -// Enums/Flags (declared as int for compatibility with old C++, to allow using as flags and to not pollute the top of this file) +// Enums/Flags (declared as int for compatibility with old C++, to allow using as flags without overhead, and to not pollute the top of this file) // - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists! // In Visual Studio IDE: CTRL+comma ("Edit.NavigateTo") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. // With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments. @@ -187,27 +203,22 @@ typedef int ImGuiTreeNodeFlags; // -> enum ImGuiTreeNodeFlags_ // Flags: f typedef int ImGuiViewportFlags; // -> enum ImGuiViewportFlags_ // Flags: for ImGuiViewport typedef int ImGuiWindowFlags; // -> enum ImGuiWindowFlags_ // Flags: for Begin(), BeginChild() -// Other types -#ifndef ImTextureID // ImTextureID [configurable type: override in imconfig.h with '#define ImTextureID xxx'] -typedef void* ImTextureID; // User data for rendering backend to identify a texture. This is whatever to you want it to be! read the FAQ about ImTextureID for details. -#endif -typedef unsigned int ImGuiID; // A unique ID used by widgets, typically hashed from a stack of string. -typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData* data); // Callback function for ImGui::InputText() -typedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data); // Callback function for ImGui::SetNextWindowSizeConstraints() -typedef void* (*ImGuiMemAllocFunc)(size_t sz, void* user_data); // Function signature for ImGui::SetAllocatorFunctions() -typedef void (*ImGuiMemFreeFunc)(void* ptr, void* user_data); // Function signature for ImGui::SetAllocatorFunctions() - -// Character types -// (we generally use UTF-8 encoded string in the API. This is storage specifically for a decoded character used for keyboard input and display) -typedef unsigned short ImWchar16; // A single decoded U16 character/code point. We encode them as multi bytes UTF-8 when used in strings. -typedef unsigned int ImWchar32; // A single decoded U32 character/code point. We encode them as multi bytes UTF-8 when used in strings. -#ifdef IMGUI_USE_WCHAR32 // ImWchar [configurable type: override in imconfig.h with '#define IMGUI_USE_WCHAR32' to support Unicode planes 1-16] -typedef ImWchar32 ImWchar; -#else -typedef ImWchar16 ImWchar; +// ImTexture: user data for renderer backend to identify a texture [Compile-time configurable type] +// - To use something else than an opaque void* pointer: override with e.g. '#define ImTextureID MyTextureType*' in your imconfig.h file. +// - This can be whatever to you want it to be! read the FAQ about ImTextureID for details. +#ifndef ImTextureID +typedef void* ImTextureID; // Default: store a pointer or an integer fitting in a pointer (most renderer backends are ok with that) #endif -// Basic scalar data types +// ImDrawIdx: vertex index. [Compile-time configurable type] +// - To use 16-bit indices + allow large meshes: backend need to set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset' and handle ImDrawCmd::VtxOffset (recommended). +// - To use 32-bit indices: override with '#define ImDrawIdx unsigned int' in your imconfig.h file. +#ifndef ImDrawIdx +typedef unsigned short ImDrawIdx; // Default: 16-bit (for maximum compatibility with renderer backends) +#endif + +// Scalar data types +typedef unsigned int ImGuiID;// A unique ID used by widgets (typically the result of hashing a stack of string) typedef signed char ImS8; // 8-bit signed integer typedef unsigned char ImU8; // 8-bit unsigned integer typedef signed short ImS16; // 16-bit signed integer @@ -226,7 +237,25 @@ typedef signed long long ImS64; // 64-bit signed integer (post C++11) typedef unsigned long long ImU64; // 64-bit unsigned integer (post C++11) #endif -// 2D vector (often used to store positions or sizes) +// Character types +// (we generally use UTF-8 encoded string in the API. This is storage specifically for a decoded character used for keyboard input and display) +typedef unsigned short ImWchar16; // A single decoded U16 character/code point. We encode them as multi bytes UTF-8 when used in strings. +typedef unsigned int ImWchar32; // A single decoded U32 character/code point. We encode them as multi bytes UTF-8 when used in strings. +#ifdef IMGUI_USE_WCHAR32 // ImWchar [configurable type: override in imconfig.h with '#define IMGUI_USE_WCHAR32' to support Unicode planes 1-16] +typedef ImWchar32 ImWchar; +#else +typedef ImWchar16 ImWchar; +#endif + +// Callback and functions types +typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData* data); // Callback function for ImGui::InputText() +typedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data); // Callback function for ImGui::SetNextWindowSizeConstraints() +typedef void* (*ImGuiMemAllocFunc)(size_t sz, void* user_data); // Function signature for ImGui::SetAllocatorFunctions() +typedef void (*ImGuiMemFreeFunc)(void* ptr, void* user_data); // Function signature for ImGui::SetAllocatorFunctions() + +// ImVec2: 2D vector used to store positions, sizes etc. [Compile-time configurable type] +// This is a frequently used type in the API. Consider using IM_VEC2_CLASS_EXTRA to create implicit cast from/to our preferred type. +IM_MSVC_RUNTIME_CHECKS_OFF struct ImVec2 { float x, y; @@ -239,7 +268,7 @@ struct ImVec2 #endif }; -// 4D vector (often used to store floating-point colors) +// ImVec4: 4D vector used to store clipping rectangles, colors etc. [Compile-time configurable type] struct ImVec4 { float x, y, z, w; @@ -249,6 +278,7 @@ struct ImVec4 IM_VEC4_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec4. #endif }; +IM_MSVC_RUNTIME_CHECKS_RESTORE //----------------------------------------------------------------------------- // [SECTION] Dear ImGui end-user API functions @@ -277,6 +307,7 @@ namespace ImGui // Demo, Debug, Information IMGUI_API void ShowDemoWindow(bool* p_open = NULL); // create Demo window. demonstrate most ImGui features. call this to learn about the library! try to make it always available in your application! 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 ShowStackToolWindow(bool* p_open = NULL); // create Stack Tool window. hover items with mouse to query information about the source of their unique ID. IMGUI_API void ShowAboutWindow(bool* p_open = NULL); // create About window. display Dear ImGui version, credits and build/system information. 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. @@ -328,7 +359,8 @@ namespace ImGui IMGUI_API float GetWindowWidth(); // get current window width (shortcut for GetWindowSize().x) IMGUI_API float GetWindowHeight(); // get current window height (shortcut for GetWindowSize().y) - // Prefer using SetNextXXX functions (before Begin) rather that SetXXX functions (after Begin). + // Window manipulation + // - Prefer using SetNextXXX functions (before Begin) rather that SetXXX functions (after Begin). IMGUI_API void SetNextWindowPos(const ImVec2& pos, ImGuiCond cond = 0, const ImVec2& pivot = ImVec2(0, 0)); // set next window position. call before Begin(). use pivot=(0.5f,0.5f) to center on given point, etc. IMGUI_API void SetNextWindowSize(const ImVec2& size, ImGuiCond cond = 0); // set next window size. set axis to 0.0f to force an auto-fit on this axis. call before Begin() IMGUI_API void SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback = NULL, void* custom_callback_data = NULL); // set next window size limits. use -1,-1 on either X/Y axis to preserve the current size. Sizes will be rounded down. Use callback to apply non-trivial programmatic constraints. @@ -340,7 +372,7 @@ namespace ImGui IMGUI_API void SetWindowSize(const ImVec2& size, ImGuiCond cond = 0); // (not recommended) set current window size - call within Begin()/End(). set to ImVec2(0, 0) to force an auto-fit. prefer using SetNextWindowSize(), as this may incur tearing and minor side-effects. IMGUI_API void SetWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // (not recommended) set current window collapsed state. prefer using SetNextWindowCollapsed(). IMGUI_API void SetWindowFocus(); // (not recommended) set current window to be focused / top-most. prefer using SetNextWindowFocus(). - IMGUI_API void SetWindowFontScale(float scale); // set font scale. Adjust IO.FontGlobalScale if you want to scale all windows. This is an old API! For correct scaling, prefer to reload font + rebuild ImFontAtlas + call style.ScaleAllSizes(). + IMGUI_API void SetWindowFontScale(float scale); // [OBSOLETE] set font scale. Adjust IO.FontGlobalScale if you want to scale all windows. This is an old API! For correct scaling, prefer to reload font + rebuild ImFontAtlas + call style.ScaleAllSizes(). IMGUI_API void SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond = 0); // set named window position. IMGUI_API void SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond = 0); // set named window size. set axis to 0.0f to force an auto-fit on this axis. IMGUI_API void SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond = 0); // set named window collapsed state @@ -351,9 +383,8 @@ namespace ImGui // - Those functions are bound to be redesigned (they are confusing, incomplete and the Min/Max return values are in local window coordinates which increases confusion) IMGUI_API ImVec2 GetContentRegionAvail(); // == GetContentRegionMax() - GetCursorPos() IMGUI_API ImVec2 GetContentRegionMax(); // current content boundaries (typically window boundaries including scrolling, or current column boundaries), in windows coordinates - IMGUI_API ImVec2 GetWindowContentRegionMin(); // content boundaries min (roughly (0,0)-Scroll), in window coordinates - IMGUI_API ImVec2 GetWindowContentRegionMax(); // content boundaries max (roughly (0,0)+Size-Scroll) where Size can be override with SetNextWindowContentSize(), in window coordinates - IMGUI_API float GetWindowContentRegionWidth(); // + IMGUI_API ImVec2 GetWindowContentRegionMin(); // content boundaries min for the full window (roughly (0,0)-Scroll), in window coordinates + IMGUI_API ImVec2 GetWindowContentRegionMax(); // content boundaries max for the full window (roughly (0,0)+Size-Scroll) where Size can be override with SetNextWindowContentSize(), in window coordinates // Windows Scrolling IMGUI_API float GetScrollX(); // get scrolling amount [0 .. GetScrollMaxX()] @@ -376,7 +407,7 @@ namespace ImGui IMGUI_API void PushStyleVar(ImGuiStyleVar idx, float val); // modify a style float variable. always use this if you modify the style after NewFrame(). IMGUI_API void PushStyleVar(ImGuiStyleVar idx, const ImVec2& val); // modify a style ImVec2 variable. always use this if you modify the style after NewFrame(). 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 PushAllowKeyboardFocus(bool allow_keyboard_focus); // == tab stop enable. 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(); @@ -390,6 +421,7 @@ namespace ImGui IMGUI_API void PopTextWrapPos(); // Style read access + // - Use the style editor (ShowStyleEditor() function) to interactively see what the colors are) 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 IMGUI_API ImVec2 GetFontTexUvWhitePixel(); // get UV coordinate for a while pixel, useful to draw custom shapes via the ImDrawList API @@ -430,11 +462,15 @@ namespace ImGui IMGUI_API float GetFrameHeightWithSpacing(); // ~ FontSize + style.FramePadding.y * 2 + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of framed widgets) // ID stack/scopes - // - Read the FAQ for more details about how ID are handled in dear imgui. If you are creating widgets in a loop you most - // likely want to push a unique identifier (e.g. object pointer, loop index) to uniquely differentiate them. - // - The resulting ID are hashes of the entire stack. + // Read the FAQ (docs/FAQ.md or http://dearimgui.org/faq) for more details about how ID are handled in dear imgui. + // - Those questions are answered and impacted by understanding of the ID stack system: + // - "Q: Why is my widget not reacting when I click on it?" + // - "Q: How can I have widgets with an empty label?" + // - "Q: How can I have multiple widgets with the same label?" + // - Short version: ID are hashes of the entire ID stack. If you are creating widgets in a loop you most likely + // want to push a unique identifier (e.g. object pointer, loop index) to uniquely differentiate them. // - You can also use the "Label##foobar" syntax within widget label to distinguish them from each others. - // - In this header file we use the "label"/"name" terminology to denote a string that will be displayed and used as an ID, + // - In this header file we use the "label"/"name" terminology to denote a string that will be displayed + used as an ID, // whereas "str_id" denote a string that is only used as an ID and not normally displayed. IMGUI_API void PushID(const char* str_id); // push string into the ID stack (will hash string). IMGUI_API void PushID(const char* str_id_begin, const char* str_id_end); // push string into the ID stack (will hash string). @@ -487,12 +523,12 @@ namespace ImGui IMGUI_API bool Combo(const char* label, int* current_item, bool(*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int popup_max_height_in_items = -1); // Widgets: Drag Sliders - // - CTRL+Click on any drag box to turn them into an input box. Manually input values aren't clamped and can go off-bounds. + // - CTRL+Click on any drag box to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp. // - For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every functions, note that a 'float v[X]' function argument is the same as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible. You can pass address of your first element out of a contiguous set, e.g. &myvector.x // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc. // - Format string may also be set to NULL or use the default format ("%f" or "%d"). // - Speed are per-pixel of mouse movement (v_speed=0.2f: mouse needs to move by 5 pixels to increase value by 1). For gamepad/keyboard navigation, minimum speed is Max(v_speed, minimum_step_at_given_precision). - // - Use v_min < v_max to clamp edits to given limits. Note that CTRL+Click manual input can override those limits. + // - Use v_min < v_max to clamp edits to given limits. Note that CTRL+Click manual input can override those limits if ImGuiSliderFlags_AlwaysClamp is not used. // - Use v_max = FLT_MAX / INT_MAX etc to avoid clamping to a maximum, same with v_min = -FLT_MAX / INT_MIN to avoid clamping to a minimum. // - We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them. // - Legacy: Pre-1.78 there are DragXXX() function signatures that takes a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument. @@ -511,7 +547,7 @@ namespace ImGui IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed = 1.0f, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0); // Widgets: Regular Sliders - // - CTRL+Click on any slider to turn them into an input box. Manually input values aren't clamped and can go off-bounds. + // - CTRL+Click on any slider to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp. // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc. // - Format string may also be set to NULL or use the default format ("%f" or "%d"). // - Legacy: Pre-1.78 there are SliderXXX() function signatures that takes a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument. @@ -548,7 +584,7 @@ namespace ImGui IMGUI_API bool InputDouble(const char* label, double* v, double step = 0.0, double step_fast = 0.0, const char* format = "%.6f", ImGuiInputTextFlags flags = 0); IMGUI_API bool InputScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_step = NULL, const void* p_step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags flags = 0); IMGUI_API bool InputScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_step = NULL, const void* p_step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags flags = 0); - IMGUI_API bool Hotkey(const char* label, int* key, const ImVec2& size); + IMGUI_API bool Hotkey(const char* label, int* key, const ImVec2& size); // Widgets: Color Editor/Picker (tip: the ColorEdit* functions have a little color square that can be left-clicked to open a picker, and right-clicked to open an option menu.) // - Note that in C++ a 'float v[X]' function argument is the _same_ as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible. @@ -598,7 +634,7 @@ namespace ImGui IMGUI_API bool ListBox(const char* label, int* current_item, bool (*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int height_in_items = -1); // Widgets: Data Plotting - // - Consider using ImPlot (https://github.com/epezent/implot) + // - Consider using ImPlot (https://github.com/epezent/implot) which is much better! IMGUI_API void PlotLines(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float)); IMGUI_API void PlotLines(const char* label, float(*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0)); IMGUI_API void PlotHistogram(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float)); @@ -640,12 +676,14 @@ namespace ImGui // - You can bypass the hovering restriction by using ImGuiHoveredFlags_AllowWhenBlockedByPopup when calling IsItemHovered() or IsWindowHovered(). // - IMPORTANT: Popup identifiers are relative to the current ID stack, so OpenPopup and BeginPopup generally needs to be at the same level of the stack. // This is sometimes leading to confusing mistakes. May rework this in the future. + // Popups: begin/end functions // - BeginPopup(): query popup state, if open start appending into the window. Call EndPopup() afterwards. ImGuiWindowFlags are forwarded to the window. // - BeginPopupModal(): block every interactions behind the window, cannot be closed by user, add a dimming background, has a title bar. IMGUI_API bool BeginPopup(const char* str_id, ImGuiWindowFlags flags = 0); // return true if the popup is open, and you can start outputting to it. IMGUI_API bool BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); // return true if the modal is open, and you can start outputting to it. IMGUI_API void EndPopup(); // only call EndPopup() if BeginPopupXXX() returns true! + // Popups: open/close functions // - OpenPopup(): set popup state to open. ImGuiPopupFlags are available for opening options. // - If not modal: they can be closed by clicking anywhere outside them, or by pressing ESCAPE. @@ -657,6 +695,7 @@ namespace ImGui IMGUI_API void OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags = 0); // id overload to facilitate calling from nested stacks IMGUI_API void OpenPopupOnItemClick(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // helper to open popup when clicked on last item. Default to ImGuiPopupFlags_MouseButtonRight == 1. (note: actually triggers on the mouse _released_ event to be consistent with popup behaviors) IMGUI_API void CloseCurrentPopup(); // manually close the popup we have begin-ed into. + // Popups: open+begin combined functions helpers // - Helpers to do OpenPopup+BeginPopup where the Open action is triggered by e.g. hovering an item and right-clicking. // - They are convenient to easily create context menus, hence the name. @@ -665,6 +704,7 @@ namespace ImGui IMGUI_API bool BeginPopupContextItem(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // open+begin popup when clicked on last item. Use str_id==NULL to associate the popup to previous item. If you want to use that on a non-interactive item such as Text() you need to pass in an explicit ID here. read comments in .cpp! IMGUI_API bool BeginPopupContextWindow(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1);// open+begin popup when clicked on current window. IMGUI_API bool BeginPopupContextVoid(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // open+begin popup when clicked in void (where there are no windows). + // Popups: query functions // - IsPopupOpen(): return true if the popup is open at the current BeginPopup() level of the popup stack. // - IsPopupOpen() with ImGuiPopupFlags_AnyPopupId: return true if any popup is open at the current BeginPopup() level of the popup stack. @@ -701,6 +741,7 @@ namespace ImGui 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 true when column is visible. IMGUI_API bool TableSetColumnIndex(int column_n); // append into the specified column. Return true when column is visible. + // Tables: Headers & Columns declaration // - Use TableSetupColumn() to specify label, resizing policy, default width/weight, id, various other flags etc. // - Use TableHeadersRow() to create a header row and automatically submit a TableHeader() for each column. @@ -713,6 +754,7 @@ namespace ImGui 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: Sorting // - Call TableGetSortSpecs() to retrieve latest sort specs for the table. NULL when not sorting. // - When 'SpecsDirty == true' you should sort your data. It will be true when sorting specs have changed @@ -720,6 +762,7 @@ namespace ImGui // 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 ImGuiTableSortSpecs* TableGetSortSpecs(); // get latest sort specs for the table (NULL if not sorting). + // Tables: Miscellaneous functions // - Functions args 'int column_n' treat the default value of -1 as the same as passing the current column index. IMGUI_API int TableGetColumnCount(); // return number of columns (value passed to BeginTable) @@ -727,10 +770,10 @@ namespace ImGui IMGUI_API int TableGetRowIndex(); // return current row index. 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 ImGuiTableColumnFlags TableGetColumnFlags(int column_n = -1); // return column flags so you can query their Enabled/Visible/Sorted/Hovered status flags. Pass -1 to use current column. - IMGUI_API void TableSetColumnEnabled(int column_n, bool v);// change enabled/disabled state of a column, set to false to hide the column. Note that end-user can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags_ContextMenuInBody) + IMGUI_API void TableSetColumnEnabled(int column_n, bool v);// change user accessible enabled/disabled state of a column. Set to false to hide the column. User can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags_ContextMenuInBody) IMGUI_API void TableSetBgColor(ImGuiTableBgTarget 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!) + // Legacy Columns API (prefer using Tables!) // - You can also use SameLine(pos_x) to mimic simplified columns. 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 @@ -772,6 +815,13 @@ namespace ImGui IMGUI_API void EndDragDropTarget(); // only call EndDragDropTarget() if BeginDragDropTarget() returns true! IMGUI_API const ImGuiPayload* GetDragDropPayload(); // peek directly into the current payload from anywhere. may return NULL. use ImGuiPayload::IsDataType() to test for the payload type. + // Disabling [BETA API] + // - Disable all user interactions and dim items visuals (applying style.DisabledAlpha over current colors) + // - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled) + // - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it. + IMGUI_API void BeginDisabled(bool disabled = true); + IMGUI_API void EndDisabled(); + // Clipping // - Mouse hovering is affected by ImGui::PushClipRect() calls, unlike direct calls to ImDrawList::PushClipRect() which are render only. IMGUI_API void PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect); @@ -820,7 +870,6 @@ namespace ImGui IMGUI_API const char* GetStyleColorName(ImGuiCol idx); // get a string corresponding to the enum value (for display, saving, etc.). IMGUI_API void SetStateStorage(ImGuiStorage* storage); // replace current window storage with our own (if you want to manipulate it yourself, typically clear subsection of it) IMGUI_API ImGuiStorage* GetStateStorage(); - IMGUI_API void CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // calculate coarse clipping for large list of evenly sized items. Prefer using the ImGuiListClipper higher-level helper if you can. IMGUI_API bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags flags = 0); // helper to create a child window / scrolling region that looks like a normal widget frame IMGUI_API void EndChildFrame(); // always call EndChildFrame() regardless of BeginChildFrame() return values (which indicates a collapsed/clipped window) @@ -848,9 +897,10 @@ namespace ImGui // - You can also use regular integer: it is forever guaranteed that 0=Left, 1=Right, 2=Middle. // - Dragging operations are only reported after mouse has moved a certain distance away from the initial clicking position (see 'lock_threshold' and 'io.MouseDraggingThreshold') IMGUI_API bool IsMouseDown(ImGuiMouseButton button); // is mouse button held? - IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, bool repeat = false); // did mouse button clicked? (went from !Down to Down) + IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, bool repeat = false); // did mouse button clicked? (went from !Down to Down). Same as GetMouseClickedCount() == 1. IMGUI_API bool IsMouseReleased(ImGuiMouseButton button); // did mouse button released? (went from Down to !Down) - IMGUI_API bool IsMouseDoubleClicked(ImGuiMouseButton button); // did mouse button double-clicked? (note that a double-click will also report IsMouseClicked() == true) + IMGUI_API bool IsMouseDoubleClicked(ImGuiMouseButton button); // did mouse button double-clicked? Same as GetMouseClickedCount() == 2. (note that a double-click will also report IsMouseClicked() == true) + IMGUI_API int GetMouseClickedCount(ImGuiMouseButton button); // return the number of successive mouse-clicks at the time where a click happen (otherwise 0). IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true);// is mouse hovering given bounding rect (in screen space). clipped by current clipping settings, but disregarding of other consideration of focus/window ordering/popup-block. IMGUI_API bool IsMousePosValid(const ImVec2* mouse_pos = NULL); // by convention we use (-FLT_MAX,-FLT_MAX) to denote that there is no mouse available IMGUI_API bool IsAnyMouseDown(); // is any mouse button held? @@ -871,6 +921,7 @@ namespace ImGui // Settings/.Ini Utilities // - The disk functions are automatically called if io.IniFilename != NULL (default is "imgui.ini"). // - Set io.IniFilename to NULL to load/save manually. Read io.WantSaveIniSettings description about handling .ini saving manually. + // - Important: default value "imgui.ini" is relative to current working dir! Most apps will want to lock this to an absolute path (e.g. same path as executables). IMGUI_API void LoadIniSettingsFromDisk(const char* ini_filename); // call after CreateContext() and before the first call to NewFrame(). NewFrame() automatically calls LoadIniSettingsFromDisk(io.IniFilename). IMGUI_API void LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size=0); // call after CreateContext() and before the first call to NewFrame() to provide .ini data from your own data source. IMGUI_API void SaveIniSettingsToDisk(const char* ini_filename); // this is automatically called (if io.IniFilename is not empty) a few seconds after any modification that should be reflected in the .ini file (and also by DestroyContext). @@ -904,7 +955,7 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_NoMove = 1 << 2, // Disable user moving the window ImGuiWindowFlags_NoScrollbar = 1 << 3, // Disable scrollbars (window can still scroll with mouse or programmatically) ImGuiWindowFlags_NoScrollWithMouse = 1 << 4, // Disable user vertically scrolling with mouse wheel. On child window, mouse wheel will be forwarded to the parent unless NoScrollbar is also set. - ImGuiWindowFlags_NoCollapse = 1 << 5, // Disable user collapsing window by double-clicking on it + ImGuiWindowFlags_NoCollapse = 1 << 5, // Disable user collapsing window by double-clicking on it. Also referred to as Window Menu Button (e.g. within a docking node). ImGuiWindowFlags_AlwaysAutoResize = 1 << 6, // Resize every window to its content every frame ImGuiWindowFlags_NoBackground = 1 << 7, // Disable drawing background color (WindowBg, etc.) and outside border. Similar as using SetNextWindowBgAlpha(0.0f). ImGuiWindowFlags_NoSavedSettings = 1 << 8, // Never load/save settings in .ini file @@ -918,13 +969,13 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 16, // Ensure child windows without border uses style.WindowPadding (ignored by default for non-bordered child windows, because more convenient) ImGuiWindowFlags_NoNavInputs = 1 << 18, // No gamepad/keyboard navigation within the window ImGuiWindowFlags_NoNavFocus = 1 << 19, // No focusing toward this window with gamepad/keyboard navigation (e.g. skipped by CTRL+TAB) - ImGuiWindowFlags_UnsavedDocument = 1 << 20, // Append '*' to title without affecting the ID, as a convenience to avoid using the ### operator. When used in a tab/docking context, tab is selected on closure and closure is deferred by one frame to allow code to cancel the closure (with a confirmation popup, etc.) without flicker. + ImGuiWindowFlags_UnsavedDocument = 1 << 20, // Display a dot next to the title. When used in a tab/docking context, tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. ImGuiWindowFlags_NoNav = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, ImGuiWindowFlags_NoDecoration = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse, ImGuiWindowFlags_NoInputs = ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, // [Internal] - ImGuiWindowFlags_NavFlattened = 1 << 23, // [BETA] Allow gamepad/keyboard navigation to cross over parent border to this child (only use on child that have no scrolling!) + ImGuiWindowFlags_NavFlattened = 1 << 23, // [BETA] On child window: allow gamepad/keyboard navigation to cross over parent border to this child or between sibling child windows. ImGuiWindowFlags_ChildWindow = 1 << 24, // Don't use! For internal use by BeginChild() ImGuiWindowFlags_Tooltip = 1 << 25, // Don't use! For internal use by BeginTooltip() ImGuiWindowFlags_Popup = 1 << 26, // Don't use! For internal use by BeginPopup() @@ -958,10 +1009,7 @@ enum ImGuiInputTextFlags_ ImGuiInputTextFlags_NoUndoRedo = 1 << 16, // Disable undo/redo. Note that input text owns the text data while active, if you want to provide your own undo/redo stack you need e.g. to call ClearActiveID(). ImGuiInputTextFlags_CharsScientific = 1 << 17, // Allow 0123456789.+-*/eE (Scientific notation input) ImGuiInputTextFlags_CallbackResize = 1 << 18, // Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. Notify when the string wants to be resized (for string types which hold a cache of their Size). You will be provided a new BufSize in the callback and NEED to honor it. (see misc/cpp/imgui_stdlib.h for an example of using this) - ImGuiInputTextFlags_CallbackEdit = 1 << 19, // Callback on any edit (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the underlying buffer while focus is active) - // [Internal] - ImGuiInputTextFlags_Multiline = 1 << 20, // For internal use by InputTextMultiline() - ImGuiInputTextFlags_NoMarkEdited = 1 << 21 // For internal use by functions using InputText() before reformatting data + ImGuiInputTextFlags_CallbackEdit = 1 << 19 // Callback on any edit (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the underlying buffer while focus is active) // Obsolete names (will be removed soon) #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS @@ -1059,7 +1107,7 @@ enum ImGuiTabBarFlags_ enum ImGuiTabItemFlags_ { ImGuiTabItemFlags_None = 0, - ImGuiTabItemFlags_UnsavedDocument = 1 << 0, // Append '*' to title without affecting the ID, as a convenience to avoid using the ### operator. Also: tab is selected on closure and closure is deferred by one frame to allow code to undo it without flicker. + ImGuiTabItemFlags_UnsavedDocument = 1 << 0, // Display a dot next to the title + tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. ImGuiTabItemFlags_SetSelected = 1 << 1, // Trigger flag to programmatically make the tab selected when calling BeginTabItem() ImGuiTabItemFlags_NoCloseWithMiddleMouseButton = 1 << 2, // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You can still repro this behavior on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false. ImGuiTabItemFlags_NoPushId = 1 << 3, // Don't call PushID(tab->ID)/PopID() on BeginTabItem()/EndTabItem() @@ -1153,28 +1201,30 @@ enum ImGuiTableColumnFlags_ { // Input configuration flags ImGuiTableColumnFlags_None = 0, - ImGuiTableColumnFlags_DefaultHide = 1 << 0, // Default as a hidden/disabled column. - ImGuiTableColumnFlags_DefaultSort = 1 << 1, // Default as a sorting column. - ImGuiTableColumnFlags_WidthStretch = 1 << 2, // Column will stretch. Preferable with horizontal scrolling disabled (default if table sizing policy is _SizingStretchSame or _SizingStretchProp). - ImGuiTableColumnFlags_WidthFixed = 1 << 3, // Column will not stretch. Preferable with horizontal scrolling enabled (default if table sizing policy is _SizingFixedFit and table is resizable). - ImGuiTableColumnFlags_NoResize = 1 << 4, // Disable manual resizing. - ImGuiTableColumnFlags_NoReorder = 1 << 5, // Disable manual reordering this column, this will also prevent other columns from crossing over this column. - ImGuiTableColumnFlags_NoHide = 1 << 6, // Disable ability to hide/disable this column. - ImGuiTableColumnFlags_NoClip = 1 << 7, // Disable clipping for this column (all NoClip columns will render in a same draw command). - ImGuiTableColumnFlags_NoSort = 1 << 8, // Disable ability to sort on this field (even if ImGuiTableFlags_Sortable is set on the table). - ImGuiTableColumnFlags_NoSortAscending = 1 << 9, // Disable ability to sort in the ascending direction. - ImGuiTableColumnFlags_NoSortDescending = 1 << 10, // Disable ability to sort in the descending direction. - ImGuiTableColumnFlags_NoHeaderWidth = 1 << 11, // Disable header text width contribution 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 column 0). - ImGuiTableColumnFlags_IndentDisable = 1 << 15, // Ignore current Indent value when entering cell (default for columns > 0). Indentation changes _within_ the cell will still be honored. + ImGuiTableColumnFlags_Disabled = 1 << 0, // Overriding/master disable flag: hide column, won't show in context menu (unlike calling TableSetColumnEnabled() which manipulates the user accessible state) + ImGuiTableColumnFlags_DefaultHide = 1 << 1, // Default as a hidden/disabled column. + ImGuiTableColumnFlags_DefaultSort = 1 << 2, // Default as a sorting column. + ImGuiTableColumnFlags_WidthStretch = 1 << 3, // Column will stretch. Preferable with horizontal scrolling disabled (default if table sizing policy is _SizingStretchSame or _SizingStretchProp). + ImGuiTableColumnFlags_WidthFixed = 1 << 4, // Column will not stretch. Preferable with horizontal scrolling enabled (default if table sizing policy is _SizingFixedFit and table is resizable). + ImGuiTableColumnFlags_NoResize = 1 << 5, // Disable manual resizing. + ImGuiTableColumnFlags_NoReorder = 1 << 6, // Disable manual reordering this column, this will also prevent other columns from crossing over this column. + ImGuiTableColumnFlags_NoHide = 1 << 7, // Disable ability to hide/disable this column. + ImGuiTableColumnFlags_NoClip = 1 << 8, // Disable clipping for this column (all NoClip columns will render in a same draw command). + ImGuiTableColumnFlags_NoSort = 1 << 9, // Disable ability to sort on this field (even if ImGuiTableFlags_Sortable is set on the table). + ImGuiTableColumnFlags_NoSortAscending = 1 << 10, // Disable ability to sort in the ascending direction. + ImGuiTableColumnFlags_NoSortDescending = 1 << 11, // Disable ability to sort in the descending direction. + ImGuiTableColumnFlags_NoHeaderLabel = 1 << 12, // TableHeadersRow() will not submit label for this column. Convenient for some small columns. Name will still appear in context menu. + ImGuiTableColumnFlags_NoHeaderWidth = 1 << 13, // Disable header text width contribution to automatic column width. + ImGuiTableColumnFlags_PreferSortAscending = 1 << 14, // Make the initial sort direction Ascending when first sorting on this column (default). + ImGuiTableColumnFlags_PreferSortDescending = 1 << 15, // Make the initial sort direction Descending when first sorting on this column. + ImGuiTableColumnFlags_IndentEnable = 1 << 16, // Use current Indent value when entering cell (default for column 0). + ImGuiTableColumnFlags_IndentDisable = 1 << 17, // Ignore current Indent value when entering cell (default for columns > 0). Indentation changes _within_ the cell will still be honored. // Output status flags, read-only via TableGetColumnFlags() - ImGuiTableColumnFlags_IsEnabled = 1 << 20, // Status: is enabled == not hidden by user/api (referred to as "Hide" in _DefaultHide and _NoHide) flags. - ImGuiTableColumnFlags_IsVisible = 1 << 21, // Status: is visible == is enabled AND not clipped by scrolling. - ImGuiTableColumnFlags_IsSorted = 1 << 22, // Status: is currently part of the sort specs - ImGuiTableColumnFlags_IsHovered = 1 << 23, // Status: is hovered by mouse + ImGuiTableColumnFlags_IsEnabled = 1 << 24, // Status: is enabled == not hidden by user/api (referred to as "Hide" in _DefaultHide and _NoHide) flags. + ImGuiTableColumnFlags_IsVisible = 1 << 25, // Status: is visible == is enabled AND not clipped by scrolling. + ImGuiTableColumnFlags_IsSorted = 1 << 26, // Status: is currently part of the sort specs + ImGuiTableColumnFlags_IsHovered = 1 << 27, // Status: is hovered by mouse // [Internal] Combinations and masks ImGuiTableColumnFlags_WidthMask_ = ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_WidthFixed, @@ -1216,9 +1266,11 @@ enum ImGuiTableBgTarget_ enum ImGuiFocusedFlags_ { ImGuiFocusedFlags_None = 0, - ImGuiFocusedFlags_ChildWindows = 1 << 0, // IsWindowFocused(): Return true if any children of the window is focused - ImGuiFocusedFlags_RootWindow = 1 << 1, // IsWindowFocused(): Test from root window (top most parent of the current hierarchy) - ImGuiFocusedFlags_AnyWindow = 1 << 2, // IsWindowFocused(): Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ! + ImGuiFocusedFlags_ChildWindows = 1 << 0, // Return true if any children of the window is focused + ImGuiFocusedFlags_RootWindow = 1 << 1, // Test from root window (top most parent of the current hierarchy) + ImGuiFocusedFlags_AnyWindow = 1 << 2, // Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ! + ImGuiFocusedFlags_NoPopupHierarchy = 1 << 3, // Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow) + //ImGuiFocusedFlags_DockHierarchy = 1 << 4, // Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow) ImGuiFocusedFlags_RootAndChildWindows = ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows }; @@ -1231,11 +1283,13 @@ enum ImGuiHoveredFlags_ ImGuiHoveredFlags_ChildWindows = 1 << 0, // IsWindowHovered() only: Return true if any children of the window is hovered ImGuiHoveredFlags_RootWindow = 1 << 1, // IsWindowHovered() only: Test from root window (top most parent of the current hierarchy) ImGuiHoveredFlags_AnyWindow = 1 << 2, // IsWindowHovered() only: Return true if any window is hovered - ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 3, // Return true even if a popup window is normally blocking access to this item/window - //ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 4, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet. - ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 5, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns. - ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 6, // Return true even if the position is obstructed or overlapped by another window - ImGuiHoveredFlags_AllowWhenDisabled = 1 << 7, // Return true even if the item is disabled + ImGuiHoveredFlags_NoPopupHierarchy = 1 << 3, // IsWindowHovered() only: Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow) + //ImGuiHoveredFlags_DockHierarchy = 1 << 4, // IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow) + ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 5, // Return true even if a popup window is normally blocking access to this item/window + //ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 6, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet. + ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 7, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns. + ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 8, // IsItemHovered() only: Return true even if the position is obstructed or overlapped by another window + ImGuiHoveredFlags_AllowWhenDisabled = 1 << 9, // IsItemHovered() only: Return true even if the item is disabled ImGuiHoveredFlags_RectOnly = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped, ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows }; @@ -1361,13 +1415,12 @@ enum ImGuiNavInput_ // [Internal] Don't use directly! This is used internally to differentiate keyboard from gamepad inputs for behaviors that require to differentiate them. // Keyboard behavior that have no corresponding gamepad mapping (e.g. CTRL+TAB) will be directly reading from io.KeysDown[] instead of io.NavInputs[]. - ImGuiNavInput_KeyMenu_, // toggle menu // = io.KeyAlt ImGuiNavInput_KeyLeft_, // move left // = Arrow keys ImGuiNavInput_KeyRight_, // move right ImGuiNavInput_KeyUp_, // move up ImGuiNavInput_KeyDown_, // move down ImGuiNavInput_COUNT, - ImGuiNavInput_InternalStart_ = ImGuiNavInput_KeyMenu_ + ImGuiNavInput_InternalStart_ = ImGuiNavInput_KeyLeft_ }; // Configuration flags stored in io.ConfigFlags. Set by user/application. @@ -1466,6 +1519,7 @@ enum ImGuiStyleVar_ { // Enum name --------------------- // Member in ImGuiStyle structure (see ImGuiStyle for descriptions) ImGuiStyleVar_Alpha, // float Alpha + ImGuiStyleVar_DisabledAlpha, // float DisabledAlpha ImGuiStyleVar_WindowPadding, // ImVec2 WindowPadding ImGuiStyleVar_WindowRounding, // float WindowRounding ImGuiStyleVar_WindowBorderSize, // float WindowBorderSize @@ -1537,13 +1591,13 @@ enum ImGuiColorEditFlags_ // Defaults Options. You can set application defaults using SetColorEditOptions(). The intent is that you probably don't want to // override them in most of your calls. Let the user choose via the option menu and/or call SetColorEditOptions() once during startup. - ImGuiColorEditFlags__OptionsDefault = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_PickerHueBar, + ImGuiColorEditFlags_DefaultOptions_ = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_PickerHueBar, // [Internal] Masks - ImGuiColorEditFlags__DisplayMask = ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_DisplayHex, - ImGuiColorEditFlags__DataTypeMask = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_Float, - ImGuiColorEditFlags__PickerMask = ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_PickerHueBar, - ImGuiColorEditFlags__InputMask = ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_InputHSV + ImGuiColorEditFlags_DisplayMask_ = ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_DisplayHex, + ImGuiColorEditFlags_DataTypeMask_ = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_Float, + ImGuiColorEditFlags_PickerMask_ = ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_PickerHueBar, + ImGuiColorEditFlags_InputMask_ = ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_InputHSV // Obsolete names (will be removed) #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS @@ -1637,6 +1691,7 @@ template void IM_DELETE(T* p) { if (p) { p->~T(); ImGui::MemFree(p // Do NOT use this class as a std::vector replacement in your own code! Many of the structures used by dear imgui can be safely initialized by a zero-memset. //----------------------------------------------------------------------------- +IM_MSVC_RUNTIME_CHECKS_OFF template struct ImVector { @@ -1653,7 +1708,11 @@ struct ImVector inline ImVector() { Size = Capacity = 0; Data = NULL; } inline ImVector(const ImVector& src) { Size = Capacity = 0; Data = NULL; operator=(src); } inline ImVector& operator=(const ImVector& src) { clear(); resize(src.Size); memcpy(Data, src.Data, (size_t)Size * sizeof(T)); return *this; } - inline ~ImVector() { if (Data) IM_FREE(Data); } + inline ~ImVector() { if (Data) IM_FREE(Data); } // Important: does not destruct anything + + inline void clear() { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } } // Important: does not destruct anything + inline void clear_delete() { for (int n = 0; n < Size; n++) IM_DELETE(Data[n]); clear(); } // Important: never called automatically! always explicit. + inline void clear_destruct() { for (int n = 0; n < Size; n++) Data[n].~T(); clear(); } // Important: never called automatically! always explicit. inline bool empty() const { return Size == 0; } inline int size() const { return Size; } @@ -1663,7 +1722,6 @@ struct ImVector inline T& operator[](int i) { IM_ASSERT(i >= 0 && i < Size); return Data[i]; } inline const T& operator[](int i) const { IM_ASSERT(i >= 0 && i < Size); return Data[i]; } - inline void clear() { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } } inline T* begin() { return Data; } inline const T* begin() const { return Data; } inline T* end() { return Data + Size; } @@ -1685,7 +1743,7 @@ struct ImVector inline void pop_back() { IM_ASSERT(Size > 0); Size--; } inline void push_front(const T& v) { if (Size == 0) push_back(v); else insert(Data, v); } inline T* erase(const T* it) { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + 1, ((size_t)Size - (size_t)off - 1) * sizeof(T)); Size--; return Data + off; } - inline T* erase(const T* it, const T* it_last){ IM_ASSERT(it >= Data && it < Data + Size && it_last > it && it_last <= Data + Size); const ptrdiff_t count = it_last - it; const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + count, ((size_t)Size - (size_t)off - count) * sizeof(T)); Size -= (int)count; return Data + off; } + inline T* erase(const T* it, const T* it_last){ IM_ASSERT(it >= Data && it < Data + Size && it_last > it && it_last <= Data + Size); const ptrdiff_t count = it_last - it; const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + count, ((size_t)Size - (size_t)off - (size_t)count) * sizeof(T)); Size -= (int)count; return Data + off; } inline T* erase_unsorted(const T* it) { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; if (it < Data + Size - 1) memcpy(Data + off, Data + Size - 1, sizeof(T)); Size--; return Data + off; } inline T* insert(const T* it, const T& v) { IM_ASSERT(it >= Data && it <= Data + Size); const ptrdiff_t off = it - Data; if (Size == Capacity) reserve(_grow_capacity(Size + 1)); if (off < (int)Size) memmove(Data + off + 1, Data + off, ((size_t)Size - (size_t)off) * sizeof(T)); memcpy(&Data[off], &v, sizeof(v)); Size++; return Data + off; } inline bool contains(const T& v) const { const T* data = Data; const T* data_end = Data + Size; while (data < data_end) if (*data++ == v) return true; return false; } @@ -1695,6 +1753,7 @@ struct ImVector inline bool find_erase_unsorted(const T& v) { const T* it = find(v); if (it < Data + Size) { erase_unsorted(it); return true; } return false; } inline int index_from_ptr(const T* it) const { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; return (int)off; } }; +IM_MSVC_RUNTIME_CHECKS_RESTORE //----------------------------------------------------------------------------- // [SECTION] ImGuiStyle @@ -1707,6 +1766,7 @@ struct ImVector struct ImGuiStyle { float Alpha; // Global alpha applies to everything in Dear ImGui. + float DisabledAlpha; // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha. ImVec2 WindowPadding; // Padding within a window. float WindowRounding; // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended. float WindowBorderSize; // Thickness of border around windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly). @@ -1769,7 +1829,7 @@ struct ImGuiIO ImVec2 DisplaySize; // // Main display size, in pixels (generally == GetMainViewport()->Size) float DeltaTime; // = 1.0f/60.0f // Time elapsed since last frame, in seconds. float IniSavingRate; // = 5.0f // Minimum time between saving positions/sizes to .ini file, in seconds. - const char* IniFilename; // = "imgui.ini" // Path to .ini file. Set NULL to disable automatic .ini loading/saving, if e.g. you want to manually load/save from memory. + const char* IniFilename; // = "imgui.ini" // Path to .ini file (important: default "imgui.ini" is relative to current working dir!). Set NULL to disable automatic .ini loading/saving or if you want to manually call LoadIniSettingsXXX() / SaveIniSettingsXXX() functions. const char* LogFilename; // = "imgui_log.txt"// Path to .log file (default parameter to ImGui::LogToFile when no file is specified). float MouseDoubleClickTime; // = 0.30f // Time for a double-click, in seconds. float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click, in pixels. @@ -1836,7 +1896,9 @@ struct ImGuiIO IMGUI_API void AddInputCharacter(unsigned int c); // Queue new character input IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue new character input from an UTF-16 character, it can be a surrogate IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue new characters input from an UTF-8 string - IMGUI_API void ClearInputCharacters(); // Clear the text input buffer manually + IMGUI_API void AddFocusEvent(bool focused); // Notifies Dear ImGui when hosting platform windows lose or gain input focus + IMGUI_API void ClearInputCharacters(); // [Internal] Clear the text input buffer manually + IMGUI_API void ClearInputKeys(); // [Internal] Release all keys //------------------------------------------------------------------ // Output - Updated by NewFrame() or EndFrame()/Render() @@ -1851,7 +1913,7 @@ struct ImGuiIO bool WantSaveIniSettings; // When manual .ini load/save is active (io.IniFilename == NULL), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. Important: clear io.WantSaveIniSettings yourself after saving! bool NavActive; // Keyboard/Gamepad navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag. bool NavVisible; // Keyboard/Gamepad navigation is visible and allowed (will handle ImGuiKey_NavXXX events). - float Framerate; // Application framerate estimate, in frame per second. Solely for convenience. Rolling average estimation based on io.DeltaTime over 120 frames. + float Framerate; // Rough estimate of application framerate, in frame per second. Solely for convenience. Rolling average estimation based on io.DeltaTime over 120 frames. int MetricsRenderVertices; // Vertices output during last call to Render() int MetricsRenderIndices; // Indices output during last call to Render() = number of triangles * 3 int MetricsRenderWindows; // Number of visible windows @@ -1863,15 +1925,19 @@ struct ImGuiIO // [Internal] Dear ImGui will maintain those fields. Forward compatibility not guaranteed! //------------------------------------------------------------------ + bool WantCaptureMouseUnlessPopupClose;// Alternative to WantCaptureMouse: (WantCaptureMouse == true && WantCaptureMouseUnlessPopupClose == false) when a click over void is expected to close a popup. ImGuiKeyModFlags KeyMods; // Key mods flags (same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags), updated by NewFrame() + ImGuiKeyModFlags KeyModsPrev; // Previous key mods ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid) ImVec2 MouseClickedPos[5]; // Position at time of clicking double MouseClickedTime[5]; // Time of last click (used to figure out double-click) - bool MouseClicked[5]; // Mouse button went from !Down to Down - bool MouseDoubleClicked[5]; // Has mouse button been double-clicked? + bool MouseClicked[5]; // Mouse button went from !Down to Down (same as MouseClickedCount[x] != 0) + bool MouseDoubleClicked[5]; // Has mouse button been double-clicked? (same as MouseClickedCount[x] == 2) + ImU16 MouseClickedCount[5]; // == 0 (not clicked), == 1 (same as MouseClicked[]), == 2 (double-clicked), == 3 (triple-clicked) etc. when going from !Down to Down + ImU16 MouseClickedLastCount[5]; // Count successive number of clicks. Stays valid after mouse release. Reset after another click is done. bool MouseReleased[5]; // Mouse button went from Down to !Down - bool MouseDownOwned[5]; // Track if button was clicked inside a dear imgui window. We don't request mouse capture from the application if click started outside ImGui bounds. - bool MouseDownWasDoubleClick[5]; // Track if button down was a double-click + bool MouseDownOwned[5]; // Track if button was clicked inside a dear imgui window or over void blocked by a popup. We don't request mouse capture from the application if click started outside ImGui bounds. + bool MouseDownOwnedUnlessPopupClose[5];//Track if button was clicked inside a dear imgui window. float MouseDownDuration[5]; // Duration the mouse button has been down (0.0f == just clicked) float MouseDownDurationPrev[5]; // Previous time the mouse button has been down ImVec2 MouseDragMaxDistanceAbs[5]; // Maximum distance, absolute, on each axis, of how much mouse has traveled from the clicking point @@ -1881,6 +1947,7 @@ struct ImGuiIO float NavInputsDownDuration[ImGuiNavInput_COUNT]; float NavInputsDownDurationPrev[ImGuiNavInput_COUNT]; float PenPressure; // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui. + bool AppFocusLost; ImWchar16 InputQueueSurrogate; // For AddInputCharacterUTF16 ImVector InputQueueCharacters; // Queue of _characters_ input (obtained by platform backend). Fill using AddInputCharacter() helper. @@ -2105,10 +2172,12 @@ struct ImGuiStorage }; // Helper: Manually clip large list of items. -// If you are submitting lots of evenly spaced items and you have a random access to the list, you can perform coarse -// clipping based on visibility to save yourself from processing those items at all. +// If you have lots evenly spaced items and you have a random access to the list, you can perform coarse +// clipping based on visibility to only submit items that are in view. // The clipper calculates the range of visible items and advance the cursor to compensate for the non-visible items we have skipped. -// (Dear ImGui already clip items based on their bounds but it needs to measure text size to do so, whereas manual coarse clipping before submission makes this cost and your own data fetching/submission cost almost null) +// (Dear ImGui already clip items based on their bounds but: it needs to first layout the item to do so, and generally +// fetching/submitting your own data incurs additional cost. Coarse clipping using ImGuiListClipper allows you to easily +// scale using lists with tens of thousands of items without a problem) // Usage: // ImGuiListClipper clipper; // clipper.Begin(1000); // We have 1000 elements, evenly spaced. @@ -2117,30 +2186,30 @@ struct ImGuiStorage // ImGui::Text("line number %d", i); // Generally what happens is: // - Clipper lets you process the first element (DisplayStart = 0, DisplayEnd = 1) regardless of it being visible or not. -// - User code submit one element. +// - User code submit that one element. // - Clipper can measure the height of the first element // - Clipper calculate the actual range of elements to display based on the current clipping rectangle, position the cursor before the first visible element. // - User code submit visible elements. +// - The clipper also handles various subtleties related to keyboard/gamepad navigation, wrapping etc. struct ImGuiListClipper { - int DisplayStart; - int DisplayEnd; - - // [Internal] - int ItemsCount; - int StepNo; - int ItemsFrozen; - float ItemsHeight; - float StartPosY; - - IMGUI_API ImGuiListClipper(); - IMGUI_API ~ImGuiListClipper(); + int DisplayStart; // First item to display, updated by each call to Step() + int DisplayEnd; // End of items to display (exclusive) + int ItemsCount; // [Internal] Number of items + float ItemsHeight; // [Internal] Height of item after a first step and item submission can calculate it + float StartPosY; // [Internal] Cursor position at the time of Begin() or after table frozen rows are all processed + void* TempData; // [Internal] Internal data // items_count: Use INT_MAX if you don't know how many items you have (in which case the cursor won't be advanced in the final step) // items_height: Use -1.0f to be calculated automatically on first step. Otherwise pass in the distance between your items, typically GetTextLineHeightWithSpacing() or GetFrameHeightWithSpacing(). - IMGUI_API void Begin(int items_count, float items_height = -1.0f); // Automatically called by constructor if you passed 'items_count' or by Step() in Step 1. - IMGUI_API void End(); // Automatically called on the last call of Step() that returns false. - IMGUI_API bool Step(); // Call until it returns false. The DisplayStart/DisplayEnd fields will be set and you can process/draw those items. + IMGUI_API ImGuiListClipper(); + IMGUI_API ~ImGuiListClipper(); + IMGUI_API void Begin(int items_count, float items_height = -1.0f); + IMGUI_API void End(); // Automatically called on the last call of Step() that returns false. + IMGUI_API bool Step(); // Call until it returns false. The DisplayStart/DisplayEnd fields will be set and you can process/draw those items. + + // Call ForceDisplayRangeByIndices() before first call to Step() if you need a range of items to be displayed regardless of visibility. + IMGUI_API void ForceDisplayRangeByIndices(int item_min, int item_max); // item_max is exclusive e.g. use (42, 42+1) to make item 42 always visible BUT due to alignment/padding of certain items it is likely that an extra item may be included on either end of the display range. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS inline ImGuiListClipper(int items_count, float items_height = -1.0f) { memset(this, 0, sizeof(*this)); ItemsCount = -1; Begin(items_count, items_height); } // [removed in 1.79] @@ -2230,14 +2299,10 @@ struct ImDrawCmd void* UserCallbackData; // 4-8 // The draw callback code can access this. ImDrawCmd() { memset(this, 0, sizeof(*this)); } // Also ensure our padding fields are zeroed -}; -// Vertex index, default to 16-bit -// To allow large meshes with 16-bit indices: set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset' and handle ImDrawCmd::VtxOffset in the renderer backend (recommended). -// To use 32-bit indices: override with '#define ImDrawIdx unsigned int' in imconfig.h. -#ifndef ImDrawIdx -typedef unsigned short ImDrawIdx; -#endif + // Since 1.83: returns ImTextureID associated with this draw call. Warning: DO NOT assume this is always same as 'TextureId' (we will change this function for an upcoming feature) + inline ImTextureID GetTexID() const { return TextureId; } +}; // Vertex layout #ifndef IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT @@ -2442,6 +2507,7 @@ struct ImDrawList IMGUI_API void _ResetForNewFrame(); IMGUI_API void _ClearFreeMemory(); IMGUI_API void _PopUnusedDrawCmd(); + IMGUI_API void _TryMergeDrawCmds(); IMGUI_API void _OnChangedClipRect(); IMGUI_API void _OnChangedTextureID(); IMGUI_API void _OnChangedVtxOffset(); @@ -2592,7 +2658,7 @@ struct ImFontAtlas IMGUI_API bool Build(); // Build pixels data. This is called automatically for you by the GetTexData*** functions. IMGUI_API void GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 1 byte per-pixel IMGUI_API void GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 4 bytes-per-pixel - bool IsBuilt() const { return Fonts.Size > 0 && (TexPixelsAlpha8 != NULL || TexPixelsRGBA32 != NULL); } + bool IsBuilt() const { return Fonts.Size > 0 && TexReady; } // Bit ambiguous: used to detect when user didn't built texture but effectively we should check TexID != 0 except that would be backend dependent... void SetTexID(ImTextureID id) { TexID = id; } //------------------------------------------- @@ -2642,6 +2708,7 @@ struct ImFontAtlas // [Internal] // NB: Access texture data via GetTexData*() calls! Which will setup a default font for you. + bool TexReady; // Set when texture was built matching current font input bool TexPixelsUseColors; // Tell whether our texture data is known to use colors (rather than just alpha channel), in order to help backend select a format. unsigned char* TexPixelsAlpha8; // 1 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight unsigned int* TexPixelsRGBA32; // 4 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight * 4 @@ -2664,7 +2731,7 @@ struct ImFontAtlas #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+ - typedef ImFontGlyphRangesBuilder GlyphRangesBuilder; // OBSOLETED in 1.67+ + //typedef ImFontGlyphRangesBuilder GlyphRangesBuilder; // OBSOLETED in 1.67+ #endif }; @@ -2686,8 +2753,9 @@ struct ImFont ImFontAtlas* ContainerAtlas; // 4-8 // out // // What we has been loaded into const ImFontConfig* ConfigData; // 4-8 // in // // Pointer within ContainerAtlas->ConfigData short ConfigDataCount; // 2 // in // ~ 1 // Number of ImFontConfig involved in creating this font. Bigger than 1 when merging multiple font sources into one ImFont. - ImWchar FallbackChar; // 2 // in // = '?' // Replacement character if a glyph isn't found. Only set via SetFallbackChar() - ImWchar EllipsisChar; // 2 // out // = -1 // Character used for ellipsis rendering. + ImWchar FallbackChar; // 2 // out // = FFFD/'?' // Character used if a glyph isn't found. + ImWchar EllipsisChar; // 2 // out // = '...' // Character used for ellipsis rendering. + ImWchar DotChar; // 2 // out // = '.' // Character used for ellipsis rendering (if a single '...' character isn't found) bool DirtyLookupTables; // 1 // out // float Scale; // 4 // in // = 1.f // Base font scale, multiplied by the per-window font scale which you can adjust with SetWindowFontScale() float Ascent, Descent; // 4+4 // out // // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] @@ -2717,7 +2785,6 @@ struct ImFont IMGUI_API void AddGlyph(const ImFontConfig* src_cfg, ImWchar c, float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1, float advance_x); IMGUI_API void AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst = true); // Makes 'dst' character/glyph points to 'src' character/glyph. Currently needs to be called AFTER fonts have been built. IMGUI_API void SetGlyphVisible(ImWchar c, bool visible); - IMGUI_API void SetFallbackChar(ImWchar c); IMGUI_API bool IsGlyphRangeUnused(unsigned int c_begin, unsigned int c_last); }; @@ -2725,7 +2792,7 @@ struct ImFont // [SECTION] Viewports //----------------------------------------------------------------------------- -// Flags stored in ImGuiViewport::Flags +// Flags stored in ImGuiViewport::Flags, giving indications to the platform backends. enum ImGuiViewportFlags_ { ImGuiViewportFlags_None = 0, @@ -2765,6 +2832,10 @@ struct ImGuiViewport #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS namespace ImGui { + // OBSOLETED in 1.86 (from November 2021) + IMGUI_API void CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // Calculate coarse clipping for large list of evenly sized items. Prefer using ImGuiListClipper. + // OBSOLETED in 1.85 (from August 2021) + static inline float GetWindowContentRegionWidth() { return GetWindowContentRegionMax().x - GetWindowContentRegionMin().x; } // OBSOLETED in 1.81 (from February 2021) IMGUI_API bool ListBoxHeader(const char* label, int items_count, int height_in_items = -1); // Helper to calculate size from items_count and height_in_items static inline bool ListBoxHeader(const char* label, const ImVec2& size = ImVec2(0, 0)) { return BeginListBox(label, size); } @@ -2794,8 +2865,18 @@ namespace ImGui static inline void SetNextTreeNodeOpen(bool open, ImGuiCond cond = 0) { SetNextItemOpen(open, cond); } // OBSOLETED in 1.70 (from May 2019) static inline float GetContentRegionAvailWidth() { return GetContentRegionAvail().x; } - // OBSOLETED in 1.69 (from Mar 2019) - static inline ImDrawList* GetOverlayDrawList() { return GetForegroundDrawList(); } + + // Some of the older obsolete names along with their replacement (commented out so they are not reported in IDE) + //static inline ImDrawList* GetOverlayDrawList() { return GetForegroundDrawList(); } // OBSOLETED in 1.69 (from Mar 2019) + //static inline void SetScrollHere(float ratio = 0.5f) { SetScrollHereY(ratio); } // OBSOLETED in 1.66 (from Nov 2018) + //static inline bool IsItemDeactivatedAfterChange() { return IsItemDeactivatedAfterEdit(); } // OBSOLETED in 1.63 (from Aug 2018) + //static inline bool IsAnyWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_AnyWindow); } // OBSOLETED in 1.60 (from Apr 2018) + //static inline bool IsAnyWindowHovered() { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); } // OBSOLETED in 1.60 (between Dec 2017 and Apr 2018) + //static inline void ShowTestWindow() { return ShowDemoWindow(); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) + //static inline bool IsRootWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_RootWindow); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) + //static inline bool IsRootWindowOrAnyChildFocused() { return IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) + //static inline void SetNextWindowContentWidth(float w) { SetNextWindowContentSize(ImVec2(w, 0.0f)); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) + //static inline float GetItemsLineHeightWithSpacing() { return GetFrameHeightWithSpacing(); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) } // OBSOLETED in 1.82 (from Mars 2021): flags for AddRect(), AddRectFilled(), AddImageRounded(), PathRect() @@ -2824,6 +2905,10 @@ enum ImDrawCornerFlags_ #pragma GCC diagnostic pop #endif +#ifdef _MSC_VER +#pragma warning (pop) +#endif + // Include imgui_user.h at the end of imgui.h (convenient for user to only explicitly include vanilla imgui.h) #ifdef IMGUI_INCLUDE_IMGUI_USER_H #include "imgui_user.h" diff --git a/external/imgui/include/imgui_impl_dx11.h b/r5dev/thirdparty/imgui/include/imgui_impl_dx11.h similarity index 81% rename from external/imgui/include/imgui_impl_dx11.h rename to r5dev/thirdparty/imgui/include/imgui_impl_dx11.h index 03fee14d..a83bce1b 100644 --- a/external/imgui/include/imgui_impl_dx11.h +++ b/r5dev/thirdparty/imgui/include/imgui_impl_dx11.h @@ -5,7 +5,8 @@ // [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. -// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs diff --git a/external/imgui/include/imgui_impl_win32.h b/r5dev/thirdparty/imgui/include/imgui_impl_win32.h similarity index 91% rename from external/imgui/include/imgui_impl_win32.h rename to r5dev/thirdparty/imgui/include/imgui_impl_win32.h index 5197b7f8..768fe169 100644 --- a/external/imgui/include/imgui_impl_win32.h +++ b/r5dev/thirdparty/imgui/include/imgui_impl_win32.h @@ -7,7 +7,8 @@ // [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE). // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. -// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs diff --git a/external/imgui/include/imgui_internal.h b/r5dev/thirdparty/imgui/include/imgui_internal.h similarity index 84% rename from external/imgui/include/imgui_internal.h rename to r5dev/thirdparty/imgui/include/imgui_internal.h index a8c2b440..73d58f6b 100644 --- a/external/imgui/include/imgui_internal.h +++ b/r5dev/thirdparty/imgui/include/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.83 WIP +// dear imgui, v1.86 // (internal structures/api) // You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! @@ -18,12 +18,14 @@ Index of this file: // [SECTION] Generic helpers // [SECTION] ImDrawList support // [SECTION] Widgets support: flags, enums, data structures +// [SECTION] Clipper support +// [SECTION] Navigation support // [SECTION] Columns support // [SECTION] Multi-select support // [SECTION] Docking support // [SECTION] Viewport support // [SECTION] Settings support -// [SECTION] Metrics, Debug +// [SECTION] Metrics, Debug tools // [SECTION] Generic context hooks // [SECTION] ImGuiContext (main imgui context) // [SECTION] ImGuiWindowTempData, ImGuiWindow @@ -43,7 +45,7 @@ Index of this file: //----------------------------------------------------------------------------- #ifndef IMGUI_VERSION -#error Must include imgui.h before imgui_internal.h +#include "imgui.h" #endif #include // FILE*, sscanf @@ -51,10 +53,21 @@ Index of this file: #include // sqrtf, fabsf, fmodf, powf, floorf, ceilf, cosf, sinf #include // INT_MIN, INT_MAX +// Enable SSE intrinsics if available +#if (defined __SSE__ || defined __x86_64__ || defined _M_X64) && !defined(IMGUI_DISABLE_SSE) +#define IMGUI_ENABLE_SSE +#include +#endif + // Visual Studio warnings #ifdef _MSC_VER #pragma warning (push) -#pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport) +#pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport) +#pragma warning (disable: 26812) // The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer) +#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6). +#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later +#pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types +#endif #endif // Clang/GCC warnings with -Weverything @@ -71,6 +84,7 @@ Index of this file: #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" #pragma clang diagnostic ignored "-Wdouble-promotion" #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision +#pragma clang diagnostic ignored "-Wmissing-noreturn" // warning: function 'xxx' could be declared with attribute 'noreturn' #elif defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind @@ -105,9 +119,9 @@ struct ImGuiContextHook; // Hook for extensions like ImGuiTestEngine struct ImGuiDataTypeInfo; // Type information associated to a ImGuiDataType enum struct ImGuiGroupData; // Stacked storage data for BeginGroup()/EndGroup() struct ImGuiInputTextState; // Internal state of the currently focused/edited text input box -struct ImGuiLastItemDataBackup; // Backup and restore IsItemHovered() internal data +struct ImGuiLastItemData; // Status storage for last submitted items struct ImGuiMenuColumns; // Simple column measurement, currently used for MenuItem() only -struct ImGuiNavMoveResult; // Result of a gamepad/keyboard directional navigation move query result +struct ImGuiNavItemData; // Result of a gamepad/keyboard directional navigation move query result struct ImGuiMetricsConfig; // Storage for ShowMetricsWindow() and DebugNodeXXX() functions struct ImGuiNextWindowData; // Storage for SetNextWindow** functions struct ImGuiNextItemData; // Storage for SetNextItem** functions @@ -121,14 +135,16 @@ 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 ImGuiTableTempData; // Temporary storage for one table (one per table in the stack), shared between tables. 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 ImGuiWindowTempData; // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame, in practice we currently keep it for each window) struct ImGuiWindowSettings; // Storage for a window .ini settings (we keep one of those even if the actual window wasn't instanced during this session) // Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists. typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical +typedef int ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later) typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag() typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for DC.LastItemStatusFlags typedef int ImGuiOldColumnFlags; // -> enum ImGuiOldColumnFlags_ // Flags: for BeginColumns() @@ -137,6 +153,7 @@ typedef int ImGuiNavDirSourceFlags; // -> enum ImGuiNavDirSourceFlags_ // F typedef int ImGuiNavMoveFlags; // -> enum ImGuiNavMoveFlags_ // Flags: for navigation requests typedef int ImGuiNextItemDataFlags; // -> enum ImGuiNextItemDataFlags_ // Flags: for SetNextItemXXX() functions typedef int ImGuiNextWindowDataFlags; // -> enum ImGuiNextWindowDataFlags_// Flags: for SetNextWindowXXX() functions +typedef int ImGuiScrollFlags; // -> enum ImGuiScrollFlags_ // Flags: for ScrollToItem() and navigation requests typedef int ImGuiSeparatorFlags; // -> enum ImGuiSeparatorFlags_ // Flags: for SeparatorEx() typedef int ImGuiTextFlags; // -> enum ImGuiTextFlags_ // Flags: for TextEx() typedef int ImGuiTooltipFlags; // -> enum ImGuiTooltipFlags_ // Flags: for BeginTooltipEx() @@ -228,13 +245,27 @@ namespace ImStb #define IMGUI_CDECL #endif +// Warnings +#if defined(_MSC_VER) && !defined(__clang__) +#define IM_MSVC_WARNING_SUPPRESS(XXXX) __pragma(warning(suppress: XXXX)) +#else +#define IM_MSVC_WARNING_SUPPRESS(XXXX) +#endif + // Debug Tools -// Use 'Metrics->Tools->Item Picker' to break into the call-stack of a specific item. +// Use 'Metrics/Debugger->Tools->Item Picker' to break into the call-stack of a specific item. +// This will call IM_DEBUG_BREAK() which you may redefine yourself. See https://github.com/scottt/debugbreak for more reference. #ifndef IM_DEBUG_BREAK -#if defined(__clang__) -#define IM_DEBUG_BREAK() __builtin_debugtrap() -#elif defined (_MSC_VER) +#if defined (_MSC_VER) #define IM_DEBUG_BREAK() __debugbreak() +#elif defined(__clang__) +#define IM_DEBUG_BREAK() __builtin_debugtrap() +#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +#define IM_DEBUG_BREAK() __asm__ volatile("int $0x03") +#elif defined(__GNUC__) && defined(__thumb__) +#define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xde01") +#elif defined(__GNUC__) && defined(__arm__) && !defined(__thumb__) +#define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xe7f001f0"); #else #define IM_DEBUG_BREAK() IM_ASSERT(0) // It is expected that you define IM_DEBUG_BREAK() into something that will break nicely in a debugger! #endif @@ -271,7 +302,9 @@ static inline ImGuiID ImHash(const void* data, int size, ImU32 seed = 0) { ret #endif // Helpers: Sorting -#define ImQsort qsort +#ifndef ImQsort +static inline void ImQsort(void* base, size_t count, size_t size_of_element, int(IMGUI_CDECL *compare_func)(void const*, void const*)) { if (count > 1) qsort(base, count, size_of_element, compare_func); } +#endif // Helpers: Color Blending IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b); @@ -304,17 +337,19 @@ static inline bool ImCharIsBlankA(char c) { return c == ' ' || c = static inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; } // Helpers: UTF-8 <> wchar conversions -IMGUI_API int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end); // return output UTF-8 bytes count -IMGUI_API int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end); // read one character. return input UTF-8 bytes count -IMGUI_API int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_remaining = NULL); // return input UTF-8 bytes count -IMGUI_API int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end); // return number of UTF-8 code-points (NOT bytes count) -IMGUI_API int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end); // return number of bytes to express one char in UTF-8 -IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string in UTF-8 +IMGUI_API const char* ImTextCharToUtf8(char out_buf[5], unsigned int c); // return out_buf +IMGUI_API int ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end); // return output UTF-8 bytes count +IMGUI_API int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end); // read one character. return input UTF-8 bytes count +IMGUI_API int ImTextStrFromUtf8(ImWchar* out_buf, int out_buf_size, const char* in_text, const char* in_text_end, const char** in_remaining = NULL); // return input UTF-8 bytes count +IMGUI_API int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end); // return number of UTF-8 code-points (NOT bytes count) +IMGUI_API int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end); // return number of bytes to express one char in UTF-8 +IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string in UTF-8 // Helpers: ImVec2/ImVec4 operators // We are keeping those disabled by default so they don't leak in user space, to allow user enabling implicit cast operators between ImVec2 and their own types (using IM_VEC2_CLASS_EXTRA etc.) // We unfortunately don't have a unary- operator for ImVec2 because this would needs to be defined inside the class itself. #ifdef IMGUI_DEFINE_MATH_OPERATORS +IM_MSVC_RUNTIME_CHECKS_OFF static inline ImVec2 operator*(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x * rhs, lhs.y * rhs); } static inline ImVec2 operator/(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x / rhs, lhs.y / rhs); } static inline ImVec2 operator+(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x + rhs.x, lhs.y + rhs.y); } @@ -330,6 +365,7 @@ static inline ImVec2& operator/=(ImVec2& lhs, const ImVec2& rhs) static inline ImVec4 operator+(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w); } static inline ImVec4 operator-(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z, lhs.w - rhs.w); } static inline ImVec4 operator*(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z, lhs.w * rhs.w); } +IM_MSVC_RUNTIME_CHECKS_RESTORE #endif // Helpers: File System @@ -355,6 +391,7 @@ IMGUI_API ImU64 ImFileWrite(const void* data, ImU64 size, ImU64 coun IMGUI_API void* ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size = NULL, int padding_bytes = 0); // Helpers: Maths +IM_MSVC_RUNTIME_CHECKS_OFF // - Wrapper for standard libs functions. (Note that imgui_demo.cpp does _not_ use them to keep the code easy to copy) #ifndef IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS #define ImFabs(X) fabsf(X) @@ -374,8 +411,14 @@ static inline double ImLog(double x) { return log(x); } static inline int ImAbs(int x) { return x < 0 ? -x : x; } static inline float ImAbs(float x) { return fabsf(x); } static inline double ImAbs(double x) { return fabs(x); } -static inline float ImSign(float x) { return (x < 0.0f) ? -1.0f : ((x > 0.0f) ? 1.0f : 0.0f); } // Sign operator - returns -1, 0 or 1 based on sign of argument -static inline double ImSign(double x) { return (x < 0.0) ? -1.0 : ((x > 0.0) ? 1.0 : 0.0); } +static inline float ImSign(float x) { return (x < 0.0f) ? -1.0f : (x > 0.0f) ? 1.0f : 0.0f; } // Sign operator - returns -1, 0 or 1 based on sign of argument +static inline double ImSign(double x) { return (x < 0.0) ? -1.0 : (x > 0.0) ? 1.0 : 0.0; } +#ifdef IMGUI_ENABLE_SSE +static inline float ImRsqrt(float x) { return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(x))); } +#else +static inline float ImRsqrt(float x) { return 1.0f / sqrtf(x); } +#endif +static inline double ImRsqrt(double x) { return 1.0 / sqrt(x); } #endif // - ImMin/ImMax/ImClamp/ImLerp/ImSwap are used by widgets which support variety of types: signed/unsigned int/long long float/double // (Exceptionally using templates here but we could also redefine them for those types) @@ -396,7 +439,7 @@ static inline ImVec4 ImLerp(const ImVec4& a, const ImVec4& b, float t) static inline float ImSaturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; } static inline float ImLengthSqr(const ImVec2& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y); } static inline float ImLengthSqr(const ImVec4& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y) + (lhs.z * lhs.z) + (lhs.w * lhs.w); } -static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return 1.0f / ImSqrt(d); return fail_value; } +static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return ImRsqrt(d); return fail_value; } static inline float ImFloor(float f) { return (float)(int)(f); } static inline float ImFloorSigned(float f) { return (float)((f >= 0 || (int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf() static inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2((float)(int)(v.x), (float)(int)(v.y)); } @@ -405,6 +448,8 @@ static inline float ImDot(const ImVec2& a, const ImVec2& b) static inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); } static inline float ImLinearSweep(float current, float target, float speed) { if (current < target) return ImMin(current + speed, target); if (current > target) return ImMax(current - speed, target); return current; } static inline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); } +static inline bool ImIsFloatAboveGuaranteedIntegerPrecision(float f) { return f <= -16777216 || f >= 16777216; } +IM_MSVC_RUNTIME_CHECKS_RESTORE // Helpers: Geometry IMGUI_API ImVec2 ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t); @@ -420,6 +465,7 @@ IMGUI_API ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy); // Helper: ImVec1 (1D vector) // (this odd construct is used to facilitate the transition between 1D and 2D, and the maintenance of some branches/patches) +IM_MSVC_RUNTIME_CHECKS_OFF struct ImVec1 { float x; @@ -473,6 +519,7 @@ struct IMGUI_API ImRect bool IsInverted() const { return Min.x > Max.x || Min.y > Max.y; } ImVec4 ToVec4() const { return ImVec4(Min.x, Min.y, Max.x, Max.y); } }; +IM_MSVC_RUNTIME_CHECKS_RESTORE // Helper: ImBitArray inline bool ImBitArrayTestBit(const ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); return (arr[n >> 5] & mask) != 0; } @@ -492,12 +539,12 @@ inline void ImBitArraySetBitRange(ImU32* arr, int n, int n2) // Works on ran } // Helper: ImBitArray class (wrapper over ImBitArray functions) -// Store 1-bit per value. NOT CLEARED by constructor. +// Store 1-bit per value. template struct IMGUI_API ImBitArray { ImU32 Storage[(BITCOUNT + 31) >> 5]; - ImBitArray() { } + ImBitArray() { ClearAllBits(); } void ClearAllBits() { memset(Storage, 0, sizeof(Storage)); } void SetAllBits() { memset(Storage, 255, sizeof(Storage)); } bool TestBit(int n) const { IM_ASSERT(n < BITCOUNT); return ImBitArrayTestBit(Storage, n); } @@ -579,20 +626,30 @@ struct IMGUI_API ImPool ImVector Buf; // Contiguous data ImGuiStorage Map; // ID->Index ImPoolIdx FreeIdx; // Next free idx to use + ImPoolIdx AliveCount; // Number of active/alive items (for display purpose) - ImPool() { FreeIdx = 0; } + ImPool() { FreeIdx = AliveCount = 0; } ~ImPool() { Clear(); } T* GetByKey(ImGuiID key) { int idx = Map.GetInt(key, -1); return (idx != -1) ? &Buf[idx] : NULL; } T* GetByIndex(ImPoolIdx n) { return &Buf[n]; } ImPoolIdx GetIndex(const T* p) const { IM_ASSERT(p >= Buf.Data && p < Buf.Data + Buf.Size); return (ImPoolIdx)(p - Buf.Data); } T* GetOrAddByKey(ImGuiID key) { int* p_idx = Map.GetIntRef(key, -1); if (*p_idx != -1) return &Buf[*p_idx]; *p_idx = FreeIdx; return Add(); } bool Contains(const T* p) const { return (p >= Buf.Data && p < Buf.Data + Buf.Size); } - void Clear() { for (int n = 0; n < Map.Data.Size; n++) { int idx = Map.Data[n].val_i; if (idx != -1) Buf[idx].~T(); } Map.Clear(); Buf.clear(); FreeIdx = 0; } - T* Add() { int idx = FreeIdx; if (idx == Buf.Size) { Buf.resize(Buf.Size + 1); FreeIdx++; } else { FreeIdx = *(int*)&Buf[idx]; } IM_PLACEMENT_NEW(&Buf[idx]) T(); return &Buf[idx]; } + void Clear() { for (int n = 0; n < Map.Data.Size; n++) { int idx = Map.Data[n].val_i; if (idx != -1) Buf[idx].~T(); } Map.Clear(); Buf.clear(); FreeIdx = AliveCount = 0; } + T* Add() { int idx = FreeIdx; if (idx == Buf.Size) { Buf.resize(Buf.Size + 1); FreeIdx++; } else { FreeIdx = *(int*)&Buf[idx]; } IM_PLACEMENT_NEW(&Buf[idx]) T(); AliveCount++; return &Buf[idx]; } void Remove(ImGuiID key, const T* p) { Remove(key, GetIndex(p)); } - void Remove(ImGuiID key, ImPoolIdx idx) { Buf[idx].~T(); *(int*)&Buf[idx] = FreeIdx; FreeIdx = idx; Map.SetInt(key, -1); } + void Remove(ImGuiID key, ImPoolIdx idx) { Buf[idx].~T(); *(int*)&Buf[idx] = FreeIdx; FreeIdx = idx; Map.SetInt(key, -1); AliveCount--; } void Reserve(int capacity) { Buf.reserve(capacity); Map.Data.reserve(capacity); } - int GetSize() const { return Buf.Size; } + + // To iterate a ImPool: for (int n = 0; n < pool.GetMapSize(); n++) if (T* t = pool.TryGetMapData(n)) { ... } + // Can be avoided if you know .Remove() has never been called on the pool, or AliveCount == GetMapSize() + int GetAliveCount() const { return AliveCount; } // Number of active/alive items in the pool (for display purpose) + int GetBufSize() const { return Buf.Size; } + int GetMapSize() const { return Map.Data.Size; } // It is the map we need iterate to find valid items, since we don't have "alive" storage anywhere + T* TryGetMapData(ImPoolIdx n) { int idx = Map.Data[n].val_i; if (idx == -1) return NULL; return GetByIndex(idx); } +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + int GetSize() { return GetMapSize(); } // For ImPlot: should use GetMapSize() from (IMGUI_VERSION_NUM >= 18304) +#endif }; // Helper: ImChunkStream<> @@ -690,39 +747,49 @@ struct ImDrawDataBuilder enum ImGuiItemFlags_ { ImGuiItemFlags_None = 0, - ImGuiItemFlags_NoTabStop = 1 << 0, // false - ImGuiItemFlags_ButtonRepeat = 1 << 1, // false // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings. - ImGuiItemFlags_Disabled = 1 << 2, // false // [BETA] Disable interactions but doesn't affect visuals yet. See github.com/ocornut/imgui/issues/211 - ImGuiItemFlags_NoNav = 1 << 3, // false - ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false - ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // MenuItem/Selectable() automatically closes current Popup window - ImGuiItemFlags_MixedValue = 1 << 6, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets) - ImGuiItemFlags_ReadOnly = 1 << 7, // false // [ALPHA] Allow hovering interactions but underlying value is not changed. - ImGuiItemFlags_Default_ = 0 + ImGuiItemFlags_NoTabStop = 1 << 0, // false // Disable keyboard tabbing (FIXME: should merge with _NoNav) + ImGuiItemFlags_ButtonRepeat = 1 << 1, // false // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings. + ImGuiItemFlags_Disabled = 1 << 2, // false // Disable interactions but doesn't affect visuals. See BeginDisabled()/EndDisabled(). See github.com/ocornut/imgui/issues/211 + ImGuiItemFlags_NoNav = 1 << 3, // false // Disable keyboard/gamepad directional navigation (FIXME: should merge with _NoTabStop) + ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false // Disable item being a candidate for default focus (e.g. used by title bar items) + ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // Disable MenuItem/Selectable() automatically closing their popup window + ImGuiItemFlags_MixedValue = 1 << 6, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets) + ImGuiItemFlags_ReadOnly = 1 << 7, // false // [ALPHA] Allow hovering interactions but underlying value is not changed. + ImGuiItemFlags_Inputable = 1 << 8 // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature. }; // Storage for LastItem data enum ImGuiItemStatusFlags_ { ImGuiItemStatusFlags_None = 0, - ImGuiItemStatusFlags_HoveredRect = 1 << 0, - ImGuiItemStatusFlags_HasDisplayRect = 1 << 1, // LastItemDisplayRect is valid + ImGuiItemStatusFlags_HoveredRect = 1 << 0, // Mouse position is within item rectangle (does NOT mean that the window is in correct z-order and can be hovered!, this is only one part of the most-common IsItemHovered test) + ImGuiItemStatusFlags_HasDisplayRect = 1 << 1, // g.LastItemData.DisplayRect is valid ImGuiItemStatusFlags_Edited = 1 << 2, // Value exposed by item was edited in the current frame (should match the bool return value of most widgets) - ImGuiItemStatusFlags_ToggledSelection = 1 << 3, // Set when Selectable(), TreeNode() reports toggling a selection. We can't report "Selected" because reporting the change allows us to handle clipping with less issues. + ImGuiItemStatusFlags_ToggledSelection = 1 << 3, // Set when Selectable(), TreeNode() reports toggling a selection. We can't report "Selected", only state changes, in order to easily handle clipping with less issues. ImGuiItemStatusFlags_ToggledOpen = 1 << 4, // Set when TreeNode() reports toggling their open state. ImGuiItemStatusFlags_HasDeactivated = 1 << 5, // Set if the widget/group is able to provide data for the ImGuiItemStatusFlags_Deactivated flag. ImGuiItemStatusFlags_Deactivated = 1 << 6, // Only valid if ImGuiItemStatusFlags_HasDeactivated is set. - ImGuiItemStatusFlags_HoveredWindow = 1 << 7 // Override the HoveredWindow test to allow cross-window hover testing. + ImGuiItemStatusFlags_HoveredWindow = 1 << 7, // Override the HoveredWindow test to allow cross-window hover testing. + ImGuiItemStatusFlags_FocusedByTabbing = 1 << 8 // Set when the Focusable item just got focused by Tabbing (FIXME: to be removed soon) #ifdef IMGUI_ENABLE_TEST_ENGINE , // [imgui_tests only] - ImGuiItemStatusFlags_Openable = 1 << 10, // - ImGuiItemStatusFlags_Opened = 1 << 11, // - ImGuiItemStatusFlags_Checkable = 1 << 12, // - ImGuiItemStatusFlags_Checked = 1 << 13 // + ImGuiItemStatusFlags_Openable = 1 << 20, // + ImGuiItemStatusFlags_Opened = 1 << 21, // + ImGuiItemStatusFlags_Checkable = 1 << 22, // + ImGuiItemStatusFlags_Checked = 1 << 23 // #endif }; +// Extend ImGuiInputTextFlags_ +enum ImGuiInputTextFlagsPrivate_ +{ + // [Internal] + ImGuiInputTextFlags_Multiline = 1 << 26, // For internal use by InputTextMultiline() + ImGuiInputTextFlags_NoMarkEdited = 1 << 27, // For internal use by functions using InputText() before reformatting data + ImGuiInputTextFlags_MergedItem = 1 << 28 // For internal use by TempInputText(), will skip calling ItemAdd(). Require bounding-box to strictly match. +}; + // Extend ImGuiButtonFlags_ enum ImGuiButtonFlagsPrivate_ { @@ -736,7 +803,7 @@ enum ImGuiButtonFlagsPrivate_ ImGuiButtonFlags_FlattenChildren = 1 << 11, // allow interactions even if a child window is overlapping ImGuiButtonFlags_AllowItemOverlap = 1 << 12, // require previous frame HoveredId to either match id or be null before being usable, use along with SetItemAllowOverlap() ImGuiButtonFlags_DontClosePopups = 1 << 13, // disable automatically closing parent popup on press // [UNUSED] - ImGuiButtonFlags_Disabled = 1 << 14, // disable interactions + //ImGuiButtonFlags_Disabled = 1 << 14, // disable interactions -> use BeginDisabled() or ImGuiItemFlags_Disabled ImGuiButtonFlags_AlignTextBaseLine = 1 << 15, // vertically align button to match text baseline - ButtonEx() only // FIXME: Should be removed and handled by SmallButton(), not possible currently because of DC.CursorPosPrevLine ImGuiButtonFlags_NoKeyModifiers = 1 << 16, // disable mouse interaction if a key modifier is held ImGuiButtonFlags_NoHoldingActiveId = 1 << 17, // don't set ActiveId while holding the mouse (ImGuiButtonFlags_PressedOnClick only) @@ -746,6 +813,12 @@ enum ImGuiButtonFlagsPrivate_ ImGuiButtonFlags_PressedOnDefault_ = ImGuiButtonFlags_PressedOnClickRelease }; +// Extend ImGuiComboFlags_ +enum ImGuiComboFlagsPrivate_ +{ + ImGuiComboFlags_CustomPreview = 1 << 20 // enable BeginComboPreview() +}; + // Extend ImGuiSliderFlags_ enum ImGuiSliderFlagsPrivate_ { @@ -758,12 +831,13 @@ enum ImGuiSelectableFlagsPrivate_ { // NB: need to be in sync with last value of ImGuiSelectableFlags_ ImGuiSelectableFlags_NoHoldingActiveID = 1 << 20, - ImGuiSelectableFlags_SelectOnClick = 1 << 21, // Override button behavior to react on Click (default is Click+Release) - ImGuiSelectableFlags_SelectOnRelease = 1 << 22, // Override button behavior to react on Release (default is Click+Release) - ImGuiSelectableFlags_SpanAvailWidth = 1 << 23, // Span all avail width even if we declared less for layout purpose. FIXME: We may be able to remove this (added in 6251d379, 2bcafc86 for menus) - ImGuiSelectableFlags_DrawHoveredWhenHeld = 1 << 24, // Always show active when held, even is not hovered. This concept could probably be renamed/formalized somehow. - ImGuiSelectableFlags_SetNavIdOnHover = 1 << 25, // Set Nav/Focus ID on mouse hover (used by MenuItem) - ImGuiSelectableFlags_NoPadWithHalfSpacing = 1 << 26 // Disable padding each side with ItemSpacing * 0.5f + ImGuiSelectableFlags_SelectOnNav = 1 << 21, // (WIP) Auto-select when moved into. This is not exposed in public API as to handle multi-select and modifiers we will need user to explicitly control focus scope. May be replaced with a BeginSelection() API. + ImGuiSelectableFlags_SelectOnClick = 1 << 22, // Override button behavior to react on Click (default is Click+Release) + ImGuiSelectableFlags_SelectOnRelease = 1 << 23, // Override button behavior to react on Release (default is Click+Release) + ImGuiSelectableFlags_SpanAvailWidth = 1 << 24, // Span all avail width even if we declared less for layout purpose. FIXME: We may be able to remove this (added in 6251d379, 2bcafc86 for menus) + ImGuiSelectableFlags_DrawHoveredWhenHeld = 1 << 25, // Always show active when held, even is not hovered. This concept could probably be renamed/formalized somehow. + ImGuiSelectableFlags_SetNavIdOnHover = 1 << 26, // Set Nav/Focus ID on mouse hover (used by MenuItem) + ImGuiSelectableFlags_NoPadWithHalfSpacing = 1 << 27 // Disable padding each side with ItemSpacing * 0.5f }; // Extend ImGuiTreeNodeFlags_ @@ -845,49 +919,6 @@ enum ImGuiInputReadMode ImGuiInputReadMode_RepeatFast }; -enum ImGuiNavHighlightFlags_ -{ - ImGuiNavHighlightFlags_None = 0, - ImGuiNavHighlightFlags_TypeDefault = 1 << 0, - ImGuiNavHighlightFlags_TypeThin = 1 << 1, - ImGuiNavHighlightFlags_AlwaysDraw = 1 << 2, // Draw rectangular highlight if (g.NavId == id) _even_ when using the mouse. - ImGuiNavHighlightFlags_NoRounding = 1 << 3 -}; - -enum ImGuiNavDirSourceFlags_ -{ - ImGuiNavDirSourceFlags_None = 0, - ImGuiNavDirSourceFlags_Keyboard = 1 << 0, - ImGuiNavDirSourceFlags_PadDPad = 1 << 1, - ImGuiNavDirSourceFlags_PadLStick = 1 << 2 -}; - -enum ImGuiNavMoveFlags_ -{ - ImGuiNavMoveFlags_None = 0, - ImGuiNavMoveFlags_LoopX = 1 << 0, // On failed request, restart from opposite side - ImGuiNavMoveFlags_LoopY = 1 << 1, - ImGuiNavMoveFlags_WrapX = 1 << 2, // On failed request, request from opposite side one line down (when NavDir==right) or one line up (when NavDir==left) - ImGuiNavMoveFlags_WrapY = 1 << 3, // This is not super useful for provided for completeness - ImGuiNavMoveFlags_AllowCurrentNavId = 1 << 4, // Allow scoring and considering the current NavId as a move target candidate. This is used when the move source is offset (e.g. pressing PageDown actually needs to send a Up move request, if we are pressing PageDown from the bottom-most item we need to stay in place) - ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5, // Store alternate result in NavMoveResultLocalVisibleSet that only comprise elements that are already fully visible. - ImGuiNavMoveFlags_ScrollToEdge = 1 << 6 -}; - -enum ImGuiNavForward -{ - ImGuiNavForward_None, - ImGuiNavForward_ForwardQueued, - ImGuiNavForward_ForwardActive -}; - -enum ImGuiNavLayer -{ - ImGuiNavLayer_Main = 0, // Main scrolling layer - ImGuiNavLayer_Menu = 1, // Menu layer (access with Alt/ImGuiNavInput_Menu) - ImGuiNavLayer_COUNT -}; - enum ImGuiPopupPositionPolicy { ImGuiPopupPositionPolicy_Default, @@ -934,6 +965,19 @@ struct ImGuiStyleMod ImGuiStyleMod(ImGuiStyleVar idx, ImVec2 v) { VarIdx = idx; BackupFloat[0] = v.x; BackupFloat[1] = v.y; } }; +// Storage data for BeginComboPreview()/EndComboPreview() +struct IMGUI_API ImGuiComboPreviewData +{ + ImRect PreviewRect; + ImVec2 BackupCursorPos; + ImVec2 BackupCursorMaxPos; + ImVec2 BackupCursorPosPrevLine; + float BackupPrevLineTextBaseOffset; + ImGuiLayoutType BackupLayout; + + ImGuiComboPreviewData() { memset(this, 0, sizeof(*this)); } +}; + // Stacked storage data for BeginGroup()/EndGroup() struct IMGUI_API ImGuiGroupData { @@ -953,14 +997,19 @@ struct IMGUI_API ImGuiGroupData // Simple column measurement, currently used for MenuItem() only.. This is very short-sighted/throw-away code and NOT a generic helper. struct IMGUI_API ImGuiMenuColumns { - float Spacing; - float Width, NextWidth; - float Pos[3], NextWidths[3]; + ImU32 TotalWidth; + ImU32 NextTotalWidth; + ImU16 Spacing; + ImU16 OffsetIcon; // Always zero for now + ImU16 OffsetLabel; // Offsets are locked in Update() + ImU16 OffsetShortcut; + ImU16 OffsetMark; + ImU16 Widths[4]; // Width of: Icon, Label, Shortcut, Mark (accumulators for current frame) 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; + void Update(float spacing, bool window_reappearing); + float DeclColumns(float w_icon, float w_label, float w_shortcut, float w_mark); + void CalcNextTotalWidth(bool update_offsets); }; // Internal state of the currently focused/edited text input box @@ -980,9 +1029,7 @@ struct IMGUI_API ImGuiInputTextState bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!) bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection bool Edited; // edited this frame - ImGuiInputTextFlags UserFlags; // Temporarily set while we call user's callback - ImGuiInputTextCallback UserCallback; // " - void* UserCallbackData; // " + ImGuiInputTextFlags Flags; // copy of InputText() flags ImGuiInputTextState() { memset(this, 0, sizeof(*this)); } void ClearText() { CurLenW = CurLenA = 0; TextW[0] = 0; TextA[0] = 0; CursorClamp(); } @@ -996,6 +1043,9 @@ struct IMGUI_API ImGuiInputTextState void CursorClamp() { Stb.cursor = ImMin(Stb.cursor, CurLenW); Stb.select_start = ImMin(Stb.select_start, CurLenW); Stb.select_end = ImMin(Stb.select_end, CurLenW); } bool HasSelection() const { return Stb.select_start != Stb.select_end; } void ClearSelection() { Stb.select_start = Stb.select_end = Stb.cursor; } + int GetCursorPos() const { return Stb.cursor; } + int GetSelectionStart() const { return Stb.select_start; } + int GetSelectionEnd() const { return Stb.select_end; } void SelectAll() { Stb.select_start = 0; Stb.cursor = Stb.select_end = CurLenW; Stb.has_preferred_x = 0; } }; @@ -1013,20 +1063,6 @@ struct ImGuiPopupData ImGuiPopupData() { memset(this, 0, sizeof(*this)); OpenFrameCount = -1; } }; -struct ImGuiNavMoveResult -{ - ImGuiWindow* Window; // Best candidate window - ImGuiID ID; // Best candidate ID - ImGuiID FocusScopeId; // Best candidate focus scope ID - float DistBox; // Best candidate box distance to current NavId - float DistCenter; // Best candidate center distance to current NavId - float DistAxial; - ImRect RectRel; // Best candidate bounding box in window relative space - - ImGuiNavMoveResult() { Clear(); } - void Clear() { Window = NULL; ID = FocusScopeId = 0; DistBox = DistCenter = DistAxial = FLT_MAX; RectRel = ImRect(); } -}; - enum ImGuiNextWindowDataFlags_ { ImGuiNextWindowDataFlags_None = 0, @@ -1057,7 +1093,7 @@ struct ImGuiNextWindowData ImGuiSizeCallback SizeCallback; void* SizeCallbackUserData; float BgAlphaVal; // Override background alpha - ImVec2 MenuBarOffsetMinVal; // *Always on* This is not exposed publicly, so we don't clear it. + ImVec2 MenuBarOffsetMinVal; // (Always on) This is not exposed publicly, so we don't clear it and it doesn't have a corresponding flag (could we? for consistency?) ImGuiNextWindowData() { memset(this, 0, sizeof(*this)); } inline void ClearFlags() { Flags = ImGuiNextWindowDataFlags_None; } @@ -1082,6 +1118,44 @@ struct ImGuiNextItemData inline void ClearFlags() { Flags = ImGuiNextItemDataFlags_None; } // Also cleared manually by ItemAdd()! }; +// Status storage for the last submitted item +struct ImGuiLastItemData +{ + ImGuiID ID; + ImGuiItemFlags InFlags; // See ImGuiItemFlags_ + ImGuiItemStatusFlags StatusFlags; // See ImGuiItemStatusFlags_ + ImRect Rect; // Full rectangle + ImRect NavRect; // Navigation scoring rectangle (not displayed) + ImRect DisplayRect; // Display rectangle (only if ImGuiItemStatusFlags_HasDisplayRect is set) + + ImGuiLastItemData() { memset(this, 0, sizeof(*this)); } +}; + +struct IMGUI_API ImGuiStackSizes +{ + short SizeOfIDStack; + short SizeOfColorStack; + short SizeOfStyleVarStack; + short SizeOfFontStack; + short SizeOfFocusScopeStack; + short SizeOfGroupStack; + short SizeOfItemFlagsStack; + short SizeOfBeginPopupStack; + short SizeOfDisabledStack; + + ImGuiStackSizes() { memset(this, 0, sizeof(*this)); } + void SetToCurrentState(); + void CompareWithCurrentState(); +}; + +// Data saved for each window pushed into the stack +struct ImGuiWindowStackData +{ + ImGuiWindow* Window; + ImGuiLastItemData ParentLastItemDataBackup; + ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting +}; + struct ImGuiShrinkWidthItem { int Index; @@ -1097,6 +1171,120 @@ struct ImGuiPtrOrIndex ImGuiPtrOrIndex(int index) { Ptr = NULL; Index = index; } }; +//----------------------------------------------------------------------------- +// [SECTION] Clipper support +//----------------------------------------------------------------------------- + +struct ImGuiListClipperRange +{ + int Min; + int Max; + bool PosToIndexConvert; // Begin/End are absolute position (will be converted to indices later) + ImS8 PosToIndexOffsetMin; // Add to Min after converting to indices + ImS8 PosToIndexOffsetMax; // Add to Min after converting to indices + + static ImGuiListClipperRange FromIndices(int min, int max) { ImGuiListClipperRange r = { min, max, false, 0, 0 }; return r; } + static ImGuiListClipperRange FromPositions(float y1, float y2, int off_min, int off_max) { ImGuiListClipperRange r = { (int)y1, (int)y2, true, (ImS8)off_min, (ImS8)off_max }; return r; } +}; + +// Temporary clipper data, buffers shared/reused between instances +struct ImGuiListClipperData +{ + ImGuiListClipper* ListClipper; + float LossynessOffset; + int StepNo; + int ItemsFrozen; + ImVector Ranges; + + ImGuiListClipperData() { memset(this, 0, sizeof(*this)); } + void Reset(ImGuiListClipper* clipper) { ListClipper = clipper; StepNo = ItemsFrozen = 0; Ranges.resize(0); } +}; + +//----------------------------------------------------------------------------- +// [SECTION] Navigation support +//----------------------------------------------------------------------------- + +enum ImGuiActivateFlags_ +{ + ImGuiActivateFlags_None = 0, + ImGuiActivateFlags_PreferInput = 1 << 0, // Favor activation that requires keyboard text input (e.g. for Slider/Drag). Default if keyboard is available. + ImGuiActivateFlags_PreferTweak = 1 << 1, // Favor activation for tweaking with arrows or gamepad (e.g. for Slider/Drag). Default if keyboard is not available. + ImGuiActivateFlags_TryToPreserveState = 1 << 2 // Request widget to preserve state if it can (e.g. InputText will try to preserve cursor/selection) +}; + +// Early work-in-progress API for ScrollToItem() +enum ImGuiScrollFlags_ +{ + ImGuiScrollFlags_None = 0, + ImGuiScrollFlags_KeepVisibleEdgeX = 1 << 0, // If item is not visible: scroll as little as possible on X axis to bring item back into view [default for X axis] + ImGuiScrollFlags_KeepVisibleEdgeY = 1 << 1, // If item is not visible: scroll as little as possible on Y axis to bring item back into view [default for Y axis for windows that are already visible] + ImGuiScrollFlags_KeepVisibleCenterX = 1 << 2, // If item is not visible: scroll to make the item centered on X axis [rarely used] + ImGuiScrollFlags_KeepVisibleCenterY = 1 << 3, // If item is not visible: scroll to make the item centered on Y axis + ImGuiScrollFlags_AlwaysCenterX = 1 << 4, // Always center the result item on X axis [rarely used] + ImGuiScrollFlags_AlwaysCenterY = 1 << 5, // Always center the result item on Y axis [default for Y axis for appearing window) + ImGuiScrollFlags_NoScrollParent = 1 << 6, // Disable forwarding scrolling to parent window if required to keep item/rect visible (only scroll window the function was applied to). + ImGuiScrollFlags_MaskX_ = ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleCenterX | ImGuiScrollFlags_AlwaysCenterX, + ImGuiScrollFlags_MaskY_ = ImGuiScrollFlags_KeepVisibleEdgeY | ImGuiScrollFlags_KeepVisibleCenterY | ImGuiScrollFlags_AlwaysCenterY +}; + +enum ImGuiNavHighlightFlags_ +{ + ImGuiNavHighlightFlags_None = 0, + ImGuiNavHighlightFlags_TypeDefault = 1 << 0, + ImGuiNavHighlightFlags_TypeThin = 1 << 1, + ImGuiNavHighlightFlags_AlwaysDraw = 1 << 2, // Draw rectangular highlight if (g.NavId == id) _even_ when using the mouse. + ImGuiNavHighlightFlags_NoRounding = 1 << 3 +}; + +enum ImGuiNavDirSourceFlags_ +{ + ImGuiNavDirSourceFlags_None = 0, + ImGuiNavDirSourceFlags_RawKeyboard = 1 << 0, // Raw keyboard (not pulled from nav), faciliate use of some functions before we can unify nav and keys + ImGuiNavDirSourceFlags_Keyboard = 1 << 1, + ImGuiNavDirSourceFlags_PadDPad = 1 << 2, + ImGuiNavDirSourceFlags_PadLStick = 1 << 3 +}; + +enum ImGuiNavMoveFlags_ +{ + ImGuiNavMoveFlags_None = 0, + ImGuiNavMoveFlags_LoopX = 1 << 0, // On failed request, restart from opposite side + ImGuiNavMoveFlags_LoopY = 1 << 1, + ImGuiNavMoveFlags_WrapX = 1 << 2, // On failed request, request from opposite side one line down (when NavDir==right) or one line up (when NavDir==left) + ImGuiNavMoveFlags_WrapY = 1 << 3, // This is not super useful but provided for completeness + ImGuiNavMoveFlags_AllowCurrentNavId = 1 << 4, // Allow scoring and considering the current NavId as a move target candidate. This is used when the move source is offset (e.g. pressing PageDown actually needs to send a Up move request, if we are pressing PageDown from the bottom-most item we need to stay in place) + ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5, // Store alternate result in NavMoveResultLocalVisible that only comprise elements that are already fully visible (used by PageUp/PageDown) + ImGuiNavMoveFlags_ScrollToEdgeY = 1 << 6, // Force scrolling to min/max (used by Home/End) // FIXME-NAV: Aim to remove or reword, probably unnecessary + ImGuiNavMoveFlags_Forwarded = 1 << 7, + ImGuiNavMoveFlags_DebugNoResult = 1 << 8, // Dummy scoring for debug purpose, don't apply result + ImGuiNavMoveFlags_FocusApi = 1 << 9, + ImGuiNavMoveFlags_Tabbing = 1 << 10, // == Focus + Activate if item is Inputable + DontChangeNavHighlight + ImGuiNavMoveFlags_Activate = 1 << 11, + ImGuiNavMoveFlags_DontSetNavHighlight = 1 << 12 // Do not alter the visible state of keyboard vs mouse nav highlight +}; + +enum ImGuiNavLayer +{ + ImGuiNavLayer_Main = 0, // Main scrolling layer + ImGuiNavLayer_Menu = 1, // Menu layer (access with Alt/ImGuiNavInput_Menu) + ImGuiNavLayer_COUNT +}; + +struct ImGuiNavItemData +{ + ImGuiWindow* Window; // Init,Move // Best candidate window (result->ItemWindow->RootWindowForNav == request->Window) + ImGuiID ID; // Init,Move // Best candidate item ID + ImGuiID FocusScopeId; // Init,Move // Best candidate focus scope ID + ImRect RectRel; // Init,Move // Best candidate bounding box in window relative space + ImGuiItemFlags InFlags; // ????,Move // Best candidate item flags + float DistBox; // Move // Best candidate box distance to current NavId + float DistCenter; // Move // Best candidate center distance to current NavId + float DistAxial; // Move // Best candidate axial distance to current NavId + + ImGuiNavItemData() { Clear(); } + void Clear() { Window = NULL; ID = FocusScopeId = 0; InFlags = 0; DistBox = DistCenter = DistAxial = FLT_MAX; } +}; + //----------------------------------------------------------------------------- // [SECTION] Columns support //----------------------------------------------------------------------------- @@ -1236,11 +1424,12 @@ struct ImGuiSettingsHandler }; //----------------------------------------------------------------------------- -// [SECTION] Metrics, Debug +// [SECTION] Metrics, Debug Tools //----------------------------------------------------------------------------- struct ImGuiMetricsConfig { + bool ShowStackTool; bool ShowWindowsRects; bool ShowWindowsBeginOrder; bool ShowTablesRects; @@ -1251,6 +1440,7 @@ struct ImGuiMetricsConfig ImGuiMetricsConfig() { + ShowStackTool = false; ShowWindowsRects = false; ShowWindowsBeginOrder = false; ShowTablesRects = false; @@ -1261,19 +1451,25 @@ struct ImGuiMetricsConfig } }; -struct IMGUI_API ImGuiStackSizes +struct ImGuiStackLevelInfo { - short SizeOfIDStack; - short SizeOfColorStack; - short SizeOfStyleVarStack; - short SizeOfFontStack; - short SizeOfFocusScopeStack; - short SizeOfGroupStack; - short SizeOfBeginPopupStack; + ImGuiID ID; + ImS8 QueryFrameCount; // >= 1: Query in progress + bool QuerySuccess; // Obtained result from DebugHookIdInfo() + char Desc[58]; // Arbitrarily sized buffer to hold a result (FIXME: could replace Results[] with a chunk stream?) - ImGuiStackSizes() { memset(this, 0, sizeof(*this)); } - void SetToCurrentState(); - void CompareWithCurrentState(); + ImGuiStackLevelInfo() { memset(this, 0, sizeof(*this)); } +}; + +// State for Stack tool queries +struct ImGuiStackTool +{ + int LastActiveFrame; + int StackLevel; // -1: query stack and resize Results, >= 0: individual stack level + ImGuiID QueryId; // ID to query details for + ImVector Results; + + ImGuiStackTool() { memset(this, 0, sizeof(*this)); } }; //----------------------------------------------------------------------------- @@ -1317,14 +1513,13 @@ struct ImGuiContext 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 // Windows state ImVector Windows; // Windows, sorted in display order, back to front ImVector WindowsFocusOrder; // Root windows, sorted in focus order, back to front. ImVector WindowsTempSortBuffer; // Temporary buffer used in EndFrame() to reorder windows so parents are kept before their child - ImVector CurrentWindowStack; + ImVector CurrentWindowStack; ImGuiStorage WindowsById; // Map window's ImGuiID to ImGuiWindow* int WindowsActiveCount; // Number of unique windows submitted by frame ImVec2 WindowsHoverPadding; // Padding around resizable windows for which hovering on counts as hovering the window == ImMax(style.TouchExtraPadding, WINDOWS_HOVER_PADDING) @@ -1337,6 +1532,7 @@ struct ImGuiContext float WheelingWindowTimer; // Item/widgets state and tracking information + ImGuiID DebugHookIdInfo; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line] ImGuiID HoveredId; // Hovered widget, filled during the frame ImGuiID HoveredIdPreviousFrame; bool HoveredIdAllowOverlap; @@ -1370,8 +1566,10 @@ struct ImGuiContext float LastActiveIdTimer; // Store the last non-zero ActiveId timer since the beginning of activation, useful for animation. // Next window/item data - ImGuiNextWindowData NextWindowData; // Storage for SetNextWindow** functions + ImGuiItemFlags CurrentItemFlags; // == g.ItemFlagsStack.back() ImGuiNextItemData NextItemData; // Storage for SetNextItem** functions + ImGuiLastItemData LastItemData; // Storage for last submitted item (setup by ItemAdd) + ImGuiNextWindowData NextWindowData; // Storage for SetNextWindow** functions // Shared stacks ImVector ColorStack; // Stack for PushStyleColor()/PopStyleColor() - inherited by Begin() @@ -1382,6 +1580,7 @@ struct ImGuiContext ImVectorGroupStack; // Stack for BeginGroup()/EndGroup() - not inherited by Begin() ImVectorOpenPopupStack; // Which popups are open (persistent) ImVectorBeginPopupStack; // Which level of BeginPopup() we are in (reset every frame) + int BeginMenuCount; // Viewports ImVector Viewports; // Active viewports (Size==1 in 'master' branch). Each viewports hold their copy of ImDrawData. @@ -1393,37 +1592,44 @@ struct ImGuiContext ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && IsNavInputPressed(ImGuiNavInput_Activate) ? NavId : 0, also set when calling ActivateItem() ImGuiID NavActivateDownId; // ~~ IsNavInputDown(ImGuiNavInput_Activate) ? NavId : 0 ImGuiID NavActivatePressedId; // ~~ IsNavInputPressed(ImGuiNavInput_Activate) ? NavId : 0 - ImGuiID NavInputId; // ~~ IsNavInputPressed(ImGuiNavInput_Input) ? NavId : 0 - ImGuiID NavJustTabbedId; // Just tabbed to this id. + ImGuiID NavActivateInputId; // ~~ IsNavInputPressed(ImGuiNavInput_Input) ? NavId : 0; ImGuiActivateFlags_PreferInput will be set and NavActivateId will be 0. + ImGuiActivateFlags NavActivateFlags; ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest). ImGuiID NavJustMovedToFocusScopeId; // Just navigated to this focus scope id (result of a successfully MoveRequest). ImGuiKeyModFlags NavJustMovedToKeyMods; ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame. + ImGuiActivateFlags NavNextActivateFlags; ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS WILL ONLY BE None or NavGamepad or NavKeyboard. - ImRect NavScoringRect; // Rectangle used for scoring, in screen space. Based of window->NavRectRel[], modified for directional navigation scoring. - int NavScoringCount; // Metrics for debugging ImGuiNavLayer NavLayer; // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later. - int NavIdTabCounter; // == NavWindow->DC.FocusIdxTabCounter at time of NavId processing bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRectRel is valid bool NavMousePosDirty; // When set we will update mouse position if (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) if set (NB: this not enabled by default) bool NavDisableHighlight; // When user starts using mouse, we hide gamepad/keyboard highlight (NB: but they are still available, which is why NavDisableHighlight isn't always != NavDisableMouseHover) bool NavDisableMouseHover; // When user starts using gamepad/keyboard, we hide mouse hovering highlight until mouse is touched again. - bool NavAnyRequest; // ~~ NavMoveRequest || NavInitRequest + + // Navigation: Init & Move Requests + bool NavAnyRequest; // ~~ NavMoveRequest || NavInitRequest this is to perform early out in ItemAdd() bool NavInitRequest; // Init request for appearing window to select first item bool NavInitRequestFromMove; ImGuiID NavInitResultId; // Init request result (first item of the window, or one for which SetItemDefaultFocus() was called) ImRect NavInitResultRectRel; // Init request result rectangle (relative to parent window) - bool NavMoveRequest; // Move request for this frame - ImGuiNavMoveFlags NavMoveRequestFlags; - ImGuiNavForward NavMoveRequestForward; // None / ForwardQueued / ForwardActive (this is used to navigate sibling parent menus from a child menu) - ImGuiKeyModFlags NavMoveRequestKeyMods; - ImGuiDir NavMoveDir, NavMoveDirLast; // Direction of the move request (left/right/up/down), direction of the previous move request + bool NavMoveSubmitted; // Move request submitted, will process result on next NewFrame() + bool NavMoveScoringItems; // Move request submitted, still scoring incoming items + bool NavMoveForwardToNextFrame; + ImGuiNavMoveFlags NavMoveFlags; + ImGuiScrollFlags NavMoveScrollFlags; + ImGuiKeyModFlags NavMoveKeyMods; + ImGuiDir NavMoveDir; // Direction of the move request (left/right/up/down) + ImGuiDir NavMoveDirForDebug; ImGuiDir NavMoveClipDir; // FIXME-NAV: Describe the purpose of this better. Might want to rename? - ImGuiNavMoveResult NavMoveResultLocal; // Best move request candidate within NavWindow - ImGuiNavMoveResult NavMoveResultLocalVisibleSet; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag) - ImGuiNavMoveResult NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag) - ImGuiWindow* NavWrapRequestWindow; // Window which requested trying nav wrap-around. - ImGuiNavMoveFlags NavWrapRequestFlags; // Wrap-around operation flags. + ImRect NavScoringRect; // Rectangle used for scoring, in screen space. Based of window->NavRectRel[], modified for directional navigation scoring. + ImRect NavScoringNoClipRect; // Some nav operations (such as PageUp/PageDown) enforce a region which clipper will attempt to always keep submitted + int NavScoringDebugCount; // Metrics for debugging + int NavTabbingDir; // Generally -1 or +1, 0 when tabbing without a nav id + int NavTabbingCounter; // >0 when counting items for tabbing + ImGuiNavItemData NavMoveResultLocal; // Best move request candidate within NavWindow + ImGuiNavItemData NavMoveResultLocalVisible; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag) + ImGuiNavItemData NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag) + ImGuiNavItemData NavTabbingResultFirst; // First tabbing request candidate within NavWindow and flattened hierarchy // Navigation: Windowing (CTRL+TAB for list, or Menu button + keys or directional pads to move/resize) ImGuiWindow* NavWindowingTarget; // Target window when doing CTRL+Tab (or Pad Menu + FocusPrev/Next), this window is temporarily displayed top-most! @@ -1433,15 +1639,6 @@ struct ImGuiContext float NavWindowingHighlightAlpha; bool NavWindowingToggleLayer; - // Legacy Focus/Tabbing system (older than Nav, active even if Nav is disabled, misnamed. FIXME-NAV: This needs a redesign!) - ImGuiWindow* TabFocusRequestCurrWindow; // - ImGuiWindow* TabFocusRequestNextWindow; // - int TabFocusRequestCurrCounterRegular; // Any item being requested for focus, stored as an index (we on layout to be stable between the frame pressing TAB and the next frame, semi-ouch) - int TabFocusRequestCurrCounterTabStop; // Tab item being requested for focus, stored as an index - int TabFocusRequestNextCounterRegular; // Stored for next frame - int TabFocusRequestNextCounterTabStop; // " - bool TabFocusPressed; // - // Render float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list) ImGuiMouseCursor MouseCursor; @@ -1465,10 +1662,15 @@ struct ImGuiContext ImVector DragDropPayloadBufHeap; // We don't expose the ImVector<> directly, ImGuiPayload only holds pointer+size unsigned char DragDropPayloadBufLocal[16]; // Local buffer for small payloads + // Clipper + int ClipperTempDataStacked; + ImVector ClipperTempData; + // Table ImGuiTable* CurrentTable; - ImPool Tables; - ImVector CurrentTableStack; + int TablesTempDataStacked; // Temporary table data size (because we leave previous instances undestructed, we generally don't use TablesTempData.Size) + ImVector TablesTempData; // Temporary table data (buffers reused/shared across instances, support nesting) + ImPool Tables; // Persistent table data ImVector TablesLastTimeActive; // Last used timestamp of each tables (SOA, for efficient GC) ImVector DrawChannelsTempMergeBuffer; @@ -1479,22 +1681,25 @@ struct ImGuiContext ImVector ShrinkWidthBuffer; // Widget state - ImVec2 LastValidMousePos; + ImVec2 MouseLastValidPos; ImGuiInputTextState InputTextState; ImFont InputTextPasswordFont; ImGuiID TempInputId; // Temporary text input when CTRL+clicking on a slider, etc. ImGuiColorEditFlags ColorEditOptions; // Store user options for color edit widgets - float ColorEditLastHue; // Backup of last Hue associated to LastColor[3], so we can restore Hue in lossy RGB<>HSV round trips - float ColorEditLastSat; // Backup of last Saturation associated to LastColor[3], so we can restore Saturation in lossy RGB<>HSV round trips - float ColorEditLastColor[3]; + float ColorEditLastHue; // Backup of last Hue associated to LastColor, so we can restore Hue in lossy RGB<>HSV round trips + float ColorEditLastSat; // Backup of last Saturation associated to LastColor, so we can restore Saturation in lossy RGB<>HSV round trips + ImU32 ColorEditLastColor; // RGB value with alpha set to 0. ImVec4 ColorPickerRef; // Initial/reference color at the time of opening the color picker. + ImGuiComboPreviewData ComboPreviewData; float SliderCurrentAccum; // Accumulated slider delta when using navigation controls. bool SliderCurrentAccumDirty; // Has the accumulated slider delta changed since last time we tried to apply it? bool DragCurrentAccumDirty; float DragCurrentAccum; // Accumulator for dragging modification. Always high-precision, not rounded by end-user precision settings float DragSpeedDefaultRatio; // If speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio float ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage? - int TooltipOverrideCount; + float DisabledAlphaBackup; // Backup for style.Alpha for BeginDisabled() + short DisabledStackSize; + short TooltipOverrideCount; float TooltipSlowDelay; // Time before slow tooltips appears (FIXME: This is temporary until we merge in tooltip timer+priority work) ImVector ClipboardHandlerData; // If no custom clipboard handler is defined ImVector MenusIdSubmittedThisFrame; // A list of menu IDs that were rendered at least once @@ -1529,12 +1734,14 @@ struct ImGuiContext // Debug Tools bool DebugItemPickerActive; // Item picker is active (started with DebugStartItemPicker()) - ImGuiID DebugItemPickerBreakId; // Will call IM_DEBUG_BREAK() when encountering this id + ImGuiID DebugItemPickerBreakId; // Will call IM_DEBUG_BREAK() when encountering this ID ImGuiMetricsConfig DebugMetricsConfig; + ImGuiStackTool DebugStackTool; // Misc float FramerateSecPerFrame[120]; // Calculate estimate of framerate for user over the last 2 seconds. int FramerateSecPerFrameIdx; + int FramerateSecPerFrameCount; float FramerateSecPerFrameAccum; int WantCaptureMouseNextFrame; // Explicit capture via CaptureKeyboardFromApp()/CaptureMouseFromApp() sets those flags int WantCaptureKeyboardNextFrame; @@ -1554,7 +1761,6 @@ struct ImGuiContext WithinFrameScope = WithinFrameScopeWithImplicitWindow = WithinEndChild = false; GcCompactAll = false; TestEngineHookItems = false; - TestEngineHookIdInfo = 0; TestEngine = NULL; WindowsActiveCount = 0; @@ -1565,6 +1771,7 @@ struct ImGuiContext WheelingWindow = NULL; WheelingWindowTimer = 0.0f; + DebugHookIdInfo = 0; HoveredId = HoveredIdPreviousFrame = 0; HoveredIdAllowOverlap = false; HoveredIdUsingMouseWheel = HoveredIdPreviousFrameUsingMouseWheel = false; @@ -1594,15 +1801,16 @@ struct ImGuiContext LastActiveId = 0; LastActiveIdTimer = 0.0f; + CurrentItemFlags = ImGuiItemFlags_None; + BeginMenuCount = 0; + NavWindow = NULL; - NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavInputId = 0; - NavJustTabbedId = NavJustMovedToId = NavJustMovedToFocusScopeId = NavNextActivateId = 0; + NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavActivateInputId = 0; + NavJustMovedToId = NavJustMovedToFocusScopeId = NavNextActivateId = 0; + NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None; NavJustMovedToKeyMods = ImGuiKeyModFlags_None; NavInputSource = ImGuiInputSource_None; - NavScoringRect = ImRect(); - NavScoringCount = 0; NavLayer = ImGuiNavLayer_Main; - NavIdTabCounter = INT_MAX; NavIdIsAlive = false; NavMousePosDirty = false; NavDisableHighlight = true; @@ -1611,23 +1819,21 @@ struct ImGuiContext NavInitRequest = false; NavInitRequestFromMove = false; NavInitResultId = 0; - NavMoveRequest = false; - NavMoveRequestFlags = ImGuiNavMoveFlags_None; - NavMoveRequestForward = ImGuiNavForward_None; - NavMoveRequestKeyMods = ImGuiKeyModFlags_None; - NavMoveDir = NavMoveDirLast = NavMoveClipDir = ImGuiDir_None; - NavWrapRequestWindow = NULL; - NavWrapRequestFlags = ImGuiNavMoveFlags_None; + NavMoveSubmitted = false; + NavMoveScoringItems = false; + NavMoveForwardToNextFrame = false; + NavMoveFlags = ImGuiNavMoveFlags_None; + NavMoveScrollFlags = ImGuiScrollFlags_None; + NavMoveKeyMods = ImGuiKeyModFlags_None; + NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None; + NavScoringDebugCount = 0; + NavTabbingDir = 0; + NavTabbingCounter = 0; NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL; NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f; NavWindowingToggleLayer = false; - TabFocusRequestCurrWindow = TabFocusRequestNextWindow = NULL; - TabFocusRequestCurrCounterRegular = TabFocusRequestCurrCounterTabStop = INT_MAX; - TabFocusRequestNextCounterRegular = TabFocusRequestNextCounterTabStop = INT_MAX; - TabFocusPressed = false; - DimBgRatio = 0.0f; MouseCursor = ImGuiMouseCursor_Arrow; @@ -1643,19 +1849,23 @@ struct ImGuiContext DragDropHoldJustPressedId = 0; memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal)); + ClipperTempDataStacked = 0; + CurrentTable = NULL; + TablesTempDataStacked = 0; CurrentTabBar = NULL; - LastValidMousePos = ImVec2(0.0f, 0.0f); TempInputId = 0; - ColorEditOptions = ImGuiColorEditFlags__OptionsDefault; + ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_; ColorEditLastHue = ColorEditLastSat = 0.0f; - ColorEditLastColor[0] = ColorEditLastColor[1] = ColorEditLastColor[2] = FLT_MAX; + ColorEditLastColor = 0; SliderCurrentAccum = 0.0f; SliderCurrentAccumDirty = false; DragCurrentAccumDirty = false; DragCurrentAccum = 0.0f; DragSpeedDefaultRatio = 1.0f / 100.0f; + DisabledAlphaBackup = 0.0f; + DisabledStackSize = 0; ScrollbarClickDeltaToGrabCenter = 0.0f; TooltipOverrideCount = 0; TooltipSlowDelay = 0.50f; @@ -1680,7 +1890,7 @@ struct ImGuiContext DebugItemPickerBreakId = 0; memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame)); - FramerateSecPerFrameIdx = 0; + FramerateSecPerFrameIdx = FramerateSecPerFrameCount = 0; FramerateSecPerFrameAccum = 0.0f; WantCaptureMouseNextFrame = WantCaptureKeyboardNextFrame = WantTextInputNextFrame = -1; memset(TempBuffer, 0, sizeof(TempBuffer)); @@ -1709,17 +1919,12 @@ struct IMGUI_API ImGuiWindowTempData ImVec1 Indent; // Indentation / start position from left of window (increased by TreePush/TreePop, etc.) ImVec1 ColumnsOffset; // Offset to the current column (if ColumnsCurrent > 0). FIXME: This and the above should be a stack to allow use cases like Tree->Column->Tree. Need revamp columns API. ImVec1 GroupOffset; - - // Last item status - ImGuiID LastItemId; // ID for last item - ImGuiItemStatusFlags LastItemStatusFlags; // Status flags for last item (see ImGuiItemStatusFlags_) - ImRect LastItemRect; // Interaction rect for last item - ImRect LastItemDisplayRect; // End-user display rect for last item (only valid if LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) + ImVec2 CursorStartPosLossyness;// Record the loss of precision of CursorStartPos due to really large scrolling amount. This is used by clipper to compensentate and fix the most common use case of large scroll area. // Keyboard/Gamepad navigation ImGuiNavLayer NavLayerCurrent; // Current layer, 0..31 (we currently only use 0..1) - int NavLayerActiveMask; // Which layers have been written to (result from previous frame) - int NavLayerActiveMaskNext; // Which layers have been written to (accumulator for current frame) + short NavLayersActiveMask; // Which layers have been written to (result from previous frame) + short NavLayersActiveMaskNext;// Which layers have been written to (accumulator for current frame) ImGuiID NavFocusScopeIdCurrent; // Current focus scope ID while appending bool NavHideHighlightOneFrame; bool NavHasScroll; // Set when scrolling can be used (ScrollMax > 0.0f) @@ -1736,17 +1941,13 @@ struct IMGUI_API ImGuiWindowTempData 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) - int FocusCounterTabStop; // (Legacy Focus/Tabbing system) Same, but only count widgets which you can Tab through. // 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; // == g.ItemFlagsStack.back() float ItemWidth; // Current item width (>0.0: width in pixels, <0.0: align xx pixels to the right of window). float TextWrapPos; // Current text wrap pos. ImVector ItemWidthStack; // Store item widths to restore (attention: .back() is not == ItemWidth) ImVector TextWrapPosStack; // Store text wrap pos to restore (attention: .back() is not == TextWrapPos) - ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting }; // Storage for one window @@ -1783,6 +1984,7 @@ struct IMGUI_API ImGuiWindow bool Appearing; // Set during the frame where the window is appearing (or re-appearing) bool Hidden; // Do not display (== HiddenFrames*** > 0) bool IsFallbackWindow; // Set on the "Debug##Default" window. + bool IsExplicitChild; // Set when passed _ChildWindow, left to false by BeginDocked() bool HasCloseButton; // Set when the window has a close button (p_open != NULL) signed char ResizeBorderHeld; // Current border being held for resize (-1: none, otherwise 0-3) short BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) @@ -1829,8 +2031,10 @@ struct IMGUI_API ImGuiWindow ImDrawList* DrawList; // == &DrawListInst (for backward compatibility reason with code using imgui_internal.h we keep this a pointer) ImDrawList DrawListInst; - ImGuiWindow* ParentWindow; // If we are a child _or_ popup window, this is pointing to our parent. Otherwise NULL. - ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window == Top-level window. + ImGuiWindow* ParentWindow; // If we are a child _or_ popup _or_ docked window, this is pointing to our parent. Otherwise NULL. + ImGuiWindow* ParentWindowInBeginStack; + ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window. Doesn't cross through popups/dock nodes. + ImGuiWindow* RootWindowPopupTree; // Point to ourself or first ancestor that is not a child window. Cross through popups parent<>child. ImGuiWindow* RootWindowForTitleBarHighlight; // Point to ourself or first ancestor which will display TitleBgActive color when this window is active. ImGuiWindow* RootWindowForNav; // Point to ourself or first ancestor which doesn't have the NavFlattened flag. @@ -1863,19 +2067,6 @@ public: ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight(); return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight()); } }; -// Backup and restore just enough data to be able to use IsItemHovered() on item A after another B in the same window has overwritten the data. -struct ImGuiLastItemDataBackup -{ - ImGuiID LastItemId; - ImGuiItemStatusFlags LastItemStatusFlags; - ImRect LastItemRect; - ImRect LastItemDisplayRect; - - ImGuiLastItemDataBackup() { Backup(); } - void Backup() { ImGuiWindow* window = GImGui->CurrentWindow; LastItemId = window->DC.LastItemId; LastItemStatusFlags = window->DC.LastItemStatusFlags; LastItemRect = window->DC.LastItemRect; LastItemDisplayRect = window->DC.LastItemDisplayRect; } - void Restore() const { ImGuiWindow* window = GImGui->CurrentWindow; window->DC.LastItemId = LastItemId; window->DC.LastItemStatusFlags = LastItemStatusFlags; window->DC.LastItemRect = LastItemRect; window->DC.LastItemDisplayRect = LastItemDisplayRect; } -}; - //----------------------------------------------------------------------------- // [SECTION] Tab bar, Tab item support //----------------------------------------------------------------------------- @@ -1896,7 +2087,7 @@ enum ImGuiTabItemFlagsPrivate_ ImGuiTabItemFlags_Button = 1 << 21 // Used by TabItemButton, change the tab item behavior to mimic a button }; -// Storage for one active tab item (sizeof() 28~32 bytes) +// Storage for one active tab item (sizeof() 40 bytes) struct ImGuiTabItem { ImGuiID ID; @@ -1906,16 +2097,16 @@ struct ImGuiTabItem float Offset; // Position relative to beginning of tab float Width; // Width currently displayed float ContentWidth; // Width of label, stored during BeginTabItem() call - ImS16 NameOffset; // When Window==NULL, offset to name within parent ImGuiTabBar::TabsNames + ImS32 NameOffset; // When Window==NULL, offset to name within parent ImGuiTabBar::TabsNames ImS16 BeginOrder; // BeginTabItem() order, used to re-order tabs after toggling ImGuiTabBarFlags_Reorderable ImS16 IndexDuringLayout; // Index only used during TabBarLayout() bool WantClose; // Marked as closed by SetTabItemClosed() - ImGuiTabItem() { memset(this, 0, sizeof(*this)); LastFrameVisible = LastFrameSelected = -1; NameOffset = BeginOrder = IndexDuringLayout = -1; } + ImGuiTabItem() { memset(this, 0, sizeof(*this)); LastFrameVisible = LastFrameSelected = -1; NameOffset = -1; BeginOrder = IndexDuringLayout = -1; } }; // Storage for a tab bar (sizeof() 152 bytes) -struct ImGuiTabBar +struct IMGUI_API ImGuiTabBar { ImVector Tabs; ImGuiTabBarFlags Flags; @@ -1953,7 +2144,7 @@ struct ImGuiTabBar int GetTabOrder(const ImGuiTabItem* tab) const { return Tabs.index_from_ptr(tab); } const char* GetTabName(const ImGuiTabItem* tab) const { - IM_ASSERT(tab->NameOffset != -1 && (int)tab->NameOffset < TabsNames.Buf.Size); + IM_ASSERT(tab->NameOffset != -1 && tab->NameOffset < TabsNames.Buf.Size); return TabsNames.Buf.Data + tab->NameOffset; } }; @@ -1962,8 +2153,6 @@ struct ImGuiTabBar // [SECTION] Table support //----------------------------------------------------------------------------- -#ifdef IMGUI_HAS_TABLE - #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 TableSetupDrawChannels() @@ -2002,10 +2191,11 @@ struct ImGuiTableColumn ImGuiTableColumnIdx NextEnabledColumn; // Index of next enabled/visible column within Columns[], -1 if last enabled/visible column ImGuiTableColumnIdx 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 ImGuiTableDrawChannelIdx DrawChannelCurrent; // Index within DrawSplitter.Channels[] - ImGuiTableDrawChannelIdx DrawChannelFrozen; - ImGuiTableDrawChannelIdx DrawChannelUnfrozen; - bool IsEnabled; // Is the column not marked Hidden by the user? (even if off view, e.g. clipped by scrolling). - bool IsEnabledNextFrame; + ImGuiTableDrawChannelIdx DrawChannelFrozen; // Draw channels for frozen rows (often headers) + ImGuiTableDrawChannelIdx DrawChannelUnfrozen; // Draw channels for unfrozen rows + bool IsEnabled; // IsUserEnabled && (Flags & ImGuiTableColumnFlags_Disabled) == 0 + bool IsUserEnabled; // Is the column not marked Hidden by the user? (unrelated to being off view, e.g. clipped by scrolling). + bool IsUserEnabledNextFrame; bool IsVisibleX; // Is actually in view (e.g. overlapping the host window clipping rectangle, not scrolled). bool IsVisibleY; bool IsRequestOutput; // Return value for TableSetColumnIndex() / TableNextColumn(): whether we request user to output contents or not. @@ -2040,12 +2230,13 @@ struct ImGuiTableCellData ImGuiTableColumnIdx Column; // Column number }; -// FIXME-TABLE: transient data could be stored in a per-stacked table structure: DrawSplitter, SortSpecs, incoming RowData -struct ImGuiTable +// FIXME-TABLE: more transient data could be stored in a per-stacked table structure: DrawSplitter, SortSpecs, incoming RowData +struct IMGUI_API ImGuiTable { ImGuiID ID; ImGuiTableFlags Flags; void* RawData; // Single allocation to hold Columns[], DisplayOrderToIndex[] and RowCellData[] + ImGuiTableTempData* TempData; // Transient data while table is active. Point within g.CurrentTableStack[] ImSpan Columns; // Point within RawData[] ImSpan DisplayOrderToIndex; // Point within RawData[]. Store display order of columns (when not reordered, the values are 0...Count-1) ImSpan RowCellData; // Point within RawData[]. Store cells background requests for current row. @@ -2097,22 +2288,13 @@ struct ImGuiTable ImRect Bg0ClipRectForDrawCmd; // Actual ImDrawCmd clip rect for BG0/1 channel. This tends to be == OuterWindow->ClipRect at BeginTable() because output in BG0/BG1 is cpu-clipped ImRect Bg2ClipRectForDrawCmd; // Actual ImDrawCmd clip rect for BG2 channel. This tends to be a correct, tight-fit, because output to BG2 are done by widgets relying on regular ClipRect. 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 HostBackupInnerClipRect; // Backup of InnerWindow->ClipRect during PushTableBackground()/PopTableBackground() - ImVec2 HostBackupPrevLineSize; // Backup of InnerWindow->DC.PrevLineSize at the end of BeginTable() - ImVec2 HostBackupCurrLineSize; // Backup of InnerWindow->DC.CurrLineSize at the end of BeginTable() - ImVec2 HostBackupCursorMaxPos; // Backup of InnerWindow->DC.CursorMaxPos at the end of BeginTable() - ImVec2 UserOuterSize; // outer_size.x passed to BeginTable() - ImVec1 HostBackupColumnsOffset; // Backup of OuterWindow->DC.ColumnsOffset at the end of BeginTable() - float HostBackupItemWidth; // Backup of OuterWindow->DC.ItemWidth at the end of BeginTable() - int HostBackupItemWidthStackSize;// Backup of OuterWindow->DC.ItemWidthStack.Size 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) + ImDrawListSplitter* DrawSplitter; // Shortcut to TempData->DrawSplitter while in table. Isolate draw commands per columns to avoid switching clip rect constantly ImGuiTableColumnSortSpecs SortSpecsSingle; - ImVector SortSpecsMulti; // FIXME-OPT: Using a small-vector pattern would work be good. + ImVector SortSpecsMulti; // FIXME-OPT: Using a small-vector pattern would be good. ImGuiTableSortSpecs SortSpecs; // Public facing sorts specs, this is what we return in TableGetSortSpecs() ImGuiTableColumnIdx SortSpecsCount; ImGuiTableColumnIdx ColumnsEnabledCount; // Number of enabled columns (<= ColumnsCount) @@ -2155,8 +2337,31 @@ struct ImGuiTable 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() { memset(this, 0, sizeof(*this)); LastFrameActive = -1; } - IMGUI_API ~ImGuiTable() { IM_FREE(RawData); } + ImGuiTable() { memset(this, 0, sizeof(*this)); LastFrameActive = -1; } + ~ImGuiTable() { IM_FREE(RawData); } +}; + +// Transient data that are only needed between BeginTable() and EndTable(), those buffers are shared (1 per level of stacked table). +// - Accessing those requires chasing an extra pointer so for very frequently used data we leave them in the main table structure. +// - We also leave out of this structure data that tend to be particularly useful for debugging/metrics. +struct IMGUI_API ImGuiTableTempData +{ + int TableIndex; // Index in g.Tables.Buf[] pool + float LastTimeActive; // Last timestamp this structure was used + + ImVec2 UserOuterSize; // outer_size.x passed to BeginTable() + ImDrawListSplitter DrawSplitter; + + ImRect HostBackupWorkRect; // Backup of InnerWindow->WorkRect at the end of BeginTable() + ImRect HostBackupParentWorkRect; // Backup of InnerWindow->ParentWorkRect at the end of BeginTable() + ImVec2 HostBackupPrevLineSize; // Backup of InnerWindow->DC.PrevLineSize at the end of BeginTable() + ImVec2 HostBackupCurrLineSize; // Backup of InnerWindow->DC.CurrLineSize at the end of BeginTable() + ImVec2 HostBackupCursorMaxPos; // Backup of InnerWindow->DC.CursorMaxPos at the end of BeginTable() + ImVec1 HostBackupColumnsOffset; // Backup of OuterWindow->DC.ColumnsOffset at the end of BeginTable() + float HostBackupItemWidth; // Backup of OuterWindow->DC.ItemWidth at the end of BeginTable() + int HostBackupItemWidthStackSize;//Backup of OuterWindow->DC.ItemWidthStack.Size at the end of BeginTable() + + ImGuiTableTempData() { memset(this, 0, sizeof(*this)); LastTimeActive = -1.0f; } }; // sizeof() ~ 12 @@ -2197,8 +2402,6 @@ struct ImGuiTableSettings ImGuiTableColumnSettings* GetColumnSettings() { return (ImGuiTableColumnSettings*)(this + 1); } }; -#endif // #ifdef IMGUI_HAS_TABLE - //----------------------------------------------------------------------------- // [SECTION] ImGui internal API // No guarantee of forward compatibility here! @@ -2217,14 +2420,16 @@ namespace ImGui IMGUI_API ImGuiWindow* FindWindowByName(const char* name); IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window); IMGUI_API ImVec2 CalcWindowNextAutoFitSize(ImGuiWindow* window); - IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent); + IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy); + IMGUI_API bool IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent); IMGUI_API bool IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below); IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window); - IMGUI_API ImRect GetWindowAllowedExtentRect(ImGuiWindow* window); IMGUI_API void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond = 0); IMGUI_API void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond = 0); IMGUI_API void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond = 0); IMGUI_API void SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size); + inline ImRect WindowRectAbsToRel(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x - off.x, r.Min.y - off.y, r.Max.x - off.x, r.Max.y - off.y); } + inline ImRect WindowRectRelToAbs(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x + off.x, r.Min.y + off.y, r.Max.x + off.x, r.Max.y + off.y); } // Windows: Display Order and Focus Order IMGUI_API void FocusWindow(ImGuiWindow* window); @@ -2232,6 +2437,9 @@ namespace ImGui IMGUI_API void BringWindowToFocusFront(ImGuiWindow* window); IMGUI_API void BringWindowToDisplayFront(ImGuiWindow* window); IMGUI_API void BringWindowToDisplayBack(ImGuiWindow* window); + IMGUI_API void BringWindowToDisplayBehind(ImGuiWindow* window, ImGuiWindow* above_window); + IMGUI_API int FindWindowDisplayIndex(ImGuiWindow* window); + IMGUI_API ImGuiWindow* FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* window); // Fonts, drawing IMGUI_API void SetCurrentFont(ImFont* font); @@ -2270,14 +2478,21 @@ namespace ImGui IMGUI_API void SetScrollY(ImGuiWindow* window, float scroll_y); IMGUI_API void SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio); IMGUI_API void SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio); - IMGUI_API ImVec2 ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect); + + // Early work-in-progress API (ScrollToItem() will become public) + IMGUI_API void ScrollToItem(ImGuiScrollFlags flags = 0); + IMGUI_API void ScrollToRect(ImGuiWindow* window, const ImRect& rect, ImGuiScrollFlags flags = 0); + IMGUI_API ImVec2 ScrollToRectEx(ImGuiWindow* window, const ImRect& rect, ImGuiScrollFlags flags = 0); +//#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + inline void ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& rect) { ScrollToRect(window, rect, ImGuiScrollFlags_KeepVisibleEdgeY); } +//#endif // Basic Accessors - inline ImGuiID GetItemID() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DC.LastItemId; } // Get ID of last item (~~ often same ImGui::GetID(label) beforehand) - inline ImGuiItemStatusFlags GetItemStatusFlags() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DC.LastItemStatusFlags; } + inline ImGuiID GetItemID() { ImGuiContext& g = *GImGui; return g.LastItemData.ID; } // Get ID of last item (~~ often same ImGui::GetID(label) beforehand) + inline ImGuiItemStatusFlags GetItemStatusFlags(){ ImGuiContext& g = *GImGui; return g.LastItemData.StatusFlags; } + inline ImGuiItemFlags GetItemFlags() { ImGuiContext& g = *GImGui; return g.LastItemData.InFlags; } 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(); @@ -2291,21 +2506,32 @@ namespace ImGui // Basic Helpers for widget code IMGUI_API void ItemSize(const ImVec2& size, float text_baseline_y = -1.0f); IMGUI_API void ItemSize(const ImRect& bb, float text_baseline_y = -1.0f); - IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL); + IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL, ImGuiItemFlags extra_flags = 0); IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id); - IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged); - IMGUI_API void SetLastItemData(ImGuiWindow* window, ImGuiID item_id, ImGuiItemStatusFlags status_flags, const ImRect& item_rect); - IMGUI_API bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id); // Return true if focus is requested - IMGUI_API void FocusableItemUnregister(ImGuiWindow* window); + IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id); + IMGUI_API void SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect); IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h); IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); IMGUI_API void PushMultiItemsWidths(int components, float width_full); - IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); - IMGUI_API void PopItemFlag(); IMGUI_API bool IsItemToggledSelection(); // Was the last item selection toggled? (after Selectable(), TreeNode() etc. We only returns toggle _event_ in order to handle clipping correctly) IMGUI_API ImVec2 GetContentRegionMaxAbs(); IMGUI_API void ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess); + // Parameter stacks + IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); + IMGUI_API void PopItemFlag(); + +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + // Currently refactoring focus/nav/tabbing system + // If you have old/custom copy-and-pasted widgets that used FocusableItemRegister(): + // (Old) IMGUI_VERSION_NUM < 18209: using 'ItemAdd(....)' and 'bool tab_focused = FocusableItemRegister(...)' + // (Old) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)' and 'bool tab_focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_Focused) != 0' + // (New) IMGUI_VERSION_NUM >= 18413: using 'ItemAdd(..., ImGuiItemFlags_Inputable)' and 'bool tab_focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_FocusedTabbing) != 0 || g.NavActivateInputId == id' (WIP) + // Widget code are simplified as there's no need to call FocusableItemUnregister() while managing the transition from regular widget to TempInputText() + inline bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id) { IM_ASSERT(0); IM_UNUSED(window); IM_UNUSED(id); return false; } // -> pass ImGuiItemAddFlags_Inputable flag to ItemAdd() + inline void FocusableItemUnregister(ImGuiWindow* window) { IM_ASSERT(0); IM_UNUSED(window); } // -> unnecessary: TempInputText() uses ImGuiInputTextFlags_MergedItem +#endif + // Logging/Capture IMGUI_API void LogBegin(ImGuiLogType type, int auto_open_depth); // -> BeginCapture() when we design v2 api, for now stay under the radar by using the old name. IMGUI_API void LogToBuffer(int auto_open_depth = -1); // Start logging/capturing to internal buffer @@ -2317,25 +2543,41 @@ namespace ImGui IMGUI_API void OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags = ImGuiPopupFlags_None); IMGUI_API void ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup); IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup); + IMGUI_API void ClosePopupsExceptModals(); IMGUI_API bool IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags); IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags); - IMGUI_API void BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags); + IMGUI_API void BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags); + IMGUI_API ImRect GetPopupAllowedExtentRect(ImGuiWindow* window); IMGUI_API ImGuiWindow* GetTopMostPopupModal(); + IMGUI_API ImGuiWindow* GetTopMostAndVisiblePopupModal(); IMGUI_API ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window); IMGUI_API ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy); + + // Menus IMGUI_API bool BeginViewportSideBar(const char* name, ImGuiViewport* viewport, ImGuiDir dir, float size, ImGuiWindowFlags window_flags); + IMGUI_API bool BeginMenuEx(const char* label, const char* icon, bool enabled = true); + IMGUI_API bool MenuItemEx(const char* label, const char* icon, const char* shortcut = NULL, bool selected = false, bool enabled = true); + + // Combos + IMGUI_API bool BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags flags); + IMGUI_API bool BeginComboPreview(); + IMGUI_API void EndComboPreview(); // Gamepad/Keyboard Navigation IMGUI_API void NavInitWindow(ImGuiWindow* window, bool force_reinit); + IMGUI_API void NavInitRequestApplyResult(); IMGUI_API bool NavMoveRequestButNoResultYet(); + IMGUI_API void NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags); + IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags); + IMGUI_API void NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result); IMGUI_API void NavMoveRequestCancel(); - IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags); + IMGUI_API void NavMoveRequestApplyResult(); IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags); IMGUI_API float GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode); IMGUI_API ImVec2 GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor = 0.0f, float fast_factor = 0.0f); IMGUI_API int CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate); IMGUI_API void ActivateItem(ImGuiID id); // Remotely activate a button, checkbox, tree node etc. given its unique ID. activation is queued and processed on the next frame when the item is encountered again. - IMGUI_API void SetNavID(ImGuiID id, int nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel); + IMGUI_API void SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel); // Focus Scope (WIP) // This is generally used to identify a selection set (multiple of which may be in the same window), as selection @@ -2348,6 +2590,7 @@ namespace ImGui // Inputs // FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions. IMGUI_API void SetItemUsingMouseWheel(); + IMGUI_API void SetActiveIdUsingNavAndKeys(); inline bool IsActiveIdUsingNavDir(ImGuiDir dir) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavDirMask & (1 << dir)) != 0; } inline bool IsActiveIdUsingNavInput(ImGuiNavInput input) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavInputMask & (1 << input)) != 0; } inline bool IsActiveIdUsingKey(ImGuiKey key) { ImGuiContext& g = *GImGui; IM_ASSERT(key < 64); return (g.ActiveIdUsingKeyInputMask & ((ImU64)1 << key)) != 0; } @@ -2413,6 +2656,7 @@ namespace ImGui IMGUI_API void TableSetColumnWidthAutoAll(ImGuiTable* table); IMGUI_API void TableRemove(ImGuiTable* table); IMGUI_API void TableGcCompactTransientBuffers(ImGuiTable* table); + IMGUI_API void TableGcCompactTransientBuffers(ImGuiTableTempData* table); IMGUI_API void TableGcCompactSettings(); // Tables: Settings @@ -2473,7 +2717,7 @@ namespace ImGui IMGUI_API bool CollapseButton(ImGuiID id, const ImVec2& pos); IMGUI_API bool ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0); IMGUI_API void Scrollbar(ImGuiAxis axis); - IMGUI_API bool ScrollbarEx(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* p_scroll_v, float avail_v, float contents_v, ImDrawFlags flags); + IMGUI_API bool ScrollbarEx(const ImRect& bb, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 avail_v, ImS64 contents_v, ImDrawFlags flags); IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec2& padding, const ImVec4& bg_col, const ImVec4& tint_col); IMGUI_API ImRect GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis); IMGUI_API ImGuiID GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis); @@ -2536,12 +2780,16 @@ namespace ImGui // Debug Tools IMGUI_API void ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL); - inline void DebugDrawItemRect(ImU32 col = IM_COL32(255,0,0,255)) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; GetForegroundDrawList(window)->AddRect(window->DC.LastItemRect.Min, window->DC.LastItemRect.Max, col); } + IMGUI_API void ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL); + inline void DebugDrawItemRect(ImU32 col = IM_COL32(255,0,0,255)) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; GetForegroundDrawList(window)->AddRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, col); } inline void DebugStartItemPicker() { ImGuiContext& g = *GImGui; g.DebugItemPickerActive = true; } + IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas); + IMGUI_API void DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end); IMGUI_API void DebugNodeColumns(ImGuiOldColumns* columns); IMGUI_API void DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, const char* label); 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 DebugNodeFont(ImFont* font); 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); @@ -2549,6 +2797,7 @@ namespace ImGui IMGUI_API void DebugNodeWindow(ImGuiWindow* window, const char* label); IMGUI_API void DebugNodeWindowSettings(ImGuiWindowSettings* settings); IMGUI_API void DebugNodeWindowsList(ImVector* windows, const char* label); + IMGUI_API void DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack); IMGUI_API void DebugNodeViewport(ImGuiViewportP* viewport); IMGUI_API void DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb); @@ -2583,20 +2832,14 @@ IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table #ifdef IMGUI_ENABLE_TEST_ENGINE extern void ImGuiTestEngineHook_ItemAdd(ImGuiContext* ctx, const ImRect& bb, ImGuiID id); extern void ImGuiTestEngineHook_ItemInfo(ImGuiContext* ctx, ImGuiID id, const char* label, ImGuiItemStatusFlags flags); -extern void ImGuiTestEngineHook_IdInfo(ImGuiContext* ctx, ImGuiDataType data_type, ImGuiID id, const void* data_id); -extern void ImGuiTestEngineHook_IdInfo(ImGuiContext* ctx, ImGuiDataType data_type, ImGuiID id, const void* data_id, const void* data_id_end); extern void ImGuiTestEngineHook_Log(ImGuiContext* ctx, const char* fmt, ...); +extern const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ctx, ImGuiID id); + #define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemAdd(&g, _BB, _ID) // Register item bounding box #define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register item label and status flags (optional) #define IMGUI_TEST_ENGINE_LOG(_FMT,...) if (g.TestEngineHookItems) ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__) // Custom log entry from user land into test log -#define IMGUI_TEST_ENGINE_ID_INFO(_ID,_TYPE,_DATA) if (g.TestEngineHookIdInfo == id) ImGuiTestEngineHook_IdInfo(&g, _TYPE, _ID, (const void*)(_DATA)); -#define IMGUI_TEST_ENGINE_ID_INFO2(_ID,_TYPE,_DATA,_DATA2) if (g.TestEngineHookIdInfo == id) ImGuiTestEngineHook_IdInfo(&g, _TYPE, _ID, (const void*)(_DATA), (const void*)(_DATA2)); #else -#define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) do { } while (0) -#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) do { } while (0) -#define IMGUI_TEST_ENGINE_LOG(_FMT,...) do { } while (0) -#define IMGUI_TEST_ENGINE_ID_INFO(_ID,_TYPE,_DATA) do { } while (0) -#define IMGUI_TEST_ENGINE_ID_INFO2(_ID,_TYPE,_DATA,_DATA2) do { } while (0) +#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) ((void)0) #endif //----------------------------------------------------------------------------- diff --git a/r5dev/include/imgui_stdlib.h b/r5dev/thirdparty/imgui/include/imgui_stdlib.h similarity index 100% rename from r5dev/include/imgui_stdlib.h rename to r5dev/thirdparty/imgui/include/imgui_stdlib.h diff --git a/r5dev/thirdparty/imgui/include/imgui_utility.h b/r5dev/thirdparty/imgui/include/imgui_utility.h new file mode 100644 index 00000000..d221c4c9 --- /dev/null +++ b/r5dev/thirdparty/imgui/include/imgui_utility.h @@ -0,0 +1,31 @@ +#pragma once + +///////////////////////////////////////////////////////////////////////////// +// Internals +int Stricmp(const char* s1, const char* s2); +int Strnicmp(const char* s1, const char* s2, int n); +char* Strdup(const char* s); +void Strtrim(char* s); + +class ImGuiConfig +{ +public: + struct + { + int m_nBind0 = VK_OEM_3; + int m_nBind1 = VK_INSERT; + int m_nAutoClearLimit = 300; + bool m_bAutoClear = true; + } IConsole_Config; + + struct + { + int m_nBind0 = VK_HOME; + int m_nBind1 = VK_F10; + } IBrowser_Config; + + void Load(); + void Save(); +}; + +extern ImGuiConfig* g_pImGuiConfig; diff --git a/external/imgui/include/imstb_rectpack.h b/r5dev/thirdparty/imgui/include/imstb_rectpack.h similarity index 98% rename from external/imgui/include/imstb_rectpack.h rename to r5dev/thirdparty/imgui/include/imstb_rectpack.h index ff2a85df..39589521 100644 --- a/external/imgui/include/imstb_rectpack.h +++ b/r5dev/thirdparty/imgui/include/imstb_rectpack.h @@ -34,7 +34,7 @@ // Minor features // Martins Mozeiko // github:IntellectualKitty -// +// // Bugfixes / warning fixes // Jeremy Jaussaud // Fabian Giesen @@ -441,7 +441,7 @@ static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int widt } } tail = tail->next; - } + } } fr.prev_link = best; @@ -602,38 +602,38 @@ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett -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 +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 +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 +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. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. -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 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 +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 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. ------------------------------------------------------------------------------ */ diff --git a/external/imgui/include/imstb_textedit.h b/r5dev/thirdparty/imgui/include/imstb_textedit.h similarity index 98% rename from external/imgui/include/imstb_textedit.h rename to r5dev/thirdparty/imgui/include/imstb_textedit.h index 76446709..2c635b27 100644 --- a/external/imgui/include/imstb_textedit.h +++ b/r5dev/thirdparty/imgui/include/imstb_textedit.h @@ -1,5 +1,5 @@ // [DEAR IMGUI] -// This is a slightly modified version of stb_textedit.h 1.13. +// This is a slightly modified version of stb_textedit.h 1.13. // Those changes would need to be pushed into nothings/stb: // - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321) // Grep for [DEAR IMGUI] to find the changes. @@ -19,7 +19,7 @@ // texts, as its performance does not scale and it has limited undo). // // Non-trivial behaviors are modelled after Windows text controls. -// +// // // LICENSE // @@ -217,20 +217,20 @@ // call this with the mouse x,y on a mouse down; it will update the cursor // and reset the selection start/end to the cursor point. the x,y must // be relative to the text widget, with (0,0) being the top left. -// +// // drag: // call this with the mouse x,y on a mouse drag/up; it will update the // cursor and the selection end point -// +// // cut: // call this to delete the current selection; returns true if there was // one. you should FIRST copy the current selection to the system paste buffer. // (To copy, just copy the current selection out of the string yourself.) -// +// // paste: // call this to paste text at the current cursor point or over the current // selection if there is one. -// +// // key: // call this for keyboard inputs sent to the textfield. you can use it // for "key down" events or for "translated" key events. if you need to @@ -241,7 +241,7 @@ // clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to // anything other type you wante before including. // -// +// // When rendering, you can read the cursor position and selection state from // the STB_TexteditState. // @@ -716,9 +716,11 @@ static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditSta state->has_preferred_x = 0; return 1; } - // remove the undo since we didn't actually insert the characters - if (state->undostate.undo_point) - --state->undostate.undo_point; + // [DEAR IMGUI] + //// remove the undo since we didn't actually insert the characters + //if (state->undostate.undo_point) + // --state->undostate.undo_point; + // note: paste failure will leave deleted selection, may be restored with an undo (see https://github.com/nothings/stb/issues/734 for details) return 0; } @@ -764,7 +766,7 @@ retry: state->insert_mode = !state->insert_mode; break; #endif - + case STB_TEXTEDIT_K_UNDO: stb_text_undo(str, state); state->has_preferred_x = 0; @@ -779,7 +781,7 @@ retry: // if currently there's a selection, move cursor to start of selection if (STB_TEXT_HAS_SELECTION(state)) stb_textedit_move_to_first(state); - else + else if (state->cursor > 0) --state->cursor; state->has_preferred_x = 0; @@ -828,7 +830,7 @@ retry: #ifdef STB_TEXTEDIT_MOVEWORDRIGHT case STB_TEXTEDIT_K_WORDRIGHT: - if (STB_TEXT_HAS_SELECTION(state)) + if (STB_TEXT_HAS_SELECTION(state)) stb_textedit_move_to_last(str, state); else { state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor); @@ -922,7 +924,7 @@ retry: } break; } - + case STB_TEXTEDIT_K_UP: case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT: case STB_TEXTEDIT_K_PGUP: @@ -1014,7 +1016,7 @@ retry: } state->has_preferred_x = 0; break; - + #ifdef STB_TEXTEDIT_K_TEXTSTART2 case STB_TEXTEDIT_K_TEXTSTART2: #endif @@ -1031,7 +1033,7 @@ retry: state->select_start = state->select_end = 0; state->has_preferred_x = 0; break; - + #ifdef STB_TEXTEDIT_K_TEXTSTART2 case STB_TEXTEDIT_K_TEXTSTART2 | STB_TEXTEDIT_K_SHIFT: #endif @@ -1410,38 +1412,38 @@ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett -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 +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 +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 +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. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. -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 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 +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 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. ------------------------------------------------------------------------------ */ diff --git a/external/imgui/include/imstb_truetype.h b/r5dev/thirdparty/imgui/include/imstb_truetype.h similarity index 99% rename from external/imgui/include/imstb_truetype.h rename to r5dev/thirdparty/imgui/include/imstb_truetype.h index fc815d74..48c20261 100644 --- a/external/imgui/include/imstb_truetype.h +++ b/r5dev/thirdparty/imgui/include/imstb_truetype.h @@ -51,7 +51,7 @@ // Rob Loach Cort Stratton // Kenney Phillis Jr. github:oyvindjam // Brian Costabile github:vassvik -// +// // VERSION HISTORY // // 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() @@ -212,7 +212,7 @@ // // Advancing for the next character: // Call GlyphHMetrics, and compute 'current_point += SF * advance'. -// +// // // ADVANCED USAGE // @@ -257,7 +257,7 @@ // Curve tessellation 120 LOC \__ 550 LOC Bitmap creation // Bitmap management 100 LOC / // Baked bitmap interface 70 LOC / -// Font name matching & access 150 LOC ---- 150 +// Font name matching & access 150 LOC ---- 150 // C runtime library abstraction 60 LOC ---- 60 // // @@ -350,7 +350,7 @@ int main(int argc, char **argv) } return 0; } -#endif +#endif // // Output: // @@ -364,9 +364,9 @@ int main(int argc, char **argv) // :@@. M@M // @@@o@@@@ // :M@@V:@@. -// +// ////////////////////////////////////////////////////////////////////////////// -// +// // Complete program: print "Hello World!" banner, with bugs // #if 0 @@ -667,7 +667,7 @@ STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, cons // Calling these functions in sequence is roughly equivalent to calling // stbtt_PackFontRanges(). If you more control over the packing of multiple // fonts, or if you want to pack custom data into a font texture, take a look -// at the source to of stbtt_PackFontRanges() and create a custom version +// at the source to of stbtt_PackFontRanges() and create a custom version // using these functions, e.g. call GatherRects multiple times, // building up a single array of rects, then call PackRects once, // then call RenderIntoRects repeatedly. This may result in a @@ -975,7 +975,7 @@ STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, floa // and computing from that can allow drop-out prevention). // // The algorithm has not been optimized at all, so expect it to be slow -// if computing lots of characters or very large sizes. +// if computing lots of characters or very large sizes. @@ -1732,7 +1732,7 @@ static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, s if (i != 0) num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); - // now start the new one + // now start the new one start_off = !(flags & 1); if (start_off) { // if we start off with an off-curve point, then when we need to find a point on the curve @@ -1785,7 +1785,7 @@ static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, s int comp_num_verts = 0, i; stbtt_vertex *comp_verts = 0, *tmp = 0; float mtx[6] = {1,0,0,1,0,0}, m, n; - + flags = ttSHORT(comp); comp+=2; gidx = ttSHORT(comp); comp+=2; @@ -1815,7 +1815,7 @@ static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, s mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; } - + // Find transformation scales. m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); @@ -2746,7 +2746,7 @@ static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, i float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); STBTT_assert(z != NULL); if (!z) return z; - + // round dx down to avoid overshooting if (dxdy < 0) z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); @@ -2824,7 +2824,7 @@ static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__ac } } } - + e = e->next; } } @@ -3554,7 +3554,7 @@ STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info { int ix0,iy0,ix1,iy1; stbtt__bitmap gbm; - stbtt_vertex *vertices; + stbtt_vertex *vertices; int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); if (scale_x == 0) scale_x = scale_y; @@ -3577,7 +3577,7 @@ STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info if (height) *height = gbm.h; if (xoff ) *xoff = ix0; if (yoff ) *yoff = iy0; - + if (gbm.w && gbm.h) { gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); if (gbm.pixels) { @@ -3588,7 +3588,7 @@ STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info } STBTT_free(vertices, info->userdata); return gbm.pixels; -} +} STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) { @@ -3600,7 +3600,7 @@ STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigne int ix0,iy0; stbtt_vertex *vertices; int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); - stbtt__bitmap gbm; + stbtt__bitmap gbm; stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); gbm.pixels = output; @@ -3622,7 +3622,7 @@ STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char * STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) { return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); -} +} STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint) { @@ -3637,7 +3637,7 @@ STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, uns STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) { return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); -} +} STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) { @@ -3762,7 +3762,7 @@ static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *no con->y = 0; con->bottom_y = 0; STBTT__NOTUSED(nodes); - STBTT__NOTUSED(num_nodes); + STBTT__NOTUSED(num_nodes); } static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) @@ -4147,7 +4147,7 @@ STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char n = 0; for (i=0; i < num_ranges; ++i) n += ranges[i].num_chars; - + rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); if (rects == NULL) return 0; @@ -4158,7 +4158,7 @@ STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); stbtt_PackFontRangesPackRects(spc, rects, n); - + return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); STBTT_free(rects, spc->user_allocator_context); @@ -4319,7 +4319,7 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y; if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; - if (x_inter < x) + if (x_inter < x) winding += (y0 < y1) ? 1 : -1; } } @@ -4345,7 +4345,7 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex y1 = (int)verts[i ].y; if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; - if (x_inter < x) + if (x_inter < x) winding += (y0 < y1) ? 1 : -1; } } else { @@ -4357,7 +4357,7 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex if (hits[1][0] < 0) winding += (hits[1][1] < 0 ? -1 : 1); } - } + } } } return winding; @@ -4438,7 +4438,7 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc // invert for y-downwards bitmaps scale_y = -scale_y; - + { int x,y,i,j; float *precompute; @@ -4587,7 +4587,7 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc STBTT_free(verts, info->userdata); } return data; -} +} STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) { @@ -4605,7 +4605,7 @@ STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata) // // check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string -static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) +static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) { stbtt_int32 i=0; @@ -4644,7 +4644,7 @@ static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, s return i; } -static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) +static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) { return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); } @@ -4773,7 +4773,7 @@ STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index) { - return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index); + return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index); } STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data) @@ -4866,38 +4866,38 @@ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett -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 +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 +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 +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. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. -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 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 +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 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. ------------------------------------------------------------------------------ */ diff --git a/external/imgui/src/imgui.cpp b/r5dev/thirdparty/imgui/src/imgui.cpp similarity index 81% rename from external/imgui/src/imgui.cpp rename to r5dev/thirdparty/imgui/src/imgui.cpp index a2557f84..41fef144 100644 --- a/external/imgui/src/imgui.cpp +++ b/r5dev/thirdparty/imgui/src/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.83 WIP +// dear imgui, v1.86 // (main code and documentation) // Help: @@ -11,11 +11,14 @@ // - FAQ http://dearimgui.org/faq // - Homepage & latest https://github.com/ocornut/imgui // - Releases & changelog https://github.com/ocornut/imgui/releases -// - Gallery https://github.com/ocornut/imgui/issues/3793 (please post your screenshots/video there!) +// - Gallery https://github.com/ocornut/imgui/issues/4451 (please post your screenshots/video there!) // - Wiki https://github.com/ocornut/imgui/wiki (lots of good stuff there) // - Glossary https://github.com/ocornut/imgui/wiki/Glossary // - Issues & support https://github.com/ocornut/imgui/issues -// - Discussions https://github.com/ocornut/imgui/discussions + +// Getting Started? +// - For first-time users having issues compiling/linking/running or issues loading fonts: +// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above. // Developed by Omar Cornut and every direct or indirect contributors to the GitHub. // See LICENSE.txt for copyright and licensing details (standard MIT License). @@ -79,6 +82,7 @@ CODE // [SECTION] VIEWPORTS // [SECTION] PLATFORM DEPENDENT HELPERS // [SECTION] METRICS/DEBUGGER WINDOW +// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, STACK TOOL) */ @@ -304,9 +308,9 @@ CODE } else { - // The texture for the draw call is specified by pcmd->TextureId. + // The texture for the draw call is specified by pcmd->GetTexID(). // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization. - MyEngineBindTexture((MyTexture*)pcmd->TextureId); + MyEngineBindTexture((MyTexture*)pcmd->GetTexID()); // We are using scissoring to clip some objects. All low-level graphics API should support it. // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches @@ -376,6 +380,15 @@ CODE When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2021/12/20 (1.86) - backends: removed obsolete Marmalade backend (imgui_impl_marmalade.cpp) + example. Find last supported version at https://github.com/ocornut/imgui/wiki/Bindings + - 2021/11/04 (1.86) - removed CalcListClipping() function. Prefer using ImGuiListClipper which can return non-contiguous ranges. Please open an issue if you think you really need this function. + - 2021/08/23 (1.85) - removed GetWindowContentRegionWidth() function. keep inline redirection helper. can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead for generally 'GetContentRegionAvail().x' is more useful. + - 2021/07/26 (1.84) - commented out redirecting functions/enums names that were marked obsolete in 1.67 and 1.69 (March 2019): + - ImGui::GetOverlayDrawList() -> use ImGui::GetForegroundDrawList() + - ImFont::GlyphRangesBuilder -> use ImFontGlyphRangesBuilder + - 2021/05/19 (1.83) - backends: obsoleted direct access to ImDrawCmd::TextureId in favor of calling ImDrawCmd::GetTexID(). + - if you are using official backends from the source tree: you have nothing to do. + - if you have copied old backend code or using your own: change access to draw_cmd->TextureId to draw_cmd->GetTexID(). - 2021/03/12 (1.82) - upgraded ImDrawList::AddRect(), AddRectFilled(), PathRect() to use ImDrawFlags instead of ImDrawCornersFlags. - ImDrawCornerFlags_TopLeft -> use ImDrawFlags_RoundCornersTopLeft - ImDrawCornerFlags_BotRight -> use ImDrawFlags_RoundCornersBottomRight @@ -393,7 +406,7 @@ CODE - ImGui::SetScrollHere() -> use ImGui::SetScrollHereY() - 2021/03/11 (1.82) - clarified that ImDrawList::PathArcTo(), ImDrawList::PathArcToFast() won't render with radius < 0.0f. Previously it sorts of accidentally worked but would generally lead to counter-clockwise paths and have an effect on anti-aliasing. - 2021/03/10 (1.82) - upgraded ImDrawList::AddPolyline() and PathStroke() "bool closed" parameter to "ImDrawFlags flags". The matching ImDrawFlags_Closed value is guaranteed to always stay == 1 in the future. - - 2021/02/22 (1.82) - win32+mingw: Re-enabled IME functions by default even under MinGW. In July 2016, issue #738 had me incorrectly disable those default functions for MinGW. MinGW users should: either link with -limm32, either set their imconfig file with '#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS'. + - 2021/02/22 (1.82) - (*undone in 1.84*) win32+mingw: Re-enabled IME functions by default even under MinGW. In July 2016, issue #738 had me incorrectly disable those default functions for MinGW. MinGW users should: either link with -limm32, either set their imconfig file with '#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS'. - 2021/02/17 (1.82) - renamed rarely used style.CircleSegmentMaxError (old default = 1.60f) to style.CircleTessellationMaxError (new default = 0.30f) as the meaning of the value changed. - 2021/02/03 (1.81) - renamed ListBoxHeader(const char* label, ImVec2 size) to BeginListBox(). Kept inline redirection function (will obsolete). - removed ListBoxHeader(const char* label, int items_count, int height_in_items = -1) in favor of specifying size. Kept inline redirection function (will obsolete). @@ -712,9 +725,11 @@ CODE Q&A: Usage ---------- - Q: Why is my widget not reacting when I click on it? - Q: How can I have widgets with an empty label? - Q: How can I have multiple widgets with the same label? + Q: About the ID Stack system.. + - Why is my widget not reacting when I click on it? + - How can I have widgets with an empty label? + - How can I have multiple widgets with the same label? + - How can I have multiple windows with the same label? Q: How can I display an image? What is ImTextureID, how does it works? Q: How can I use my own math types instead of ImVec2/ImVec4? Q: How can I interact with standard C++ types (such as std::string and std::vector)? @@ -765,13 +780,13 @@ CODE #define _CRT_SECURE_NO_WARNINGS #endif -#include "imgui.h" +#include "thirdparty/imgui/include/imgui.h" #ifndef IMGUI_DISABLE #ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS #endif -#include "imgui_internal.h" +#include "thirdparty/imgui/include/imgui_internal.h" // System includes #include // toupper @@ -782,6 +797,11 @@ CODE #include // intptr_t #endif +// [Windows] On non-Visual Studio compilers, we default to IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS unless explicitly enabled +#if defined(_WIN32) && !defined(_MSC_VER) && !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) +#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS +#endif + // [Windows] OS specific includes (optional) #if defined(_WIN32) && defined(IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) #define IMGUI_DISABLE_WIN32_FUNCTIONS @@ -816,6 +836,9 @@ CODE #if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later #pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types #endif +#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). +#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6). +#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). #endif // Clang/GCC warnings with -Weverything @@ -892,34 +915,41 @@ namespace ImGui static void NavUpdate(); static void NavUpdateWindowing(); static void NavUpdateWindowingOverlay(); -static void NavUpdateMoveResult(); -static void NavUpdateInitResult(); +static void NavUpdateCancelRequest(); +static void NavUpdateCreateMoveRequest(); +static void NavUpdateCreateTabbingRequest(); static float NavUpdatePageUpPageDown(); static inline void NavUpdateAnyRequestFlag(); +static void NavUpdateCreateWrappingRequest(); static void NavEndFrame(); -static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand); -static void NavApplyItemToResult(ImGuiNavMoveResult* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel); -static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id); +static bool NavScoreItem(ImGuiNavItemData* result); +static void NavApplyItemToResult(ImGuiNavItemData* result); +static void NavProcessItem(); +static void NavProcessItemForTabbingRequest(ImGuiID id); static ImVec2 NavCalcPreferredRefPos(); static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window); static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); static void NavRestoreLayer(ImGuiNavLayer layer); +static void NavRestoreHighlightAfterMove(); static int FindWindowFocusIndex(ImGuiWindow* window); -// Error Checking +// Error Checking and Debug Tools static void ErrorCheckNewFrameSanityChecks(); static void ErrorCheckEndFrameSanityChecks(); +static void UpdateDebugToolItemPicker(); +static void UpdateDebugToolStackQueries(); // Misc static void UpdateSettings(); static void UpdateMouseInputs(); static void UpdateMouseWheel(); -static void UpdateTabFocus(); -static void UpdateDebugToolItemPicker(); static bool UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect); static void RenderWindowOuterBorders(ImGuiWindow* window); static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size); static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open); +static void RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col); +static void RenderDimmedBackgrounds(); +static ImGuiWindow* FindBlockingModal(ImGuiWindow* window); // Viewports static void UpdateViewportsNewFrame(); @@ -974,7 +1004,8 @@ static void* GImAllocatorUserData = NULL; ImGuiStyle::ImGuiStyle() { - Alpha = 1.0f; // Global alpha applies to everything in ImGui + Alpha = 1.0f; // Global alpha applies to everything in Dear ImGui. + DisabledAlpha = 0.60f; // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha. WindowPadding = ImVec2(8,8); // Padding within a window WindowRounding = 0.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended. WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested. @@ -1059,8 +1090,8 @@ ImGuiIO::ImGuiIO() DisplaySize = ImVec2(-1.0f, -1.0f); DeltaTime = 1.0f / 60.0f; IniSavingRate = 5.0f; - IniFilename = "platform\\imgui.ini"; - LogFilename = "platform\\log\\imgui_log.txt"; + IniFilename = "platform\\layout.ini"; // Important: "imgui.ini" is relative to current working dir, most apps will want to lock this to an absolute path (e.g. same path as executables). + LogFilename = "platform\\logs\\imgui_log.txt"; MouseDoubleClickTime = 0.30f; MouseDoubleClickMaxDist = 6.0f; for (int i = 0; i < ImGuiKey_COUNT; i++) @@ -1166,6 +1197,24 @@ void ImGuiIO::ClearInputCharacters() InputQueueCharacters.resize(0); } +void ImGuiIO::ClearInputKeys() +{ + memset(KeysDown, 0, sizeof(KeysDown)); + for (int n = 0; n < IM_ARRAYSIZE(KeysDownDuration); n++) + KeysDownDuration[n] = KeysDownDurationPrev[n] = -1.0f; + KeyCtrl = KeyShift = KeyAlt = KeySuper = false; + KeyMods = KeyModsPrev = ImGuiKeyModFlags_None; + for (int n = 0; n < IM_ARRAYSIZE(NavInputsDownDuration); n++) + NavInputsDownDuration[n] = NavInputsDownDurationPrev[n] = -1.0f; +} + +void ImGuiIO::AddFocusEvent(bool focused) +{ + // We intentionally overwrite this and process in NewFrame(), in order to give a chance + // to multi-viewports backends to queue AddFocusEvent(false),AddFocusEvent(true) in same frame. + AppFocusLost = !focused; +} + //----------------------------------------------------------------------------- // [SECTION] MISC HELPERS/UTILITIES (Geometry functions) //----------------------------------------------------------------------------- @@ -1695,7 +1744,7 @@ int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end) } // Based on stb_to_utf8() from github.com/nothings/stb/ -static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c) +static inline int ImTextCharToUtf8_inline(char* buf, int buf_size, unsigned int c) { if (c < 0x80) { @@ -1730,6 +1779,13 @@ static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c) return 0; } +const char* ImTextCharToUtf8(char out_buf[5], unsigned int c) +{ + int count = ImTextCharToUtf8_inline(out_buf, 5, c); + out_buf[count] = 0; + return out_buf; +} + // Not optimal but we very rarely use this function. int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end) { @@ -1746,20 +1802,20 @@ static inline int ImTextCountUtf8BytesFromChar(unsigned int c) return 3; } -int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end) +int ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end) { - char* buf_out = buf; - const char* buf_end = buf + buf_size; - while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text) + char* buf_p = out_buf; + const char* buf_end = out_buf + out_buf_size; + while (buf_p < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text) { unsigned int c = (unsigned int)(*in_text++); if (c < 0x80) - *buf_out++ = (char)c; + *buf_p++ = (char)c; else - buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end - buf_out - 1), c); + buf_p += ImTextCharToUtf8_inline(buf_p, (int)(buf_end - buf_p - 1), c); } - *buf_out = 0; - return (int)(buf_out - buf); + *buf_p = 0; + return (int)(buf_p - out_buf); } int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end) @@ -1894,7 +1950,7 @@ void ImGuiStorage::BuildSortByKey() { struct StaticFunc { - static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs) + static int IMGUI_CDECL PairComparerByID(const void* lhs, const void* rhs) { // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that. if (((const ImGuiStoragePair*)lhs)->key > ((const ImGuiStoragePair*)rhs)->key) return +1; @@ -1902,8 +1958,7 @@ void ImGuiStorage::BuildSortByKey() return 0; } }; - if (Data.Size > 1) - ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairCompareByID); + ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairComparerByID); } int ImGuiStorage::GetInt(ImGuiID key, int default_val) const @@ -2195,9 +2250,10 @@ static bool GetSkipItemForListClipping() return (g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems); } -// Helper to calculate coarse clipping of large list of evenly sized items. -// NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern. -// NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +// Legacy helper to calculate coarse clipping of large list of evenly sized items. +// This legacy API is not ideal because it assume we will return a single contiguous rectangle. +// Prefer using ImGuiListClipper which can returns non-contiguous ranges. void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end) { ImGuiContext& g = *GImGui; @@ -2215,21 +2271,24 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items return; } - // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect - ImRect unclipped_rect = window->ClipRect; - if (g.NavMoveRequest) - unclipped_rect.Add(g.NavScoringRect); + // We create the union of the ClipRect and the scoring rect which at worst should be 1 page away from ClipRect + // We don't include g.NavId's rectangle in there (unless g.NavJustMovedToId is set) because the rectangle enlargement can get costly. + ImRect rect = window->ClipRect; + if (g.NavMoveScoringItems) + rect.Add(g.NavScoringNoClipRect); if (g.NavJustMovedToId && window->NavLastIds[0] == g.NavJustMovedToId) - unclipped_rect.Add(ImRect(window->Pos + window->NavRectRel[0].Min, window->Pos + window->NavRectRel[0].Max)); + rect.Add(WindowRectRelToAbs(window, window->NavRectRel[0])); // Could store and use NavJustMovedToRectRel const ImVec2 pos = window->DC.CursorPos; - int start = (int)((unclipped_rect.Min.y - pos.y) / items_height); - int end = (int)((unclipped_rect.Max.y - pos.y) / items_height); + int start = (int)((rect.Min.y - pos.y) / items_height); + int end = (int)((rect.Max.y - pos.y) / items_height); // When performing a navigation request, ensure we have one item extra in the direction we are moving to - if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up) + // FIXME: Verify this works with tabbing + const bool is_nav_request = (g.NavMoveScoringItems && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav); + if (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up) start--; - if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down) + if (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down) end++; start = ImClamp(start, 0, items_count); @@ -2237,17 +2296,42 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items *out_items_display_start = start; *out_items_display_end = end; } +#endif -static void SetCursorPosYAndSetupForPrevLine(float pos_y, float line_height) +static void ImGuiListClipper_SortAndFuseRanges(ImVector& ranges, int offset = 0) +{ + if (ranges.Size - offset <= 1) + return; + + // Helper to order ranges and fuse them together if possible (bubble sort is fine as we are only sorting 2-3 entries) + for (int sort_end = ranges.Size - offset - 1; sort_end > 0; --sort_end) + for (int i = offset; i < sort_end + offset; ++i) + if (ranges[i].Min > ranges[i + 1].Min) + ImSwap(ranges[i], ranges[i + 1]); + + // Now fuse ranges together as much as possible. + for (int i = 1 + offset; i < ranges.Size; i++) + { + IM_ASSERT(!ranges[i].PosToIndexConvert && !ranges[i - 1].PosToIndexConvert); + if (ranges[i - 1].Max < ranges[i].Min) + continue; + ranges[i - 1].Min = ImMin(ranges[i - 1].Min, ranges[i].Min); + ranges[i - 1].Max = ImMax(ranges[i - 1].Max, ranges[i].Max); + ranges.erase(ranges.Data + i); + i--; + } +} + +static void ImGuiListClipper_SeekCursorAndSetupPrevLine(float pos_y, float line_height) { // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor. // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue. - // The clipper should probably have a 4th step to display the last item in a regular manner. + // The clipper should probably have a final step to display the last item in a regular manner, maybe with an opt-out flag for data sets which may have costly seek? ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; float off_y = pos_y - window->DC.CursorPos.y; window->DC.CursorPos.y = pos_y; - window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y); + window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y - g.Style.ItemSpacing.y); window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height; // Setting those fields so that SetScrollHereY() can properly function after the end of our clipper usage. window->DC.PrevLineSize.y = (line_height - g.Style.ItemSpacing.y); // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list. if (ImGuiOldColumns* columns = window->DC.CurrentColumns) @@ -2263,6 +2347,15 @@ static void SetCursorPosYAndSetupForPrevLine(float pos_y, float line_height) } } +static void ImGuiListClipper_SeekCursorForItem(ImGuiListClipper* clipper, int item_n) +{ + // StartPosY starts from ItemsFrozen hence the subtraction + // Perform the add and multiply with double to allow seeking through larger ranges + ImGuiListClipperData* data = (ImGuiListClipperData*)clipper->TempData; + float pos_y = (float)((double)clipper->StartPosY + data->LossynessOffset + (double)(item_n - data->ItemsFrozen) * clipper->ItemsHeight); + ImGuiListClipper_SeekCursorAndSetupPrevLine(pos_y, clipper->ItemsHeight); +} + ImGuiListClipper::ImGuiListClipper() { memset(this, 0, sizeof(*this)); @@ -2271,7 +2364,7 @@ ImGuiListClipper::ImGuiListClipper() ImGuiListClipper::~ImGuiListClipper() { - IM_ASSERT(ItemsCount == -1 && "Forgot to call End(), or to Step() until false?"); + End(); } // Use case A: Begin() called from constructor with items_height<0, then called again from Step() in StepNo 1 @@ -2289,28 +2382,54 @@ void ImGuiListClipper::Begin(int items_count, float items_height) StartPosY = window->DC.CursorPos.y; ItemsHeight = items_height; ItemsCount = items_count; - ItemsFrozen = 0; - StepNo = 0; DisplayStart = -1; DisplayEnd = 0; + + // Acquire temporary buffer + if (++g.ClipperTempDataStacked > g.ClipperTempData.Size) + g.ClipperTempData.resize(g.ClipperTempDataStacked, ImGuiListClipperData()); + ImGuiListClipperData* data = &g.ClipperTempData[g.ClipperTempDataStacked - 1]; + data->Reset(this); + data->LossynessOffset = window->DC.CursorStartPosLossyness.y; + TempData = data; } void ImGuiListClipper::End() { - if (ItemsCount < 0) // Already ended - return; - - // In theory here we should assert that ImGui::GetCursorPosY() == StartPosY + DisplayEnd * ItemsHeight, but it feels saner to just seek at the end and not assert/crash the user. - if (ItemsCount < INT_MAX && DisplayStart >= 0) - SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight); + // In theory here we should assert that we are already at the right position, but it seems saner to just seek at the end and not assert/crash the user. + ImGuiContext& g = *GImGui; + if (ItemsCount >= 0 && ItemsCount < INT_MAX && DisplayStart >= 0) + ImGuiListClipper_SeekCursorForItem(this, ItemsCount); ItemsCount = -1; - StepNo = 3; + + // Restore temporary buffer and fix back pointers which may be invalidated when nesting + if (ImGuiListClipperData* data = (ImGuiListClipperData*)TempData) + { + IM_ASSERT(data->ListClipper == this); + data->StepNo = data->Ranges.Size; + if (--g.ClipperTempDataStacked > 0) + { + data = &g.ClipperTempData[g.ClipperTempDataStacked - 1]; + data->ListClipper->TempData = data; + } + TempData = NULL; + } +} + +void ImGuiListClipper::ForceDisplayRangeByIndices(int item_min, int item_max) +{ + ImGuiListClipperData* data = (ImGuiListClipperData*)TempData; + IM_ASSERT(DisplayStart < 0); // Only allowed after Begin() and if there has not been a specified range yet. + IM_ASSERT(item_min <= item_max); + if (item_min < item_max) + data->Ranges.push_back(ImGuiListClipperRange::FromIndices(item_min, item_max)); } bool ImGuiListClipper::Step() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; + ImGuiListClipperData* data = (ImGuiListClipperData*)TempData; ImGuiTable* table = g.CurrentTable; if (table && table->IsInsideRow) @@ -2318,95 +2437,117 @@ bool ImGuiListClipper::Step() // No items if (ItemsCount == 0 || GetSkipItemForListClipping()) + return (void)End(), false; + + // While we are in frozen row state, keep displaying items one by one, unclipped + // FIXME: Could be stored as a table-agnostic state. + if (data->StepNo == 0 && table != NULL && !table->IsUnfrozenRows) { - End(); - return false; - } - - // Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height) - if (StepNo == 0) - { - // While we are in frozen row state, keep displaying items one by one, unclipped - // FIXME: Could be stored as a table-agnostic state. - if (table != NULL && !table->IsUnfrozenRows) - { - DisplayStart = ItemsFrozen; - DisplayEnd = ItemsFrozen + 1; - ItemsFrozen++; - return true; - } - - StartPosY = window->DC.CursorPos.y; - if (ItemsHeight <= 0.0f) - { - // Submit the first item so we can measure its height (generally it is 0..1) - DisplayStart = ItemsFrozen; - DisplayEnd = ItemsFrozen + 1; - StepNo = 1; - return true; - } - - // Already has item height (given by user in Begin): skip to calculating step - DisplayStart = DisplayEnd; - StepNo = 2; - } - - // Step 1: the clipper infer height from first element - if (StepNo == 1) - { - IM_ASSERT(ItemsHeight <= 0.0f); - if (table) - { - const float pos_y1 = table->RowPosY1; // Using this instead of StartPosY to handle clipper straddling the frozen row - const float pos_y2 = table->RowPosY2; // Using this instead of CursorPos.y to take account of tallest cell. - ItemsHeight = pos_y2 - pos_y1; - window->DC.CursorPos.y = pos_y2; - } - else - { - ItemsHeight = window->DC.CursorPos.y - StartPosY; - } - IM_ASSERT(ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!"); - StepNo = 2; - } - - // Reached end of list - if (DisplayEnd >= ItemsCount) - { - End(); - return false; - } - - // Step 2: calculate the actual range of elements to display, and position the cursor before the first element - if (StepNo == 2) - { - IM_ASSERT(ItemsHeight > 0.0f); - - int already_submitted = DisplayEnd; - ImGui::CalcListClipping(ItemsCount - already_submitted, ItemsHeight, &DisplayStart, &DisplayEnd); - DisplayStart += already_submitted; - DisplayEnd += already_submitted; - - // Seek cursor - if (DisplayStart > already_submitted) - SetCursorPosYAndSetupForPrevLine(StartPosY + (DisplayStart - ItemsFrozen) * ItemsHeight, ItemsHeight); - - StepNo = 3; + DisplayStart = data->ItemsFrozen; + DisplayEnd = data->ItemsFrozen + 1; + if (DisplayStart >= ItemsCount) + return (void)End(), false; + data->ItemsFrozen++; return true; } - // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), - // Advance the cursor to the end of the list and then returns 'false' to end the loop. - if (StepNo == 3) + // Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height) + bool calc_clipping = false; + if (data->StepNo == 0) { - // Seek cursor - if (ItemsCount < INT_MAX) - SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight); // advance cursor - ItemsCount = -1; - return false; + StartPosY = window->DC.CursorPos.y; + if (ItemsHeight <= 0.0f) + { + // Submit the first item (or range) so we can measure its height (generally the first range is 0..1) + data->Ranges.push_front(ImGuiListClipperRange::FromIndices(data->ItemsFrozen, data->ItemsFrozen + 1)); + DisplayStart = ImMax(data->Ranges[0].Min, data->ItemsFrozen); + DisplayEnd = ImMin(data->Ranges[0].Max, ItemsCount); + if (DisplayStart == DisplayEnd) + return (void)End(), false; + data->StepNo = 1; + return true; + } + calc_clipping = true; // If on the first step with known item height, calculate clipping. } - IM_ASSERT(0); + // Step 1: Let the clipper infer height from first range + if (ItemsHeight <= 0.0f) + { + IM_ASSERT(data->StepNo == 1); + if (table) + IM_ASSERT(table->RowPosY1 == StartPosY && table->RowPosY2 == window->DC.CursorPos.y); + + ItemsHeight = (window->DC.CursorPos.y - StartPosY) / (float)(DisplayEnd - DisplayStart); + bool affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision(StartPosY) || ImIsFloatAboveGuaranteedIntegerPrecision(window->DC.CursorPos.y); + if (affected_by_floating_point_precision) + ItemsHeight = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y; // FIXME: Technically wouldn't allow multi-line entries. + + IM_ASSERT(ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!"); + calc_clipping = true; // If item height had to be calculated, calculate clipping afterwards. + } + + // Step 0 or 1: Calculate the actual ranges of visible elements. + const int already_submitted = DisplayEnd; + if (calc_clipping) + { + if (g.LogEnabled) + { + // If logging is active, do not perform any clipping + data->Ranges.push_back(ImGuiListClipperRange::FromIndices(0, ItemsCount)); + } + else + { + // Add range selected to be included for navigation + const bool is_nav_request = (g.NavMoveScoringItems && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav); + if (is_nav_request) + data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringNoClipRect.Min.y, g.NavScoringNoClipRect.Max.y, 0, 0)); + if (is_nav_request && (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) && g.NavTabbingDir == -1) + data->Ranges.push_back(ImGuiListClipperRange::FromIndices(ItemsCount - 1, ItemsCount)); + + // Add focused/active item + ImRect nav_rect_abs = ImGui::WindowRectRelToAbs(window, window->NavRectRel[0]); + if (g.NavId != 0 && window->NavLastIds[0] == g.NavId) + data->Ranges.push_back(ImGuiListClipperRange::FromPositions(nav_rect_abs.Min.y, nav_rect_abs.Max.y, 0, 0)); + + // Add visible range + const int off_min = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up) ? -1 : 0; + const int off_max = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down) ? 1 : 0; + data->Ranges.push_back(ImGuiListClipperRange::FromPositions(window->ClipRect.Min.y, window->ClipRect.Max.y, off_min, off_max)); + } + + // Convert position ranges to item index ranges + // - Very important: when a starting position is after our maximum item, we set Min to (ItemsCount - 1). This allows us to handle most forms of wrapping. + // - Due to how Selectable extra padding they tend to be "unaligned" with exact unit in the item list, + // which with the flooring/ceiling tend to lead to 2 items instead of one being submitted. + for (int i = 0; i < data->Ranges.Size; i++) + if (data->Ranges[i].PosToIndexConvert) + { + int m1 = (int)(((double)data->Ranges[i].Min - window->DC.CursorPos.y - data->LossynessOffset) / ItemsHeight); + int m2 = (int)((((double)data->Ranges[i].Max - window->DC.CursorPos.y - data->LossynessOffset) / ItemsHeight) + 0.999999f); + data->Ranges[i].Min = ImClamp(already_submitted + m1 + data->Ranges[i].PosToIndexOffsetMin, already_submitted, ItemsCount - 1); + data->Ranges[i].Max = ImClamp(already_submitted + m2 + data->Ranges[i].PosToIndexOffsetMax, data->Ranges[i].Min + 1, ItemsCount); + data->Ranges[i].PosToIndexConvert = false; + } + ImGuiListClipper_SortAndFuseRanges(data->Ranges, data->StepNo); + } + + // Step 0+ (if item height is given in advance) or 1+: Display the next range in line. + if (data->StepNo < data->Ranges.Size) + { + DisplayStart = ImMax(data->Ranges[data->StepNo].Min, already_submitted); + DisplayEnd = ImMin(data->Ranges[data->StepNo].Max, ItemsCount); + if (DisplayStart > already_submitted) //-V1051 + ImGuiListClipper_SeekCursorForItem(this, DisplayStart); + data->StepNo++; + return true; + } + + // After the last step: Let the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), + // Advance the cursor to the end of the list and then returns 'false' to end the loop. + if (ItemsCount < INT_MAX) + ImGuiListClipper_SeekCursorForItem(this, ItemsCount); + ItemsCount = -1; + return false; } @@ -2496,6 +2637,7 @@ struct ImGuiStyleVarInfo static const ImGuiStyleVarInfo GStyleVarInfo[] = { { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, DisabledAlpha) }, // ImGuiStyleVar_DisabledAlpha { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize @@ -2773,7 +2915,7 @@ void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, con int ellipsis_char_count = 1; if (ellipsis_char == (ImWchar)-1) { - ellipsis_char = (ImWchar)'.'; + ellipsis_char = font->DotChar; ellipsis_char_count = 3; } const ImFontGlyph* glyph = font->FindGlyph(ellipsis_char); @@ -2914,8 +3056,7 @@ ImGuiWindow::~ImGuiWindow() { IM_ASSERT(DrawList == &DrawListInst); IM_DELETE(Name); - for (int i = 0; i != ColumnsStorage.Size; i++) - ColumnsStorage[i].~ImGuiOldColumns(); + ColumnsStorage.clear_destruct(); } ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) @@ -2923,10 +3064,9 @@ ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) ImGuiID seed = IDStack.back(); ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); ImGui::KeepAliveID(id); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end); return id; } @@ -2935,10 +3075,9 @@ ImGuiID ImGuiWindow::GetID(const void* ptr) ImGuiID seed = IDStack.back(); ImGuiID id = ImHashData(&ptr, sizeof(void*), seed); ImGui::KeepAliveID(id); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL); return id; } @@ -2947,10 +3086,9 @@ ImGuiID ImGuiWindow::GetID(int n) ImGuiID seed = IDStack.back(); ImGuiID id = ImHashData(&n, sizeof(n), seed); ImGui::KeepAliveID(id); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL); return id; } @@ -2958,10 +3096,9 @@ ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end) { ImGuiID seed = IDStack.back(); ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end); return id; } @@ -2969,10 +3106,9 @@ ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr) { ImGuiID seed = IDStack.back(); ImGuiID id = ImHashData(&ptr, sizeof(void*), seed); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL); return id; } @@ -2980,10 +3116,9 @@ ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n) { ImGuiID seed = IDStack.back(); ImGuiID id = ImHashData(&n, sizeof(n), seed); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL); return id; } @@ -2991,7 +3126,7 @@ ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n) ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs) { ImGuiID seed = IDStack.back(); - const int r_rel[4] = { (int)(r_abs.Min.x - Pos.x), (int)(r_abs.Min.y - Pos.y), (int)(r_abs.Max.x - Pos.x), (int)(r_abs.Max.y - Pos.y) }; + ImRect r_rel = ImGui::WindowRectAbsToRel(this, r_abs); ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed); ImGui::KeepAliveID(id); return id; @@ -3064,7 +3199,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) if (id) { g.ActiveIdIsAlive = id; - g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; + g.ActiveIdSource = (g.NavActivateId == id || g.NavActivateInputId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; } // Clear declaration of inputs claimed by the widget @@ -3115,7 +3250,7 @@ void ImGui::MarkItemEdited(ImGuiID id) //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id); g.ActiveIdHasBeenEditedThisFrame = true; g.ActiveIdHasBeenEditedBefore = true; - g.CurrentWindow->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited; } static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags) @@ -3145,41 +3280,49 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (g.NavDisableMouseHover && !g.NavDisableHighlight) - return IsItemFocused(); + { + if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled)) + return false; + if (!IsItemFocused()) + return false; + } + else + { + // Test for bounding box overlap, as updated as ItemAdd() + ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags; + if (!(status_flags & ImGuiItemStatusFlags_HoveredRect)) + return false; + IM_ASSERT((flags & (ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy)) == 0); // Flags not supported by this function - // Test for bounding box overlap, as updated as ItemAdd() - ImGuiItemStatusFlags status_flags = window->DC.LastItemStatusFlags; - if (!(status_flags & ImGuiItemStatusFlags_HoveredRect)) - return false; - IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0); // Flags not supported by this function + // Test if we are hovering the right window (our window could be behind another window) + // [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851) + // [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable + // to use IsItemHovered() after EndChild() itself. Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was + // the test that has been running for a long while. + if (g.HoveredWindow != window && (status_flags & ImGuiItemStatusFlags_HoveredWindow) == 0) + if ((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0) + return false; - // Test if we are hovering the right window (our window could be behind another window) - // [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851) - // [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable - // to use IsItemHovered() after EndChild() itself. Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was - // the test that has been running for a long while. - if (g.HoveredWindow != window && (status_flags & ImGuiItemStatusFlags_HoveredWindow) == 0) - if ((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0) + // Test if another item is active (e.g. being dragged) + if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0) + if (g.ActiveId != 0 && g.ActiveId != g.LastItemData.ID && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId) + return false; + + // Test if interactions on this window are blocked by an active popup or modal. + // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here. + if (!IsWindowContentHoverable(window, flags)) return false; - // Test if another item is active (e.g. being dragged) - if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0) - if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId) + // Test if the item is disabled + if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled)) return false; - // Test if interactions on this window are blocked by an active popup or modal. - // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here. - if (!IsWindowContentHoverable(window, flags)) - return false; + // Special handling for calling after Begin() which represent the title bar or tab. + // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case. + if (g.LastItemData.ID == window->MoveId && window->WriteAccessed) + return false; + } - // Test if the item is disabled - if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled)) - return false; - - // Special handling for calling after Begin() which represent the title bar or tab. - // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case. - if (window->DC.LastItemId == window->MoveId && window->WriteAccessed) - return false; return true; } @@ -3199,7 +3342,7 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) return false; if (g.NavDisableMouseHover) return false; - if (!IsWindowContentHoverable(window, ImGuiHoveredFlags_None) || (window->DC.ItemFlags & ImGuiItemFlags_Disabled)) + if (!IsWindowContentHoverable(window, ImGuiHoveredFlags_None)) { g.HoveredIdDisabled = true; return false; @@ -3208,9 +3351,21 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) // We exceptionally allow this function to be called with id==0 to allow using it for easy high-level // hover test in widgets code. We could also decide to split this function is two. if (id != 0) - { SetHoveredID(id); + // When disabled we'll return false but still set HoveredId + ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.InFlags : g.CurrentItemFlags); + if (item_flags & ImGuiItemFlags_Disabled) + { + // Release active id if turning disabled + if (g.ActiveId == id) + ClearActiveID(); + g.HoveredIdDisabled = true; + return false; + } + + if (id != 0) + { // [DEBUG] Item Picker tool! // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making // the cost of this tool near-zero. We can get slightly better call-stack and support picking non-hovered @@ -3225,68 +3380,26 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) return true; } -bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged) +bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (!bb.Overlaps(window->ClipRect)) if (id == 0 || (id != g.ActiveId && id != g.NavId)) - if (clip_even_when_logged || !g.LogEnabled) + if (!g.LogEnabled) return true; return false; } // This is also inlined in ItemAdd() // Note: if ImGuiItemStatusFlags_HasDisplayRect is set, user needs to set window->DC.LastItemDisplayRect! -void ImGui::SetLastItemData(ImGuiWindow* window, ImGuiID item_id, ImGuiItemStatusFlags item_flags, const ImRect& item_rect) -{ - window->DC.LastItemId = item_id; - window->DC.LastItemStatusFlags = item_flags; - window->DC.LastItemRect = item_rect; -} - -// Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out. -bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id) +void ImGui::SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags item_flags, const ImRect& item_rect) { ImGuiContext& g = *GImGui; - - // Increment counters - const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0; - window->DC.FocusCounterRegular++; - if (is_tab_stop) - window->DC.FocusCounterTabStop++; - - // Process TAB/Shift-TAB to tab *OUT* of the currently focused item. - // (Note that we can always TAB out of a widget that doesn't allow tabbing in) - if (g.ActiveId == id && g.TabFocusPressed && !IsActiveIdUsingKey(ImGuiKey_Tab) && g.TabFocusRequestNextWindow == NULL) - { - g.TabFocusRequestNextWindow = window; - g.TabFocusRequestNextCounterTabStop = window->DC.FocusCounterTabStop + (g.IO.KeyShift ? (is_tab_stop ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items. - } - - // Handle focus requests - if (g.TabFocusRequestCurrWindow == window) - { - if (window->DC.FocusCounterRegular == g.TabFocusRequestCurrCounterRegular) - return true; - if (is_tab_stop && window->DC.FocusCounterTabStop == g.TabFocusRequestCurrCounterTabStop) - { - g.NavJustTabbedId = id; - return true; - } - - // If another item is about to be focused, we clear our own active id - if (g.ActiveId == id) - ClearActiveID(); - } - - return false; -} - -void ImGui::FocusableItemUnregister(ImGuiWindow* window) -{ - window->DC.FocusCounterRegular--; - window->DC.FocusCounterTabStop--; + g.LastItemData.ID = item_id; + g.LastItemData.InFlags = in_flags; + g.LastItemData.StatusFlags = item_flags; + g.LastItemData.Rect = item_rect; } float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x) @@ -3512,8 +3625,9 @@ void ImGui::StartMouseMovingWindow(ImGuiWindow* window) FocusWindow(window); SetActiveID(window->MoveId, window); g.NavDisableHighlight = true; + g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindow->Pos; g.ActiveIdNoClearOnFocusLoss = true; - g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos; + SetActiveIdUsingNavAndKeys(); bool can_move_window = true; if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove)) @@ -3549,8 +3663,8 @@ void ImGui::UpdateMouseMovingWindowNewFrame() } else { - ClearActiveID(); g.MovingWindow = NULL; + ClearActiveID(); } } else @@ -3614,7 +3728,7 @@ void ImGui::UpdateMouseMovingWindowEndFrame() // Find the top-most window between HoveredWindow and the top-most Modal Window. // This is where we can trim the popup stack. ImGuiWindow* modal = GetTopMostPopupModal(); - bool hovered_window_above_modal = g.HoveredWindow && IsWindowAbove(g.HoveredWindow, modal); + bool hovered_window_above_modal = g.HoveredWindow && (modal == NULL || IsWindowAbove(g.HoveredWindow, modal)); ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true); } } @@ -3630,13 +3744,15 @@ static void ImGui::UpdateMouseInputs() // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well) if (IsMousePosValid(&g.IO.MousePos)) - g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos); + g.IO.MousePos = g.MouseLastValidPos = ImFloor(g.IO.MousePos); // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev)) g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev; else g.IO.MouseDelta = ImVec2(0.0f, 0.0f); + + // If mouse moved we re-enable mouse hovering in case it was disabled by gamepad/keyboard. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true. if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f) g.NavDisableMouseHover = false; @@ -3644,25 +3760,26 @@ static void ImGui::UpdateMouseInputs() for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) { g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f; + g.IO.MouseClickedCount[i] = 0; // Will be filled below g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f; g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i]; g.IO.MouseDownDuration[i] = g.IO.MouseDown[i] ? (g.IO.MouseDownDuration[i] < 0.0f ? 0.0f : g.IO.MouseDownDuration[i] + g.IO.DeltaTime) : -1.0f; - g.IO.MouseDoubleClicked[i] = false; if (g.IO.MouseClicked[i]) { + bool is_repeated_click = false; if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime) { ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist) - g.IO.MouseDoubleClicked[i] = true; - g.IO.MouseClickedTime[i] = -g.IO.MouseDoubleClickTime * 2.0f; // Mark as "old enough" so the third click isn't turned into a double-click + is_repeated_click = true; } + if (is_repeated_click) + g.IO.MouseClickedLastCount[i]++; else - { - g.IO.MouseClickedTime[i] = g.Time; - } + g.IO.MouseClickedLastCount[i] = 1; + g.IO.MouseClickedTime[i] = g.Time; g.IO.MouseClickedPos[i] = g.IO.MousePos; - g.IO.MouseDownWasDoubleClick[i] = g.IO.MouseDoubleClicked[i]; + g.IO.MouseClickedCount[i] = g.IO.MouseClickedLastCount[i]; g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f); g.IO.MouseDragMaxDistanceSqr[i] = 0.0f; } @@ -3674,9 +3791,12 @@ static void ImGui::UpdateMouseInputs() g.IO.MouseDragMaxDistanceAbs[i].x = ImMax(g.IO.MouseDragMaxDistanceAbs[i].x, delta_from_click_pos.x < 0.0f ? -delta_from_click_pos.x : delta_from_click_pos.x); g.IO.MouseDragMaxDistanceAbs[i].y = ImMax(g.IO.MouseDragMaxDistanceAbs[i].y, delta_from_click_pos.y < 0.0f ? -delta_from_click_pos.y : delta_from_click_pos.y); } - if (!g.IO.MouseDown[i] && !g.IO.MouseReleased[i]) - g.IO.MouseDownWasDoubleClick[i] = false; - if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation + + // We provide io.MouseDoubleClicked[] as a legacy service + g.IO.MouseDoubleClicked[i] = (g.IO.MouseClickedCount[i] == 2); + + // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation + if (g.IO.MouseClicked[i]) g.NavDisableMouseHover = false; } } @@ -3776,46 +3896,11 @@ void ImGui::UpdateMouseWheel() } } -void ImGui::UpdateTabFocus() -{ - ImGuiContext& g = *GImGui; - - // Pressing TAB activate widget focus - g.TabFocusPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab)); - if (g.ActiveId == 0 && g.TabFocusPressed) - { - // Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also - // manipulate the Next fields even, even though they will be turned into Curr fields by the code below. - g.TabFocusRequestNextWindow = g.NavWindow; - g.TabFocusRequestNextCounterRegular = INT_MAX; - if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX) - g.TabFocusRequestNextCounterTabStop = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1); - else - g.TabFocusRequestNextCounterTabStop = g.IO.KeyShift ? -1 : 0; - } - - // Turn queued focus request into current one - g.TabFocusRequestCurrWindow = NULL; - g.TabFocusRequestCurrCounterRegular = g.TabFocusRequestCurrCounterTabStop = INT_MAX; - if (g.TabFocusRequestNextWindow != NULL) - { - ImGuiWindow* window = g.TabFocusRequestNextWindow; - g.TabFocusRequestCurrWindow = window; - if (g.TabFocusRequestNextCounterRegular != INT_MAX && window->DC.FocusCounterRegular != -1) - g.TabFocusRequestCurrCounterRegular = ImModPositive(g.TabFocusRequestNextCounterRegular, window->DC.FocusCounterRegular + 1); - if (g.TabFocusRequestNextCounterTabStop != INT_MAX && window->DC.FocusCounterTabStop != -1) - g.TabFocusRequestCurrCounterTabStop = ImModPositive(g.TabFocusRequestNextCounterTabStop, window->DC.FocusCounterTabStop + 1); - g.TabFocusRequestNextWindow = NULL; - g.TabFocusRequestNextCounterRegular = g.TabFocusRequestNextCounterTabStop = INT_MAX; - } - - g.NavIdTabCounter = INT_MAX; -} - // The reason this is exposed in imgui_internal.h is: on touch-based system that don't have hovering, we want to dispatch inputs to the right target (imgui vs imgui+app) void ImGui::UpdateHoveredWindowAndCaptureFlags() { ImGuiContext& g = *GImGui; + ImGuiIO& io = g.IO; g.WindowsHoverPadding = ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_HOVER_PADDING, WINDOWS_HOVER_PADDING)); // Find the window hovered by mouse: @@ -3827,52 +3912,65 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() // Modal windows prevents mouse from hovering behind them. ImGuiWindow* modal_window = GetTopMostPopupModal(); - if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindow, modal_window)) + if (modal_window && g.HoveredWindow && !IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, modal_window)) clear_hovered_windows = true; // Disabled mouse? - if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse) + if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) clear_hovered_windows = true; - // We track click ownership. When clicked outside of a window the click is owned by the application and won't report hovering nor request capture even while dragging over our windows afterward. - int mouse_earliest_button_down = -1; + // We track click ownership. When clicked outside of a window the click is owned by the application and + // won't report hovering nor request capture even while dragging over our windows afterward. + const bool has_open_popup = (g.OpenPopupStack.Size > 0); + const bool has_open_modal = (modal_window != NULL); + int mouse_earliest_down = -1; bool mouse_any_down = false; - for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) + for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) { - if (g.IO.MouseClicked[i]) - g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (g.OpenPopupStack.Size > 0); - mouse_any_down |= g.IO.MouseDown[i]; - if (g.IO.MouseDown[i]) - if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down]) - mouse_earliest_button_down = i; + if (io.MouseClicked[i]) + { + io.MouseDownOwned[i] = (g.HoveredWindow != NULL) || has_open_popup; + io.MouseDownOwnedUnlessPopupClose[i] = (g.HoveredWindow != NULL) || has_open_modal; + } + mouse_any_down |= io.MouseDown[i]; + if (io.MouseDown[i]) + if (mouse_earliest_down == -1 || io.MouseClickedTime[i] < io.MouseClickedTime[mouse_earliest_down]) + mouse_earliest_down = i; } - const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down]; + const bool mouse_avail = (mouse_earliest_down == -1) || io.MouseDownOwned[mouse_earliest_down]; + const bool mouse_avail_unless_popup_close = (mouse_earliest_down == -1) || io.MouseDownOwnedUnlessPopupClose[mouse_earliest_down]; // If mouse was first clicked outside of ImGui bounds we also cancel out hovering. // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02) const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0; - if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload) + if (!mouse_avail && !mouse_dragging_extern_payload) clear_hovered_windows = true; if (clear_hovered_windows) g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL; - // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to Dear ImGui + app) + // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to Dear ImGui only, false = dispatch mouse to Dear ImGui + underlying app) + // Update io.WantCaptureMouseAllowPopupClose (experimental) to give a chance for app to react to popup closure with a drag if (g.WantCaptureMouseNextFrame != -1) - g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0); + { + io.WantCaptureMouse = io.WantCaptureMouseUnlessPopupClose = (g.WantCaptureMouseNextFrame != 0); + } else - g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (g.OpenPopupStack.Size > 0); + { + io.WantCaptureMouse = (mouse_avail && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_popup; + io.WantCaptureMouseUnlessPopupClose = (mouse_avail_unless_popup_close && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_modal; + } - // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to Dear ImGui + app) + // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to Dear ImGui only, false = dispatch keyboard info to Dear ImGui + underlying app) if (g.WantCaptureKeyboardNextFrame != -1) - g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0); + io.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0); else - g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL); - if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)) - g.IO.WantCaptureKeyboard = true; + io.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL); + if (io.NavActive && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)) + io.WantCaptureKeyboard = true; // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible - g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false; + io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false; } ImGuiKeyModFlags ImGui::GetMergedKeyModFlags() @@ -3916,7 +4014,8 @@ void ImGui::NewFrame() g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx]; g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime; g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame); - g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX; + g.FramerateSecPerFrameCount = ImMin(g.FramerateSecPerFrameCount + 1, IM_ARRAYSIZE(g.FramerateSecPerFrame)); + g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)g.FramerateSecPerFrameCount)) : FLT_MAX; UpdateViewportsNewFrame(); @@ -3997,6 +4096,18 @@ void ImGui::NewFrame() g.DragDropWithinTarget = false; g.DragDropHoldJustPressedId = 0; + // Close popups on focus lost (currently wip/opt-in) + //if (g.IO.AppFocusLost) + // ClosePopupsExceptModals(); + + // Clear buttons state when focus is lost + // (this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle) + if (g.IO.AppFocusLost) + { + g.IO.ClearInputKeys(); + g.IO.AppFocusLost = false; + } + // Update keyboard input state // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools g.IO.KeyMods = GetMergedKeyModFlags(); @@ -4030,9 +4141,6 @@ void ImGui::NewFrame() // Mouse wheel scrolling, scale UpdateMouseWheel(); - // Update legacy TAB focus - UpdateTabFocus(); - // Mark all windows as not visible and compact unused memory. IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size); const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer; @@ -4053,6 +4161,9 @@ void ImGui::NewFrame() for (int i = 0; i < g.TablesLastTimeActive.Size; i++) if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time) TableGcCompactTransientBuffers(g.Tables.GetByIndex(i)); + for (int i = 0; i < g.TablesTempData.Size; i++) + if (g.TablesTempData[i].LastTimeActive >= 0.0f && g.TablesTempData[i].LastTimeActive < memory_compact_start_time) + TableGcCompactTransientBuffers(&g.TablesTempData[i]); if (g.GcCompactAll) GcCompactTransientMiscBuffers(); g.GcCompactAll = false; @@ -4066,12 +4177,12 @@ void ImGui::NewFrame() g.CurrentWindowStack.resize(0); g.BeginPopupStack.resize(0); g.ItemFlagsStack.resize(0); - g.ItemFlagsStack.push_back(ImGuiItemFlags_Default_); + g.ItemFlagsStack.push_back(ImGuiItemFlags_None); g.GroupStack.resize(0); - ClosePopupsOverWindow(g.NavWindow, false); - // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack. + // [DEBUG] Update debug features UpdateDebugToolItemPicker(); + UpdateDebugToolStackQueries(); // Create implicit/fallback window - which we will only render it if the user has added something to it. // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags. @@ -4084,31 +4195,6 @@ void ImGui::NewFrame() CallContextHooks(&g, ImGuiContextHookType_NewFramePost); } -// [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack. -void ImGui::UpdateDebugToolItemPicker() -{ - ImGuiContext& g = *GImGui; - g.DebugItemPickerBreakId = 0; - if (g.DebugItemPickerActive) - { - const ImGuiID hovered_id = g.HoveredIdPreviousFrame; - ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); - if (ImGui::IsKeyPressedMap(ImGuiKey_Escape)) - g.DebugItemPickerActive = false; - if (ImGui::IsMouseClicked(0) && hovered_id) - { - g.DebugItemPickerBreakId = hovered_id; - g.DebugItemPickerActive = false; - } - ImGui::SetNextWindowBgAlpha(0.60f); - ImGui::BeginTooltip(); - ImGui::Text("HoveredId: 0x%08X", hovered_id); - ImGui::Text("Press ESC to abort picking."); - ImGui::TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!"); - ImGui::EndTooltip(); - } -} - void ImGui::Initialize(ImGuiContext* context) { ImGuiContext& g = *context; @@ -4127,17 +4213,15 @@ void ImGui::Initialize(ImGuiContext* context) g.SettingsHandlers.push_back(ini_handler); } -#ifdef IMGUI_HAS_TABLE // Add .ini handle for ImGuiTable type TableSettingsInstallHandler(context); -#endif // #ifdef IMGUI_HAS_TABLE // Create default viewport ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)(); g.Viewports.push_back(viewport); #ifdef IMGUI_HAS_DOCK -#endif // #ifdef IMGUI_HAS_DOCK +#endif g.Initialized = true; } @@ -4170,9 +4254,7 @@ void ImGui::Shutdown(ImGuiContext* context) CallContextHooks(&g, ImGuiContextHookType_Shutdown); // Clear everything else - for (int i = 0; i < g.Windows.Size; i++) - IM_DELETE(g.Windows[i]); - g.Windows.clear(); + g.Windows.clear_delete(); g.WindowsFocusOrder.clear(); g.WindowsTempSortBuffer.clear(); g.CurrentWindow = NULL; @@ -4188,16 +4270,16 @@ void ImGui::Shutdown(ImGuiContext* context) g.OpenPopupStack.clear(); g.BeginPopupStack.clear(); - for (int i = 0; i < g.Viewports.Size; i++) - IM_DELETE(g.Viewports[i]); - g.Viewports.clear(); + g.Viewports.clear_delete(); g.TabBars.Clear(); g.CurrentTabBarStack.clear(); g.ShrinkWidthBuffer.clear(); + g.ClipperTempData.clear_destruct(); + g.Tables.Clear(); - g.CurrentTableStack.clear(); + g.TablesTempData.clear_destruct(); g.DrawChannelsTempMergeBuffer.clear(); g.ClipboardHandlerData.clear(); @@ -4238,8 +4320,7 @@ static void AddWindowToSortBuffer(ImVector* out_sorted_windows, Im if (window->Active) { int count = window->DC.ChildWindows.Size; - if (count > 1) - ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer); + ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer); for (int i = 0; i < count; i++) { ImGuiWindow* child = window->DC.ChildWindows[i]; @@ -4299,11 +4380,15 @@ static void AddWindowToDrawData(ImGuiWindow* window, int layer) } } -// Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu) -static void AddRootWindowToDrawData(ImGuiWindow* window) +static inline int GetWindowDisplayLayer(ImGuiWindow* window) { - int layer = (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0; - AddWindowToDrawData(window, layer); + return (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0; +} + +// Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu) +static inline void AddRootWindowToDrawData(ImGuiWindow* window) +{ + AddWindowToDrawData(window, GetWindowDisplayLayer(window)); } void ImDrawDataBuilder::FlattenIntoSingleLayer() @@ -4362,6 +4447,84 @@ void ImGui::PopClipRect() window->ClipRect = window->DrawList->_ClipRectStack.back(); } +static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + ImGuiViewportP* viewport = (ImGuiViewportP*)GetMainViewport(); + ImRect viewport_rect = viewport->GetMainRect(); + + // Draw behind window by moving the draw command at the FRONT of the draw list + { + // We've already called AddWindowToDrawData() which called DrawList->ChannelsMerge() on DockNodeHost windows, + // and draw list have been trimmed already, hence the explicit recreation of a draw command if missing. + ImDrawList* draw_list = window->RootWindow->DrawList; + if (draw_list->CmdBuffer.Size == 0) + draw_list->AddDrawCmd(); + draw_list->PushClipRect(viewport_rect.Min - ImVec2(1, 1), viewport_rect.Max + ImVec2(1, 1), false); // Ensure ImDrawCmd are not merged + draw_list->AddRectFilled(viewport_rect.Min, viewport_rect.Max, col); + ImDrawCmd cmd = draw_list->CmdBuffer.back(); + IM_ASSERT(cmd.ElemCount == 6); + draw_list->CmdBuffer.pop_back(); + draw_list->CmdBuffer.push_front(cmd); + draw_list->PopClipRect(); + } +} + +ImGuiWindow* ImGui::FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* parent_window) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* bottom_most_visible_window = parent_window; + for (int i = FindWindowDisplayIndex(parent_window); i >= 0; i--) + { + ImGuiWindow* window = g.Windows[i]; + if (window->Flags & ImGuiWindowFlags_ChildWindow) + continue; + if (!IsWindowWithinBeginStackOf(window, parent_window)) + break; + if (IsWindowActiveAndVisible(window) && GetWindowDisplayLayer(window) <= GetWindowDisplayLayer(parent_window)) + bottom_most_visible_window = window; + } + return bottom_most_visible_window; +} + +static void ImGui::RenderDimmedBackgrounds() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* modal_window = GetTopMostAndVisiblePopupModal(); + const bool dim_bg_for_modal = (modal_window != NULL); + const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL && g.NavWindowingTargetAnim->Active); + if (!dim_bg_for_modal && !dim_bg_for_window_list) + return; + + if (dim_bg_for_modal) + { + // Draw dimming behind modal or a begin stack child, whichever comes first in draw order. + ImGuiWindow* dim_behind_window = FindBottomMostVisibleWindowWithinBeginStack(modal_window); + RenderDimmedBackgroundBehindWindow(dim_behind_window, GetColorU32(ImGuiCol_ModalWindowDimBg, g.DimBgRatio)); + } + else if (dim_bg_for_window_list) + { + // Draw dimming behind CTRL+Tab target window + RenderDimmedBackgroundBehindWindow(g.NavWindowingTargetAnim, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio)); + + // Draw border around CTRL+Tab target window + ImGuiWindow* window = g.NavWindowingTargetAnim; + ImGuiViewport* viewport = GetMainViewport(); + float distance = g.FontSize; + ImRect bb = window->Rect(); + bb.Expand(distance); + if (bb.GetWidth() >= viewport->Size.x && bb.GetHeight() >= viewport->Size.y) + bb.Expand(-distance - 1.0f); // If a window fits the entire viewport, adjust its highlight inward + if (window->DrawList->CmdBuffer.Size == 0) + window->DrawList->AddDrawCmd(); + window->DrawList->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size); + window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), window->WindowRounding, 0, 3.0f); + window->DrawList->PopClipRect(); + } +} + // This is normally called by Render(). You may want to call it directly if you want to avoid calling Render() but the gain will be very minimal. void ImGui::EndFrame() { @@ -4440,11 +4603,15 @@ void ImGui::EndFrame() // Clear Input data for next frame g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f; g.IO.InputQueueCharacters.resize(0); + g.IO.KeyModsPrev = g.IO.KeyMods; // doing it here is better than in NewFrame() as we'll tolerate backend writing to KeyMods. If we want to firmly disallow it we should detect it. memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs)); CallContextHooks(&g, ImGuiContextHookType_EndFramePost); } +// Prepare the data for rendering so you can call GetDrawData() +// (As with anything within the ImGui:: namspace this doesn't touch your GPU or graphics API at all: +// it is the role of the ImGui_ImplXXXX_RenderDrawData() function provided by the renderer backend) void ImGui::Render() { ImGuiContext& g = *GImGui; @@ -4452,6 +4619,7 @@ void ImGui::Render() if (g.FrameCountEnded != g.FrameCount) EndFrame(); + const bool first_render_of_frame = (g.FrameCountRendered != g.FrameCount); g.FrameCountRendered = g.FrameCount; g.IO.MetricsRenderWindows = 0; @@ -4466,6 +4634,10 @@ void ImGui::Render() AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport)); } + // Draw modal/window whitening backgrounds + if (first_render_of_frame) + RenderDimmedBackgrounds(); + // Add ImDrawList to render ImGuiWindow* windows_to_render_top_most[2]; windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL; @@ -4473,6 +4645,7 @@ void ImGui::Render() for (int n = 0; n != g.Windows.Size; n++) { ImGuiWindow* window = g.Windows[n]; + IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'" if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1]) AddRootWindowToDrawData(window); } @@ -4488,7 +4661,7 @@ void ImGui::Render() viewport->DrawDataBuilder.FlattenIntoSingleLayer(); // Draw software mouse cursor if requested by io.MouseDrawCursor flag - if (g.IO.MouseDrawCursor) + if (g.IO.MouseDrawCursor && first_render_of_frame) RenderMouseCursor(GetForegroundDrawList(viewport), g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48)); // Add foreground ImDrawList (for each active viewport) @@ -4550,6 +4723,7 @@ static void FindHoveredWindow() for (int i = g.Windows.Size - 1; i >= 0; i--) { ImGuiWindow* window = g.Windows[i]; + IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer. if (!window->Active || window->Hidden) continue; if (window->Flags & ImGuiWindowFlags_NoMouseInputs) @@ -4576,6 +4750,7 @@ static void FindHoveredWindow() if (hovered_window == NULL) hovered_window = window; + IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer. if (hovered_window_ignoring_moving_window == NULL && (!g.MovingWindow || window->RootWindow != g.MovingWindow->RootWindow)) hovered_window_ignoring_moving_window = window; if (hovered_window && hovered_window_ignoring_moving_window) @@ -4709,7 +4884,14 @@ bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button) { ImGuiContext& g = *GImGui; IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - return g.IO.MouseDoubleClicked[button]; + return g.IO.MouseClickedCount[button] == 2; +} + +int ImGui::GetMouseClickedCount(ImGuiMouseButton button) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + return g.IO.MouseClickedCount[button]; } // Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame. @@ -4815,10 +4997,7 @@ bool ImGui::IsItemActive() { ImGuiContext& g = *GImGui; if (g.ActiveId) - { - ImGuiWindow* window = g.CurrentWindow; - return g.ActiveId == window->DC.LastItemId; - } + return g.ActiveId == g.LastItemData.ID; return false; } @@ -4826,21 +5005,17 @@ bool ImGui::IsItemActivated() { ImGuiContext& g = *GImGui; if (g.ActiveId) - { - ImGuiWindow* window = g.CurrentWindow; - if (g.ActiveId == window->DC.LastItemId && g.ActiveIdPreviousFrame != window->DC.LastItemId) + if (g.ActiveId == g.LastItemData.ID && g.ActiveIdPreviousFrame != g.LastItemData.ID) return true; - } return false; } bool ImGui::IsItemDeactivated() { ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDeactivated) - return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Deactivated) != 0; - return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId); + if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDeactivated) + return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Deactivated) != 0; + return (g.ActiveIdPreviousFrame == g.LastItemData.ID && g.ActiveIdPreviousFrame != 0 && g.ActiveId != g.LastItemData.ID); } bool ImGui::IsItemDeactivatedAfterEdit() @@ -4853,9 +5028,7 @@ bool ImGui::IsItemDeactivatedAfterEdit() bool ImGui::IsItemFocused() { ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - if (g.NavId != window->DC.LastItemId || g.NavId == 0) + if (g.NavId != g.LastItemData.ID || g.NavId == 0) return false; return true; } @@ -4870,13 +5043,13 @@ bool ImGui::IsItemClicked(ImGuiMouseButton mouse_button) bool ImGui::IsItemToggledOpen() { ImGuiContext& g = *GImGui; - return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false; + return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false; } bool ImGui::IsItemToggledSelection() { ImGuiContext& g = *GImGui; - return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false; + return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false; } bool ImGui::IsAnyItemHovered() @@ -4899,14 +5072,14 @@ bool ImGui::IsAnyItemFocused() bool ImGui::IsItemVisible() { - ImGuiWindow* window = GetCurrentWindowRead(); - return window->ClipRect.Overlaps(window->DC.LastItemRect); + ImGuiContext& g = *GImGui; + return g.CurrentWindow->ClipRect.Overlaps(g.LastItemData.Rect); } bool ImGui::IsItemEdited() { - ImGuiWindow* window = GetCurrentWindowRead(); - return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Edited) != 0; + ImGuiContext& g = *GImGui; + return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Edited) != 0; } // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority. @@ -4914,7 +5087,7 @@ bool ImGui::IsItemEdited() void ImGui::SetItemAllowOverlap() { ImGuiContext& g = *GImGui; - ImGuiID id = g.CurrentWindow->DC.LastItemId; + ImGuiID id = g.LastItemData.ID; if (g.HoveredId == id) g.HoveredIdAllowOverlap = true; if (g.ActiveId == id) @@ -4924,29 +5097,39 @@ void ImGui::SetItemAllowOverlap() void ImGui::SetItemUsingMouseWheel() { ImGuiContext& g = *GImGui; - ImGuiID id = g.CurrentWindow->DC.LastItemId; + ImGuiID id = g.LastItemData.ID; if (g.HoveredId == id) g.HoveredIdUsingMouseWheel = true; if (g.ActiveId == id) g.ActiveIdUsingMouseWheel = true; } +void ImGui::SetActiveIdUsingNavAndKeys() +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.ActiveId != 0); + g.ActiveIdUsingNavDirMask = ~(ImU32)0; + g.ActiveIdUsingNavInputMask = ~(ImU32)0; + g.ActiveIdUsingKeyInputMask = ~(ImU64)0; + NavMoveRequestCancel(); +} + ImVec2 ImGui::GetItemRectMin() { - ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.LastItemRect.Min; + ImGuiContext& g = *GImGui; + return g.LastItemData.Rect.Min; } ImVec2 ImGui::GetItemRectMax() { - ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.LastItemRect.Max; + ImGuiContext& g = *GImGui; + return g.LastItemData.Rect.Max; } ImVec2 ImGui::GetItemRectSize() { - ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.LastItemRect.GetSize(); + ImGuiContext& g = *GImGui; + return g.LastItemData.Rect.GetSize(); } bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags) @@ -4989,7 +5172,7 @@ bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, b parent_window->DC.CursorPos = child_window->Pos; // Process navigation-in immediately so NavInit can run on first frame - if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll)) + if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavHasScroll)) { FocusWindow(child_window); NavInitWindow(child_window, false); @@ -5036,13 +5219,13 @@ void ImGui::EndChild() ImGuiWindow* parent_window = g.CurrentWindow; ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz); ItemSize(sz); - if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened)) + if ((window->DC.NavLayersActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened)) { ItemAdd(bb, window->ChildId); RenderNavHighlight(bb, window->ChildId); - // When browsing a window that has no activable items (scroll only) we keep a highlight on the child - if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow) + // When browsing a window that has no activable items (scroll only) we keep a highlight on the child (pass g.NavId to trick into always displaying) + if (window->DC.NavLayersActiveMask == 0 && window == g.NavWindow) RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_TypeThin); } else @@ -5051,7 +5234,7 @@ void ImGui::EndChild() ItemAdd(bb, 0); } if (g.HoveredWindow == window) - parent_window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredWindow; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; } g.WithinEndChild = false; g.LogLinePosY = -FLT_MAX; // To enforce a carriage return @@ -5104,6 +5287,29 @@ static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settin window->Collapsed = settings->Collapsed; } +static void UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, ImGuiWindowFlags new_flags) +{ + ImGuiContext& g = *GImGui; + + const bool new_is_explicit_child = (new_flags & ImGuiWindowFlags_ChildWindow) != 0; + const bool child_flag_changed = new_is_explicit_child != window->IsExplicitChild; + if ((just_created || child_flag_changed) && !new_is_explicit_child) + { + IM_ASSERT(!g.WindowsFocusOrder.contains(window)); + g.WindowsFocusOrder.push_back(window); + window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1); + } + else if (!just_created && child_flag_changed && new_is_explicit_child) + { + IM_ASSERT(g.WindowsFocusOrder[window->FocusOrder] == window); + for (int n = window->FocusOrder + 1; n < g.WindowsFocusOrder.Size; n++) + g.WindowsFocusOrder[n]->FocusOrder--; + g.WindowsFocusOrder.erase(g.WindowsFocusOrder.Data + window->FocusOrder); + window->FocusOrder = -1; + } + window->IsExplicitChild = new_is_explicit_child; +} + static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) { ImGuiContext& g = *GImGui; @@ -5143,16 +5349,12 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0); } - if (!(flags & ImGuiWindowFlags_ChildWindow)) - { - g.WindowsFocusOrder.push_back(window); - window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1); - } - if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus) g.Windows.push_front(window); // Quite slow but rare and only once else g.Windows.push_back(window); + UpdateWindowInFocusOrderList(window, true, window->Flags); + return window; } @@ -5259,11 +5461,11 @@ ImVec2 ImGui::CalcWindowNextAutoFitSize(ImGuiWindow* window) return size_final; } -static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags) +static ImGuiCol GetWindowBgColorIdx(ImGuiWindow* window) { - if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) + if (window->Flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) return ImGuiCol_PopupBg; - if (flags & ImGuiWindowFlags_ChildWindow) + if (window->Flags & ImGuiWindowFlags_ChildWindow) return ImGuiCol_ChildBg; return ImGuiCol_WindowBg; } @@ -5388,7 +5590,7 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s if (hovered || held) g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; - if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0) + if (held && g.IO.MouseClickedCount[0] == 2 && resize_grip_n == 0) { // Manual auto-fit when double-clicking size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit); @@ -5418,7 +5620,7 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s bool hovered, held; ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_HOVER_PADDING); ImGuiID border_id = window->GetID(border_n + 4); // == GetWindowResizeBorderID() - ButtonBehavior(border_rect, border_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren); + ButtonBehavior(border_rect, border_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus); //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255)); if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held) { @@ -5446,7 +5648,7 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s { ImVec2 nav_resize_delta; if (g.NavInputSource == ImGuiInputSource_Keyboard && g.IO.KeyShift) - nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down); + nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_RawKeyboard, ImGuiInputReadMode_Down); if (g.NavInputSource == ImGuiInputSource_Gamepad) nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down); if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f) @@ -5541,7 +5743,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar // Window background if (!(flags & ImGuiWindowFlags_NoBackground)) { - ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags)); + ImU32 bg_col = GetColorU32(GetWindowBgColorIdx(window)); bool override_alpha = false; float alpha = 1.0f; if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha) @@ -5607,8 +5809,9 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None); // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer) - const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags; - window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus; + // FIXME-NAV: Might want (or not?) to set the equivalent of ImGuiButtonFlags_NoNavFocus so that mouse clicks on standard title bar items don't necessarily set nav/keyboard ref? + const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags; + g.CurrentItemFlags |= ImGuiItemFlags_NoNavDefaultFocus; window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; // Layout buttons @@ -5645,12 +5848,11 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl *p_open = false; window->DC.NavLayerCurrent = ImGuiNavLayer_Main; - window->DC.ItemFlags = item_flags_backup; + g.CurrentItemFlags = item_flags_backup; // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker) // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code.. - const char* UNSAVED_DOCUMENT_MARKER = "*"; - const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? CalcTextSize(UNSAVED_DOCUMENT_MARKER, NULL, false).x : 0.0f; + const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? button_sz * 0.80f : 0.0f; const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f); // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button, @@ -5669,23 +5871,30 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl ImRect layout_r(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y, title_bar_rect.Max.x - pad_r, title_bar_rect.Max.y); ImRect clip_r(layout_r.Min.x, layout_r.Min.y, ImMin(layout_r.Max.x + g.Style.ItemInnerSpacing.x, title_bar_rect.Max.x), layout_r.Max.y); + if (flags & ImGuiWindowFlags_UnsavedDocument) + { + ImVec2 marker_pos; + marker_pos.x = ImClamp(layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x + text_size.x, layout_r.Min.x, layout_r.Max.x); + marker_pos.y = (layout_r.Min.y + layout_r.Max.y) * 0.5f; + if (marker_pos.x > layout_r.Min.x) + { + RenderBullet(window->DrawList, marker_pos, GetColorU32(ImGuiCol_Text)); + clip_r.Max.x = ImMin(clip_r.Max.x, marker_pos.x - (int)(marker_size_x * 0.5f)); + } + } //if (g.IO.KeyShift) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG] //if (g.IO.KeyCtrl) window->DrawList->AddRect(clip_r.Min, clip_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG] RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r); - if (flags & ImGuiWindowFlags_UnsavedDocument) - { - ImVec2 marker_pos = ImVec2(ImMax(layout_r.Min.x, layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x) + text_size.x, layout_r.Min.y) + ImVec2(2 - marker_size_x, 0.0f); - ImVec2 off = ImVec2(0.0f, IM_FLOOR(-g.FontSize * 0.25f)); - RenderTextClipped(marker_pos + off, layout_r.Max + off, UNSAVED_DOCUMENT_MARKER, NULL, NULL, ImVec2(0, style.WindowTitleAlign.y), &clip_r); - } } void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window) { window->ParentWindow = parent_window; - window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; + window->RootWindow = window->RootWindowPopupTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) window->RootWindow = parent_window->RootWindow; + if (parent_window && (flags & ImGuiWindowFlags_Popup)) + window->RootWindowPopupTree = parent_window->RootWindowPopupTree; if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight; while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened) @@ -5695,6 +5904,36 @@ void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags } } +// When a modal popup is open, newly created windows that want focus (i.e. are not popups and do not specify ImGuiWindowFlags_NoFocusOnAppearing) +// should be positioned behind that modal window, unless the window was created inside the modal begin-stack. +// In case of multiple stacked modals newly created window honors begin stack order and does not go below its own modal parent. +// - Window // FindBlockingModal() returns Modal1 +// - Window // .. returns Modal1 +// - Modal1 // .. returns Modal2 +// - Window // .. returns Modal2 +// - Window // .. returns Modal2 +// - Modal2 // .. returns Modal2 +static ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + if (g.OpenPopupStack.Size <= 0) + return NULL; + + // Find a modal that has common parent with specified window. Specified window should be positioned behind that modal. + for (int i = g.OpenPopupStack.Size - 1; i >= 0; i--) + { + ImGuiWindow* popup_window = g.OpenPopupStack.Data[i].Window; + if (popup_window == NULL || !popup_window->WasActive || !(popup_window->Flags & ImGuiWindowFlags_Modal)) // Check WasActive, because this code may run before popup renders on current frame. + continue; + if (IsWindowWithinBeginStackOf(window, popup_window)) // Window is rendered over last modal, no render order change needed. + break; + for (ImGuiWindow* parent = popup_window->ParentWindowInBeginStack->RootWindow; parent != NULL; parent = parent->ParentWindowInBeginStack->RootWindow) + if (IsWindowWithinBeginStackOf(window, parent)) + return popup_window; // Place window above its begin stack parent. + } + return NULL; +} + // Push a new Dear ImGui window to add widgets to. // - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair. // - Begin/End can be called multiple times during the frame with the same window name to append content. @@ -5715,6 +5954,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) const bool window_just_created = (window == NULL); if (window_just_created) window = CreateNewWindow(name, flags); + else + UpdateWindowInFocusOrderList(window, window_just_created, flags); // Automatically disable manual moving/resizing when NoInputs is set if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs) @@ -5754,7 +5995,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack - ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back(); + ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back().Window; ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow; IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow)); @@ -5764,10 +6005,15 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Add to stack // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow() - g.CurrentWindowStack.push_back(window); g.CurrentWindow = window; - window->DC.StackSizesOnBegin.SetToCurrentState(); + ImGuiWindowStackData window_stack_data; + window_stack_data.Window = window; + window_stack_data.ParentLastItemDataBackup = g.LastItemData; + window_stack_data.StackSizesOnBegin.SetToCurrentState(); + g.CurrentWindowStack.push_back(window_stack_data); g.CurrentWindow = NULL; + if (flags & ImGuiWindowFlags_ChildMenu) + g.BeginMenuCount++; if (flags & ImGuiWindowFlags_Popup) { @@ -5779,7 +6025,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Update ->RootWindow and others pointers (before any possible call to FocusWindow) if (first_begin_of_the_frame) + { UpdateWindowParentAndRootLinks(window, flags, parent_window); + window->ParentWindowInBeginStack = parent_window_in_stack; + } // Process SetNextWindow***() calls // (FIXME: Consider splitting the HasXXX flags into X/Y components @@ -5914,7 +6163,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { // We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed), so verify that we don't have items over the title bar. ImRect title_bar_rect = window->TitleBarRect(); - if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0]) + if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseClickedCount[0] == 2) window->WantCollapseToggle = true; if (window->WantCollapseToggle) { @@ -6034,6 +6283,22 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) want_focus = true; else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0) want_focus = true; + + ImGuiWindow* modal = GetTopMostPopupModal(); + if (modal != NULL && !IsWindowWithinBeginStackOf(window, modal)) + { + // Avoid focusing a window that is created outside of active modal. This will prevent active modal from being closed. + // Since window is not focused it would reappear at the same display position like the last time it was visible. + // In case of completely new windows it would go to the top (over current modal), but input to such window would still be blocked by modal. + // Position window behind a modal that is not a begin-parent of this window. + want_focus = false; + if (window == window->RootWindow) + { + ImGuiWindow* blocking_modal = FindBlockingModal(window); + IM_ASSERT(blocking_modal != NULL); + BringWindowToDisplayBehind(window, blocking_modal); + } + } } // Handle manual resize: Resize Grips, Borders, Gamepad @@ -6084,7 +6349,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Inner rectangle // Not affected by window border size. Used by: // - InnerClipRect - // - ScrollToBringRectIntoView() + // - ScrollToRectEx() // - NavUpdatePageUpPageDown() // - Scrollbar() window->InnerRect.Min.x = window->Pos.x; @@ -6131,34 +6396,21 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID); PushClipRect(host_rect.Min, host_rect.Max, false); - // Draw modal window background (darkens what is behind them, all viewports) - const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetTopMostPopupModal() && window->HiddenFramesCannotSkipItems <= 0; - const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow); - if (dim_bg_for_modal || dim_bg_for_window_list) - { - const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio); - window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col); - } - - // Draw navigation selection/windowing rectangle background - if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim) - { - ImRect bb = window->Rect(); - bb.Expand(g.FontSize); - if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway - window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding); - } - - // Since 1.71, child window can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call. + // Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71) // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order. - // We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping child. - // We also disabled this when we have dimming overlay behind this specific one child. - // FIXME: More code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected. + // FIXME: User code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected (github #4493) { bool render_decorations_in_parent = false; if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) - if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_window->DrawList->VtxBuffer.Size > 0) + { + // - We test overlap with the previous child window only (testing all would end up being O(log N) not a good investment here) + // - We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping childs + ImGuiWindow* previous_child = parent_window->DC.ChildWindows.Size >= 2 ? parent_window->DC.ChildWindows[parent_window->DC.ChildWindows.Size - 2] : NULL; + bool previous_child_overlapping = previous_child ? previous_child->Rect().Overlaps(window->Rect()) : false; + bool parent_is_empty = parent_window->DrawList->VtxBuffer.Size > 0; + if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_is_empty && !previous_child_overlapping) render_decorations_in_parent = true; + } if (render_decorations_in_parent) window->DrawList = parent_window->DrawList; @@ -6171,20 +6423,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DrawList = &window->DrawListInst; } - // Draw navigation selection/windowing rectangle border - if (g.NavWindowingTargetAnim == window) - { - float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding); - ImRect bb = window->Rect(); - bb.Expand(g.FontSize); - if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward - { - bb.Expand(-g.FontSize - 1.0f); - rounding = window->WindowRounding; - } - window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, 0, 3.0f); - } - // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING) // Work rectangle. @@ -6216,7 +6454,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x; window->DC.GroupOffset.x = 0.0f; window->DC.ColumnsOffset.x = 0.0f; - window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, decoration_up_height + window->WindowPadding.y - window->Scroll.y); + + // Record the loss of precision of CursorStartPos which can happen due to really large scrolling amount. + // This is used by clipper to compensate and fix the most common use case of large scroll area. Easy and cheap, next best thing compared to switching everything to double or ImU64. + double start_pos_highp_x = (double)window->Pos.x + window->WindowPadding.x - (double)window->Scroll.x + window->DC.ColumnsOffset.x; + double start_pos_highp_y = (double)window->Pos.y + window->WindowPadding.y - (double)window->Scroll.y + decoration_up_height; + window->DC.CursorStartPos = ImVec2((float)start_pos_highp_x, (float)start_pos_highp_y); + window->DC.CursorStartPosLossyness = ImVec2((float)(start_pos_highp_x - window->DC.CursorStartPos.x), (float)(start_pos_highp_y - window->DC.CursorStartPos.y)); window->DC.CursorPos = window->DC.CursorStartPos; window->DC.CursorPosPrevLine = window->DC.CursorPos; window->DC.CursorMaxPos = window->DC.CursorStartPos; @@ -6225,13 +6469,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f; window->DC.NavLayerCurrent = ImGuiNavLayer_Main; - window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext; - window->DC.NavLayerActiveMaskNext = 0x00; + window->DC.NavLayersActiveMask = window->DC.NavLayersActiveMaskNext; + window->DC.NavLayersActiveMaskNext = 0x00; window->DC.NavHideHighlightOneFrame = false; window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f); window->DC.MenuBarAppending = false; - window->DC.MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user); + window->DC.MenuColumns.Update(style.ItemSpacing.x, window_just_activated_by_user); window->DC.TreeDepth = 0; window->DC.TreeJumpToParentOnPopMask = 0x00; window->DC.ChildWindows.resize(0); @@ -6239,7 +6483,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.CurrentColumns = NULL; window->DC.LayoutType = ImGuiLayoutType_Vertical; window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical; - window->DC.FocusCounterRegular = window->DC.FocusCounterTabStop = -1; window->DC.ItemWidth = window->ItemWidthDefault; window->DC.TextWrapPos = -1.0f; // disabled @@ -6277,11 +6520,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin(). // This is useful to allow creating context menus on title bar only, etc. - SetLastItemData(window, window->MoveId, IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, title_bar_rect); + SetLastItemData(window->MoveId, g.CurrentItemFlags, IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, title_bar_rect); #ifdef IMGUI_ENABLE_TEST_ENGINE if (!(window->Flags & ImGuiWindowFlags_NoTitleBar)) - IMGUI_TEST_ENGINE_ITEM_ADD(window->DC.LastItemRect, window->DC.LastItemId); + IMGUI_TEST_ENGINE_ITEM_ADD(g.LastItemData.Rect, g.LastItemData.ID); #endif } else @@ -6291,8 +6534,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } // Pull/inherit current state - window->DC.ItemFlags = g.ItemFlagsStack.back(); // Inherit from shared stack - window->DC.NavFocusScopeIdCurrent = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->DC.NavFocusScopeIdCurrent : 0; // Inherit from parent only // -V595 + window->DC.NavFocusScopeIdCurrent = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->DC.NavFocusScopeIdCurrent : window->GetID("#FOCUSSCOPE"); // Inherit from parent only // -V595 PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true); @@ -6310,9 +6552,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar). IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0); if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) // FIXME: Doesn't make sense for ChildWindow?? - if (!g.LogEnabled) + { + const bool nav_request = (flags & ImGuiWindowFlags_NavFlattened) && (g.NavAnyRequest && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav); + if (!g.LogEnabled && !nav_request) if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y) window->HiddenFramesCanSkipItems = 1; + } // Hide along with parent or if parent is collapsed if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0)) @@ -6326,7 +6571,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->HiddenFramesCanSkipItems = 1; // Update the Hidden flag - window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0) || (window->HiddenFramesForRenderOnly > 0); + bool hidden_regular = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0); + window->Hidden = hidden_regular || (window->HiddenFramesForRenderOnly > 0); // Disable inputs for requested number of frames if (window->DisableInputsFrames > 0) @@ -6337,7 +6583,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Update the SkipItems flag, used to early out of all items functions (no layout required) bool skip_items = false; - if (window->Collapsed || !window->Active || window->Hidden) + if (window->Collapsed || !window->Active || hidden_regular) if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0) skip_items = true; window->SkipItems = skip_items; @@ -6373,11 +6619,14 @@ void ImGui::End() LogFinish(); // Pop from window stack - g.CurrentWindowStack.pop_back(); + g.LastItemData = g.CurrentWindowStack.back().ParentLastItemDataBackup; + if (window->Flags & ImGuiWindowFlags_ChildMenu) + g.BeginMenuCount--; if (window->Flags & ImGuiWindowFlags_Popup) g.BeginPopupStack.pop_back(); - window->DC.StackSizesOnBegin.CompareWithCurrentState(); - SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back()); + g.CurrentWindowStack.back().StackSizesOnBegin.CompareWithCurrentState(); + g.CurrentWindowStack.pop_back(); + SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window); } void ImGui::BringWindowToFocusFront(ImGuiWindow* window) @@ -6430,6 +6679,34 @@ void ImGui::BringWindowToDisplayBack(ImGuiWindow* window) } } +void ImGui::BringWindowToDisplayBehind(ImGuiWindow* window, ImGuiWindow* behind_window) +{ + IM_ASSERT(window != NULL && behind_window != NULL); + ImGuiContext& g = *GImGui; + window = window->RootWindow; + behind_window = behind_window->RootWindow; + int pos_wnd = FindWindowDisplayIndex(window); + int pos_beh = FindWindowDisplayIndex(behind_window); + if (pos_wnd < pos_beh) + { + size_t copy_bytes = (pos_beh - pos_wnd - 1) * sizeof(ImGuiWindow*); + memmove(&g.Windows.Data[pos_wnd], &g.Windows.Data[pos_wnd + 1], copy_bytes); + g.Windows[pos_beh - 1] = window; + } + else + { + size_t copy_bytes = (pos_wnd - pos_beh) * sizeof(ImGuiWindow*); + memmove(&g.Windows.Data[pos_beh + 1], &g.Windows.Data[pos_beh], copy_bytes); + g.Windows[pos_beh] = window; + } +} + +int ImGui::FindWindowDisplayIndex(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + return g.Windows.index_from_ptr(g.Windows.find(window)); +} + // Moving window to front of display and set focus (which happens to be back of our sorted list) void ImGui::FocusWindow(ImGuiWindow* window) { @@ -6444,7 +6721,7 @@ void ImGui::FocusWindow(ImGuiWindow* window) g.NavFocusScopeId = 0; g.NavIdIsAlive = false; g.NavLayer = ImGuiNavLayer_Main; - g.NavInitRequest = g.NavMoveRequest = false; + g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false; NavUpdateAnyRequestFlag(); //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL); } @@ -6477,8 +6754,18 @@ void ImGui::FocusWindow(ImGuiWindow* window) void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window) { ImGuiContext& g = *GImGui; - - const int start_idx = ((under_this_window != NULL) ? FindWindowFocusIndex(under_this_window) : g.WindowsFocusOrder.Size) - 1; + int start_idx = g.WindowsFocusOrder.Size - 1; + if (under_this_window != NULL) + { + // Aim at root window behind us, if we are in a child window that's our own root (see #4640) + int offset = -1; + while (under_this_window->Flags & ImGuiWindowFlags_ChildWindow) + { + under_this_window = under_this_window->ParentWindow; + offset = 0; + } + start_idx = FindWindowFocusIndex(under_this_window) + offset; + } for (int i = start_idx; i >= 0; i--) { // We may later decide to test for different NoXXXInputs based on the active navigation input (mouse vs nav) but that may feel more confusing to the user. @@ -6533,24 +6820,56 @@ void ImGui::PopFont() void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled) { ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImGuiItemFlags item_flags = window->DC.ItemFlags; + ImGuiItemFlags item_flags = g.CurrentItemFlags; IM_ASSERT(item_flags == g.ItemFlagsStack.back()); if (enabled) item_flags |= option; else item_flags &= ~option; - window->DC.ItemFlags = item_flags; + g.CurrentItemFlags = item_flags; g.ItemFlagsStack.push_back(item_flags); } void ImGui::PopItemFlag() { ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; IM_ASSERT(g.ItemFlagsStack.Size > 1); // Too many calls to PopItemFlag() - we always leave a 0 at the bottom of the stack. g.ItemFlagsStack.pop_back(); - window->DC.ItemFlags = g.ItemFlagsStack.back(); + g.CurrentItemFlags = g.ItemFlagsStack.back(); +} + +// BeginDisabled()/EndDisabled() +// - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled) +// - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently. +// - Feedback welcome at https://github.com/ocornut/imgui/issues/211 +// - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it. +// - Optimized shortcuts instead of PushStyleVar() + PushItemFlag() +void ImGui::BeginDisabled(bool disabled) +{ + ImGuiContext& g = *GImGui; + bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; + if (!was_disabled && disabled) + { + g.DisabledAlphaBackup = g.Style.Alpha; + g.Style.Alpha *= g.Style.DisabledAlpha; // PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * g.Style.DisabledAlpha); + } + if (was_disabled || disabled) + g.CurrentItemFlags |= ImGuiItemFlags_Disabled; + g.ItemFlagsStack.push_back(g.CurrentItemFlags); + g.DisabledStackSize++; +} + +void ImGui::EndDisabled() +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.DisabledStackSize > 0); + g.DisabledStackSize--; + bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; + //PopItemFlag(); + g.ItemFlagsStack.pop_back(); + g.CurrentItemFlags = g.ItemFlagsStack.back(); + if (was_disabled && (g.CurrentItemFlags & ImGuiItemFlags_Disabled) == 0) + g.Style.Alpha = g.DisabledAlphaBackup; //PopStyleVar(); } // FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system. @@ -6588,7 +6907,36 @@ void ImGui::PopTextWrapPos() window->DC.TextWrapPosStack.pop_back(); } -bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent) +static ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierarchy) +{ + ImGuiWindow* last_window = NULL; + while (last_window != window) + { + last_window = window; + window = window->RootWindow; + if (popup_hierarchy) + window = window->RootWindowPopupTree; + } + return window; +} + +bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy) +{ + ImGuiWindow* window_root = GetCombinedRootWindow(window, popup_hierarchy); + if (window_root == potential_parent) + return true; + while (window != NULL) + { + if (window == potential_parent) + return true; + if (window == window_root) // end of chain + return false; + window = window->ParentWindow; + } + return false; +} + +bool ImGui::IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent) { if (window->RootWindow == potential_parent) return true; @@ -6596,7 +6944,7 @@ bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent) { if (window == potential_parent) return true; - window = window->ParentWindow; + window = window->ParentWindowInBeginStack; } return false; } @@ -6604,6 +6952,12 @@ bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent) bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below) { ImGuiContext& g = *GImGui; + + // It would be saner to ensure that display layer is always reflected in the g.Windows[] order, which would likely requires altering all manipulations of that array + const int display_layer_delta = GetWindowDisplayLayer(potential_above) - GetWindowDisplayLayer(potential_below); + if (display_layer_delta != 0) + return display_layer_delta > 0; + for (int i = g.Windows.Size - 1; i >= 0; i--) { ImGuiWindow* candidate_window = g.Windows[i]; @@ -6617,39 +6971,33 @@ bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_b bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) { - IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function + IM_ASSERT((flags & (ImGuiHoveredFlags_AllowWhenOverlapped | ImGuiHoveredFlags_AllowWhenDisabled)) == 0); // Flags not supported by this function ImGuiContext& g = *GImGui; - if (g.HoveredWindow == NULL) + ImGuiWindow* ref_window = g.HoveredWindow; + ImGuiWindow* cur_window = g.CurrentWindow; + if (ref_window == NULL) return false; if ((flags & ImGuiHoveredFlags_AnyWindow) == 0) { - ImGuiWindow* window = g.CurrentWindow; - switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) - { - case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows: - if (g.HoveredWindow->RootWindow != window->RootWindow) - return false; - break; - case ImGuiHoveredFlags_RootWindow: - if (g.HoveredWindow != window->RootWindow) - return false; - break; - case ImGuiHoveredFlags_ChildWindows: - if (!IsWindowChildOf(g.HoveredWindow, window)) - return false; - break; - default: - if (g.HoveredWindow != window) - return false; - break; - } + IM_ASSERT(cur_window); // Not inside a Begin()/End() + const bool popup_hierarchy = (flags & ImGuiHoveredFlags_NoPopupHierarchy) == 0; + if (flags & ImGuiHoveredFlags_RootWindow) + cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy); + + bool result; + if (flags & ImGuiHoveredFlags_ChildWindows) + result = IsWindowChildOf(ref_window, cur_window, popup_hierarchy); + else + result = (ref_window == cur_window); + if (!result) + return false; } - if (!IsWindowContentHoverable(g.HoveredWindow, flags)) + if (!IsWindowContentHoverable(ref_window, flags)) return false; if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) - if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId) + if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != ref_window->MoveId) return false; return true; } @@ -6657,22 +7005,23 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) { ImGuiContext& g = *GImGui; + ImGuiWindow* ref_window = g.NavWindow; + ImGuiWindow* cur_window = g.CurrentWindow; + if (ref_window == NULL) + return false; if (flags & ImGuiFocusedFlags_AnyWindow) - return g.NavWindow != NULL; + return true; - IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End() - switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows)) - { - case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows: - return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow; - case ImGuiFocusedFlags_RootWindow: - return g.NavWindow == g.CurrentWindow->RootWindow; - case ImGuiFocusedFlags_ChildWindows: - return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow); - default: - return g.NavWindow == g.CurrentWindow; - } + IM_ASSERT(cur_window); // Not inside a Begin()/End() + const bool popup_hierarchy = (flags & ImGuiFocusedFlags_NoPopupHierarchy) == 0; + if (flags & ImGuiHoveredFlags_RootWindow) + cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy); + + if (flags & ImGuiHoveredFlags_ChildWindows) + return IsWindowChildOf(ref_window, cur_window, popup_hierarchy); + else + return (ref_window == cur_window); } // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext) @@ -6942,6 +7291,7 @@ void ImGui::ActivateItem(ImGuiID id) { ImGuiContext& g = *GImGui; g.NavNextActivateId = id; + g.NavNextActivateFlags = ImGuiActivateFlags_None; } void ImGui::PushFocusScope(ImGuiID id) @@ -6963,12 +7313,21 @@ void ImGui::PopFocusScope() void ImGui::SetKeyboardFocusHere(int offset) { - IM_ASSERT(offset >= -1); // -1 is allowed but not below ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - g.TabFocusRequestNextWindow = window; - g.TabFocusRequestNextCounterRegular = window->DC.FocusCounterRegular + 1 + offset; - g.TabFocusRequestNextCounterTabStop = INT_MAX; + IM_ASSERT(offset >= -1); // -1 is allowed but not below + g.NavWindow = window; + ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY; + NavMoveRequestSubmit(ImGuiDir_None, offset < 0 ? ImGuiDir_Up : ImGuiDir_Down, ImGuiNavMoveFlags_Tabbing | ImGuiNavMoveFlags_FocusApi, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable. + if (offset == -1) + { + NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal); + } + else + { + g.NavTabbingDir = 1; + g.NavTabbingCounter = offset + 1; + } } void ImGui::SetItemDefaultFocus() @@ -6977,15 +7336,17 @@ void ImGui::SetItemDefaultFocus() ImGuiWindow* window = g.CurrentWindow; if (!window->Appearing) return; - if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent) - { - g.NavInitRequest = false; - g.NavInitResultId = g.NavWindow->DC.LastItemId; - g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos); - NavUpdateAnyRequestFlag(); - if (!IsItemVisible()) - SetScrollHereY(); - } + if (g.NavWindow != window->RootWindowForNav || (!g.NavInitRequest && g.NavInitResultId == 0) || g.NavLayer != window->DC.NavLayerCurrent) + return; + + g.NavInitRequest = false; + g.NavInitResultId = g.LastItemData.ID; + g.NavInitResultRectRel = WindowRectAbsToRel(window, g.LastItemData.Rect); + NavUpdateAnyRequestFlag(); + + // Scroll could be done in NavInitRequestApplyResult() via a opt-in flag (we however don't want regular init requests to scroll) + if (!IsItemVisible()) + ScrollToRectEx(window, g.LastItemData.Rect, ImGuiScrollFlags_None); } void ImGui::SetStateStorage(ImGuiStorage* tree) @@ -7037,6 +7398,8 @@ void ImGui::PushOverrideID(ImGuiID id) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; + if (g.DebugHookIdInfo == id) + DebugHookIdInfo(id, ImGuiDataType_ID, NULL, NULL); window->IDStack.push_back(id); } @@ -7046,11 +7409,10 @@ void ImGui::PushOverrideID(ImGuiID id) ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed) { ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); - ImGui::KeepAliveID(id); -#ifdef IMGUI_ENABLE_TEST_ENGINE + KeepAliveID(id); ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end); -#endif + if (g.DebugHookIdInfo == id) + DebugHookIdInfo(id, ImGuiDataType_String, str, str_end); return id; } @@ -7132,8 +7494,7 @@ static void ImGui::ErrorCheckNewFrameSanityChecks() IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0) && "Need a positive DeltaTime!"); IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?"); IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!"); - IM_ASSERT(g.IO.Fonts->Fonts.Size > 0 && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()?"); - IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded() && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()?"); + IM_ASSERT(g.IO.Fonts->IsBuilt() && "Font Atlas not built! Make sure you called ImGui_ImplXXXX_NewFrame() function for renderer backend, which should call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()"); IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!"); IM_ASSERT(g.Style.CircleTessellationMaxError > 0.0f && "Invalid style setting!"); IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting!"); // Allows us to avoid a few clamps in color computations @@ -7196,55 +7557,13 @@ void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, voi { // PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations" ImGuiContext& g = *GImGui; - while (g.CurrentWindowStack.Size > 0) + while (g.CurrentWindowStack.Size > 0) //-V1044 { -#ifdef IMGUI_HAS_TABLE - while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow)) - { - if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name); - EndTable(); - } -#endif + ErrorCheckEndWindowRecover(log_callback, user_data); ImGuiWindow* window = g.CurrentWindow; - IM_ASSERT(window != NULL); - while (g.CurrentTabBar != NULL) //-V1044 - { - if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name); - EndTabBar(); - } - while (window->DC.TreeDepth > 0) - { - if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name); - TreePop(); - } - while (g.GroupStack.Size > window->DC.StackSizesOnBegin.SizeOfGroupStack) - { - if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name); - EndGroup(); - } - while (window->IDStack.Size > 1) - { - if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name); - PopID(); - } - while (g.ColorStack.Size > window->DC.StackSizesOnBegin.SizeOfColorStack) - { - if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col)); - PopStyleColor(); - } - while (g.StyleVarStack.Size > window->DC.StackSizesOnBegin.SizeOfStyleVarStack) - { - if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name); - PopStyleVar(); - } - while (g.FocusScopeStack.Size > window->DC.StackSizesOnBegin.SizeOfFocusScopeStack) - { - if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name); - PopFocusScope(); - } if (g.CurrentWindowStack.Size == 1) { - IM_ASSERT(g.CurrentWindow->IsFallbackWindow); + IM_ASSERT(window->IsFallbackWindow); break; } IM_ASSERT(window == g.CurrentWindow); @@ -7261,6 +7580,66 @@ void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, voi } } +// Must be called before End()/EndChild() +void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data) +{ + ImGuiContext& g = *GImGui; + while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow)) + { + if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name); + EndTable(); + } + + ImGuiWindow* window = g.CurrentWindow; + ImGuiStackSizes* stack_sizes = &g.CurrentWindowStack.back().StackSizesOnBegin; + IM_ASSERT(window != NULL); + while (g.CurrentTabBar != NULL) //-V1044 + { + if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name); + EndTabBar(); + } + while (window->DC.TreeDepth > 0) + { + if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name); + TreePop(); + } + while (g.GroupStack.Size > stack_sizes->SizeOfGroupStack) //-V1044 + { + if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name); + EndGroup(); + } + while (window->IDStack.Size > 1) + { + if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name); + PopID(); + } + while (g.DisabledStackSize > stack_sizes->SizeOfDisabledStack) //-V1044 + { + if (log_callback) log_callback(user_data, "Recovered from missing EndDisabled() in '%s'", window->Name); + EndDisabled(); + } + while (g.ColorStack.Size > stack_sizes->SizeOfColorStack) + { + if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col)); + PopStyleColor(); + } + while (g.ItemFlagsStack.Size > stack_sizes->SizeOfItemFlagsStack) //-V1044 + { + if (log_callback) log_callback(user_data, "Recovered from missing PopItemFlag() in '%s'", window->Name); + PopItemFlag(); + } + while (g.StyleVarStack.Size > stack_sizes->SizeOfStyleVarStack) //-V1044 + { + if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name); + PopStyleVar(); + } + while (g.FocusScopeStack.Size > stack_sizes->SizeOfFocusScopeStack) //-V1044 + { + if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name); + PopFocusScope(); + } +} + // Save current stack sizes for later compare void ImGuiStackSizes::SetToCurrentState() { @@ -7272,7 +7651,9 @@ void ImGuiStackSizes::SetToCurrentState() SizeOfFontStack = (short)g.FontStack.Size; SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size; SizeOfGroupStack = (short)g.GroupStack.Size; + SizeOfItemFlagsStack = (short)g.ItemFlagsStack.Size; SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size; + SizeOfDisabledStack = (short)g.DisabledStackSize; } // Compare to detect usage errors @@ -7290,6 +7671,8 @@ void ImGuiStackSizes::CompareWithCurrentState() // For color, style and font stacks there is an incentive to use Push/Begin/Pop/.../End patterns, so we relax our checks a little to allow them. IM_ASSERT(SizeOfGroupStack == g.GroupStack.Size && "BeginGroup/EndGroup Mismatch!"); IM_ASSERT(SizeOfBeginPopupStack == g.BeginPopupStack.Size && "BeginPopup/EndPopup or BeginMenu/EndMenu Mismatch!"); + IM_ASSERT(SizeOfDisabledStack == g.DisabledStackSize && "BeginDisabled/EndDisabled Mismatch!"); + IM_ASSERT(SizeOfItemFlagsStack >= g.ItemFlagsStack.Size && "PushItemFlag/PopItemFlag Mismatch!"); IM_ASSERT(SizeOfColorStack >= g.ColorStack.Size && "PushStyleColor/PopStyleColor Mismatch!"); IM_ASSERT(SizeOfStyleVarStack >= g.StyleVarStack.Size && "PushStyleVar/PopStyleVar Mismatch!"); IM_ASSERT(SizeOfFontStack >= g.FontStack.Size && "PushFont/PopFont Mismatch!"); @@ -7324,7 +7707,6 @@ void ImGuiStackSizes::CompareWithCurrentState() // - GetContentRegionMaxAbs() [Internal] // - GetContentRegionAvail(), // - GetWindowContentRegionMin(), GetWindowContentRegionMax() -// - GetWindowContentRegionWidth() // - BeginGroup() // - EndGroup() // Also see in imgui_widgets: tab bars, columns. @@ -7374,14 +7756,23 @@ void ImGui::ItemSize(const ImRect& bb, float text_baseline_y) // Declare item bounding box for clipping and interaction. // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface // declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction. -bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) +bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemFlags extra_flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; + // Set item data + // (DisplayRect is left untouched, made valid when ImGuiItemStatusFlags_HasDisplayRect is set) + g.LastItemData.ID = id; + g.LastItemData.Rect = bb; + g.LastItemData.NavRect = nav_bb_arg ? *nav_bb_arg : bb; + g.LastItemData.InFlags = g.CurrentItemFlags | extra_flags; + g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None; + + // Directional navigation processing if (id != 0) { - // Navigation processing runs prior to clipping early-out + // Runs prior to clipping early-out // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests // unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of @@ -7390,11 +7781,16 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) // to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick). // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null. // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere. - window->DC.NavLayerActiveMaskNext |= (1 << window->DC.NavLayerCurrent); + window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent); if (g.NavId == id || g.NavAnyRequest) if (g.NavWindow->RootWindowForNav == window->RootWindowForNav) if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened)) - NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id); + NavProcessItem(); + + // [DEBUG] People keep stumbling on this problem and using "" as identifier in the root of a window instead of "##something". + // Empty identifier are valid and useful in a small amount of cases, but 99.9% of the time you want to use "##something". + // READ THE FAQ: https://dearimgui.org/faq + IM_ASSERT(id != window->ID && "Cannot have an empty ID at the root of a window. If you need an empty label, use ## and read the FAQ about how the ID Stack works!"); // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd() #ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX @@ -7405,11 +7801,6 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) } #endif } - - // Equivalent to calling SetLastItemData() - window->DC.LastItemId = id; - window->DC.LastItemRect = bb; - window->DC.LastItemStatusFlags = ImGuiItemStatusFlags_None; g.NextItemData.Flags = ImGuiNextItemDataFlags_None; #ifdef IMGUI_ENABLE_TEST_ENGINE @@ -7418,14 +7809,14 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) #endif // Clipping test - const bool is_clipped = IsClippedEx(bb, id, false); + const bool is_clipped = IsClippedEx(bb, id); if (is_clipped) return false; //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG] // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them) if (IsMouseHoveringRect(bb.Min, bb.Max)) - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect; return true; } @@ -7685,14 +8076,9 @@ ImVec2 ImGui::GetWindowContentRegionMax() return window->ContentRegionRect.Max - window->Pos; } -float ImGui::GetWindowContentRegionWidth() -{ - ImGuiWindow* window = GImGui->CurrentWindow; - return window->ContentRegionRect.GetWidth(); -} - // Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.) // Groups are currently a mishmash of functionalities which should perhaps be clarified and separated. +// FIXME-OPT: Could we safely early out on ->SkipItems? void ImGui::BeginGroup() { ImGuiContext& g = *GImGui; @@ -7748,7 +8134,7 @@ void ImGui::EndGroup() window->DC.CurrLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrLineTextBaseOffset); // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now. ItemSize(group_bb.GetSize()); - ItemAdd(group_bb, 0); + ItemAdd(group_bb, 0, NULL, ImGuiItemFlags_NoTabStop); // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive(), IsItemDeactivated() etc. will be functional on the entire group. // It would be be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but would put a little more burden on individual widgets. @@ -7757,24 +8143,24 @@ void ImGui::EndGroup() const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId; const bool group_contains_prev_active_id = (group_data.BackupActiveIdPreviousFrameIsAlive == false) && (g.ActiveIdPreviousFrameIsAlive == true); if (group_contains_curr_active_id) - window->DC.LastItemId = g.ActiveId; + g.LastItemData.ID = g.ActiveId; else if (group_contains_prev_active_id) - window->DC.LastItemId = g.ActiveIdPreviousFrame; - window->DC.LastItemRect = group_bb; + g.LastItemData.ID = g.ActiveIdPreviousFrame; + g.LastItemData.Rect = group_bb; // Forward Hovered flag const bool group_contains_curr_hovered_id = (group_data.BackupHoveredIdIsAlive == false) && g.HoveredId != 0; if (group_contains_curr_hovered_id) - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredWindow; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; // Forward Edited flag if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame) - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited; // Forward Deactivated flag - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDeactivated; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDeactivated; if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame) - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Deactivated; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Deactivated; g.GroupStack.pop_back(); //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug] @@ -7837,32 +8223,80 @@ static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window) return scroll; } +void ImGui::ScrollToItem(ImGuiScrollFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ScrollToRectEx(window, g.LastItemData.NavRect, flags); +} + +void ImGui::ScrollToRect(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags) +{ + ScrollToRectEx(window, item_rect, flags); +} + // Scroll to keep newly navigated item fully into view -ImVec2 ImGui::ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect) +ImVec2 ImGui::ScrollToRectEx(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags) { ImGuiContext& g = *GImGui; ImRect window_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1)); //GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG] - ImVec2 delta_scroll; - if (!window_rect.Contains(item_rect)) - { - if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x) - SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x - g.Style.ItemSpacing.x, 0.0f); - else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x) - SetScrollFromPosX(window, item_rect.Max.x - window->Pos.x + g.Style.ItemSpacing.x, 1.0f); - if (item_rect.Min.y < window_rect.Min.y) - SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y - g.Style.ItemSpacing.y, 0.0f); - else if (item_rect.Max.y >= window_rect.Max.y) - SetScrollFromPosY(window, item_rect.Max.y - window->Pos.y + g.Style.ItemSpacing.y, 1.0f); + // Check that only one behavior is selected per axis + IM_ASSERT((flags & ImGuiScrollFlags_MaskX_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskX_)); + IM_ASSERT((flags & ImGuiScrollFlags_MaskY_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskY_)); - ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window); - delta_scroll = next_scroll - window->Scroll; + // Defaults + ImGuiScrollFlags in_flags = flags; + if ((flags & ImGuiScrollFlags_MaskX_) == 0 && window->ScrollbarX) + flags |= ImGuiScrollFlags_KeepVisibleEdgeX; + if ((flags & ImGuiScrollFlags_MaskY_) == 0) + flags |= window->Appearing ? ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeY; + + const bool fully_visible_x = item_rect.Min.x >= window_rect.Min.x && item_rect.Max.x <= window_rect.Max.x; + const bool fully_visible_y = item_rect.Min.y >= window_rect.Min.y && item_rect.Max.y <= window_rect.Max.y; + const bool can_be_fully_visible_x = (item_rect.GetWidth() + g.Style.ItemSpacing.x * 2.0f) <= window_rect.GetWidth(); + const bool can_be_fully_visible_y = (item_rect.GetHeight() + g.Style.ItemSpacing.y * 2.0f) <= window_rect.GetHeight(); + + if ((flags & ImGuiScrollFlags_KeepVisibleEdgeX) && !fully_visible_x) + { + if (item_rect.Min.x < window_rect.Min.x || !can_be_fully_visible_x) + SetScrollFromPosX(window, item_rect.Min.x - g.Style.ItemSpacing.x - window->Pos.x, 0.0f); + else if (item_rect.Max.x >= window_rect.Max.x) + SetScrollFromPosX(window, item_rect.Max.x + g.Style.ItemSpacing.x - window->Pos.x, 1.0f); + } + else if (((flags & ImGuiScrollFlags_KeepVisibleCenterX) && !fully_visible_x) || (flags & ImGuiScrollFlags_AlwaysCenterX)) + { + float target_x = can_be_fully_visible_x ? ImFloor((item_rect.Min.x + item_rect.Max.x - window->InnerRect.GetWidth()) * 0.5f) : item_rect.Min.x; + SetScrollFromPosX(window, target_x - window->Pos.x, 0.0f); } + if ((flags & ImGuiScrollFlags_KeepVisibleEdgeY) && !fully_visible_y) + { + if (item_rect.Min.y < window_rect.Min.y || !can_be_fully_visible_y) + SetScrollFromPosY(window, item_rect.Min.y - g.Style.ItemSpacing.y - window->Pos.y, 0.0f); + else if (item_rect.Max.y >= window_rect.Max.y) + SetScrollFromPosY(window, item_rect.Max.y + g.Style.ItemSpacing.y - window->Pos.y, 1.0f); + } + else if (((flags & ImGuiScrollFlags_KeepVisibleCenterY) && !fully_visible_y) || (flags & ImGuiScrollFlags_AlwaysCenterY)) + { + float target_y = can_be_fully_visible_y ? ImFloor((item_rect.Min.y + item_rect.Max.y - window->InnerRect.GetHeight()) * 0.5f) : item_rect.Min.y; + SetScrollFromPosY(window, target_y - window->Pos.y, 0.0f); + } + + ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window); + ImVec2 delta_scroll = next_scroll - window->Scroll; + // Also scroll parent window to keep us into view if necessary - if (window->Flags & ImGuiWindowFlags_ChildWindow) - delta_scroll += ScrollToBringRectIntoView(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll)); + if (!(flags & ImGuiScrollFlags_NoScrollParent) && (window->Flags & ImGuiWindowFlags_ChildWindow)) + { + // FIXME-SCROLL: May be an option? + if ((in_flags & (ImGuiScrollFlags_AlwaysCenterX | ImGuiScrollFlags_KeepVisibleCenterX)) != 0) + in_flags = (in_flags & ~ImGuiScrollFlags_MaskX_) | ImGuiScrollFlags_KeepVisibleEdgeX; + if ((in_flags & (ImGuiScrollFlags_AlwaysCenterY | ImGuiScrollFlags_KeepVisibleCenterY)) != 0) + in_flags = (in_flags & ~ImGuiScrollFlags_MaskY_) | ImGuiScrollFlags_KeepVisibleEdgeY; + delta_scroll += ScrollToRectEx(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll), in_flags); + } return delta_scroll; } @@ -7963,7 +8397,7 @@ void ImGui::SetScrollHereX(float center_x_ratio) ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; float spacing_x = ImMax(window->WindowPadding.x, g.Style.ItemSpacing.x); - float target_pos_x = ImLerp(window->DC.LastItemRect.Min.x - spacing_x, window->DC.LastItemRect.Max.x + spacing_x, center_x_ratio); + float target_pos_x = ImLerp(g.LastItemData.Rect.Min.x - spacing_x, g.LastItemData.Rect.Max.x + spacing_x, center_x_ratio); SetScrollFromPosX(window, target_pos_x - window->Pos.x, center_x_ratio); // Convert from absolute to local pos // Tweak: snap on edges when aiming at an item very close to the edge @@ -7989,10 +8423,10 @@ void ImGui::SetScrollHereY(float center_y_ratio) void ImGui::BeginTooltip() { - BeginTooltipEx(ImGuiWindowFlags_None, ImGuiTooltipFlags_None); + BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None); } -void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags) +void ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags) { ImGuiContext& g = *GImGui; @@ -8021,7 +8455,7 @@ void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags toolt ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount); } ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize; - Begin(window_name, NULL, flags | extra_flags); + Begin(window_name, NULL, flags | extra_window_flags); } void ImGui::EndTooltip() @@ -8032,7 +8466,7 @@ void ImGui::EndTooltip() void ImGui::SetTooltipV(const char* fmt, va_list args) { - BeginTooltipEx(0, ImGuiTooltipFlags_OverridePreviousTooltip); + BeginTooltipEx(ImGuiTooltipFlags_OverridePreviousTooltip, ImGuiWindowFlags_None); TextV(fmt, args); EndTooltip(); } @@ -8100,6 +8534,16 @@ ImGuiWindow* ImGui::GetTopMostPopupModal() return NULL; } +ImGuiWindow* ImGui::GetTopMostAndVisiblePopupModal() +{ + ImGuiContext& g = *GImGui; + for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--) + if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window) + if ((popup->Flags & ImGuiWindowFlags_Modal) && IsWindowActiveAndVisible(popup)) + return popup; + return NULL; +} + void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags) { ImGuiContext& g = *GImGui; @@ -8192,7 +8636,7 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to bool ref_window_is_descendent_of_popup = false; for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++) if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window) - if (popup_window->RootWindow == ref_window->RootWindow) + if (IsWindowWithinBeginStackOf(ref_window, popup_window)) { ref_window_is_descendent_of_popup = true; break; @@ -8208,6 +8652,21 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to } } +void ImGui::ClosePopupsExceptModals() +{ + ImGuiContext& g = *GImGui; + + int popup_count_to_keep; + for (popup_count_to_keep = g.OpenPopupStack.Size; popup_count_to_keep > 0; popup_count_to_keep--) + { + ImGuiWindow* window = g.OpenPopupStack[popup_count_to_keep - 1].Window; + if (!window || window->Flags & ImGuiWindowFlags_Modal) + break; + } + if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below + ClosePopupToLevel(popup_count_to_keep, true); +} + void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup) { ImGuiContext& g = *GImGui; @@ -8250,7 +8709,7 @@ void ImGui::CloseCurrentPopup() ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window; bool close_parent = false; if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu)) - if (parent_popup_window == NULL || !(parent_popup_window->Flags & ImGuiWindowFlags_Modal)) + if (parent_popup_window && !(parent_popup_window->Flags & ImGuiWindowFlags_MenuBar)) close_parent = true; if (!close_parent) break; @@ -8278,7 +8737,7 @@ bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags) char name[20]; if (flags & ImGuiWindowFlags_ChildMenu) - ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth + ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginMenuCount); // Recycle windows based on depth else ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame @@ -8343,7 +8802,7 @@ void ImGui::EndPopup() IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls IM_ASSERT(g.BeginPopupStack.Size > 0); - // Make all menus and popups wrap around for now, may need to expose that policy. + // Make all menus and popups wrap around for now, may need to expose that policy (e.g. focus scope could include wrap/loop policy flags used by new move requests) if (g.NavWindow == window) NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY); @@ -8359,12 +8818,13 @@ void ImGui::EndPopup() // - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup() void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags) { - ImGuiWindow* window = GImGui->CurrentWindow; + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_); if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) { - ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! - IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) + ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! + IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) OpenPopupEx(id, popup_flags); } } @@ -8387,11 +8847,12 @@ void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags // The main difference being that this is tweaked to avoid computing the ID twice. bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags) { - ImGuiWindow* window = GImGui->CurrentWindow; + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; if (window->SkipItems) return false; - ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! - IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) + ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! + IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_); if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) OpenPopupEx(id, popup_flags); @@ -8400,7 +8861,8 @@ bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flag bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_flags) { - ImGuiWindow* window = GImGui->CurrentWindow; + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; if (!str_id) str_id = "window_context"; ImGuiID id = window->GetID(str_id); @@ -8413,7 +8875,8 @@ bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_fl bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flags) { - ImGuiWindow* window = GImGui->CurrentWindow; + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; if (!str_id) str_id = "void_context"; ImGuiID id = window->GetID(str_id); @@ -8504,7 +8967,7 @@ ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& s } // Note that this is used for popups, which can overlap the non work-area of individual viewports. -ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow* window) +ImRect ImGui::GetPopupAllowedExtentRect(ImGuiWindow* window) { ImGuiContext& g = *GImGui; IM_UNUSED(window); @@ -8518,13 +8981,13 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) { ImGuiContext& g = *GImGui; - ImRect r_outer = GetWindowAllowedExtentRect(window); + ImRect r_outer = GetPopupAllowedExtentRect(window); if (window->Flags & ImGuiWindowFlags_ChildMenu) { // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds. // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu. IM_ASSERT(g.CurrentWindow == window); - ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2]; + ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2].Window; float horizontal_overlap = g.Style.ItemInnerSpacing.x; // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x). ImRect r_avoid; if (parent_window->DC.MenuBarAppending) @@ -8559,18 +9022,17 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) //----------------------------------------------------------------------------- // FIXME-NAV: The existence of SetNavID vs SetFocusID properly needs to be clarified/reworked. -void ImGui::SetNavID(ImGuiID id, int nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel) +// In our terminology those should be interchangeable. Those two functions are merely a legacy artifact, so at minimum naming should be clarified. +void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel) { ImGuiContext& g = *GImGui; IM_ASSERT(g.NavWindow != NULL); IM_ASSERT(nav_layer == ImGuiNavLayer_Main || nav_layer == ImGuiNavLayer_Menu); g.NavId = id; - g.NavLayer = (ImGuiNavLayer)nav_layer; + g.NavLayer = nav_layer; g.NavFocusScopeId = focus_scope_id; g.NavWindow->NavLastIds[nav_layer] = id; g.NavWindow->NavRectRel[nav_layer] = rect_rel; - //g.NavDisableHighlight = false; - //g.NavDisableMouseHover = g.NavMousePosDirty = true; } void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) @@ -8588,8 +9050,8 @@ void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) g.NavLayer = nav_layer; g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent; window->NavLastIds[nav_layer] = id; - if (window->DC.LastItemId == id) - window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos); + if (g.LastItemData.ID == id) + window->NavRectRel[nav_layer] = WindowRectAbsToRel(window, g.LastItemData.NavRect); if (g.ActiveIdSource == ImGuiInputSource_Nav) g.NavDisableMouseHover = true; @@ -8620,7 +9082,7 @@ static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y); r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y); } - else + else // FIXME: PageUp/PageDown are leaving move_dir == None { r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x); r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x); @@ -8628,15 +9090,17 @@ static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect } // Scoring function for gamepad/keyboard directional navigation. Based on https://gist.github.com/rygorous/6981057 -static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) +static bool ImGui::NavScoreItem(ImGuiNavItemData* result) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (g.NavLayer != window->DC.NavLayerCurrent) return false; - const ImRect& curr = g.NavScoringRect; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width) - g.NavScoringCount++; + // FIXME: Those are not good variables names + ImRect cand = g.LastItemData.NavRect; // Current item nav rectangle + const ImRect curr = g.NavScoringRect; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width) + g.NavScoringDebugCount++; // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring if (window->ParentWindow == g.NavWindow) @@ -8686,7 +9150,7 @@ static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) else { // Degenerate case: two overlapping buttons with same center, break ties arbitrarily (note that LastItemId here is really the _previous_ item order, but it doesn't matter) - quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right; + quadrant = (g.LastItemData.ID < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right; } #if IMGUI_DEBUG_NAV_SCORING @@ -8698,24 +9162,24 @@ static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100)); draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200)); draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40,0,0,150)); - draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf); + draw_list->AddText(cand.Max, ~0U, buf); } else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate. { - if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; } if (quadrant == g.NavMoveDir) { ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center); ImDrawList* draw_list = GetForegroundDrawList(window); draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200)); - draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf); + draw_list->AddText(cand.Min, IM_COL32(255, 255, 255, 255), buf); } } #endif // Is it in the quadrant we're interesting in moving to? bool new_best = false; - if (quadrant == g.NavMoveDir) + const ImGuiDir move_dir = g.NavMoveDir; + if (quadrant == move_dir) { // Does it beat the current best candidate? if (dist_box < result->DistBox) @@ -8737,7 +9201,7 @@ static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items // (with higher index) to the right/downwards by an infinitesimal amount since we the current "best" button already (so it must have a lower index), // this is fairly easy. This rule ensures that all buttons with dx==dy==0 will end up being linked in order of appearance along the x axis. - if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance + if (((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance new_best = true; } } @@ -8750,7 +9214,7 @@ static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option? if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) - if ((g.NavMoveDir == ImGuiDir_Left && dax < 0.0f) || (g.NavMoveDir == ImGuiDir_Right && dax > 0.0f) || (g.NavMoveDir == ImGuiDir_Up && day < 0.0f) || (g.NavMoveDir == ImGuiDir_Down && day > 0.0f)) + if ((move_dir == ImGuiDir_Left && dax < 0.0f) || (move_dir == ImGuiDir_Right && dax > 0.0f) || (move_dir == ImGuiDir_Up && day < 0.0f) || (move_dir == ImGuiDir_Down && day > 0.0f)) { result->DistAxial = dist_axial; new_best = true; @@ -8759,34 +9223,38 @@ static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) return new_best; } -static void ImGui::NavApplyItemToResult(ImGuiNavMoveResult* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel) +static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result) { + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; result->Window = window; - result->ID = id; + result->ID = g.LastItemData.ID; result->FocusScopeId = window->DC.NavFocusScopeIdCurrent; - result->RectRel = nav_bb_rel; + result->InFlags = g.LastItemData.InFlags; + result->RectRel = WindowRectAbsToRel(window, g.LastItemData.NavRect); } // We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above) -static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id) +// This is called after LastItemData is set. +static void ImGui::NavProcessItem() { ImGuiContext& g = *GImGui; - //if (!g.IO.NavActive) // [2017/10/06] Removed this possibly redundant test but I am not sure of all the side-effects yet. Some of the feature here will need to work regardless of using a _NoNavInputs flag. - // return; - - const ImGuiItemFlags item_flags = window->DC.ItemFlags; - const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); + ImGuiWindow* window = g.CurrentWindow; + const ImGuiID id = g.LastItemData.ID; + const ImRect nav_bb = g.LastItemData.NavRect; + const ImGuiItemFlags item_flags = g.LastItemData.InFlags; // Process Init Request if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent) { // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback - if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0) + const bool candidate_for_nav_default_focus = (item_flags & (ImGuiItemFlags_NoNavDefaultFocus | ImGuiItemFlags_Disabled)) == 0; + if (candidate_for_nav_default_focus || g.NavInitResultId == 0) { g.NavInitResultId = id; - g.NavInitResultRectRel = nav_bb_rel; + g.NavInitResultRectRel = WindowRectAbsToRel(window, nav_bb); } - if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) + if (candidate_for_nav_default_focus) { g.NavInitRequest = false; // Found a match, clear request NavUpdateAnyRequestFlag(); @@ -8794,27 +9262,32 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con } // Process Move Request (scoring for navigation) - // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy) - if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav))) + // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy) + if (g.NavMoveScoringItems) { - ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; -#if IMGUI_DEBUG_NAV_SCORING - // [DEBUG] Score all items in NavWindow at all times - if (!g.NavMoveRequest) - g.NavMoveDir = g.NavMoveDirLast; - bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest; -#else - bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb); -#endif - if (new_best) - NavApplyItemToResult(result, window, id, nav_bb_rel); + const bool is_tab_stop = (item_flags & ImGuiItemFlags_Inputable) && (item_flags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0; + const bool is_tabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) != 0; + if (is_tabbing) + { + if (is_tab_stop || (g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi)) + NavProcessItemForTabbingRequest(id); + } + else if ((g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav))) + { + ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; + if (!is_tabbing) + { + if (NavScoreItem(result)) + NavApplyItemToResult(result); - // Features like PageUp/PageDown need to maintain a separate score for the visible set of items. - const float VISIBLE_RATIO = 0.70f; - if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb)) - if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO) - if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb)) - NavApplyItemToResult(&g.NavMoveResultLocalVisibleSet, window, id, nav_bb_rel); + // Features like PageUp/PageDown need to maintain a separate score for the visible set of items. + const float VISIBLE_RATIO = 0.70f; + if ((g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb)) + if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO) + if (NavScoreItem(&g.NavMoveResultLocalVisible)) + NavApplyItemToResult(&g.NavMoveResultLocalVisible); + } + } } // Update window-relative bounding box of navigated item @@ -8824,44 +9297,124 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con g.NavLayer = window->DC.NavLayerCurrent; g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent; g.NavIdIsAlive = true; - g.NavIdTabCounter = window->DC.FocusCounterTabStop; - window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position) + window->NavRectRel[window->DC.NavLayerCurrent] = WindowRectAbsToRel(window, nav_bb); // Store item bounding box (relative to window position) + } +} + +// Handle "scoring" of an item for a tabbing/focusing request initiated by NavUpdateCreateTabbingRequest(). +// Note that SetKeyboardFocusHere() API calls are considered tabbing requests! +// - Case 1: no nav/active id: set result to first eligible item, stop storing. +// - Case 2: tab forward: on ref id set counter, on counter elapse store result +// - Case 3: tab forward wrap: set result to first eligible item (preemptively), on ref id set counter, on next frame if counter hasn't elapsed store result. // FIXME-TABBING: Could be done as a next-frame forwarded request +// - Case 4: tab backward: store all results, on ref id pick prev, stop storing +// - Case 5: tab backward wrap: store all results, on ref id if no result keep storing until last // FIXME-TABBING: Could be done as next-frame forwarded requested +void ImGui::NavProcessItemForTabbingRequest(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + + // Always store in NavMoveResultLocal (unlike directional request which uses NavMoveResultOther on sibling/flattened windows) + ImGuiNavItemData* result = &g.NavMoveResultLocal; + if (g.NavTabbingDir == +1) + { + // Tab Forward or SetKeyboardFocusHere() with >= 0 + if (g.NavTabbingResultFirst.ID == 0) + NavApplyItemToResult(&g.NavTabbingResultFirst); + if (--g.NavTabbingCounter == 0) + NavMoveRequestResolveWithLastItem(result); + else if (g.NavId == id) + g.NavTabbingCounter = 1; + } + else if (g.NavTabbingDir == -1) + { + // Tab Backward + if (g.NavId == id) + { + if (result->ID) + { + g.NavMoveScoringItems = false; + NavUpdateAnyRequestFlag(); + } + } + else + { + NavApplyItemToResult(result); + } + } + else if (g.NavTabbingDir == 0) + { + // Tab Init + if (g.NavTabbingResultFirst.ID == 0) + NavMoveRequestResolveWithLastItem(&g.NavTabbingResultFirst); } } bool ImGui::NavMoveRequestButNoResultYet() { ImGuiContext& g = *GImGui; - return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0; + return g.NavMoveScoringItems && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0; +} + +// FIXME: ScoringRect is not set +void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.NavWindow != NULL); + + if (move_flags & ImGuiNavMoveFlags_Tabbing) + move_flags |= ImGuiNavMoveFlags_AllowCurrentNavId; + + g.NavMoveSubmitted = g.NavMoveScoringItems = true; + g.NavMoveDir = move_dir; + g.NavMoveDirForDebug = move_dir; + g.NavMoveClipDir = clip_dir; + g.NavMoveFlags = move_flags; + g.NavMoveScrollFlags = scroll_flags; + g.NavMoveForwardToNextFrame = false; + g.NavMoveKeyMods = g.IO.KeyMods; + g.NavTabbingCounter = 0; + g.NavMoveResultLocal.Clear(); + g.NavMoveResultLocalVisible.Clear(); + g.NavMoveResultOther.Clear(); + NavUpdateAnyRequestFlag(); +} + +void ImGui::NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result) +{ + ImGuiContext& g = *GImGui; + g.NavMoveScoringItems = false; // Ensure request doesn't need more processing + NavApplyItemToResult(result); + NavUpdateAnyRequestFlag(); } void ImGui::NavMoveRequestCancel() { ImGuiContext& g = *GImGui; - g.NavMoveRequest = false; + g.NavMoveSubmitted = g.NavMoveScoringItems = false; NavUpdateAnyRequestFlag(); } -void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags) +// Forward will reuse the move request again on the next frame (generally with modifications done to it) +void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags) { ImGuiContext& g = *GImGui; - IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None); + IM_ASSERT(g.NavMoveForwardToNextFrame == false); NavMoveRequestCancel(); + g.NavMoveForwardToNextFrame = true; g.NavMoveDir = move_dir; g.NavMoveClipDir = clip_dir; - g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; - g.NavMoveRequestFlags = move_flags; - g.NavWindow->NavRectRel[g.NavLayer] = bb_rel; + g.NavMoveFlags = move_flags | ImGuiNavMoveFlags_Forwarded; + g.NavMoveScrollFlags = scroll_flags; } -void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags) +// Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire +// popup is assembled and in case of appended popups it is not clear which EndPopup() call is final. +void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags wrap_flags) { ImGuiContext& g = *GImGui; - - // Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire - // popup is assembled and in case of appended popups it is not clear which EndPopup() call is final. - g.NavWrapRequestWindow = window; - g.NavWrapRequestFlags = move_flags; + IM_ASSERT(wrap_flags != 0); // Call with _WrapX, _WrapY, _LoopX, _LoopY + // In theory we should test for NavMoveRequestButNoResultYet() but there's no point doing it, NavEndFrame() will do the same test + if (g.NavWindow == window && g.NavMoveScoringItems && g.NavLayer == ImGuiNavLayer_Main) + g.NavMoveFlags |= wrap_flags; } // FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0). @@ -8893,8 +9446,6 @@ void ImGui::NavRestoreLayer(ImGuiNavLayer layer) if (window->NavLastIds[layer] != 0) { SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]); - g.NavDisableHighlight = false; - g.NavDisableMouseHover = g.NavMousePosDirty = true; } else { @@ -8903,10 +9454,17 @@ void ImGui::NavRestoreLayer(ImGuiNavLayer layer) } } +void ImGui::NavRestoreHighlightAfterMove() +{ + ImGuiContext& g = *GImGui; + g.NavDisableHighlight = false; + g.NavDisableMouseHover = g.NavMousePosDirty = true; +} + static inline void ImGui::NavUpdateAnyRequestFlag() { ImGuiContext& g = *GImGui; - g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL); + g.NavAnyRequest = g.NavMoveScoringItems || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL); if (g.NavAnyRequest) IM_ASSERT(g.NavWindow != NULL); } @@ -8916,10 +9474,16 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) { ImGuiContext& g = *GImGui; IM_ASSERT(window == g.NavWindow); + + if (window->Flags & ImGuiWindowFlags_NoNavInputs) + { + g.NavId = g.NavFocusScopeId = 0; + return; + } + bool init_for_nav = false; - if (!(window->Flags & ImGuiWindowFlags_NoNavInputs)) - if (window == window->RootWindow || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit) - init_for_nav = true; + if (window == window->RootWindow || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit) + init_for_nav = true; IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from NavInitWindow(), init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer); if (init_for_nav) { @@ -8940,18 +9504,25 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) static ImVec2 ImGui::NavCalcPreferredRefPos() { ImGuiContext& g = *GImGui; - if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow) + ImGuiWindow* window = g.NavWindow; + if (g.NavDisableHighlight || !g.NavDisableMouseHover || !window) { // Mouse (we need a fallback in case the mouse becomes invalid after being used) if (IsMousePosValid(&g.IO.MousePos)) return g.IO.MousePos; - return g.LastValidMousePos; + return g.MouseLastValidPos; } else { - // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item. - const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer]; - ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x * 4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); + // When navigation is active and mouse is disabled, pick a position around the bottom left of the currently navigated item + // Take account of upcoming scrolling (maybe set mouse pos should be done in EndFrame?) + ImRect rect_rel = WindowRectRelToAbs(window, window->NavRectRel[g.NavLayer]); + if (window->LastFrameActive != g.FrameCount && (window->ScrollTarget.x != FLT_MAX || window->ScrollTarget.y != FLT_MAX)) + { + ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window); + rect_rel.Translate(window->Scroll - next_scroll); + } + ImVec2 pos = ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x * 4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); ImGuiViewport* viewport = GetMainViewport(); return ImFloor(ImClamp(pos, viewport->Pos, viewport->Pos + viewport->Size)); // ImFloor() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta. } @@ -8982,6 +9553,8 @@ float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode) ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor) { ImVec2 delta(0.0f, 0.0f); + if (dir_sources & ImGuiNavDirSourceFlags_RawKeyboard) + delta += ImVec2((float)IsKeyDown(GetKeyIndex(ImGuiKey_RightArrow)) - (float)IsKeyDown(GetKeyIndex(ImGuiKey_LeftArrow)), (float)IsKeyDown(GetKeyIndex(ImGuiKey_DownArrow)) - (float)IsKeyDown(GetKeyIndex(ImGuiKey_UpArrow))); if (dir_sources & ImGuiNavDirSourceFlags_Keyboard) delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode)); if (dir_sources & ImGuiNavDirSourceFlags_PadDPad) @@ -9001,16 +9574,12 @@ static void ImGui::NavUpdate() ImGuiIO& io = g.IO; io.WantSetMousePos = false; - g.NavWrapRequestWindow = NULL; - g.NavWrapRequestFlags = ImGuiNavMoveFlags_None; -#if 0 - if (g.NavScoringCount > 0) IMGUI_DEBUG_LOG("NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); -#endif + //if (g.NavScoringDebugCount > 0) IMGUI_DEBUG_LOG("NavScoringDebugCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.NavScoringDebugCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); // Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard) // (do it before we map Keyboard input!) - bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; - bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; + const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; + const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; if (nav_gamepad_active && g.NavInputSource != ImGuiInputSource_Gamepad) { if (io.NavInputs[ImGuiNavInput_Activate] > 0.0f || io.NavInputs[ImGuiNavInput_Input] > 0.0f || io.NavInputs[ImGuiNavInput_Cancel] > 0.0f || io.NavInputs[ImGuiNavInput_Menu] > 0.0f @@ -9033,8 +9602,6 @@ static void ImGui::NavUpdate() io.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f; if (io.KeyShift) io.NavInputs[ImGuiNavInput_TweakFast] = 1.0f; - if (io.KeyAlt && !io.KeyCtrl) // AltGR is Alt+Ctrl, also even on keyboards without AltGR we don't want Alt+Ctrl to open menu. - io.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f; #undef NAV_MAP_KEY } memcpy(io.NavInputsDownDurationPrev, io.NavInputsDownDuration, sizeof(io.NavInputsDownDuration)); @@ -9043,42 +9610,27 @@ static void ImGui::NavUpdate() // Process navigation init request (select first/default focus) if (g.NavInitResultId != 0) - NavUpdateInitResult(); + NavInitRequestApplyResult(); g.NavInitRequest = false; g.NavInitRequestFromMove = false; g.NavInitResultId = 0; g.NavJustMovedToId = 0; // Process navigation move request - if (g.NavMoveRequest) - NavUpdateMoveResult(); + if (g.NavMoveSubmitted) + NavMoveRequestApplyResult(); + g.NavTabbingCounter = 0; + g.NavMoveSubmitted = g.NavMoveScoringItems = false; - // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame - if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive) - { - IM_ASSERT(g.NavMoveRequest); - if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0) - g.NavDisableHighlight = false; - g.NavMoveRequestForward = ImGuiNavForward_None; - } - - // Apply application mouse position movement, after we had a chance to process move request result. + // Schedule mouse position update (will be done at the bottom of this function, after 1) processing all move requests and 2) updating scrolling) + bool set_mouse_pos = false; if (g.NavMousePosDirty && g.NavIdIsAlive) - { - // Set mouse position given our knowledge of the navigated item position from last frame - if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) - if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow) - { - io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos(); - io.WantSetMousePos = true; - } - g.NavMousePosDirty = false; - } - g.NavIdIsAlive = false; - g.NavJustTabbedId = 0; - IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1); + if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow) + set_mouse_pos = true; + g.NavMousePosDirty = false; + IM_ASSERT(g.NavLayer == ImGuiNavLayer_Main || g.NavLayer == ImGuiNavLayer_Menu); - // Store our return window (for returning from Layer 1 to Layer 0) and clear it as soon as we step back in our own Layer 0 + // Store our return window (for returning from Menu Layer to Main Layer) and clear it as soon as we step back in our own Layer 0 if (g.NavWindow) NavSaveLastChildNavWindowIntoParent(g.NavWindow); if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main) @@ -9092,117 +9644,55 @@ static void ImGui::NavUpdate() io.NavVisible = (io.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL); // Process NavCancel input (to close a popup, get back to parent, clear focus) - if (IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed)) - { - IMGUI_DEBUG_LOG_NAV("[nav] ImGuiNavInput_Cancel\n"); - if (g.ActiveId != 0) - { - if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel)) - ClearActiveID(); - } - else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow) - { - // Exit child window - ImGuiWindow* child_window = g.NavWindow; - ImGuiWindow* parent_window = g.NavWindow->ParentWindow; - IM_ASSERT(child_window->ChildId != 0); - ImRect child_rect = child_window->Rect(); - FocusWindow(parent_window); - SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, ImRect(child_rect.Min - parent_window->Pos, child_rect.Max - parent_window->Pos)); - } - else if (g.OpenPopupStack.Size > 0) - { - // Close open popup/menu - if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal)) - ClosePopupToLevel(g.OpenPopupStack.Size - 1, true); - } - else if (g.NavLayer != ImGuiNavLayer_Main) - { - // Leave the "menu" layer - NavRestoreLayer(ImGuiNavLayer_Main); - } - else - { - // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were - if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow))) - g.NavWindow->NavLastIds[0] = 0; - g.NavId = g.NavFocusScopeId = 0; - } - } + NavUpdateCancelRequest(); // Process manual activation request - g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0; + g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavActivateInputId = 0; + g.NavActivateFlags = ImGuiActivateFlags_None; if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) { bool activate_down = IsNavInputDown(ImGuiNavInput_Activate); + bool input_down = IsNavInputDown(ImGuiNavInput_Input); bool activate_pressed = activate_down && IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed); + bool input_pressed = input_down && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed); if (g.ActiveId == 0 && activate_pressed) + { g.NavActivateId = g.NavId; + g.NavActivateFlags = ImGuiActivateFlags_PreferTweak; + } + if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && input_pressed) + { + g.NavActivateInputId = g.NavId; + g.NavActivateFlags = ImGuiActivateFlags_PreferInput; + } if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down) g.NavActivateDownId = g.NavId; if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed) g.NavActivatePressedId = g.NavId; - if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed)) - g.NavInputId = g.NavId; } if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) g.NavDisableHighlight = true; if (g.NavActivateId != 0) IM_ASSERT(g.NavActivateDownId == g.NavActivateId); - g.NavMoveRequest = false; // Process programmatic activation request + // FIXME-NAV: Those should eventually be queued (unlike focus they don't cancel each others) if (g.NavNextActivateId != 0) - g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId; + { + if (g.NavNextActivateFlags & ImGuiActivateFlags_PreferInput) + g.NavActivateInputId = g.NavNextActivateId; + else + g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavNextActivateId; + g.NavActivateFlags = g.NavNextActivateFlags; + } g.NavNextActivateId = 0; - // Initiate directional inputs request - if (g.NavMoveRequestForward == ImGuiNavForward_None) - { - g.NavMoveDir = ImGuiDir_None; - g.NavMoveRequestFlags = ImGuiNavMoveFlags_None; - if (g.NavWindow && !g.NavWindowingTarget && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) - { - const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat; - if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && (IsNavInputTest(ImGuiNavInput_DpadLeft, read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_, read_mode))) { g.NavMoveDir = ImGuiDir_Left; } - if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; } - if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && (IsNavInputTest(ImGuiNavInput_DpadUp, read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_, read_mode))) { g.NavMoveDir = ImGuiDir_Up; } - if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && (IsNavInputTest(ImGuiNavInput_DpadDown, read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_, read_mode))) { g.NavMoveDir = ImGuiDir_Down; } - } - g.NavMoveClipDir = g.NavMoveDir; - } - else - { - // Forwarding previous request (which has been modified, e.g. wrap around menus rewrite the requests with a starting rectangle at the other side of the window) - // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function) - IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None); - IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued); - IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir); - g.NavMoveRequestForward = ImGuiNavForward_ForwardActive; - } - - // Update PageUp/PageDown/Home/End scroll - // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag? - float nav_scoring_rect_offset_y = 0.0f; - if (nav_keyboard_active) - nav_scoring_rect_offset_y = NavUpdatePageUpPageDown(); - - // If we initiate a movement request and have no current NavId, we initiate a InitDefautRequest that will be used as a fallback if the direction fails to find a match - if (g.NavMoveDir != ImGuiDir_None) - { - g.NavMoveRequest = true; - g.NavMoveRequestKeyMods = io.KeyMods; - g.NavMoveDirLast = g.NavMoveDir; - } - if (g.NavMoveRequest && g.NavId == 0) - { - IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer); - g.NavInitRequest = g.NavInitRequestFromMove = true; - // Reassigning with same value, we're being explicit here. - g.NavInitResultId = 0; // -V1048 - g.NavDisableHighlight = false; - } + // Process move requests + NavUpdateCreateMoveRequest(); + if (g.NavMoveDir == ImGuiDir_None) + NavUpdateCreateTabbingRequest(); NavUpdateAnyRequestFlag(); + g.NavIdIsAlive = false; // Scrolling if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) @@ -9210,12 +9700,13 @@ static void ImGui::NavUpdate() // *Fallback* manual-scroll with Nav directional keys when window has no navigable item ImGuiWindow* window = g.NavWindow; const float scroll_speed = IM_ROUND(window->CalcFontSize() * 100 * io.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. - if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest) + const ImGuiDir move_dir = g.NavMoveDir; + if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll && move_dir != ImGuiDir_None) { - if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) - SetScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); - if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) - SetScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); + if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right) + SetScrollX(window, ImFloor(window->Scroll.x + ((move_dir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); + if (move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) + SetScrollY(window, ImFloor(window->Scroll.y + ((move_dir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); } // *Normal* Manual scroll with NavScrollXXX keys @@ -9227,18 +9718,125 @@ static void ImGui::NavUpdate() SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed)); } - // Reset search results - g.NavMoveResultLocal.Clear(); - g.NavMoveResultLocalVisibleSet.Clear(); - g.NavMoveResultOther.Clear(); + // Always prioritize mouse highlight if navigation is disabled + if (!nav_keyboard_active && !nav_gamepad_active) + { + g.NavDisableHighlight = true; + g.NavDisableMouseHover = set_mouse_pos = false; + } + + // Update mouse position if requested + // (This will take into account the possibility that a Scroll was queued in the window to offset our absolute mouse position before scroll has been applied) + if (set_mouse_pos && (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) + { + io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos(); + io.WantSetMousePos = true; + //IMGUI_DEBUG_LOG("SetMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y); + } + + // [DEBUG] + g.NavScoringDebugCount = 0; +#if IMGUI_DEBUG_NAV_RECTS + if (g.NavWindow) + { + ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow); + if (1) { for (int layer = 0; layer < 2; layer++) { ImRect r = WindowRectRelToAbs(g.NavWindow, g.NavWindow->NavRectRel[layer]); draw_list->AddRect(r.Min, r.Max, IM_COL32(255,200,0,255)); } } // [DEBUG] + if (1) { ImU32 col = (!g.NavWindow->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); draw_list->AddCircleFilled(p, 3.0f, col); draw_list->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } + } +#endif +} + +void ImGui::NavInitRequestApplyResult() +{ + // In very rare cases g.NavWindow may be null (e.g. clearing focus after requesting an init request, which does happen when releasing Alt while clicking on void) + ImGuiContext& g = *GImGui; + if (!g.NavWindow) + return; + + // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called) + // FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently. + IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name); + SetNavID(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel); + g.NavIdIsAlive = true; // Mark as alive from previous frame as we got a result + if (g.NavInitRequestFromMove) + NavRestoreHighlightAfterMove(); +} + +void ImGui::NavUpdateCreateMoveRequest() +{ + ImGuiContext& g = *GImGui; + ImGuiIO& io = g.IO; + ImGuiWindow* window = g.NavWindow; + + if (g.NavMoveForwardToNextFrame && window != NULL) + { + // Forwarding previous request (which has been modified, e.g. wrap around menus rewrite the requests with a starting rectangle at the other side of the window) + // (preserve most state, which were already set by the NavMoveRequestForward() function) + IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None); + IM_ASSERT(g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded); + IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir); + } + else + { + // Initiate directional inputs request + g.NavMoveDir = ImGuiDir_None; + g.NavMoveFlags = ImGuiNavMoveFlags_None; + g.NavMoveScrollFlags = ImGuiScrollFlags_None; + if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs)) + { + const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat; + if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && (IsNavInputTest(ImGuiNavInput_DpadLeft, read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_, read_mode))) { g.NavMoveDir = ImGuiDir_Left; } + if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; } + if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && (IsNavInputTest(ImGuiNavInput_DpadUp, read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_, read_mode))) { g.NavMoveDir = ImGuiDir_Up; } + if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && (IsNavInputTest(ImGuiNavInput_DpadDown, read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_, read_mode))) { g.NavMoveDir = ImGuiDir_Down; } + } + g.NavMoveClipDir = g.NavMoveDir; + g.NavScoringNoClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX); + } + + // Update PageUp/PageDown/Home/End scroll + // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag? + const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; + float scoring_rect_offset_y = 0.0f; + if (window && g.NavMoveDir == ImGuiDir_None && nav_keyboard_active) + scoring_rect_offset_y = NavUpdatePageUpPageDown(); + if (scoring_rect_offset_y != 0.0f) + { + g.NavScoringNoClipRect = window->InnerRect; + g.NavScoringNoClipRect.TranslateY(scoring_rect_offset_y); + } + + // [DEBUG] Always send a request +#if IMGUI_DEBUG_NAV_SCORING + if (io.KeyCtrl && IsKeyPressedMap(ImGuiKey_C)) + g.NavMoveDirForDebug = (ImGuiDir)((g.NavMoveDirForDebug + 1) & 3); + if (io.KeyCtrl && g.NavMoveDir == ImGuiDir_None) + { + g.NavMoveDir = g.NavMoveDirForDebug; + g.NavMoveFlags |= ImGuiNavMoveFlags_DebugNoResult; + } +#endif + + // Submit + g.NavMoveForwardToNextFrame = false; + if (g.NavMoveDir != ImGuiDir_None) + NavMoveRequestSubmit(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags); + + // Moving with no reference triggers a init request (will be used as a fallback if the direction fails to find a match) + if (g.NavMoveSubmitted && g.NavId == 0) + { + IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer); + g.NavInitRequest = g.NavInitRequestFromMove = true; + g.NavInitResultId = 0; + g.NavDisableHighlight = false; + } // When using gamepad, we project the reference nav bounding box into window visible area. // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling, since with gamepad every movements are relative // (can't focus a visible object like we can with the mouse). - if (g.NavMoveRequest && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main) + if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL) { - ImGuiWindow* window = g.NavWindow; - ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1)); + ImRect window_rect_rel = WindowRectAbsToRel(window, ImRect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1))); if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer])) { IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel\n"); @@ -9250,63 +9848,78 @@ static void ImGui::NavUpdate() } // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items) - ImRect nav_rect_rel = g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted() ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0); - g.NavScoringRect = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : ImRect(0, 0, 0, 0); - g.NavScoringRect.TranslateY(nav_scoring_rect_offset_y); - g.NavScoringRect.Min.x = ImMin(g.NavScoringRect.Min.x + 1.0f, g.NavScoringRect.Max.x); - g.NavScoringRect.Max.x = g.NavScoringRect.Min.x; - IM_ASSERT(!g.NavScoringRect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem(). - //GetForegroundDrawList()->AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] - g.NavScoringCount = 0; -#if IMGUI_DEBUG_NAV_RECTS - if (g.NavWindow) + ImRect scoring_rect; + if (window != NULL) { - ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow); - if (1) { for (int layer = 0; layer < 2; layer++) draw_list->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] - if (1) { ImU32 col = (!g.NavWindow->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); draw_list->AddCircleFilled(p, 3.0f, col); draw_list->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } + ImRect nav_rect_rel = !window->NavRectRel[g.NavLayer].IsInverted() ? window->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0); + scoring_rect = WindowRectRelToAbs(window, nav_rect_rel); + scoring_rect.TranslateY(scoring_rect_offset_y); + scoring_rect.Min.x = ImMin(scoring_rect.Min.x + 1.0f, scoring_rect.Max.x); + scoring_rect.Max.x = scoring_rect.Min.x; + IM_ASSERT(!scoring_rect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem(). + //GetForegroundDrawList()->AddRect(scoring_rect.Min, scoring_rect.Max, IM_COL32(255,200,0,255)); // [DEBUG] + //if (!g.NavScoringNoClipRect.IsInverted()) { GetForegroundDrawList()->AddRect(g.NavScoringNoClipRect.Min, g.NavScoringNoClipRect.Max, IM_COL32(255, 200, 0, 255)); } // [DEBUG] } + g.NavScoringRect = scoring_rect; + g.NavScoringNoClipRect.Add(scoring_rect); +} + +void ImGui::NavUpdateCreateTabbingRequest() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.NavWindow; + IM_ASSERT(g.NavMoveDir == ImGuiDir_None); + if (window == NULL || g.NavWindowingTarget != NULL || (window->Flags & ImGuiWindowFlags_NoNavInputs)) + return; + + const bool tab_pressed = IsKeyPressedMap(ImGuiKey_Tab, true) && !IsActiveIdUsingKey(ImGuiKey_Tab) && !g.IO.KeyCtrl && !g.IO.KeyAlt; + if (!tab_pressed) + return; + + // Initiate tabbing request + // (this is ALWAYS ENABLED, regardless of ImGuiConfigFlags_NavEnableKeyboard flag!) + // Initially this was designed to use counters and modulo arithmetic, but that could not work with unsubmitted items (list clipper). Instead we use a strategy close to other move requests. + // See NavProcessItemForTabbingRequest() for a description of the various forward/backward tabbing cases with and without wrapping. + //// FIXME: We use (g.ActiveId == 0) but (g.NavDisableHighlight == false) might be righter once we can tab through anything + g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.ActiveId == 0) ? 0 : +1; + ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY; + ImGuiDir clip_dir = (g.NavTabbingDir < 0) ? ImGuiDir_Up : ImGuiDir_Down; + NavMoveRequestSubmit(ImGuiDir_None, clip_dir, ImGuiNavMoveFlags_Tabbing, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable. + g.NavTabbingResultFirst.Clear(); + g.NavTabbingCounter = -1; +} + +// Apply result from previous frame navigation directional move request. Always called from NavUpdate() +void ImGui::NavMoveRequestApplyResult() +{ + ImGuiContext& g = *GImGui; +#if IMGUI_DEBUG_NAV_SCORING + if (g.NavMoveFlags & ImGuiNavMoveFlags_DebugNoResult) // [DEBUG] Scoring all items in NavWindow at all times + return; #endif -} - -static void ImGui::NavUpdateInitResult() -{ - // In very rare cases g.NavWindow may be null (e.g. clearing focus after requesting an init request, which does happen when releasing Alt while clicking on void) - ImGuiContext& g = *GImGui; - if (!g.NavWindow) - return; - - // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called) - IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name); - SetNavID(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel); - if (g.NavInitRequestFromMove) - { - g.NavDisableHighlight = false; - g.NavDisableMouseHover = g.NavMousePosDirty = true; - } -} - -// Apply result from previous frame navigation directional move request -static void ImGui::NavUpdateMoveResult() -{ - ImGuiContext& g = *GImGui; - if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0) - { - // In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result) - if (g.NavId != 0) - { - g.NavDisableHighlight = false; - g.NavDisableMouseHover = true; - } - return; - } // Select which result to use - ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; + ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : (g.NavMoveResultOther.ID != 0) ? &g.NavMoveResultOther : NULL; + + // Tabbing forward wrap + if (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) + if ((g.NavTabbingCounter == 1 || g.NavTabbingDir == 0) && g.NavTabbingResultFirst.ID) + result = &g.NavTabbingResultFirst; + + // In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result) + if (result == NULL) + { + if (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) + g.NavMoveFlags |= ImGuiNavMoveFlags_DontSetNavHighlight; + if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_DontSetNavHighlight) == 0) + NavRestoreHighlightAfterMove(); + return; + } // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page. - if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) - if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId) - result = &g.NavMoveResultLocalVisibleSet; + if (g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) + if (g.NavMoveResultLocalVisible.ID != 0 && g.NavMoveResultLocalVisible.ID != g.NavId) + result = &g.NavMoveResultLocalVisible; // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules. if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow) @@ -9317,109 +9930,178 @@ static void ImGui::NavUpdateMoveResult() // Scroll to keep newly navigated item fully into view. if (g.NavLayer == ImGuiNavLayer_Main) { - ImVec2 delta_scroll; - if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_ScrollToEdge) + if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdgeY) { + // FIXME: Should remove this float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f; - delta_scroll.y = result->Window->Scroll.y - scroll_target; SetScrollY(result->Window, scroll_target); } else { - ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos); - delta_scroll = ScrollToBringRectIntoView(result->Window, rect_abs); + ImRect rect_abs = WindowRectRelToAbs(result->Window, result->RectRel); + ScrollToRectEx(result->Window, rect_abs, g.NavMoveScrollFlags); } - - // Offset our result position so mouse position can be applied immediately after in NavUpdate() - result->RectRel.TranslateX(-delta_scroll.x); - result->RectRel.TranslateY(-delta_scroll.y); } - ClearActiveID(); g.NavWindow = result->Window; + if (g.ActiveId != result->ID) + ClearActiveID(); if (g.NavId != result->ID) { // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId) g.NavJustMovedToId = result->ID; g.NavJustMovedToFocusScopeId = result->FocusScopeId; - g.NavJustMovedToKeyMods = g.NavMoveRequestKeyMods; + g.NavJustMovedToKeyMods = g.NavMoveKeyMods; } + + // Focus IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name); SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel); - g.NavDisableHighlight = false; - g.NavDisableMouseHover = g.NavMousePosDirty = true; + + // Tabbing: Activates Inputable or Focus non-Inputable + if ((g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) && (result->InFlags & ImGuiItemFlags_Inputable)) + { + g.NavNextActivateId = result->ID; + g.NavNextActivateFlags = ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_TryToPreserveState; + g.NavMoveFlags |= ImGuiNavMoveFlags_DontSetNavHighlight; + } + + // Activate + if (g.NavMoveFlags & ImGuiNavMoveFlags_Activate) + { + g.NavNextActivateId = result->ID; + g.NavNextActivateFlags = ImGuiActivateFlags_None; + } + + // Enable nav highlight + if ((g.NavMoveFlags & ImGuiNavMoveFlags_DontSetNavHighlight) == 0) + NavRestoreHighlightAfterMove(); +} + +// Process NavCancel input (to close a popup, get back to parent, clear focus) +// FIXME: In order to support e.g. Escape to clear a selection we'll need: +// - either to store the equivalent of ActiveIdUsingKeyInputMask for a FocusScope and test for it. +// - either to move most/all of those tests to the epilogue/end functions of the scope they are dealing with (e.g. exit child window in EndChild()) or in EndFrame(), to allow an earlier intercept +static void ImGui::NavUpdateCancelRequest() +{ + ImGuiContext& g = *GImGui; + if (!IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed)) + return; + + IMGUI_DEBUG_LOG_NAV("[nav] ImGuiNavInput_Cancel\n"); + if (g.ActiveId != 0) + { + if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel)) + ClearActiveID(); + } + else if (g.NavLayer != ImGuiNavLayer_Main) + { + // Leave the "menu" layer + NavRestoreLayer(ImGuiNavLayer_Main); + NavRestoreHighlightAfterMove(); + } + else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow) + { + // Exit child window + ImGuiWindow* child_window = g.NavWindow; + ImGuiWindow* parent_window = g.NavWindow->ParentWindow; + IM_ASSERT(child_window->ChildId != 0); + ImRect child_rect = child_window->Rect(); + FocusWindow(parent_window); + SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, WindowRectAbsToRel(parent_window, child_rect)); + NavRestoreHighlightAfterMove(); + } + else if (g.OpenPopupStack.Size > 0) + { + // Close open popup/menu + if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal)) + ClosePopupToLevel(g.OpenPopupStack.Size - 1, true); + } + else + { + // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were + if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow))) + g.NavWindow->NavLastIds[0] = 0; + g.NavId = g.NavFocusScopeId = 0; + } } // Handle PageUp/PageDown/Home/End keys +// Called from NavUpdateCreateMoveRequest() which will use our output to create a move request +// FIXME-NAV: This doesn't work properly with NavFlattened siblings as we use NavWindow rectangle for reference +// FIXME-NAV: how to get Home/End to aim at the beginning/end of a 2D grid? static float ImGui::NavUpdatePageUpPageDown() { ImGuiContext& g = *GImGui; ImGuiIO& io = g.IO; - if (g.NavMoveDir != ImGuiDir_None || g.NavWindow == NULL) - return 0.0f; - if ((g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != ImGuiNavLayer_Main) + ImGuiWindow* window = g.NavWindow; + if ((window->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL) return 0.0f; - ImGuiWindow* window = g.NavWindow; const bool page_up_held = IsKeyDown(io.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp); const bool page_down_held = IsKeyDown(io.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown); const bool home_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home); const bool end_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End); - if (page_up_held != page_down_held || home_pressed != end_pressed) // If either (not both) are pressed + if (page_up_held == page_down_held && home_pressed == end_pressed) // Proceed if either (not both) are pressed, otherwise early out + return 0.0f; + + if (g.NavLayer != ImGuiNavLayer_Main) + NavRestoreLayer(ImGuiNavLayer_Main); + + if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll) { - if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll) + // Fallback manual-scroll when window has no navigable item + if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true)) + SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight()); + else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true)) + SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight()); + else if (home_pressed) + SetScrollY(window, 0.0f); + else if (end_pressed) + SetScrollY(window, window->ScrollMax.y); + } + else + { + ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer]; + const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight()); + float nav_scoring_rect_offset_y = 0.0f; + if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true)) { - // Fallback manual-scroll when window has no navigable item - if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true)) - SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight()); - else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true)) - SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight()); - else if (home_pressed) - SetScrollY(window, 0.0f); - else if (end_pressed) - SetScrollY(window, window->ScrollMax.y); + nav_scoring_rect_offset_y = -page_offset_y; + g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset up, we request the down direction (so we can always land on the last item) + g.NavMoveClipDir = ImGuiDir_Up; + g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; } - else + else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true)) { - ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer]; - const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight()); - float nav_scoring_rect_offset_y = 0.0f; - if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true)) - { - nav_scoring_rect_offset_y = -page_offset_y; - g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset up, we request the down direction (so we can always land on the last item) - g.NavMoveClipDir = ImGuiDir_Up; - g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; - } - else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true)) - { - nav_scoring_rect_offset_y = +page_offset_y; - g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset down, we request the up direction (so we can always land on the last item) - g.NavMoveClipDir = ImGuiDir_Down; - g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; - } - else if (home_pressed) - { - // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y - // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result. - // Preserve current horizontal position if we have any. - nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y; - if (nav_rect_rel.IsInverted()) - nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; - g.NavMoveDir = ImGuiDir_Down; - g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge; - } - else if (end_pressed) - { - nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y; - if (nav_rect_rel.IsInverted()) - nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; - g.NavMoveDir = ImGuiDir_Up; - g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge; - } - return nav_scoring_rect_offset_y; + nav_scoring_rect_offset_y = +page_offset_y; + g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset down, we request the up direction (so we can always land on the last item) + g.NavMoveClipDir = ImGuiDir_Down; + g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; } + else if (home_pressed) + { + // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y + // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdgeY flag, we don't scroll immediately to avoid scrolling happening before nav result. + // Preserve current horizontal position if we have any. + nav_rect_rel.Min.y = nav_rect_rel.Max.y = 0.0f; + if (nav_rect_rel.IsInverted()) + nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; + g.NavMoveDir = ImGuiDir_Down; + g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY; + // FIXME-NAV: MoveClipDir left to _None, intentional? + } + else if (end_pressed) + { + nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ContentSize.y; + if (nav_rect_rel.IsInverted()) + nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; + g.NavMoveDir = ImGuiDir_Up; + g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY; + // FIXME-NAV: MoveClipDir left to _None, intentional? + } + return nav_scoring_rect_offset_y; } return 0.0f; } @@ -9433,63 +10115,74 @@ static void ImGui::NavEndFrame() NavUpdateWindowingOverlay(); // Perform wrap-around in menus - ImGuiWindow* window = g.NavWrapRequestWindow; - ImGuiNavMoveFlags move_flags = g.NavWrapRequestFlags; - if (window != NULL && g.NavWindow == window && NavMoveRequestButNoResultYet() && g.NavMoveRequestForward == ImGuiNavForward_None && g.NavLayer == ImGuiNavLayer_Main) - { - IM_ASSERT(move_flags != 0); // No points calling this with no wrapping - ImRect bb_rel = window->NavRectRel[0]; + // FIXME-NAV: Wrap may need to apply a weight bias on the other axis. e.g. 4x4 grid with 2 last items missing on last item won't handle LoopY/WrapY correctly. + // FIXME-NAV: Wrap (not Loop) support could be handled by the scoring function and then WrapX would function without an extra frame. + const ImGuiNavMoveFlags wanted_flags = ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY; + if (g.NavWindow && NavMoveRequestButNoResultYet() && (g.NavMoveFlags & wanted_flags) && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0) + NavUpdateCreateWrappingRequest(); +} - ImGuiDir clip_dir = g.NavMoveDir; - if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) +static void ImGui::NavUpdateCreateWrappingRequest() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.NavWindow; + + bool do_forward = false; + ImRect bb_rel = window->NavRectRel[g.NavLayer]; + ImGuiDir clip_dir = g.NavMoveDir; + const ImGuiNavMoveFlags move_flags = g.NavMoveFlags; + if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) + { + bb_rel.Min.x = bb_rel.Max.x = window->ContentSize.x + window->WindowPadding.x; + if (move_flags & ImGuiNavMoveFlags_WrapX) { - bb_rel.Min.x = bb_rel.Max.x = - ImMax(window->SizeFull.x, window->ContentSize.x + window->WindowPadding.x * 2.0f) - window->Scroll.x; - if (move_flags & ImGuiNavMoveFlags_WrapX) - { - bb_rel.TranslateY(-bb_rel.GetHeight()); - clip_dir = ImGuiDir_Up; - } - NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); - } - if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) - { - bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x; - if (move_flags & ImGuiNavMoveFlags_WrapX) - { - bb_rel.TranslateY(+bb_rel.GetHeight()); - clip_dir = ImGuiDir_Down; - } - NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); - } - if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) - { - bb_rel.Min.y = bb_rel.Max.y = - ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y; - if (move_flags & ImGuiNavMoveFlags_WrapY) - { - bb_rel.TranslateX(-bb_rel.GetWidth()); - clip_dir = ImGuiDir_Left; - } - NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); - } - if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) - { - bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y; - if (move_flags & ImGuiNavMoveFlags_WrapY) - { - bb_rel.TranslateX(+bb_rel.GetWidth()); - clip_dir = ImGuiDir_Right; - } - NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); + bb_rel.TranslateY(-bb_rel.GetHeight()); // Previous row + clip_dir = ImGuiDir_Up; } + do_forward = true; } + if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) + { + bb_rel.Min.x = bb_rel.Max.x = -window->WindowPadding.x; + if (move_flags & ImGuiNavMoveFlags_WrapX) + { + bb_rel.TranslateY(+bb_rel.GetHeight()); // Next row + clip_dir = ImGuiDir_Down; + } + do_forward = true; + } + if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) + { + bb_rel.Min.y = bb_rel.Max.y = window->ContentSize.y + window->WindowPadding.y; + if (move_flags & ImGuiNavMoveFlags_WrapY) + { + bb_rel.TranslateX(-bb_rel.GetWidth()); // Previous column + clip_dir = ImGuiDir_Left; + } + do_forward = true; + } + if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) + { + bb_rel.Min.y = bb_rel.Max.y = -window->WindowPadding.y; + if (move_flags & ImGuiNavMoveFlags_WrapY) + { + bb_rel.TranslateX(+bb_rel.GetWidth()); // Next column + clip_dir = ImGuiDir_Right; + } + do_forward = true; + } + if (!do_forward) + return; + window->NavRectRel[g.NavLayer] = bb_rel; + NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags, g.NavMoveScrollFlags); } static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) { ImGuiContext& g = *GImGui; + IM_UNUSED(g); int order = window->FocusOrder; + IM_ASSERT(window->RootWindow == window); // No child window (not testing _ChildWindow because of docking) IM_ASSERT(g.WindowsFocusOrder[order] == window); return order; } @@ -9525,6 +10218,8 @@ static void NavUpdateWindowingHighlightWindow(int focus_change_dir) static void ImGui::NavUpdateWindowing() { ImGuiContext& g = *GImGui; + ImGuiIO& io = g.IO; + ImGuiWindow* apply_focus_window = NULL; bool apply_toggle_layer = false; @@ -9536,25 +10231,25 @@ static void ImGui::NavUpdateWindowing() // Fade out if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL) { - g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f); + g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - io.DeltaTime * 10.0f, 0.0f); if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f) g.NavWindowingTargetAnim = NULL; } - // Start CTRL-TAB or Square+L/R window selection - bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); - bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard); + // Start CTRL+Tab or Square+L/R window selection + const bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); + const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && io.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab); if (start_windowing_with_gamepad || start_windowing_with_keyboard) if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) { - g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; // FIXME-DOCK: Will need to use RootWindowDockStop + g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f; - g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true; + g.NavWindowingToggleLayer = start_windowing_with_gamepad ? true : false; // Gamepad starts toggling layer g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad; } // Gamepad update - g.NavWindowingTimer += g.IO.DeltaTime; + g.NavWindowingTimer += io.DeltaTime; if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Gamepad) { // Highlight only appears after a brief time holding the button, so that a fast tap on PadMenu (to toggle NavLayer) doesn't add visual noise @@ -9586,31 +10281,50 @@ static void ImGui::NavUpdateWindowing() // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f if (IsKeyPressedMap(ImGuiKey_Tab, true)) - NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1); - if (!g.IO.KeyCtrl) + NavUpdateWindowingHighlightWindow(io.KeyShift ? +1 : -1); + if (!io.KeyCtrl) apply_focus_window = g.NavWindowingTarget; } // Keyboard: Press and Release ALT to toggle menu layer - // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of backend clearing releases all keys on ALT-TAB - if (IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Pressed)) + // - Testing that only Alt is tested prevents Alt+Shift or AltGR from toggling menu layer. + // - AltGR is normally Alt+Ctrl but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl). But even on keyboards without AltGR we don't want Alt+Ctrl to open menu anyway. + const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; + if (nav_keyboard_active && io.KeyMods == ImGuiKeyModFlags_Alt && (io.KeyModsPrev & ImGuiKeyModFlags_Alt) == 0) + { g.NavWindowingToggleLayer = true; - if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && g.NavWindowingToggleLayer && IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released)) - if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev)) - apply_toggle_layer = true; + g.NavInputSource = ImGuiInputSource_Keyboard; + } + if (g.NavWindowingToggleLayer && g.NavInputSource == ImGuiInputSource_Keyboard) + { + // We cancel toggling nav layer when any text has been typed (generally while holding Alt). (See #370) + // We cancel toggling nav layer when other modifiers are pressed. (See #4439) + if (io.InputQueueCharacters.Size > 0 || io.KeyCtrl || io.KeyShift || io.KeySuper) + g.NavWindowingToggleLayer = false; + + // Apply layer toggle on release + // Important: we don't assume that Alt was previously held in order to handle loss of focus when backend calls io.AddFocusEvent(false) + // Important: as before version <18314 we lacked an explicit IO event for focus gain/loss, we also compare mouse validity to detect old backends clearing mouse pos on focus loss. + if (!(io.KeyMods & ImGuiKeyModFlags_Alt) && (io.KeyModsPrev & ImGuiKeyModFlags_Alt) && g.NavWindowingToggleLayer) + if (g.ActiveId == 0 || g.ActiveIdAllowOverlap) + if (IsMousePosValid(&io.MousePos) == IsMousePosValid(&io.MousePosPrev)) + apply_toggle_layer = true; + if (!io.KeyAlt) + g.NavWindowingToggleLayer = false; + } // Move window if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove)) { ImVec2 move_delta; - if (g.NavInputSource == ImGuiInputSource_Keyboard && !g.IO.KeyShift) - move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down); + if (g.NavInputSource == ImGuiInputSource_Keyboard && !io.KeyShift) + move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_RawKeyboard, ImGuiInputReadMode_Down); if (g.NavInputSource == ImGuiInputSource_Gamepad) move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down); if (move_delta.x != 0.0f || move_delta.y != 0.0f) { const float NAV_MOVE_SPEED = 800.0f; - const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't handle variable framerate very well + const float move_speed = ImFloor(NAV_MOVE_SPEED * io.DeltaTime * ImMin(io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y)); // FIXME: Doesn't handle variable framerate very well ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow; SetWindowPos(moving_window, moving_window->Pos + move_delta * move_speed, ImGuiCond_Always); MarkIniSettingsDirty(moving_window); @@ -9622,16 +10336,21 @@ static void ImGui::NavUpdateWindowing() if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow)) { ClearActiveID(); - g.NavDisableHighlight = false; - g.NavDisableMouseHover = true; + NavRestoreHighlightAfterMove(); apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window); ClosePopupsOverWindow(apply_focus_window, false); FocusWindow(apply_focus_window); if (apply_focus_window->NavLastIds[0] == 0) NavInitWindow(apply_focus_window, false); - // If the window only has a menu layer, select it directly - if (apply_focus_window->DC.NavLayerActiveMask == (1 << ImGuiNavLayer_Menu)) + // If the window has ONLY a menu layer (no main layer), select it directly + // Use NavLayersActiveMaskNext since windows didn't have a chance to be Begin()-ed on this frame, + // so CTRL+Tab where the keys are only held for 1 frame will be able to use correct layers mask since + // the target window as already been previewed once. + // FIXME-NAV: This should be done in NavInit.. or in FocusWindow... However in both of those cases, + // we won't have a guarantee that windows has been visible before and therefore NavLayersActiveMask* + // won't be valid. + if (apply_focus_window->DC.NavLayersActiveMaskNext == (1 << ImGuiNavLayer_Menu)) g.NavLayer = ImGuiNavLayer_Menu; } if (apply_focus_window) @@ -9640,10 +10359,12 @@ static void ImGui::NavUpdateWindowing() // Apply menu/layer toggle if (apply_toggle_layer && g.NavWindow) { + ClearActiveID(); + // Move to parent menu if necessary ImGuiWindow* new_nav_window = g.NavWindow; while (new_nav_window->ParentWindow - && (new_nav_window->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) == 0 + && (new_nav_window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) new_nav_window = new_nav_window->ParentWindow; @@ -9653,14 +10374,17 @@ static void ImGui::NavUpdateWindowing() FocusWindow(new_nav_window); new_nav_window->NavLastChildNavWindow = old_nav_window; } - g.NavDisableHighlight = false; - g.NavDisableMouseHover = true; - // Reinitialize navigation when entering menu bar with the Alt key. - const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main; - if (new_nav_layer == ImGuiNavLayer_Menu) - g.NavWindow->NavLastIds[new_nav_layer] = 0; - NavRestoreLayer(new_nav_layer); + // Toggle layer + const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main; + if (new_nav_layer != g.NavLayer) + { + // Reinitialize navigation when entering menu bar with the Alt key (FIXME: could be a properly of the layer?) + if (new_nav_layer == ImGuiNavLayer_Menu) + g.NavWindow->NavLastIds[new_nav_layer] = 0; + NavRestoreLayer(new_nav_layer); + NavRestoreHighlightAfterMove(); + } } } @@ -9693,6 +10417,7 @@ void ImGui::NavUpdateWindowingOverlay() for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--) { ImGuiWindow* window = g.WindowsFocusOrder[n]; + IM_ASSERT(window != NULL); // Fix static analyzers if (!IsWindowNavFocusable(window)) continue; const char* label = window->Name; @@ -9744,7 +10469,7 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) ImGuiID source_parent_id = 0; if (!(flags & ImGuiDragDropFlags_SourceExtern)) { - source_id = window->DC.LastItemId; + source_id = g.LastItemData.ID; if (source_id != 0) { // Common path: items with ID @@ -9752,34 +10477,33 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) return false; if (g.ActiveIdMouseButton != -1) mouse_button = g.ActiveIdMouseButton; - if (g.IO.MouseDown[mouse_button] == false) + if (g.IO.MouseDown[mouse_button] == false || window->SkipItems) return false; g.ActiveIdAllowOverlap = false; } else { // Uncommon path: items without ID - if (g.IO.MouseDown[mouse_button] == false) + if (g.IO.MouseDown[mouse_button] == false || window->SkipItems) + return false; + if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window)) return false; // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to: - // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride. + // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag. if (!(flags & ImGuiDragDropFlags_SourceAllowNullID)) { IM_ASSERT(0); return false; } - // Early out - if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window)) - return false; - - // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image() + // Magic fallback to handle items with no assigned ID, e.g. Text(), Image() // We build a throwaway ID based on current ID stack + relative AABB of items in window. - // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled. + // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING/RESIZINGG OF THE WIDGET, so if your widget moves your dragging operation will be canceled. // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive. - source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect); - bool is_hovered = ItemHoverable(window->DC.LastItemRect, source_id); + // Rely on keeping other window->LastItemXXX fields intact. + source_id = g.LastItemData.ID = window->GetIDFromRectangle(g.LastItemData.Rect); + bool is_hovered = ItemHoverable(g.LastItemData.Rect, source_id); if (is_hovered && g.IO.MouseClicked[mouse_button]) { SetActiveID(source_id, window); @@ -9793,10 +10517,8 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) source_parent_id = window->IDStack.back(); source_drag_active = IsMouseDragging(mouse_button); - // Disable navigation and key inputs while dragging - g.ActiveIdUsingNavDirMask = ~(ImU32)0; - g.ActiveIdUsingNavInputMask = ~(ImU32)0; - g.ActiveIdUsingKeyInputMask = ~(ImU64)0; + // Disable navigation and key inputs while dragging + cancel existing request if any + SetActiveIdUsingNavAndKeys(); } else { @@ -9831,13 +10553,13 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip)) { ImGuiWindow* tooltip_window = g.CurrentWindow; - tooltip_window->SkipItems = true; + tooltip_window->Hidden = tooltip_window->SkipItems = true; tooltip_window->HiddenFramesCanSkipItems = 1; } } if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern)) - window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect; + g.LastItemData.StatusFlags &= ~ImGuiItemStatusFlags_HoveredRect; return true; } @@ -9937,14 +10659,14 @@ bool ImGui::BeginDragDropTarget() return false; ImGuiWindow* window = g.CurrentWindow; - if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect)) + if (!(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect)) return false; ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow; - if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow) + if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow || window->SkipItems) return false; - const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect; - ImGuiID id = window->DC.LastItemId; + const ImRect& display_rect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? g.LastItemData.DisplayRect : g.LastItemData.Rect; + ImGuiID id = g.LastItemData.ID; if (id == 0) id = window->GetIDFromRectangle(display_rect); if (g.DragDropPayload.SourceId == id) @@ -9986,17 +10708,11 @@ const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDrop } // Render default drop visuals + // FIXME-DRAGDROP: Settle on a proper default visuals for drop target. payload.Preview = was_accepted_previously; flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame) if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview) - { - // FIXME-DRAGDROP: Settle on a proper default visuals for drop target. - r.Expand(3.5f); - bool push_clip_rect = !window->ClipRect.Contains(r); - if (push_clip_rect) window->DrawList->PushClipRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1)); - window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, 0, 2.0f); - if (push_clip_rect) window->DrawList->PopClipRect(); - } + window->DrawList->AddRect(r.Min - ImVec2(3.5f,3.5f), r.Max + ImVec2(3.5f, 3.5f), GetColorU32(ImGuiCol_DragDropTarget), 0.0f, 0, 2.0f); g.DragDropAcceptFrameCount = g.FrameCount; payload.Delivery = was_accepted_previously && !IsMouseDown(g.DragDropMouseButton); // For extern drag sources affecting os window focus, it's easier to just test !IsMouseDown() instead of IsMouseReleased() @@ -10558,8 +11274,9 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings); } IM_ASSERT(settings->ID == window->ID); - settings->Pos = ImVec2ih((short)window->Pos.x, (short)window->Pos.y); - settings->Size = ImVec2ih((short)window->SizeFull.x, (short)window->SizeFull.y); + settings->Pos = ImVec2ih(window->Pos); + settings->Size = ImVec2ih(window->SizeFull); + settings->Collapsed = window->Collapsed; } @@ -10799,6 +11516,7 @@ static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {} // - DebugNodeWindow() [Internal] // - DebugNodeWindowSettings() [Internal] // - DebugNodeWindowsList() [Internal] +// - DebugNodeWindowsListByBeginStackParent() [Internal] //----------------------------------------------------------------------------- #ifndef IMGUI_DISABLE_METRICS_WINDOW @@ -10868,24 +11586,29 @@ static void MetricsHelpMarker(const char* desc) } } +#ifndef IMGUI_DISABLE_DEMO_WINDOWS +namespace ImGui { void ShowFontAtlas(ImFontAtlas* atlas); } +#endif + void ImGui::ShowMetricsWindow(bool* p_open) { - if (!Begin("Dear ImGui Metrics/Debugger", p_open)) + ImGuiContext& g = *GImGui; + ImGuiIO& io = g.IO; + ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig; + if (cfg->ShowStackTool) + ShowStackToolWindow(&cfg->ShowStackTool); + + if (!Begin("Dear ImGui Metrics/Debugger", p_open) || GetCurrentWindow()->BeginCount > 1) { End(); return; } - ImGuiContext& g = *GImGui; - ImGuiIO& io = g.IO; - ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig; - // Basic info Text("Dear ImGui %s", GetVersion()); Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3); - Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows); - Text("%d active allocations", io.MetricsActiveAllocations); + Text("%d visible windows, %d active allocations", io.MetricsRenderWindows, io.MetricsActiveAllocations); //SameLine(); if (SmallButton("GC")) { g.GcCompactAll = true; } Separator(); @@ -10939,11 +11662,10 @@ void ImGui::ShowMetricsWindow(bool* p_open) // Tools if (TreeNode("Tools")) { - // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted. - if (Button("Item Picker..")) - DebugStartItemPicker(); + // Stack Tool is your best friend! + Checkbox("Show stack tool", &cfg->ShowStackTool); SameLine(); - MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash."); + MetricsHelpMarker("You can also call ImGui::ShowStackToolWindow() from your code."); Checkbox("Show windows begin order", &cfg->ShowWindowsBeginOrder); Checkbox("Show windows rectangles", &cfg->ShowWindowsRects); @@ -10961,8 +11683,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) } Unindent(); } - Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh); - Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes); Checkbox("Show tables rectangles", &cfg->ShowTablesRects); SameLine(); @@ -10970,10 +11690,10 @@ void ImGui::ShowMetricsWindow(bool* p_open) cfg->ShowTablesRects |= Combo("##show_table_rects_type", &cfg->ShowTablesRectsType, trt_rects_names, TRT_Count, TRT_Count); if (cfg->ShowTablesRects && g.NavWindow != NULL) { - for (int table_n = 0; table_n < g.Tables.GetSize(); table_n++) + for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++) { - ImGuiTable* table = g.Tables.GetByIndex(table_n); - if (table->LastFrameActive < g.FrameCount - 1 || (table->OuterWindow != g.NavWindow && table->InnerWindow != g.NavWindow)) + ImGuiTable* table = g.Tables.TryGetMapData(table_n); + if (table == NULL || table->LastFrameActive < g.FrameCount - 1 || (table->OuterWindow != g.NavWindow && table->InnerWindow != g.NavWindow)) continue; BulletText("Table 0x%08X (%d columns, in '%s')", table->ID, table->ColumnsCount, table->OuterWindow->Name); @@ -11009,12 +11729,37 @@ void ImGui::ShowMetricsWindow(bool* p_open) } } + // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted. + if (Button("Item Picker..")) + DebugStartItemPicker(); + SameLine(); + MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash."); + TreePop(); } // Windows - DebugNodeWindowsList(&g.Windows, "Windows"); - //DebugNodeWindowsList(&g.WindowsFocusOrder, "WindowsFocusOrder"); + if (TreeNode("Windows", "Windows (%d)", g.Windows.Size)) + { + //SetNextItemOpen(true, ImGuiCond_Once); + DebugNodeWindowsList(&g.Windows, "By display order"); + DebugNodeWindowsList(&g.WindowsFocusOrder, "By focus order (root windows)"); + if (TreeNode("By submission order (begin stack)")) + { + // Here we display windows in their submitted order/hierarchy, however note that the Begin stack doesn't constitute a Parent<>Child relationship! + ImVector& temp_buffer = g.WindowsTempSortBuffer; + temp_buffer.resize(0); + for (int i = 0; i < g.Windows.Size; i++) + if (g.Windows[i]->LastFrameActive + 1 >= g.FrameCount) + temp_buffer.push_back(g.Windows[i]); + struct Func { static int IMGUI_CDECL WindowComparerByBeginOrder(const void* lhs, const void* rhs) { return ((int)(*(const ImGuiWindow* const *)lhs)->BeginOrderWithinContext - (*(const ImGuiWindow* const*)rhs)->BeginOrderWithinContext); } }; + ImQsort(temp_buffer.Data, (size_t)temp_buffer.Size, sizeof(ImGuiWindow*), Func::WindowComparerByBeginOrder); + DebugNodeWindowsListByBeginStackParent(temp_buffer.Data, temp_buffer.Size, NULL); + TreePop(); + } + + TreePop(); + } // DrawLists int drawlist_count = 0; @@ -11022,6 +11767,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) drawlist_count += g.Viewports[viewport_i]->DrawDataBuilder.GetDrawListCount(); if (TreeNode("DrawLists", "DrawLists (%d)", drawlist_count)) { + Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh); + Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes); for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++) { ImGuiViewportP* viewport = g.Viewports[viewport_i]; @@ -11055,22 +11802,36 @@ void ImGui::ShowMetricsWindow(bool* p_open) } // Details for TabBars - if (TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetSize())) + if (TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetAliveCount())) { - for (int n = 0; n < g.TabBars.GetSize(); n++) - DebugNodeTabBar(g.TabBars.GetByIndex(n), "TabBar"); + for (int n = 0; n < g.TabBars.GetMapSize(); n++) + if (ImGuiTabBar* tab_bar = g.TabBars.TryGetMapData(n)) + { + PushID(tab_bar); + DebugNodeTabBar(tab_bar, "TabBar"); + PopID(); + } TreePop(); } // Details for Tables -#ifdef IMGUI_HAS_TABLE - if (TreeNode("Tables", "Tables (%d)", g.Tables.GetSize())) + if (TreeNode("Tables", "Tables (%d)", g.Tables.GetAliveCount())) { - for (int n = 0; n < g.Tables.GetSize(); n++) - DebugNodeTable(g.Tables.GetByIndex(n)); + for (int n = 0; n < g.Tables.GetMapSize(); n++) + if (ImGuiTable* table = g.Tables.TryGetMapData(n)) + DebugNodeTable(table); TreePop(); } -#endif // #ifdef IMGUI_HAS_TABLE + + // Details for Fonts +#ifndef IMGUI_DISABLE_DEMO_WINDOWS + ImFontAtlas* atlas = g.IO.Fonts; + if (TreeNode("Fonts", "Fonts (%d)", atlas->Fonts.Size)) + { + ShowFontAtlas(atlas); + TreePop(); + } +#endif // Details for Docking #ifdef IMGUI_HAS_DOCK @@ -11110,14 +11871,12 @@ void ImGui::ShowMetricsWindow(bool* p_open) TreePop(); } -#ifdef IMGUI_HAS_TABLE if (TreeNode("SettingsTables", "Settings packed data: Tables: %d bytes", g.SettingsTables.size())) { for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings)) DebugNodeTableSettings(settings); TreePop(); } -#endif // #ifdef IMGUI_HAS_TABLE #ifdef IMGUI_HAS_DOCK #endif // #ifdef IMGUI_HAS_DOCK @@ -11147,7 +11906,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) Indent(); Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, input_source_names[g.ActiveIdSource]); Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); - Text("HoveredId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not + Text("ActiveIdUsing: Wheel: %d, NavDirMask: %X, NavInputMask: %X, KeyInputMask: %llX", g.ActiveIdUsingMouseWheel, g.ActiveIdUsingNavDirMask, g.ActiveIdUsingNavInputMask, g.ActiveIdUsingKeyInputMask); + Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Not displaying g.HoveredId as it is update mid-frame Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); Unindent(); @@ -11157,7 +11917,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer); Text("NavInputSource: %s", input_source_names[g.NavInputSource]); Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible); - Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId); + Text("NavActivateId/DownId/PressedId/InputId: %08X/%08X/%08X/%08X", g.NavActivateId, g.NavActivateDownId, g.NavActivatePressedId, g.NavActivateInputId); + Text("NavActivateFlags: %04X", g.NavActivateFlags); Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId); Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL"); @@ -11191,14 +11952,13 @@ void ImGui::ShowMetricsWindow(bool* p_open) } } -#ifdef IMGUI_HAS_TABLE // Overlay: Display Tables Rectangles if (cfg->ShowTablesRects) { - for (int table_n = 0; table_n < g.Tables.GetSize(); table_n++) + for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++) { - ImGuiTable* table = g.Tables.GetByIndex(table_n); - if (table->LastFrameActive < g.FrameCount - 1) + ImGuiTable* table = g.Tables.TryGetMapData(table_n); + if (table == NULL || table->LastFrameActive < g.FrameCount - 1) continue; ImDrawList* draw_list = GetForegroundDrawList(table->OuterWindow); if (cfg->ShowTablesRectsType >= TRT_ColumnsRect) @@ -11218,7 +11978,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) } } } -#endif // #ifdef IMGUI_HAS_TABLE #ifdef IMGUI_HAS_DOCK // Overlay: Display Docking info @@ -11230,6 +11989,25 @@ void ImGui::ShowMetricsWindow(bool* p_open) End(); } +// [DEBUG] List fonts in a font atlas and display its texture +void ImGui::ShowFontAtlas(ImFontAtlas* atlas) +{ + for (int i = 0; i < atlas->Fonts.Size; i++) + { + ImFont* font = atlas->Fonts[i]; + PushID(font); + DebugNodeFont(font); + PopID(); + } + if (TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight)) + { + ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); + ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f); + Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), tint_col, border_col); + TreePop(); + } +} + // [DEBUG] Display contents of Columns void ImGui::DebugNodeColumns(ImGuiOldColumns* columns) { @@ -11260,7 +12038,7 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, } ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list - if (window && IsItemHovered()) + if (window && IsItemHovered() && fg_draw_list) fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); if (!node_open) return; @@ -11339,16 +12117,17 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb) { IM_ASSERT(show_mesh || show_aabb); - ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; - ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + draw_cmd->VtxOffset; // Draw wire-frame version of all triangles ImRect clip_rect = draw_cmd->ClipRect; ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); ImDrawListFlags backup_flags = out_draw_list->Flags; out_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles. - for (unsigned int idx_n = draw_cmd->IdxOffset; idx_n < draw_cmd->IdxOffset + draw_cmd->ElemCount; ) + for (unsigned int idx_n = draw_cmd->IdxOffset, idx_end = draw_cmd->IdxOffset + draw_cmd->ElemCount; idx_n < idx_end; ) { + ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; // We don't hold on those pointers past iterations as ->AddPolyline() may invalidate them if out_draw_list==draw_list + ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + draw_cmd->VtxOffset; + ImVec2 triangle[3]; for (int n = 0; n < 3; n++, idx_n++) vtxs_rect.Add((triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos)); @@ -11364,6 +12143,102 @@ void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, co out_draw_list->Flags = backup_flags; } +// [DEBUG] Display details for a single font, called by ShowStyleEditor(). +void ImGui::DebugNodeFont(ImFont* font) +{ + bool opened = TreeNode(font, "Font: \"%s\"\n%.2f px, %d glyphs, %d file(s)", + font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size, font->ConfigDataCount); + SameLine(); + if (SmallButton("Set as default")) + GetIO().FontDefault = font; + if (!opened) + return; + + // Display preview text + PushFont(font); + Text("The quick brown fox jumps over the lazy dog"); + PopFont(); + + // Display details + SetNextItemWidth(GetFontSize() * 8); + DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f"); + SameLine(); MetricsHelpMarker( + "Note than the default embedded font is NOT meant to be scaled.\n\n" + "Font are currently rendered into bitmaps at a given size at the time of building the atlas. " + "You may oversample them to get some flexibility with scaling. " + "You can also render at multiple sizes and select which one to use at runtime.\n\n" + "(Glimmer of hope: the atlas system will be rewritten in the future to make scaling more flexible.)"); + Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent); + char c_str[5]; + Text("Fallback character: '%s' (U+%04X)", ImTextCharToUtf8(c_str, font->FallbackChar), font->FallbackChar); + Text("Ellipsis character: '%s' (U+%04X)", ImTextCharToUtf8(c_str, font->EllipsisChar), font->EllipsisChar); + const int surface_sqrt = (int)ImSqrt((float)font->MetricsTotalSurface); + Text("Texture Area: about %d px ~%dx%d px", font->MetricsTotalSurface, surface_sqrt, surface_sqrt); + for (int config_i = 0; config_i < font->ConfigDataCount; config_i++) + if (font->ConfigData) + if (const ImFontConfig* cfg = &font->ConfigData[config_i]) + BulletText("Input %d: \'%s\', Oversample: (%d,%d), PixelSnapH: %d, Offset: (%.1f,%.1f)", + config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH, cfg->GlyphOffset.x, cfg->GlyphOffset.y); + + // Display all glyphs of the fonts in separate pages of 256 characters + if (TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size)) + { + ImDrawList* draw_list = GetWindowDrawList(); + const ImU32 glyph_col = GetColorU32(ImGuiCol_Text); + const float cell_size = font->FontSize * 1; + const float cell_spacing = GetStyle().ItemSpacing.y; + for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256) + { + // Skip ahead if a large bunch of glyphs are not present in the font (test in chunks of 4k) + // This is only a small optimization to reduce the number of iterations when IM_UNICODE_MAX_CODEPOINT + // is large // (if ImWchar==ImWchar32 we will do at least about 272 queries here) + if (!(base & 4095) && font->IsGlyphRangeUnused(base, base + 4095)) + { + base += 4096 - 256; + continue; + } + + int count = 0; + for (unsigned int n = 0; n < 256; n++) + if (font->FindGlyphNoFallback((ImWchar)(base + n))) + count++; + if (count <= 0) + continue; + if (!TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, count > 1 ? "glyphs" : "glyph")) + continue; + + // Draw a 16x16 grid of glyphs + ImVec2 base_pos = GetCursorScreenPos(); + for (unsigned int n = 0; n < 256; n++) + { + // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions + // available here and thus cannot easily generate a zero-terminated UTF-8 encoded string. + ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing)); + ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size); + const ImFontGlyph* glyph = font->FindGlyphNoFallback((ImWchar)(base + n)); + draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50)); + if (glyph) + font->RenderChar(draw_list, cell_size, cell_p1, glyph_col, (ImWchar)(base + n)); + if (glyph && IsMouseHoveringRect(cell_p1, cell_p2)) + { + BeginTooltip(); + Text("Codepoint: U+%04X", base + n); + Separator(); + Text("Visible: %d", glyph->Visible); + Text("AdvanceX: %.1f", glyph->AdvanceX); + Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1); + Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1); + EndTooltip(); + } + } + Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16)); + TreePop(); + } + TreePop(); + } + TreePop(); +} + // [DEBUG] Display contents of ImGuiStorage void ImGui::DebugNodeStorage(ImGuiStorage* storage, const char* label) { @@ -11386,9 +12261,16 @@ void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label) const char* buf_end = buf + IM_ARRAYSIZE(buf); const bool is_active = (tab_bar->PrevFrameVisible >= GetFrameCount() - 2); p += ImFormatString(p, buf_end - p, "%s 0x%08X (%d tabs)%s", label, tab_bar->ID, tab_bar->Tabs.Size, is_active ? "" : " *Inactive*"); - IM_UNUSED(p); + p += ImFormatString(p, buf_end - p, " { "); + for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + p += ImFormatString(p, buf_end - p, "%s'%s'", + tab_n > 0 ? ", " : "", (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "???"); + } + p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? " ... }" : " } "); if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); } - bool open = TreeNode(tab_bar, "%s", buf); + bool open = TreeNode(label, "%s", buf); if (!is_active) { PopStyleColor(); } if (is_active && IsItemHovered()) { @@ -11406,7 +12288,7 @@ void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label) if (SmallButton("<")) { TabBarQueueReorder(tab_bar, tab, -1); } SameLine(0, 2); if (SmallButton(">")) { TabBarQueueReorder(tab_bar, tab, +1); } SameLine(); Text("%02d%c Tab 0x%08X '%s' Offset: %.1f, Width: %.1f/%.1f", - tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "", tab->Offset, tab->Width, tab->ContentWidth); + tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "???", tab->Offset, tab->Width, tab->ContentWidth); PopID(); } TreePop(); @@ -11465,12 +12347,19 @@ void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label) BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f) Scrollbar:%s%s", window->Scroll.x, window->ScrollMax.x, window->Scroll.y, window->ScrollMax.y, window->ScrollbarX ? "X" : "", window->ScrollbarY ? "Y" : ""); BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1); BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems); - BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask); - BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL"); - if (!window->NavRectRel[0].IsInverted()) - BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y); - else - BulletText("NavRectRel[0]: "); + for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++) + { + ImRect r = window->NavRectRel[layer]; + if (r.Min.x >= r.Max.y && r.Min.y >= r.Max.y) + { + BulletText("NavLastIds[%d]: 0x%08X", layer, window->NavLastIds[layer]); + continue; + } + BulletText("NavLastIds[%d]: 0x%08X at +(%.1f,%.1f)(%.1f,%.1f)", layer, window->NavLastIds[layer], r.Min.x, r.Min.y, r.Max.x, r.Max.y); + if (IsItemHovered()) + GetForegroundDrawList(window)->AddRect(r.Min + window->Pos, r.Max + window->Pos, IM_COL32(255, 255, 0, 255)); + } + BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL"); if (window->RootWindow != window) { DebugNodeWindow(window->RootWindow, "RootWindow"); } if (window->ParentWindow != NULL) { DebugNodeWindow(window->ParentWindow, "ParentWindow"); } if (window->DC.ChildWindows.Size > 0) { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); } @@ -11494,7 +12383,6 @@ void ImGui::DebugNodeWindowsList(ImVector* windows, const char* la { if (!TreeNode(label, "%s (%d)", label, windows->Size)) return; - Text("(In front-to-back order:)"); for (int i = windows->Size - 1; i >= 0; i--) // Iterate front to back { PushID((*windows)[i]); @@ -11504,12 +12392,214 @@ void ImGui::DebugNodeWindowsList(ImVector* windows, const char* la TreePop(); } +// FIXME-OPT: This is technically suboptimal, but it is simpler this way. +void ImGui::DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack) +{ + for (int i = 0; i < windows_size; i++) + { + ImGuiWindow* window = windows[i]; + if (window->ParentWindowInBeginStack != parent_in_begin_stack) + continue; + char buf[20]; + ImFormatString(buf, IM_ARRAYSIZE(buf), "[%04d] Window", window->BeginOrderWithinContext); + //BulletText("[%04d] Window '%s'", window->BeginOrderWithinContext, window->Name); + DebugNodeWindow(window, buf); + Indent(); + DebugNodeWindowsListByBeginStackParent(windows + i + 1, windows_size - i - 1, window); + Unindent(); + } +} + +//----------------------------------------------------------------------------- +// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, STACK TOOL) +//----------------------------------------------------------------------------- + +// [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack. +void ImGui::UpdateDebugToolItemPicker() +{ + ImGuiContext& g = *GImGui; + g.DebugItemPickerBreakId = 0; + if (!g.DebugItemPickerActive) + return; + + const ImGuiID hovered_id = g.HoveredIdPreviousFrame; + SetMouseCursor(ImGuiMouseCursor_Hand); + if (IsKeyPressedMap(ImGuiKey_Escape)) + g.DebugItemPickerActive = false; + if (IsMouseClicked(0) && hovered_id) + { + g.DebugItemPickerBreakId = hovered_id; + g.DebugItemPickerActive = false; + } + SetNextWindowBgAlpha(0.60f); + BeginTooltip(); + Text("HoveredId: 0x%08X", hovered_id); + Text("Press ESC to abort picking."); + TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!"); + EndTooltip(); +} + +// [DEBUG] Stack Tool: update queries. Called by NewFrame() +void ImGui::UpdateDebugToolStackQueries() +{ + ImGuiContext& g = *GImGui; + ImGuiStackTool* tool = &g.DebugStackTool; + + // Clear hook when stack tool is not visible + g.DebugHookIdInfo = 0; + if (g.FrameCount != tool->LastActiveFrame + 1) + return; + + // Update queries. The steps are: -1: query Stack, >= 0: query each stack item + // We can only perform 1 ID Info query every frame. This is designed so the GetID() tests are cheap and constant-time + const ImGuiID query_id = g.HoveredIdPreviousFrame ? g.HoveredIdPreviousFrame : g.ActiveId; + if (tool->QueryId != query_id) + { + tool->QueryId = query_id; + tool->StackLevel = -1; + tool->Results.resize(0); + } + if (query_id == 0) + return; + + // Advance to next stack level when we got our result, or after 2 frames (in case we never get a result) + int stack_level = tool->StackLevel; + if (stack_level >= 0 && stack_level < tool->Results.Size) + if (tool->Results[stack_level].QuerySuccess || tool->Results[stack_level].QueryFrameCount > 2) + tool->StackLevel++; + + // Update hook + stack_level = tool->StackLevel; + if (stack_level == -1) + g.DebugHookIdInfo = query_id; + if (stack_level >= 0 && stack_level < tool->Results.Size) + { + g.DebugHookIdInfo = tool->Results[stack_level].ID; + tool->Results[stack_level].QueryFrameCount++; + } +} + +// [DEBUG] Stack tool: hooks called by GetID() family functions +void ImGui::DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiStackTool* tool = &g.DebugStackTool; + + // Step 0: stack query + // This assume that the ID was computed with the current ID stack, which tends to be the case for our widget. + if (tool->StackLevel == -1) + { + tool->StackLevel++; + tool->Results.resize(window->IDStack.Size + 1, ImGuiStackLevelInfo()); + for (int n = 0; n < window->IDStack.Size + 1; n++) + tool->Results[n].ID = (n < window->IDStack.Size) ? window->IDStack[n] : id; + return; + } + + // Step 1+: query for individual level + IM_ASSERT(tool->StackLevel >= 0); + if (tool->StackLevel != window->IDStack.Size) + return; + ImGuiStackLevelInfo* info = &tool->Results[tool->StackLevel]; + IM_ASSERT(info->ID == id && info->QueryFrameCount > 0); + + int data_len; + switch (data_type) + { + case ImGuiDataType_S32: + ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "%d", (int)(intptr_t)data_id); + break; + case ImGuiDataType_String: + data_len = data_id_end ? (int)((const char*)data_id_end - (const char*)data_id) : (int)strlen((const char*)data_id); + ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "\"%.*s\"", data_len, (const char*)data_id); + break; + case ImGuiDataType_Pointer: + ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "(void*)0x%p", data_id); + break; + case ImGuiDataType_ID: + if (info->Desc[0] == 0) // PushOverrideID() is often used to avoid hashing twice, which would lead to 2 calls to DebugHookIdInfo(). We prioritize the first one. + ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "0x%08X [override]", id); + break; + default: + IM_ASSERT(0); + } + info->QuerySuccess = true; +} + +// Stack Tool: Display UI +void ImGui::ShowStackToolWindow(bool* p_open) +{ + ImGuiContext& g = *GImGui; + if (!(g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)) + SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 8.0f), ImGuiCond_FirstUseEver); + if (!Begin("Dear ImGui Stack Tool", p_open) || GetCurrentWindow()->BeginCount > 1) + { + End(); + return; + } + + // Display hovered/active status + const ImGuiID hovered_id = g.HoveredIdPreviousFrame; + const ImGuiID active_id = g.ActiveId; +#ifdef IMGUI_ENABLE_TEST_ENGINE + Text("HoveredId: 0x%08X (\"%s\"), ActiveId: 0x%08X (\"%s\")", hovered_id, hovered_id ? ImGuiTestEngine_FindItemDebugLabel(&g, hovered_id) : "", active_id, active_id ? ImGuiTestEngine_FindItemDebugLabel(&g, active_id) : ""); +#else + Text("HoveredId: 0x%08X, ActiveId: 0x%08X", hovered_id, active_id); +#endif + SameLine(); + MetricsHelpMarker("Hover an item with the mouse to display elements of the ID Stack leading to the item's final ID.\nEach level of the stack correspond to a PushID() call.\nAll levels of the stack are hashed together to make the final ID of a widget (ID displayed at the bottom level of the stack).\nRead FAQ entry about the ID stack for details."); + + // Display decorated stack + ImGuiStackTool* tool = &g.DebugStackTool; + tool->LastActiveFrame = g.FrameCount; + if (tool->Results.Size > 0 && BeginTable("##table", 3, ImGuiTableFlags_Borders)) + { + const float id_width = CalcTextSize("0xDDDDDDDD").x; + TableSetupColumn("Seed", ImGuiTableColumnFlags_WidthFixed, id_width); + TableSetupColumn("PushID", ImGuiTableColumnFlags_WidthStretch); + TableSetupColumn("Result", ImGuiTableColumnFlags_WidthFixed, id_width); + TableHeadersRow(); + for (int n = 0; n < tool->Results.Size; n++) + { + ImGuiStackLevelInfo* info = &tool->Results[n]; + TableNextColumn(); + Text("0x%08X", (n > 0) ? tool->Results[n - 1].ID : 0); + + TableNextColumn(); + ImGuiWindow* window = (info->Desc[0] == 0 && n == 0) ? FindWindowByID(info->ID) : NULL; + if (window) // Source: window name (because the root ID don't call GetID() and so doesn't get hooked) + Text("\"%s\" [window]", window->Name); + else if (info->QuerySuccess) // Source: GetID() hooks (prioritize over ItemInfo() because we frequently use patterns like: PushID(str), Button("") where they both have same id) + TextUnformatted(info->Desc); + else if (tool->StackLevel >= tool->Results.Size) // Only start using fallback below when all queries are done, so during queries we don't flickering ??? markers. + { +#ifdef IMGUI_ENABLE_TEST_ENGINE + if (const char* label = ImGuiTestEngine_FindItemDebugLabel(&g, info->ID)) // Source: ImGuiTestEngine's ItemInfo() + Text("??? \"%s\"", label); + else +#endif + TextUnformatted("???"); + } + + TableNextColumn(); + Text("0x%08X", info->ID); + if (n == tool->Results.Size - 1) + TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_Header)); + } + EndTable(); + } + End(); +} + #else void ImGui::ShowMetricsWindow(bool*) {} +void ImGui::ShowFontAtlas(ImFontAtlas*) {} void ImGui::DebugNodeColumns(ImGuiOldColumns*) {} void ImGui::DebugNodeDrawList(ImGuiWindow*, const ImDrawList*, const char*) {} void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList*, const ImDrawList*, const ImDrawCmd*, bool, bool) {} +void ImGui::DebugNodeFont(ImFont*) {} void ImGui::DebugNodeStorage(ImGuiStorage*, const char*) {} void ImGui::DebugNodeTabBar(ImGuiTabBar*, const char*) {} void ImGui::DebugNodeWindow(ImGuiWindow*, const char*) {} @@ -11517,7 +12607,12 @@ void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings*) {} void ImGui::DebugNodeWindowsList(ImVector*, const char*) {} void ImGui::DebugNodeViewport(ImGuiViewportP*) {} -#endif +void ImGui::ShowStackToolWindow(bool*) {} +void ImGui::DebugHookIdInfo(ImGuiID, ImGuiDataType, const void*, const void*) {} +void ImGui::UpdateDebugToolItemPicker() {} +void ImGui::UpdateDebugToolStackQueries() {} + +#endif // #ifndef IMGUI_DISABLE_METRICS_WINDOW //----------------------------------------------------------------------------- diff --git a/external/imgui/src/imgui_demo.cpp b/r5dev/thirdparty/imgui/src/imgui_demo.cpp similarity index 95% rename from external/imgui/src/imgui_demo.cpp rename to r5dev/thirdparty/imgui/src/imgui_demo.cpp index 6cf6848e..ddb06af6 100644 --- a/external/imgui/src/imgui_demo.cpp +++ b/r5dev/thirdparty/imgui/src/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.83 WIP +// dear imgui, v1.86 // (demo code) // Help: @@ -75,7 +75,7 @@ Index of this file: #define _CRT_SECURE_NO_WARNINGS #endif -#include "imgui.h" +#include "thirdparty/imgui/include/imgui.h" #ifndef IMGUI_DISABLE // System includes @@ -92,7 +92,8 @@ Index of this file: // Visual Studio warnings #ifdef _MSC_VER -#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen +#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen +#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). #endif // Clang/GCC warnings with -Weverything @@ -199,6 +200,14 @@ static void HelpMarker(const char* desc) } } +// Helper to wire demo markers located in code to a interactive browser +typedef void (*ImGuiDemoMarkerCallback)(const char* file, int line, const char* section, void* user_data); +extern ImGuiDemoMarkerCallback GImGuiDemoMarkerCallback; +extern void* GImGuiDemoMarkerCallbackUserData; +ImGuiDemoMarkerCallback GImGuiDemoMarkerCallback = NULL; +void* GImGuiDemoMarkerCallbackUserData = NULL; +#define IMGUI_DEMO_MARKER(section) do { if (GImGuiDemoMarkerCallback != NULL) GImGuiDemoMarkerCallback(__FILE__, __LINE__, section, GImGuiDemoMarkerCallbackUserData); } while (0) + // Helper to display basic user controls. void ImGui::ShowUserGuide() { @@ -209,7 +218,8 @@ void ImGui::ShowUserGuide() "(double-click to auto fit window to its contents)."); ImGui::BulletText("CTRL+Click on a slider or drag box to input value as text."); ImGui::BulletText("TAB/SHIFT+TAB to cycle through keyboard editable fields."); - if (io.FontAllowUserScaling) + ImGui::BulletText("CTRL+Tab to select a window."); + if (io.FontAllowUserScaling) ImGui::BulletText("CTRL+Mouse Wheel to zoom window contents."); ImGui::BulletText("While inputing text:\n"); ImGui::Indent(); @@ -227,7 +237,6 @@ void ImGui::ShowUserGuide() ImGui::BulletText("Return to input text into a widget."); ImGui::BulletText("Escape to deactivate a widget, close popup, exit child window."); ImGui::BulletText("Alt to jump to the menu layer of a window."); - ImGui::BulletText("CTRL+Tab to select a window."); ImGui::Unindent(); } @@ -293,10 +302,12 @@ void ImGui::ShowDemoWindow(bool* p_open) // Dear ImGui Apps (accessible from the "Tools" menu) static bool show_app_metrics = false; + static bool show_app_stack_tool = false; static bool show_app_style_editor = false; static bool show_app_about = false; if (show_app_metrics) { ImGui::ShowMetricsWindow(&show_app_metrics); } + if (show_app_stack_tool) { ImGui::ShowStackToolWindow(&show_app_stack_tool); } if (show_app_about) { ImGui::ShowAboutWindow(&show_app_about); } if (show_app_style_editor) { @@ -316,6 +327,7 @@ void ImGui::ShowDemoWindow(bool* p_open) static bool no_nav = false; static bool no_background = false; static bool no_bring_to_front = false; + static bool unsaved_document = false; ImGuiWindowFlags window_flags = 0; if (no_titlebar) window_flags |= ImGuiWindowFlags_NoTitleBar; @@ -327,6 +339,7 @@ void ImGui::ShowDemoWindow(bool* p_open) if (no_nav) window_flags |= ImGuiWindowFlags_NoNav; if (no_background) window_flags |= ImGuiWindowFlags_NoBackground; if (no_bring_to_front) window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus; + if (unsaved_document) window_flags |= ImGuiWindowFlags_UnsavedDocument; if (no_close) p_open = NULL; // Don't pass our bool* to Begin // We specify a default position/size in case there's no data in the .ini file. @@ -356,11 +369,13 @@ void ImGui::ShowDemoWindow(bool* p_open) { if (ImGui::BeginMenu("Menu")) { + IMGUI_DEMO_MARKER("Menu/File"); ShowExampleMenuFile(); ImGui::EndMenu(); } if (ImGui::BeginMenu("Examples")) { + IMGUI_DEMO_MARKER("Menu/Examples"); ImGui::MenuItem("Main menu bar", NULL, &show_app_main_menu_bar); ImGui::MenuItem("Console", NULL, &show_app_console); ImGui::MenuItem("Log", NULL, &show_app_log); @@ -376,9 +391,14 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::MenuItem("Documents", NULL, &show_app_documents); ImGui::EndMenu(); } + //if (ImGui::MenuItem("MenuItem")) {} // You can also use MenuItem() inside a menu bar! if (ImGui::BeginMenu("Tools")) { + IMGUI_DEMO_MARKER("Menu/Tools"); +#ifndef IMGUI_DISABLE_METRICS_WINDOW ImGui::MenuItem("Metrics/Debugger", NULL, &show_app_metrics); + ImGui::MenuItem("Stack Tool", NULL, &show_app_stack_tool); +#endif ImGui::MenuItem("Style Editor", NULL, &show_app_style_editor); ImGui::MenuItem("About Dear ImGui", NULL, &show_app_about); ImGui::EndMenu(); @@ -389,6 +409,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Text("dear imgui says hello. (%s)", IMGUI_VERSION); ImGui::Spacing(); + IMGUI_DEMO_MARKER("Help"); if (ImGui::CollapsingHeader("Help")) { ImGui::Text("ABOUT THIS DEMO:"); @@ -411,6 +432,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::ShowUserGuide(); } + IMGUI_DEMO_MARKER("Configuration"); if (ImGui::CollapsingHeader("Configuration")) { ImGuiIO& io = ImGui::GetIO(); @@ -451,11 +473,12 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Separator(); } + IMGUI_DEMO_MARKER("Configuration/Backend Flags"); if (ImGui::TreeNode("Backend Flags")) { HelpMarker( "Those flags are set by the backends (imgui_impl_xxx files) to specify their capabilities.\n" - "Here we expose then as read-only fields to avoid breaking interactions with your backend."); + "Here we expose them as read-only fields to avoid breaking interactions with your backend."); // Make a local copy to avoid modifying actual backend flags. ImGuiBackendFlags backend_flags = io.BackendFlags; @@ -467,6 +490,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Separator(); } + IMGUI_DEMO_MARKER("Configuration/Style"); if (ImGui::TreeNode("Style")) { HelpMarker("The same contents can be accessed in 'Tools->Style Editor' or by calling the ShowStyleEditor() function."); @@ -475,6 +499,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Separator(); } + IMGUI_DEMO_MARKER("Configuration/Capture, Logging"); if (ImGui::TreeNode("Capture/Logging")) { HelpMarker( @@ -494,6 +519,7 @@ void ImGui::ShowDemoWindow(bool* p_open) } } + IMGUI_DEMO_MARKER("Window options"); if (ImGui::CollapsingHeader("Window options")) { if (ImGui::BeginTable("split", 3)) @@ -508,6 +534,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::TableNextColumn(); ImGui::Checkbox("No nav", &no_nav); ImGui::TableNextColumn(); ImGui::Checkbox("No background", &no_background); ImGui::TableNextColumn(); ImGui::Checkbox("No bring to front", &no_bring_to_front); + ImGui::TableNextColumn(); ImGui::Checkbox("Unsaved document", &unsaved_document); ImGui::EndTable(); } } @@ -526,11 +553,18 @@ void ImGui::ShowDemoWindow(bool* p_open) static void ShowDemoWindowWidgets() { + IMGUI_DEMO_MARKER("Widgets"); if (!ImGui::CollapsingHeader("Widgets")) return; + static bool disable_all = false; // The Checkbox for that is inside the "Disabled" section at the bottom + if (disable_all) + ImGui::BeginDisabled(); + + IMGUI_DEMO_MARKER("Widgets/Basic"); if (ImGui::TreeNode("Basic")) { + IMGUI_DEMO_MARKER("Widgets/Basic/Button"); static int clicked = 0; if (ImGui::Button("Button")) clicked++; @@ -540,15 +574,18 @@ static void ShowDemoWindowWidgets() ImGui::Text("Thanks for clicking me!"); } + IMGUI_DEMO_MARKER("Widgets/Basic/Checkbox"); static bool check = true; ImGui::Checkbox("checkbox", &check); + IMGUI_DEMO_MARKER("Widgets/Basic/RadioButton"); static int e = 0; ImGui::RadioButton("radio a", &e, 0); ImGui::SameLine(); ImGui::RadioButton("radio b", &e, 1); ImGui::SameLine(); ImGui::RadioButton("radio c", &e, 2); // Color buttons, demonstrate using PushID() to add unique identifier in the ID stack, and changing style. + IMGUI_DEMO_MARKER("Widgets/Basic/Buttons (Colored)"); for (int i = 0; i < 7; i++) { if (i > 0) @@ -570,6 +607,7 @@ static void ShowDemoWindowWidgets() ImGui::SameLine(); // Arrow buttons with Repeater + IMGUI_DEMO_MARKER("Widgets/Basic/Buttons (Repeating)"); static int counter = 0; float spacing = ImGui::GetStyle().ItemInnerSpacing.x; ImGui::PushButtonRepeat(true); @@ -580,6 +618,7 @@ static void ShowDemoWindowWidgets() ImGui::SameLine(); ImGui::Text("%d", counter); + IMGUI_DEMO_MARKER("Widgets/Basic/Tooltips"); ImGui::Text("Hover over me"); if (ImGui::IsItemHovered()) ImGui::SetTooltip("I am a tooltip"); @@ -596,12 +635,12 @@ static void ShowDemoWindowWidgets() } ImGui::Separator(); - ImGui::LabelText("label", "Value"); { // Using the _simplified_ one-liner Combo() api here // See "Combo" section for examples of how to use the more flexible BeginCombo()/EndCombo() api. + IMGUI_DEMO_MARKER("Widgets/Basic/Combo"); const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIIIIII", "JJJJ", "KKKKKKK" }; static int item_current = 0; ImGui::Combo("combo", &item_current, items, IM_ARRAYSIZE(items)); @@ -612,6 +651,7 @@ static void ShowDemoWindowWidgets() { // To wire InputText() with std::string or any other custom string type, // see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file. + IMGUI_DEMO_MARKER("Widgets/Basic/InputText"); static char str0[128] = "Hello, world!"; ImGui::InputText("input text", str0, IM_ARRAYSIZE(str0)); ImGui::SameLine(); HelpMarker( @@ -630,6 +670,7 @@ static void ShowDemoWindowWidgets() static char str1[128] = ""; ImGui::InputTextWithHint("input text (w/ hint)", "enter text here", str1, IM_ARRAYSIZE(str1)); + IMGUI_DEMO_MARKER("Widgets/Basic/InputInt, InputFloat"); static int i0 = 123; ImGui::InputInt("input int", &i0); ImGui::SameLine(); HelpMarker( @@ -654,6 +695,7 @@ static void ShowDemoWindowWidgets() } { + IMGUI_DEMO_MARKER("Widgets/Basic/DragInt, DragFloat"); static int i1 = 50, i2 = 42; ImGui::DragInt("drag int", &i1, 1); ImGui::SameLine(); HelpMarker( @@ -669,6 +711,7 @@ static void ShowDemoWindowWidgets() } { + IMGUI_DEMO_MARKER("Widgets/Basic/SliderInt, SliderFloat"); static int i1 = 0; ImGui::SliderInt("slider int", &i1, -1, 3); ImGui::SameLine(); HelpMarker("CTRL+click to input value."); @@ -677,12 +720,14 @@ static void ShowDemoWindowWidgets() ImGui::SliderFloat("slider float", &f1, 0.0f, 1.0f, "ratio = %.3f"); ImGui::SliderFloat("slider float (log)", &f2, -10.0f, 10.0f, "%.4f", ImGuiSliderFlags_Logarithmic); + IMGUI_DEMO_MARKER("Widgets/Basic/SliderAngle"); static float angle = 0.0f; ImGui::SliderAngle("slider angle", &angle); // Using the format string to display a name instead of an integer. // Here we completely omit '%d' from the format string, so it'll only display a name. // This technique can also be used with DragInt(). + IMGUI_DEMO_MARKER("Widgets/Basic/Slider (enum)"); enum Element { Element_Fire, Element_Earth, Element_Air, Element_Water, Element_COUNT }; static int elem = Element_Fire; const char* elems_names[Element_COUNT] = { "Fire", "Earth", "Air", "Water" }; @@ -692,6 +737,7 @@ static void ShowDemoWindowWidgets() } { + IMGUI_DEMO_MARKER("Widgets/Basic/ColorEdit3, ColorEdit4"); static float col1[3] = { 1.0f, 0.0f, 0.2f }; static float col2[4] = { 0.4f, 0.7f, 0.0f, 0.5f }; ImGui::ColorEdit3("color 1", col1); @@ -707,6 +753,7 @@ static void ShowDemoWindowWidgets() { // Using the _simplified_ one-liner ListBox() api here // See "List boxes" section for examples of how to use the more flexible BeginListBox()/EndListBox() api. + IMGUI_DEMO_MARKER("Widgets/Basic/ListBox"); const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" }; static int item_current = 1; ImGui::ListBox("listbox", &item_current, items, IM_ARRAYSIZE(items), 4); @@ -723,8 +770,10 @@ static void ShowDemoWindowWidgets() // if (once) // ImGui::Text("This will be displayed only once."); + IMGUI_DEMO_MARKER("Widgets/Trees"); if (ImGui::TreeNode("Trees")) { + IMGUI_DEMO_MARKER("Widgets/Trees/Basic trees"); if (ImGui::TreeNode("Basic trees")) { for (int i = 0; i < 5; i++) @@ -745,6 +794,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Trees/Advanced, with Selectable nodes"); if (ImGui::TreeNode("Advanced, with Selectable nodes")) { HelpMarker( @@ -772,6 +822,7 @@ static void ShowDemoWindowWidgets() for (int i = 0; i < 6; i++) { // Disable the default "open on single-click behavior" + set Selected flag according to our selection. + // To alter selection we use IsItemClicked() && !IsItemToggledOpen(), so clicking on an arrow doesn't alter selection. ImGuiTreeNodeFlags node_flags = base_flags; const bool is_selected = (selection_mask & (1 << i)) != 0; if (is_selected) @@ -780,7 +831,7 @@ static void ShowDemoWindowWidgets() { // Items 0..2 are Tree Node bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Node %d", i); - if (ImGui::IsItemClicked()) + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) node_clicked = i; if (test_drag_and_drop && ImGui::BeginDragDropSource()) { @@ -801,7 +852,7 @@ static void ShowDemoWindowWidgets() // use BulletText() or advance the cursor by GetTreeNodeToLabelSpacing() and call Text(). node_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; // ImGuiTreeNodeFlags_Bullet ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Leaf %d", i); - if (ImGui::IsItemClicked()) + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) node_clicked = i; if (test_drag_and_drop && ImGui::BeginDragDropSource()) { @@ -827,6 +878,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Collapsing Headers"); if (ImGui::TreeNode("Collapsing Headers")) { static bool closable_group = true; @@ -850,6 +902,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Bullets"); if (ImGui::TreeNode("Bullets")) { ImGui::BulletText("Bullet point 1"); @@ -864,8 +917,10 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Text"); if (ImGui::TreeNode("Text")) { + IMGUI_DEMO_MARKER("Widgets/Text/Colored Text"); if (ImGui::TreeNode("Colorful Text")) { // Using shortcut. You can use PushStyleColor()/PopStyleColor() for more flexibility. @@ -876,6 +931,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Text/Word Wrapping"); if (ImGui::TreeNode("Word Wrapping")) { // Using shortcut. You can use PushTextWrapPos()/PopTextWrapPos() for more flexibility. @@ -909,6 +965,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Text/UTF-8 Text"); if (ImGui::TreeNode("UTF-8 Text")) { // UTF-8 test with Japanese characters @@ -935,6 +992,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Images"); if (ImGui::TreeNode("Images")) { ImGuiIO& io = ImGui::GetIO(); @@ -988,6 +1046,8 @@ static void ShowDemoWindowWidgets() ImGui::EndTooltip(); } } + + IMGUI_DEMO_MARKER("Widgets/Images/Textured buttons"); ImGui::TextWrapped("And now some textured buttons.."); static int pressed_count = 0; for (int i = 0; i < 8; i++) @@ -1009,6 +1069,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Combo"); if (ImGui::TreeNode("Combo")) { // Expose flags as checkbox for the demo @@ -1025,8 +1086,8 @@ static void ShowDemoWindowWidgets() // stored in the object itself, etc.) const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" }; static int item_current_idx = 0; // Here we store our selection data as an index. - const char* combo_label = items[item_current_idx]; // Label to preview before opening the combo (technically it could be anything) - if (ImGui::BeginCombo("combo 1", combo_label, flags)) + const char* combo_preview_value = items[item_current_idx]; // Pass in the preview value visible before opening the combo (it could be anything) + if (ImGui::BeginCombo("combo 1", combo_preview_value, flags)) { for (int n = 0; n < IM_ARRAYSIZE(items); n++) { @@ -1042,10 +1103,12 @@ static void ShowDemoWindowWidgets() } // Simplified one-liner Combo() API, using values packed in a single constant string + // This is a convenience for when the selection set is small and known at compile-time. static int item_current_2 = 0; ImGui::Combo("combo 2 (one-liner)", &item_current_2, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0"); // Simplified one-liner Combo() using an array of const char* + // This is not very useful (may obsolete): prefer using BeginCombo()/EndCombo() for full control. static int item_current_3 = -1; // If the selection isn't within 0..count, Combo won't display a preview ImGui::Combo("combo 3 (array)", &item_current_3, items, IM_ARRAYSIZE(items)); @@ -1057,6 +1120,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/List Boxes"); if (ImGui::TreeNode("List boxes")) { // Using the generic BeginListBox() API, you have full control over how to display the combo contents. @@ -1099,6 +1163,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Selectables"); if (ImGui::TreeNode("Selectables")) { // Selectable() has 2 overloads: @@ -1107,18 +1172,20 @@ static void ShowDemoWindowWidgets() // - The one taking "bool* p_selected" as a read-write selection information (convenient in some cases) // The earlier is more flexible, as in real application your selection may be stored in many different ways // and not necessarily inside a bool value (e.g. in flags within objects, as an external list, etc). + IMGUI_DEMO_MARKER("Widgets/Selectables/Basic"); if (ImGui::TreeNode("Basic")) { static bool selection[5] = { false, true, false, false, false }; ImGui::Selectable("1. I am selectable", &selection[0]); ImGui::Selectable("2. I am selectable", &selection[1]); - ImGui::Text("3. I am not selectable"); + ImGui::Text("(I am not selectable)"); ImGui::Selectable("4. I am selectable", &selection[3]); if (ImGui::Selectable("5. I am double clickable", selection[4], ImGuiSelectableFlags_AllowDoubleClick)) if (ImGui::IsMouseDoubleClicked(0)) selection[4] = !selection[4]; ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Selectables/Single Selection"); if (ImGui::TreeNode("Selection State: Single Selection")) { static int selected = -1; @@ -1131,6 +1198,7 @@ static void ShowDemoWindowWidgets() } ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Selectables/Multiple Selection"); if (ImGui::TreeNode("Selection State: Multiple Selection")) { HelpMarker("Hold CTRL and click to select multiple items."); @@ -1148,6 +1216,7 @@ static void ShowDemoWindowWidgets() } ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Selectables/Rendering more text into the same line"); if (ImGui::TreeNode("Rendering more text into the same line")) { // Using the Selectable() override that takes "bool* p_selected" parameter, @@ -1158,11 +1227,12 @@ static void ShowDemoWindowWidgets() ImGui::Selectable("Hello.h", &selected[2]); ImGui::SameLine(300); ImGui::Text(" 2,345 bytes"); ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Selectables/In columns"); if (ImGui::TreeNode("In columns")) { static bool selected[10] = {}; - if (ImGui::BeginTable("split1", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings)) + if (ImGui::BeginTable("split1", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders)) { for (int i = 0; i < 10; i++) { @@ -1173,8 +1243,8 @@ static void ShowDemoWindowWidgets() } ImGui::EndTable(); } - ImGui::Separator(); - if (ImGui::BeginTable("split2", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings)) + ImGui::Spacing(); + if (ImGui::BeginTable("split2", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders)) { for (int i = 0; i < 10; i++) { @@ -1192,6 +1262,7 @@ static void ShowDemoWindowWidgets() } ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Selectables/Grid"); if (ImGui::TreeNode("Grid")) { static char selected[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } }; @@ -1224,6 +1295,7 @@ static void ShowDemoWindowWidgets() ImGui::PopStyleVar(); ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Selectables/Alignment"); if (ImGui::TreeNode("Alignment")) { HelpMarker( @@ -1251,8 +1323,10 @@ static void ShowDemoWindowWidgets() // To wire InputText() with std::string or any other custom string type, // see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file. + IMGUI_DEMO_MARKER("Widgets/Text Input"); if (ImGui::TreeNode("Text Input")) { + IMGUI_DEMO_MARKER("Widgets/Text Input/Multi-line Text Input"); if (ImGui::TreeNode("Multi-line Text Input")) { // Note: we are using a fixed-sized buffer for simplicity here. See ImGuiInputTextFlags_CallbackResize @@ -1278,6 +1352,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Text Input/Filtered Text Input"); if (ImGui::TreeNode("Filtered Text Input")) { struct TextFilters @@ -1300,6 +1375,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Text Input/Password input"); if (ImGui::TreeNode("Password Input")) { static char password[64] = "password123"; @@ -1366,6 +1442,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Text Input/Resize Callback"); if (ImGui::TreeNode("Resize Callback")) { // To wire InputText() with std::string or any other custom string type, @@ -1412,8 +1489,10 @@ static void ShowDemoWindowWidgets() } // Tabs + IMGUI_DEMO_MARKER("Widgets/Tabs"); if (ImGui::TreeNode("Tabs")) { + IMGUI_DEMO_MARKER("Widgets/Tabs/Basic"); if (ImGui::TreeNode("Basic")) { ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_None; @@ -1440,6 +1519,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Tabs/Advanced & Close Button"); if (ImGui::TreeNode("Advanced & Close Button")) { // Expose a couple of the available flags. In most cases you may just call BeginTabBar() with no flags (0). @@ -1482,6 +1562,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Tabs/TabItemButton & Leading-Trailing flags"); if (ImGui::TreeNode("TabItemButton & Leading/Trailing flags")) { static ImVector active_tabs; @@ -1551,16 +1632,19 @@ static void ShowDemoWindowWidgets() } // Plot/Graph widgets are not very good. - // Consider writing your own, or using a third-party one, see: - // - ImPlot https://github.com/epezent/implot - // - others https://github.com/ocornut/imgui/wiki/Useful-Widgets - if (ImGui::TreeNode("Plots Widgets")) + // Consider using a third-party library such as ImPlot: https://github.com/epezent/implot + // (see others https://github.com/ocornut/imgui/wiki/Useful-Extensions) + IMGUI_DEMO_MARKER("Widgets/Plotting"); + if (ImGui::TreeNode("Plotting")) { static bool animate = true; ImGui::Checkbox("Animate", &animate); + // Plot as lines and plot as histogram + IMGUI_DEMO_MARKER("Widgets/Plotting/PlotLines, PlotHistogram"); static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; ImGui::PlotLines("Frame Times", arr, IM_ARRAYSIZE(arr)); + ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0, 80.0f)); // Fill an array of contiguous float values to plot // Tip: If your float aren't contiguous but part of a structure, you can pass a pointer to your first float @@ -1590,7 +1674,6 @@ static void ShowDemoWindowWidgets() sprintf(overlay, "avg %f", average); ImGui::PlotLines("Lines", values, IM_ARRAYSIZE(values), values_offset, overlay, -1.0f, 1.0f, ImVec2(0, 80.0f)); } - ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0, 80.0f)); // Use functions to generate output // FIXME: This is rather awkward because current plot API only pass in indices. @@ -1602,7 +1685,7 @@ static void ShowDemoWindowWidgets() }; static int func_type = 0, display_count = 70; ImGui::Separator(); - ImGui::SetNextItemWidth(100); + ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); ImGui::Combo("func", &func_type, "Sin\0Saw\0"); ImGui::SameLine(); ImGui::SliderInt("Sample count", &display_count, 1, 400); @@ -1612,6 +1695,7 @@ static void ShowDemoWindowWidgets() ImGui::Separator(); // Animate a simple progress bar + IMGUI_DEMO_MARKER("Widgets/Plotting/ProgressBar"); static float progress = 0.0f, progress_dir = 1.0f; if (animate) { @@ -1633,6 +1717,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Color"); if (ImGui::TreeNode("Color/Picker Widgets")) { static ImVec4 color = ImVec4(114.0f / 255.0f, 144.0f / 255.0f, 154.0f / 255.0f, 200.0f / 255.0f); @@ -1649,18 +1734,22 @@ static void ShowDemoWindowWidgets() ImGui::Checkbox("With HDR", &hdr); ImGui::SameLine(); HelpMarker("Currently all this does is to lift the 0..1 limits on dragging widgets."); ImGuiColorEditFlags misc_flags = (hdr ? ImGuiColorEditFlags_HDR : 0) | (drag_and_drop ? 0 : ImGuiColorEditFlags_NoDragDrop) | (alpha_half_preview ? ImGuiColorEditFlags_AlphaPreviewHalf : (alpha_preview ? ImGuiColorEditFlags_AlphaPreview : 0)) | (options_menu ? 0 : ImGuiColorEditFlags_NoOptions); + IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit"); ImGui::Text("Color widget:"); ImGui::SameLine(); HelpMarker( "Click on the color square to open a color picker.\n" "CTRL+click on individual component to input value.\n"); ImGui::ColorEdit3("MyColor##1", (float*)&color, misc_flags); + IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (HSV, with Alpha)"); ImGui::Text("Color widget HSV with Alpha:"); ImGui::ColorEdit4("MyColor##2", (float*)&color, ImGuiColorEditFlags_DisplayHSV | misc_flags); + IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (float display)"); ImGui::Text("Color widget with Float Display:"); ImGui::ColorEdit4("MyColor##2f", (float*)&color, ImGuiColorEditFlags_Float | misc_flags); + IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (with Picker)"); ImGui::Text("Color button with Picker:"); ImGui::SameLine(); HelpMarker( "With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\n" @@ -1668,6 +1757,7 @@ static void ShowDemoWindowWidgets() "be used for the tooltip and picker popup."); ImGui::ColorEdit4("MyColor##3", (float*)&color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | misc_flags); + IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (with custom Picker popup)"); ImGui::Text("Color button with Custom Picker Popup:"); // Generate a default palette. The palette will persist and can be edited. @@ -1735,11 +1825,13 @@ static void ShowDemoWindowWidgets() ImGui::EndPopup(); } + IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (simple)"); ImGui::Text("Color button only:"); static bool no_border = false; ImGui::Checkbox("ImGuiColorEditFlags_NoBorder", &no_border); ImGui::ColorButton("MyColor##3c", *(ImVec4*)&color, misc_flags | (no_border ? ImGuiColorEditFlags_NoBorder : 0), ImVec2(80, 80)); + IMGUI_DEMO_MARKER("Widgets/Color/ColorPicker"); ImGui::Text("Color picker:"); static bool alpha = true; static bool alpha_bar = true; @@ -1807,6 +1899,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Drag and Slider Flags"); if (ImGui::TreeNode("Drag/Slider Flags")) { // Demonstrate using advanced flags for DragXXX and SliderXXX functions. Note that the flags are the same! @@ -1840,6 +1933,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Range Widgets"); if (ImGui::TreeNode("Range Widgets")) { static float begin = 10, end = 90; @@ -1850,6 +1944,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Data Types"); if (ImGui::TreeNode("Data Types")) { // DragScalar/InputScalar/SliderScalar functions allow various data types @@ -1899,6 +1994,7 @@ static void ShowDemoWindowWidgets() const float drag_speed = 0.2f; static bool drag_clamp = false; + IMGUI_DEMO_MARKER("Widgets/Data Types/Drags"); ImGui::Text("Drags:"); ImGui::Checkbox("Clamp integers to 0..50", &drag_clamp); ImGui::SameLine(); HelpMarker( @@ -1917,6 +2013,7 @@ static void ShowDemoWindowWidgets() ImGui::DragScalar("drag double", ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, NULL, "%.10f grams"); ImGui::DragScalar("drag double log",ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, &f64_one, "0 < %.10f < 1", ImGuiSliderFlags_Logarithmic); + IMGUI_DEMO_MARKER("Widgets/Data Types/Sliders"); ImGui::Text("Sliders"); ImGui::SliderScalar("slider s8 full", ImGuiDataType_S8, &s8_v, &s8_min, &s8_max, "%d"); ImGui::SliderScalar("slider u8 full", ImGuiDataType_U8, &u8_v, &u8_min, &u8_max, "%u"); @@ -1949,6 +2046,7 @@ static void ShowDemoWindowWidgets() ImGui::SliderScalar("slider s64 reverse", ImGuiDataType_S64, &s64_v, &s64_fifty, &s64_zero, "%" IM_PRId64); ImGui::SliderScalar("slider u64 reverse", ImGuiDataType_U64, &u64_v, &u64_fifty, &u64_zero, "%" IM_PRIu64 " ms"); + IMGUI_DEMO_MARKER("Widgets/Data Types/Inputs"); static bool inputs_step = true; ImGui::Text("Inputs"); ImGui::Checkbox("Show step buttons", &inputs_step); @@ -1968,6 +2066,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Multi-component Widgets"); if (ImGui::TreeNode("Multi-component Widgets")) { static float vec4f[4] = { 0.10f, 0.20f, 0.30f, 0.44f }; @@ -1999,6 +2098,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Vertical Sliders"); if (ImGui::TreeNode("Vertical Sliders")) { const float spacing = 4; @@ -2063,8 +2163,10 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Drag and drop"); if (ImGui::TreeNode("Drag and Drop")) { + IMGUI_DEMO_MARKER("Widgets/Drag and drop/Standard widgets"); if (ImGui::TreeNode("Drag and drop in standard widgets")) { // ColorEdit widgets automatically act as drag source and drag target. @@ -2079,6 +2181,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Drag and drop/Copy-swap items"); if (ImGui::TreeNode("Drag and drop to copy/swap items")) { enum Mode @@ -2146,6 +2249,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Drag and Drop/Drag to reorder items (simple)"); if (ImGui::TreeNode("Drag to reorder items (simple)")) { // Simple reordering @@ -2175,38 +2279,45 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } - if (ImGui::TreeNode("Querying Status (Edited/Active/Focused/Hovered etc.)")) + IMGUI_DEMO_MARKER("Widgets/Querying Item Status (Edited,Active,Hovered etc.)"); + if (ImGui::TreeNode("Querying Item Status (Edited/Active/Hovered etc.)")) { // Select an item type const char* item_names[] = { - "Text", "Button", "Button (w/ repeat)", "Checkbox", "SliderFloat", "InputText", "InputFloat", - "InputFloat3", "ColorEdit4", "MenuItem", "TreeNode", "TreeNode (w/ double-click)", "Combo", "ListBox" + "Text", "Button", "Button (w/ repeat)", "Checkbox", "SliderFloat", "InputText", "InputTextMultiline", "InputFloat", + "InputFloat3", "ColorEdit4", "Selectable", "MenuItem", "TreeNode", "TreeNode (w/ double-click)", "Combo", "ListBox" }; - static int item_type = 1; + static int item_type = 4; + static bool item_disabled = false; ImGui::Combo("Item Type", &item_type, item_names, IM_ARRAYSIZE(item_names), IM_ARRAYSIZE(item_names)); ImGui::SameLine(); HelpMarker("Testing how various types of items are interacting with the IsItemXXX functions. Note that the bool return value of most ImGui function is generally equivalent to calling ImGui::IsItemHovered()."); + ImGui::Checkbox("Item Disabled", &item_disabled); // Submit selected item item so we can query their status in the code following it. bool ret = false; static bool b = false; static float col4f[4] = { 1.0f, 0.5, 0.0f, 1.0f }; static char str[16] = {}; + if (item_disabled) + ImGui::BeginDisabled(true); if (item_type == 0) { ImGui::Text("ITEM: Text"); } // Testing text items with no identifier/interaction if (item_type == 1) { ret = ImGui::Button("ITEM: Button"); } // Testing button if (item_type == 2) { ImGui::PushButtonRepeat(true); ret = ImGui::Button("ITEM: Button"); ImGui::PopButtonRepeat(); } // Testing button (with repeater) if (item_type == 3) { ret = ImGui::Checkbox("ITEM: Checkbox", &b); } // Testing checkbox if (item_type == 4) { ret = ImGui::SliderFloat("ITEM: SliderFloat", &col4f[0], 0.0f, 1.0f); } // Testing basic item if (item_type == 5) { ret = ImGui::InputText("ITEM: InputText", &str[0], IM_ARRAYSIZE(str)); } // Testing input text (which handles tabbing) - if (item_type == 6) { ret = ImGui::InputFloat("ITEM: InputFloat", col4f, 1.0f); } // Testing +/- buttons on scalar input - if (item_type == 7) { ret = ImGui::InputFloat3("ITEM: InputFloat3", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) - if (item_type == 8) { ret = ImGui::ColorEdit4("ITEM: ColorEdit4", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) - if (item_type == 9) { ret = ImGui::MenuItem("ITEM: MenuItem"); } // Testing menu item (they use ImGuiButtonFlags_PressedOnRelease button policy) - if (item_type == 10){ ret = ImGui::TreeNode("ITEM: TreeNode"); if (ret) ImGui::TreePop(); } // Testing tree node - if (item_type == 11){ ret = ImGui::TreeNodeEx("ITEM: TreeNode w/ ImGuiTreeNodeFlags_OpenOnDoubleClick", ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_NoTreePushOnOpen); } // Testing tree node with ImGuiButtonFlags_PressedOnDoubleClick button policy. - if (item_type == 12){ const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::Combo("ITEM: Combo", ¤t, items, IM_ARRAYSIZE(items)); } - if (item_type == 13){ const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", ¤t, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); } + if (item_type == 6) { ret = ImGui::InputTextMultiline("ITEM: InputTextMultiline", &str[0], IM_ARRAYSIZE(str)); } // Testing input text (which uses a child window) + if (item_type == 7) { ret = ImGui::InputFloat("ITEM: InputFloat", col4f, 1.0f); } // Testing +/- buttons on scalar input + if (item_type == 8) { ret = ImGui::InputFloat3("ITEM: InputFloat3", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) + if (item_type == 9) { ret = ImGui::ColorEdit4("ITEM: ColorEdit4", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) + if (item_type == 10){ ret = ImGui::Selectable("ITEM: Selectable"); } // Testing selectable item + if (item_type == 11){ ret = ImGui::MenuItem("ITEM: MenuItem"); } // Testing menu item (they use ImGuiButtonFlags_PressedOnRelease button policy) + if (item_type == 12){ ret = ImGui::TreeNode("ITEM: TreeNode"); if (ret) ImGui::TreePop(); } // Testing tree node + if (item_type == 13){ ret = ImGui::TreeNodeEx("ITEM: TreeNode w/ ImGuiTreeNodeFlags_OpenOnDoubleClick", ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_NoTreePushOnOpen); } // Testing tree node with ImGuiButtonFlags_PressedOnDoubleClick button policy. + if (item_type == 14){ const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::Combo("ITEM: Combo", ¤t, items, IM_ARRAYSIZE(items)); } + if (item_type == 15){ const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", ¤t, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); } // Display the values of IsItemHovered() and other common item state functions. // Note that the ImGuiHoveredFlags_XXX flags can be combined. @@ -2219,6 +2330,7 @@ static void ShowDemoWindowWidgets() "IsItemHovered(_AllowWhenBlockedByPopup) = %d\n" "IsItemHovered(_AllowWhenBlockedByActiveItem) = %d\n" "IsItemHovered(_AllowWhenOverlapped) = %d\n" + "IsItemHovered(_AllowWhenDisabled) = %d\n" "IsItemHovered(_RectOnly) = %d\n" "IsItemActive() = %d\n" "IsItemEdited() = %d\n" @@ -2237,6 +2349,7 @@ static void ShowDemoWindowWidgets() ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlapped), + ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled), ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly), ImGui::IsItemActive(), ImGui::IsItemEdited(), @@ -2251,43 +2364,67 @@ static void ShowDemoWindowWidgets() ImGui::GetItemRectSize().x, ImGui::GetItemRectSize().y ); + if (item_disabled) + ImGui::EndDisabled(); + + char buf[1] = ""; + ImGui::InputText("unused", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_ReadOnly); + ImGui::SameLine(); + HelpMarker("This widget is only here to be able to tab-out of the widgets above and see e.g. Deactivated() status."); + + ImGui::TreePop(); + } + + IMGUI_DEMO_MARKER("Widgets/Querying Window Status (Focused,Hovered etc.)"); + if (ImGui::TreeNode("Querying Window Status (Focused/Hovered etc.)")) + { static bool embed_all_inside_a_child_window = false; - ImGui::Checkbox("Embed everything inside a child window (for additional testing)", &embed_all_inside_a_child_window); + ImGui::Checkbox("Embed everything inside a child window for testing _RootWindow flag.", &embed_all_inside_a_child_window); if (embed_all_inside_a_child_window) ImGui::BeginChild("outer_child", ImVec2(0, ImGui::GetFontSize() * 20.0f), true); // Testing IsWindowFocused() function with its various flags. - // Note that the ImGuiFocusedFlags_XXX flags can be combined. ImGui::BulletText( "IsWindowFocused() = %d\n" "IsWindowFocused(_ChildWindows) = %d\n" + "IsWindowFocused(_ChildWindows|_NoPopupHierarchy) = %d\n" "IsWindowFocused(_ChildWindows|_RootWindow) = %d\n" + "IsWindowFocused(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n" "IsWindowFocused(_RootWindow) = %d\n" + "IsWindowFocused(_RootWindow|_NoPopupHierarchy) = %d\n" "IsWindowFocused(_AnyWindow) = %d\n", ImGui::IsWindowFocused(), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows), + ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_NoPopupHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow), + ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow), + ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)); // Testing IsWindowHovered() function with its various flags. - // Note that the ImGuiHoveredFlags_XXX flags can be combined. ImGui::BulletText( "IsWindowHovered() = %d\n" "IsWindowHovered(_AllowWhenBlockedByPopup) = %d\n" "IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\n" "IsWindowHovered(_ChildWindows) = %d\n" + "IsWindowHovered(_ChildWindows|_NoPopupHierarchy) = %d\n" "IsWindowHovered(_ChildWindows|_RootWindow) = %d\n" - "IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n" + "IsWindowHovered(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n" "IsWindowHovered(_RootWindow) = %d\n" + "IsWindowHovered(_RootWindow|_NoPopupHierarchy) = %d\n" + "IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n" "IsWindowHovered(_AnyWindow) = %d\n", ImGui::IsWindowHovered(), ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows), + ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow), - ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup), + ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow), + ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy), + ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup), ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)); ImGui::BeginChild("child", ImVec2(0, 50), true); @@ -2296,9 +2433,6 @@ static void ShowDemoWindowWidgets() if (embed_all_inside_a_child_window) ImGui::EndChild(); - static char unused_str[] = "This widget is only here to be able to tab-out of the widgets above."; - ImGui::InputText("unused", unused_str, IM_ARRAYSIZE(unused_str), ImGuiInputTextFlags_ReadOnly); - // Calling IsItemHovered() after begin returns the hovered status of the title bar. // This is useful in particular if you want to create a context menu associated to the title bar of a window. static bool test_window = false; @@ -2320,13 +2454,28 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + + // Demonstrate BeginDisabled/EndDisabled using a checkbox located at the bottom of the section (which is a bit odd: + // logically we'd have this checkbox at the top of the section, but we don't want this feature to steal that space) + if (disable_all) + ImGui::EndDisabled(); + + IMGUI_DEMO_MARKER("Widgets/Disable Block"); + if (ImGui::TreeNode("Disable block")) + { + ImGui::Checkbox("Disable entire section above", &disable_all); + ImGui::SameLine(); HelpMarker("Demonstrate using BeginDisabled()/EndDisabled() across this section."); + ImGui::TreePop(); + } } static void ShowDemoWindowLayout() { + IMGUI_DEMO_MARKER("Layout"); if (!ImGui::CollapsingHeader("Layout & Scrolling")) return; + IMGUI_DEMO_MARKER("Layout/Child windows"); if (ImGui::TreeNode("Child windows")) { HelpMarker("Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window."); @@ -2340,7 +2489,7 @@ static void ShowDemoWindowLayout() ImGuiWindowFlags window_flags = ImGuiWindowFlags_HorizontalScrollbar; if (disable_mouse_wheel) window_flags |= ImGuiWindowFlags_NoScrollWithMouse; - ImGui::BeginChild("ChildL", ImVec2(ImGui::GetWindowContentRegionWidth() * 0.5f, 260), false, window_flags); + ImGui::BeginChild("ChildL", ImVec2(ImGui::GetContentRegionAvail().x * 0.5f, 260), false, window_flags); for (int i = 0; i < 100; i++) ImGui::Text("%04d: scrollable region", i); ImGui::EndChild(); @@ -2389,10 +2538,10 @@ static void ShowDemoWindowLayout() // You can also call SetNextWindowPos() to position the child window. The parent window will effectively // layout from this position. // - Using ImGui::GetItemRectMin/Max() to query the "item" state (because the child window is an item from - // the POV of the parent window). See 'Demo->Querying Status (Active/Focused/Hovered etc.)' for details. + // the POV of the parent window). See 'Demo->Querying Status (Edited/Active/Hovered etc.)' for details. { static int offset_x = 0; - ImGui::SetNextItemWidth(100); + ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); ImGui::DragInt("Offset X", &offset_x, 1.0f, -1000, 1000); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (float)offset_x); @@ -2412,17 +2561,18 @@ static void ShowDemoWindowLayout() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Layout/Widgets Width"); if (ImGui::TreeNode("Widgets Width")) { + static float f = 0.0f; + static bool show_indented_items = true; + ImGui::Checkbox("Show indented items", &show_indented_items); + // Use SetNextItemWidth() to set the width of a single upcoming item. // Use PushItemWidth()/PopItemWidth() to set the width of a group of items. // In real code use you'll probably want to choose width values that are proportional to your font size // e.g. Using '20.0f * GetFontSize()' as width instead of '200.0f', etc. - static float f = 0.0f; - static bool show_indented_items = true; - ImGui::Checkbox("Show indented items", &show_indented_items); - ImGui::Text("SetNextItemWidth/PushItemWidth(100)"); ImGui::SameLine(); HelpMarker("Fixed width."); ImGui::PushItemWidth(100); @@ -2488,11 +2638,13 @@ static void ShowDemoWindowLayout() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout"); if (ImGui::TreeNode("Basic Horizontal Layout")) { ImGui::TextWrapped("(Use ImGui::SameLine() to keep adding items to the right of the preceding item)"); // Text + IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine"); ImGui::Text("Two items: Hello"); ImGui::SameLine(); ImGui::TextColored(ImVec4(1,1,0,1), "Sailor"); @@ -2513,6 +2665,7 @@ static void ShowDemoWindowLayout() ImGui::Text("can fit within a text block."); // Aligned to arbitrary position. Easy/cheap column. + IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine (with offset)"); ImGui::Text("Aligned"); ImGui::SameLine(150); ImGui::Text("x=150"); ImGui::SameLine(300); ImGui::Text("x=300"); @@ -2521,6 +2674,7 @@ static void ShowDemoWindowLayout() ImGui::SameLine(300); ImGui::SmallButton("x=300"); // Checkbox + IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine (more)"); static bool c1 = false, c2 = false, c3 = false, c4 = false; ImGui::Checkbox("My", &c1); ImGui::SameLine(); ImGui::Checkbox("Tailor", &c2); ImGui::SameLine(); @@ -2552,6 +2706,7 @@ static void ShowDemoWindowLayout() ImGui::PopItemWidth(); // Dummy + IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/Dummy"); ImVec2 button_sz(40, 40); ImGui::Button("A", button_sz); ImGui::SameLine(); ImGui::Dummy(button_sz); ImGui::SameLine(); @@ -2559,7 +2714,8 @@ static void ShowDemoWindowLayout() // Manually wrapping // (we should eventually provide this as an automatic layout feature, but for now you can do it manually) - ImGui::Text("Manually wrapping:"); + IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/Manual wrapping"); + ImGui::Text("Manual wrapping:"); ImGuiStyle& style = ImGui::GetStyle(); int buttons_count = 20; float window_visible_x2 = ImGui::GetWindowPos().x + ImGui::GetWindowContentRegionMax().x; @@ -2577,6 +2733,7 @@ static void ShowDemoWindowLayout() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Layout/Groups"); if (ImGui::TreeNode("Groups")) { HelpMarker( @@ -2624,6 +2781,7 @@ static void ShowDemoWindowLayout() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Layout/Text Baseline Alignment"); if (ImGui::TreeNode("Text Baseline Alignment")) { { @@ -2742,9 +2900,11 @@ static void ShowDemoWindowLayout() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Layout/Scrolling"); if (ImGui::TreeNode("Scrolling")) { // Vertical scroll functions + IMGUI_DEMO_MARKER("Layout/Scrolling/Vertical"); HelpMarker("Use SetScrollHereY() or SetScrollFromPosY() to scroll to a given vertical position."); static int track_item = 50; @@ -2817,6 +2977,7 @@ static void ShowDemoWindowLayout() ImGui::PopID(); // Horizontal scroll functions + IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal"); ImGui::Spacing(); HelpMarker( "Use SetScrollHereX() or SetScrollFromPosX() to scroll to a given horizontal position.\n\n" @@ -2862,6 +3023,7 @@ static void ShowDemoWindowLayout() ImGui::PopID(); // Miscellaneous Horizontal Scrolling Demo + IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal (more)"); HelpMarker( "Horizontal scrolling for a window is enabled via the ImGuiWindowFlags_HorizontalScrollbar flag.\n\n" "You may want to also explicitly specify content width by using SetNextWindowContentWidth() before Begin()."); @@ -2936,6 +3098,7 @@ static void ShowDemoWindowLayout() if (explicit_content_size) ImGui::SetNextWindowContentSize(ImVec2(contents_size_x, 0.0f)); ImGui::Begin("Horizontal contents size demo window", &show_horizontal_contents_size_demo_window, show_h_scrollbar ? ImGuiWindowFlags_HorizontalScrollbar : 0); + IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal contents size demo window"); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2, 0)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 0)); HelpMarker("Test of different widgets react and impact the work rectangle growing when horizontal scrolling is enabled.\n\nUse 'Metrics->Tools->Show windows rectangles' to visualize rectangles."); @@ -3022,6 +3185,7 @@ static void ShowDemoWindowLayout() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Layout/Clipping"); if (ImGui::TreeNode("Clipping")) { static ImVec2 size(100.0f, 100.0f); @@ -3090,6 +3254,7 @@ static void ShowDemoWindowLayout() static void ShowDemoWindowPopups() { + IMGUI_DEMO_MARKER("Popups"); if (!ImGui::CollapsingHeader("Popups & Modal windows")) return; @@ -3111,6 +3276,7 @@ static void ShowDemoWindowPopups() // With popups we have to go through a library call (here OpenPopup) to manipulate the visibility state. // This may be a bit confusing at first but it should quickly make sense. Follow on the examples below. + IMGUI_DEMO_MARKER("Popups/Popups"); if (ImGui::TreeNode("Popups")) { ImGui::TextWrapped( @@ -3179,17 +3345,33 @@ static void ShowDemoWindowPopups() } // Call the more complete ShowExampleMenuFile which we use in various places of this demo - if (ImGui::Button("File Menu..")) + if (ImGui::Button("With a menu..")) ImGui::OpenPopup("my_file_popup"); - if (ImGui::BeginPopup("my_file_popup")) + if (ImGui::BeginPopup("my_file_popup", ImGuiWindowFlags_MenuBar)) { - ShowExampleMenuFile(); + if (ImGui::BeginMenuBar()) + { + if (ImGui::BeginMenu("File")) + { + ShowExampleMenuFile(); + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Edit")) + { + ImGui::MenuItem("Dummy"); + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); + } + ImGui::Text("Hello from popup!"); + ImGui::Button("This is a dummy button.."); ImGui::EndPopup(); } ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Popups/Context menus"); if (ImGui::TreeNode("Context menus")) { HelpMarker("\"Context\" functions are simple helpers to associate a Popup to a given Item or Window identifier."); @@ -3274,6 +3456,7 @@ static void ShowDemoWindowPopups() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Popups/Modals"); if (ImGui::TreeNode("Modals")) { ImGui::TextWrapped("Modal windows are like popups but the user cannot close them by clicking outside."); @@ -3349,6 +3532,7 @@ static void ShowDemoWindowPopups() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Popups/Menus inside a regular window"); if (ImGui::TreeNode("Menus inside a regular window")) { ImGui::TextWrapped("Below we are testing adding menu items to a regular window. It's rather unusual but should work!"); @@ -3495,6 +3679,7 @@ static void EditTableSizingFlags(ImGuiTableFlags* p_flags) static void EditTableColumnsFlags(ImGuiTableColumnFlags* p_flags) { + ImGui::CheckboxFlags("_Disabled", p_flags, ImGuiTableColumnFlags_Disabled); ImGui::SameLine(); HelpMarker("Master disable flag (also hide from context menu)"); ImGui::CheckboxFlags("_DefaultHide", p_flags, ImGuiTableColumnFlags_DefaultHide); ImGui::CheckboxFlags("_DefaultSort", p_flags, ImGuiTableColumnFlags_DefaultSort); if (ImGui::CheckboxFlags("_WidthStretch", p_flags, ImGuiTableColumnFlags_WidthStretch)) @@ -3508,6 +3693,7 @@ static void EditTableColumnsFlags(ImGuiTableColumnFlags* p_flags) ImGui::CheckboxFlags("_NoSort", p_flags, ImGuiTableColumnFlags_NoSort); ImGui::CheckboxFlags("_NoSortAscending", p_flags, ImGuiTableColumnFlags_NoSortAscending); ImGui::CheckboxFlags("_NoSortDescending", p_flags, ImGuiTableColumnFlags_NoSortDescending); + ImGui::CheckboxFlags("_NoHeaderLabel", p_flags, ImGuiTableColumnFlags_NoHeaderLabel); ImGui::CheckboxFlags("_NoHeaderWidth", p_flags, ImGuiTableColumnFlags_NoHeaderWidth); ImGui::CheckboxFlags("_PreferSortAscending", p_flags, ImGuiTableColumnFlags_PreferSortAscending); ImGui::CheckboxFlags("_PreferSortDescending", p_flags, ImGuiTableColumnFlags_PreferSortDescending); @@ -3526,6 +3712,7 @@ static void ShowTableColumnsStatusFlags(ImGuiTableColumnFlags flags) static void ShowDemoWindowTables() { //ImGui::SetNextItemOpen(true, ImGuiCond_Once); + IMGUI_DEMO_MARKER("Tables"); if (!ImGui::CollapsingHeader("Tables & Columns")) return; @@ -3565,6 +3752,7 @@ static void ShowDemoWindowTables() // Demos if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Basic"); if (ImGui::TreeNode("Basic")) { // Here we will showcase three different ways to output a table. @@ -3626,6 +3814,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Borders, background"); if (ImGui::TreeNode("Borders, background")) { // Expose a few Borders related flags interactively @@ -3696,6 +3885,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Resizable, stretch"); if (ImGui::TreeNode("Resizable, stretch")) { // By default, if we don't enable ScrollX the sizing policy for each columns is "Stretch" @@ -3725,6 +3915,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Resizable, fixed"); if (ImGui::TreeNode("Resizable, fixed")) { // Here we use ImGuiTableFlags_SizingFixedFit (even though _ScrollX is not set) @@ -3758,6 +3949,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Resizable, mixed"); if (ImGui::TreeNode("Resizable, mixed")) { HelpMarker( @@ -3807,6 +3999,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Reorderable, hideable, with headers"); if (ImGui::TreeNode("Reorderable, hideable, with headers")) { HelpMarker( @@ -3864,6 +4057,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Padding"); if (ImGui::TreeNode("Padding")) { // First example: showcase use of padding flags and effect of BorderOuterV/BorderInnerV on X padding. @@ -3972,6 +4166,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Explicit widths"); if (ImGui::TreeNode("Sizing policies")) { static ImGuiTableFlags flags1 = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_RowBg | ImGuiTableFlags_ContextMenuInBody; @@ -4075,6 +4270,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Vertical scrolling, with clipping"); if (ImGui::TreeNode("Vertical scrolling, with clipping")) { HelpMarker("Here we activate ScrollY, which will create a child window container to allow hosting scrollable contents.\n\nWe also demonstrate using ImGuiListClipper to virtualize the submission of many items."); @@ -4117,6 +4313,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Horizontal scrolling"); if (ImGui::TreeNode("Horizontal scrolling")) { HelpMarker( @@ -4205,6 +4402,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Columns flags"); if (ImGui::TreeNode("Columns flags")) { // Create a first table just to show all the options/flags we want to make visible in our example! @@ -4269,6 +4467,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Columns widths"); if (ImGui::TreeNode("Columns widths")) { HelpMarker("Using TableSetupColumn() to setup default width."); @@ -4334,6 +4533,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Nested tables"); if (ImGui::TreeNode("Nested tables")) { HelpMarker("This demonstrate embedding a table into another table cell."); @@ -4378,6 +4578,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Row height"); if (ImGui::TreeNode("Row height")) { HelpMarker("You can pass a 'min_row_height' to TableNextRow().\n\nRows are padded with 'style.CellPadding.y' on top and bottom, so effectively the minimum row height will always be >= 'style.CellPadding.y * 2.0f'.\n\nWe cannot honor a _maximum_ row height as that would requires a unique clipping rectangle per row."); @@ -4397,6 +4598,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Outer size"); if (ImGui::TreeNode("Outer size")) { // Showcasing use of ImGuiTableFlags_NoHostExtendX and ImGuiTableFlags_NoHostExtendY @@ -4463,6 +4665,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Background color"); if (ImGui::TreeNode("Background color")) { static ImGuiTableFlags flags = ImGuiTableFlags_RowBg; @@ -4520,6 +4723,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Tree view"); if (ImGui::TreeNode("Tree view")) { static ImGuiTableFlags flags = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody; @@ -4591,6 +4795,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Item width"); if (ImGui::TreeNode("Item width")) { HelpMarker( @@ -4636,6 +4841,7 @@ static void ShowDemoWindowTables() // Demonstrate using TableHeader() calls instead of TableHeadersRow() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Custom headers"); if (ImGui::TreeNode("Custom headers")) { const int COLUMNS_COUNT = 3; @@ -4683,6 +4889,7 @@ static void ShowDemoWindowTables() // Demonstrate creating custom context menus inside columns, while playing it nice with context menus provided by TableHeadersRow()/TableHeader() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Context menus"); if (ImGui::TreeNode("Context menus")) { HelpMarker("By default, right-clicking over a TableHeadersRow()/TableHeader() line will open the default context-menu.\nUsing ImGuiTableFlags_ContextMenuInBody we also allow right-clicking over columns body."); @@ -4789,6 +4996,7 @@ static void ShowDemoWindowTables() // Demonstrate creating multiple tables with the same ID if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Synced instances"); if (ImGui::TreeNode("Synced instances")) { HelpMarker("Multiple tables with the same identifier will share their settings, width, visibility, order etc."); @@ -4824,6 +5032,7 @@ static void ShowDemoWindowTables() }; if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Sorting"); if (ImGui::TreeNode("Sorting")) { // Create item list @@ -4911,6 +5120,7 @@ static void ShowDemoWindowTables() //ImGui::SetNextItemOpen(true, ImGuiCond_Once); // [DEBUG] if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Advanced"); if (ImGui::TreeNode("Advanced")) { static ImGuiTableFlags flags = @@ -5227,6 +5437,7 @@ static void ShowDemoWindowTables() // [2020: Columns are under-featured and not maintained. Prefer using the more flexible and powerful BeginTable() API!] static void ShowDemoWindowColumns() { + IMGUI_DEMO_MARKER("Columns (legacy API)"); bool open = ImGui::TreeNode("Legacy Columns API"); ImGui::SameLine(); HelpMarker("Columns() is an old API! Prefer using the more flexible and powerful BeginTable() API!"); @@ -5234,6 +5445,7 @@ static void ShowDemoWindowColumns() return; // Basic columns + IMGUI_DEMO_MARKER("Columns (legacy API)/Basic"); if (ImGui::TreeNode("Basic")) { ImGui::Text("Without border:"); @@ -5278,6 +5490,7 @@ static void ShowDemoWindowColumns() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Columns (legacy API)/Borders"); if (ImGui::TreeNode("Borders")) { // NB: Future columns API should allow automatic horizontal borders. @@ -5313,6 +5526,7 @@ static void ShowDemoWindowColumns() } // Create multiple items in a same cell before switching to next column + IMGUI_DEMO_MARKER("Columns (legacy API)/Mixed items"); if (ImGui::TreeNode("Mixed items")) { ImGui::Columns(3, "mixed"); @@ -5344,6 +5558,7 @@ static void ShowDemoWindowColumns() } // Word wrapping + IMGUI_DEMO_MARKER("Columns (legacy API)/Word-wrapping"); if (ImGui::TreeNode("Word-wrapping")) { ImGui::Columns(2, "word-wrapping"); @@ -5358,6 +5573,7 @@ static void ShowDemoWindowColumns() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Columns (legacy API)/Horizontal Scrolling"); if (ImGui::TreeNode("Horizontal Scrolling")) { ImGui::SetNextWindowContentSize(ImVec2(1500.0f, 0.0f)); @@ -5383,6 +5599,7 @@ static void ShowDemoWindowColumns() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Columns (legacy API)/Tree"); if (ImGui::TreeNode("Tree")) { ImGui::Columns(2, "tree", true); @@ -5424,6 +5641,7 @@ static void ShowDemoWindowColumns() static void ShowDemoWindowMisc() { + IMGUI_DEMO_MARKER("Filtering"); if (ImGui::CollapsingHeader("Filtering")) { // Helper class to easy setup a text filter. @@ -5441,18 +5659,21 @@ static void ShowDemoWindowMisc() ImGui::BulletText("%s", lines[i]); } + IMGUI_DEMO_MARKER("Inputs, Navigation & Focus"); if (ImGui::CollapsingHeader("Inputs, Navigation & Focus")) { ImGuiIO& io = ImGui::GetIO(); // Display ImGuiIO output flags ImGui::Text("WantCaptureMouse: %d", io.WantCaptureMouse); + ImGui::Text("WantCaptureMouseUnlessPopupClose: %d", io.WantCaptureMouseUnlessPopupClose); ImGui::Text("WantCaptureKeyboard: %d", io.WantCaptureKeyboard); ImGui::Text("WantTextInput: %d", io.WantTextInput); ImGui::Text("WantSetMousePos: %d", io.WantSetMousePos); ImGui::Text("NavActive: %d, NavVisible: %d", io.NavActive, io.NavVisible); // Display Mouse state + IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Mouse State"); if (ImGui::TreeNode("Mouse State")) { if (ImGui::IsMousePosValid()) @@ -5460,16 +5681,18 @@ static void ShowDemoWindowMisc() else ImGui::Text("Mouse pos: "); ImGui::Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y); - ImGui::Text("Mouse down:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDown(i)) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } - ImGui::Text("Mouse clicked:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } - ImGui::Text("Mouse dblclick:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDoubleClicked(i)){ ImGui::SameLine(); ImGui::Text("b%d", i); } - ImGui::Text("Mouse released:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseReleased(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } + + int count = IM_ARRAYSIZE(io.MouseDown); + ImGui::Text("Mouse down:"); for (int i = 0; i < count; i++) if (ImGui::IsMouseDown(i)) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } + ImGui::Text("Mouse clicked:"); for (int i = 0; i < count; i++) if (ImGui::IsMouseClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d (%d)", i, ImGui::GetMouseClickedCount(i)); } + ImGui::Text("Mouse released:"); for (int i = 0; i < count; i++) if (ImGui::IsMouseReleased(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } ImGui::Text("Mouse wheel: %.1f", io.MouseWheel); ImGui::Text("Pen Pressure: %.1f", io.PenPressure); // Note: currently unused ImGui::TreePop(); } // Display Keyboard/Mouse state + IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Keyboard & Navigation State"); if (ImGui::TreeNode("Keyboard & Navigation State")) { ImGui::Text("Keys down:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyDown(i)) { ImGui::SameLine(); ImGui::Text("%d (0x%X) (%.02f secs)", i, i, io.KeysDownDuration[i]); } @@ -5491,6 +5714,7 @@ static void ShowDemoWindowMisc() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Tabbing"); if (ImGui::TreeNode("Tabbing")) { ImGui::Text("Use TAB/SHIFT+TAB to cycle through keyboard editable fields."); @@ -5500,12 +5724,13 @@ static void ShowDemoWindowMisc() ImGui::InputText("3", buf, IM_ARRAYSIZE(buf)); ImGui::PushAllowKeyboardFocus(false); ImGui::InputText("4 (tab skip)", buf, IM_ARRAYSIZE(buf)); - //ImGui::SameLine(); HelpMarker("Use ImGui::PushAllowKeyboardFocus(bool) to disable tabbing through certain widgets."); + ImGui::SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab."); ImGui::PopAllowKeyboardFocus(); ImGui::InputText("5", buf, IM_ARRAYSIZE(buf)); ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Focus from code"); if (ImGui::TreeNode("Focus from code")) { bool focus_1 = ImGui::Button("Focus on 1"); ImGui::SameLine(); @@ -5526,6 +5751,7 @@ static void ShowDemoWindowMisc() if (focus_3) ImGui::SetKeyboardFocusHere(); ImGui::InputText("3 (tab skip)", buf, IM_ARRAYSIZE(buf)); if (ImGui::IsItemActive()) has_focus = 3; + ImGui::SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab."); ImGui::PopAllowKeyboardFocus(); if (has_focus) @@ -5546,6 +5772,7 @@ static void ShowDemoWindowMisc() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Dragging"); if (ImGui::TreeNode("Dragging")) { ImGui::TextWrapped("You can use ImGui::GetMouseDragDelta(0) to query for the dragged amount on any widget."); @@ -5574,6 +5801,7 @@ static void ShowDemoWindowMisc() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Mouse cursors"); if (ImGui::TreeNode("Mouse cursors")) { const char* mouse_cursors_names[] = { "Arrow", "TextInput", "ResizeAll", "ResizeNS", "ResizeEW", "ResizeNESW", "ResizeNWSE", "Hand", "NotAllowed" }; @@ -5611,6 +5839,7 @@ void ImGui::ShowAboutWindow(bool* p_open) ImGui::End(); return; } + IMGUI_DEMO_MARKER("Tools/About Dear ImGui"); ImGui::Text("Dear ImGui %s", ImGui::GetVersion()); ImGui::Separator(); ImGui::Text("By Omar Cornut and all Dear ImGui contributors."); @@ -5743,29 +5972,13 @@ void ImGui::ShowAboutWindow(bool* p_open) //----------------------------------------------------------------------------- // [SECTION] Style Editor / ShowStyleEditor() //----------------------------------------------------------------------------- -// - ShowStyleSelector() // - ShowFontSelector() +// - ShowStyleSelector() // - ShowStyleEditor() //----------------------------------------------------------------------------- -// Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options. -// Here we use the simplified Combo() api that packs items into a single literal string. -// Useful for quick combo boxes where the choices are known locally. -bool ImGui::ShowStyleSelector(const char* label) -{ - static int style_idx = -1; - if (ImGui::Combo(label, &style_idx, "Dark\0Light\0Classic\0")) - { - switch (style_idx) - { - case 0: ImGui::StyleColorsDark(); break; - case 1: ImGui::StyleColorsLight(); break; - case 2: ImGui::StyleColorsClassic(); break; - } - return true; - } - return false; -} +// Forward declare ShowFontAtlas() which isn't worth putting in public API yet +namespace ImGui { IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas); } // Demo helper function to select among loaded fonts. // Here we use the regular BeginCombo()/EndCombo() api which is more the more flexible one. @@ -5793,96 +6006,28 @@ void ImGui::ShowFontSelector(const char* label) "- If you need to add/remove fonts at runtime (e.g. for DPI change), do it before calling NewFrame()."); } -// [Internal] Display details for a single font, called by ShowStyleEditor(). -static void NodeFont(ImFont* font) +// Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options. +// Here we use the simplified Combo() api that packs items into a single literal string. +// Useful for quick combo boxes where the choices are known locally. +bool ImGui::ShowStyleSelector(const char* label) { - ImGuiIO& io = ImGui::GetIO(); - ImGuiStyle& style = ImGui::GetStyle(); - bool font_details_opened = ImGui::TreeNode(font, "Font: \"%s\"\n%.2f px, %d glyphs, %d file(s)", - font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size, font->ConfigDataCount); - ImGui::SameLine(); if (ImGui::SmallButton("Set as default")) { io.FontDefault = font; } - if (!font_details_opened) - return; - - ImGui::PushFont(font); - ImGui::Text("The quick brown fox jumps over the lazy dog"); - ImGui::PopFont(); - ImGui::DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f"); // Scale only this font - ImGui::SameLine(); HelpMarker( - "Note than the default embedded font is NOT meant to be scaled.\n\n" - "Font are currently rendered into bitmaps at a given size at the time of building the atlas. " - "You may oversample them to get some flexibility with scaling. " - "You can also render at multiple sizes and select which one to use at runtime.\n\n" - "(Glimmer of hope: the atlas system will be rewritten in the future to make scaling more flexible.)"); - ImGui::Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent); - ImGui::Text("Fallback character: '%c' (U+%04X)", font->FallbackChar, font->FallbackChar); - ImGui::Text("Ellipsis character: '%c' (U+%04X)", font->EllipsisChar, font->EllipsisChar); - const int surface_sqrt = (int)sqrtf((float)font->MetricsTotalSurface); - ImGui::Text("Texture Area: about %d px ~%dx%d px", font->MetricsTotalSurface, surface_sqrt, surface_sqrt); - for (int config_i = 0; config_i < font->ConfigDataCount; config_i++) - if (font->ConfigData) - if (const ImFontConfig* cfg = &font->ConfigData[config_i]) - ImGui::BulletText("Input %d: \'%s\', Oversample: (%d,%d), PixelSnapH: %d, Offset: (%.1f,%.1f)", - config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH, cfg->GlyphOffset.x, cfg->GlyphOffset.y); - if (ImGui::TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size)) + static int style_idx = -1; + if (ImGui::Combo(label, &style_idx, "Dark\0Light\0Classic\0")) { - // Display all glyphs of the fonts in separate pages of 256 characters - const ImU32 glyph_col = ImGui::GetColorU32(ImGuiCol_Text); - for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256) + switch (style_idx) { - // Skip ahead if a large bunch of glyphs are not present in the font (test in chunks of 4k) - // This is only a small optimization to reduce the number of iterations when IM_UNICODE_MAX_CODEPOINT - // is large // (if ImWchar==ImWchar32 we will do at least about 272 queries here) - if (!(base & 4095) && font->IsGlyphRangeUnused(base, base + 4095)) - { - base += 4096 - 256; - continue; - } - - int count = 0; - for (unsigned int n = 0; n < 256; n++) - if (font->FindGlyphNoFallback((ImWchar)(base + n))) - count++; - if (count <= 0) - continue; - if (!ImGui::TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, count > 1 ? "glyphs" : "glyph")) - continue; - float cell_size = font->FontSize * 1; - float cell_spacing = style.ItemSpacing.y; - ImVec2 base_pos = ImGui::GetCursorScreenPos(); - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - for (unsigned int n = 0; n < 256; n++) - { - // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions - // available here and thus cannot easily generate a zero-terminated UTF-8 encoded string. - ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing)); - ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size); - const ImFontGlyph* glyph = font->FindGlyphNoFallback((ImWchar)(base + n)); - draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50)); - if (glyph) - font->RenderChar(draw_list, cell_size, cell_p1, glyph_col, (ImWchar)(base + n)); - if (glyph && ImGui::IsMouseHoveringRect(cell_p1, cell_p2)) - { - ImGui::BeginTooltip(); - ImGui::Text("Codepoint: U+%04X", base + n); - ImGui::Separator(); - ImGui::Text("Visible: %d", glyph->Visible); - ImGui::Text("AdvanceX: %.1f", glyph->AdvanceX); - ImGui::Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1); - ImGui::Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1); - ImGui::EndTooltip(); - } - } - ImGui::Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16)); - ImGui::TreePop(); + case 0: ImGui::StyleColorsDark(); break; + case 1: ImGui::StyleColorsLight(); break; + case 2: ImGui::StyleColorsClassic(); break; } - ImGui::TreePop(); + return true; } - ImGui::TreePop(); + return false; } void ImGui::ShowStyleEditor(ImGuiStyle* ref) { + IMGUI_DEMO_MARKER("Tools/Style Editor"); // You can pass in a reference ImGuiStyle structure to compare to, revert to and save to // (without a reference style pointer, we will use one compared locally as a reference) ImGuiStyle& style = ImGui::GetStyle(); @@ -6037,21 +6182,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGuiIO& io = ImGui::GetIO(); ImFontAtlas* atlas = io.Fonts; HelpMarker("Read FAQ and docs/FONTS.md for details on font loading."); - ImGui::PushItemWidth(120); - for (int i = 0; i < atlas->Fonts.Size; i++) - { - ImFont* font = atlas->Fonts[i]; - ImGui::PushID(font); - NodeFont(font); - ImGui::PopID(); - } - if (ImGui::TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight)) - { - ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); - ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f); - ImGui::Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0, 0), ImVec2(1, 1), tint_col, border_col); - ImGui::TreePop(); - } + ImGui::ShowFontAtlas(atlas); // Post-baking font scaling. Note that this is NOT the nice way of scaling fonts, read below. // (we enforce hard clamping manually as by default DragFloat/SliderFloat allows CTRL+Click text to get out of bounds). @@ -6063,6 +6194,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) "rebuild the font atlas, and call style.ScaleAllSizes() on a reference ImGuiStyle structure.\n" "Using those settings here will give you poor quality results."); static float window_scale = 1.0f; + ImGui::PushItemWidth(ImGui::GetFontSize() * 8); if (ImGui::DragFloat("window scale", &window_scale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp)) // Scale only this window ImGui::SetWindowFontScale(window_scale); ImGui::DragFloat("global scale", &io.FontGlobalScale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp); // Scale everything @@ -6082,7 +6214,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) HelpMarker("Faster lines using texture data. Require backend to render with bilinear filtering (not point/nearest filtering)."); ImGui::Checkbox("Anti-aliased fill", &style.AntiAliasedFill); - ImGui::PushItemWidth(100); + ImGui::PushItemWidth(ImGui::GetFontSize() * 8); ImGui::DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, 10.0f, "%.2f"); if (style.CurveTessellationTol < 0.10f) style.CurveTessellationTol = 0.10f; @@ -6129,6 +6261,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) HelpMarker("When drawing circle primitives with \"num_segments == 0\" tesselation will be calculated automatically."); ImGui::DragFloat("Global Alpha", &style.Alpha, 0.005f, 0.20f, 1.0f, "%.2f"); // Not exposing zero here so user doesn't "lose" the UI (zero alpha clips all widgets). But application code could have a toggle to switch between zero and non-zero. + ImGui::DragFloat("Disabled Alpha", &style.DisabledAlpha, 0.005f, 0.0f, 1.0f, "%.2f"); ImGui::SameLine(); HelpMarker("Additional alpha multiplier for disabled items (multiply over current value of Alpha)."); ImGui::PopItemWidth(); ImGui::EndTabItem(); @@ -6178,6 +6311,7 @@ static void ShowExampleAppMainMenuBar() // (future version will add explicit flags to BeginMenu() to request processing shortcuts) static void ShowExampleMenuFile() { + IMGUI_DEMO_MARKER("Examples/Menu"); ImGui::MenuItem("(demo menu)", NULL, false, false); if (ImGui::MenuItem("New")) {} if (ImGui::MenuItem("Open", "Ctrl+O")) {} @@ -6203,6 +6337,7 @@ static void ShowExampleMenuFile() if (ImGui::MenuItem("Save As..")) {} ImGui::Separator(); + IMGUI_DEMO_MARKER("Examples/Menu/Options"); if (ImGui::BeginMenu("Options")) { static bool enabled = true; @@ -6219,6 +6354,7 @@ static void ShowExampleMenuFile() ImGui::EndMenu(); } + IMGUI_DEMO_MARKER("Examples/Menu/Colors"); if (ImGui::BeginMenu("Colors")) { float sz = ImGui::GetTextLineHeight(); @@ -6239,6 +6375,7 @@ static void ShowExampleMenuFile() // In a real code-base using it would make senses to use this feature from very different code locations. if (ImGui::BeginMenu("Options")) // <-- Append! { + IMGUI_DEMO_MARKER("Examples/Menu/Append to an existing menu"); static bool b = true; ImGui::Checkbox("SomeOption", &b); ImGui::EndMenu(); @@ -6271,6 +6408,7 @@ struct ExampleAppConsole ExampleAppConsole() { + IMGUI_DEMO_MARKER("Examples/Console"); ClearLog(); memset(InputBuf, 0, sizeof(InputBuf)); HistoryPos = -1; @@ -6748,6 +6886,7 @@ static void ShowExampleAppLog(bool* p_open) // Most of the contents of the window will be added by the log.Draw() call. ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver); ImGui::Begin("Example: Log", p_open); + IMGUI_DEMO_MARKER("Examples/Log"); if (ImGui::SmallButton("[Debug] Add 5 entries")) { static int counter = 0; @@ -6778,6 +6917,7 @@ static void ShowExampleAppLayout(bool* p_open) ImGui::SetNextWindowSize(ImVec2(500, 440), ImGuiCond_FirstUseEver); if (ImGui::Begin("Example: Simple layout", p_open, ImGuiWindowFlags_MenuBar)) { + IMGUI_DEMO_MARKER("Examples/Simple layout"); if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenu("File")) @@ -6794,6 +6934,7 @@ static void ShowExampleAppLayout(bool* p_open) ImGui::BeginChild("left pane", ImVec2(150, 0), true); for (int i = 0; i < 100; i++) { + // FIXME: Good candidate to use ImGuiSelectableFlags_SelectOnNav char label[128]; sprintf(label, "MyObject %d", i); if (ImGui::Selectable(label, selected == i)) @@ -6893,6 +7034,7 @@ static void ShowExampleAppPropertyEditor(bool* p_open) ImGui::End(); return; } + IMGUI_DEMO_MARKER("Examples/Property Editor"); HelpMarker( "This example shows how you may implement a property editor using two columns.\n" @@ -6928,6 +7070,7 @@ static void ShowExampleAppLongText(bool* p_open) ImGui::End(); return; } + IMGUI_DEMO_MARKER("Examples/Long text display"); static int test_type = 0; static ImGuiTextBuffer log; @@ -6989,6 +7132,7 @@ static void ShowExampleAppAutoResize(bool* p_open) ImGui::End(); return; } + IMGUI_DEMO_MARKER("Examples/Auto-resizing window"); static int lines = 10; ImGui::TextUnformatted( @@ -7040,6 +7184,7 @@ static void ShowExampleAppConstrainedResize(bool* p_open) ImGuiWindowFlags flags = auto_resize ? ImGuiWindowFlags_AlwaysAutoResize : 0; if (ImGui::Begin("Example: Constrained Resize", p_open, flags)) { + IMGUI_DEMO_MARKER("Examples/Constrained Resizing window"); if (ImGui::Button("200x200")) { ImGui::SetWindowSize(ImVec2(200, 200)); } ImGui::SameLine(); if (ImGui::Button("500x500")) { ImGui::SetWindowSize(ImVec2(500, 500)); } ImGui::SameLine(); if (ImGui::Button("800x200")) { ImGui::SetWindowSize(ImVec2(800, 200)); } @@ -7062,12 +7207,12 @@ static void ShowExampleAppConstrainedResize(bool* p_open) // + a context-menu to choose which corner of the screen to use. static void ShowExampleAppSimpleOverlay(bool* p_open) { - const float PAD = 10.0f; static int corner = 0; ImGuiIO& io = ImGui::GetIO(); ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav; if (corner != -1) { + const float PAD = 10.0f; const ImGuiViewport* viewport = ImGui::GetMainViewport(); ImVec2 work_pos = viewport->WorkPos; // Use work area to avoid menu-bar/task-bar, if any! ImVec2 work_size = viewport->WorkSize; @@ -7082,6 +7227,7 @@ static void ShowExampleAppSimpleOverlay(bool* p_open) ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background if (ImGui::Begin("Example: Simple overlay", p_open, window_flags)) { + IMGUI_DEMO_MARKER("Examples/Simple Overlay"); ImGui::Text("Simple overlay\n" "in the corner of the screen.\n" "(right-click to change position)"); ImGui::Separator(); if (ImGui::IsMousePosValid()) @@ -7156,6 +7302,7 @@ static void ShowExampleAppWindowTitles(bool*) // Using "##" to display same title but have unique identifier. ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 100), ImGuiCond_FirstUseEver); ImGui::Begin("Same title as another window##1"); + IMGUI_DEMO_MARKER("Examples/Manipulating window titles"); ImGui::Text("This is window 1.\nMy title is the same as window 2, but my identifier is unique."); ImGui::End(); @@ -7185,6 +7332,7 @@ static void ShowExampleAppCustomRendering(bool* p_open) ImGui::End(); return; } + IMGUI_DEMO_MARKER("Examples/Custom Rendering"); // Tip: If you do a lot of custom rendering, you probably want to use your own geometrical types and benefit of // overloaded operators, etc. Define IM_VEC2_CLASS_EXTRA in imconfig.h to create implicit conversions between your @@ -7567,6 +7715,16 @@ void ShowExampleAppDocuments(bool* p_open) ImGui::Separator(); + // About the ImGuiWindowFlags_UnsavedDocument / ImGuiTabItemFlags_UnsavedDocument flags. + // They have multiple effects: + // - Display a dot next to the title. + // - Tab is selected when clicking the X close button. + // - Closure is not assumed (will wait for user to stop submitting the tab). + // Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. + // We need to assume closure by default otherwise waiting for "lack of submission" on the next frame would leave an empty + // hole for one-frame, both in the tab-bar and in tab-contents when closing a tab/window. + // The rarely used SetTabItemClosed() function is a way to notify of programmatic closure to avoid the one-frame hole. + // Submit Tab Bar and Tabs { ImGuiTabBarFlags tab_bar_flags = (opt_fitting_flags) | (opt_reorderable ? ImGuiTabBarFlags_Reorderable : 0); diff --git a/external/imgui/src/imgui_draw.cpp b/r5dev/thirdparty/imgui/src/imgui_draw.cpp similarity index 95% rename from external/imgui/src/imgui_draw.cpp rename to r5dev/thirdparty/imgui/src/imgui_draw.cpp index 8f851d26..583ae6c0 100644 --- a/external/imgui/src/imgui_draw.cpp +++ b/r5dev/thirdparty/imgui/src/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.83 WIP +// dear imgui, v1.86 // (drawing and font code) /* @@ -26,14 +26,14 @@ Index of this file: #define _CRT_SECURE_NO_WARNINGS #endif -#include "imgui.h" +#include "thirdparty/imgui/include/imgui.h" #ifndef IMGUI_DISABLE #ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS #endif -#include "imgui_internal.h" +#include "thirdparty/imgui/include/imgui_internal.h" #ifdef IMGUI_ENABLE_FREETYPE #include "misc/freetype/imgui_freetype.h" #endif @@ -54,9 +54,12 @@ Index of this file: // Visual Studio warnings #ifdef _MSC_VER -#pragma warning (disable: 4127) // condition expression is constant -#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) -#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen +#pragma warning (disable: 4127) // condition expression is constant +#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) +#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen +#pragma warning (disable: 6255) // [Static Analyzer] _alloca indicates failure by raising a stack overflow exception. Consider using _malloca instead. +#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). +#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer) #endif // Clang/GCC warnings with -Weverything @@ -105,6 +108,9 @@ namespace IMGUI_STB_NAMESPACE #ifdef _MSC_VER #pragma warning (push) #pragma warning (disable: 4456) // declaration of 'xx' hides previous local declaration +#pragma warning (disable: 6011) // (stb_rectpack) Dereferencing NULL pointer 'cur->next'. +#pragma warning (disable: 6385) // (stb_truetype) Reading invalid data from 'buffer': the readable size is '_Old_3`kernel_width' bytes, but '3' bytes may be read. +#pragma warning (disable: 28182) // (stb_rectpack) Dereferencing NULL pointer. 'cur' contains the same NULL value as 'cur->next' did. #endif #if defined(__clang__) @@ -131,7 +137,7 @@ namespace IMGUI_STB_NAMESPACE #ifdef IMGUI_STB_RECT_PACK_FILENAME #include IMGUI_STB_RECT_PACK_FILENAME #else -#include "imstb_rectpack.h" +#include "thirdparty/imgui/include/imstb_rectpack.h" #endif #endif @@ -155,7 +161,7 @@ namespace IMGUI_STB_NAMESPACE #ifdef IMGUI_STB_TRUETYPE_FILENAME #include IMGUI_STB_TRUETYPE_FILENAME #else -#include "imstb_truetype.h" +#include "thirdparty/imgui/include/imstb_truetype.h" #endif #endif #endif // IMGUI_ENABLE_STB_TRUETYPE @@ -467,6 +473,7 @@ void ImDrawList::_PopUnusedDrawCmd() void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data) { + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; IM_ASSERT(curr_cmd->UserCallback == NULL); if (curr_cmd->ElemCount != 0) @@ -485,11 +492,25 @@ void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data) #define ImDrawCmd_HeaderCompare(CMD_LHS, CMD_RHS) (memcmp(CMD_LHS, CMD_RHS, ImDrawCmd_HeaderSize)) // Compare ClipRect, TextureId, VtxOffset #define ImDrawCmd_HeaderCopy(CMD_DST, CMD_SRC) (memcpy(CMD_DST, CMD_SRC, ImDrawCmd_HeaderSize)) // Copy ClipRect, TextureId, VtxOffset +// Try to merge two last draw commands +void ImDrawList::_TryMergeDrawCmds() +{ + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); + ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; + ImDrawCmd* prev_cmd = curr_cmd - 1; + if (ImDrawCmd_HeaderCompare(curr_cmd, prev_cmd) == 0 && curr_cmd->UserCallback == NULL && prev_cmd->UserCallback == NULL) + { + prev_cmd->ElemCount += curr_cmd->ElemCount; + CmdBuffer.pop_back(); + } +} + // Our scheme may appears a bit unusual, basically we want the most-common calls AddLine AddRect etc. to not have to perform any check so we always have a command ready in the stack. // The cost of figuring out if a new command has to be added or if we can merge is paid in those Update** functions only. void ImDrawList::_OnChangedClipRect() { // If current command is used with different settings we need to add a new command + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; if (curr_cmd->ElemCount != 0 && memcmp(&curr_cmd->ClipRect, &_CmdHeader.ClipRect, sizeof(ImVec4)) != 0) { @@ -512,6 +533,7 @@ void ImDrawList::_OnChangedClipRect() void ImDrawList::_OnChangedTextureID() { // If current command is used with different settings we need to add a new command + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; if (curr_cmd->ElemCount != 0 && curr_cmd->TextureId != _CmdHeader.TextureId) { @@ -535,6 +557,7 @@ void ImDrawList::_OnChangedVtxOffset() { // We don't need to compare curr_cmd->VtxOffset != _CmdHeader.VtxOffset because we know it'll be different at the time we call this. _VtxCurrentIdx = 0; + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; //IM_ASSERT(curr_cmd->VtxOffset != _CmdHeader.VtxOffset); // See #3349 if (curr_cmd->ElemCount != 0) @@ -687,10 +710,11 @@ void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, c } // On AddPolyline() and AddConvexPolyFilled() we intentionally avoid using ImVec2 and superfluous function calls to optimize debug/non-inlined builds. -// Those macros expects l-values. -#define IM_NORMALIZE2F_OVER_ZERO(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = 1.0f / ImSqrt(d2); VX *= inv_len; VY *= inv_len; } } while (0) +// - Those macros expects l-values and need to be used as their own statement. +// - Those macros are intentionally not surrounded by the 'do {} while (0)' idiom because even that translates to runtime with debug compilers. +#define IM_NORMALIZE2F_OVER_ZERO(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = ImRsqrt(d2); VX *= inv_len; VY *= inv_len; } } (void)0 #define IM_FIXNORMAL2F_MAX_INVLEN2 100.0f // 500.0f (see #4053, #3366) -#define IM_FIXNORMAL2F(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 > 0.000001f) { float inv_len2 = 1.0f / d2; if (inv_len2 > IM_FIXNORMAL2F_MAX_INVLEN2) inv_len2 = IM_FIXNORMAL2F_MAX_INVLEN2; VX *= inv_len2; VY *= inv_len2; } } while (0) +#define IM_FIXNORMAL2F(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 > 0.000001f) { float inv_len2 = 1.0f / d2; if (inv_len2 > IM_FIXNORMAL2F_MAX_INVLEN2) inv_len2 = IM_FIXNORMAL2F_MAX_INVLEN2; VX *= inv_len2; VY *= inv_len2; } } (void)0 // TODO: Thickness anti-aliased lines cap are missing their AA fringe. // We avoid using the ImVec2 math operators here to reduce cost to a minimum for debug/non-inlined builds. @@ -1460,24 +1484,22 @@ void ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int nu if ((col & IM_COL32_A_MASK) == 0 || radius <= 0.0f) return; - // Obtain segment count if (num_segments <= 0) { - // Automatic segment count - num_segments = _CalcCircleAutoSegmentCount(radius); + // Use arc with automatic segment count + _PathArcToFastEx(center, radius - 0.5f, 0, IM_DRAWLIST_ARCFAST_SAMPLE_MAX, 0); + _Path.Size--; } else { // Explicit segment count (still clamp to avoid drawing insanely tessellated shapes) num_segments = ImClamp(num_segments, 3, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX); + + // Because we are filling a closed shape we remove 1 from the count of segments/points + const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments; + PathArcTo(center, radius - 0.5f, 0.0f, a_max, num_segments - 1); } - // Because we are filling a closed shape we remove 1 from the count of segments/points - const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments; - if (num_segments == 12) - PathArcToFast(center, radius - 0.5f, 0, 12 - 1); - else - PathArcTo(center, radius - 0.5f, 0.0f, a_max, num_segments - 1); PathStroke(col, ImDrawFlags_Closed, thickness); } @@ -1486,24 +1508,22 @@ void ImDrawList::AddCircleFilled(const ImVec2& center, float radius, ImU32 col, if ((col & IM_COL32_A_MASK) == 0 || radius <= 0.0f) return; - // Obtain segment count if (num_segments <= 0) { - // Automatic segment count - num_segments = _CalcCircleAutoSegmentCount(radius); + // Use arc with automatic segment count + _PathArcToFastEx(center, radius, 0, IM_DRAWLIST_ARCFAST_SAMPLE_MAX, 0); + _Path.Size--; } else { // Explicit segment count (still clamp to avoid drawing insanely tessellated shapes) num_segments = ImClamp(num_segments, 3, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX); + + // Because we are filling a closed shape we remove 1 from the count of segments/points + const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments; + PathArcTo(center, radius, 0.0f, a_max, num_segments - 1); } - // Because we are filling a closed shape we remove 1 from the count of segments/points - const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments; - if (num_segments == 12) - PathArcToFast(center, radius, 0, 12 - 1); - else - PathArcTo(center, radius, 0.0f, a_max, num_segments - 1); PathFillConvex(col); } @@ -1904,37 +1924,38 @@ ImFontConfig::ImFontConfig() // A work of art lies ahead! (. = white layer, X = black layer, others are blank) // The 2x2 white texels on the top left are the ones we'll use everywhere in Dear ImGui to render filled shapes. -const int FONT_ATLAS_DEFAULT_TEX_DATA_W = 108; // Actual texture will be 2 times that + 1 spacing. +// (This is used when io.MouseDrawCursor = true) +const int FONT_ATLAS_DEFAULT_TEX_DATA_W = 122; // Actual texture will be 2 times that + 1 spacing. const int FONT_ATLAS_DEFAULT_TEX_DATA_H = 27; static const char FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[FONT_ATLAS_DEFAULT_TEX_DATA_W * FONT_ATLAS_DEFAULT_TEX_DATA_H + 1] = { - "..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX- XX " - "..- -X.....X- X.X - X.X -X.....X - X.....X- X..X " - "--- -XXX.XXX- X...X - X...X -X....X - X....X- X..X " - "X - X.X - X.....X - X.....X -X...X - X...X- X..X " - "XX - X.X -X.......X- X.......X -X..X.X - X.X..X- X..X " - "X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X- X..XXX " - "X..X - X.X - X.X - X.X -XX X.X - X.X XX- X..X..XXX " - "X...X - X.X - X.X - XX X.X XX - X.X - X.X - X..X..X..XX " - "X....X - X.X - X.X - X.X X.X X.X - X.X - X.X - X..X..X..X.X " - "X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X -XXX X..X..X..X..X" - "X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X -X..XX........X..X" - "X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X -X...X...........X" - "X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X - X..............X" - "X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X - X.............X" - "X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X - X.............X" - "X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X - X............X" - "X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX - X...........X " - "X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------- X..........X " - "X.X X..X - -X.......X- X.......X - XX XX - - X..........X " - "XX X..X - - X.....X - X.....X - X.X X.X - - X........X " - " X..X - X...X - X...X - X..X X..X - - X........X " - " XX - X.X - X.X - X...XXXXXXXXXXXXX...X - - XXXXXXXXXX " - "------------ - X - X -X.....................X- ------------------" - " ----------------------------------- X...XXXXXXXXXXXXX...X - " - " - X..X X..X - " - " - X.X X.X - " - " - XX XX - " + "..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX- XX - XX XX " + "..- -X.....X- X.X - X.X -X.....X - X.....X- X..X -X..X X..X" + "--- -XXX.XXX- X...X - X...X -X....X - X....X- X..X -X...X X...X" + "X - X.X - X.....X - X.....X -X...X - X...X- X..X - X...X X...X " + "XX - X.X -X.......X- X.......X -X..X.X - X.X..X- X..X - X...X...X " + "X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X- X..XXX - X.....X " + "X..X - X.X - X.X - X.X -XX X.X - X.X XX- X..X..XXX - X...X " + "X...X - X.X - X.X - XX X.X XX - X.X - X.X - X..X..X..XX - X.X " + "X....X - X.X - X.X - X.X X.X X.X - X.X - X.X - X..X..X..X.X - X...X " + "X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X -XXX X..X..X..X..X- X.....X " + "X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X -X..XX........X..X- X...X...X " + "X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X -X...X...........X- X...X X...X " + "X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X - X..............X-X...X X...X" + "X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X - X.............X-X..X X..X" + "X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X - X.............X- XX XX " + "X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X - X............X--------------" + "X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX - X...........X - " + "X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------- X..........X - " + "X.X X..X - -X.......X- X.......X - XX XX - - X..........X - " + "XX X..X - - X.....X - X.....X - X.X X.X - - X........X - " + " X..X - - X...X - X...X - X..X X..X - - X........X - " + " XX - - X.X - X.X - X...XXXXXXXXXXXXX...X - - XXXXXXXXXX - " + "------------- - X - X -X.....................X- ------------------- " + " ----------------------------------- X...XXXXXXXXXXXXX...X - " + " - X..X X..X - " + " - X.X X.X - " + " - XX XX - " }; static const ImVec2 FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[ImGuiMouseCursor_COUNT][3] = @@ -1948,6 +1969,7 @@ static const ImVec2 FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[ImGuiMouseCursor_COUNT][3 { ImVec2(73,0), ImVec2(17,17), ImVec2( 8, 8) }, // ImGuiMouseCursor_ResizeNESW { ImVec2(55,0), ImVec2(17,17), ImVec2( 8, 8) }, // ImGuiMouseCursor_ResizeNWSE { ImVec2(91,0), ImVec2(17,22), ImVec2( 5, 0) }, // ImGuiMouseCursor_Hand + { ImVec2(109,0),ImVec2(13,15), ImVec2( 6, 7) }, // ImGuiMouseCursor_NotAllowed }; ImFontAtlas::ImFontAtlas() @@ -1983,6 +2005,7 @@ void ImFontAtlas::ClearInputData() ConfigData.clear(); CustomRects.clear(); PackIdMouseCursors = PackIdLines = -1; + // Important: we leave TexReady untouched } void ImFontAtlas::ClearTexData() @@ -1995,14 +2018,14 @@ void ImFontAtlas::ClearTexData() TexPixelsAlpha8 = NULL; TexPixelsRGBA32 = NULL; TexPixelsUseColors = false; + // Important: we leave TexReady untouched } void ImFontAtlas::ClearFonts() { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); - for (int i = 0; i < Fonts.Size; i++) - IM_DELETE(Fonts[i]); - Fonts.clear(); + Fonts.clear_delete(); + TexReady = false; } void ImFontAtlas::Clear() @@ -2016,11 +2039,7 @@ void ImFontAtlas::GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_wid { // Build atlas on demand if (TexPixelsAlpha8 == NULL) - { - if (ConfigData.empty()) - AddFontDefault(); Build(); - } *out_pixels = TexPixelsAlpha8; if (out_width) *out_width = TexWidth; @@ -2079,6 +2098,7 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg) new_font_cfg.DstFont->EllipsisChar = font_cfg->EllipsisChar; // Invalidate texture + TexReady = false; ClearTexData(); return new_font_cfg.DstFont; } @@ -2150,7 +2170,7 @@ ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float si IM_ASSERT(font_cfg.FontData == NULL); font_cfg.FontData = ttf_data; font_cfg.FontDataSize = ttf_size; - font_cfg.SizePixels = size_pixels; + font_cfg.SizePixels = size_pixels > 0.0f ? size_pixels : font_cfg.SizePixels; if (glyph_ranges) font_cfg.GlyphRanges = glyph_ranges; return AddFont(&font_cfg); @@ -2241,6 +2261,10 @@ bool ImFontAtlas::Build() { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); + // Default font is none are specified + if (ConfigData.Size == 0) + AddFontDefault(); + // Select builder // - Note that we do not reassign to atlas->FontBuilderIO, since it is likely to point to static data which // may mess with some hot-reloading schemes. If you need to assign to this (for dynamic selection) AND are @@ -2562,9 +2586,8 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) } } - // Cleanup temporary (ImVector doesn't honor destructor) - for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) - src_tmp_array[src_i].~ImFontBuildSrcData(); + // Cleanup + src_tmp_array.clear_destruct(); ImFontAtlasBuildFinish(atlas); return true; @@ -2780,22 +2803,7 @@ void ImFontAtlasBuildFinish(ImFontAtlas* atlas) if (atlas->Fonts[i]->DirtyLookupTables) atlas->Fonts[i]->BuildLookupTable(); - // Ellipsis character is required for rendering elided text. We prefer using U+2026 (horizontal ellipsis). - // However some old fonts may contain ellipsis at U+0085. Here we auto-detect most suitable ellipsis character. - // FIXME: Also note that 0x2026 is currently seldom included in our font ranges. Because of this we are more likely to use three individual dots. - for (int i = 0; i < atlas->Fonts.size(); i++) - { - ImFont* font = atlas->Fonts[i]; - if (font->EllipsisChar != (ImWchar)-1) - continue; - const ImWchar ellipsis_variants[] = { (ImWchar)0x2026, (ImWchar)0x0085 }; - for (int j = 0; j < IM_ARRAYSIZE(ellipsis_variants); j++) - if (font->FindGlyphNoFallback(ellipsis_variants[j]) != NULL) // Verify glyph exists - { - font->EllipsisChar = ellipsis_variants[j]; - break; - } - } + atlas->TexReady = true; } // Retrieve list of range (2 int per range, values are inclusive) @@ -2816,6 +2824,7 @@ const ImWchar* ImFontAtlas::GetGlyphRangesKorean() 0x0020, 0x00FF, // Basic Latin + Latin Supplement 0x3131, 0x3163, // Korean alphabets 0xAC00, 0xD7A3, // Korean characters + 0xFFFD, 0xFFFD, // Invalid 0, }; return &ranges[0]; @@ -2830,6 +2839,7 @@ const ImWchar* ImFontAtlas::GetGlyphRangesChineseFull() 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana 0x31F0, 0x31FF, // Katakana Phonetic Extensions 0xFF00, 0xFFEF, // Half-width characters + 0xFFFD, 0xFFFD, // Invalid 0x4e00, 0x9FAF, // CJK Ideograms 0, }; @@ -2906,7 +2916,8 @@ const ImWchar* ImFontAtlas::GetGlyphRangesChineseSimplifiedCommon() 0x2000, 0x206F, // General Punctuation 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana 0x31F0, 0x31FF, // Katakana Phonetic Extensions - 0xFF00, 0xFFEF // Half-width characters + 0xFF00, 0xFFEF, // Half-width characters + 0xFFFD, 0xFFFD // Invalid }; static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00) * 2 + 1] = { 0 }; if (!full_ranges[0]) @@ -2995,7 +3006,8 @@ const ImWchar* ImFontAtlas::GetGlyphRangesJapanese() 0x0020, 0x00FF, // Basic Latin + Latin Supplement 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana 0x31F0, 0x31FF, // Katakana Phonetic Extensions - 0xFF00, 0xFFEF // Half-width characters + 0xFF00, 0xFFEF, // Half-width characters + 0xFFFD, 0xFFFD // Invalid }; static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00)*2 + 1] = { 0 }; if (!full_ranges[0]) @@ -3068,8 +3080,8 @@ void ImFontGlyphRangesBuilder::AddText(const char* text, const char* text_end) void ImFontGlyphRangesBuilder::AddRanges(const ImWchar* ranges) { for (; ranges[0]; ranges += 2) - for (ImWchar c = ranges[0]; c <= ranges[1]; c++) - AddChar(c); + for (unsigned int c = ranges[0]; c <= ranges[1] && c <= IM_UNICODE_CODEPOINT_MAX; c++) //-V560 + AddChar((ImWchar)c); } void ImFontGlyphRangesBuilder::BuildRanges(ImVector* out_ranges) @@ -3094,8 +3106,9 @@ ImFont::ImFont() { FontSize = 0.0f; FallbackAdvanceX = 0.0f; - FallbackChar = (ImWchar)'?'; + FallbackChar = (ImWchar)-1; EllipsisChar = (ImWchar)-1; + DotChar = (ImWchar)-1; FallbackGlyph = NULL; ContainerAtlas = NULL; ConfigData = NULL; @@ -3126,6 +3139,14 @@ void ImFont::ClearOutputData() MetricsTotalSurface = 0; } +static ImWchar FindFirstExistingGlyph(ImFont* font, const ImWchar* candidate_chars, int candidate_chars_count) +{ + for (int n = 0; n < candidate_chars_count; n++) + if (font->FindGlyphNoFallback(candidate_chars[n]) != NULL) + return candidate_chars[n]; + return (ImWchar)-1; +} + void ImFont::BuildLookupTable() { int max_codepoint = 0; @@ -3168,9 +3189,31 @@ void ImFont::BuildLookupTable() SetGlyphVisible((ImWchar)' ', false); SetGlyphVisible((ImWchar)'\t', false); - // Setup fall-backs + // Ellipsis character is required for rendering elided text. We prefer using U+2026 (horizontal ellipsis). + // However some old fonts may contain ellipsis at U+0085. Here we auto-detect most suitable ellipsis character. + // FIXME: Note that 0x2026 is rarely included in our font ranges. Because of this we are more likely to use three individual dots. + const ImWchar ellipsis_chars[] = { (ImWchar)0x2026, (ImWchar)0x0085 }; + const ImWchar dots_chars[] = { (ImWchar)'.', (ImWchar)0xFF0E }; + if (EllipsisChar == (ImWchar)-1) + EllipsisChar = FindFirstExistingGlyph(this, ellipsis_chars, IM_ARRAYSIZE(ellipsis_chars)); + if (DotChar == (ImWchar)-1) + DotChar = FindFirstExistingGlyph(this, dots_chars, IM_ARRAYSIZE(dots_chars)); + + // Setup fallback character + const ImWchar fallback_chars[] = { (ImWchar)IM_UNICODE_CODEPOINT_INVALID, (ImWchar)'?', (ImWchar)' ' }; FallbackGlyph = FindGlyphNoFallback(FallbackChar); - FallbackAdvanceX = FallbackGlyph ? FallbackGlyph->AdvanceX : 0.0f; + if (FallbackGlyph == NULL) + { + FallbackChar = FindFirstExistingGlyph(this, fallback_chars, IM_ARRAYSIZE(fallback_chars)); + FallbackGlyph = FindGlyphNoFallback(FallbackChar); + if (FallbackGlyph == NULL) + { + FallbackGlyph = &Glyphs.back(); + FallbackChar = (ImWchar)FallbackGlyph->Codepoint; + } + } + + FallbackAdvanceX = FallbackGlyph->AdvanceX; for (int i = 0; i < max_codepoint + 1; i++) if (IndexAdvanceX[i] < 0.0f) IndexAdvanceX[i] = FallbackAdvanceX; @@ -3195,12 +3238,6 @@ void ImFont::SetGlyphVisible(ImWchar c, bool visible) glyph->Visible = visible ? 1 : 0; } -void ImFont::SetFallbackChar(ImWchar c) -{ - FallbackChar = c; - BuildLookupTable(); -} - void ImFont::GrowIndex(int new_size) { IM_ASSERT(IndexAdvanceX.Size == IndexLookup.Size); @@ -3702,6 +3739,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col // - RenderMouseCursor() // - RenderArrowPointingAt() // - RenderRectFilledRangeH() +// - RenderRectFilledWithHole() //----------------------------------------------------------------------------- // Function in need of a redesign (legacy mess) // - RenderColorRectWithAlphaCheckerboard() @@ -3770,7 +3808,7 @@ void ImGui::RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, Im if (font_atlas->GetMouseCursorTexData(mouse_cursor, &offset, &size, &uv[0], &uv[2])) { pos -= offset; - const ImTextureID tex_id = font_atlas->TexID; + ImTextureID tex_id = font_atlas->TexID; draw_list->PushTextureID(tex_id); draw_list->AddImage(tex_id, pos + ImVec2(1, 0) * scale, pos + (ImVec2(1, 0) + size) * scale, uv[2], uv[3], col_shadow); draw_list->AddImage(tex_id, pos + ImVec2(2, 0) * scale, pos + (ImVec2(2, 0) + size) * scale, uv[2], uv[3], col_shadow); @@ -3868,10 +3906,10 @@ void ImGui::RenderRectFilledWithHole(ImDrawList* draw_list, ImRect outer, ImRect const bool fill_R = (inner.Max.x < outer.Max.x); const bool fill_U = (inner.Min.y > outer.Min.y); const bool fill_D = (inner.Max.y < outer.Max.y); - if (fill_L) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Min.y), ImVec2(inner.Min.x, inner.Max.y), col, rounding, (fill_U ? 0 : ImDrawFlags_RoundCornersTopLeft) | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomLeft)); - if (fill_R) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Min.y), ImVec2(outer.Max.x, inner.Max.y), col, rounding, (fill_U ? 0 : ImDrawFlags_RoundCornersTopRight) | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomRight)); - if (fill_U) draw_list->AddRectFilled(ImVec2(inner.Min.x, outer.Min.y), ImVec2(inner.Max.x, inner.Min.y), col, rounding, (fill_L ? 0 : ImDrawFlags_RoundCornersTopLeft) | (fill_R ? 0 : ImDrawFlags_RoundCornersTopRight)); - if (fill_D) draw_list->AddRectFilled(ImVec2(inner.Min.x, inner.Max.y), ImVec2(inner.Max.x, outer.Max.y), col, rounding, (fill_L ? 0 : ImDrawFlags_RoundCornersBottomLeft) | (fill_R ? 0 : ImDrawFlags_RoundCornersBottomRight)); + if (fill_L) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Min.y), ImVec2(inner.Min.x, inner.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_U ? 0 : ImDrawFlags_RoundCornersTopLeft) | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomLeft)); + if (fill_R) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Min.y), ImVec2(outer.Max.x, inner.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_U ? 0 : ImDrawFlags_RoundCornersTopRight) | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomRight)); + if (fill_U) draw_list->AddRectFilled(ImVec2(inner.Min.x, outer.Min.y), ImVec2(inner.Max.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_L ? 0 : ImDrawFlags_RoundCornersTopLeft) | (fill_R ? 0 : ImDrawFlags_RoundCornersTopRight)); + if (fill_D) draw_list->AddRectFilled(ImVec2(inner.Min.x, inner.Max.y), ImVec2(inner.Max.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_L ? 0 : ImDrawFlags_RoundCornersBottomLeft) | (fill_R ? 0 : ImDrawFlags_RoundCornersBottomRight)); if (fill_L && fill_U) draw_list->AddRectFilled(ImVec2(outer.Min.x, outer.Min.y), ImVec2(inner.Min.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersTopLeft); if (fill_R && fill_U) draw_list->AddRectFilled(ImVec2(inner.Max.x, outer.Min.y), ImVec2(outer.Max.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersTopRight); if (fill_L && fill_D) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Max.y), ImVec2(inner.Min.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersBottomLeft); diff --git a/external/imgui/src/imgui_impl_dx11.cpp b/r5dev/thirdparty/imgui/src/imgui_impl_dx11.cpp similarity index 72% rename from external/imgui/src/imgui_impl_dx11.cpp rename to r5dev/thirdparty/imgui/src/imgui_impl_dx11.cpp index 869dd4e1..7d616a60 100644 --- a/external/imgui/src/imgui_impl_dx11.cpp +++ b/r5dev/thirdparty/imgui/src/imgui_impl_dx11.cpp @@ -5,12 +5,15 @@ // [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. -// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). +// 2021-05-19: DirectX11: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) // 2021-02-18: DirectX11: Change blending equation to preserve alpha in output buffer. // 2019-08-01: DirectX11: Fixed code querying the Geometry Shader state (would generally error with Debug layer enabled). // 2019-07-21: DirectX11: Backup, clear and restore Geometry Shader is any is bound when calling ImGui_ImplDX10_RenderDrawData. Clearing Hull/Domain/Compute shaders without backup/restore. @@ -26,8 +29,8 @@ // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. // 2016-05-07: DirectX11: Disabling depth-write. -#include "imgui.h" -#include "imgui_impl_dx11.h" +#include "thirdparty/imgui/include/imgui.h" +#include "thirdparty/imgui/include/imgui_impl_dx11.h" // DirectX #include @@ -37,30 +40,46 @@ #pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below. #endif -// DirectX data -static ID3D11Device* g_pd3dDevice = NULL; -static ID3D11DeviceContext* g_pd3dDeviceContext = NULL; -static IDXGIFactory* g_pFactory = NULL; -static ID3D11Buffer* g_pVB = NULL; -static ID3D11Buffer* g_pIB = NULL; -static ID3D11VertexShader* g_pVertexShader = NULL; -static ID3D11InputLayout* g_pInputLayout = NULL; -static ID3D11Buffer* g_pVertexConstantBuffer = NULL; -static ID3D11PixelShader* g_pPixelShader = NULL; -static ID3D11SamplerState* g_pFontSampler = NULL; -static ID3D11ShaderResourceView*g_pFontTextureView = NULL; -static ID3D11RasterizerState* g_pRasterizerState = NULL; -static ID3D11BlendState* g_pBlendState = NULL; -static ID3D11DepthStencilState* g_pDepthStencilState = NULL; -static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000; +// DirectX11 data +struct ImGui_ImplDX11_Data +{ + ID3D11Device* pd3dDevice; + ID3D11DeviceContext* pd3dDeviceContext; + IDXGIFactory* pFactory; + ID3D11Buffer* pVB; + ID3D11Buffer* pIB; + ID3D11VertexShader* pVertexShader; + ID3D11InputLayout* pInputLayout; + ID3D11Buffer* pVertexConstantBuffer; + ID3D11PixelShader* pPixelShader; + ID3D11SamplerState* pFontSampler; + ID3D11ShaderResourceView* pFontTextureView; + ID3D11RasterizerState* pRasterizerState; + ID3D11BlendState* pBlendState; + ID3D11DepthStencilState* pDepthStencilState; + int VertexBufferSize; + int IndexBufferSize; + + ImGui_ImplDX11_Data() { memset(this, 0, sizeof(*this)); VertexBufferSize = 5000; IndexBufferSize = 10000; } +}; struct VERTEX_CONSTANT_BUFFER { float mvp[4][4]; }; +// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts +// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. +static ImGui_ImplDX11_Data* ImGui_ImplDX11_GetBackendData() +{ + return ImGui::GetCurrentContext() ? (ImGui_ImplDX11_Data*)ImGui::GetIO().BackendRendererUserData : NULL; +} + +// Functions static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceContext* ctx) { + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + // Setup viewport D3D11_VIEWPORT vp; memset(&vp, 0, sizeof(D3D11_VIEWPORT)); @@ -74,14 +93,14 @@ static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceC // Setup shader and vertex buffers unsigned int stride = sizeof(ImDrawVert); unsigned int offset = 0; - ctx->IASetInputLayout(g_pInputLayout); - ctx->IASetVertexBuffers(0, 1, &g_pVB, &stride, &offset); - ctx->IASetIndexBuffer(g_pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0); + ctx->IASetInputLayout(bd->pInputLayout); + ctx->IASetVertexBuffers(0, 1, &bd->pVB, &stride, &offset); + ctx->IASetIndexBuffer(bd->pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0); ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - ctx->VSSetShader(g_pVertexShader, NULL, 0); - ctx->VSSetConstantBuffers(0, 1, &g_pVertexConstantBuffer); - ctx->PSSetShader(g_pPixelShader, NULL, 0); - ctx->PSSetSamplers(0, 1, &g_pFontSampler); + ctx->VSSetShader(bd->pVertexShader, NULL, 0); + ctx->VSSetConstantBuffers(0, 1, &bd->pVertexConstantBuffer); + ctx->PSSetShader(bd->pPixelShader, NULL, 0); + ctx->PSSetSamplers(0, 1, &bd->pFontSampler); ctx->GSSetShader(NULL, NULL, 0); ctx->HSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used.. ctx->DSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used.. @@ -89,9 +108,9 @@ static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceC // Setup blend state const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f }; - ctx->OMSetBlendState(g_pBlendState, blend_factor, 0xffffffff); - ctx->OMSetDepthStencilState(g_pDepthStencilState, 0); - ctx->RSSetState(g_pRasterizerState); + ctx->OMSetBlendState(bd->pBlendState, blend_factor, 0xffffffff); + ctx->OMSetDepthStencilState(bd->pDepthStencilState, 0); + ctx->RSSetState(bd->pRasterizerState); } // Render function @@ -101,42 +120,43 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f) return; - ID3D11DeviceContext* ctx = g_pd3dDeviceContext; + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + ID3D11DeviceContext* ctx = bd->pd3dDeviceContext; // Create and grow vertex/index buffers if needed - if (!g_pVB || g_VertexBufferSize < draw_data->TotalVtxCount) + if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount) { - if (g_pVB) { g_pVB->Release(); g_pVB = NULL; } - g_VertexBufferSize = draw_data->TotalVtxCount + 5000; + if (bd->pVB) { bd->pVB->Release(); bd->pVB = NULL; } + bd->VertexBufferSize = draw_data->TotalVtxCount + 5000; D3D11_BUFFER_DESC desc; memset(&desc, 0, sizeof(D3D11_BUFFER_DESC)); desc.Usage = D3D11_USAGE_DYNAMIC; - desc.ByteWidth = g_VertexBufferSize * sizeof(ImDrawVert); + desc.ByteWidth = bd->VertexBufferSize * sizeof(ImDrawVert); desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; desc.MiscFlags = 0; - if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVB) < 0) + if (bd->pd3dDevice->CreateBuffer(&desc, NULL, &bd->pVB) < 0) return; } - if (!g_pIB || g_IndexBufferSize < draw_data->TotalIdxCount) + if (!bd->pIB || bd->IndexBufferSize < draw_data->TotalIdxCount) { - if (g_pIB) { g_pIB->Release(); g_pIB = NULL; } - g_IndexBufferSize = draw_data->TotalIdxCount + 10000; + if (bd->pIB) { bd->pIB->Release(); bd->pIB = NULL; } + bd->IndexBufferSize = draw_data->TotalIdxCount + 10000; D3D11_BUFFER_DESC desc; memset(&desc, 0, sizeof(D3D11_BUFFER_DESC)); desc.Usage = D3D11_USAGE_DYNAMIC; - desc.ByteWidth = g_IndexBufferSize * sizeof(ImDrawIdx); + desc.ByteWidth = bd->IndexBufferSize * sizeof(ImDrawIdx); desc.BindFlags = D3D11_BIND_INDEX_BUFFER; desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; - if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pIB) < 0) + if (bd->pd3dDevice->CreateBuffer(&desc, NULL, &bd->pIB) < 0) return; } // Upload vertex/index data into a single contiguous GPU buffer D3D11_MAPPED_SUBRESOURCE vtx_resource, idx_resource; - if (ctx->Map(g_pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK) + if (ctx->Map(bd->pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK) return; - if (ctx->Map(g_pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK) + if (ctx->Map(bd->pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK) return; ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource.pData; ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource.pData; @@ -148,14 +168,14 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) vtx_dst += cmd_list->VtxBuffer.Size; idx_dst += cmd_list->IdxBuffer.Size; } - ctx->Unmap(g_pVB, 0); - ctx->Unmap(g_pIB, 0); + ctx->Unmap(bd->pVB, 0); + ctx->Unmap(bd->pIB, 0); // Setup orthographic projection matrix into our constant buffer // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. { D3D11_MAPPED_SUBRESOURCE mapped_resource; - if (ctx->Map(g_pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK) + if (ctx->Map(bd->pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK) return; VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource.pData; float L = draw_data->DisplayPos.x; @@ -170,7 +190,7 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) { (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f }, }; memcpy(&constant_buffer->mvp, mvp, sizeof(mvp)); - ctx->Unmap(g_pVertexConstantBuffer, 0); + ctx->Unmap(bd->pVertexConstantBuffer, 0); } // Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!) @@ -243,12 +263,18 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) } else { + // Project scissor/clipping rectangles into framebuffer space + ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y); + ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y); + if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) + continue; + // Apply scissor/clipping rectangle - const D3D11_RECT r = { (LONG)(pcmd->ClipRect.x - clip_off.x), (LONG)(pcmd->ClipRect.y - clip_off.y), (LONG)(pcmd->ClipRect.z - clip_off.x), (LONG)(pcmd->ClipRect.w - clip_off.y) }; + const D3D11_RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y }; ctx->RSSetScissorRects(1, &r); // Bind texture, Draw - ID3D11ShaderResourceView* texture_srv = (ID3D11ShaderResourceView*)pcmd->TextureId; + ID3D11ShaderResourceView* texture_srv = (ID3D11ShaderResourceView*)pcmd->GetTexID(); ctx->PSSetShaderResources(0, 1, &texture_srv); ctx->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset); } @@ -281,6 +307,7 @@ static void ImGui_ImplDX11_CreateFontsTexture() { // Build texture atlas ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); @@ -304,7 +331,8 @@ static void ImGui_ImplDX11_CreateFontsTexture() subResource.pSysMem = pixels; subResource.SysMemPitch = desc.Width * 4; subResource.SysMemSlicePitch = 0; - g_pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture); + bd->pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture); + IM_ASSERT(pTexture != NULL); // Create texture view D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; @@ -313,12 +341,12 @@ static void ImGui_ImplDX11_CreateFontsTexture() srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; srvDesc.Texture2D.MipLevels = desc.MipLevels; srvDesc.Texture2D.MostDetailedMip = 0; - g_pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, &g_pFontTextureView); + bd->pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, &bd->pFontTextureView); pTexture->Release(); } // Store our identifier - io.Fonts->SetTexID((ImTextureID)g_pFontTextureView); + io.Fonts->SetTexID((ImTextureID)bd->pFontTextureView); // Create texture sampler { @@ -332,15 +360,16 @@ static void ImGui_ImplDX11_CreateFontsTexture() desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; desc.MinLOD = 0.f; desc.MaxLOD = 0.f; - g_pd3dDevice->CreateSamplerState(&desc, &g_pFontSampler); + bd->pd3dDevice->CreateSamplerState(&desc, &bd->pFontSampler); } } bool ImGui_ImplDX11_CreateDeviceObjects() { - if (!g_pd3dDevice) + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + if (!bd->pd3dDevice) return false; - if (g_pFontSampler) + if (bd->pFontSampler) ImGui_ImplDX11_InvalidateDeviceObjects(); // By using D3DCompile() from / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A) @@ -375,7 +404,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() {\ PS_INPUT output;\ output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\ - output.col.xyz = pow(abs(input.col.xyz), 2.2f);\ + output.col.xyz = pow(abs(input.col.xyz), 2.2f);\ output.col.w = input.col.w;\ output.uv = input.uv;\ return output;\ @@ -384,7 +413,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() ID3DBlob* vertexShaderBlob; if (FAILED(D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_4_0", 0, 0, &vertexShaderBlob, NULL))) return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! - if (g_pd3dDevice->CreateVertexShader(vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), NULL, &g_pVertexShader) != S_OK) + if (bd->pd3dDevice->CreateVertexShader(vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), NULL, &bd->pVertexShader) != S_OK) { vertexShaderBlob->Release(); return false; @@ -397,7 +426,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, uv), D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)IM_OFFSETOF(ImDrawVert, col), D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; - if (g_pd3dDevice->CreateInputLayout(local_layout, 3, vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), &g_pInputLayout) != S_OK) + if (bd->pd3dDevice->CreateInputLayout(local_layout, 3, vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), &bd->pInputLayout) != S_OK) { vertexShaderBlob->Release(); return false; @@ -412,7 +441,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; desc.MiscFlags = 0; - g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVertexConstantBuffer); + bd->pd3dDevice->CreateBuffer(&desc, NULL, &bd->pVertexConstantBuffer); } } @@ -437,7 +466,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() ID3DBlob* pixelShaderBlob; if (FAILED(D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_4_0", 0, 0, &pixelShaderBlob, NULL))) return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! - if (g_pd3dDevice->CreatePixelShader(pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize(), NULL, &g_pPixelShader) != S_OK) + if (bd->pd3dDevice->CreatePixelShader(pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize(), NULL, &bd->pPixelShader) != S_OK) { pixelShaderBlob->Release(); return false; @@ -458,7 +487,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; - g_pd3dDevice->CreateBlendState(&desc, &g_pBlendState); + bd->pd3dDevice->CreateBlendState(&desc, &bd->pBlendState); } // Create the rasterizer state @@ -469,7 +498,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() desc.CullMode = D3D11_CULL_NONE; desc.ScissorEnable = true; desc.DepthClipEnable = true; - g_pd3dDevice->CreateRasterizerState(&desc, &g_pRasterizerState); + bd->pd3dDevice->CreateRasterizerState(&desc, &bd->pRasterizerState); } // Create depth-stencil State @@ -483,7 +512,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; desc.BackFace = desc.FrontFace; - g_pd3dDevice->CreateDepthStencilState(&desc, &g_pDepthStencilState); + bd->pd3dDevice->CreateDepthStencilState(&desc, &bd->pDepthStencilState); } ImGui_ImplDX11_CreateFontsTexture(); @@ -493,27 +522,31 @@ bool ImGui_ImplDX11_CreateDeviceObjects() void ImGui_ImplDX11_InvalidateDeviceObjects() { - if (!g_pd3dDevice) + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + if (!bd->pd3dDevice) return; - if (g_pFontSampler) { g_pFontSampler->Release(); g_pFontSampler = NULL; } - if (g_pFontTextureView) { g_pFontTextureView->Release(); g_pFontTextureView = NULL; ImGui::GetIO().Fonts->SetTexID(NULL); } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well. - if (g_pIB) { g_pIB->Release(); g_pIB = NULL; } - if (g_pVB) { g_pVB->Release(); g_pVB = NULL; } - - if (g_pBlendState) { g_pBlendState->Release(); g_pBlendState = NULL; } - if (g_pDepthStencilState) { g_pDepthStencilState->Release(); g_pDepthStencilState = NULL; } - if (g_pRasterizerState) { g_pRasterizerState->Release(); g_pRasterizerState = NULL; } - if (g_pPixelShader) { g_pPixelShader->Release(); g_pPixelShader = NULL; } - if (g_pVertexConstantBuffer) { g_pVertexConstantBuffer->Release(); g_pVertexConstantBuffer = NULL; } - if (g_pInputLayout) { g_pInputLayout->Release(); g_pInputLayout = NULL; } - if (g_pVertexShader) { g_pVertexShader->Release(); g_pVertexShader = NULL; } + if (bd->pFontSampler) { bd->pFontSampler->Release(); bd->pFontSampler = NULL; } + if (bd->pFontTextureView) { bd->pFontTextureView->Release(); bd->pFontTextureView = NULL; ImGui::GetIO().Fonts->SetTexID(NULL); } // We copied data->pFontTextureView to io.Fonts->TexID so let's clear that as well. + if (bd->pIB) { bd->pIB->Release(); bd->pIB = NULL; } + if (bd->pVB) { bd->pVB->Release(); bd->pVB = NULL; } + if (bd->pBlendState) { bd->pBlendState->Release(); bd->pBlendState = NULL; } + if (bd->pDepthStencilState) { bd->pDepthStencilState->Release(); bd->pDepthStencilState = NULL; } + if (bd->pRasterizerState) { bd->pRasterizerState->Release(); bd->pRasterizerState = NULL; } + if (bd->pPixelShader) { bd->pPixelShader->Release(); bd->pPixelShader = NULL; } + if (bd->pVertexConstantBuffer) { bd->pVertexConstantBuffer->Release(); bd->pVertexConstantBuffer = NULL; } + if (bd->pInputLayout) { bd->pInputLayout->Release(); bd->pInputLayout = NULL; } + if (bd->pVertexShader) { bd->pVertexShader->Release(); bd->pVertexShader = NULL; } } bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context) { - // Setup backend capabilities flags ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!"); + + // Setup backend capabilities flags + ImGui_ImplDX11_Data* bd = IM_NEW(ImGui_ImplDX11_Data)(); + io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_dx11"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. @@ -526,28 +559,38 @@ bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_co if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) == S_OK) if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) == S_OK) { - g_pd3dDevice = device; - g_pd3dDeviceContext = device_context; - g_pFactory = pFactory; + bd->pd3dDevice = device; + bd->pd3dDeviceContext = device_context; + bd->pFactory = pFactory; } if (pDXGIDevice) pDXGIDevice->Release(); if (pDXGIAdapter) pDXGIAdapter->Release(); - g_pd3dDevice->AddRef(); - g_pd3dDeviceContext->AddRef(); + bd->pd3dDevice->AddRef(); + bd->pd3dDeviceContext->AddRef(); return true; } void ImGui_ImplDX11_Shutdown() { + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplDX11_InvalidateDeviceObjects(); - if (g_pFactory) { g_pFactory->Release(); g_pFactory = NULL; } - if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; } - if (g_pd3dDeviceContext) { g_pd3dDeviceContext->Release(); g_pd3dDeviceContext = NULL; } + if (bd->pFactory) { bd->pFactory->Release(); } + if (bd->pd3dDevice) { bd->pd3dDevice->Release(); } + if (bd->pd3dDeviceContext) { bd->pd3dDeviceContext->Release(); } + io.BackendRendererName = NULL; + io.BackendRendererUserData = NULL; + IM_DELETE(bd); } void ImGui_ImplDX11_NewFrame() { - if (!g_pFontSampler) + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + IM_ASSERT(bd != NULL && "Did you call ImGui_ImplDX11_Init()?"); + + if (!bd->pFontSampler) ImGui_ImplDX11_CreateDeviceObjects(); } diff --git a/external/imgui/src/imgui_impl_win32.cpp b/r5dev/thirdparty/imgui/src/imgui_impl_win32.cpp similarity index 67% rename from external/imgui/src/imgui_impl_win32.cpp rename to r5dev/thirdparty/imgui/src/imgui_impl_win32.cpp index edb7c3c5..3573797f 100644 --- a/external/imgui/src/imgui_impl_win32.cpp +++ b/r5dev/thirdparty/imgui/src/imgui_impl_win32.cpp @@ -7,12 +7,13 @@ // [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE). // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. -// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs -#include "imgui.h" -#include "imgui_impl_win32.h" +#include "thirdparty/imgui/include/imgui.h" +#include "thirdparty/imgui/include/imgui_impl_win32.h" #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif @@ -21,17 +22,23 @@ #include // Configuration flags to add in your imconfig.h file: -//#define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD // Disable gamepad support (this used to be meaningful before <1.81) but we know load XInput dynamically so the option is less relevant now. +//#define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD // Disable gamepad support. This was meaningful before <1.81 but we now load XInput dynamically so the option is now less relevant. // Using XInput for gamepad (will load DLL dynamically) #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD -#include +#include typedef DWORD (WINAPI *PFN_XInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES*); typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); #endif // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-12-16: Inputs: Fill VK_LCONTROL/VK_RCONTROL/VK_LSHIFT/VK_RSHIFT/VK_LMENU/VK_RMENU for completeness. +// 2021-08-17: Calling io.AddFocusEvent() on WM_SETFOCUS/WM_KILLFOCUS messages. +// 2021-08-02: Inputs: Fixed keyboard modifiers being reported when host window doesn't have focus. +// 2021-07-29: Inputs: MousePos is correctly reported when the host platform window is hovered but not focused (using TrackMouseEvent() to receive WM_MOUSELEAVE events). +// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). +// 2021-06-08: Fixed ImGui_ImplWin32_EnableDpiAwareness() and ImGui_ImplWin32_GetDpiScaleForMonitor() to handle Windows 8.1/10 features without a manifest (per-monitor DPI, and properly calls SetProcessDpiAwareness() on 8.1). // 2021-03-23: Inputs: Clearing keyboard down array when losing focus (WM_KILLFOCUS). // 2021-02-18: Added ImGui_ImplWin32_EnableAlphaCompositing(). Non Visual Studio users will need to link with dwmapi.lib (MinGW/gcc: use -ldwmapi). // 2021-02-17: Fixed ImGui_ImplWin32_EnableDpiAwareness() attempting to get SetProcessDpiAwareness from shcore.dll on Windows 8 whereas it is only supported on Windows 8.1. @@ -61,35 +68,60 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); // 2017-10-23: Inputs: Using Win32 ::SetCapture/::GetCapture() to retrieve mouse positions outside the client area when dragging. // 2016-11-12: Inputs: Only call Win32 ::SetCursor(NULL) when io.MouseDrawCursor is set. -// Win32 Data -static HWND g_hWnd = NULL; -static INT64 g_Time = 0; -static INT64 g_TicksPerSecond = 0; -static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT; -static bool g_HasGamepad = false; -static bool g_WantUpdateHasGamepad = true; +struct ImGui_ImplWin32_Data +{ + HWND hWnd; + HWND MouseHwnd; + bool MouseTracked; + INT64 Time; + INT64 TicksPerSecond; + ImGuiMouseCursor LastMouseCursor; + bool HasGamepad; + bool WantUpdateHasGamepad; -// XInput DLL and functions #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD -static HMODULE g_XInputDLL = NULL; -static PFN_XInputGetCapabilities g_XInputGetCapabilities = NULL; -static PFN_XInputGetState g_XInputGetState = NULL; + HMODULE XInputDLL; + PFN_XInputGetCapabilities XInputGetCapabilities; + PFN_XInputGetState XInputGetState; #endif + ImGui_ImplWin32_Data() { memset(this, 0, sizeof(*this)); } +}; + +// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts +// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. +// FIXME: multi-context support is not well tested and probably dysfunctional in this backend. +// FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context. +static ImGui_ImplWin32_Data* ImGui_ImplWin32_GetBackendData() +{ + return ImGui::GetCurrentContext() ? (ImGui_ImplWin32_Data*)ImGui::GetIO().BackendPlatformUserData : NULL; +} + // Functions bool ImGui_ImplWin32_Init(void* hwnd) { - if (!::QueryPerformanceFrequency((LARGE_INTEGER*)&g_TicksPerSecond)) + ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.BackendPlatformUserData == NULL && "Already initialized a platform backend!"); + + INT64 perf_frequency, perf_counter; + if (!::QueryPerformanceFrequency((LARGE_INTEGER*)&perf_frequency)) return false; - if (!::QueryPerformanceCounter((LARGE_INTEGER*)&g_Time)) + if (!::QueryPerformanceCounter((LARGE_INTEGER*)&perf_counter)) return false; // Setup backend capabilities flags - g_hWnd = (HWND)hwnd; - ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplWin32_Data* bd = IM_NEW(ImGui_ImplWin32_Data)(); + io.BackendPlatformUserData = (void*)bd; + io.BackendPlatformName = "imgui_impl_win32"; io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) - io.BackendPlatformName = "imgui_impl_win32"; + + bd->hWnd = (HWND)hwnd; + bd->WantUpdateHasGamepad = true; + bd->TicksPerSecond = perf_frequency; + bd->Time = perf_counter; + bd->LastMouseCursor = ImGuiMouseCursor_COUNT; + io.ImeWindowHandle = hwnd; // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime. @@ -129,9 +161,9 @@ bool ImGui_ImplWin32_Init(void* hwnd) for (int n = 0; n < IM_ARRAYSIZE(xinput_dll_names); n++) if (HMODULE dll = ::LoadLibraryA(xinput_dll_names[n])) { - g_XInputDLL = dll; - g_XInputGetCapabilities = (PFN_XInputGetCapabilities)::GetProcAddress(dll, "XInputGetCapabilities"); - g_XInputGetState = (PFN_XInputGetState)::GetProcAddress(dll, "XInputGetState"); + bd->XInputDLL = dll; + bd->XInputGetCapabilities = (PFN_XInputGetCapabilities)::GetProcAddress(dll, "XInputGetCapabilities"); + bd->XInputGetState = (PFN_XInputGetState)::GetProcAddress(dll, "XInputGetState"); break; } #endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD @@ -141,21 +173,19 @@ bool ImGui_ImplWin32_Init(void* hwnd) void ImGui_ImplWin32_Shutdown() { + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + IM_ASSERT(bd != NULL && "No platform backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + // Unload XInput library #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD - if (g_XInputDLL) - ::FreeLibrary(g_XInputDLL); - g_XInputDLL = NULL; - g_XInputGetCapabilities = NULL; - g_XInputGetState = NULL; + if (bd->XInputDLL) + ::FreeLibrary(bd->XInputDLL); #endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD - g_hWnd = NULL; - g_Time = 0; - g_TicksPerSecond = 0; - g_LastMouseCursor = ImGuiMouseCursor_COUNT; - g_HasGamepad = false; - g_WantUpdateHasGamepad = true; + io.BackendPlatformName = NULL; + io.BackendPlatformUserData = NULL; + IM_DELETE(bd); } static bool ImGui_ImplWin32_UpdateMouseCursor() @@ -193,24 +223,36 @@ static bool ImGui_ImplWin32_UpdateMouseCursor() static void ImGui_ImplWin32_UpdateMousePos() { + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); ImGuiIO& io = ImGui::GetIO(); - IM_ASSERT(g_hWnd != 0); + IM_ASSERT(bd->hWnd != 0); - // Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) + const ImVec2 mouse_pos_prev = io.MousePos; + io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); + + // Obtain focused and hovered window. We forward mouse input when focused or when hovered (and no other window is capturing) + HWND focused_window = ::GetForegroundWindow(); + HWND hovered_window = bd->MouseHwnd; + HWND mouse_window = NULL; + if (hovered_window && (hovered_window == bd->hWnd || ::IsChild(hovered_window, bd->hWnd))) + mouse_window = hovered_window; + else if (focused_window && (focused_window == bd->hWnd || ::IsChild(focused_window, bd->hWnd))) + mouse_window = focused_window; + if (mouse_window == NULL) + return; + + // Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) if (io.WantSetMousePos) { - POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; - if (::ClientToScreen(g_hWnd, &pos)) + POINT pos = { (int)mouse_pos_prev.x, (int)mouse_pos_prev.y }; + if (::ClientToScreen(bd->hWnd, &pos)) ::SetCursorPos(pos.x, pos.y); } - // Set mouse position - io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); + // Set Dear ImGui mouse position from OS position POINT pos; - if (HWND active_window = ::GetForegroundWindow()) - if (active_window == g_hWnd || ::IsChild(active_window, g_hWnd)) - if (::GetCursorPos(&pos) && ::ScreenToClient(g_hWnd, &pos)) - io.MousePos = ImVec2((float)pos.x, (float)pos.y); + if (::GetCursorPos(&pos) && ::ScreenToClient(mouse_window, &pos)) + io.MousePos = ImVec2((float)pos.x, (float)pos.y); } // Gamepad navigation mapping @@ -218,22 +260,23 @@ static void ImGui_ImplWin32_UpdateGamepads() { #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); memset(io.NavInputs, 0, sizeof(io.NavInputs)); if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) return; // Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow. // Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE. - if (g_WantUpdateHasGamepad) + if (bd->WantUpdateHasGamepad) { XINPUT_CAPABILITIES caps; - g_HasGamepad = g_XInputGetCapabilities ? (g_XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS) : false; - g_WantUpdateHasGamepad = false; + bd->HasGamepad = bd->XInputGetCapabilities ? (bd->XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS) : false; + bd->WantUpdateHasGamepad = false; } io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; XINPUT_STATE xinput_state; - if (g_HasGamepad && g_XInputGetState && g_XInputGetState(0, &xinput_state) == ERROR_SUCCESS) + if (bd->HasGamepad && bd->XInputGetState && bd->XInputGetState(0, &xinput_state) == ERROR_SUCCESS) { const XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad; io.BackendFlags |= ImGuiBackendFlags_HasGamepad; @@ -265,34 +308,28 @@ static void ImGui_ImplWin32_UpdateGamepads() void ImGui_ImplWin32_NewFrame() { ImGuiIO& io = ImGui::GetIO(); - IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer backend. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame()."); + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + IM_ASSERT(bd != NULL && "Did you call ImGui_ImplWin32_Init()?"); // Setup display size (every frame to accommodate for window resizing) RECT rect = { 0, 0, 0, 0 }; - ::GetClientRect(g_hWnd, &rect); + ::GetClientRect(bd->hWnd, &rect); io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top)); // Setup time step INT64 current_time = 0; ::QueryPerformanceCounter((LARGE_INTEGER*)¤t_time); - io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond; - g_Time = current_time; - - // Read keyboard modifiers inputs - io.KeyCtrl = (::GetKeyState(VK_CONTROL) & 0x8000) != 0; - io.KeyShift = (::GetKeyState(VK_SHIFT) & 0x8000) != 0; - io.KeyAlt = (::GetKeyState(VK_MENU) & 0x8000) != 0; - io.KeySuper = false; - // io.KeysDown[], io.MousePos, io.MouseDown[], io.MouseWheel: filled by the WndProc handler below. + io.DeltaTime = (float)(current_time - bd->Time) / bd->TicksPerSecond; + bd->Time = current_time; // Update OS mouse position ImGui_ImplWin32_UpdateMousePos(); // Update OS mouse cursor with the cursor requested by imgui ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor(); - if (g_LastMouseCursor != mouse_cursor) + if (bd->LastMouseCursor != mouse_cursor) { - g_LastMouseCursor = mouse_cursor; + bd->LastMouseCursor = mouse_cursor; ImGui_ImplWin32_UpdateMouseCursor(); } @@ -326,8 +363,25 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA return 0; ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + switch (msg) { + case WM_MOUSEMOVE: + // We need to call TrackMouseEvent in order to receive WM_MOUSELEAVE events + bd->MouseHwnd = hwnd; + if (!bd->MouseTracked) + { + TRACKMOUSEEVENT tme = { sizeof(tme), TME_LEAVE, hwnd, 0 }; + ::TrackMouseEvent(&tme); + bd->MouseTracked = true; + } + break; + case WM_MOUSELEAVE: + if (bd->MouseHwnd == hwnd) + bd->MouseHwnd = NULL; + bd->MouseTracked = false; + break; case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: @@ -365,17 +419,36 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA io.MouseWheelH += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA; return 0; case WM_KEYDOWN: - case WM_SYSKEYDOWN: - if (wParam < 256) - io.KeysDown[wParam] = 1; - return 0; case WM_KEYUP: + case WM_SYSKEYDOWN: case WM_SYSKEYUP: + { + bool down = (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN); if (wParam < 256) - io.KeysDown[wParam] = 0; + io.KeysDown[wParam] = down; + if (wParam == VK_CONTROL) + { + io.KeysDown[VK_LCONTROL] = ((::GetKeyState(VK_LCONTROL) & 0x8000) != 0); + io.KeysDown[VK_RCONTROL] = ((::GetKeyState(VK_RCONTROL) & 0x8000) != 0); + io.KeyCtrl = io.KeysDown[VK_LCONTROL] || io.KeysDown[VK_RCONTROL]; + } + if (wParam == VK_SHIFT) + { + io.KeysDown[VK_LSHIFT] = ((::GetKeyState(VK_LSHIFT) & 0x8000) != 0); + io.KeysDown[VK_RSHIFT] = ((::GetKeyState(VK_RSHIFT) & 0x8000) != 0); + io.KeyShift = io.KeysDown[VK_LSHIFT] || io.KeysDown[VK_RSHIFT]; + } + if (wParam == VK_MENU) + { + io.KeysDown[VK_LMENU] = ((::GetKeyState(VK_LMENU) & 0x8000) != 0); + io.KeysDown[VK_RMENU] = ((::GetKeyState(VK_RMENU) & 0x8000) != 0); + io.KeyAlt = io.KeysDown[VK_LMENU] || io.KeysDown[VK_RMENU]; + } return 0; + } + case WM_SETFOCUS: case WM_KILLFOCUS: - memset(io.KeysDown, 0, sizeof(io.KeysDown)); + io.AddFocusEvent(msg == WM_SETFOCUS); return 0; case WM_CHAR: // You can also use ToAscii()+GetKeyboardState() to retrieve characters. @@ -388,7 +461,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA return 0; case WM_DEVICECHANGE: if ((UINT)wParam == DBT_DEVNODES_CHANGED) - g_WantUpdateHasGamepad = true; + bd->WantUpdateHasGamepad = true; return 0; } return 0; @@ -409,21 +482,32 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA // If you are trying to implement your own backend for your own engine, you may ignore that noise. //--------------------------------------------------------------------------------------------------------- -// Implement some of the functions and types normally declared in recent Windows SDK. -#if !defined(_versionhelpers_H_INCLUDED_) && !defined(_INC_VERSIONHELPERS) -static BOOL IsWindowsVersionOrGreater(WORD major, WORD minor, WORD sp) +// Perform our own check with RtlVerifyVersionInfo() instead of using functions from as they +// require a manifest to be functional for checks above 8.1. See https://github.com/ocornut/imgui/issues/4200 +static BOOL _IsWindowsVersionOrGreater(WORD major, WORD minor, WORD) { - OSVERSIONINFOEXW osvi = { sizeof(osvi), major, minor, 0, 0, { 0 }, sp, 0, 0, 0, 0 }; - DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR; - ULONGLONG cond = ::VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL); - cond = ::VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL); - cond = ::VerSetConditionMask(cond, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); - return ::VerifyVersionInfoW(&osvi, mask, cond); + typedef LONG(WINAPI* PFN_RtlVerifyVersionInfo)(OSVERSIONINFOEXW*, ULONG, ULONGLONG); + static PFN_RtlVerifyVersionInfo RtlVerifyVersionInfoFn = NULL; + if (RtlVerifyVersionInfoFn == NULL) + if (HMODULE ntdllModule = ::GetModuleHandleA("ntdll.dll")) + RtlVerifyVersionInfoFn = (PFN_RtlVerifyVersionInfo)GetProcAddress(ntdllModule, "RtlVerifyVersionInfo"); + if (RtlVerifyVersionInfoFn == NULL) + return FALSE; + + RTL_OSVERSIONINFOEXW versionInfo = { }; + ULONGLONG conditionMask = 0; + versionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); + versionInfo.dwMajorVersion = major; + versionInfo.dwMinorVersion = minor; + VER_SET_CONDITION(conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL); + VER_SET_CONDITION(conditionMask, VER_MINORVERSION, VER_GREATER_EQUAL); + return (RtlVerifyVersionInfoFn(&versionInfo, VER_MAJORVERSION | VER_MINORVERSION, conditionMask) == 0) ? TRUE : FALSE; } -#define IsWindowsVistaOrGreater() IsWindowsVersionOrGreater(HIBYTE(0x0600), LOBYTE(0x0600), 0) // _WIN32_WINNT_VISTA -#define IsWindows8OrGreater() IsWindowsVersionOrGreater(HIBYTE(0x0602), LOBYTE(0x0602), 0) // _WIN32_WINNT_WIN8 -#define IsWindows8Point1OrGreater() IsWindowsVersionOrGreater(HIBYTE(0x0603), LOBYTE(0x0603), 0) // _WIN32_WINNT_WINBLUE -#endif + +#define _IsWindowsVistaOrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0600), LOBYTE(0x0600), 0) // _WIN32_WINNT_VISTA +#define _IsWindows8OrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0602), LOBYTE(0x0602), 0) // _WIN32_WINNT_WIN8 +#define _IsWindows8Point1OrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0603), LOBYTE(0x0603), 0) // _WIN32_WINNT_WINBLUE +#define _IsWindows10OrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0A00), LOBYTE(0x0A00), 0) // _WIN32_WINNT_WINTHRESHOLD / _WIN32_WINNT_WIN10 #ifndef DPI_ENUMS_DECLARED typedef enum { PROCESS_DPI_UNAWARE = 0, PROCESS_SYSTEM_DPI_AWARE = 1, PROCESS_PER_MONITOR_DPI_AWARE = 2 } PROCESS_DPI_AWARENESS; @@ -443,7 +527,7 @@ typedef DPI_AWARENESS_CONTEXT(WINAPI* PFN_SetThreadDpiAwarenessContext)(DPI_AWAR // Helper function to enable DPI awareness without setting up a manifest void ImGui_ImplWin32_EnableDpiAwareness() { - // if (IsWindows10OrGreater()) // This needs a manifest to succeed. Instead we try to grab the function pointer! + if (_IsWindows10OrGreater()) { static HINSTANCE user32_dll = ::LoadLibraryA("user32.dll"); // Reference counted per-process if (PFN_SetThreadDpiAwarenessContext SetThreadDpiAwarenessContextFn = (PFN_SetThreadDpiAwarenessContext)::GetProcAddress(user32_dll, "SetThreadDpiAwarenessContext")) @@ -452,7 +536,7 @@ void ImGui_ImplWin32_EnableDpiAwareness() return; } } - if (IsWindows8Point1OrGreater()) + if (_IsWindows8Point1OrGreater()) { static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process if (PFN_SetProcessDpiAwareness SetProcessDpiAwarenessFn = (PFN_SetProcessDpiAwareness)::GetProcAddress(shcore_dll, "SetProcessDpiAwareness")) @@ -473,23 +557,26 @@ void ImGui_ImplWin32_EnableDpiAwareness() float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor) { UINT xdpi = 96, ydpi = 96; - static BOOL bIsWindows8Point1OrGreater = IsWindows8Point1OrGreater(); - if (bIsWindows8Point1OrGreater) + if (_IsWindows8Point1OrGreater()) { - static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process - if (PFN_GetDpiForMonitor GetDpiForMonitorFn = (PFN_GetDpiForMonitor)::GetProcAddress(shcore_dll, "GetDpiForMonitor")) - GetDpiForMonitorFn((HMONITOR)monitor, MDT_EFFECTIVE_DPI, &xdpi, &ydpi); + static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process + static PFN_GetDpiForMonitor GetDpiForMonitorFn = NULL; + if (GetDpiForMonitorFn == NULL && shcore_dll != NULL) + GetDpiForMonitorFn = (PFN_GetDpiForMonitor)::GetProcAddress(shcore_dll, "GetDpiForMonitor"); + if (GetDpiForMonitorFn != NULL) + { + GetDpiForMonitorFn((HMONITOR)monitor, MDT_EFFECTIVE_DPI, &xdpi, &ydpi); + IM_ASSERT(xdpi == ydpi); // Please contact me if you hit this assert! + return xdpi / 96.0f; + } } #ifndef NOGDI - else - { - const HDC dc = ::GetDC(NULL); - xdpi = ::GetDeviceCaps(dc, LOGPIXELSX); - ydpi = ::GetDeviceCaps(dc, LOGPIXELSY); - ::ReleaseDC(NULL, dc); - } -#endif + const HDC dc = ::GetDC(NULL); + xdpi = ::GetDeviceCaps(dc, LOGPIXELSX); + ydpi = ::GetDeviceCaps(dc, LOGPIXELSY); IM_ASSERT(xdpi == ydpi); // Please contact me if you hit this assert! + ::ReleaseDC(NULL, dc); +#endif return xdpi / 96.0f; } @@ -512,7 +599,7 @@ float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd) // (the Dwm* functions are Vista era functions but we are borrowing logic from GLFW) void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd) { - if (!IsWindowsVistaOrGreater()) + if (!_IsWindowsVistaOrGreater()) return; BOOL composition; @@ -521,7 +608,7 @@ void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd) BOOL opaque; DWORD color; - if (IsWindows8OrGreater() || (SUCCEEDED(::DwmGetColorizationColor(&color, &opaque)) && !opaque)) + if (_IsWindows8OrGreater() || (SUCCEEDED(::DwmGetColorizationColor(&color, &opaque)) && !opaque)) { HRGN region = ::CreateRectRgn(0, 0, -1, -1); DWM_BLURBEHIND bb = {}; diff --git a/external/imgui/src/imgui_stdlib.cpp b/r5dev/thirdparty/imgui/src/imgui_stdlib.cpp similarity index 97% rename from external/imgui/src/imgui_stdlib.cpp rename to r5dev/thirdparty/imgui/src/imgui_stdlib.cpp index cb1fe174..c3834c06 100644 --- a/external/imgui/src/imgui_stdlib.cpp +++ b/r5dev/thirdparty/imgui/src/imgui_stdlib.cpp @@ -8,8 +8,8 @@ // Changelog: // - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string -#include "imgui.h" -#include "imgui_stdlib.h" +#include "thirdparty/imgui/include/imgui.h" +#include "thirdparty/imgui/include/imgui_stdlib.h" struct InputTextCallback_UserData { diff --git a/external/imgui/src/imgui_tables.cpp b/r5dev/thirdparty/imgui/src/imgui_tables.cpp similarity index 95% rename from external/imgui/src/imgui_tables.cpp rename to r5dev/thirdparty/imgui/src/imgui_tables.cpp index 3677f9e4..1161d1fe 100644 --- a/external/imgui/src/imgui_tables.cpp +++ b/r5dev/thirdparty/imgui/src/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.83 WIP +// dear imgui, v1.86 // (tables and columns code) /* @@ -188,13 +188,13 @@ Index of this file: #define _CRT_SECURE_NO_WARNINGS #endif -#include "imgui.h" +#include "thirdparty/imgui/include/imgui.h" #ifndef IMGUI_DISABLE #ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS #endif -#include "imgui_internal.h" +#include "thirdparty/imgui/include/imgui_internal.h" // System includes #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier @@ -210,6 +210,8 @@ Index of this file: #if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later #pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types #endif +#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). +#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). #endif // Clang/GCC warnings with -Weverything @@ -286,12 +288,7 @@ inline ImGuiTableFlags TableFixFlags(ImGuiTableFlags flags, ImGuiWindow* outer_w flags |= ImGuiTableFlags_NoSavedSettings; // Inherit _NoSavedSettings from top-level window (child windows always have _NoSavedSettings set) -#ifdef IMGUI_HAS_DOCK - ImGuiWindow* window_for_settings = outer_window->RootWindowDockStop; -#else - ImGuiWindow* window_for_settings = outer_window->RootWindow; -#endif - if (window_for_settings->Flags & ImGuiWindowFlags_NoSavedSettings) + if (outer_window->RootWindow->Flags & ImGuiWindowFlags_NoSavedSettings) flags |= ImGuiTableFlags_NoSavedSettings; return flags; @@ -327,7 +324,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG const ImVec2 avail_size = GetContentRegionAvail(); ImVec2 actual_outer_size = CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f); ImRect outer_rect(outer_window->DC.CursorPos, outer_window->DC.CursorPos + actual_outer_size); - if (use_child_window && IsClippedEx(outer_rect, 0, false)) + if (use_child_window && IsClippedEx(outer_rect, 0)) { ItemSize(outer_rect); return false; @@ -341,6 +338,15 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG if (instance_no > 0) IM_ASSERT(table->ColumnsCount == columns_count && "BeginTable(): Cannot change columns count mid-frame while preserving same ID"); + // Acquire temporary buffers + const int table_idx = g.Tables.GetIndex(table); + if (++g.TablesTempDataStacked > g.TablesTempData.Size) + g.TablesTempData.resize(g.TablesTempDataStacked, ImGuiTableTempData()); + ImGuiTableTempData* temp_data = table->TempData = &g.TablesTempData[g.TablesTempDataStacked - 1]; + temp_data->TableIndex = table_idx; + table->DrawSplitter = &table->TempData->DrawSplitter; + table->DrawSplitter->Clear(); + // Fix flags table->IsDefaultSizingPolicy = (flags & ImGuiTableFlags_SizingMask_) == 0; flags = TableFixFlags(flags, outer_window); @@ -354,7 +360,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->ColumnsCount = columns_count; table->IsLayoutLocked = false; table->InnerWidth = inner_width; - table->UserOuterSize = outer_size; + temp_data->UserOuterSize = outer_size; // When not using a child window, WorkRect.Max will grow as we append contents. if (use_child_window) @@ -403,14 +409,14 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->HostIndentX = inner_window->DC.Indent.x; table->HostClipRect = inner_window->ClipRect; table->HostSkipItems = inner_window->SkipItems; - table->HostBackupWorkRect = inner_window->WorkRect; - table->HostBackupParentWorkRect = inner_window->ParentWorkRect; - table->HostBackupColumnsOffset = outer_window->DC.ColumnsOffset; - table->HostBackupPrevLineSize = inner_window->DC.PrevLineSize; - table->HostBackupCurrLineSize = inner_window->DC.CurrLineSize; - table->HostBackupCursorMaxPos = inner_window->DC.CursorMaxPos; - table->HostBackupItemWidth = outer_window->DC.ItemWidth; - table->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size; + temp_data->HostBackupWorkRect = inner_window->WorkRect; + temp_data->HostBackupParentWorkRect = inner_window->ParentWorkRect; + temp_data->HostBackupColumnsOffset = outer_window->DC.ColumnsOffset; + temp_data->HostBackupPrevLineSize = inner_window->DC.PrevLineSize; + temp_data->HostBackupCurrLineSize = inner_window->DC.CurrLineSize; + temp_data->HostBackupCursorMaxPos = inner_window->DC.CursorMaxPos; + temp_data->HostBackupItemWidth = outer_window->DC.ItemWidth; + temp_data->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size; inner_window->DC.PrevLineSize = inner_window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); // Padding and Spacing @@ -453,8 +459,6 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->BorderColorLight = GetColorU32(ImGuiCol_TableBorderLight); // Make table current - const int table_idx = g.Tables.GetIndex(table); - g.CurrentTableStack.push_back(ImGuiPtrOrIndex(table_idx)); g.CurrentTable = table; outer_window->DC.CurrentTableIdx = table_idx; if (inner_window != outer_window) // So EndChild() within the inner window can restore the table properly. @@ -467,6 +471,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG if (table_idx >= g.TablesLastTimeActive.Size) g.TablesLastTimeActive.resize(table_idx + 1, -1.0f); g.TablesLastTimeActive[table_idx] = (float)g.Time; + temp_data->LastTimeActive = (float)g.Time; table->MemoryCompacted = false; // Setup memory buffer (clear data if columns count changed) @@ -511,7 +516,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG *column = ImGuiTableColumn(); column->WidthAuto = width_auto; column->IsPreserveWidthAuto = true; // Preserve WidthAuto when reinitializing a live table: not technically necessary but remove a visible flicker - column->IsEnabled = column->IsEnabledNextFrame = true; + column->IsEnabled = column->IsUserEnabled = column->IsUserEnabledNextFrame = true; } column->DisplayOrder = table->DisplayOrderToIndex[n] = (ImGuiTableColumnIdx)n; } @@ -558,6 +563,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG // + 0 (for ImGuiTable instance, we are pooling allocations in g.Tables) // + 1 (for table->RawData allocated below) // + 1 (for table->ColumnsNames, if names are used) +// Shared allocations per number of nested tables // + 1 (for table->Splitter._Channels) // + 2 * active_channels_count (for ImDrawCmd and ImDrawIdx buffers inside channels) // Where active_channels_count is variable but often == columns_count or columns_count + 1, see TableSetupDrawChannels() for details. @@ -745,16 +751,18 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) column->InitStretchWeightOrWidth = -1.0f; } - // Update Enabled state, mark settings/sortspecs dirty + // Update Enabled state, mark settings and sort specs dirty if (!(table->Flags & ImGuiTableFlags_Hideable) || (column->Flags & ImGuiTableColumnFlags_NoHide)) - column->IsEnabledNextFrame = true; - if (column->IsEnabled != column->IsEnabledNextFrame) + column->IsUserEnabledNextFrame = true; + if (column->IsUserEnabled != column->IsUserEnabledNextFrame) { - column->IsEnabled = column->IsEnabledNextFrame; + column->IsUserEnabled = column->IsUserEnabledNextFrame; table->IsSettingsDirty = true; - if (!column->IsEnabled && column->SortOrder != -1) - table->IsSortSpecsDirty = true; } + column->IsEnabled = column->IsUserEnabled && (column->Flags & ImGuiTableColumnFlags_Disabled) == 0; + + if (column->SortOrder != -1 && !column->IsEnabled) + table->IsSortSpecsDirty = true; if (column->SortOrder > 0 && !(table->Flags & ImGuiTableFlags_SortMulti)) table->IsSortSpecsDirty = true; @@ -1115,7 +1123,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // Initial state ImGuiWindow* inner_window = table->InnerWindow; if (table->Flags & ImGuiTableFlags_NoClip) - table->DrawSplitter.SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP); + table->DrawSplitter->SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP); else inner_window->DrawList->PushClipRect(inner_window->ClipRect.Min, inner_window->ClipRect.Max, false); } @@ -1153,9 +1161,8 @@ void ImGui::TableUpdateBorders(ImGuiTable* table) if ((table->Flags & ImGuiTableFlags_NoBordersInBody) && table->IsUsingHeaders == false) continue; - if (table->FreezeColumnsCount > 0) - if (column->MaxX < table->Columns[table->DisplayOrderToIndex[table->FreezeColumnsCount - 1]].MaxX) - continue; + if (!column->IsVisibleX && table->LastResizedColumn != column_n) + continue; ImGuiID column_id = TableGetColumnResizeID(table, column_n, table->InstanceCurrent); ImRect hit_rect(column->MaxX - hit_half_width, hit_y1, column->MaxX + hit_half_width, border_y2_hit); @@ -1163,7 +1170,7 @@ void ImGui::TableUpdateBorders(ImGuiTable* table) KeepAliveID(column_id); bool hovered = false, held = false; - bool pressed = ButtonBehavior(hit_rect, column_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick); + bool pressed = ButtonBehavior(hit_rect, column_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_NoNavFocus); if (pressed && IsMouseDoubleClicked(0)) { TableSetColumnWidthAutoSingle(table, column_n); @@ -1203,6 +1210,7 @@ void ImGui::EndTable() const ImGuiTableFlags flags = table->Flags; ImGuiWindow* inner_window = table->InnerWindow; ImGuiWindow* outer_window = table->OuterWindow; + ImGuiTableTempData* temp_data = table->TempData; IM_ASSERT(inner_window == g.CurrentWindow); IM_ASSERT(outer_window == inner_window || outer_window == inner_window->ParentWindow); @@ -1215,9 +1223,9 @@ void ImGui::EndTable() TableOpenContextMenu((int)table->HoveredColumnBody); // Finalize table height - inner_window->DC.PrevLineSize = table->HostBackupPrevLineSize; - inner_window->DC.CurrLineSize = table->HostBackupCurrLineSize; - inner_window->DC.CursorMaxPos = table->HostBackupCursorMaxPos; + inner_window->DC.PrevLineSize = temp_data->HostBackupPrevLineSize; + inner_window->DC.CurrLineSize = temp_data->HostBackupCurrLineSize; + inner_window->DC.CursorMaxPos = temp_data->HostBackupCursorMaxPos; const float inner_content_max_y = table->RowPosY2; IM_ASSERT(table->RowPosY2 == inner_window->DC.CursorPos.y); if (inner_window != outer_window) @@ -1264,10 +1272,11 @@ void ImGui::EndTable() #endif // Flatten channels and merge draw calls - table->DrawSplitter.SetCurrentChannel(inner_window->DrawList, 0); + ImDrawListSplitter* splitter = table->DrawSplitter; + splitter->SetCurrentChannel(inner_window->DrawList, 0); if ((table->Flags & ImGuiTableFlags_NoClip) == 0) TableMergeDrawChannels(table); - table->DrawSplitter.Merge(inner_window->DrawList); + splitter->Merge(inner_window->DrawList); // Update ColumnsAutoFitWidth to get us ahead for host using our size to auto-resize without waiting for next BeginTable() const float width_spacings = (table->OuterPaddingX * 2.0f) + (table->CellSpacingX1 + table->CellSpacingX2) * (table->ColumnsEnabledCount - 1); @@ -1309,18 +1318,18 @@ void ImGui::EndTable() // Pop from id stack IM_ASSERT_USER_ERROR(inner_window->IDStack.back() == table->ID + table->InstanceCurrent, "Mismatching PushID/PopID!"); - IM_ASSERT_USER_ERROR(outer_window->DC.ItemWidthStack.Size >= table->HostBackupItemWidthStackSize, "Too many PopItemWidth!"); + IM_ASSERT_USER_ERROR(outer_window->DC.ItemWidthStack.Size >= temp_data->HostBackupItemWidthStackSize, "Too many PopItemWidth!"); PopID(); // Restore window data that we modified const ImVec2 backup_outer_max_pos = outer_window->DC.CursorMaxPos; - inner_window->WorkRect = table->HostBackupWorkRect; - inner_window->ParentWorkRect = table->HostBackupParentWorkRect; + inner_window->WorkRect = temp_data->HostBackupWorkRect; + inner_window->ParentWorkRect = temp_data->HostBackupParentWorkRect; inner_window->SkipItems = table->HostSkipItems; outer_window->DC.CursorPos = table->OuterRect.Min; - outer_window->DC.ItemWidth = table->HostBackupItemWidth; - outer_window->DC.ItemWidthStack.Size = table->HostBackupItemWidthStackSize; - outer_window->DC.ColumnsOffset = table->HostBackupColumnsOffset; + outer_window->DC.ItemWidth = temp_data->HostBackupItemWidth; + outer_window->DC.ItemWidthStack.Size = temp_data->HostBackupItemWidthStackSize; + outer_window->DC.ColumnsOffset = temp_data->HostBackupColumnsOffset; // Layout in outer window // (FIXME: To allow auto-fit and allow desirable effect of SameLine() we dissociate 'used' vs 'ideal' size by overriding @@ -1343,20 +1352,20 @@ void ImGui::EndTable() IM_ASSERT((table->Flags & ImGuiTableFlags_ScrollX) == 0); outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth); } - else if (table->UserOuterSize.x <= 0.0f) + else if (temp_data->UserOuterSize.x <= 0.0f) { const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollX) ? inner_window->ScrollbarSizes.x : 0.0f; - outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth + decoration_size - table->UserOuterSize.x); + outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth + decoration_size - temp_data->UserOuterSize.x); outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table->OuterRect.Max.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth)); } else { outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, table->OuterRect.Max.x); } - if (table->UserOuterSize.y <= 0.0f) + if (temp_data->UserOuterSize.y <= 0.0f) { const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollY) ? inner_window->ScrollbarSizes.y : 0.0f; - outer_window->DC.IdealMaxPos.y = ImMax(outer_window->DC.IdealMaxPos.y, inner_content_max_y + decoration_size - table->UserOuterSize.y); + outer_window->DC.IdealMaxPos.y = ImMax(outer_window->DC.IdealMaxPos.y, inner_content_max_y + decoration_size - temp_data->UserOuterSize.y); outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, ImMin(table->OuterRect.Max.y, inner_content_max_y)); } else @@ -1372,8 +1381,14 @@ void ImGui::EndTable() // Clear or restore current table, if any IM_ASSERT(g.CurrentWindow == outer_window && g.CurrentTable == table); - g.CurrentTableStack.pop_back(); - g.CurrentTable = g.CurrentTableStack.Size ? g.Tables.GetByIndex(g.CurrentTableStack.back().Index) : NULL; + IM_ASSERT(g.TablesTempDataStacked > 0); + temp_data = (--g.TablesTempDataStacked > 0) ? &g.TablesTempData[g.TablesTempDataStacked - 1] : NULL; + g.CurrentTable = temp_data ? g.Tables.GetByIndex(temp_data->TableIndex) : NULL; + if (g.CurrentTable) + { + g.CurrentTable->TempData = temp_data; + g.CurrentTable->DrawSplitter = &temp_data->DrawSplitter; + } outer_window->DC.CurrentTableIdx = g.CurrentTable ? g.Tables.GetIndex(g.CurrentTable) : -1; } @@ -1429,7 +1444,7 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo // Init default visibility/sort state if ((flags & ImGuiTableColumnFlags_DefaultHide) && (table->SettingsLoadedFlags & ImGuiTableFlags_Hideable) == 0) - column->IsEnabled = column->IsEnabledNextFrame = false; + column->IsUserEnabled = column->IsUserEnabledNextFrame = false; if (flags & ImGuiTableColumnFlags_DefaultSort && (table->SettingsLoadedFlags & ImGuiTableFlags_Sortable) == 0) { column->SortOrder = 0; // Multiple columns using _DefaultSort will be reassigned unique SortOrder values when building the sort specs. @@ -1456,11 +1471,23 @@ void ImGui::TableSetupScrollFreeze(int columns, int rows) IM_ASSERT(columns >= 0 && columns < IMGUI_TABLE_MAX_COLUMNS); IM_ASSERT(rows >= 0 && rows < 128); // Arbitrary limit - table->FreezeColumnsRequest = (table->Flags & ImGuiTableFlags_ScrollX) ? (ImGuiTableColumnIdx)columns : 0; + table->FreezeColumnsRequest = (table->Flags & ImGuiTableFlags_ScrollX) ? (ImGuiTableColumnIdx)ImMin(columns, table->ColumnsCount) : 0; table->FreezeColumnsCount = (table->InnerWindow->Scroll.x != 0.0f) ? table->FreezeColumnsRequest : 0; table->FreezeRowsRequest = (table->Flags & ImGuiTableFlags_ScrollY) ? (ImGuiTableColumnIdx)rows : 0; table->FreezeRowsCount = (table->InnerWindow->Scroll.y != 0.0f) ? table->FreezeRowsRequest : 0; table->IsUnfrozenRows = (table->FreezeRowsCount == 0); // Make sure this is set before TableUpdateLayout() so ImGuiListClipper can benefit from it.b + + // Ensure frozen columns are ordered in their section. We still allow multiple frozen columns to be reordered. + // FIXME-TABLE: This work for preserving 2143 into 21|43. How about 4321 turning into 21|43? (preserve relative order in each section) + for (int column_n = 0; column_n < table->FreezeColumnsRequest; column_n++) + { + int order_n = table->DisplayOrderToIndex[column_n]; + if (order_n != column_n && order_n >= table->FreezeColumnsRequest) + { + ImSwap(table->Columns[table->DisplayOrderToIndex[order_n]].DisplayOrder, table->Columns[table->DisplayOrderToIndex[column_n]].DisplayOrder); + ImSwap(table->DisplayOrderToIndex[order_n], table->DisplayOrderToIndex[column_n]); + } + } } //----------------------------------------------------------------------------- @@ -1469,7 +1496,7 @@ void ImGui::TableSetupScrollFreeze(int columns, int rows) // - TableGetColumnCount() // - TableGetColumnName() // - TableGetColumnName() [Internal] -// - TableSetColumnEnabled() [Internal] +// - TableSetColumnEnabled() // - TableGetColumnFlags() // - TableGetCellBgRect() [Internal] // - TableGetColumnResizeID() [Internal] @@ -1505,10 +1532,12 @@ const char* ImGui::TableGetColumnName(const ImGuiTable* table, int column_n) return &table->ColumnsNames.Buf[column->NameOffset]; } -// Request enabling/disabling a column (often perceived as "showing/hiding" from users point of view) +// Change user accessible enabled/disabled state of a column (often perceived as "showing/hiding" from users point of view) // Note that end-user can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags_ContextMenuInBody) -// Request will be applied during next layout, which happens on the first call to TableNextRow() after BeginTable() -// For the getter you can use (TableGetColumnFlags() & ImGuiTableColumnFlags_IsEnabled) +// - Require table to have the ImGuiTableFlags_Hideable flag because we are manipulating user accessible state. +// - Request will be applied during next layout, which happens on the first call to TableNextRow() after BeginTable(). +// - For the getter you can test (TableGetColumnFlags() & ImGuiTableColumnFlags_IsEnabled) != 0. +// - Alternative: the ImGuiTableColumnFlags_Disabled is an overriding/master disable flag which will also hide the column from context menu. void ImGui::TableSetColumnEnabled(int column_n, bool enabled) { ImGuiContext& g = *GImGui; @@ -1516,11 +1545,12 @@ void ImGui::TableSetColumnEnabled(int column_n, bool enabled) IM_ASSERT(table != NULL); if (!table) return; + IM_ASSERT(table->Flags & ImGuiTableFlags_Hideable); // See comments above if (column_n < 0) column_n = table->CurrentColumn; IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount); ImGuiTableColumn* column = &table->Columns[column_n]; - column->IsEnabledNextFrame = enabled; + column->IsUserEnabledNextFrame = enabled; } // We allow querying for an extra column in order to poll the IsHovered state of the right-most section @@ -1746,7 +1776,7 @@ void ImGui::TableEndRow(ImGuiTable* table) // always followed by a change of clipping rectangle we perform the smallest overwrite possible here. if ((table->Flags & ImGuiTableFlags_NoClip) == 0) window->DrawList->_CmdHeader.ClipRect = table->Bg0ClipRectForDrawCmd.ToVec4(); - table->DrawSplitter.SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_BG0); + table->DrawSplitter->SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_BG0); } // Draw row background @@ -1818,7 +1848,7 @@ void ImGui::TableEndRow(ImGuiTable* table) // Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y SetWindowClipRectBeforeSetChannel(window, table->Columns[0].ClipRect); - table->DrawSplitter.SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent); + table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent); } if (!(table->RowFlags & ImGuiTableRowFlags_Headers)) @@ -1926,21 +1956,22 @@ void ImGui::TableBeginCell(ImGuiTable* table, int column_n) window->SkipItems = column->IsSkipItems; if (column->IsSkipItems) { - window->DC.LastItemId = 0; - window->DC.LastItemStatusFlags = 0; + ImGuiContext& g = *GImGui; + g.LastItemData.ID = 0; + g.LastItemData.StatusFlags = 0; } if (table->Flags & ImGuiTableFlags_NoClip) { // FIXME: if we end up drawing all borders/bg in EndTable, could remove this and just assert that channel hasn't changed. - table->DrawSplitter.SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP); + table->DrawSplitter->SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP); //IM_ASSERT(table->DrawSplitter._Current == TABLE_DRAW_CHANNEL_NOCLIP); } else { // FIXME-TABLE: Could avoid this if draw channel is dummy channel? SetWindowClipRectBeforeSetChannel(window, column->ClipRect); - table->DrawSplitter.SetCurrentChannel(window->DrawList, column->DrawChannelCurrent); + table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent); } // Logging @@ -1993,6 +2024,7 @@ float ImGui::TableGetMaxColumnWidth(const ImGuiTable* table, int column_n) if (table->Flags & ImGuiTableFlags_ScrollX) { // Frozen columns can't reach beyond visible width else scrolling will naturally break. + // (we use DisplayOrder as within a set of multiple frozen column reordering is possible) if (column->DisplayOrder < table->FreezeColumnsRequest) { max_width = (table->InnerClipRect.Max.x - (table->FreezeColumnsRequest - column->DisplayOrder) * min_column_distance) - column->MinX; @@ -2188,7 +2220,7 @@ void ImGui::TablePushBackgroundChannel() // Optimization: avoid SetCurrentChannel() + PushClipRect() table->HostBackupInnerClipRect = window->ClipRect; SetWindowClipRectBeforeSetChannel(window, table->Bg2ClipRectForDrawCmd); - table->DrawSplitter.SetCurrentChannel(window->DrawList, table->Bg2DrawChannelCurrent); + table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Bg2DrawChannelCurrent); } void ImGui::TablePopBackgroundChannel() @@ -2200,7 +2232,7 @@ void ImGui::TablePopBackgroundChannel() // Optimization: avoid PopClipRect() + SetCurrentChannel() SetWindowClipRectBeforeSetChannel(window, table->HostBackupInnerClipRect); - table->DrawSplitter.SetCurrentChannel(window->DrawList, column->DrawChannelCurrent); + table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent); } // Allocate draw channels. Called by TableUpdateLayout() @@ -2226,7 +2258,7 @@ void ImGui::TableSetupDrawChannels(ImGuiTable* table) const int channels_for_bg = 1 + 1 * freeze_row_multiplier; const int channels_for_dummy = (table->ColumnsEnabledCount < table->ColumnsCount || table->VisibleMaskByIndex != table->EnabledMaskByIndex) ? +1 : 0; const int channels_total = channels_for_bg + (channels_for_row * freeze_row_multiplier) + channels_for_dummy; - table->DrawSplitter.Split(table->InnerWindow->DrawList, channels_total); + table->DrawSplitter->Split(table->InnerWindow->DrawList, channels_total); table->DummyDrawChannel = (ImGuiTableDrawChannelIdx)((channels_for_dummy > 0) ? channels_total - 1 : -1); table->Bg2DrawChannelCurrent = TABLE_DRAW_CHANNEL_BG2_FROZEN; table->Bg2DrawChannelUnfrozen = (ImGuiTableDrawChannelIdx)((table->FreezeRowsCount > 0) ? 2 + channels_for_row : TABLE_DRAW_CHANNEL_BG2_FROZEN); @@ -2290,7 +2322,7 @@ void ImGui::TableSetupDrawChannels(ImGuiTable* table) void ImGui::TableMergeDrawChannels(ImGuiTable* table) { ImGuiContext& g = *GImGui; - ImDrawListSplitter* splitter = &table->DrawSplitter; + ImDrawListSplitter* splitter = table->DrawSplitter; const bool has_freeze_v = (table->FreezeRowsCount > 0); const bool has_freeze_h = (table->FreezeColumnsCount > 0); IM_ASSERT(splitter->_Current == 0); @@ -2301,10 +2333,11 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table) ImRect ClipRect; int ChannelsCount; ImBitArray ChannelsMask; + + MergeGroup() { ChannelsCount = 0; } }; int merge_group_mask = 0x00; MergeGroup merge_groups[4]; - memset(merge_groups, 0, sizeof(merge_groups)); // 1. Scan channels and take note of those which can be merged for (int column_n = 0; column_n < table->ColumnsCount; column_n++) @@ -2382,7 +2415,6 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table) g.DrawChannelsTempMergeBuffer.resize(splitter->_Count - LEADING_DRAW_CHANNELS); // Use shared temporary storage so the allocation gets amortized ImDrawChannel* dst_tmp = g.DrawChannelsTempMergeBuffer.Data; ImBitArray remaining_mask; // We need 132-bit of storage - remaining_mask.ClearAllBits(); remaining_mask.SetBitRange(LEADING_DRAW_CHANNELS, splitter->_Count); remaining_mask.ClearBit(table->Bg2DrawChannelUnfrozen); IM_ASSERT(has_freeze_v == false || table->Bg2DrawChannelUnfrozen != TABLE_DRAW_CHANNEL_BG2_FROZEN); @@ -2461,7 +2493,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table) return; ImDrawList* inner_drawlist = inner_window->DrawList; - table->DrawSplitter.SetCurrentChannel(inner_drawlist, TABLE_DRAW_CHANNEL_BG0); + table->DrawSplitter->SetCurrentChannel(inner_drawlist, TABLE_DRAW_CHANNEL_BG0); inner_drawlist->PushClipRect(table->Bg0ClipRectForDrawCmd.Min, table->Bg0ClipRectForDrawCmd.Max, false); // Draw inner border and resizing feedback @@ -2481,7 +2513,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table) const bool is_hovered = (table->HoveredColumnBorder == column_n); const bool is_resized = (table->ResizedColumn == column_n) && (table->InstanceInteracted == table->InstanceCurrent); const bool is_resizable = (column->Flags & (ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_NoDirectResize_)) == 0; - const bool is_frozen_separator = (table->FreezeColumnsCount != -1 && table->FreezeColumnsCount == order_n + 1); + const bool is_frozen_separator = (table->FreezeColumnsCount == order_n + 1); if (column->MaxX > table->InnerClipRect.Max.x && !is_resized) continue; @@ -2577,8 +2609,7 @@ ImGuiTableSortSpecs* ImGui::TableGetSortSpecs() if (!table->IsLayoutLocked) TableUpdateLayout(table); - if (table->IsSortSpecsDirty) - TableSortSpecsBuild(table); + TableSortSpecsBuild(table); return &table->SortSpecs; } @@ -2717,13 +2748,18 @@ void ImGui::TableSortSpecsSanitize(ImGuiTable* table) void ImGui::TableSortSpecsBuild(ImGuiTable* table) { - IM_ASSERT(table->IsSortSpecsDirty); - TableSortSpecsSanitize(table); + bool dirty = table->IsSortSpecsDirty; + if (dirty) + { + TableSortSpecsSanitize(table); + table->SortSpecsMulti.resize(table->SortSpecsCount <= 1 ? 0 : table->SortSpecsCount); + table->SortSpecs.SpecsDirty = true; // Mark as dirty for user + table->IsSortSpecsDirty = false; // Mark as not dirty for us + } // Write output - table->SortSpecsMulti.resize(table->SortSpecsCount <= 1 ? 0 : table->SortSpecsCount); ImGuiTableColumnSortSpecs* sort_specs = (table->SortSpecsCount == 0) ? NULL : (table->SortSpecsCount == 1) ? &table->SortSpecsSingle : table->SortSpecsMulti.Data; - if (sort_specs != NULL) + if (dirty && sort_specs != NULL) for (int column_n = 0; column_n < table->ColumnsCount; column_n++) { ImGuiTableColumn* column = &table->Columns[column_n]; @@ -2736,10 +2772,9 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table) sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder; sort_spec->SortDirection = column->SortDirection; } + table->SortSpecs.Specs = sort_specs; table->SortSpecs.SpecsCount = table->SortSpecsCount; - table->SortSpecs.SpecsDirty = true; // Mark as dirty for user - table->IsSortSpecsDirty = false; // Mark as not dirty for us } //------------------------------------------------------------------------- @@ -2759,8 +2794,11 @@ float ImGui::TableGetHeaderRowHeight() float row_height = GetTextLineHeight(); int columns_count = TableGetColumnCount(); for (int column_n = 0; column_n < columns_count; column_n++) - if (TableGetColumnFlags(column_n) & ImGuiTableColumnFlags_IsEnabled) + { + ImGuiTableColumnFlags flags = TableGetColumnFlags(column_n); + if ((flags & ImGuiTableColumnFlags_IsEnabled) && !(flags & ImGuiTableColumnFlags_NoHeaderLabel)) row_height = ImMax(row_height, CalcTextSize(TableGetColumnName(column_n)).y); + } row_height += GetStyle().CellPadding.y * 2.0f; return row_height; } @@ -2797,7 +2835,7 @@ void ImGui::TableHeadersRow() // Push an id to allow unnamed labels (generally accidental, but let's behave nicely with them) // - in your own code you may omit the PushID/PopID all-together, provided you know they won't collide // - table->InstanceCurrent is only >0 when we use multiple BeginTable/EndTable calls with same identifier. - const char* name = TableGetColumnName(column_n); + const char* name = (TableGetColumnFlags(column_n) & ImGuiTableColumnFlags_NoHeaderLabel) ? "" : TableGetColumnName(column_n); PushID(table->InstanceCurrent * table->ColumnsCount + column_n); TableHeader(name); PopID(); @@ -2879,7 +2917,6 @@ void ImGui::TableHeader(const char* label) const ImU32 col = GetColorU32(held ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); //RenderFrame(bb.Min, bb.Max, col, false, 0.0f); TableSetBgColor(ImGuiTableBgTarget_CellBg, col, table->CurrentColumn); - RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); } else { @@ -2887,6 +2924,7 @@ void ImGui::TableHeader(const char* label) if ((table->RowFlags & ImGuiTableRowFlags_Headers) == 0) TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_TableHeaderBg), table->CurrentColumn); } + RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); if (held) table->HeldHeaderColumn = (ImGuiTableColumnIdx)column_n; window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f; @@ -3052,16 +3090,19 @@ void ImGui::TableDrawContextMenu(ImGuiTable* table) for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++) { ImGuiTableColumn* other_column = &table->Columns[other_column_n]; + if (other_column->Flags & ImGuiTableColumnFlags_Disabled) + continue; + const char* name = TableGetColumnName(table, other_column_n); if (name == NULL || name[0] == 0) name = ""; // Make sure we can't hide the last active column bool menu_item_active = (other_column->Flags & ImGuiTableColumnFlags_NoHide) ? false : true; - if (other_column->IsEnabled && table->ColumnsEnabledCount <= 1) + if (other_column->IsUserEnabled && table->ColumnsEnabledCount <= 1) menu_item_active = false; - if (MenuItem(name, NULL, other_column->IsEnabled, menu_item_active)) - other_column->IsEnabledNextFrame = !other_column->IsEnabled; + if (MenuItem(name, NULL, other_column->IsUserEnabled, menu_item_active)) + other_column->IsUserEnabledNextFrame = !other_column->IsUserEnabled; } PopItemFlag(); } @@ -3186,7 +3227,7 @@ void ImGui::TableSaveSettings(ImGuiTable* table) column_settings->DisplayOrder = column->DisplayOrder; column_settings->SortOrder = column->SortOrder; column_settings->SortDirection = column->SortDirection; - column_settings->IsEnabled = column->IsEnabled; + column_settings->IsEnabled = column->IsUserEnabled; column_settings->IsStretch = (column->Flags & ImGuiTableColumnFlags_WidthStretch) ? 1 : 0; if ((column->Flags & ImGuiTableColumnFlags_WidthStretch) == 0) save_ref_scale = true; @@ -3200,7 +3241,7 @@ void ImGui::TableSaveSettings(ImGuiTable* table) settings->SaveFlags |= ImGuiTableFlags_Reorderable; if (column->SortOrder != -1) settings->SaveFlags |= ImGuiTableFlags_Sortable; - if (column->IsEnabled != ((column->Flags & ImGuiTableColumnFlags_DefaultHide) == 0)) + if (column->IsUserEnabled != ((column->Flags & ImGuiTableColumnFlags_DefaultHide) == 0)) settings->SaveFlags |= ImGuiTableFlags_Hideable; } settings->SaveFlags &= table->Flags; @@ -3258,7 +3299,7 @@ void ImGui::TableLoadSettings(ImGuiTable* table) else column->DisplayOrder = (ImGuiTableColumnIdx)column_n; display_order_mask |= (ImU64)1 << column->DisplayOrder; - column->IsEnabled = column->IsEnabledNextFrame = column_settings->IsEnabled; + column->IsUserEnabled = column->IsUserEnabledNextFrame = column_settings->IsEnabled; column->SortOrder = column_settings->SortOrder; column->SortDirection = column_settings->SortDirection; } @@ -3277,8 +3318,9 @@ void ImGui::TableLoadSettings(ImGuiTable* table) static void TableSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*) { ImGuiContext& g = *ctx; - for (int i = 0; i != g.Tables.GetSize(); i++) - g.Tables.GetByIndex(i)->SettingsOffset = -1; + for (int i = 0; i != g.Tables.GetMapSize(); i++) + if (ImGuiTable* table = g.Tables.TryGetMapData(i)) + table->SettingsOffset = -1; g.SettingsTables.clear(); } @@ -3286,12 +3328,12 @@ static void TableSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandle static void TableSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*) { ImGuiContext& g = *ctx; - for (int i = 0; i != g.Tables.GetSize(); i++) - { - ImGuiTable* table = g.Tables.GetByIndex(i); - table->IsSettingsRequestLoad = true; - table->SettingsOffset = -1; - } + for (int i = 0; i != g.Tables.GetMapSize(); i++) + if (ImGuiTable* table = g.Tables.TryGetMapData(i)) + { + table->IsSettingsRequestLoad = true; + table->SettingsOffset = -1; + } } static void* TableSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) @@ -3420,10 +3462,9 @@ void ImGui::TableGcCompactTransientBuffers(ImGuiTable* table) //IMGUI_DEBUG_LOG("TableGcCompactTransientBuffers() id=0x%08X\n", table->ID); ImGuiContext& g = *GImGui; IM_ASSERT(table->MemoryCompacted == false); - table->DrawSplitter.ClearFreeMemory(); - table->SortSpecsMulti.clear(); table->SortSpecs.Specs = NULL; - table->IsSortSpecsDirty = true; + table->SortSpecsMulti.clear(); + table->IsSortSpecsDirty = true; // FIXME: shouldn't have to leak into user performing a sort table->ColumnsNames.clear(); table->MemoryCompacted = true; for (int n = 0; n < table->ColumnsCount; n++) @@ -3431,6 +3472,12 @@ void ImGui::TableGcCompactTransientBuffers(ImGuiTable* table) g.TablesLastTimeActive[g.Tables.GetIndex(table)] = -1.0f; } +void ImGui::TableGcCompactTransientBuffers(ImGuiTableTempData* temp_data) +{ + temp_data->DrawSplitter.ClearFreeMemory(); + temp_data->LastTimeActive = -1.0f; +} + // Compact and remove unused settings data (currently only used by TestEngine) void ImGui::TableGcCompactSettings() { @@ -3940,7 +3987,7 @@ void ImGui::EndColumns() const float column_hit_hw = COLUMNS_HIT_RECT_HALF_WIDTH; const ImRect column_hit_rect(ImVec2(x - column_hit_hw, y1), ImVec2(x + column_hit_hw, y2)); KeepAliveID(column_id); - if (IsClippedEx(column_hit_rect, column_id, false)) + if (IsClippedEx(column_hit_rect, column_id)) // FIXME: Can be removed or replaced with a lower-level test continue; bool hovered = false, held = false; diff --git a/r5dev/thirdparty/imgui/src/imgui_utility.cpp b/r5dev/thirdparty/imgui/src/imgui_utility.cpp new file mode 100644 index 00000000..fda55ca1 --- /dev/null +++ b/r5dev/thirdparty/imgui/src/imgui_utility.cpp @@ -0,0 +1,103 @@ +/*----------------------------------------------------------------------------- + * _imgui_utility.cpp + *-----------------------------------------------------------------------------*/ + +#include "core/stdafx.h" +#include "engine/sys_utils.h" +#include "thirdparty/imgui/include/imgui_utility.h" + +int Stricmp(const char* s1, const char* s2) +{ + int d; + while ((d = toupper(*s2) - toupper(*s1)) == 0 && *s1) + { + s1++; s2++; + } + return d; +} + +int Strnicmp(const char* s1, const char* s2, int n) +{ + int d = 0; while (n > 0 && (d = toupper(*s2) - toupper(*s1)) == 0 && *s1) + { + s1++; s2++; n--; + } + return d; +} + +char* Strdup(const char* s) +{ + IM_ASSERT(s); size_t len = strlen(s) + 1; void* buf = malloc(len); IM_ASSERT(buf); if (buf != NULL) + { + return (char*)memcpy(buf, (const void*)s, len); + } + return NULL; +} + +void Strtrim(char* s) +{ + char* str_end = s + strlen(s); + + while (str_end > s && str_end[-1] == ' ') + str_end--; *str_end = 0; +} + +void ImGuiConfig::Load() +{ + std::filesystem::path fsPath = std::filesystem::current_path() /= "platform\\imgui.json"; // Get current path + imgui.json + DevMsg(eDLL_T::MS, "Loading ImGui config file '%s'\n", fsPath.string().c_str()); + + nlohmann::json jsIn; + try + { + std::ifstream configFile(fsPath, std::ios::binary); // Parse config file. + configFile >> jsIn; + configFile.close(); + + if (!jsIn.is_null()) + { + if (!jsIn["config"].is_null()) + { + // IConsole + IConsole_Config.m_nBind0 = jsIn["config"]["IConsole"]["bind0"].get(); + IConsole_Config.m_nBind1 = jsIn["config"]["IConsole"]["bind1"].get(); + IConsole_Config.m_nAutoClearLimit = jsIn["config"]["IConsole"]["autoClearLimit"].get(); + IConsole_Config.m_bAutoClear = jsIn["config"]["IConsole"]["autoClear"].get(); + + // IBrowser + IBrowser_Config.m_nBind0 = jsIn["config"]["IBrowser"]["bind0"].get(); + IBrowser_Config.m_nBind1 = jsIn["config"]["IBrowser"]["bind1"].get(); + } + } + } + catch (const std::exception& ex) + { + DevMsg(eDLL_T::MS, "ImGui config file '%s' not found. Changing the settings in the console or server browser options re-create's it. Exception: '%s'\n", fsPath.string().c_str(), ex.what()); + return; + } +} + +void ImGuiConfig::Save() +{ + nlohmann::json jsOut; + + // IConsole + jsOut["config"]["IConsole"]["bind0"] = IConsole_Config.m_nBind0; + jsOut["config"]["IConsole"]["bind1"] = IConsole_Config.m_nBind1; + jsOut["config"]["IConsole"]["autoClearLimit"] = IConsole_Config.m_nAutoClearLimit; + jsOut["config"]["IConsole"]["autoClear"] = IConsole_Config.m_bAutoClear; + + // IBrowser + jsOut["config"]["IBrowser"]["bind0"] = IBrowser_Config.m_nBind0; + jsOut["config"]["IBrowser"]["bind1"] = IBrowser_Config.m_nBind1; + + std::filesystem::path fsPath = std::filesystem::current_path() /= "platform\\imgui.json"; // Get current path + imgui.json + + DevMsg(eDLL_T::MS, "Saving ImGui config file '%s'\n", fsPath.string().c_str()); + std::ofstream outFile(fsPath, std::ios::out | std::ios::trunc); // Write config file. + + outFile << jsOut.dump(4); // Dump it into config file. + outFile.close(); // Close the file handle. +} + +ImGuiConfig* g_pImGuiConfig = new ImGuiConfig(); diff --git a/external/imgui/src/imgui_widgets.cpp b/r5dev/thirdparty/imgui/src/imgui_widgets.cpp similarity index 89% rename from external/imgui/src/imgui_widgets.cpp rename to r5dev/thirdparty/imgui/src/imgui_widgets.cpp index 2f6b05a2..8be4538b 100644 --- a/external/imgui/src/imgui_widgets.cpp +++ b/r5dev/thirdparty/imgui/src/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.83 WIP +// dear imgui, v1.86 // (widgets code) /* @@ -32,13 +32,13 @@ Index of this file: #define _CRT_SECURE_NO_WARNINGS #endif -#include "imgui.h" +#include "thirdparty/imgui/include/imgui.h" #ifndef IMGUI_DISABLE #ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS #endif -#include "imgui_internal.h" +#include "thirdparty/imgui/include/imgui_internal.h" // System includes #include // toupper @@ -59,6 +59,8 @@ Index of this file: #if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later #pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types #endif +#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). +#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). #endif // Clang/GCC warnings with -Weverything @@ -150,9 +152,13 @@ void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags) ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return; - ImGuiContext& g = *GImGui; - IM_ASSERT(text != NULL); + + // Accept null ranges + if (text == text_end) + text = text_end = ""; + + // Calculate length const char* text_begin = text; if (text_end == NULL) text_end = text + strlen(text); // FIXME-OPT @@ -199,7 +205,7 @@ void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags) ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height)); while (line < text_end) { - if (IsClippedEx(line_rect, 0, false)) + if (IsClippedEx(line_rect, 0)) break; const char* line_end = (const char*)memchr(line, '\n', text_end - line); @@ -267,6 +273,7 @@ void ImGui::TextV(const char* fmt, va_list args) if (window->SkipItems) return; + // FIXME-OPT: Handle the %s shortcut? ImGuiContext& g = *GImGui; const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); TextEx(g.TempBuffer, text_end, ImGuiTextFlags_NoWidthForLargeClippedText); @@ -486,14 +493,6 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); - if (flags & ImGuiButtonFlags_Disabled) - { - if (out_hovered) *out_hovered = false; - if (out_held) *out_held = false; - if (g.ActiveId == id) ClearActiveID(); - return false; - } - // Default only reacts to left mouse button if ((flags & ImGuiButtonFlags_MouseButtonMask_) == 0) flags |= ImGuiButtonFlags_MouseButtonDefault_; @@ -508,7 +507,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool g.HoveredWindow = window; #ifdef IMGUI_ENABLE_TEST_ENGINE - if (id != 0 && window->DC.LastItemId != id) + if (id != 0 && g.LastItemData.ID != id) IMGUI_TEST_ENGINE_ITEM_ADD(bb, id); #endif @@ -525,7 +524,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool { hovered = true; SetHoveredID(id); - if (CalcTypematicRepeatAmount(g.HoveredIdTimer + 0.0001f - g.IO.DeltaTime, g.HoveredIdTimer + 0.0001f, DRAGDROP_HOLD_TO_OPEN_TIMER, 0.00f)) + if (g.HoveredIdTimer - g.IO.DeltaTime <= DRAGDROP_HOLD_TO_OPEN_TIMER && g.HoveredIdTimer >= DRAGDROP_HOLD_TO_OPEN_TIMER) { pressed = true; g.DragDropHoldJustPressedId = id; @@ -565,13 +564,15 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool SetFocusID(id, window); FocusWindow(window); } - if ((flags & ImGuiButtonFlags_PressedOnClick) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDoubleClicked[mouse_button_clicked])) + if ((flags & ImGuiButtonFlags_PressedOnClick) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseClickedCount[mouse_button_clicked] == 2)) { pressed = true; if (flags & ImGuiButtonFlags_NoHoldingActiveId) ClearActiveID(); else SetActiveID(id, window); // Hold on ID + if (!(flags & ImGuiButtonFlags_NoNavFocus)) + SetFocusID(id, window); g.ActiveIdMouseButton = mouse_button_clicked; FocusWindow(window); } @@ -582,6 +583,8 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool const bool has_repeated_at_least_once = (flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[mouse_button_released] >= g.IO.KeyRepeatDelay; if (!has_repeated_at_least_once) pressed = true; + if (!(flags & ImGuiButtonFlags_NoNavFocus)) + SetFocusID(id, window); ClearActiveID(); } @@ -606,13 +609,12 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool bool nav_activated_by_code = (g.NavActivateId == id); bool nav_activated_by_inputs = IsNavInputTest(ImGuiNavInput_Activate, (flags & ImGuiButtonFlags_Repeat) ? ImGuiInputReadMode_Repeat : ImGuiInputReadMode_Pressed); if (nav_activated_by_code || nav_activated_by_inputs) - pressed = true; - if (nav_activated_by_code || nav_activated_by_inputs || g.ActiveId == id) { // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button. - g.NavActivateId = id; // This is so SetActiveId assign a Nav source + pressed = true; SetActiveID(id, window); - if ((nav_activated_by_code || nav_activated_by_inputs) && !(flags & ImGuiButtonFlags_NoNavFocus)) + g.ActiveIdSource = ImGuiInputSource_Nav; + if (!(flags & ImGuiButtonFlags_NoNavFocus)) SetFocusID(id, window); } } @@ -639,7 +641,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool if ((release_in || release_anywhere) && !g.DragDropActive) { // Report as pressed when releasing the mouse (this is the most common path) - bool is_double_click_release = (flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDownWasDoubleClick[mouse_button]; + bool is_double_click_release = (flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseReleased[mouse_button] && g.IO.MouseClickedLastCount[mouse_button] == 2; bool is_repeating_already = (flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[mouse_button] >= g.IO.KeyRepeatDelay; // Repeat mode trumps if (!is_double_click_release && !is_repeating_already) pressed = true; @@ -686,8 +688,9 @@ bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags if (!ItemAdd(bb, id)) return false; - if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat) + if (g.LastItemData.InFlags & ImGuiItemFlags_ButtonRepeat) flags |= ImGuiButtonFlags_Repeat; + bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); @@ -704,7 +707,7 @@ bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup)) // CloseCurrentPopup(); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); return pressed; } @@ -762,7 +765,7 @@ bool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiBu if (!ItemAdd(bb, id)) return false; - if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat) + if (g.LastItemData.InFlags & ImGuiItemFlags_ButtonRepeat) flags |= ImGuiButtonFlags_Repeat; bool hovered, held; @@ -893,7 +896,9 @@ void ImGui::Scrollbar(ImGuiAxis axis) } float size_avail = window->InnerRect.Max[axis] - window->InnerRect.Min[axis]; float size_contents = window->ContentSize[axis] + window->WindowPadding[axis] * 2.0f; - ScrollbarEx(bb, id, axis, &window->Scroll[axis], size_avail, size_contents, rounding_corners); + ImS64 scroll = (ImS64)window->Scroll[axis]; + ScrollbarEx(bb, id, axis, &scroll, (ImS64)size_avail, (ImS64)size_contents, rounding_corners); + window->Scroll[axis] = (float)scroll; } // Vertical/Horizontal scrollbar @@ -902,7 +907,7 @@ void ImGui::Scrollbar(ImGuiAxis axis) // - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar // - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal. // Still, the code should probably be made simpler.. -bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, float* p_scroll_v, float size_avail_v, float size_contents_v, ImDrawFlags flags) +bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 size_avail_v, ImS64 size_contents_v, ImDrawFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; @@ -933,8 +938,8 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, floa // Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount) // But we maintain a minimum size in pixel to allow for the user to still aim inside. IM_ASSERT(ImMax(size_contents_v, size_avail_v) > 0.0f); // Adding this assert to check if the ImMax(XXX,1.0f) is still needed. PLEASE CONTACT ME if this triggers. - const float win_size_v = ImMax(ImMax(size_contents_v, size_avail_v), 1.0f); - const float grab_h_pixels = ImClamp(scrollbar_size_v * (size_avail_v / win_size_v), style.GrabMinSize, scrollbar_size_v); + const ImS64 win_size_v = ImMax(ImMax(size_contents_v, size_avail_v), (ImS64)1); + const float grab_h_pixels = ImClamp(scrollbar_size_v * ((float)size_avail_v / (float)win_size_v), style.GrabMinSize, scrollbar_size_v); const float grab_h_norm = grab_h_pixels / scrollbar_size_v; // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar(). @@ -942,13 +947,13 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, floa bool hovered = false; ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus); - float scroll_max = ImMax(1.0f, size_contents_v - size_avail_v); - float scroll_ratio = ImSaturate(*p_scroll_v / scroll_max); + const ImS64 scroll_max = ImMax((ImS64)1, size_contents_v - size_avail_v); + float scroll_ratio = ImSaturate((float)*p_scroll_v / (float)scroll_max); float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; // Grab position in normalized space if (held && allow_interaction && grab_h_norm < 1.0f) { - float scrollbar_pos_v = bb.Min[axis]; - float mouse_pos_v = g.IO.MousePos[axis]; + const float scrollbar_pos_v = bb.Min[axis]; + const float mouse_pos_v = g.IO.MousePos[axis]; // Click position in scrollbar normalized space (0.0f->1.0f) const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v); @@ -968,10 +973,10 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, floa // Apply scroll (p_scroll_v will generally point on one member of window->Scroll) // It is ok to modify Scroll here because we are being called in Begin() after the calculation of ContentSize and before setting up our starting position const float scroll_v_norm = ImSaturate((clicked_v_norm - g.ScrollbarClickDeltaToGrabCenter - grab_h_norm * 0.5f) / (1.0f - grab_h_norm)); - *p_scroll_v = IM_ROUND(scroll_v_norm * scroll_max);//(win_size_contents_v - win_size_v)); + *p_scroll_v = (ImS64)(scroll_v_norm * scroll_max); // Update values for rendering - scroll_ratio = ImSaturate(*p_scroll_v / scroll_max); + scroll_ratio = ImSaturate((float)*p_scroll_v / (float)scroll_max); grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; // Update distance to grab now that we have seeked and saturated @@ -1081,7 +1086,7 @@ bool ImGui::Checkbox(const char* label, bool* v) ItemSize(total_bb, style.FramePadding.y); if (!ItemAdd(total_bb, id)) { - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); return false; } @@ -1097,7 +1102,7 @@ bool ImGui::Checkbox(const char* label, bool* v) RenderNavHighlight(total_bb, id); RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); ImU32 check_col = GetColorU32(ImGuiCol_CheckMark); - bool mixed_value = (window->DC.ItemFlags & ImGuiItemFlags_MixedValue) != 0; + bool mixed_value = (g.LastItemData.InFlags & ImGuiItemFlags_MixedValue) != 0; if (mixed_value) { // Undocumented tristate/mixed/indeterminate checkbox (#2644) @@ -1117,7 +1122,7 @@ bool ImGui::Checkbox(const char* label, bool* v) if (label_size.x > 0.0f) RenderText(label_pos, label); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); return pressed; } @@ -1129,11 +1134,11 @@ bool ImGui::CheckboxFlagsT(const char* label, T* flags, T flags_value) bool pressed; if (!all_on && any_on) { - ImGuiWindow* window = GetCurrentWindow(); - ImGuiItemFlags backup_item_flags = window->DC.ItemFlags; - window->DC.ItemFlags |= ImGuiItemFlags_MixedValue; + ImGuiContext& g = *GImGui; + ImGuiItemFlags backup_item_flags = g.CurrentItemFlags; + g.CurrentItemFlags |= ImGuiItemFlags_MixedValue; pressed = Checkbox(label, &all_on); - window->DC.ItemFlags = backup_item_flags; + g.CurrentItemFlags = backup_item_flags; } else { @@ -1219,7 +1224,7 @@ bool ImGui::RadioButton(const char* label, bool active) if (label_size.x > 0.0f) RenderText(label_pos, label); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); return pressed; } @@ -1388,11 +1393,20 @@ void ImGui::SeparatorEx(ImGuiSeparatorFlags flags) if (g.GroupStack.Size > 0 && g.GroupStack.back().WindowID == window->ID) x1 += window->DC.Indent.x; + // FIXME-WORKRECT: In theory we should simply be using WorkRect.Min.x/Max.x everywhere but it isn't aesthetically what we want, + // need to introduce a variant of WorkRect for that purpose. (#4787) + if (ImGuiTable* table = g.CurrentTable) + { + x1 = table->Columns[table->CurrentColumn].MinX; + x2 = table->Columns[table->CurrentColumn].MaxX; + } + ImGuiOldColumns* columns = (flags & ImGuiSeparatorFlags_SpanAllColumns) ? window->DC.CurrentColumns : NULL; if (columns) PushColumnsBackground(); // We don't provide our width to the layout so that it doesn't get feed back into AutoFit + // FIXME: This prevents ->CursorMaxPos based bounding box evaluation from working (e.g. TableEndCell) const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y + thickness_draw)); ItemSize(ImVec2(0.0f, thickness_layout)); const bool item_visible = ItemAdd(bb, 0); @@ -1421,7 +1435,7 @@ void ImGui::Separator() // Those flags should eventually be overridable by the user ImGuiSeparatorFlags flags = (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal; - flags |= ImGuiSeparatorFlags_SpanAllColumns; + flags |= ImGuiSeparatorFlags_SpanAllColumns; // NB: this only applies to legacy Columns() api as they relied on Separator() a lot. SeparatorEx(flags); } @@ -1431,10 +1445,10 @@ bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags; - window->DC.ItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus; + const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags; + g.CurrentItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus; bool item_add = ItemAdd(bb, id); - window->DC.ItemFlags = item_flags_backup; + g.CurrentItemFlags = item_flags_backup; if (!item_add) return false; @@ -1442,10 +1456,12 @@ bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float ImRect bb_interact = bb; bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f)); ButtonBehavior(bb_interact, id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap); + if (hovered) + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect; // for IsItemHovered(), because bb_interact is larger than bb if (g.ActiveId != id) SetItemAllowOverlap(); - if (held || (g.HoveredId == id && g.HoveredIdPreviousFrame == id && g.HoveredIdTimer >= hover_visibility_delay)) + if (held || (hovered && g.HoveredIdPreviousFrame == id && g.HoveredIdTimer >= hover_visibility_delay)) SetMouseCursor(axis == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW); ImRect bb_render = bb; @@ -1535,8 +1551,12 @@ void ImGui::ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_exc //------------------------------------------------------------------------- // [SECTION] Widgets: ComboBox //------------------------------------------------------------------------- +// - CalcMaxPopupHeightFromItemCount() [Internal] // - BeginCombo() +// - BeginComboPopup() [Internal] // - EndCombo() +// - BeginComboPreview() [Internal] +// - EndComboPreview() [Internal] // - Combo() //------------------------------------------------------------------------- @@ -1550,79 +1570,99 @@ static float CalcMaxPopupHeightFromItemCount(int items_count) bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags) { - // Always consume the SetNextWindowSizeConstraint() call in our early return paths ImGuiContext& g = *GImGui; - bool has_window_size_constraint = (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint) != 0; - g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint; - ImGuiWindow* window = GetCurrentWindow(); + + ImGuiNextWindowDataFlags backup_next_window_data_flags = g.NextWindowData.Flags; + g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values if (window->SkipItems) return false; - IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together - const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); + IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight(); const ImVec2 label_size = CalcTextSize(label, NULL, true); - const float expected_w = CalcItemWidth(); - const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : expected_w; - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); - const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : CalcItemWidth(); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); + const ImRect total_bb(bb.Min, bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb)) + if (!ItemAdd(total_bb, id, &bb)) return false; + // Open on click bool hovered, held; - bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held); - bool popup_open = IsPopupOpen(id, ImGuiPopupFlags_None); + bool pressed = ButtonBehavior(bb, id, &hovered, &held); + const ImGuiID popup_id = ImHashStr("##ComboPopup", 0, id); + bool popup_open = IsPopupOpen(popup_id, ImGuiPopupFlags_None); + if (pressed && !popup_open) + { + OpenPopupEx(popup_id, ImGuiPopupFlags_None); + popup_open = true; + } + // Render shape const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); - const float value_x2 = ImMax(frame_bb.Min.x, frame_bb.Max.x - arrow_size); - RenderNavHighlight(frame_bb, id); + const float value_x2 = ImMax(bb.Min.x, bb.Max.x - arrow_size); + RenderNavHighlight(bb, id); if (!(flags & ImGuiComboFlags_NoPreview)) - window->DrawList->AddRectFilled(frame_bb.Min, ImVec2(value_x2, frame_bb.Max.y), frame_col, style.FrameRounding, (flags & ImGuiComboFlags_NoArrowButton) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersLeft); + window->DrawList->AddRectFilled(bb.Min, ImVec2(value_x2, bb.Max.y), frame_col, style.FrameRounding, (flags & ImGuiComboFlags_NoArrowButton) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersLeft); if (!(flags & ImGuiComboFlags_NoArrowButton)) { ImU32 bg_col = GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button); ImU32 text_col = GetColorU32(ImGuiCol_Text); - window->DrawList->AddRectFilled(ImVec2(value_x2, frame_bb.Min.y), frame_bb.Max, bg_col, style.FrameRounding, (w <= arrow_size) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersRight); - if (value_x2 + arrow_size - style.FramePadding.x <= frame_bb.Max.x) - RenderArrow(window->DrawList, ImVec2(value_x2 + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), text_col, ImGuiDir_Down, 1.0f); + window->DrawList->AddRectFilled(ImVec2(value_x2, bb.Min.y), bb.Max, bg_col, style.FrameRounding, (w <= arrow_size) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersRight); + if (value_x2 + arrow_size - style.FramePadding.x <= bb.Max.x) + RenderArrow(window->DrawList, ImVec2(value_x2 + style.FramePadding.y, bb.Min.y + style.FramePadding.y), text_col, ImGuiDir_Down, 1.0f); } - RenderFrameBorder(frame_bb.Min, frame_bb.Max, style.FrameRounding); + RenderFrameBorder(bb.Min, bb.Max, style.FrameRounding); + + // Custom preview + if (flags & ImGuiComboFlags_CustomPreview) + { + g.ComboPreviewData.PreviewRect = ImRect(bb.Min.x, bb.Min.y, value_x2, bb.Max.y); + IM_ASSERT(preview_value == NULL || preview_value[0] == 0); + preview_value = NULL; + } + + // Render preview and label if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview)) { - ImVec2 preview_pos = frame_bb.Min + style.FramePadding; if (g.LogEnabled) LogSetNextTextDecoration("{", "}"); - RenderTextClipped(preview_pos, ImVec2(value_x2, frame_bb.Max.y), preview_value, NULL, NULL, ImVec2(0.0f, 0.0f)); + RenderTextClipped(bb.Min + style.FramePadding, ImVec2(value_x2, bb.Max.y), preview_value, NULL, NULL); } if (label_size.x > 0) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - - if ((pressed || g.NavActivateId == id) && !popup_open) - { - if (window->DC.NavLayerCurrent == 0) - window->NavLastIds[0] = id; - OpenPopupEx(id, ImGuiPopupFlags_None); - popup_open = true; - } + RenderText(ImVec2(bb.Max.x + style.ItemInnerSpacing.x, bb.Min.y + style.FramePadding.y), label); if (!popup_open) return false; - if (has_window_size_constraint) + g.NextWindowData.Flags = backup_next_window_data_flags; + return BeginComboPopup(popup_id, bb, flags); +} + +bool ImGui::BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags flags) +{ + ImGuiContext& g = *GImGui; + if (!IsPopupOpen(popup_id, ImGuiPopupFlags_None)) + { + g.NextWindowData.ClearFlags(); + return false; + } + + // Set popup size + float w = bb.GetWidth(); + if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint) { - g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint; g.NextWindowData.SizeConstraintRect.Min.x = ImMax(g.NextWindowData.SizeConstraintRect.Min.x, w); } else { if ((flags & ImGuiComboFlags_HeightMask_) == 0) flags |= ImGuiComboFlags_HeightRegular; - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_)); // Only one + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_)); // Only one int popup_max_height_in_items = -1; if (flags & ImGuiComboFlags_HeightRegular) popup_max_height_in_items = 8; else if (flags & ImGuiComboFlags_HeightSmall) popup_max_height_in_items = 4; @@ -1630,30 +1670,27 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF SetNextWindowSizeConstraints(ImVec2(w, 0.0f), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items))); } + // This is essentially a specialized version of BeginPopupEx() char name[16]; ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth - // Position the window given a custom constraint (peak into expected window size so we can position it) - // This might be easier to express with an hypothetical SetNextWindowPosConstraints() function. + // Set position given a custom constraint (peak into expected window size so we can position it) + // FIXME: This might be easier to express with an hypothetical SetNextWindowPosConstraints() function? + // FIXME: This might be moved to Begin() or at least around the same spot where Tooltips and other Popups are calling FindBestWindowPosForPopupEx()? if (ImGuiWindow* popup_window = FindWindowByName(name)) if (popup_window->WasActive) { // Always override 'AutoPosLastDirection' to not leave a chance for a past value to affect us. ImVec2 size_expected = CalcWindowNextAutoFitSize(popup_window); - if (flags & ImGuiComboFlags_PopupAlignLeft) - popup_window->AutoPosLastDirection = ImGuiDir_Left; // "Below, Toward Left" - else - popup_window->AutoPosLastDirection = ImGuiDir_Down; // "Below, Toward Right (default)" - ImRect r_outer = GetWindowAllowedExtentRect(popup_window); - ImVec2 pos = FindBestWindowPosForPopupEx(frame_bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, frame_bb, ImGuiPopupPositionPolicy_ComboBox); + popup_window->AutoPosLastDirection = (flags & ImGuiComboFlags_PopupAlignLeft) ? ImGuiDir_Left : ImGuiDir_Down; // Left = "Below, Toward Left", Down = "Below, Toward Right (default)" + ImRect r_outer = GetPopupAllowedExtentRect(popup_window); + ImVec2 pos = FindBestWindowPosForPopupEx(bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, bb, ImGuiPopupPositionPolicy_ComboBox); SetNextWindowPos(pos); } // We don't use BeginPopupEx() solely because we have a custom name string, which we could make an argument to BeginPopupEx() ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoMove; - - // Horizontally align ourselves with the framed text - PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(style.FramePadding.x, style.WindowPadding.y)); + PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(g.Style.FramePadding.x, g.Style.WindowPadding.y)); // Horizontally align ourselves with the framed text bool ret = Begin(name, NULL, window_flags); PopStyleVar(); if (!ret) @@ -1670,6 +1707,57 @@ void ImGui::EndCombo() EndPopup(); } +// Call directly after the BeginCombo/EndCombo block. The preview is designed to only host non-interactive elements +// (Experimental, see GitHub issues: #1658, #4168) +bool ImGui::BeginComboPreview() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiComboPreviewData* preview_data = &g.ComboPreviewData; + + if (window->SkipItems || !window->ClipRect.Overlaps(g.LastItemData.Rect)) // FIXME: Because we don't have a ImGuiItemStatusFlags_Visible flag to test last ItemAdd() result + return false; + IM_ASSERT(g.LastItemData.Rect.Min.x == preview_data->PreviewRect.Min.x && g.LastItemData.Rect.Min.y == preview_data->PreviewRect.Min.y); // Didn't call after BeginCombo/EndCombo block or forgot to pass ImGuiComboFlags_CustomPreview flag? + if (!window->ClipRect.Contains(preview_data->PreviewRect)) // Narrower test (optional) + return false; + + // FIXME: This could be contained in a PushWorkRect() api + preview_data->BackupCursorPos = window->DC.CursorPos; + preview_data->BackupCursorMaxPos = window->DC.CursorMaxPos; + preview_data->BackupCursorPosPrevLine = window->DC.CursorPosPrevLine; + preview_data->BackupPrevLineTextBaseOffset = window->DC.PrevLineTextBaseOffset; + preview_data->BackupLayout = window->DC.LayoutType; + window->DC.CursorPos = preview_data->PreviewRect.Min + g.Style.FramePadding; + window->DC.CursorMaxPos = window->DC.CursorPos; + window->DC.LayoutType = ImGuiLayoutType_Horizontal; + PushClipRect(preview_data->PreviewRect.Min, preview_data->PreviewRect.Max, true); + + return true; +} + +void ImGui::EndComboPreview() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiComboPreviewData* preview_data = &g.ComboPreviewData; + + // FIXME: Using CursorMaxPos approximation instead of correct AABB which we will store in ImDrawCmd in the future + ImDrawList* draw_list = window->DrawList; + if (window->DC.CursorMaxPos.x < preview_data->PreviewRect.Max.x && window->DC.CursorMaxPos.y < preview_data->PreviewRect.Max.y) + if (draw_list->CmdBuffer.Size > 1) // Unlikely case that the PushClipRect() didn't create a command + { + draw_list->_CmdHeader.ClipRect = draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 1].ClipRect = draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 2].ClipRect; + draw_list->_TryMergeDrawCmds(); + } + PopClipRect(); + window->DC.CursorPos = preview_data->BackupCursorPos; + window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, preview_data->BackupCursorMaxPos); + window->DC.CursorPosPrevLine = preview_data->BackupCursorPosPrevLine; + window->DC.PrevLineTextBaseOffset = preview_data->BackupPrevLineTextBaseOffset; + window->DC.LayoutType = preview_data->BackupLayout; + preview_data->PreviewRect = ImRect(); +} + // Getter for the old Combo() API: const char*[] static bool Items_ArrayGetter(void* data, int idx, const char** out_text) { @@ -1722,7 +1810,7 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi bool value_changed = false; for (int i = 0; i < items_count; i++) { - PushID((void*)(intptr_t)i); + PushID(i); const bool item_selected = (i == *current_item); const char* item_text; if (!items_getter(data, i, &item_text)) @@ -1738,8 +1826,9 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi } EndCombo(); + if (value_changed) - MarkItemEdited(g.CurrentWindow->DC.LastItemId); + MarkItemEdited(g.LastItemData.ID); return value_changed; } @@ -1980,13 +2069,15 @@ bool ImGui::DataTypeApplyOpFromText(const char* buf, const char* initial_value_b { // All other types assign constant // We don't bother handling support for legacy operators since they are a little too crappy. Instead we will later implement a proper expression evaluator in the future. - sscanf(buf, format, p_data); + if (sscanf(buf, format, p_data) < 1) + return false; } else { // Small types need a 32-bit buffer to receive the result from scanf() int v32; - sscanf(buf, format, &v32); + if (sscanf(buf, format, &v32) < 1) + return false; if (data_type == ImGuiDataType_S8) *(ImS8*)p_data = (ImS8)ImClamp(v32, (int)IM_S8_MIN, (int)IM_S8_MAX); else if (data_type == ImGuiDataType_U8) @@ -2273,7 +2364,7 @@ bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v } if (g.ActiveId != id) return false; - if ((g.CurrentWindow->DC.ItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly)) + if ((g.LastItemData.InFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly)) return false; switch (data_type) @@ -2311,8 +2402,9 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0; ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb)) + if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0)) return false; // Default format string when passing NULL @@ -2323,33 +2415,30 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, // Tabbing or CTRL-clicking on Drag turns it into an InputText const bool hovered = ItemHoverable(frame_bb, id); - const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0; bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); if (!temp_input_is_active) { - const bool focus_requested = temp_input_allowed && FocusableItemRegister(window, id); + const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; const bool clicked = (hovered && g.IO.MouseClicked[0]); - const bool double_clicked = (hovered && g.IO.MouseDoubleClicked[0]); - if (focus_requested || clicked || double_clicked || g.NavActivateId == id || g.NavInputId == id) + const bool double_clicked = (hovered && g.IO.MouseClickedCount[0] == 2); + if (input_requested_by_tabbing || clicked || double_clicked || g.NavActivateId == id || g.NavActivateInputId == id) { SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); - if (temp_input_allowed && (focus_requested || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavInputId == id)) - { - temp_input_is_active = true; - FocusableItemUnregister(window); - } + if (temp_input_allowed) + if (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavActivateInputId == id) + temp_input_is_active = true; } + // Experimental: simple click (without moving) turns Drag into an InputText - // FIXME: Currently polling ImGuiConfigFlags_IsTouchScreen, may either poll an hypothetical ImGuiBackendFlags_HasKeyboard and/or an explicit drag settings. if (g.IO.ConfigDragClickToInputText && temp_input_allowed && !temp_input_is_active) if (g.ActiveId == id && hovered && g.IO.MouseReleased[0] && !IsMouseDragPastThreshold(0, g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR)) { - g.NavInputId = id; + g.NavActivateId = g.NavActivateInputId = id; + g.NavActivateFlags = ImGuiActivateFlags_PreferInput; temp_input_is_active = true; - FocusableItemUnregister(window); } } @@ -2361,7 +2450,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, } // Draw frame - const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); + const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); RenderNavHighlight(frame_bb, id); RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding); @@ -2380,7 +2469,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, if (label_size.x > 0.0f) RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); return value_changed; } @@ -2468,6 +2557,7 @@ bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_cu TextEx(label, FindRenderedTextEnd(label)); EndGroup(); PopID(); + return value_changed; } @@ -2880,7 +2970,7 @@ bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) && "Invalid ImGuiSliderFlags flag! Has the 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead."); ImGuiContext& g = *GImGui; - if ((g.CurrentWindow->DC.ItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly)) + if ((g.LastItemData.InFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly)) return false; switch (data_type) @@ -2930,8 +3020,9 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0; ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb)) + if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0)) return false; // Default format string when passing NULL @@ -2942,23 +3033,19 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat // Tabbing or CTRL-clicking on Slider turns it into an input box const bool hovered = ItemHoverable(frame_bb, id); - const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0; bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); if (!temp_input_is_active) { - const bool focus_requested = temp_input_allowed && FocusableItemRegister(window, id); + const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; const bool clicked = (hovered && g.IO.MouseClicked[0]); - if (focus_requested || clicked || g.NavActivateId == id || g.NavInputId == id) + if (input_requested_by_tabbing || clicked || g.NavActivateId == id || g.NavActivateInputId == id) { SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); - if (temp_input_allowed && (focus_requested || (clicked && g.IO.KeyCtrl) || g.NavInputId == id)) - { + if (temp_input_allowed && (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || g.NavActivateInputId == id)) temp_input_is_active = true; - FocusableItemUnregister(window); - } } } @@ -2970,7 +3057,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat } // Draw frame - const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); + const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); RenderNavHighlight(frame_bb, id); RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding); @@ -2994,7 +3081,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat if (label_size.x > 0.0f) RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); return value_changed; } @@ -3109,7 +3196,7 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d format = PatchFormatStringFloatToInt(format); const bool hovered = ItemHoverable(frame_bb, id); - if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavInputId == id) + if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavActivateInputId == id) { SetActiveID(id, window); SetFocusID(id, window); @@ -3118,7 +3205,7 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d } // Draw frame - const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); + const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); RenderNavHighlight(frame_bb, id); RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding); @@ -3285,7 +3372,7 @@ bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* ClearActiveID(); g.CurrentWindow->DC.CursorPos = bb.Min; - bool value_changed = InputTextEx(label, NULL, buf, buf_size, bb.GetSize(), flags); + bool value_changed = InputTextEx(label, NULL, buf, buf_size, bb.GetSize(), flags | ImGuiInputTextFlags_MergedItem); if (init) { // First frame we started displaying the InputText widget, we expect it to take the active id. @@ -3373,7 +3460,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data style.FramePadding.x = style.FramePadding.y; ImGuiButtonFlags button_flags = ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups; if (flags & ImGuiInputTextFlags_ReadOnly) - button_flags |= ImGuiButtonFlags_Disabled; + BeginDisabled(); SameLine(0, style.ItemInnerSpacing.x); if (ButtonEx("-", ImVec2(button_size, button_size), button_flags)) { @@ -3386,6 +3473,8 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data DataTypeApplyOp(data_type, '+', p_data, p_data, g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step); value_changed = true; } + if (flags & ImGuiInputTextFlags_ReadOnly) + EndDisabled(); const char* label_end = FindRenderedTextEnd(label); if (label != label_end) @@ -3404,7 +3493,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialTextA.Data, data_type, p_data, format); } if (value_changed) - MarkItemEdited(window->DC.LastItemId); + MarkItemEdited(g.LastItemData.ID); return value_changed; } @@ -3582,12 +3671,12 @@ static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* t namespace ImStb { -static int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj) { return obj->CurLenW; } -static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx) { return obj->TextW[idx]; } -static float STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx + char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *GImGui; return g.Font->GetCharAdvance(c) * (g.FontSize / g.Font->FontSize); } +static int STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj) { return obj->CurLenW; } +static ImWchar STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { return obj->TextW[idx]; } +static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx + char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *GImGui; return g.Font->GetCharAdvance(c) * (g.FontSize / g.Font->FontSize); } static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x200000 ? 0 : key; } static ImWchar STB_TEXTEDIT_NEWLINE = '\n'; -static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx) +static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx) { const ImWchar* text = obj->TextW.Data; const ImWchar* text_remaining = NULL; @@ -3600,19 +3689,21 @@ static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* ob r->num_chars = (int)(text_remaining - (text + line_start_idx)); } -static bool is_separator(unsigned int c) { return ImCharIsBlankW(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; } -static int is_word_boundary_from_right(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (is_separator(obj->TextW[idx - 1]) && !is_separator(obj->TextW[idx]) ) : 1; } -static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; } -#ifdef __APPLE__ // FIXME: Move setting to IO structure -static int is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (!is_separator(obj->TextW[idx - 1]) && is_separator(obj->TextW[idx]) ) : 1; } -static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; } -#else -static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } -#endif +// When ImGuiInputTextFlags_Password is set, we don't want actions such as CTRL+Arrow to leak the fact that underlying data are blanks or separators. +static bool is_separator(unsigned int c) { return ImCharIsBlankW(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|' || c=='\n' || c=='\r'; } +static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (is_separator(obj->TextW[idx - 1]) && !is_separator(obj->TextW[idx]) ) : 1; } +static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (!is_separator(obj->TextW[idx - 1]) && is_separator(obj->TextW[idx])) : 1; } +static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; } +static int STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; } #define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h -#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL +#ifdef __APPLE__ // FIXME: Move setting to IO structure +#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_MAC +#else +static int STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } +#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_WIN +#endif -static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n) +static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n) { ImWchar* dst = obj->TextW.Data + pos; @@ -3628,9 +3719,9 @@ static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n) *dst = '\0'; } -static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len) +static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const ImWchar* new_text, int new_text_len) { - const bool is_resizable = (obj->UserFlags & ImGuiInputTextFlags_CallbackResize) != 0; + const bool is_resizable = (obj->Flags & ImGuiInputTextFlags_CallbackResize) != 0; const int text_len = obj->CurLenW; IM_ASSERT(pos <= text_len); @@ -3680,11 +3771,11 @@ static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const Im #define STB_TEXTEDIT_K_SHIFT 0x400000 #define STB_TEXTEDIT_IMPLEMENTATION -#include "imstb_textedit.h" +#include "thirdparty/imgui/include/imstb_textedit.h" // stb_textedit internally allows for a single undo record to do addition and deletion, but somehow, calling // the stb_textedit_paste() function creates two separate records, so we perform it manually. (FIXME: Report to nothings/stb?) -static void stb_textedit_replace(STB_TEXTEDIT_STRING* str, STB_TexteditState* state, const STB_TEXTEDIT_CHARTYPE* text, int text_len) +static void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* state, const STB_TEXTEDIT_CHARTYPE* text, int text_len) { stb_text_makeundo_replace(str, state, 0, str->CurLenW, text_len); ImStb::STB_TEXTEDIT_DELETECHARS(str, 0, str->CurLenW); @@ -3773,6 +3864,7 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f unsigned int c = *p_char; // Filter non-printable (NB: isprint is unreliable! see #2467) + bool apply_named_filters = true; if (c < 0x20) { bool pass = false; @@ -3780,6 +3872,7 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput)); if (!pass) return false; + apply_named_filters = false; // Override named filters below so newline and tabs can still be inserted. } if (input_source != ImGuiInputSource_Clipboard) @@ -3798,13 +3891,14 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f return false; // Generic named filters - if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific)) + if (apply_named_filters && (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific))) { - // The libc allows overriding locale, with e.g. 'setlocale(LC_NUMERIC, "de_DE.UTF-8");' which affect the output/input of printf/scanf. + // The libc allows overriding locale, with e.g. 'setlocale(LC_NUMERIC, "de_DE.UTF-8");' which affect the output/input of printf/scanf to use e.g. ',' instead of '.'. // The standard mandate that programs starts in the "C" locale where the decimal point is '.'. // We don't really intend to provide widespread support for it, but out of empathy for people stuck with using odd API, we support the bare minimum aka overriding the decimal point. // Change the default decimal_point with: // ImGui::GetCurrentContext()->PlatformLocaleDecimalPoint = *localeconv()->decimal_point; + // Users of non-default decimal point (in particular ',') may be affected by word-selection logic (is_word_boundary_from_right/is_word_boundary_from_left) functions. ImGuiContext& g = *GImGui; const unsigned c_decimal_point = (unsigned int)g.PlatformLocaleDecimalPoint; @@ -3883,7 +3977,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (is_resizable) IM_ASSERT(callback != NULL); // Must provide a callback if you set the ImGuiInputTextFlags_CallbackResize flag! - if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope, + if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope (including the scrollbar) BeginGroup(); const ImGuiID id = window->GetID(label); const ImVec2 label_size = CalcTextSize(label, NULL, true); @@ -3895,21 +3989,29 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ ImGuiWindow* draw_window = window; ImVec2 inner_size = frame_size; + ImGuiItemStatusFlags item_status_flags = 0; + ImGuiLastItemData item_data_backup; if (is_multiline) { - if (!ItemAdd(total_bb, id, &frame_bb)) + ImVec2 backup_pos = window->DC.CursorPos; + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable)) { - ItemSize(total_bb, style.FramePadding.y); EndGroup(); return false; } + item_status_flags = g.LastItemData.StatusFlags; + item_data_backup = g.LastItemData; + window->DC.CursorPos = backup_pos; // We reproduce the contents of BeginChildFrame() in order to provide 'label' so our window internal data are easier to read/debug. + // FIXME-NAV: Pressing NavActivate will trigger general child activation right before triggering our own below. Harmless but bizarre. PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]); PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding); PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize); + PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Ensure no clip rect so mouse hover can reach FramePadding edges bool child_visible = BeginChildEx(label, id, frame_bb.GetSize(), true, ImGuiWindowFlags_NoMove); - PopStyleVar(2); + PopStyleVar(3); PopStyleColor(); if (!child_visible) { @@ -3918,15 +4020,18 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ return false; } draw_window = g.CurrentWindow; // Child window - draw_window->DC.NavLayerActiveMaskNext |= (1 << draw_window->DC.NavLayerCurrent); // This is to ensure that EndChild() will display a navigation highlight so we can "enter" into it. + draw_window->DC.NavLayersActiveMaskNext |= (1 << draw_window->DC.NavLayerCurrent); // This is to ensure that EndChild() will display a navigation highlight so we can "enter" into it. draw_window->DC.CursorPos += style.FramePadding; inner_size.x -= draw_window->ScrollbarSizes.x; } else { + // Support for internal ImGuiInputTextFlags_MergedItem flag, which could be redesigned as an ItemFlags if needed (with test performed in ItemAdd) ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb)) - return false; + if (!(flags & ImGuiInputTextFlags_MergedItem)) + if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable)) + return false; + item_status_flags = g.LastItemData.StatusFlags; } const bool hovered = ItemHoverable(frame_bb, id); if (hovered) @@ -3935,22 +4040,19 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // We are only allowed to access the state if we are already the active widget. ImGuiInputTextState* state = GetInputTextState(id); - const bool focus_requested = FocusableItemRegister(window, id); - const bool focus_requested_by_code = focus_requested && (g.TabFocusRequestCurrWindow == window && g.TabFocusRequestCurrCounterRegular == window->DC.FocusCounterRegular); - const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code; + const bool input_requested_by_tabbing = (item_status_flags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; + const bool input_requested_by_nav = (g.ActiveId != id) && ((g.NavActivateInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_Keyboard)); const bool user_clicked = hovered && io.MouseClicked[0]; - const bool user_nav_input_start = (g.ActiveId != id) && ((g.NavInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_Keyboard)); const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); - bool clear_active_id = false; - bool select_all = (g.ActiveId != id) && ((flags & ImGuiInputTextFlags_AutoSelectAll) != 0 || user_nav_input_start) && (!is_multiline); + bool select_all = false; float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX; const bool init_changed_specs = (state != NULL && state->Stb.single_line != !is_multiline); - const bool init_make_active = (focus_requested || user_clicked || user_scroll_finish || user_nav_input_start); + const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav || input_requested_by_tabbing); const bool init_state = (init_make_active || user_scroll_active); if ((init_state && g.ActiveId != id) || init_changed_specs) { @@ -3986,13 +4088,20 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ state->ID = id; state->ScrollX = 0.0f; stb_textedit_initialize_state(&state->Stb, !is_multiline); - if (!is_multiline && focus_requested_by_code) + } + + if (!is_multiline) + { + if (flags & ImGuiInputTextFlags_AutoSelectAll) + select_all = true; + if (input_requested_by_nav && (!recycle_state || !(g.NavActivateFlags & ImGuiActivateFlags_TryToPreserveState))) + select_all = true; + if (input_requested_by_tabbing || (user_clicked && io.KeyCtrl)) select_all = true; } + if (flags & ImGuiInputTextFlags_AlwaysOverwrite) state->Stb.insert_mode = 1; // stb field name is indeed incorrect (see #2863) - if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl))) - select_all = true; } if (g.ActiveId != id && init_make_active) @@ -4025,7 +4134,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Lock the decision of whether we are going to take the path displaying the cursor or selection const bool render_cursor = (g.ActiveId == id) || (state && user_scroll_active); - bool render_selection = state && state->HasSelection() && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor); + bool render_selection = state && (state->HasSelection() || select_all) && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor); bool value_changed = false; bool enter_pressed = false; @@ -4069,9 +4178,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ backup_current_text_length = state->CurLenA; state->Edited = false; state->BufCapacityA = buf_size; - state->UserFlags = flags; - state->UserCallback = callback; - state->UserCallbackData = callback_user_data; + state->Flags = flags; // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. // Down the line we should have a cleaner library-wide concept of Selected vs Active. @@ -4083,19 +4190,49 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y) : (g.FontSize * 0.5f)); const bool is_osx = io.ConfigMacOSXBehaviors; - if (select_all || (hovered && !is_osx && io.MouseDoubleClicked[0])) + if (select_all) { state->SelectAll(); state->SelectedAllMouseLock = true; } - else if (hovered && is_osx && io.MouseDoubleClicked[0]) + else if (hovered && io.MouseClickedCount[0] >= 2 && !io.KeyShift) { - // Double-click select a word only, OS X style (by simulating keystrokes) - state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); - state->OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); + stb_textedit_click(state, &state->Stb, mouse_x, mouse_y); + const int multiclick_count = (io.MouseClickedCount[0] - 2); + if ((multiclick_count % 2) == 0) + { + // Double-click: Select word + // We always use the "Mac" word advance for double-click select vs CTRL+Right which use the platform dependent variant: + // FIXME: There are likely many ways to improve this behavior, but there's no "right" behavior (depends on use-case, software, OS) + const bool is_bol = (state->Stb.cursor == 0) || ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb.cursor - 1) == '\n'; + if (STB_TEXT_HAS_SELECTION(&state->Stb) || !is_bol) + state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); + //state->OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); + if (!STB_TEXT_HAS_SELECTION(&state->Stb)) + ImStb::stb_textedit_prep_selection_at_cursor(&state->Stb); + state->Stb.cursor = ImStb::STB_TEXTEDIT_MOVEWORDRIGHT_MAC(state, state->Stb.cursor); + state->Stb.select_end = state->Stb.cursor; + ImStb::stb_textedit_clamp(state, &state->Stb); + } + else + { + // Triple-click: Select line + const bool is_eol = ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb.cursor) == '\n'; + state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART); + state->OnKeyPressed(STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT); + state->OnKeyPressed(STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT); + if (!is_eol && is_multiline) + { + ImSwap(state->Stb.select_start, state->Stb.select_end); + state->Stb.cursor = state->Stb.select_end; + } + state->CursorFollow = false; + } + state->CursorAnimReset(); } else if (io.MouseClicked[0] && !state->SelectedAllMouseLock) { + // FIXME: unselect on late click could be done release? if (hovered) { stb_textedit_click(state, &state->Stb, mouse_x, mouse_y); @@ -4126,7 +4263,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // We ignore CTRL inputs, but need to allow ALT+CTRL as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters. if (io.InputQueueCharacters.Size > 0) { - if (!ignore_char_inputs && !is_readonly && !user_nav_input_start) + if (!ignore_char_inputs && !is_readonly && !input_requested_by_nav) for (int n = 0; n < io.InputQueueCharacters.Size; n++) { // Insert character if they pass filtering @@ -4167,6 +4304,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const bool is_undo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Z)) && !is_readonly && is_undoable); const bool is_redo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressedMap(ImGuiKey_Z))) && !is_readonly && is_undoable; + // We allow validate/cancel with Nav source (gamepad) to makes it easier to undo an accidental NavInput press with no keyboard wired, but otherwise it isn't very useful. + const bool is_validate_enter = IsKeyPressedMap(ImGuiKey_Enter) || IsKeyPressedMap(ImGuiKey_KeyPadEnter); + const bool is_validate_nav = (IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed) && !IsKeyPressedMap(ImGuiKey_Space)) || IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed); + const bool is_cancel = IsKeyPressedMap(ImGuiKey_Escape) || IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed); + if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); } else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); } else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); } @@ -4175,7 +4317,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ else if (IsKeyPressedMap(ImGuiKey_PageDown) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGDOWN | k_mask); scroll_y += row_count_per_page * g.FontSize; } else if (IsKeyPressedMap(ImGuiKey_Home)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); } else if (IsKeyPressedMap(ImGuiKey_End)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Delete) && !is_readonly) { state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_Delete) && !is_readonly && !is_cut) { state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); } else if (IsKeyPressedMap(ImGuiKey_Backspace) && !is_readonly) { if (!state->HasSelection()) @@ -4187,7 +4329,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } state->OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Enter) || IsKeyPressedMap(ImGuiKey_KeyPadEnter)) + else if (is_validate_enter) { bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0; if (!is_multiline || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl)) @@ -4201,7 +4343,12 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ state->OnKeyPressed((int)c); } } - else if (IsKeyPressedMap(ImGuiKey_Escape)) + else if (is_validate_nav) + { + IM_ASSERT(!is_validate_enter); + enter_pressed = clear_active_id = true; + } + else if (is_cancel) { clear_active_id = cancel_edit = true; } @@ -4269,11 +4416,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } // Process callbacks and apply result back to user's buffer. + const char* apply_new_text = NULL; + int apply_new_text_length = 0; if (g.ActiveId == id) { IM_ASSERT(state != NULL); - const char* apply_new_text = NULL; - int apply_new_text_length = 0; if (cancel_edit) { // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents. @@ -4349,8 +4496,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ callback_data.Flags = flags; callback_data.UserData = callback_user_data; + char* callback_buf = is_readonly ? buf : state->TextA.Data; callback_data.EventKey = event_key; - callback_data.Buf = state->TextA.Data; + callback_data.Buf = callback_buf; callback_data.BufTextLen = state->CurLenA; callback_data.BufSize = state->BufCapacityA; callback_data.BufDirty = false; @@ -4365,7 +4513,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ callback(&callback_data); // Read back what user may have modified - IM_ASSERT(callback_data.Buf == state->TextA.Data); // Invalid to modify those fields + callback_buf = is_readonly ? buf : state->TextA.Data; // Pointer may have been invalidated by a resize callback + IM_ASSERT(callback_data.Buf == callback_buf); // Invalid to modify those fields IM_ASSERT(callback_data.BufSize == state->BufCapacityA); IM_ASSERT(callback_data.Flags == flags); const bool buf_dirty = callback_data.BufDirty; @@ -4392,39 +4541,37 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } } - // Copy result to user buffer - if (apply_new_text) - { - // We cannot test for 'backup_current_text_length != apply_new_text_length' here because we have no guarantee that the size - // of our owned buffer matches the size of the string object held by the user, and by design we allow InputText() to be used - // without any storage on user's side. - IM_ASSERT(apply_new_text_length >= 0); - if (is_resizable) - { - ImGuiInputTextCallbackData callback_data; - callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize; - callback_data.Flags = flags; - callback_data.Buf = buf; - callback_data.BufTextLen = apply_new_text_length; - callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1); - callback_data.UserData = callback_user_data; - callback(&callback_data); - buf = callback_data.Buf; - buf_size = callback_data.BufSize; - apply_new_text_length = ImMin(callback_data.BufTextLen, buf_size - 1); - IM_ASSERT(apply_new_text_length <= buf_size); - } - //IMGUI_DEBUG_LOG("InputText(\"%s\"): apply_new_text length %d\n", label, apply_new_text_length); - - // If the underlying buffer resize was denied or not carried to the next frame, apply_new_text_length+1 may be >= buf_size. - ImStrncpy(buf, apply_new_text, ImMin(apply_new_text_length + 1, buf_size)); - value_changed = true; - } - // Clear temporary user storage - state->UserFlags = 0; - state->UserCallback = NULL; - state->UserCallbackData = NULL; + state->Flags = ImGuiInputTextFlags_None; + } + + // Copy result to user buffer. This can currently only happen when (g.ActiveId == id) + if (apply_new_text != NULL) + { + // We cannot test for 'backup_current_text_length != apply_new_text_length' here because we have no guarantee that the size + // of our owned buffer matches the size of the string object held by the user, and by design we allow InputText() to be used + // without any storage on user's side. + IM_ASSERT(apply_new_text_length >= 0); + if (is_resizable) + { + ImGuiInputTextCallbackData callback_data; + callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize; + callback_data.Flags = flags; + callback_data.Buf = buf; + callback_data.BufTextLen = apply_new_text_length; + callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1); + callback_data.UserData = callback_user_data; + callback(&callback_data); + buf = callback_data.Buf; + buf_size = callback_data.BufSize; + apply_new_text_length = ImMin(callback_data.BufTextLen, buf_size - 1); + IM_ASSERT(apply_new_text_length <= buf_size); + } + //IMGUI_DEBUG_LOG("InputText(\"%s\"): apply_new_text length %d\n", label, apply_new_text_length); + + // If the underlying buffer resize was denied or not carried to the next frame, apply_new_text_length+1 may be >= buf_size. + ImStrncpy(buf, apply_new_text, ImMin(apply_new_text_length + 1, buf_size)); + value_changed = true; } // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value) @@ -4546,7 +4693,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Test if cursor is vertically visible if (cursor_offset.y - g.FontSize < scroll_y) scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize); - else if (cursor_offset.y - inner_size.y >= scroll_y) + else if (cursor_offset.y - (inner_size.y - style.FramePadding.y * 2.0f) >= scroll_y) scroll_y = cursor_offset.y - inner_size.y + style.FramePadding.y * 2.0f; const float scroll_max_y = ImMax((text_size.y + style.FramePadding.y * 2.0f) - inner_size.y, 0.0f); scroll_y = ImClamp(scroll_y, 0.0f, scroll_max_y); @@ -4606,7 +4753,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { state->CursorAnim += io.DeltaTime; bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (state->CursorAnim <= 0.0f) || ImFmod(state->CursorAnim, 1.20f) <= 0.80f; - ImVec2 cursor_screen_pos = draw_pos + cursor_offset - draw_scroll; + ImVec2 cursor_screen_pos = ImFloor(draw_pos + cursor_offset - draw_scroll); ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f); if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect)) draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text)); @@ -4638,9 +4785,23 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (is_multiline) { + // For focus requests to work on our multiline we need to ensure our child ItemAdd() call specifies the ImGuiItemFlags_Inputable (ref issue #4761)... Dummy(ImVec2(text_size.x, text_size.y + style.FramePadding.y)); + ImGuiItemFlags backup_item_flags = g.CurrentItemFlags; + g.CurrentItemFlags |= ImGuiItemFlags_Inputable | ImGuiItemFlags_NoTabStop; EndChild(); + item_data_backup.StatusFlags |= (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredWindow); + g.CurrentItemFlags = backup_item_flags; + + // ...and then we need to undo the group overriding last item data, which gets a bit messy as EndGroup() tries to forward scrollbar being active... + // FIXME: This quite messy/tricky, should attempt to get rid of the child window. EndGroup(); + if (g.LastItemData.ID == 0) + { + g.LastItemData.ID = id; + g.LastItemData.InFlags = item_data_backup.InFlags; + g.LastItemData.StatusFlags = item_data_backup.StatusFlags; + } } // Log as text @@ -4656,7 +4817,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (value_changed && !(flags & ImGuiInputTextFlags_NoMarkEdited)) MarkItemEdited(id); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0) return enter_pressed; else @@ -4683,6 +4844,30 @@ bool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flag return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha); } +// ColorEdit supports RGB and HSV inputs. In case of RGB input resulting color may have undefined hue and/or saturation. +// Since widget displays both RGB and HSV values we must preserve hue and saturation to prevent these values resetting. +static void ColorEditRestoreHS(const float* col, float* H, float* S, float* V) +{ + // This check is optional. Suppose we have two color widgets side by side, both widgets display different colors, but both colors have hue and/or saturation undefined. + // With color check: hue/saturation is preserved in one widget. Editing color in one widget would reset hue/saturation in another one. + // Without color check: common hue/saturation would be displayed in all widgets that have hue/saturation undefined. + // g.ColorEditLastColor is stored as ImU32 RGB value: this essentially gives us color equality check with reduced precision. + // Tiny external color changes would not be detected and this check would still pass. This is OK, since we only restore hue/saturation _only_ if they are undefined, + // therefore this change flipping hue/saturation from undefined to a very tiny value would still be represented in color picker. + ImGuiContext& g = *GImGui; + if (g.ColorEditLastColor != ImGui::ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0))) + return; + + // When S == 0, H is undefined. + // When H == 1 it wraps around to 0. + if (*S == 0.0f || (*H == 0.0f && g.ColorEditLastHue == 1)) + *H = g.ColorEditLastHue; + + // When V == 0, S is undefined. + if (*V == 0.0f) + *S = g.ColorEditLastSat; +} + // Edit colors components (each component in 0.0f..1.0f range). // See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. // With typical options: Left-click on color square to open color picker. Right-click to open option menu. CTRL-Click over input fields to edit them and TAB to go to next item. @@ -4707,24 +4892,24 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag // If we're not showing any slider there's no point in doing any HSV conversions const ImGuiColorEditFlags flags_untouched = flags; if (flags & ImGuiColorEditFlags_NoInputs) - flags = (flags & (~ImGuiColorEditFlags__DisplayMask)) | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_NoOptions; + flags = (flags & (~ImGuiColorEditFlags_DisplayMask_)) | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_NoOptions; // Context menu: display and modify options (before defaults are applied) if (!(flags & ImGuiColorEditFlags_NoOptions)) ColorEditOptionsPopup(col, flags); // Read stored options - if (!(flags & ImGuiColorEditFlags__DisplayMask)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags__DisplayMask); - if (!(flags & ImGuiColorEditFlags__DataTypeMask)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags__DataTypeMask); - if (!(flags & ImGuiColorEditFlags__PickerMask)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags__PickerMask); - if (!(flags & ImGuiColorEditFlags__InputMask)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags__InputMask); - flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags__DisplayMask | ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags__InputMask)); - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__DisplayMask)); // Check that only 1 is selected - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__InputMask)); // Check that only 1 is selected + if (!(flags & ImGuiColorEditFlags_DisplayMask_)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags_DisplayMask_); + if (!(flags & ImGuiColorEditFlags_DataTypeMask_)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags_DataTypeMask_); + if (!(flags & ImGuiColorEditFlags_PickerMask_)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags_PickerMask_); + if (!(flags & ImGuiColorEditFlags_InputMask_)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags_InputMask_); + flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_)); + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DisplayMask_)); // Check that only 1 is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_)); // Check that only 1 is selected const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0; const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0; @@ -4738,13 +4923,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag { // Hue is lost when converting from greyscale rgb (saturation=0). Restore it. ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); - if (memcmp(g.ColorEditLastColor, col, sizeof(float) * 3) == 0) - { - if (f[1] == 0) - f[0] = g.ColorEditLastHue; - if (f[2] == 0) - f[1] = g.ColorEditLastSat; - } + ColorEditRestoreHS(col, &f[0], &f[1], &f[2]); } int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) }; @@ -4814,10 +4993,12 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag p++; i[0] = i[1] = i[2] = 0; i[3] = 0xFF; // alpha default to 255 is not parsed by scanf (e.g. inputting #FFFFFF omitting alpha) + int r; if (alpha) - sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned) + r = sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned) else - sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]); + r = sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]); + IM_UNUSED(r); // Fixes C6031: Return value ignored: 'sscanf'. } if (!(flags & ImGuiColorEditFlags_NoOptions)) OpenPopupOnItemClick("context"); @@ -4837,7 +5018,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag // Store current color and open a picker g.ColorPickerRef = col_v4; OpenPopup("picker"); - SetNextWindowPos(window->DC.LastItemRect.GetBL() + ImVec2(-1, style.ItemSpacing.y)); + SetNextWindowPos(g.LastItemData.Rect.GetBL() + ImVec2(-1, style.ItemSpacing.y)); } } if (!(flags & ImGuiColorEditFlags_NoOptions)) @@ -4851,8 +5032,8 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag TextEx(label, label_display_end); Spacing(); } - ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar; - ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags__DisplayMask | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf; + ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar; + ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf; SetNextItemWidth(square_sz * 12.0f); // Use 256 + bar sizes? value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x); EndPopup(); @@ -4877,7 +5058,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag g.ColorEditLastHue = f[0]; g.ColorEditLastSat = f[1]; ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]); - memcpy(g.ColorEditLastColor, f, sizeof(float) * 3); + g.ColorEditLastColor = ColorConvertFloat4ToU32(ImVec4(f[0], f[1], f[2], 0)); } if ((flags & ImGuiColorEditFlags_DisplayRGB) && (flags & ImGuiColorEditFlags_InputHSV)) ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); @@ -4894,7 +5075,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag // Drag and Drop Target // NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test. - if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropTarget()) + if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropTarget()) { bool accepted_drag_drop = false; if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) @@ -4916,10 +5097,10 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag // When picker is being actively used, use its active id so IsItemActive() will function on ColorEdit4(). if (picker_active_window && g.ActiveId != 0 && g.ActiveIdWindow == picker_active_window) - window->DC.LastItemId = g.ActiveId; + g.LastItemData.ID = g.ActiveId; if (value_changed) - MarkItemEdited(window->DC.LastItemId); + MarkItemEdited(g.LastItemData.ID); return value_changed; } @@ -4972,12 +5153,12 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl ColorPickerOptionsPopup(col, flags); // Read stored options - if (!(flags & ImGuiColorEditFlags__PickerMask)) - flags |= ((g.ColorEditOptions & ImGuiColorEditFlags__PickerMask) ? g.ColorEditOptions : ImGuiColorEditFlags__OptionsDefault) & ImGuiColorEditFlags__PickerMask; - if (!(flags & ImGuiColorEditFlags__InputMask)) - flags |= ((g.ColorEditOptions & ImGuiColorEditFlags__InputMask) ? g.ColorEditOptions : ImGuiColorEditFlags__OptionsDefault) & ImGuiColorEditFlags__InputMask; - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__PickerMask)); // Check that only 1 is selected - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__InputMask)); // Check that only 1 is selected + if (!(flags & ImGuiColorEditFlags_PickerMask_)) + flags |= ((g.ColorEditOptions & ImGuiColorEditFlags_PickerMask_) ? g.ColorEditOptions : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_PickerMask_; + if (!(flags & ImGuiColorEditFlags_InputMask_)) + flags |= ((g.ColorEditOptions & ImGuiColorEditFlags_InputMask_) ? g.ColorEditOptions : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_InputMask_; + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_PickerMask_)); // Check that only 1 is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_)); // Check that only 1 is selected if (!(flags & ImGuiColorEditFlags_NoOptions)) flags |= (g.ColorEditOptions & ImGuiColorEditFlags_AlphaBar); @@ -5012,13 +5193,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl { // Hue is lost when converting from greyscale rgb (saturation=0). Restore it. ColorConvertRGBtoHSV(R, G, B, H, S, V); - if (memcmp(g.ColorEditLastColor, col, sizeof(float) * 3) == 0) - { - if (S == 0) - H = g.ColorEditLastHue; - if (V == 0) - S = g.ColorEditLastSat; - } + ColorEditRestoreHS(col, &H, &S, &V); } else if (flags & ImGuiColorEditFlags_InputHSV) { @@ -5071,6 +5246,10 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl { S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size - 1)); V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1)); + + // Greatly reduces hue jitter and reset to 0 when hue == 255 and color is rapidly modified using SV square. + if (g.ColorEditLastColor == ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0))) + H = g.ColorEditLastHue; value_changed = value_changed_sv = true; } if (!(flags & ImGuiColorEditFlags_NoOptions)) @@ -5123,7 +5302,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl if ((flags & ImGuiColorEditFlags_NoLabel)) Text("Current"); - ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf | ImGuiColorEditFlags_NoTooltip; + ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf | ImGuiColorEditFlags_NoTooltip; ColorButton("##current", col_v4, (flags & sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2)); if (ref_col != NULL) { @@ -5144,10 +5323,10 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl { if (flags & ImGuiColorEditFlags_InputRGB) { - ColorConvertHSVtoRGB(H >= 1.0f ? H - 10 * 1e-6f : H, S > 0.0f ? S : 10 * 1e-6f, V > 0.0f ? V : 1e-6f, col[0], col[1], col[2]); + ColorConvertHSVtoRGB(H, S, V, col[0], col[1], col[2]); g.ColorEditLastHue = H; g.ColorEditLastSat = S; - memcpy(g.ColorEditLastColor, col, sizeof(float) * 3); + g.ColorEditLastColor = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0)); } else if (flags & ImGuiColorEditFlags_InputHSV) { @@ -5162,9 +5341,9 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl if ((flags & ImGuiColorEditFlags_NoInputs) == 0) { PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x); - ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf; + ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf; ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker; - if (flags & ImGuiColorEditFlags_DisplayRGB || (flags & ImGuiColorEditFlags__DisplayMask) == 0) + if (flags & ImGuiColorEditFlags_DisplayRGB || (flags & ImGuiColorEditFlags_DisplayMask_) == 0) if (ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_DisplayRGB)) { // FIXME: Hackily differentiating using the DragInt (ActiveId != 0 && !ActiveIdAllowOverlap) vs. using the InputText or DropTarget. @@ -5172,9 +5351,9 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl value_changed_fix_hue_wrap = (g.ActiveId != 0 && !g.ActiveIdAllowOverlap); value_changed = true; } - if (flags & ImGuiColorEditFlags_DisplayHSV || (flags & ImGuiColorEditFlags__DisplayMask) == 0) + if (flags & ImGuiColorEditFlags_DisplayHSV || (flags & ImGuiColorEditFlags_DisplayMask_) == 0) value_changed |= ColorEdit4("##hsv", col, sub_flags | ImGuiColorEditFlags_DisplayHSV); - if (flags & ImGuiColorEditFlags_DisplayHex || (flags & ImGuiColorEditFlags__DisplayMask) == 0) + if (flags & ImGuiColorEditFlags_DisplayHex || (flags & ImGuiColorEditFlags_DisplayMask_) == 0) value_changed |= ColorEdit4("##hex", col, sub_flags | ImGuiColorEditFlags_DisplayHex); PopItemWidth(); } @@ -5201,13 +5380,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl G = col[1]; B = col[2]; ColorConvertRGBtoHSV(R, G, B, H, S, V); - if (memcmp(g.ColorEditLastColor, col, sizeof(float) * 3) == 0) // Fix local Hue as display below will use it immediately. - { - if (S == 0) - H = g.ColorEditLastHue; - if (V == 0) - S = g.ColorEditLastSat; - } + ColorEditRestoreHS(col, &H, &S, &V); // Fix local Hue as display below will use it immediately. } else if (flags & ImGuiColorEditFlags_InputHSV) { @@ -5315,7 +5488,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl if (value_changed && memcmp(backup_initial_col, col, components * sizeof(float)) == 0) value_changed = false; if (value_changed) - MarkItemEdited(window->DC.LastItemId); + MarkItemEdited(g.LastItemData.ID); PopID(); @@ -5404,7 +5577,7 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl // Tooltip if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered) - ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)); + ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)); return pressed; } @@ -5413,18 +5586,18 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl void ImGui::SetColorEditOptions(ImGuiColorEditFlags flags) { ImGuiContext& g = *GImGui; - if ((flags & ImGuiColorEditFlags__DisplayMask) == 0) - flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__DisplayMask; - if ((flags & ImGuiColorEditFlags__DataTypeMask) == 0) - flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__DataTypeMask; - if ((flags & ImGuiColorEditFlags__PickerMask) == 0) - flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__PickerMask; - if ((flags & ImGuiColorEditFlags__InputMask) == 0) - flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__InputMask; - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__DisplayMask)); // Check only 1 option is selected - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__DataTypeMask)); // Check only 1 option is selected - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__PickerMask)); // Check only 1 option is selected - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__InputMask)); // Check only 1 option is selected + if ((flags & ImGuiColorEditFlags_DisplayMask_) == 0) + flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DisplayMask_; + if ((flags & ImGuiColorEditFlags_DataTypeMask_) == 0) + flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DataTypeMask_; + if ((flags & ImGuiColorEditFlags_PickerMask_) == 0) + flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_PickerMask_; + if ((flags & ImGuiColorEditFlags_InputMask_) == 0) + flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_InputMask_; + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DisplayMask_)); // Check only 1 option is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DataTypeMask_)); // Check only 1 option is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_PickerMask_)); // Check only 1 option is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_)); // Check only 1 option is selected g.ColorEditOptions = flags; } @@ -5433,7 +5606,7 @@ void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags { ImGuiContext& g = *GImGui; - BeginTooltipEx(0, ImGuiTooltipFlags_OverridePreviousTooltip); + BeginTooltipEx(ImGuiTooltipFlags_OverridePreviousTooltip, ImGuiWindowFlags_None); const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text; if (text_end > text) { @@ -5444,9 +5617,9 @@ void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags ImVec2 sz(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2); ImVec4 cf(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); - ColorButton("##preview", cf, (flags & (ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz); + ColorButton("##preview", cf, (flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz); SameLine(); - if ((flags & ImGuiColorEditFlags_InputRGB) || !(flags & ImGuiColorEditFlags__InputMask)) + if ((flags & ImGuiColorEditFlags_InputRGB) || !(flags & ImGuiColorEditFlags_InputMask_)) { if (flags & ImGuiColorEditFlags_NoAlpha) Text("#%02X%02X%02X\nR: %d, G: %d, B: %d\n(%.3f, %.3f, %.3f)", cr, cg, cb, cr, cg, cb, col[0], col[1], col[2]); @@ -5465,23 +5638,23 @@ void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags) { - bool allow_opt_inputs = !(flags & ImGuiColorEditFlags__DisplayMask); - bool allow_opt_datatype = !(flags & ImGuiColorEditFlags__DataTypeMask); + bool allow_opt_inputs = !(flags & ImGuiColorEditFlags_DisplayMask_); + bool allow_opt_datatype = !(flags & ImGuiColorEditFlags_DataTypeMask_); if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup("context")) return; ImGuiContext& g = *GImGui; ImGuiColorEditFlags opts = g.ColorEditOptions; if (allow_opt_inputs) { - if (RadioButton("RGB", (opts & ImGuiColorEditFlags_DisplayRGB) != 0)) opts = (opts & ~ImGuiColorEditFlags__DisplayMask) | ImGuiColorEditFlags_DisplayRGB; - if (RadioButton("HSV", (opts & ImGuiColorEditFlags_DisplayHSV) != 0)) opts = (opts & ~ImGuiColorEditFlags__DisplayMask) | ImGuiColorEditFlags_DisplayHSV; - if (RadioButton("Hex", (opts & ImGuiColorEditFlags_DisplayHex) != 0)) opts = (opts & ~ImGuiColorEditFlags__DisplayMask) | ImGuiColorEditFlags_DisplayHex; + if (RadioButton("RGB", (opts & ImGuiColorEditFlags_DisplayRGB) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayRGB; + if (RadioButton("HSV", (opts & ImGuiColorEditFlags_DisplayHSV) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayHSV; + if (RadioButton("Hex", (opts & ImGuiColorEditFlags_DisplayHex) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayHex; } if (allow_opt_datatype) { if (allow_opt_inputs) Separator(); - if (RadioButton("0..255", (opts & ImGuiColorEditFlags_Uint8) != 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Uint8; - if (RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) != 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Float; + if (RadioButton("0..255", (opts & ImGuiColorEditFlags_Uint8) != 0)) opts = (opts & ~ImGuiColorEditFlags_DataTypeMask_) | ImGuiColorEditFlags_Uint8; + if (RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) != 0)) opts = (opts & ~ImGuiColorEditFlags_DataTypeMask_) | ImGuiColorEditFlags_Float; } if (allow_opt_inputs || allow_opt_datatype) @@ -5516,7 +5689,7 @@ void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags) void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags) { - bool allow_opt_picker = !(flags & ImGuiColorEditFlags__PickerMask); + bool allow_opt_picker = !(flags & ImGuiColorEditFlags_PickerMask_); bool allow_opt_alpha_bar = !(flags & ImGuiColorEditFlags_NoAlpha) && !(flags & ImGuiColorEditFlags_AlphaBar); if ((!allow_opt_picker && !allow_opt_alpha_bar) || !BeginPopup("context")) return; @@ -5535,7 +5708,7 @@ void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags fl if (picker_type == 1) picker_flags |= ImGuiColorEditFlags_PickerHueWheel; ImVec2 backup_pos = GetCursorScreenPos(); if (Selectable("##selectable", false, 0, picker_size)) // By default, Selectable() is closing popup - g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags__PickerMask) | (picker_flags & ImGuiColorEditFlags__PickerMask); + g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags_PickerMask_) | (picker_flags & ImGuiColorEditFlags_PickerMask_); SetCursorScreenPos(backup_pos); ImVec4 previewing_ref_col; memcpy(&previewing_ref_col, ref_col, sizeof(float) * ((picker_flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4)); @@ -5748,14 +5921,14 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l window->DC.TreeJumpToParentOnPopMask |= (1 << window->DC.TreeDepth); bool item_add = ItemAdd(interact_bb, id); - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; - window->DC.LastItemDisplayRect = frame_bb; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; + g.LastItemData.DisplayRect = frame_bb; if (!item_add) { if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) TreePushOverrideID(id); - IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.ItemFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); + IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); return is_open; } @@ -5804,7 +5977,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l toggled = true; if (flags & ImGuiTreeNodeFlags_OpenOnArrow) toggled |= is_mouse_x_over_arrow && !g.NavDisableMouseHover; // Lightweight equivalent of IsMouseHoveringRect() since ButtonBehavior() already did the job - if ((flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) && g.IO.MouseDoubleClicked[0]) + if ((flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) && g.IO.MouseClickedCount[0] == 2) toggled = true; } else if (pressed && g.DragDropHoldJustPressedId == id) @@ -5814,12 +5987,12 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l toggled = true; } - if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Left && is_open) + if (g.NavId == id && g.NavMoveDir == ImGuiDir_Left && is_open) { toggled = true; NavMoveRequestCancel(); } - if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority? + if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority? { toggled = true; NavMoveRequestCancel(); @@ -5829,7 +6002,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l { is_open = !is_open; window->DC.StateStorage->SetInt(id, is_open); - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_ToggledOpen; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledOpen; } } if (flags & ImGuiTreeNodeFlags_AllowItemOverlap) @@ -5837,7 +6010,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l // In this branch, TreeNodeBehavior() cannot toggle the selection so this will never trigger. if (selected != was_selected) //-V547 - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_ToggledSelection; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection; // Render const ImU32 text_col = GetColorU32(ImGuiCol_Text); @@ -5868,8 +6041,8 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l { const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false); - RenderNavHighlight(frame_bb, id, nav_highlight_flags); } + RenderNavHighlight(frame_bb, id, nav_highlight_flags); if (flags & ImGuiTreeNodeFlags_Bullet) RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.5f, text_pos.y + g.FontSize * 0.5f), text_col); else if (!is_leaf) @@ -5881,7 +6054,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) TreePushOverrideID(id); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); return is_open; } @@ -5890,7 +6063,7 @@ void ImGui::TreePush(const char* str_id) ImGuiWindow* window = GetCurrentWindow(); Indent(); window->DC.TreeDepth++; - PushID(str_id ? str_id : "#TreePush"); + PushID(str_id); } void ImGui::TreePush(const void* ptr_id) @@ -5898,7 +6071,7 @@ void ImGui::TreePush(const void* ptr_id) ImGuiWindow* window = GetCurrentWindow(); Indent(); window->DC.TreeDepth++; - PushID(ptr_id ? ptr_id : (const void*)"#TreePush"); + PushID(ptr_id); } void ImGui::TreePushOverrideID(ImGuiID id) @@ -5907,7 +6080,7 @@ void ImGui::TreePushOverrideID(ImGuiID id) ImGuiWindow* window = g.CurrentWindow; Indent(); window->DC.TreeDepth++; - window->IDStack.push_back(id); + PushOverrideID(id); } void ImGui::TreePop() @@ -5985,14 +6158,14 @@ bool ImGui::CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFl // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc. // FIXME: CloseButton can overlap into text, need find a way to clip the text somehow. ImGuiContext& g = *GImGui; - ImGuiLastItemDataBackup last_item_backup; + ImGuiLastItemData last_item_backup = g.LastItemData; float button_size = g.FontSize; - float button_x = ImMax(window->DC.LastItemRect.Min.x, window->DC.LastItemRect.Max.x - g.Style.FramePadding.x * 2.0f - button_size); - float button_y = window->DC.LastItemRect.Min.y; + float button_x = ImMax(g.LastItemData.Rect.Min.x, g.LastItemData.Rect.Max.x - g.Style.FramePadding.x * 2.0f - button_size); + float button_y = g.LastItemData.Rect.Min.y; ImGuiID close_button_id = GetIDWithSeed("#CLOSE", NULL, id); if (CloseButton(close_button_id, ImVec2(button_x, button_y))) *p_visible = false; - last_item_backup.Restore(); + g.LastItemData = last_item_backup; } return is_open; @@ -6061,19 +6234,8 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl window->ClipRect.Max.x = window->ParentWorkRect.Max.x; } - bool item_add; - if (flags & ImGuiSelectableFlags_Disabled) - { - ImGuiItemFlags backup_item_flags = window->DC.ItemFlags; - window->DC.ItemFlags |= ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNavDefaultFocus; - item_add = ItemAdd(bb, id); - window->DC.ItemFlags = backup_item_flags; - } - else - { - item_add = ItemAdd(bb, id); - } - + const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0; + const bool item_add = ItemAdd(bb, id, NULL, disabled_item ? ImGuiItemFlags_Disabled : ImGuiItemFlags_None); if (span_all_columns) { window->ClipRect.Min.x = backup_clip_rect_min_x; @@ -6083,6 +6245,10 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (!item_add) return false; + const bool disabled_global = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; + if (disabled_item && !disabled_global) // Only testing this as an optimization + BeginDisabled(); + // FIXME: We can standardize the behavior of those two, we could also keep the fast path of override ClipRect + full push on render only, // which would be advantageous since most selectable are not selected. if (span_all_columns && window->DC.CurrentColumns) @@ -6095,23 +6261,30 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (flags & ImGuiSelectableFlags_NoHoldingActiveID) { button_flags |= ImGuiButtonFlags_NoHoldingActiveId; } if (flags & ImGuiSelectableFlags_SelectOnClick) { button_flags |= ImGuiButtonFlags_PressedOnClick; } if (flags & ImGuiSelectableFlags_SelectOnRelease) { button_flags |= ImGuiButtonFlags_PressedOnRelease; } - if (flags & ImGuiSelectableFlags_Disabled) { button_flags |= ImGuiButtonFlags_Disabled; } if (flags & ImGuiSelectableFlags_AllowDoubleClick) { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; } if (flags & ImGuiSelectableFlags_AllowItemOverlap) { button_flags |= ImGuiButtonFlags_AllowItemOverlap; } - if (flags & ImGuiSelectableFlags_Disabled) - selected = false; - const bool was_selected = selected; bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); + // Auto-select when moved into + // - This will be more fully fleshed in the range-select branch + // - This is not exposed as it won't nicely work with some user side handling of shift/control + // - We cannot do 'if (g.NavJustMovedToId != id) { selected = false; pressed = was_selected; }' for two reasons + // - (1) it would require focus scope to be set, need exposing PushFocusScope() or equivalent (e.g. BeginSelection() calling PushFocusScope()) + // - (2) usage will fail with clipped items + // The multi-select API aim to fix those issues, e.g. may be replaced with a BeginSelection() API. + if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == window->DC.NavFocusScopeIdCurrent) + if (g.NavJustMovedToId == id) + selected = pressed = true; + // Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with gamepad/keyboard if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover))) { if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent) { - SetNavID(id, window->DC.NavLayerCurrent, window->DC.NavFocusScopeIdCurrent, ImRect(bb.Min - window->Pos, bb.Max - window->Pos)); + SetNavID(id, window->DC.NavLayerCurrent, window->DC.NavFocusScopeIdCurrent, WindowRectAbsToRel(window, bb)); // (bb == NavRect) g.NavDisableHighlight = true; } } @@ -6123,7 +6296,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl // In this branch, Selectable() cannot toggle the selection so this will never trigger. if (selected != was_selected) //-V547 - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_ToggledSelection; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection; // Render if (held && (flags & ImGuiSelectableFlags_DrawHoveredWhenHeld)) @@ -6132,24 +6305,25 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl { const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); RenderFrame(bb.Min, bb.Max, col, false, 0.0f); - RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); } + RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); if (span_all_columns && window->DC.CurrentColumns) PopColumnsBackground(); else if (span_all_columns && g.CurrentTable) TablePopBackgroundChannel(); - if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb); - if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor(); // Automatically close popups - if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(window->DC.ItemFlags & ImGuiItemFlags_SelectableDontClosePopup)) + if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(g.LastItemData.InFlags & ImGuiItemFlags_SelectableDontClosePopup)) CloseCurrentPopup(); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); - return pressed; + if (disabled_item && !disabled_global) + EndDisabled(); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); + return pressed; //-V1020 } bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) @@ -6281,8 +6455,9 @@ bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(v PopID(); } EndListBox(); + if (value_changed) - MarkItemEdited(g.CurrentWindow->DC.LastItemId); + MarkItemEdited(g.LastItemData.ID); return value_changed; } @@ -6297,7 +6472,7 @@ bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(v // Plot/Graph widgets are not very good. // Consider writing your own, or using a third-party one, see: // - ImPlot https://github.com/epezent/implot -// - others https://github.com/ocornut/imgui/wiki/Useful-Widgets +// - others https://github.com/ocornut/imgui/wiki/Useful-Extensions //------------------------------------------------------------------------- int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 frame_size) @@ -6374,7 +6549,7 @@ int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_get float v0 = values_getter(data, (0 + values_offset) % values_count); float t0 = 0.0f; ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) * inv_scale) ); // Point in the normalized space of our target rectangle - float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (-scale_min * inv_scale) : (scale_min < 0.0f ? 0.0f : 1.0f); // Where does the zero line stands + float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (1 + scale_min * inv_scale) : (scale_min < 0.0f ? 0.0f : 1.0f); // Where does the zero line stands const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram); const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered); @@ -6501,42 +6676,51 @@ void ImGui::Value(const char* prefix, float v, const char* float_format) // - EndMainMenuBar() // - BeginMenu() // - EndMenu() +// - MenuItemEx() [Internal] // - MenuItem() //------------------------------------------------------------------------- // Helpers for internal use -void ImGuiMenuColumns::Update(int count, float spacing, bool clear) +void ImGuiMenuColumns::Update(float spacing, bool window_reappearing) { - IM_ASSERT(count == IM_ARRAYSIZE(Pos)); - IM_UNUSED(count); - Width = NextWidth = 0.0f; - Spacing = spacing; - if (clear) - memset(NextWidths, 0, sizeof(NextWidths)); - for (int i = 0; i < IM_ARRAYSIZE(Pos); i++) + if (window_reappearing) + memset(Widths, 0, sizeof(Widths)); + Spacing = (ImU16)spacing; + CalcNextTotalWidth(true); + memset(Widths, 0, sizeof(Widths)); + TotalWidth = NextTotalWidth; + NextTotalWidth = 0; +} + +void ImGuiMenuColumns::CalcNextTotalWidth(bool update_offsets) +{ + ImU16 offset = 0; + bool want_spacing = false; + for (int i = 0; i < IM_ARRAYSIZE(Widths); i++) { - if (i > 0 && NextWidths[i] > 0.0f) - Width += Spacing; - Pos[i] = IM_FLOOR(Width); - Width += NextWidths[i]; - NextWidths[i] = 0.0f; + ImU16 width = Widths[i]; + if (want_spacing && width > 0) + offset += Spacing; + want_spacing |= (width > 0); + if (update_offsets) + { + if (i == 1) { OffsetLabel = offset; } + if (i == 2) { OffsetShortcut = offset; } + if (i == 3) { OffsetMark = offset; } + } + offset += width; } + NextTotalWidth = offset; } -float ImGuiMenuColumns::DeclColumns(float w0, float w1, float w2) // not using va_arg because they promote float to double +float ImGuiMenuColumns::DeclColumns(float w_icon, float w_label, float w_shortcut, float w_mark) { - NextWidth = 0.0f; - NextWidths[0] = ImMax(NextWidths[0], w0); - NextWidths[1] = ImMax(NextWidths[1], w1); - NextWidths[2] = ImMax(NextWidths[2], w2); - for (int i = 0; i < IM_ARRAYSIZE(Pos); i++) - NextWidth += NextWidths[i] + ((i > 0 && NextWidths[i] > 0.0f) ? Spacing : 0.0f); - return ImMax(Width, NextWidth); -} - -float ImGuiMenuColumns::CalcExtraSpace(float avail_w) const -{ - return ImMax(0.0f, avail_w - Width); + Widths[0] = ImMax(Widths[0], (ImU16)w_icon); + Widths[1] = ImMax(Widths[1], (ImU16)w_label); + Widths[2] = ImMax(Widths[2], (ImU16)w_shortcut); + Widths[3] = ImMax(Widths[3], (ImU16)w_mark); + CalcNextTotalWidth(false); + return (float)ImMax(TotalWidth, NextTotalWidth); } // FIXME: Provided a rectangle perhaps e.g. a BeginMenuBarEx() could be used anywhere.. @@ -6581,24 +6765,25 @@ void ImGui::EndMenuBar() // Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings. if (NavMoveRequestButNoResultYet() && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) { + // Try to find out if the request is for one of our child menu ImGuiWindow* nav_earliest_child = g.NavWindow; while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu)) nav_earliest_child = nav_earliest_child->ParentWindow; - if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && g.NavMoveRequestForward == ImGuiNavForward_None) + if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0) { // To do so we claim focus back, restore NavId and then process the movement request for yet another frame. - // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth the hassle/cost) + // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth bothering) const ImGuiNavLayer layer = ImGuiNavLayer_Menu; - IM_ASSERT(window->DC.NavLayerActiveMaskNext & (1 << layer)); // Sanity check + IM_ASSERT(window->DC.NavLayersActiveMaskNext & (1 << layer)); // Sanity check FocusWindow(window); SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]); g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection. g.NavDisableMouseHover = g.NavMousePosDirty = true; - g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; - NavMoveRequestCancel(); + NavMoveRequestForward(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags); // Repeat } } + IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'" IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar); IM_ASSERT(window->DC.MenuBarAppending); PopClipRect(); @@ -6683,7 +6868,24 @@ void ImGui::EndMainMenuBar() End(); } -bool ImGui::BeginMenu(const char* label, bool enabled) +static bool IsRootOfOpenMenuSet() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if ((g.OpenPopupStack.Size <= g.BeginPopupStack.Size) || (window->Flags & ImGuiWindowFlags_ChildMenu)) + return false; + + // Initially we used 'OpenParentId' to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items) based on parent ID. + // This would however prevent the use of e.g. PuhsID() user code submitting menus. + // Previously this worked between popup and a first child menu because the first child menu always had the _ChildWindow flag, + // making hovering on parent popup possible while first child menu was focused - but this was generally a bug with other side effects. + // Instead we don't treat Popup specifically (in order to consistently support menu features in them), maybe the first child menu of a Popup + // doesn't have the _ChildWindow flag, and we rely on this IsRootOfOpenMenuSet() check to allow hovering between root window/popup and first chilld menu. + const ImGuiPopupData* upper_popup = &g.OpenPopupStack[g.BeginPopupStack.Size]; + return (/*upper_popup->OpenParentId == window->IDStack.back() &&*/ upper_popup->Window && (upper_popup->Window->Flags & ImGuiWindowFlags_ChildMenu)); +} + +bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) @@ -6695,8 +6897,9 @@ bool ImGui::BeginMenu(const char* label, bool enabled) bool menu_is_open = IsPopupOpen(id, ImGuiPopupFlags_None); // Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu) + // The first menu in a hierarchy isn't so hovering doesn't get accross (otherwise e.g. resizing borders with ImGuiButtonFlags_FlattenChildren would react), but top-most BeginMenu() will bypass that limitation. ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus; - if (window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) + if (window->Flags & ImGuiWindowFlags_ChildMenu) flags |= ImGuiWindowFlags_ChildWindow; // If a menu with same the ID was already submitted, we will append to it, matching the behavior of Begin(). @@ -6715,16 +6918,22 @@ bool ImGui::BeginMenu(const char* label, bool enabled) g.MenusIdSubmittedThisFrame.push_back(id); ImVec2 label_size = CalcTextSize(label, NULL, true); - bool pressed; - bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].OpenParentId == window->IDStack.back()); + + // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent without always being a Child window) + const bool menuset_is_open = IsRootOfOpenMenuSet(); ImGuiWindow* backed_nav_window = g.NavWindow; if (menuset_is_open) - g.NavWindow = window; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent) + g.NavWindow = window; // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu, // However the final position is going to be different! It is chosen by FindBestWindowPosForPopup(). // e.g. Menus tend to overlap each other horizontally to amplify relative Z-ordering. ImVec2 popup_pos, pos = window->DC.CursorPos; + PushID(label); + if (!enabled) + BeginDisabled(); + const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; + bool pressed; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { // Menu inside an horizontal menu bar @@ -6734,24 +6943,33 @@ bool ImGui::BeginMenu(const char* label, bool enabled) window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f); PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y)); float w = label_size.x; - pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f)); + ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); + pressed = Selectable("", menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups, ImVec2(w, 0.0f)); + RenderText(text_pos, label); PopStyleVar(); window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). } else { - // Menu inside a menu + // Menu inside a regular/vertical menu // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system. popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y); - float min_w = window->DC.MenuColumns.DeclColumns(label_size.x, 0.0f, IM_FLOOR(g.FontSize * 1.20f)); // Feedback to next frame + float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f; + float checkmark_w = IM_FLOOR(g.FontSize * 1.20f); + float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, 0.0f, checkmark_w); // Feedback to next frame float extra_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); - pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_SpanAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(min_w, 0.0f)); - ImU32 text_col = GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled); - RenderArrow(window->DrawList, pos + ImVec2(window->DC.MenuColumns.Pos[2] + extra_w + g.FontSize * 0.30f, 0.0f), text_col, ImGuiDir_Right); + ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); + pressed = Selectable("", menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f)); + RenderText(text_pos, label); + if (icon_w > 0.0f) + RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon); + RenderArrow(window->DrawList, pos + ImVec2(offsets->OffsetMark + extra_w + g.FontSize * 0.30f, 0.0f), GetColorU32(ImGuiCol_Text), ImGuiDir_Right); } + if (!enabled) + EndDisabled(); - const bool hovered = enabled && ItemHoverable(window->DC.LastItemRect, id); + const bool hovered = (g.HoveredId == id) && enabled; if (menuset_is_open) g.NavWindow = backed_nav_window; @@ -6762,36 +6980,30 @@ bool ImGui::BeginMenu(const char* label, bool enabled) // Close menu when not hovering it anymore unless we are moving roughly in the direction of the menu // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive. bool moving_toward_other_child_menu = false; - ImGuiWindow* child_menu_window = (g.BeginPopupStack.Size < g.OpenPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].SourceWindow == window) ? g.OpenPopupStack[g.BeginPopupStack.Size].Window : NULL; if (g.HoveredWindow == window && child_menu_window != NULL && !(window->Flags & ImGuiWindowFlags_MenuBar)) { - // FIXME-DPI: Values should be derived from a master "scale" factor. + float ref_unit = g.FontSize; // FIXME-DPI ImRect next_window_rect = child_menu_window->Rect(); - ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta; + ImVec2 ta = (g.IO.MousePos - g.IO.MouseDelta); ImVec2 tb = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR(); ImVec2 tc = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR(); - float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack. - ta.x += (window->Pos.x < child_menu_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues - tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale? - tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f); + float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, ref_unit * 0.5f, ref_unit * 2.5f); // add a bit of extra slack. + ta.x += (window->Pos.x < child_menu_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues (FIXME: ??) + tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -ref_unit * 8.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale? + tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +ref_unit * 8.0f); moving_toward_other_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos); - //GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG] + //GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_toward_other_child_menu ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG] } if (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_toward_other_child_menu) want_close = true; - if (!menu_is_open && hovered && pressed) // Click to open + // Open + if (!menu_is_open && pressed) // Click/activate to open want_open = true; else if (!menu_is_open && hovered && !moving_toward_other_child_menu) // Hover to open want_open = true; - - if (g.NavActivateId == id) - { - want_close = menu_is_open; - want_open = !menu_is_open; - } - if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open + if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open { want_open = true; NavMoveRequestCancel(); @@ -6809,7 +7021,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) { want_open = true; } - else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open + else if (g.NavId == id && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open { want_open = true; NavMoveRequestCancel(); @@ -6821,7 +7033,8 @@ bool ImGui::BeginMenu(const char* label, bool enabled) if (want_close && IsPopupOpen(id, ImGuiPopupFlags_None)) ClosePopupToLevel(g.BeginPopupStack.Size, true); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0)); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0)); + PopID(); if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size) { @@ -6837,7 +7050,9 @@ bool ImGui::BeginMenu(const char* label, bool enabled) if (menu_is_open) { SetNextWindowPos(popup_pos, ImGuiCond_Always); // Note: this is super misleading! The value will serve as reference for FindBestWindowPosForPopup(), not actual pos. + PushStyleVar(ImGuiStyleVar_ChildRounding, style.PopupRounding); // First level will use _PopupRounding, subsequent will use _ChildRounding menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) + PopStyleVar(); } else { @@ -6847,6 +7062,11 @@ bool ImGui::BeginMenu(const char* label, bool enabled) return menu_is_open; } +bool ImGui::BeginMenu(const char* label, bool enabled) +{ + return BeginMenuEx(label, NULL, enabled); +} + void ImGui::EndMenu() { // Nav: When a left move request _within our child menu_ failed, close ourselves (the _parent_ menu). @@ -6854,16 +7074,17 @@ void ImGui::EndMenu() // However, it means that with the current code, a BeginMenu() from outside another menu or a menu-bar won't be closable with the Left direction. ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical) - { - ClosePopupToLevel(g.BeginPopupStack.Size, true); - NavMoveRequestCancel(); - } + if (g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical) + if (g.NavWindow && (g.NavWindow->RootWindowForNav->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->RootWindowForNav->ParentWindow == window) + { + ClosePopupToLevel(g.BeginPopupStack.Size, true); + NavMoveRequestCancel(); + } EndPopup(); } -bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled) +bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut, bool selected, bool enabled) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) @@ -6874,19 +7095,31 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, boo ImVec2 pos = window->DC.CursorPos; ImVec2 label_size = CalcTextSize(label, NULL, true); + const bool menuset_is_open = IsRootOfOpenMenuSet(); + ImGuiWindow* backed_nav_window = g.NavWindow; + if (menuset_is_open) + g.NavWindow = window; + // We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73), // but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only. - ImGuiSelectableFlags flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_SetNavIdOnHover | (enabled ? 0 : ImGuiSelectableFlags_Disabled); bool pressed; + PushID(label); + if (!enabled) + BeginDisabled(); + + const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_SetNavIdOnHover; + const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful - // Note that in this situation we render neither the shortcut neither the selected tick mark + // Note that in this situation: we don't render the shortcut, we render a highlight instead of the selected tick mark. float w = label_size.x; window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f); + ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y)); - pressed = Selectable(label, false, flags, ImVec2(w, 0.0f)); + pressed = Selectable("", selected, selectable_flags, ImVec2(w, 0.0f)); PopStyleVar(); + RenderText(text_pos, label); window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). } else @@ -6894,27 +7127,42 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, boo // Menu item inside a vertical menu // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system. - float shortcut_w = shortcut ? CalcTextSize(shortcut, NULL).x : 0.0f; - float min_w = window->DC.MenuColumns.DeclColumns(label_size.x, shortcut_w, IM_FLOOR(g.FontSize * 1.20f)); // Feedback for next frame - float extra_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); - pressed = Selectable(label, false, flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f)); + float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f; + float shortcut_w = (shortcut && shortcut[0]) ? CalcTextSize(shortcut, NULL).x : 0.0f; + float checkmark_w = IM_FLOOR(g.FontSize * 1.20f); + float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, shortcut_w, checkmark_w); // Feedback for next frame + float stretch_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); + pressed = Selectable("", false, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f)); + RenderText(pos + ImVec2(offsets->OffsetLabel, 0.0f), label); + if (icon_w > 0.0f) + RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon); if (shortcut_w > 0.0f) { - PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); - RenderText(pos + ImVec2(window->DC.MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false); + PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); + RenderText(pos + ImVec2(offsets->OffsetShortcut + stretch_w, 0.0f), shortcut, NULL, false); PopStyleColor(); } if (selected) - RenderCheckMark(window->DrawList, pos + ImVec2(window->DC.MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled), g.FontSize * 0.866f); + RenderCheckMark(window->DrawList, pos + ImVec2(offsets->OffsetMark + stretch_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(ImGuiCol_Text), g.FontSize * 0.866f); } + IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0)); + if (!enabled) + EndDisabled(); + PopID(); + if (menuset_is_open) + g.NavWindow = backed_nav_window; - IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0)); return pressed; } +bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled) +{ + return MenuItemEx(label, NULL, shortcut, selected, enabled); +} + bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled) { - if (MenuItem(label, shortcut, p_selected ? *p_selected : false, enabled)) + if (MenuItemEx(label, NULL, shortcut, p_selected ? *p_selected : false, enabled)) { if (p_selected) *p_selected = !*p_selected; @@ -7045,8 +7293,7 @@ bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImG // Ensure correct ordering when toggling ImGuiTabBarFlags_Reorderable flag, or when a new tab was added while being not reorderable if ((flags & ImGuiTabBarFlags_Reorderable) != (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (tab_bar->TabsAddedNew && !(flags & ImGuiTabBarFlags_Reorderable))) - if (tab_bar->Tabs.Size > 1) - ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByBeginOrder); + ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByBeginOrder); tab_bar->TabsAddedNew = false; // Flags @@ -7230,6 +7477,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) curr_section_n = section_n; // Store data so we can build an array sorted by width if we need to shrink tabs down + IM_MSVC_WARNING_SUPPRESS(6385); int shrink_buffer_index = shrink_buffer_indexes[section_n]++; g.ShrinkWidthBuffer[shrink_buffer_index].Index = tab_n; g.ShrinkWidthBuffer[shrink_buffer_index].Width = tab->ContentWidth; @@ -7715,12 +7963,10 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // If the user called us with *p_open == false, we early out and don't render. // We make a call to ItemAdd() so that attempts to use a contextual popup menu with an implicit ID won't use an older ID. - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); if (p_open && !*p_open) { - PushItemFlag(ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus, true); - ItemAdd(ImRect(), id); - PopItemFlag(); + ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus); return false; } @@ -7760,7 +8006,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, tab->Flags = flags; // Append name with zero-terminator - tab->NameOffset = (ImS16)tab_bar->TabsNames.size(); + tab->NameOffset = (ImS32)tab_bar->TabsNames.size(); tab_bar->TabsNames.append(label, label + strlen(label) + 1); // Update selected tab @@ -7787,9 +8033,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // and then gets submitted again, the tabs will have 'tab_appearing=true' but 'tab_is_new=false'. if (tab_appearing && (!tab_bar_appearing || tab_is_new)) { - PushItemFlag(ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus, true); - ItemAdd(ImRect(), id); - PopItemFlag(); + ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus); if (is_tab_button) return false; return tab_contents_visible; @@ -7836,7 +8080,6 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); if (pressed && !is_tab_button) tab_bar->NextSelectedTabId = id; - hovered |= (g.HoveredId == id); // Allow the close button to overlap unless we are dragging (in which case we don't want any overlapping tabs to be hovered) if (g.ActiveId != id) @@ -7900,8 +8143,11 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, PopClipRect(); window->DC.CursorPos = backup_main_cursor_pos; - // Tooltip (FIXME: Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer) - // We test IsItemHovered() to discard e.g. when another item is active or drag and drop over the tab bar (which g.HoveredId ignores) + // Tooltip + // (Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer-> seems ok) + // (We test IsItemHovered() to discard e.g. when another item is active or drag and drop over the tab bar, which g.HoveredId ignores) + // FIXME: This is a mess. + // FIXME: We may want disabled tab to still display the tooltip? if (text_clipped && g.HoveredId == id && !held && g.HoveredIdNotActiveTimer > g.TooltipSlowDelay && IsItemHovered()) if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip) && !(tab->Flags & ImGuiTabItemFlags_NoTooltip)) SetTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); @@ -7989,14 +8235,7 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, #endif // Render text label (with clipping + alpha gradient) + unsaved marker - const char* TAB_UNSAVED_MARKER = "*"; ImRect text_pixel_clip_bb(bb.Min.x + frame_padding.x, bb.Min.y + frame_padding.y, bb.Max.x - frame_padding.x, bb.Max.y); - if (flags & ImGuiTabItemFlags_UnsavedDocument) - { - text_pixel_clip_bb.Max.x -= CalcTextSize(TAB_UNSAVED_MARKER, NULL, false).x; - ImVec2 unsaved_marker_pos(ImMin(bb.Min.x + frame_padding.x + label_size.x + 2, text_pixel_clip_bb.Max.x), bb.Min.y + frame_padding.y + IM_FLOOR(-g.FontSize * 0.25f)); - RenderTextClippedEx(draw_list, unsaved_marker_pos, bb.Max - frame_padding, TAB_UNSAVED_MARKER, NULL, NULL); - } ImRect text_ellipsis_clip_bb = text_pixel_clip_bb; // Return clipped state ignoring the close button @@ -8006,7 +8245,10 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, //draw_list->AddCircle(text_ellipsis_clip_bb.Min, 3.0f, *out_text_clipped ? IM_COL32(255, 0, 0, 255) : IM_COL32(0, 255, 0, 255)); } - // Close Button + const float button_sz = g.FontSize; + const ImVec2 button_pos(ImMax(bb.Min.x, bb.Max.x - frame_padding.x * 2.0f - button_sz), bb.Min.y); + + // Close Button & Unsaved Marker // We are relying on a subtle and confusing distinction between 'hovered' and 'g.HoveredId' which happens because we are using ImGuiButtonFlags_AllowOverlapMode + SetItemAllowOverlap() // 'hovered' will be true when hovering the Tab but NOT when hovering the close button // 'g.HoveredId==id' will be true when hovering the Tab including when hovering the close button @@ -8014,28 +8256,40 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, bool close_button_pressed = false; bool close_button_visible = false; if (close_button_id != 0) - if (is_contents_visible || bb.GetWidth() >= g.Style.TabMinWidthForCloseButton) + if (is_contents_visible || bb.GetWidth() >= ImMax(button_sz, g.Style.TabMinWidthForCloseButton)) if (g.HoveredId == tab_id || g.HoveredId == close_button_id || g.ActiveId == tab_id || g.ActiveId == close_button_id) close_button_visible = true; + bool unsaved_marker_visible = (flags & ImGuiTabItemFlags_UnsavedDocument) != 0 && (button_pos.x + button_sz <= bb.Max.x); + if (close_button_visible) { - ImGuiLastItemDataBackup last_item_backup; - const float close_button_sz = g.FontSize; + ImGuiLastItemData last_item_backup = g.LastItemData; PushStyleVar(ImGuiStyleVar_FramePadding, frame_padding); - if (CloseButton(close_button_id, ImVec2(bb.Max.x - frame_padding.x * 2.0f - close_button_sz, bb.Min.y))) + if (CloseButton(close_button_id, button_pos)) close_button_pressed = true; PopStyleVar(); - last_item_backup.Restore(); + g.LastItemData = last_item_backup; // Close with middle mouse button if (!(flags & ImGuiTabItemFlags_NoCloseWithMiddleMouseButton) && IsMouseClicked(2)) close_button_pressed = true; - - text_pixel_clip_bb.Max.x -= close_button_sz; + } + else if (unsaved_marker_visible) + { + const ImRect bullet_bb(button_pos, button_pos + ImVec2(button_sz, button_sz) + g.Style.FramePadding * 2.0f); + RenderBullet(draw_list, bullet_bb.GetCenter(), GetColorU32(ImGuiCol_Text)); } + // This is all rather complicated + // (the main idea is that because the close button only appears on hover, we don't want it to alter the ellipsis position) // FIXME: if FramePadding is noticeably large, ellipsis_max_x will be wrong here (e.g. #3497), maybe for consistency that parameter of RenderTextEllipsis() shouldn't exist.. float ellipsis_max_x = close_button_visible ? text_pixel_clip_bb.Max.x : bb.Max.x - 1.0f; + if (close_button_visible || unsaved_marker_visible) + { + text_pixel_clip_bb.Max.x -= close_button_visible ? (button_sz) : (button_sz * 0.80f); + text_ellipsis_clip_bb.Max.x -= unsaved_marker_visible ? (button_sz * 0.80f) : 0.0f; + ellipsis_max_x = text_pixel_clip_bb.Max.x; + } RenderTextEllipsis(draw_list, text_ellipsis_clip_bb.Min, text_ellipsis_clip_bb.Max, text_pixel_clip_bb.Max.x, ellipsis_max_x, label, NULL, &label_size); #if 0 @@ -8047,7 +8301,6 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, *out_just_closed = close_button_pressed; } - const char* const KeyNames[] = { "Null", @@ -8356,7 +8609,7 @@ bool ImGui::Hotkey(const char* label, int* key, const ImVec2& ssize) ImGui::SetActiveID(id, window); ImGui::FocusWindow(window); } - else if (io.MouseClicked[0]) + else if (io.MouseClicked[0]) { if (g.ActiveId == id) { diff --git a/r5dev/thirdparty/lzham/include/lzham.h b/r5dev/thirdparty/lzham/include/lzham.h new file mode 100644 index 00000000..526c0ab7 --- /dev/null +++ b/r5dev/thirdparty/lzham/include/lzham.h @@ -0,0 +1,765 @@ +// File: lzham.h - Copyright (c) 2009-2012 Richard Geldreich, Jr. +// LZHAM uses the MIT License. See Copyright Notice and license at the end of this file. +// +// This is the main header file, includable from C or C++ files, which defines all the publically available API's, structs, and types used by the LZHAM codec. +// +// Notes: +// +// As of LZHAM alpha8, there are now two sets of API's: +// - The first (oldest) API directly exposes all of the codec's functionality. See lzham_compress_init(), lzham_decompress_init(), etc. This API has the lowest overhead +// and is the most tested. +// - The new API implements the most useful/popular subset of the zlib API, but doesn't expose all of the codec's functionality yet. See the lzham_z* functions. +// This functionality is provided because most users of compression libraries are already very familiar with the nuts and bolts of the zlib API. +// For the most common zlib usage cases LZHAM is an almost drop-in replacement for zlib. To make switching from zlib even easier, you can define the LZHAM_DEFINE_ZLIB_API macro, +// which causes this header to #define most zlib symbols to their LZHAM equivalents. +// Note that LZHAM does not actually implement the deflate/inflate algorithm, so it cannot decompress streams created by standard zlib yet (and of course, zlib cannot decompress +// streams created by LZHAM). Internally, this API is mostly implemented via the older low-level LZHAM API. + +#ifndef __LZHAM_H__ +#define __LZHAM_H__ + +#ifdef _MSC_VER +#pragma once +#endif + +#include + +// Upper byte = major version +// Lower byte = minor version +#define LZHAM_DLL_VERSION 0x1008 + +#if defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) + #define LZHAM_64BIT 1 +#endif + +#if defined(_MSC_VER) + #define LZHAM_CDECL __cdecl +#else + #define LZHAM_CDECL +#endif + +#ifdef LZHAM_EXPORTS + #define LZHAM_DLL_EXPORT __declspec(dllexport) +#else + #define LZHAM_DLL_EXPORT +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + typedef unsigned char lzham_uint8; + typedef signed int lzham_int32; + typedef unsigned int lzham_uint32; + typedef unsigned int lzham_bool; + + // Returns DLL version (LZHAM_DLL_VERSION). + LZHAM_DLL_EXPORT lzham_uint32 LZHAM_CDECL lzham_get_version(void); + + // User provided memory allocation + + // Custom allocation function must return pointers with LZHAM_MIN_ALLOC_ALIGNMENT (or better). + #define LZHAM_MIN_ALLOC_ALIGNMENT sizeof(size_t) * 2 + + typedef void* (LZHAM_CDECL *lzham_realloc_func)(void* p, size_t size, size_t* pActual_size, lzham_bool movable, void* pUser_data); + typedef size_t (LZHAM_CDECL *lzham_msize_func)(void* p, void* pUser_data); + + // Call this function to force LZHAM to use custom memory malloc(), realloc(), free() and msize functions. + LZHAM_DLL_EXPORT void LZHAM_CDECL lzham_set_memory_callbacks(lzham_realloc_func pRealloc, lzham_msize_func pMSize, void* pUser_data); + + // lzham_flush_t must map directly to the zlib-style API flush types (LZHAM_Z_NO_FLUSH, etc.) + typedef enum + { + LZHAM_NO_FLUSH = 0, + LZHAM_SYNC_FLUSH = 2, + LZHAM_FULL_FLUSH = 3, + LZHAM_FINISH = 4, + LZHAM_TABLE_FLUSH = 10 + } lzham_flush_t; + + // Compression + #define LZHAM_MIN_DICT_SIZE_LOG2 15 + #define LZHAM_MAX_DICT_SIZE_LOG2_X86 26 + #define LZHAM_MAX_DICT_SIZE_LOG2_X64 29 + + #define LZHAM_MAX_HELPER_THREADS 64 + + typedef enum + { + LZHAM_COMP_STATUS_NOT_FINISHED = 0, + LZHAM_COMP_STATUS_NEEDS_MORE_INPUT, + LZHAM_COMP_STATUS_HAS_MORE_OUTPUT, + + // All the following enums must indicate failure/success. + + LZHAM_COMP_STATUS_FIRST_SUCCESS_OR_FAILURE_CODE, + LZHAM_COMP_STATUS_SUCCESS = LZHAM_COMP_STATUS_FIRST_SUCCESS_OR_FAILURE_CODE, + + LZHAM_COMP_STATUS_FIRST_FAILURE_CODE, + LZHAM_COMP_STATUS_FAILED = LZHAM_COMP_STATUS_FIRST_FAILURE_CODE, + LZHAM_COMP_STATUS_FAILED_INITIALIZING, + LZHAM_COMP_STATUS_INVALID_PARAMETER, + LZHAM_COMP_STATUS_OUTPUT_BUF_TOO_SMALL, + + LZHAM_COMP_STATUS_FORCE_DWORD = 0xFFFFFFFF + } lzham_compress_status_t; + + typedef enum + { + LZHAM_COMP_LEVEL_FASTEST = 0, + LZHAM_COMP_LEVEL_FASTER, + LZHAM_COMP_LEVEL_DEFAULT, + LZHAM_COMP_LEVEL_BETTER, + LZHAM_COMP_LEVEL_UBER, + + LZHAM_TOTAL_COMP_LEVELS, + + LZHAM_COMP_LEVEL_FORCE_DWORD = 0xFFFFFFFF + } lzham_compress_level; + + // Streaming compression + typedef void *lzham_compress_state_ptr; + + typedef enum + { + LZHAM_COMP_FLAG_FORCE_POLAR_CODING = 1, // Forces Polar codes vs. Huffman, for a slight increase in decompression speed. + LZHAM_COMP_FLAG_EXTREME_PARSING = 2, // Improves ratio by allowing the compressor's parse graph to grow "higher" (up to 4 parent nodes per output node), but is much slower. + LZHAM_COMP_FLAG_DETERMINISTIC_PARSING = 4, // Guarantees that the compressed output will always be the same given the same input and parameters (no variation between runs due to kernel threading scheduling). + + // If enabled, the compressor is free to use any optimizations which could lower the decompression rate (such + // as adaptively resetting the Huffman table update rate to maximum frequency, which is costly for the decompressor). + LZHAM_COMP_FLAG_TRADEOFF_DECOMPRESSION_RATE_FOR_COMP_RATIO = 16, + + LZHAM_COMP_FLAG_WRITE_ZLIB_STREAM = 32, + } lzham_compress_flags; + + typedef struct + { + lzham_uint32 m_struct_size; // set to sizeof(lzham_compress_params) + lzham_uint32 m_dict_size_log2; // set to the log2(dictionary_size), must range between [LZHAM_MIN_DICT_SIZE_LOG2, LZHAM_MAX_DICT_SIZE_LOG2_X86] for x86 LZHAM_MAX_DICT_SIZE_LOG2_X64 for x64 + lzham_compress_level m_level; // set to LZHAM_COMP_LEVEL_FASTEST, etc. + lzham_int32 m_max_helper_threads; // max # of additional "helper" threads to create, must range between [-1,LZHAM_MAX_HELPER_THREADS], where -1=max practical + lzham_uint32 m_cpucache_total_lines; // set to 0 (optimize compressed stream to avoid L1/L2 cache misses - not currently supported) + lzham_uint32 m_cpucache_line_size; // set to 0 + lzham_uint32 m_compress_flags; // optional compression flags (see lzham_compress_flags enum) + lzham_uint32 m_num_seed_bytes; // for delta compression (optional) - number of seed bytes pointed to by m_pSeed_bytes + const void *m_pSeed_bytes; // for delta compression (optional) - pointer to seed bytes buffer, must be at least m_num_seed_bytes long + } lzham_compress_params; + + typedef struct + { + lzham_uint32 adler32; + lzham_uint32 crc32; + } lzham_compress_checksums; + + // Initializes a compressor. Returns a pointer to the compressor's internal state, or NULL on failure. + // pParams cannot be NULL. Be sure to initialize the pParams->m_struct_size member to sizeof(lzham_compress_params) (along with the other members to reasonable values) before calling this function. + // TODO: With large dictionaries this function could take a while (due to memory allocation). I need to add a reinit() API for compression (decompression already has one). + LZHAM_DLL_EXPORT lzham_compress_state_ptr LZHAM_CDECL lzham_compress_init(const lzham_compress_params *pParams); + + LZHAM_DLL_EXPORT lzham_compress_state_ptr LZHAM_CDECL lzham_compress_reinit(lzham_compress_state_ptr pState); + + // Deinitializes a compressor, releasing all allocated memory. + // returns adler32 and crc32 of source data (valid only on success). + LZHAM_DLL_EXPORT lzham_compress_checksums* LZHAM_CDECL lzham_compress_deinit(lzham_compress_state_ptr pState); + + // Compresses an arbitrarily sized block of data, writing as much available compressed data as possible to the output buffer. + // This method may be called as many times as needed, but for best perf. try not to call it with tiny buffers. + // pState - Pointer to internal compression state, created by lzham_compress_init. + // pIn_buf, pIn_buf_size - Pointer to input data buffer, and pointer to a size_t containing the number of bytes available in this buffer. + // On return, *pIn_buf_size will be set to the number of bytes read from the buffer. + // pOut_buf, pOut_buf_size - Pointer to the output data buffer, and a pointer to a size_t containing the max number of bytes that can be written to this buffer. + // On return, *pOut_buf_size will be set to the number of bytes written to this buffer. + // no_more_input_bytes_flag - Set to true to indicate that no more input bytes are available to compress (EOF). Once you call this function with this param set to true, it must stay set to true in all future calls. + // + // Normal return status codes: + // LZHAM_COMP_STATUS_NOT_FINISHED - Compression can continue, but the compressor needs more input, or it needs more room in the output buffer. + // LZHAM_COMP_STATUS_NEEDS_MORE_INPUT - Compression can contintue, but the compressor has no more output, and has no input but we're not at EOF. Supply more input to continue. + // Success/failure return status codes: + // LZHAM_COMP_STATUS_SUCCESS - Compression has completed successfully. + // LZHAM_COMP_STATUS_FAILED, LZHAM_COMP_STATUS_FAILED_INITIALIZING, LZHAM_COMP_STATUS_INVALID_PARAMETER - Something went wrong. + LZHAM_DLL_EXPORT lzham_compress_status_t LZHAM_CDECL lzham_compress( + lzham_compress_state_ptr pState, + const lzham_uint8 *pIn_buf, size_t *pIn_buf_size, + lzham_uint8 *pOut_buf, size_t *pOut_buf_size, + lzham_bool no_more_input_bytes_flag); + + LZHAM_DLL_EXPORT lzham_compress_status_t LZHAM_CDECL lzham_compress2( + lzham_compress_state_ptr pState, + const lzham_uint8 *pIn_buf, size_t *pIn_buf_size, + lzham_uint8 *pOut_buf, size_t *pOut_buf_size, + lzham_flush_t flush_type); + + // Single function call compression interface. + // Same return codes as lzham_compress, except this function can also return LZHAM_COMP_STATUS_OUTPUT_BUF_TOO_SMALL. + LZHAM_DLL_EXPORT lzham_compress_status_t LZHAM_CDECL lzham_compress_memory( + const lzham_compress_params *pParams, + lzham_uint8* pDst_buf, + size_t *pDst_len, + const lzham_uint8* pSrc_buf, + size_t src_len, + lzham_uint32 *pAdler32, + lzham_uint32 *pCrc32); + + // Decompression + typedef enum + { + // LZHAM_DECOMP_STATUS_NOT_FINISHED indicates that the decompressor is flushing its internal buffer to the caller's output buffer. + // There may be more bytes available to decompress on the next call, but there is no guarantee. + LZHAM_DECOMP_STATUS_NOT_FINISHED = 0, + + // LZHAM_DECOMP_STATUS_HAS_MORE_OUTPUT indicates that the decompressor is trying to flush its internal buffer to the caller's output buffer, + // but the caller hasn't provided any space to copy this data to the caller's output buffer. Call the lzham_decompress() again with a non-empty sized output buffer. + LZHAM_DECOMP_STATUS_HAS_MORE_OUTPUT, + + // LZHAM_DECOMP_STATUS_NEEDS_MORE_INPUT indicates that the decompressor has consumed all input bytes, has not encountered an "end of stream" code, + // and the caller hasn't set no_more_input_bytes_flag to true, so it's expecting more input to proceed. + LZHAM_DECOMP_STATUS_NEEDS_MORE_INPUT, + + // All the following enums always (and MUST) indicate failure/success. + LZHAM_DECOMP_STATUS_FIRST_SUCCESS_OR_FAILURE_CODE, + + // LZHAM_DECOMP_STATUS_SUCCESS indicates decompression has successfully completed. + LZHAM_DECOMP_STATUS_SUCCESS = LZHAM_DECOMP_STATUS_FIRST_SUCCESS_OR_FAILURE_CODE, + + // The remaining status codes indicate a failure of some sort. Most failures are unrecoverable. TODO: Document which codes are recoverable. + LZHAM_DECOMP_STATUS_FIRST_FAILURE_CODE, + + LZHAM_DECOMP_STATUS_FAILED_INITIALIZING = LZHAM_DECOMP_STATUS_FIRST_FAILURE_CODE, + LZHAM_DECOMP_STATUS_FAILED_DEST_BUF_TOO_SMALL, + LZHAM_DECOMP_STATUS_FAILED_EXPECTED_MORE_RAW_BYTES, + LZHAM_DECOMP_STATUS_FAILED_BAD_CODE, + LZHAM_DECOMP_STATUS_FAILED_ADLER32, + LZHAM_DECOMP_STATUS_FAILED_CRC32, + LZHAM_DECOMP_STATUS_FAILED_BAD_RAW_BLOCK, + LZHAM_DECOMP_STATUS_FAILED_BAD_COMP_BLOCK_SYNC_CHECK, + LZHAM_DECOMP_STATUS_FAILED_BAD_ZLIB_HEADER, + LZHAM_DECOMP_STATUS_FAILED_NEED_SEED_BYTES, + LZHAM_DECOMP_STATUS_FAILED_BAD_SEED_BYTES, + LZHAM_DECOMP_STATUS_FAILED_BAD_SYNC_BLOCK, + LZHAM_DECOMP_STATUS_INVALID_PARAMETER, + } lzham_decompress_status_t; + + typedef void *lzham_decompress_state_ptr; + + typedef enum + { + LZHAM_DECOMP_FLAG_NONE = 0, + LZHAM_DECOMP_FLAG_OUTPUT_UNBUFFERED = 1 << 0, + LZHAM_DECOMP_FLAG_COMPUTE_ADLER32 = 1 << 1, + LZHAM_DECOMP_FLAG_COMPUTE_CRC32 = 1 << 2, + LZHAM_DECOMP_FLAG_READ_ZLIB_STREAM = 1 << 3, + } lzham_decompress_flags; + + // Decompression parameters structure. + // Notes: + // m_dict_size_log2 MUST match the value used during compression! + // If m_num_seed_bytes != 0, LZHAM_DECOMP_FLAG_OUTPUT_UNBUFFERED must not be set (i.e. static "seed" dictionaries are not compatible with unbuffered decompression). + // The seed buffer's contents and size must match the seed buffer used during compression. + typedef struct + { + lzham_uint32 m_struct_size; // set to sizeof(lzham_decompress_params) + lzham_uint32 m_dict_size_log2; // set to the log2(dictionary_size), must range between [LZHAM_MIN_DICT_SIZE_LOG2, LZHAM_MAX_DICT_SIZE_LOG2_X86] for x86 LZHAM_MAX_DICT_SIZE_LOG2_X64 for x64 + lzham_uint32 m_decompress_flags; // optional decompression flags (see lzham_decompress_flags enum) + lzham_uint32 m_num_seed_bytes; // for delta compression (optional) - number of seed bytes pointed to by m_pSeed_bytes + const void *m_pSeed_bytes; // for delta compression (optional) - pointer to seed bytes buffer, must be at least m_num_seed_bytes long + } lzham_decompress_params; + + typedef struct + { + lzham_uint32 adler32; + lzham_uint32 crc32; + } lzham_decompress_checksums; + + // Initializes a decompressor. + // pParams cannot be NULL. Be sure to initialize the pParams->m_struct_size member to sizeof(lzham_decompress_params) (along with the other members to reasonable values) before calling this function. + // Note: With large dictionaries this function could take a while (due to memory allocation). To serially decompress multiple streams, it's faster to init a compressor once and + // reuse it using by calling lzham_decompress_reinit(). + LZHAM_DLL_EXPORT lzham_decompress_state_ptr LZHAM_CDECL lzham_decompress_init(const lzham_decompress_params *pParams); + + // Quickly re-initializes the decompressor to its initial state given an already allocated/initialized state (doesn't do any memory alloc unless necessary). + LZHAM_DLL_EXPORT lzham_decompress_state_ptr LZHAM_CDECL lzham_decompress_reinit(lzham_decompress_state_ptr pState, const lzham_decompress_params *pParams); + + // Deinitializes a decompressor. + // returns adler32 of decompressed data if compute_adler32 was true, otherwise it returns the adler32 from the compressed stream. + LZHAM_DLL_EXPORT lzham_decompress_checksums* LZHAM_CDECL lzham_decompress_deinit(lzham_decompress_state_ptr pState); + + // Decompresses an arbitrarily sized block of compressed data, writing as much available decompressed data as possible to the output buffer. + // This method is implemented as a coroutine so it may be called as many times as needed. However, for best perf. try not to call it with tiny buffers. + // pState - Pointer to internal decompression state, originally created by lzham_decompress_init. + // pIn_buf, pIn_buf_size - Pointer to input data buffer, and pointer to a size_t containing the number of bytes available in this buffer. + // On return, *pIn_buf_size will be set to the number of bytes read from the buffer. + // pOut_buf, pOut_buf_size - Pointer to the output data buffer, and a pointer to a size_t containing the max number of bytes that can be written to this buffer. + // On return, *pOut_buf_size will be set to the number of bytes written to this buffer. + // no_more_input_bytes_flag - Set to true to indicate that no more input bytes are available to compress (EOF). Once you call this function with this param set to true, it must stay set to true in all future calls. + // Notes: + // In unbuffered mode, the output buffer MUST be large enough to hold the entire decompressed stream. Otherwise, you'll receive the + // LZHAM_DECOMP_STATUS_FAILED_DEST_BUF_TOO_SMALL error (which is currently unrecoverable during unbuffered decompression). + // In buffered mode, if the output buffer's size is 0 bytes, the caller is indicating that no more output bytes are expected from the + // decompressor. In this case, if the decompressor actually has more bytes you'll receive the LZHAM_DECOMP_STATUS_HAS_MORE_OUTPUT + // error (which is recoverable in the buffered case - just call lzham_decompress() again with a non-zero size output buffer). + LZHAM_DLL_EXPORT lzham_decompress_status_t LZHAM_CDECL lzham_decompress( + lzham_decompress_state_ptr pState, + const lzham_uint8 *pIn_buf, size_t *pIn_buf_size, + lzham_uint8 *pOut_buf, size_t *pOut_buf_size, + lzham_bool no_more_input_bytes_flag); + + // Single function call interface. + LZHAM_DLL_EXPORT lzham_decompress_status_t LZHAM_CDECL lzham_decompress_memory( + const lzham_decompress_params *pParams, + lzham_uint8* pDst_buf, + size_t *pDst_len, + const lzham_uint8* pSrc_buf, + size_t src_len, + lzham_uint32 *pAdler32, + lzham_uint32 *pCrc32); + + // ------------------- zlib-style API Definitions. + + // Important note: LZHAM doesn't internally support the Deflate algorithm, but for API compatibility the "Deflate" and "Inflate" names are retained here. + + typedef unsigned long lzham_z_ulong; + + // Heap allocation callbacks. + // Note that lzham_alloc_func parameter types purposely differ from zlib's: items/size is size_t, not unsigned long. + typedef void *(*lzham_z_alloc_func)(void *opaque, size_t items, size_t size); + typedef void (*lzham_z_free_func)(void *opaque, void *address); + typedef void *(*lzham_z_realloc_func)(void *opaque, void *address, size_t items, size_t size); + + #define LZHAM_Z_ADLER32_INIT (1) + // lzham_adler32() returns the initial adler-32 value to use when called with ptr==NULL. + LZHAM_DLL_EXPORT lzham_z_ulong lzham_z_adler32(lzham_z_ulong adler, const unsigned char *ptr, size_t buf_len); + + #define LZHAM_Z_CRC32_INIT (0) + // lzham_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. + LZHAM_DLL_EXPORT lzham_z_ulong lzham_z_crc32(lzham_z_ulong crc, const unsigned char *ptr, size_t buf_len); + + // Compression strategies. + enum + { + LZHAM_Z_DEFAULT_STRATEGY = 0, + LZHAM_Z_FILTERED = 1, + LZHAM_Z_HUFFMAN_ONLY = 2, + LZHAM_Z_RLE = 3, + LZHAM_Z_FIXED = 4 + }; + + // Method + #define LZHAM_Z_DEFLATED 8 + #define LZHAM_Z_LZHAM 14 + + #define LZHAM_Z_VERSION "10.8.1" + #define LZHAM_Z_VERNUM 0xA810 + #define LZHAM_Z_VER_MAJOR 10 + #define LZHAM_Z_VER_MINOR 8 + #define LZHAM_Z_VER_REVISION 1 + #define LZHAM_Z_VER_SUBREVISION 0 + + // Flush values. + // For compression, you typically only need to use LZHAM_NO_FLUSH and LZHAM_FINISH. + // LZHAM_Z_SYNC_FLUSH and LZHAM_Z_FULL_FLUSH during compression forces compression of all buffered input. + // + // For decompression, you typically only need to use LZHAM_Z_SYNC_FLUSH or LZHAM_Z_FINISH. + // LZHAM_Z_FINISH during decompression guarantees that the output buffer is large enough to hold all remaining data to decompress. + // See http://www.bolet.org/~pornin/deflate-flush.html + // Must directly map to lzham_flush_t + enum + { + LZHAM_Z_NO_FLUSH = 0, // compression/decompression + LZHAM_Z_PARTIAL_FLUSH = 1, // compression/decompression, same as LZHAM_Z_SYNC_FLUSH + LZHAM_Z_SYNC_FLUSH = 2, // compression/decompression, when compressing: flush current block (if any), always outputs sync block (aligns output to byte boundary, a 0xFFFF0000 marker will appear in the output stream) + LZHAM_Z_FULL_FLUSH = 3, // compression/decompression, when compressing: same as LZHAM_Z_SYNC_FLUSH but also forces a full state flush (LZ dictionary, all symbol statistics) + LZHAM_Z_FINISH = 4, // compression/decompression + LZHAM_Z_BLOCK = 5, // not supported + LZHAM_Z_TABLE_FLUSH = 10 // compression only, resets all symbol table update rates to maximum frequency (LZHAM extension) + }; + + // Return status codes. LZHAM_Z_PARAM_ERROR is non-standard. + enum + { + LZHAM_Z_OK = 0, + LZHAM_Z_STREAM_END = 1, + LZHAM_Z_NEED_DICT = 2, + LZHAM_Z_ERRNO = -1, + LZHAM_Z_STREAM_ERROR = -2, + LZHAM_Z_DATA_ERROR = -3, + LZHAM_Z_MEM_ERROR = -4, + LZHAM_Z_BUF_ERROR = -5, + LZHAM_Z_VERSION_ERROR = -6, + LZHAM_Z_PARAM_ERROR = -10000 + }; + + // Compression levels. + enum + { + LZHAM_Z_NO_COMPRESSION = 0, + LZHAM_Z_BEST_SPEED = 1, + LZHAM_Z_BEST_COMPRESSION = 9, + LZHAM_Z_UBER_COMPRESSION = 10, // uber = best with extreme parsing (can be very slow) + LZHAM_Z_DEFAULT_COMPRESSION = -1 + }; + + // Window bits + // Important note: The zlib-style API's default to 32KB dictionary for API compatibility. For improved compression, be sure to call deflateInit2/inflateInit2 and specify larger custom window_bits values! + // If changing the calling code isn't practical, unremark LZHAM_Z_API_FORCE_WINDOW_BITS. + #define LZHAM_Z_DEFAULT_WINDOW_BITS 15 + + // Define LZHAM_Z_API_FORCE_WINDOW_BITS to force the entire library to use a constant value for window_bits (helps with porting) in all zlib API's. + // TODO: Might be useful to provide an API to control this at runtime. + //#define LZHAM_Z_API_FORCE_WINDOW_BITS 23 + + // Data types + #define LZHAM_Z_BINARY 0 + #define LZHAM_Z_TEXT 1 + #define LZHAM_Z_ASCII 1 + #define LZHAM_Z_UNKNOWN 2 + + struct lzham_z_internal_state; + + // Compression/decompression stream struct. + typedef struct + { + const unsigned char *next_in; // pointer to next byte to read + unsigned int avail_in; // number of bytes available at next_in + lzham_z_ulong total_in; // total number of bytes consumed so far + + unsigned char *next_out; // pointer to next byte to write + unsigned int avail_out; // number of bytes that can be written to next_out + lzham_z_ulong total_out; // total number of bytes produced so far + + char *msg; // error msg (unused) + struct lzham_z_internal_state *state; // internal state, allocated by zalloc/zfree + + // LZHAM does not support per-stream heap callbacks. Use lzham_set_memory_callbacks() instead. + // These members are ignored - they are here for backwards compatibility with zlib. + lzham_z_alloc_func zalloc; // optional heap allocation function (defaults to malloc) + lzham_z_free_func zfree; // optional heap free function (defaults to free) + void *opaque; // heap alloc function user pointer + + int data_type; // data_type (unused) + lzham_z_ulong adler32; // adler32 of the source or uncompressed data + lzham_z_ulong crc32; // crc32 of the source or uncompressed data + lzham_z_ulong reserved; // not used + } lzham_z_stream; + + typedef lzham_z_stream *lzham_z_streamp; + + LZHAM_DLL_EXPORT const char *lzham_z_version(void); + + // lzham_deflateInit() initializes a compressor with default options: + // Parameters: + // pStream must point to an initialized lzham_stream struct. + // level must be between [LZHAM_NO_COMPRESSION, LZHAM_BEST_COMPRESSION]. + // level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. + // Return values: + // LZHAM_OK on success. + // LZHAM_STREAM_ERROR if the stream is bogus. + // LZHAM_PARAM_ERROR if the input parameters are bogus. + // LZHAM_MEM_ERROR on out of memory. + LZHAM_DLL_EXPORT int lzham_z_deflateInit(lzham_z_streamp pStream, int level); + + // lzham_deflateInit2() is like lzham_deflate(), except with more control: + // Additional parameters: + // method must be LZHAM_Z_DEFLATED or LZHAM_Z_LZHAM (LZHAM_Z_DEFLATED will be internally converted to LZHAM_Z_LZHAM with a windowsize of LZHAM_Z_DEFAULT_WINDOW_BITS) + // window_bits must be LZHAM_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -LZHAM_Z_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) + // mem_level must be between [1, 9] (it's checked but ignored by lzham) + LZHAM_DLL_EXPORT int lzham_z_deflateInit2(lzham_z_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); + + // Quickly resets a compressor without having to reallocate anything. Same as calling lzham_z_deflateEnd() followed by lzham_z_deflateInit()/lzham_z_deflateInit2(). + LZHAM_DLL_EXPORT int lzham_z_deflateReset(lzham_z_streamp pStream); + + // lzham_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. + // Parameters: + // pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. + // flush may be LZHAM_Z_NO_FLUSH, LZHAM_Z_PARTIAL_FLUSH/LZHAM_Z_SYNC_FLUSH, LZHAM_Z_FULL_FLUSH, or LZHAM_Z_FINISH. + // Return values: + // LZHAM_Z_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). + // LZHAM_Z_STREAM_END if all input has been consumed and all output bytes have been written. Don't call lzham_z_deflate() on the stream anymore. + // LZHAM_Z_STREAM_ERROR if the stream is bogus. + // LZHAM_Z_PARAM_ERROR if one of the parameters is invalid. + // LZHAM_Z_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) + LZHAM_DLL_EXPORT int lzham_z_deflate(lzham_z_streamp pStream, int flush); + + // lzham_deflateEnd() deinitializes a compressor: + // Return values: + // LZHAM_Z_OK on success. + // LZHAM_Z_STREAM_ERROR if the stream is bogus. + LZHAM_DLL_EXPORT int lzham_z_deflateEnd(lzham_z_streamp pStream); + + // lzham_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by lzham_z_deflate(), assuming flush is set to only LZHAM_Z_NO_FLUSH or LZHAM_Z_FINISH. + LZHAM_DLL_EXPORT lzham_z_ulong lzham_z_deflateBound(lzham_z_streamp pStream, lzham_z_ulong source_len); + + // Single-call compression functions lzham_z_compress() and lzham_z_compress2(): + // Returns LZHAM_Z_OK on success, or one of the error codes from lzham_z_deflate() on failure. + LZHAM_DLL_EXPORT int lzham_z_compress(unsigned char *pDest, lzham_z_ulong *pDest_len, const unsigned char *pSource, lzham_z_ulong source_len); + LZHAM_DLL_EXPORT int lzham_z_compress2(unsigned char *pDest, lzham_z_ulong *pDest_len, const unsigned char *pSource, lzham_z_ulong source_len, int level); + + // lzham_z_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling lzham_z_compress(). + LZHAM_DLL_EXPORT lzham_z_ulong lzham_z_compressBound(lzham_z_ulong source_len); + + // Initializes a decompressor. + LZHAM_DLL_EXPORT int lzham_z_inflateInit(lzham_z_streamp pStream); + + // lzham_z_inflateInit2() is like lzham_z_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: + // window_bits must be LZHAM_Z_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -LZHAM_Z_DEFAULT_WINDOW_BITS (raw stream with no zlib header/footer). + LZHAM_DLL_EXPORT int lzham_z_inflateInit2(lzham_z_streamp pStream, int window_bits); + + LZHAM_DLL_EXPORT int lzham_z_inflateReset(lzham_z_streamp pStream); + + // Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. + // Parameters: + // pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. + // flush may be LZHAM_Z_NO_FLUSH, LZHAM_Z_SYNC_FLUSH, or LZHAM_Z_FINISH. + // On the first call, if flush is LZHAM_Z_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). + // LZHAM_Z_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. + // Return values: + // LZHAM_Z_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. + // LZHAM_Z_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. + // LZHAM_Z_STREAM_ERROR if the stream is bogus. + // LZHAM_Z_DATA_ERROR if the deflate stream is invalid. + // LZHAM_Z_PARAM_ERROR if one of the parameters is invalid. + // LZHAM_Z_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call lzham_inflate() again + // with more input data, or with more room in the output buffer (except when using single call decompression, described above). + LZHAM_DLL_EXPORT int lzham_z_inflate(lzham_z_streamp pStream, int flush); + + // Deinitializes a decompressor. + LZHAM_DLL_EXPORT int lzham_z_inflateEnd(lzham_z_streamp pStream); + + // Single-call decompression. + // Returns LZHAM_OK on success, or one of the error codes from lzham_inflate() on failure. + LZHAM_DLL_EXPORT int lzham_z_uncompress(unsigned char *pDest, lzham_z_ulong *pDest_len, const unsigned char *pSource, lzham_z_ulong source_len); + + // Returns a string description of the specified error code, or NULL if the error code is invalid. + LZHAM_DLL_EXPORT const char *lzham_z_error(int err); + + // Redefine zlib-compatible names to lzham equivalents, so lzham can be used as a more or less drop-in replacement for the subset of zlib that lzham supports. + // Define LZHAM_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. + #ifdef LZHAM_DEFINE_ZLIB_API + typedef unsigned char Byte; + typedef unsigned int uInt; + typedef lzham_z_ulong uLong; + typedef Byte Bytef; + typedef uInt uIntf; + typedef char charf; + typedef int intf; + typedef void *voidpf; + typedef uLong uLongf; + typedef void *voidp; + typedef void *const voidpc; + #define Z_NULL 0 + #define Z_NO_FLUSH LZHAM_Z_NO_FLUSH + #define Z_PARTIAL_FLUSH LZHAM_Z_PARTIAL_FLUSH + #define Z_SYNC_FLUSH LZHAM_Z_SYNC_FLUSH + #define Z_FULL_FLUSH LZHAM_Z_FULL_FLUSH + #define Z_FINISH LZHAM_Z_FINISH + #define Z_BLOCK LZHAM_Z_BLOCK + #define Z_OK LZHAM_Z_OK + #define Z_STREAM_END LZHAM_Z_STREAM_END + #define Z_NEED_DICT LZHAM_Z_NEED_DICT + #define Z_ERRNO LZHAM_Z_ERRNO + #define Z_STREAM_ERROR LZHAM_Z_STREAM_ERROR + #define Z_DATA_ERROR LZHAM_Z_DATA_ERROR + #define Z_MEM_ERROR LZHAM_Z_MEM_ERROR + #define Z_BUF_ERROR LZHAM_Z_BUF_ERROR + #define Z_VERSION_ERROR LZHAM_Z_VERSION_ERROR + #define Z_PARAM_ERROR LZHAM_Z_PARAM_ERROR + #define Z_NO_COMPRESSION LZHAM_Z_NO_COMPRESSION + #define Z_BEST_SPEED LZHAM_Z_BEST_SPEED + #define Z_BEST_COMPRESSION LZHAM_Z_BEST_COMPRESSION + #define Z_DEFAULT_COMPRESSION LZHAM_Z_DEFAULT_COMPRESSION + #define Z_DEFAULT_STRATEGY LZHAM_Z_DEFAULT_STRATEGY + #define Z_FILTERED LZHAM_Z_FILTERED + #define Z_HUFFMAN_ONLY LZHAM_Z_HUFFMAN_ONLY + #define Z_RLE LZHAM_Z_RLE + #define Z_FIXED LZHAM_Z_FIXED + #define Z_DEFLATED LZHAM_Z_DEFLATED + #define Z_DEFAULT_WINDOW_BITS LZHAM_Z_DEFAULT_WINDOW_BITS + #define alloc_func lzham_z_alloc_func + #define free_func lzham_z_free_func + #define internal_state lzham_z_internal_state + #define z_stream lzham_z_stream + #define deflateInit lzham_z_deflateInit + #define deflateInit2 lzham_z_deflateInit2 + #define deflateReset lzham_z_deflateReset + #define deflate lzham_z_deflate + #define deflateEnd lzham_z_deflateEnd + #define deflateBound lzham_z_deflateBound + #define compress lzham_z_compress + #define compress2 lzham_z_compress2 + #define compressBound lzham_z_compressBound + #define inflateInit lzham_z_inflateInit + #define inflateInit2 lzham_z_inflateInit2 + #define inflateReset lzham_z_inflateReset + #define inflate lzham_z_inflate + #define inflateEnd lzham_z_inflateEnd + #define uncompress lzham_z_uncompress + #define crc32 lzham_z_crc32 + #define adler32 lzham_z_adler32 + #define MAX_WBITS 26 + #define MAX_MEM_LEVEL 9 + #define zError lzham_z_error + #define ZLIB_VERSION LZHAM_Z_VERSION + #define ZLIB_VERNUM LZHAM_Z_VERNUM + #define ZLIB_VER_MAJOR LZHAM_Z_VER_MAJOR + #define ZLIB_VER_MINOR LZHAM_Z_VER_MINOR + #define ZLIB_VER_REVISION LZHAM_Z_VER_REVISION + #define ZLIB_VER_SUBREVISION LZHAM_Z_VER_SUBREVISION + #define zlibVersion lzham_z_version + #define zlib_version lzham_z_version() + #define Z_BINARY LZHAM_Z_BINARY + #define Z_TEXT LZHAM_Z_TEST + #define Z_ASCII LZHAM_Z_ASCII + #define Z_UNKNOWN LZHAM_Z_UNKNOWN + #endif // #ifdef LZHAM_DEFINE_ZLIB_API + + // Exported function typedefs, to simplify loading the LZHAM DLL dynamically. + typedef lzham_uint32 (LZHAM_CDECL *lzham_get_version_func)(void); + typedef void (LZHAM_CDECL *lzham_set_memory_callbacks_func)(lzham_realloc_func pRealloc, lzham_msize_func pMSize, void* pUser_data); + + typedef lzham_compress_state_ptr (LZHAM_CDECL *lzham_compress_init_func)(const lzham_compress_params *pParams); + typedef lzham_compress_state_ptr (LZHAM_CDECL *lzham_compress_reinit_func)(lzham_compress_state_ptr pState); + typedef lzham_compress_checksums* (LZHAM_CDECL *lzham_compress_deinit_func)(lzham_compress_state_ptr pState); + typedef lzham_compress_status_t (LZHAM_CDECL *lzham_compress_func)(lzham_compress_state_ptr pState, const lzham_uint8 *pIn_buf, size_t *pIn_buf_size, lzham_uint8 *pOut_buf, size_t *pOut_buf_size, lzham_bool no_more_input_bytes_flag); + typedef lzham_compress_status_t (LZHAM_CDECL *lzham_compress2_func)(lzham_compress_state_ptr pState, const lzham_uint8 *pIn_buf, size_t *pIn_buf_size, lzham_uint8 *pOut_buf, size_t *pOut_buf_size, lzham_flush_t flush_type); + typedef lzham_compress_status_t (LZHAM_CDECL *lzham_compress_memory_func)(const lzham_compress_params *pParams, lzham_uint8* pDst_buf, size_t *pDst_len, const lzham_uint8* pSrc_buf, size_t src_len, lzham_uint32 *pAdler32, lzham_uint32* pCrc32); + + typedef lzham_decompress_state_ptr (LZHAM_CDECL *lzham_decompress_init_func)(const lzham_decompress_params *pParams); + typedef lzham_decompress_state_ptr (LZHAM_CDECL *lzham_decompress_reinit_func)(lzham_compress_state_ptr pState, const lzham_decompress_params *pParams); + typedef lzham_decompress_checksums* (LZHAM_CDECL *lzham_decompress_deinit_func)(lzham_decompress_state_ptr pState); + typedef lzham_decompress_status_t (LZHAM_CDECL *lzham_decompress_func)(lzham_decompress_state_ptr pState, const lzham_uint8 *pIn_buf, size_t *pIn_buf_size, lzham_uint8 *pOut_buf, size_t *pOut_buf_size, lzham_bool no_more_input_bytes_flag); + typedef lzham_decompress_status_t (LZHAM_CDECL *lzham_decompress_memory_func)(const lzham_decompress_params *pParams, lzham_uint8* pDst_buf, size_t *pDst_len, const lzham_uint8* pSrc_buf, size_t src_len, lzham_uint32 *pAdler32, lzham_uint32 *pCrc32); + + typedef const char *(LZHAM_CDECL *lzham_z_version_func)(void); + typedef int (LZHAM_CDECL *lzham_z_deflateInit_func)(lzham_z_streamp pStream, int level); + typedef int (LZHAM_CDECL *lzham_z_deflateInit2_func)(lzham_z_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); + typedef int (LZHAM_CDECL *lzham_z_deflateReset_func)(lzham_z_streamp pStream); + typedef int (LZHAM_CDECL *lzham_z_deflate_func)(lzham_z_streamp pStream, int flush); + typedef int (LZHAM_CDECL *lzham_z_deflateEnd_func)(lzham_z_streamp pStream); + typedef lzham_z_ulong (LZHAM_CDECL *lzham_z_deflateBound_func)(lzham_z_streamp pStream, lzham_z_ulong source_len); + typedef int (LZHAM_CDECL *lzham_z_compress_func)(unsigned char *pDest, lzham_z_ulong *pDest_len, const unsigned char *pSource, lzham_z_ulong source_len); + typedef int (LZHAM_CDECL *lzham_z_compress2_func)(unsigned char *pDest, lzham_z_ulong *pDest_len, const unsigned char *pSource, lzham_z_ulong source_len, int level); + typedef lzham_z_ulong (LZHAM_CDECL *lzham_z_compressBound_func)(lzham_z_ulong source_len); + typedef int (LZHAM_CDECL *lzham_z_inflateInit_func)(lzham_z_streamp pStream); + typedef int (LZHAM_CDECL *lzham_z_inflateInit2_func)(lzham_z_streamp pStream, int window_bits); + typedef int (LZHAM_CDECL *lzham_z_inflateReset_func)(lzham_z_streamp pStream); + typedef int (LZHAM_CDECL *lzham_z_inflate_func)(lzham_z_streamp pStream, int flush); + typedef int (LZHAM_CDECL *lzham_z_inflateEnd_func)(lzham_z_streamp pStream); + typedef int (LZHAM_CDECL *lzham_z_uncompress_func)(unsigned char *pDest, lzham_z_ulong *pDest_len, const unsigned char *pSource, lzham_z_ulong source_len); + typedef const char *(LZHAM_CDECL *lzham_z_error_func)(int err); + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus +// This optional interface is used by the dynamic/static link helpers defined in lzham_dynamic_lib.h and lzham_static_lib.h. +// It allows code to always call LZHAM the same way, independent of how it was linked into the app (statically or dynamically). +class ilzham +{ + ilzham(const ilzham &other); + ilzham& operator= (const ilzham &rhs); + +public: + ilzham() { clear(); } + + virtual ~ilzham() { } + virtual bool load() = 0; + virtual void unload() = 0; + virtual bool is_loaded() = 0; + + void clear() + { + this->lzham_get_version = NULL; + this->lzham_set_memory_callbacks = NULL; + + this->lzham_compress_init = NULL; + this->lzham_compress_reinit = NULL; + this->lzham_compress_deinit = NULL; + this->lzham_compress = NULL; + this->lzham_compress2 = NULL; + this->lzham_compress_memory = NULL; + + this->lzham_decompress_init = NULL; + this->lzham_decompress_reinit = NULL; + this->lzham_decompress_deinit = NULL; + this->lzham_decompress = NULL; + this->lzham_decompress_memory = NULL; + + this->lzham_z_version = NULL; + this->lzham_z_deflateInit = NULL; + this->lzham_z_deflateInit2 = NULL; + this->lzham_z_deflateReset = NULL; + this->lzham_z_deflate = NULL; + this->lzham_z_deflateEnd = NULL; + this->lzham_z_deflateBound = NULL; + this->lzham_z_compress = NULL; + this->lzham_z_compress2 = NULL; + this->lzham_z_compressBound = NULL; + this->lzham_z_inflateInit = NULL; + this->lzham_z_inflateInit2 = NULL; + this->lzham_z_inflate = NULL; + this->lzham_z_inflateEnd = NULL; + this->lzham_z_uncompress = NULL; + this->lzham_z_error = NULL; + } + + lzham_get_version_func lzham_get_version; + lzham_set_memory_callbacks_func lzham_set_memory_callbacks; + + lzham_compress_init_func lzham_compress_init; + lzham_compress_reinit_func lzham_compress_reinit; + lzham_compress_deinit_func lzham_compress_deinit; + lzham_compress_func lzham_compress; + lzham_compress2_func lzham_compress2; + lzham_compress_memory_func lzham_compress_memory; + + lzham_decompress_init_func lzham_decompress_init; + lzham_decompress_reinit_func lzham_decompress_reinit; + lzham_decompress_deinit_func lzham_decompress_deinit; + lzham_decompress_func lzham_decompress; + lzham_decompress_memory_func lzham_decompress_memory; + + lzham_z_version_func lzham_z_version; + lzham_z_deflateInit_func lzham_z_deflateInit; + lzham_z_deflateInit2_func lzham_z_deflateInit2; + lzham_z_deflateReset_func lzham_z_deflateReset; + lzham_z_deflate_func lzham_z_deflate; + lzham_z_deflateEnd_func lzham_z_deflateEnd; + lzham_z_deflateBound_func lzham_z_deflateBound; + lzham_z_compress_func lzham_z_compress; + lzham_z_compress2_func lzham_z_compress2; + lzham_z_compressBound_func lzham_z_compressBound; + lzham_z_inflateInit_func lzham_z_inflateInit; + lzham_z_inflateInit2_func lzham_z_inflateInit2; + lzham_z_inflateReset_func lzham_z_inflateReset; + lzham_z_inflate_func lzham_z_inflate; + lzham_z_inflateEnd_func lzham_z_inflateEnd; + lzham_z_uncompress_func lzham_z_uncompress; + lzham_z_error_func lzham_z_error; +}; +#endif // #ifdef __cplusplus + +#endif // #ifndef __LZHAM_H__ + +// Copyright (c) 2009-2012 Richard Geldreich, Jr. +// +// LZHAM uses the MIT License: +// +// 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. + diff --git a/r5dev/thirdparty/lzham/include/lzham_static_lib.h b/r5dev/thirdparty/lzham/include/lzham_static_lib.h new file mode 100644 index 00000000..274ccb12 --- /dev/null +++ b/r5dev/thirdparty/lzham/include/lzham_static_lib.h @@ -0,0 +1,57 @@ +#pragma once + +#define LZHAM_STATIC_LIB 1 +#include "lzham.h" + +#ifdef __cplusplus +// Like lzham_dynamic_lib, except it sets the function pointer members to point directly to the C functions in lzhamlib +class lzham_static_lib : public ilzham +{ + lzham_static_lib(const lzham_static_lib &other); + lzham_static_lib& operator= (const lzham_static_lib &rhs); + +public: + lzham_static_lib() : ilzham() { } + + virtual ~lzham_static_lib() { } + + virtual bool load() + { + this->lzham_get_version = ::lzham_get_version; + this->lzham_set_memory_callbacks = ::lzham_set_memory_callbacks; + this->lzham_compress_init = ::lzham_compress_init; + this->lzham_compress_deinit = ::lzham_compress_deinit; + this->lzham_compress = ::lzham_compress; + this->lzham_compress2 = ::lzham_compress2; + this->lzham_compress_memory = ::lzham_compress_memory; + this->lzham_decompress_init = ::lzham_decompress_init; + this->lzham_decompress_reinit = ::lzham_decompress_reinit; + this->lzham_decompress_deinit = ::lzham_decompress_deinit; + this->lzham_decompress = ::lzham_decompress; + this->lzham_decompress_memory = ::lzham_decompress_memory; + + this->lzham_z_version = ::lzham_z_version; + this->lzham_z_deflateInit = ::lzham_z_deflateInit; + this->lzham_z_deflateInit2 = ::lzham_z_deflateInit2; + this->lzham_z_deflateReset = ::lzham_z_deflateReset; + this->lzham_z_deflate = ::lzham_z_deflate; + this->lzham_z_deflateEnd = ::lzham_z_deflateEnd; + this->lzham_z_deflateBound = ::lzham_z_deflateBound; + this->lzham_z_compress = ::lzham_z_compress; + this->lzham_z_compress2 = ::lzham_z_compress2; + this->lzham_z_compressBound = ::lzham_z_compressBound; + this->lzham_z_inflateInit = ::lzham_z_inflateInit; + this->lzham_z_inflateInit2 = ::lzham_z_inflateInit2; + this->lzham_z_inflate = ::lzham_z_inflate; + this->lzham_z_inflateEnd = ::lzham_z_inflateEnd; + this->lzham_z_uncompress = ::lzham_z_uncompress; + this->lzham_z_error = ::lzham_z_error; + + return true; + } + + virtual void unload() { clear(); } + + virtual bool is_loaded() { return lzham_get_version != NULL; } +}; +#endif // __cplusplus diff --git a/r5dev/thirdparty/lzham/libs/lzhamcomp_x64.lib b/r5dev/thirdparty/lzham/libs/lzhamcomp_x64.lib new file mode 100644 index 0000000000000000000000000000000000000000..7f5d6e43e27fe022626fa4eceaa19b0d22bb128a GIT binary patch literal 556410 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

    I9^wSmSrU2OoqvUiGEd!etOX#zCJH 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 %cpqXiOLt!L$|e21 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

    acI|{(>`c_Zp<6 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 KOaJ|(=l>VZ1acn$ literal 0 HcmV?d00001 diff --git a/r5dev/thirdparty/lzham/libs/lzhamcomp_x64D.lib b/r5dev/thirdparty/lzham/libs/lzhamcomp_x64D.lib new file mode 100644 index 0000000000000000000000000000000000000000..f593696f9eb493b9d5b596b90a93aa54870bcd90 GIT binary patch literal 824648 zcmeEv3t(MUmG()KLMVl_SZhJVP$NbJnzU(2Yi-Vb=G^4IXj3Ryy}eCtnm}IUCZ#Yw ziYZ7bAft>j%8ZPo4&&$yIt*52#+D+*$2dMv8OL$(G1@A>1IqB@W8nY3wfA}5d+sBE z^ZGsQ&f0hHwbx#I?X}l_o_)^Vn4caT*|ca?tN-cn{8WDL-`3VO7j|{76Pz833}eAg z!+6)1>vx}dn_>LU%}WN2Cp~EV&izgXzte&L-a2sNOxXCH`~Oo0i%$Ni@jLgwLk1`R zpI;gO16|MH1(^LLt+}To+46GvQb)ZjOxh ztnF!wrh3*+_%PO6{e78uB%3jrz2KyQm+o0R>cfk~&3Go2N`|eJsYq$GaepC%kG6PH zsI|pGr>8BFbQp@p%}g|riDnXM6A_8=+|cOYOs+V3O#zted)9U$w9m_ihrUAv0!U_( zk#MpM3)r)Ea=7U57s;6EY$6lti>FP5+qts9om=%UsS8ixr~!pNGt(e4Ixz~7fU(?6 zerGXICALu+Is|blKb;>hLJ_D`(}iMja@wmsrl7Vd5ls}XGPf$Pjh!pboWfLxr&dk_V$|C3cB!t3s9fy{j-IH8V3ZUC0j=&6#}h3N(<(F`L9iv2ZBC z5)rdFJ($Z6jTNTXtVXMd9Or3bh@C5~A>9EalAf)j3pYC0e0SE}h7w#WHwm_~J6L z+2WIG^+fyYlG<@`kRbiZeHeZUS{pn$1gd~1zQgo#Z>RTV5lq5|qNq?OD#HJl>`Sb3u1to<(4bo^821kp~DCALIqJWIOB<7daR+C(QdOCkK zY+4b@ouAIlTsnIw1q)84OVrbO^sM**QoR(2F!+8+YlCQ!+Md4RTQ(gX55;|4j2q#fi z^d47oebpkj%3vCu&!E%$R7Iaa&DsbytWVw!k4l;eso<(Lu;;tDy%8PxB>F6*=-H3~sN2qPZBI7{p*0UD;rMDnB?nL;G4fti<3%4FzSE z=!_l$?QwA1^b9#XqqB+Uq21yIUxwP9@ljp;%VmC!Z{Cjfn@9KAF{M}Q^=sTByhZ1;pN=~5Zl!x|ah z%@%VriU3FYAe8IJpV(?KH06wO%BaWo^CW2JK0G#C9ATanNYzZg40@fDx}|BC>A`r6_~z4 zQddNQsVe0dn;5E*vO<30$yskws!5rc`b*Q=x>h11)(i-QWBp}p+9~`@x(O=fP|BXL zGM~ks4$MDBCw57zsI@TEQQnz2BY>Wc`OibLnJ8()> zt&A_koULsfCUJar9P?y{^zPu*BkATT6(Ua2{_M;TUJ)=NU?t|# zAt)xX2gfk;tbnzR3r!^mXb!;>25mEKm1ZuIzW#+O%3`d*Nomy_oQ1Od)KmejHMk0; zgco(yhk~^mm8W#TZZn=Kif)KbGa<^* zXmQ4@f`c{6v3x}orA%0D#z6DADN1;&qbO|#R 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 KucZ4U_x}TGEpRRX diff --git a/external/minhook/include/MinHook.h b/external/minhook/include/MinHook.h deleted file mode 100644 index 492d83f8..00000000 --- a/external/minhook/include/MinHook.h +++ /dev/null @@ -1,185 +0,0 @@ -/* - * MinHook - The Minimalistic API Hooking Library for x64/x86 - * Copyright (C) 2009-2017 Tsuda Kageyu. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#if !(defined _M_IX86) && !(defined _M_X64) && !(defined __i386__) && !(defined __x86_64__) - #error MinHook supports only x86 and x64 systems. -#endif - -#include - -// MinHook Error Codes. -typedef enum MH_STATUS -{ - // Unknown error. Should not be returned. - MH_UNKNOWN = -1, - - // Successful. - MH_OK = 0, - - // MinHook is already initialized. - MH_ERROR_ALREADY_INITIALIZED, - - // MinHook is not initialized yet, or already uninitialized. - MH_ERROR_NOT_INITIALIZED, - - // The hook for the specified target function is already created. - MH_ERROR_ALREADY_CREATED, - - // The hook for the specified target function is not created yet. - MH_ERROR_NOT_CREATED, - - // The hook for the specified target function is already enabled. - MH_ERROR_ENABLED, - - // The hook for the specified target function is not enabled yet, or already - // disabled. - MH_ERROR_DISABLED, - - // The specified pointer is invalid. It points the address of non-allocated - // and/or non-executable region. - MH_ERROR_NOT_EXECUTABLE, - - // The specified target function cannot be hooked. - MH_ERROR_UNSUPPORTED_FUNCTION, - - // Failed to allocate memory. - MH_ERROR_MEMORY_ALLOC, - - // Failed to change the memory protection. - MH_ERROR_MEMORY_PROTECT, - - // The specified module is not loaded. - MH_ERROR_MODULE_NOT_FOUND, - - // The specified function is not found. - MH_ERROR_FUNCTION_NOT_FOUND -} -MH_STATUS; - -// Can be passed as a parameter to MH_EnableHook, MH_DisableHook, -// MH_QueueEnableHook or MH_QueueDisableHook. -#define MH_ALL_HOOKS NULL - -#ifdef __cplusplus -extern "C" { -#endif - - // Initialize the MinHook library. You must call this function EXACTLY ONCE - // at the beginning of your program. - MH_STATUS WINAPI MH_Initialize(VOID); - - // Uninitialize the MinHook library. You must call this function EXACTLY - // ONCE at the end of your program. - MH_STATUS WINAPI MH_Uninitialize(VOID); - - // Creates a hook for the specified target function, in disabled state. - // Parameters: - // pTarget [in] A pointer to the target function, which will be - // overridden by the detour function. - // pDetour [in] A pointer to the detour function, which will override - // the target function. - // ppOriginal [out] A pointer to the trampoline function, which will be - // used to call the original target function. - // This parameter can be NULL. - MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal); - - // Creates a hook for the specified API function, in disabled state. - // Parameters: - // pszModule [in] A pointer to the loaded module name which contains the - // target function. - // pszProcName [in] A pointer to the target function name, which will be - // overridden by the detour function. - // pDetour [in] A pointer to the detour function, which will override - // the target function. - // ppOriginal [out] A pointer to the trampoline function, which will be - // used to call the original target function. - // This parameter can be NULL. - MH_STATUS WINAPI MH_CreateHookApi( - LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal); - - // Creates a hook for the specified API function, in disabled state. - // Parameters: - // pszModule [in] A pointer to the loaded module name which contains the - // target function. - // pszProcName [in] A pointer to the target function name, which will be - // overridden by the detour function. - // pDetour [in] A pointer to the detour function, which will override - // the target function. - // ppOriginal [out] A pointer to the trampoline function, which will be - // used to call the original target function. - // This parameter can be NULL. - // ppTarget [out] A pointer to the target function, which will be used - // with other functions. - // This parameter can be NULL. - MH_STATUS WINAPI MH_CreateHookApiEx( - LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal, LPVOID *ppTarget); - - // Removes an already created hook. - // Parameters: - // pTarget [in] A pointer to the target function. - MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget); - - // Enables an already created hook. - // Parameters: - // pTarget [in] A pointer to the target function. - // If this parameter is MH_ALL_HOOKS, all created hooks are - // enabled in one go. - MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget); - - // Disables an already created hook. - // Parameters: - // pTarget [in] A pointer to the target function. - // If this parameter is MH_ALL_HOOKS, all created hooks are - // disabled in one go. - MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget); - - // Queues to enable an already created hook. - // Parameters: - // pTarget [in] A pointer to the target function. - // If this parameter is MH_ALL_HOOKS, all created hooks are - // queued to be enabled. - MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget); - - // Queues to disable an already created hook. - // Parameters: - // pTarget [in] A pointer to the target function. - // If this parameter is MH_ALL_HOOKS, all created hooks are - // queued to be disabled. - MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget); - - // Applies all queued changes in one go. - MH_STATUS WINAPI MH_ApplyQueued(VOID); - - // Translates the MH_STATUS to its name as a string. - const char * WINAPI MH_StatusToString(MH_STATUS status); - -#ifdef __cplusplus -} -#endif diff --git a/external/minhook/libMinHook.vcxproj b/external/minhook/libMinHook.vcxproj deleted file mode 100644 index e4bfd790..00000000 --- a/external/minhook/libMinHook.vcxproj +++ /dev/null @@ -1,173 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - - - - - - - - - - - - - - - - - - {F142A341-5EE0-442D-A15F-98AE9B48DBAE} - libMinHook - Win32Proj - 10.0 - Minhook - - - - StaticLibrary - Unicode - true - v142 - - - StaticLibrary - Unicode - v142 - - - StaticLibrary - Unicode - true - v142 - - - StaticLibrary - Unicode - v142 - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.40219.1 - $(SolutionDir)lib\$(Configuration)\ - $(Platform)\$(Configuration)\$(ProjectName)\ - $(ProjectDir)lib\$(Configuration) - $(SolutionDir)build\$(ProjectName)\$(Configuration) - $(SolutionDir)lib\$(Configuration)\ - $(Platform)\$(Configuration)\$(ProjectName)\ - $(ProjectDir)lib\$(Configuration) - $(SolutionDir)build\$(ProjectName)\$(Configuration) - $(ProjectName).x86 - $(ProjectName).x86 - $(ProjectName).x64 - $(ProjectName).x64 - - - - Disabled - %(AdditionalIncludeDirectories) - WIN32;_DEBUG;_LIB;STRICT;%(PreprocessorDefinitions) - false - EnableFastChecks - MultiThreadedDebug - Level3 - None - NoExtensions - - - - - - X64 - - - Disabled - %(AdditionalIncludeDirectories) - WIN32;_DEBUG;_LIB;STRICT;%(PreprocessorDefinitions) - false - EnableFastChecks - MultiThreadedDebug - Level3 - None - NotSet - stdcpp17 - Default - - - - - - MinSpace - true - %(AdditionalIncludeDirectories) - WIN32;NDEBUG;_LIB;STRICT;%(PreprocessorDefinitions) - false - MultiThreaded - true - Level3 - None - AnySuitable - true - NoExtensions - - - - - - X64 - - - MinSpace - true - %(AdditionalIncludeDirectories) - WIN32;NDEBUG;_LIB;STRICT;%(PreprocessorDefinitions) - false - MultiThreaded - true - Level3 - None - true - AnySuitable - stdcpp17 - Default - - - - - - - \ No newline at end of file diff --git a/external/minhook/libMinHook.vcxproj.filters b/external/minhook/libMinHook.vcxproj.filters deleted file mode 100644 index 76694c63..00000000 --- a/external/minhook/libMinHook.vcxproj.filters +++ /dev/null @@ -1,57 +0,0 @@ - - - - - src - - - src - - - src - - - src\hde - - - src\hde - - - - - src - - - src - - - src\hde - - - src\hde - - - src\hde - - - src\hde - - - src\hde - - - include - - - - - {62eb648f-1c53-4909-8805-77a8cdc6f408} - - - {161a8d4e-b988-418b-a31e-d089ae0e9e22} - - - {dd59bf86-2fa2-4576-bb49-7b65617b67a5} - - - \ No newline at end of file diff --git a/external/minhook/src/buffer.c b/external/minhook/src/buffer.c deleted file mode 100644 index 55412b0f..00000000 --- a/external/minhook/src/buffer.c +++ /dev/null @@ -1,312 +0,0 @@ -/* - * MinHook - The Minimalistic API Hooking Library for x64/x86 - * Copyright (C) 2009-2017 Tsuda Kageyu. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include "buffer.h" - -// Size of each memory block. (= page size of VirtualAlloc) -#define MEMORY_BLOCK_SIZE 0x1000 - -// Max range for seeking a memory block. (= 1024MB) -#define MAX_MEMORY_RANGE 0x40000000 - -// Memory protection flags to check the executable address. -#define PAGE_EXECUTE_FLAGS \ - (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) - -// Memory slot. -typedef struct _MEMORY_SLOT -{ - union - { - struct _MEMORY_SLOT *pNext; - UINT8 buffer[MEMORY_SLOT_SIZE]; - }; -} MEMORY_SLOT, *PMEMORY_SLOT; - -// Memory block info. Placed at the head of each block. -typedef struct _MEMORY_BLOCK -{ - struct _MEMORY_BLOCK *pNext; - PMEMORY_SLOT pFree; // First element of the free slot list. - UINT usedCount; -} MEMORY_BLOCK, *PMEMORY_BLOCK; - -//------------------------------------------------------------------------- -// Global Variables: -//------------------------------------------------------------------------- - -// First element of the memory block list. -PMEMORY_BLOCK g_pMemoryBlocks; - -//------------------------------------------------------------------------- -VOID InitializeBuffer(VOID) -{ - // Nothing to do for now. -} - -//------------------------------------------------------------------------- -VOID UninitializeBuffer(VOID) -{ - PMEMORY_BLOCK pBlock = g_pMemoryBlocks; - g_pMemoryBlocks = NULL; - - while (pBlock) - { - PMEMORY_BLOCK pNext = pBlock->pNext; - VirtualFree(pBlock, 0, MEM_RELEASE); - pBlock = pNext; - } -} - -//------------------------------------------------------------------------- -#if defined(_M_X64) || defined(__x86_64__) -static LPVOID FindPrevFreeRegion(LPVOID pAddress, LPVOID pMinAddr, DWORD dwAllocationGranularity) -{ - ULONG_PTR tryAddr = (ULONG_PTR)pAddress; - - // Round down to the allocation granularity. - tryAddr -= tryAddr % dwAllocationGranularity; - - // Start from the previous allocation granularity multiply. - tryAddr -= dwAllocationGranularity; - - while (tryAddr >= (ULONG_PTR)pMinAddr) - { - MEMORY_BASIC_INFORMATION mbi; - if (VirtualQuery((LPVOID)tryAddr, &mbi, sizeof(mbi)) == 0) - break; - - if (mbi.State == MEM_FREE) - return (LPVOID)tryAddr; - - if ((ULONG_PTR)mbi.AllocationBase < dwAllocationGranularity) - break; - - tryAddr = (ULONG_PTR)mbi.AllocationBase - dwAllocationGranularity; - } - - return NULL; -} -#endif - -//------------------------------------------------------------------------- -#if defined(_M_X64) || defined(__x86_64__) -static LPVOID FindNextFreeRegion(LPVOID pAddress, LPVOID pMaxAddr, DWORD dwAllocationGranularity) -{ - ULONG_PTR tryAddr = (ULONG_PTR)pAddress; - - // Round down to the allocation granularity. - tryAddr -= tryAddr % dwAllocationGranularity; - - // Start from the next allocation granularity multiply. - tryAddr += dwAllocationGranularity; - - while (tryAddr <= (ULONG_PTR)pMaxAddr) - { - MEMORY_BASIC_INFORMATION mbi; - if (VirtualQuery((LPVOID)tryAddr, &mbi, sizeof(mbi)) == 0) - break; - - if (mbi.State == MEM_FREE) - return (LPVOID)tryAddr; - - tryAddr = (ULONG_PTR)mbi.BaseAddress + mbi.RegionSize; - - // Round up to the next allocation granularity. - tryAddr += dwAllocationGranularity - 1; - tryAddr -= tryAddr % dwAllocationGranularity; - } - - return NULL; -} -#endif - -//------------------------------------------------------------------------- -static PMEMORY_BLOCK GetMemoryBlock(LPVOID pOrigin) -{ - PMEMORY_BLOCK pBlock; -#if defined(_M_X64) || defined(__x86_64__) - ULONG_PTR minAddr; - ULONG_PTR maxAddr; - - SYSTEM_INFO si; - GetSystemInfo(&si); - minAddr = (ULONG_PTR)si.lpMinimumApplicationAddress; - maxAddr = (ULONG_PTR)si.lpMaximumApplicationAddress; - - // pOrigin ± 512MB - if ((ULONG_PTR)pOrigin > MAX_MEMORY_RANGE && minAddr < (ULONG_PTR)pOrigin - MAX_MEMORY_RANGE) - minAddr = (ULONG_PTR)pOrigin - MAX_MEMORY_RANGE; - - if (maxAddr > (ULONG_PTR)pOrigin + MAX_MEMORY_RANGE) - maxAddr = (ULONG_PTR)pOrigin + MAX_MEMORY_RANGE; - - // Make room for MEMORY_BLOCK_SIZE bytes. - maxAddr -= MEMORY_BLOCK_SIZE - 1; -#endif - - // Look the registered blocks for a reachable one. - for (pBlock = g_pMemoryBlocks; pBlock != NULL; pBlock = pBlock->pNext) - { -#if defined(_M_X64) || defined(__x86_64__) - // Ignore the blocks too far. - if ((ULONG_PTR)pBlock < minAddr || (ULONG_PTR)pBlock >= maxAddr) - continue; -#endif - // The block has at least one unused slot. - if (pBlock->pFree != NULL) - return pBlock; - } - -#if defined(_M_X64) || defined(__x86_64__) - // Alloc a new block above if not found. - { - LPVOID pAlloc = pOrigin; - while ((ULONG_PTR)pAlloc >= minAddr) - { - pAlloc = FindPrevFreeRegion(pAlloc, (LPVOID)minAddr, si.dwAllocationGranularity); - if (pAlloc == NULL) - break; - - pBlock = (PMEMORY_BLOCK)VirtualAlloc( - pAlloc, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); - if (pBlock != NULL) - break; - } - } - - // Alloc a new block below if not found. - if (pBlock == NULL) - { - LPVOID pAlloc = pOrigin; - while ((ULONG_PTR)pAlloc <= maxAddr) - { - pAlloc = FindNextFreeRegion(pAlloc, (LPVOID)maxAddr, si.dwAllocationGranularity); - if (pAlloc == NULL) - break; - - pBlock = (PMEMORY_BLOCK)VirtualAlloc( - pAlloc, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); - if (pBlock != NULL) - break; - } - } -#else - // In x86 mode, a memory block can be placed anywhere. - pBlock = (PMEMORY_BLOCK)VirtualAlloc( - NULL, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); -#endif - - if (pBlock != NULL) - { - // Build a linked list of all the slots. - PMEMORY_SLOT pSlot = (PMEMORY_SLOT)pBlock + 1; - pBlock->pFree = NULL; - pBlock->usedCount = 0; - do - { - pSlot->pNext = pBlock->pFree; - pBlock->pFree = pSlot; - pSlot++; - } while ((ULONG_PTR)pSlot - (ULONG_PTR)pBlock <= MEMORY_BLOCK_SIZE - MEMORY_SLOT_SIZE); - - pBlock->pNext = g_pMemoryBlocks; - g_pMemoryBlocks = pBlock; - } - - return pBlock; -} - -//------------------------------------------------------------------------- -LPVOID AllocateBuffer(LPVOID pOrigin) -{ - PMEMORY_SLOT pSlot; - PMEMORY_BLOCK pBlock = GetMemoryBlock(pOrigin); - if (pBlock == NULL) - return NULL; - - // Remove an unused slot from the list. - pSlot = pBlock->pFree; - pBlock->pFree = pSlot->pNext; - pBlock->usedCount++; -#ifdef _DEBUG - // Fill the slot with INT3 for debugging. - memset(pSlot, 0xCC, sizeof(MEMORY_SLOT)); -#endif - return pSlot; -} - -//------------------------------------------------------------------------- -VOID FreeBuffer(LPVOID pBuffer) -{ - PMEMORY_BLOCK pBlock = g_pMemoryBlocks; - PMEMORY_BLOCK pPrev = NULL; - ULONG_PTR pTargetBlock = ((ULONG_PTR)pBuffer / MEMORY_BLOCK_SIZE) * MEMORY_BLOCK_SIZE; - - while (pBlock != NULL) - { - if ((ULONG_PTR)pBlock == pTargetBlock) - { - PMEMORY_SLOT pSlot = (PMEMORY_SLOT)pBuffer; -#ifdef _DEBUG - // Clear the released slot for debugging. - memset(pSlot, 0x00, sizeof(MEMORY_SLOT)); -#endif - // Restore the released slot to the list. - pSlot->pNext = pBlock->pFree; - pBlock->pFree = pSlot; - pBlock->usedCount--; - - // Free if unused. - if (pBlock->usedCount == 0) - { - if (pPrev) - pPrev->pNext = pBlock->pNext; - else - g_pMemoryBlocks = pBlock->pNext; - - VirtualFree(pBlock, 0, MEM_RELEASE); - } - - break; - } - - pPrev = pBlock; - pBlock = pBlock->pNext; - } -} - -//------------------------------------------------------------------------- -BOOL IsExecutableAddress(LPVOID pAddress) -{ - MEMORY_BASIC_INFORMATION mi; - VirtualQuery(pAddress, &mi, sizeof(mi)); - - return (mi.State == MEM_COMMIT && (mi.Protect & PAGE_EXECUTE_FLAGS)); -} diff --git a/external/minhook/src/buffer.h b/external/minhook/src/buffer.h deleted file mode 100644 index 204d551b..00000000 --- a/external/minhook/src/buffer.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * MinHook - The Minimalistic API Hooking Library for x64/x86 - * Copyright (C) 2009-2017 Tsuda Kageyu. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -// Size of each memory slot. -#if defined(_M_X64) || defined(__x86_64__) - #define MEMORY_SLOT_SIZE 64 -#else - #define MEMORY_SLOT_SIZE 32 -#endif - -VOID InitializeBuffer(VOID); -VOID UninitializeBuffer(VOID); -LPVOID AllocateBuffer(LPVOID pOrigin); -VOID FreeBuffer(LPVOID pBuffer); -BOOL IsExecutableAddress(LPVOID pAddress); diff --git a/external/minhook/src/hde/hde32.c b/external/minhook/src/hde/hde32.c deleted file mode 100644 index eb6af9b8..00000000 --- a/external/minhook/src/hde/hde32.c +++ /dev/null @@ -1,324 +0,0 @@ -/* - * Hacker Disassembler Engine 32 C - * Copyright (c) 2008-2009, Vyacheslav Patkov. - * All rights reserved. - * - */ - -#if defined(_M_IX86) || defined(__i386__) - -#include -#include "hde32.h" -#include "table32.h" - -unsigned int hde32_disasm(const void *code, hde32s *hs) -{ - uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0; - uint8_t *ht = hde32_table, m_mod, m_reg, m_rm, disp_size = 0; - - memset(hs, 0, sizeof(hde32s)); - - for (x = 16; x; x--) - switch (c = *p++) { - case 0xf3: - hs->p_rep = c; - pref |= PRE_F3; - break; - case 0xf2: - hs->p_rep = c; - pref |= PRE_F2; - break; - case 0xf0: - hs->p_lock = c; - pref |= PRE_LOCK; - break; - case 0x26: case 0x2e: case 0x36: - case 0x3e: case 0x64: case 0x65: - hs->p_seg = c; - pref |= PRE_SEG; - break; - case 0x66: - hs->p_66 = c; - pref |= PRE_66; - break; - case 0x67: - hs->p_67 = c; - pref |= PRE_67; - break; - default: - goto pref_done; - } - pref_done: - - hs->flags = (uint32_t)pref << 23; - - if (!pref) - pref |= PRE_NONE; - - if ((hs->opcode = c) == 0x0f) { - hs->opcode2 = c = *p++; - ht += DELTA_OPCODES; - } else if (c >= 0xa0 && c <= 0xa3) { - if (pref & PRE_67) - pref |= PRE_66; - else - pref &= ~PRE_66; - } - - opcode = c; - cflags = ht[ht[opcode / 4] + (opcode % 4)]; - - if (cflags == C_ERROR) { - hs->flags |= F_ERROR | F_ERROR_OPCODE; - cflags = 0; - if ((opcode & -3) == 0x24) - cflags++; - } - - x = 0; - if (cflags & C_GROUP) { - uint16_t t; - t = *(uint16_t *)(ht + (cflags & 0x7f)); - cflags = (uint8_t)t; - x = (uint8_t)(t >> 8); - } - - if (hs->opcode2) { - ht = hde32_table + DELTA_PREFIXES; - if (ht[ht[opcode / 4] + (opcode % 4)] & pref) - hs->flags |= F_ERROR | F_ERROR_OPCODE; - } - - if (cflags & C_MODRM) { - hs->flags |= F_MODRM; - hs->modrm = c = *p++; - hs->modrm_mod = m_mod = c >> 6; - hs->modrm_rm = m_rm = c & 7; - hs->modrm_reg = m_reg = (c & 0x3f) >> 3; - - if (x && ((x << m_reg) & 0x80)) - hs->flags |= F_ERROR | F_ERROR_OPCODE; - - if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) { - uint8_t t = opcode - 0xd9; - if (m_mod == 3) { - ht = hde32_table + DELTA_FPU_MODRM + t*8; - t = ht[m_reg] << m_rm; - } else { - ht = hde32_table + DELTA_FPU_REG; - t = ht[t] << m_reg; - } - if (t & 0x80) - hs->flags |= F_ERROR | F_ERROR_OPCODE; - } - - if (pref & PRE_LOCK) { - if (m_mod == 3) { - hs->flags |= F_ERROR | F_ERROR_LOCK; - } else { - uint8_t *table_end, op = opcode; - if (hs->opcode2) { - ht = hde32_table + DELTA_OP2_LOCK_OK; - table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK; - } else { - ht = hde32_table + DELTA_OP_LOCK_OK; - table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK; - op &= -2; - } - for (; ht != table_end; ht++) - if (*ht++ == op) { - if (!((*ht << m_reg) & 0x80)) - goto no_lock_error; - else - break; - } - hs->flags |= F_ERROR | F_ERROR_LOCK; - no_lock_error: - ; - } - } - - if (hs->opcode2) { - switch (opcode) { - case 0x20: case 0x22: - m_mod = 3; - if (m_reg > 4 || m_reg == 1) - goto error_operand; - else - goto no_error_operand; - case 0x21: case 0x23: - m_mod = 3; - if (m_reg == 4 || m_reg == 5) - goto error_operand; - else - goto no_error_operand; - } - } else { - switch (opcode) { - case 0x8c: - if (m_reg > 5) - goto error_operand; - else - goto no_error_operand; - case 0x8e: - if (m_reg == 1 || m_reg > 5) - goto error_operand; - else - goto no_error_operand; - } - } - - if (m_mod == 3) { - uint8_t *table_end; - if (hs->opcode2) { - ht = hde32_table + DELTA_OP2_ONLY_MEM; - table_end = ht + sizeof(hde32_table) - DELTA_OP2_ONLY_MEM; - } else { - ht = hde32_table + DELTA_OP_ONLY_MEM; - table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM; - } - for (; ht != table_end; ht += 2) - if (*ht++ == opcode) { - if ((*ht++ & pref) && !((*ht << m_reg) & 0x80)) - goto error_operand; - else - break; - } - goto no_error_operand; - } else if (hs->opcode2) { - switch (opcode) { - case 0x50: case 0xd7: case 0xf7: - if (pref & (PRE_NONE | PRE_66)) - goto error_operand; - break; - case 0xd6: - if (pref & (PRE_F2 | PRE_F3)) - goto error_operand; - break; - case 0xc5: - goto error_operand; - } - goto no_error_operand; - } else - goto no_error_operand; - - error_operand: - hs->flags |= F_ERROR | F_ERROR_OPERAND; - no_error_operand: - - c = *p++; - if (m_reg <= 1) { - if (opcode == 0xf6) - cflags |= C_IMM8; - else if (opcode == 0xf7) - cflags |= C_IMM_P66; - } - - switch (m_mod) { - case 0: - if (pref & PRE_67) { - if (m_rm == 6) - disp_size = 2; - } else - if (m_rm == 5) - disp_size = 4; - break; - case 1: - disp_size = 1; - break; - case 2: - disp_size = 2; - if (!(pref & PRE_67)) - disp_size <<= 1; - break; - } - - if (m_mod != 3 && m_rm == 4 && !(pref & PRE_67)) { - hs->flags |= F_SIB; - p++; - hs->sib = c; - hs->sib_scale = c >> 6; - hs->sib_index = (c & 0x3f) >> 3; - if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1)) - disp_size = 4; - } - - p--; - switch (disp_size) { - case 1: - hs->flags |= F_DISP8; - hs->disp.disp8 = *p; - break; - case 2: - hs->flags |= F_DISP16; - hs->disp.disp16 = *(uint16_t *)p; - break; - case 4: - hs->flags |= F_DISP32; - hs->disp.disp32 = *(uint32_t *)p; - break; - } - p += disp_size; - } else if (pref & PRE_LOCK) - hs->flags |= F_ERROR | F_ERROR_LOCK; - - if (cflags & C_IMM_P66) { - if (cflags & C_REL32) { - if (pref & PRE_66) { - hs->flags |= F_IMM16 | F_RELATIVE; - hs->imm.imm16 = *(uint16_t *)p; - p += 2; - goto disasm_done; - } - goto rel32_ok; - } - if (pref & PRE_66) { - hs->flags |= F_IMM16; - hs->imm.imm16 = *(uint16_t *)p; - p += 2; - } else { - hs->flags |= F_IMM32; - hs->imm.imm32 = *(uint32_t *)p; - p += 4; - } - } - - if (cflags & C_IMM16) { - if (hs->flags & F_IMM32) { - hs->flags |= F_IMM16; - hs->disp.disp16 = *(uint16_t *)p; - } else if (hs->flags & F_IMM16) { - hs->flags |= F_2IMM16; - hs->disp.disp16 = *(uint16_t *)p; - } else { - hs->flags |= F_IMM16; - hs->imm.imm16 = *(uint16_t *)p; - } - p += 2; - } - if (cflags & C_IMM8) { - hs->flags |= F_IMM8; - hs->imm.imm8 = *p++; - } - - if (cflags & C_REL32) { - rel32_ok: - hs->flags |= F_IMM32 | F_RELATIVE; - hs->imm.imm32 = *(uint32_t *)p; - p += 4; - } else if (cflags & C_REL8) { - hs->flags |= F_IMM8 | F_RELATIVE; - hs->imm.imm8 = *p++; - } - - disasm_done: - - if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) { - hs->flags |= F_ERROR | F_ERROR_LENGTH; - hs->len = 15; - } - - return (unsigned int)hs->len; -} - -#endif // defined(_M_IX86) || defined(__i386__) diff --git a/external/minhook/src/hde/hde32.h b/external/minhook/src/hde/hde32.h deleted file mode 100644 index 1112450d..00000000 --- a/external/minhook/src/hde/hde32.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Hacker Disassembler Engine 32 - * Copyright (c) 2006-2009, Vyacheslav Patkov. - * All rights reserved. - * - * hde32.h: C/C++ header file - * - */ - -#ifndef _HDE32_H_ -#define _HDE32_H_ - -/* stdint.h - C99 standard header - * http://en.wikipedia.org/wiki/stdint.h - * - * if your compiler doesn't contain "stdint.h" header (for - * example, Microsoft Visual C++), you can download file: - * http://www.azillionmonkeys.com/qed/pstdint.h - * and change next line to: - * #include "pstdint.h" - */ -#include "pstdint.h" - -#define F_MODRM 0x00000001 -#define F_SIB 0x00000002 -#define F_IMM8 0x00000004 -#define F_IMM16 0x00000008 -#define F_IMM32 0x00000010 -#define F_DISP8 0x00000020 -#define F_DISP16 0x00000040 -#define F_DISP32 0x00000080 -#define F_RELATIVE 0x00000100 -#define F_2IMM16 0x00000800 -#define F_ERROR 0x00001000 -#define F_ERROR_OPCODE 0x00002000 -#define F_ERROR_LENGTH 0x00004000 -#define F_ERROR_LOCK 0x00008000 -#define F_ERROR_OPERAND 0x00010000 -#define F_PREFIX_REPNZ 0x01000000 -#define F_PREFIX_REPX 0x02000000 -#define F_PREFIX_REP 0x03000000 -#define F_PREFIX_66 0x04000000 -#define F_PREFIX_67 0x08000000 -#define F_PREFIX_LOCK 0x10000000 -#define F_PREFIX_SEG 0x20000000 -#define F_PREFIX_ANY 0x3f000000 - -#define PREFIX_SEGMENT_CS 0x2e -#define PREFIX_SEGMENT_SS 0x36 -#define PREFIX_SEGMENT_DS 0x3e -#define PREFIX_SEGMENT_ES 0x26 -#define PREFIX_SEGMENT_FS 0x64 -#define PREFIX_SEGMENT_GS 0x65 -#define PREFIX_LOCK 0xf0 -#define PREFIX_REPNZ 0xf2 -#define PREFIX_REPX 0xf3 -#define PREFIX_OPERAND_SIZE 0x66 -#define PREFIX_ADDRESS_SIZE 0x67 - -#pragma pack(push,1) - -typedef struct { - uint8_t len; - uint8_t p_rep; - uint8_t p_lock; - uint8_t p_seg; - uint8_t p_66; - uint8_t p_67; - uint8_t opcode; - uint8_t opcode2; - uint8_t modrm; - uint8_t modrm_mod; - uint8_t modrm_reg; - uint8_t modrm_rm; - uint8_t sib; - uint8_t sib_scale; - uint8_t sib_index; - uint8_t sib_base; - union { - uint8_t imm8; - uint16_t imm16; - uint32_t imm32; - } imm; - union { - uint8_t disp8; - uint16_t disp16; - uint32_t disp32; - } disp; - uint32_t flags; -} hde32s; - -#pragma pack(pop) - -#ifdef __cplusplus -extern "C" { -#endif - -/* __cdecl */ -unsigned int hde32_disasm(const void *code, hde32s *hs); - -#ifdef __cplusplus -} -#endif - -#endif /* _HDE32_H_ */ diff --git a/external/minhook/src/hde/hde64.c b/external/minhook/src/hde/hde64.c deleted file mode 100644 index 55a702e7..00000000 --- a/external/minhook/src/hde/hde64.c +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Hacker Disassembler Engine 64 C - * Copyright (c) 2008-2009, Vyacheslav Patkov. - * All rights reserved. - * - */ - -#if defined(_M_X64) || defined(__x86_64__) - -#include -#include "hde64.h" -#include "table64.h" - -unsigned int hde64_disasm(const void *code, hde64s *hs) -{ - uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0; - uint8_t *ht = hde64_table, m_mod, m_reg, m_rm, disp_size = 0; - uint8_t op64 = 0; - - memset(hs, 0, sizeof(hde64s)); - - for (x = 16; x; x--) - switch (c = *p++) { - case 0xf3: - hs->p_rep = c; - pref |= PRE_F3; - break; - case 0xf2: - hs->p_rep = c; - pref |= PRE_F2; - break; - case 0xf0: - hs->p_lock = c; - pref |= PRE_LOCK; - break; - case 0x26: case 0x2e: case 0x36: - case 0x3e: case 0x64: case 0x65: - hs->p_seg = c; - pref |= PRE_SEG; - break; - case 0x66: - hs->p_66 = c; - pref |= PRE_66; - break; - case 0x67: - hs->p_67 = c; - pref |= PRE_67; - break; - default: - goto pref_done; - } - pref_done: - - hs->flags = (uint32_t)pref << 23; - - if (!pref) - pref |= PRE_NONE; - - if ((c & 0xf0) == 0x40) { - hs->flags |= F_PREFIX_REX; - if ((hs->rex_w = (c & 0xf) >> 3) && (*p & 0xf8) == 0xb8) - op64++; - hs->rex_r = (c & 7) >> 2; - hs->rex_x = (c & 3) >> 1; - hs->rex_b = c & 1; - if (((c = *p++) & 0xf0) == 0x40) { - opcode = c; - goto error_opcode; - } - } - - if ((hs->opcode = c) == 0x0f) { - hs->opcode2 = c = *p++; - ht += DELTA_OPCODES; - } else if (c >= 0xa0 && c <= 0xa3) { - op64++; - if (pref & PRE_67) - pref |= PRE_66; - else - pref &= ~PRE_66; - } - - opcode = c; - cflags = ht[ht[opcode / 4] + (opcode % 4)]; - - if (cflags == C_ERROR) { - error_opcode: - hs->flags |= F_ERROR | F_ERROR_OPCODE; - cflags = 0; - if ((opcode & -3) == 0x24) - cflags++; - } - - x = 0; - if (cflags & C_GROUP) { - uint16_t t; - t = *(uint16_t *)(ht + (cflags & 0x7f)); - cflags = (uint8_t)t; - x = (uint8_t)(t >> 8); - } - - if (hs->opcode2) { - ht = hde64_table + DELTA_PREFIXES; - if (ht[ht[opcode / 4] + (opcode % 4)] & pref) - hs->flags |= F_ERROR | F_ERROR_OPCODE; - } - - if (cflags & C_MODRM) { - hs->flags |= F_MODRM; - hs->modrm = c = *p++; - hs->modrm_mod = m_mod = c >> 6; - hs->modrm_rm = m_rm = c & 7; - hs->modrm_reg = m_reg = (c & 0x3f) >> 3; - - if (x && ((x << m_reg) & 0x80)) - hs->flags |= F_ERROR | F_ERROR_OPCODE; - - if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) { - uint8_t t = opcode - 0xd9; - if (m_mod == 3) { - ht = hde64_table + DELTA_FPU_MODRM + t*8; - t = ht[m_reg] << m_rm; - } else { - ht = hde64_table + DELTA_FPU_REG; - t = ht[t] << m_reg; - } - if (t & 0x80) - hs->flags |= F_ERROR | F_ERROR_OPCODE; - } - - if (pref & PRE_LOCK) { - if (m_mod == 3) { - hs->flags |= F_ERROR | F_ERROR_LOCK; - } else { - uint8_t *table_end, op = opcode; - if (hs->opcode2) { - ht = hde64_table + DELTA_OP2_LOCK_OK; - table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK; - } else { - ht = hde64_table + DELTA_OP_LOCK_OK; - table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK; - op &= -2; - } - for (; ht != table_end; ht++) - if (*ht++ == op) { - if (!((*ht << m_reg) & 0x80)) - goto no_lock_error; - else - break; - } - hs->flags |= F_ERROR | F_ERROR_LOCK; - no_lock_error: - ; - } - } - - if (hs->opcode2) { - switch (opcode) { - case 0x20: case 0x22: - m_mod = 3; - if (m_reg > 4 || m_reg == 1) - goto error_operand; - else - goto no_error_operand; - case 0x21: case 0x23: - m_mod = 3; - if (m_reg == 4 || m_reg == 5) - goto error_operand; - else - goto no_error_operand; - } - } else { - switch (opcode) { - case 0x8c: - if (m_reg > 5) - goto error_operand; - else - goto no_error_operand; - case 0x8e: - if (m_reg == 1 || m_reg > 5) - goto error_operand; - else - goto no_error_operand; - } - } - - if (m_mod == 3) { - uint8_t *table_end; - if (hs->opcode2) { - ht = hde64_table + DELTA_OP2_ONLY_MEM; - table_end = ht + sizeof(hde64_table) - DELTA_OP2_ONLY_MEM; - } else { - ht = hde64_table + DELTA_OP_ONLY_MEM; - table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM; - } - for (; ht != table_end; ht += 2) - if (*ht++ == opcode) { - if (*ht++ & pref && !((*ht << m_reg) & 0x80)) - goto error_operand; - else - break; - } - goto no_error_operand; - } else if (hs->opcode2) { - switch (opcode) { - case 0x50: case 0xd7: case 0xf7: - if (pref & (PRE_NONE | PRE_66)) - goto error_operand; - break; - case 0xd6: - if (pref & (PRE_F2 | PRE_F3)) - goto error_operand; - break; - case 0xc5: - goto error_operand; - } - goto no_error_operand; - } else - goto no_error_operand; - - error_operand: - hs->flags |= F_ERROR | F_ERROR_OPERAND; - no_error_operand: - - c = *p++; - if (m_reg <= 1) { - if (opcode == 0xf6) - cflags |= C_IMM8; - else if (opcode == 0xf7) - cflags |= C_IMM_P66; - } - - switch (m_mod) { - case 0: - if (pref & PRE_67) { - if (m_rm == 6) - disp_size = 2; - } else - if (m_rm == 5) - disp_size = 4; - break; - case 1: - disp_size = 1; - break; - case 2: - disp_size = 2; - if (!(pref & PRE_67)) - disp_size <<= 1; - } - - if (m_mod != 3 && m_rm == 4) { - hs->flags |= F_SIB; - p++; - hs->sib = c; - hs->sib_scale = c >> 6; - hs->sib_index = (c & 0x3f) >> 3; - if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1)) - disp_size = 4; - } - - p--; - switch (disp_size) { - case 1: - hs->flags |= F_DISP8; - hs->disp.disp8 = *p; - break; - case 2: - hs->flags |= F_DISP16; - hs->disp.disp16 = *(uint16_t *)p; - break; - case 4: - hs->flags |= F_DISP32; - hs->disp.disp32 = *(uint32_t *)p; - } - p += disp_size; - } else if (pref & PRE_LOCK) - hs->flags |= F_ERROR | F_ERROR_LOCK; - - if (cflags & C_IMM_P66) { - if (cflags & C_REL32) { - if (pref & PRE_66) { - hs->flags |= F_IMM16 | F_RELATIVE; - hs->imm.imm16 = *(uint16_t *)p; - p += 2; - goto disasm_done; - } - goto rel32_ok; - } - if (op64) { - hs->flags |= F_IMM64; - hs->imm.imm64 = *(uint64_t *)p; - p += 8; - } else if (!(pref & PRE_66)) { - hs->flags |= F_IMM32; - hs->imm.imm32 = *(uint32_t *)p; - p += 4; - } else - goto imm16_ok; - } - - - if (cflags & C_IMM16) { - imm16_ok: - hs->flags |= F_IMM16; - hs->imm.imm16 = *(uint16_t *)p; - p += 2; - } - if (cflags & C_IMM8) { - hs->flags |= F_IMM8; - hs->imm.imm8 = *p++; - } - - if (cflags & C_REL32) { - rel32_ok: - hs->flags |= F_IMM32 | F_RELATIVE; - hs->imm.imm32 = *(uint32_t *)p; - p += 4; - } else if (cflags & C_REL8) { - hs->flags |= F_IMM8 | F_RELATIVE; - hs->imm.imm8 = *p++; - } - - disasm_done: - - if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) { - hs->flags |= F_ERROR | F_ERROR_LENGTH; - hs->len = 15; - } - - return (unsigned int)hs->len; -} - -#endif // defined(_M_X64) || defined(__x86_64__) diff --git a/external/minhook/src/hde/hde64.h b/external/minhook/src/hde/hde64.h deleted file mode 100644 index ecbf4df9..00000000 --- a/external/minhook/src/hde/hde64.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Hacker Disassembler Engine 64 - * Copyright (c) 2008-2009, Vyacheslav Patkov. - * All rights reserved. - * - * hde64.h: C/C++ header file - * - */ - -#ifndef _HDE64_H_ -#define _HDE64_H_ - -/* stdint.h - C99 standard header - * http://en.wikipedia.org/wiki/stdint.h - * - * if your compiler doesn't contain "stdint.h" header (for - * example, Microsoft Visual C++), you can download file: - * http://www.azillionmonkeys.com/qed/pstdint.h - * and change next line to: - * #include "pstdint.h" - */ -#include "pstdint.h" - -#define F_MODRM 0x00000001 -#define F_SIB 0x00000002 -#define F_IMM8 0x00000004 -#define F_IMM16 0x00000008 -#define F_IMM32 0x00000010 -#define F_IMM64 0x00000020 -#define F_DISP8 0x00000040 -#define F_DISP16 0x00000080 -#define F_DISP32 0x00000100 -#define F_RELATIVE 0x00000200 -#define F_ERROR 0x00001000 -#define F_ERROR_OPCODE 0x00002000 -#define F_ERROR_LENGTH 0x00004000 -#define F_ERROR_LOCK 0x00008000 -#define F_ERROR_OPERAND 0x00010000 -#define F_PREFIX_REPNZ 0x01000000 -#define F_PREFIX_REPX 0x02000000 -#define F_PREFIX_REP 0x03000000 -#define F_PREFIX_66 0x04000000 -#define F_PREFIX_67 0x08000000 -#define F_PREFIX_LOCK 0x10000000 -#define F_PREFIX_SEG 0x20000000 -#define F_PREFIX_REX 0x40000000 -#define F_PREFIX_ANY 0x7f000000 - -#define PREFIX_SEGMENT_CS 0x2e -#define PREFIX_SEGMENT_SS 0x36 -#define PREFIX_SEGMENT_DS 0x3e -#define PREFIX_SEGMENT_ES 0x26 -#define PREFIX_SEGMENT_FS 0x64 -#define PREFIX_SEGMENT_GS 0x65 -#define PREFIX_LOCK 0xf0 -#define PREFIX_REPNZ 0xf2 -#define PREFIX_REPX 0xf3 -#define PREFIX_OPERAND_SIZE 0x66 -#define PREFIX_ADDRESS_SIZE 0x67 - -#pragma pack(push,1) - -typedef struct { - uint8_t len; - uint8_t p_rep; - uint8_t p_lock; - uint8_t p_seg; - uint8_t p_66; - uint8_t p_67; - uint8_t rex; - uint8_t rex_w; - uint8_t rex_r; - uint8_t rex_x; - uint8_t rex_b; - uint8_t opcode; - uint8_t opcode2; - uint8_t modrm; - uint8_t modrm_mod; - uint8_t modrm_reg; - uint8_t modrm_rm; - uint8_t sib; - uint8_t sib_scale; - uint8_t sib_index; - uint8_t sib_base; - union { - uint8_t imm8; - uint16_t imm16; - uint32_t imm32; - uint64_t imm64; - } imm; - union { - uint8_t disp8; - uint16_t disp16; - uint32_t disp32; - } disp; - uint32_t flags; -} hde64s; - -#pragma pack(pop) - -#ifdef __cplusplus -extern "C" { -#endif - -/* __cdecl */ -unsigned int hde64_disasm(const void *code, hde64s *hs); - -#ifdef __cplusplus -} -#endif - -#endif /* _HDE64_H_ */ diff --git a/external/minhook/src/hde/pstdint.h b/external/minhook/src/hde/pstdint.h deleted file mode 100644 index 84d82a01..00000000 --- a/external/minhook/src/hde/pstdint.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * MinHook - The Minimalistic API Hooking Library for x64/x86 - * Copyright (C) 2009-2017 Tsuda Kageyu. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include - -// Integer types for HDE. -typedef INT8 int8_t; -typedef INT16 int16_t; -typedef INT32 int32_t; -typedef INT64 int64_t; -typedef UINT8 uint8_t; -typedef UINT16 uint16_t; -typedef UINT32 uint32_t; -typedef UINT64 uint64_t; diff --git a/external/minhook/src/hde/table32.h b/external/minhook/src/hde/table32.h deleted file mode 100644 index 7b3e12e3..00000000 --- a/external/minhook/src/hde/table32.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Hacker Disassembler Engine 32 C - * Copyright (c) 2008-2009, Vyacheslav Patkov. - * All rights reserved. - * - */ - -#define C_NONE 0x00 -#define C_MODRM 0x01 -#define C_IMM8 0x02 -#define C_IMM16 0x04 -#define C_IMM_P66 0x10 -#define C_REL8 0x20 -#define C_REL32 0x40 -#define C_GROUP 0x80 -#define C_ERROR 0xff - -#define PRE_ANY 0x00 -#define PRE_NONE 0x01 -#define PRE_F2 0x02 -#define PRE_F3 0x04 -#define PRE_66 0x08 -#define PRE_67 0x10 -#define PRE_LOCK 0x20 -#define PRE_SEG 0x40 -#define PRE_ALL 0xff - -#define DELTA_OPCODES 0x4a -#define DELTA_FPU_REG 0xf1 -#define DELTA_FPU_MODRM 0xf8 -#define DELTA_PREFIXES 0x130 -#define DELTA_OP_LOCK_OK 0x1a1 -#define DELTA_OP2_LOCK_OK 0x1b9 -#define DELTA_OP_ONLY_MEM 0x1cb -#define DELTA_OP2_ONLY_MEM 0x1da - -unsigned char hde32_table[] = { - 0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3, - 0xa8,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xac,0xaa,0xb2,0xaa,0x9f,0x9f, - 0x9f,0x9f,0xb5,0xa3,0xa3,0xa4,0xaa,0xaa,0xba,0xaa,0x96,0xaa,0xa8,0xaa,0xc3, - 0xc3,0x96,0x96,0xb7,0xae,0xd6,0xbd,0xa3,0xc5,0xa3,0xa3,0x9f,0xc3,0x9c,0xaa, - 0xaa,0xac,0xaa,0xbf,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0x90, - 0x82,0x7d,0x97,0x59,0x59,0x59,0x59,0x59,0x7f,0x59,0x59,0x60,0x7d,0x7f,0x7f, - 0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x9a,0x88,0x7d, - 0x59,0x50,0x50,0x50,0x50,0x59,0x59,0x59,0x59,0x61,0x94,0x61,0x9e,0x59,0x59, - 0x85,0x59,0x92,0xa3,0x60,0x60,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59, - 0x59,0x59,0x9f,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xcc,0x01,0xbc,0x03,0xf0, - 0x10,0x10,0x10,0x10,0x50,0x50,0x50,0x50,0x14,0x20,0x20,0x20,0x20,0x01,0x01, - 0x01,0x01,0xc4,0x02,0x10,0x00,0x00,0x00,0x00,0x01,0x01,0xc0,0xc2,0x10,0x11, - 0x02,0x03,0x11,0x03,0x03,0x04,0x00,0x00,0x14,0x00,0x02,0x00,0x00,0xc6,0xc8, - 0x02,0x02,0x02,0x02,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xca, - 0x01,0x01,0x01,0x00,0x06,0x00,0x04,0x00,0xc0,0xc2,0x01,0x01,0x03,0x01,0xff, - 0xff,0x01,0x00,0x03,0xc4,0xc4,0xc6,0x03,0x01,0x01,0x01,0xff,0x03,0x03,0x03, - 0xc8,0x40,0x00,0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00, - 0x00,0x00,0x00,0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00, - 0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0xff,0xff,0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x7f,0x00,0x00,0xff,0x4a,0x4a,0x4a,0x4a,0x4b,0x52,0x4a,0x4a,0x4a,0x4a,0x4f, - 0x4c,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x55,0x45,0x40,0x4a,0x4a,0x4a, - 0x45,0x59,0x4d,0x46,0x4a,0x5d,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a, - 0x4a,0x4a,0x4a,0x4a,0x4a,0x61,0x63,0x67,0x4e,0x4a,0x4a,0x6b,0x6d,0x4a,0x4a, - 0x45,0x6d,0x4a,0x4a,0x44,0x45,0x4a,0x4a,0x00,0x00,0x00,0x02,0x0d,0x06,0x06, - 0x06,0x06,0x0e,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x00,0x06,0x06,0x02,0x06, - 0x00,0x0a,0x0a,0x07,0x07,0x06,0x02,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04, - 0x04,0x04,0x00,0x00,0x00,0x0e,0x05,0x06,0x06,0x06,0x01,0x06,0x00,0x00,0x08, - 0x00,0x10,0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01, - 0x86,0x00,0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba, - 0xf8,0xbb,0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00, - 0xc4,0xff,0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00, - 0x13,0x09,0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07, - 0xb2,0xff,0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf, - 0xe7,0x08,0x00,0xf0,0x02,0x00 -}; diff --git a/external/minhook/src/hde/table64.h b/external/minhook/src/hde/table64.h deleted file mode 100644 index 01d4541f..00000000 --- a/external/minhook/src/hde/table64.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Hacker Disassembler Engine 64 C - * Copyright (c) 2008-2009, Vyacheslav Patkov. - * All rights reserved. - * - */ - -#define C_NONE 0x00 -#define C_MODRM 0x01 -#define C_IMM8 0x02 -#define C_IMM16 0x04 -#define C_IMM_P66 0x10 -#define C_REL8 0x20 -#define C_REL32 0x40 -#define C_GROUP 0x80 -#define C_ERROR 0xff - -#define PRE_ANY 0x00 -#define PRE_NONE 0x01 -#define PRE_F2 0x02 -#define PRE_F3 0x04 -#define PRE_66 0x08 -#define PRE_67 0x10 -#define PRE_LOCK 0x20 -#define PRE_SEG 0x40 -#define PRE_ALL 0xff - -#define DELTA_OPCODES 0x4a -#define DELTA_FPU_REG 0xfd -#define DELTA_FPU_MODRM 0x104 -#define DELTA_PREFIXES 0x13c -#define DELTA_OP_LOCK_OK 0x1ae -#define DELTA_OP2_LOCK_OK 0x1c6 -#define DELTA_OP_ONLY_MEM 0x1d8 -#define DELTA_OP2_ONLY_MEM 0x1e7 - -unsigned char hde64_table[] = { - 0xa5,0xaa,0xa5,0xb8,0xa5,0xaa,0xa5,0xaa,0xa5,0xb8,0xa5,0xb8,0xa5,0xb8,0xa5, - 0xb8,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xac,0xc0,0xcc,0xc0,0xa1,0xa1, - 0xa1,0xa1,0xb1,0xa5,0xa5,0xa6,0xc0,0xc0,0xd7,0xda,0xe0,0xc0,0xe4,0xc0,0xea, - 0xea,0xe0,0xe0,0x98,0xc8,0xee,0xf1,0xa5,0xd3,0xa5,0xa5,0xa1,0xea,0x9e,0xc0, - 0xc0,0xc2,0xc0,0xe6,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0xab, - 0x8b,0x90,0x64,0x5b,0x5b,0x5b,0x5b,0x5b,0x92,0x5b,0x5b,0x76,0x90,0x92,0x92, - 0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x6a,0x73,0x90, - 0x5b,0x52,0x52,0x52,0x52,0x5b,0x5b,0x5b,0x5b,0x77,0x7c,0x77,0x85,0x5b,0x5b, - 0x70,0x5b,0x7a,0xaf,0x76,0x76,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b, - 0x5b,0x5b,0x86,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xd5,0x03,0xcc,0x01,0xbc, - 0x03,0xf0,0x03,0x03,0x04,0x00,0x50,0x50,0x50,0x50,0xff,0x20,0x20,0x20,0x20, - 0x01,0x01,0x01,0x01,0xc4,0x02,0x10,0xff,0xff,0xff,0x01,0x00,0x03,0x11,0xff, - 0x03,0xc4,0xc6,0xc8,0x02,0x10,0x00,0xff,0xcc,0x01,0x01,0x01,0x00,0x00,0x00, - 0x00,0x01,0x01,0x03,0x01,0xff,0xff,0xc0,0xc2,0x10,0x11,0x02,0x03,0x01,0x01, - 0x01,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x10, - 0x10,0x10,0x10,0x02,0x10,0x00,0x00,0xc6,0xc8,0x02,0x02,0x02,0x02,0x06,0x00, - 0x04,0x00,0x02,0xff,0x00,0xc0,0xc2,0x01,0x01,0x03,0x03,0x03,0xca,0x40,0x00, - 0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00,0x00,0x00,0x00, - 0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0xff,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, - 0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x00, - 0xff,0x40,0x40,0x40,0x40,0x41,0x49,0x40,0x40,0x40,0x40,0x4c,0x42,0x40,0x40, - 0x40,0x40,0x40,0x40,0x40,0x40,0x4f,0x44,0x53,0x40,0x40,0x40,0x44,0x57,0x43, - 0x5c,0x40,0x60,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, - 0x40,0x40,0x64,0x66,0x6e,0x6b,0x40,0x40,0x6a,0x46,0x40,0x40,0x44,0x46,0x40, - 0x40,0x5b,0x44,0x40,0x40,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x06,0x01,0x06, - 0x06,0x02,0x06,0x06,0x00,0x06,0x00,0x0a,0x0a,0x00,0x00,0x00,0x02,0x07,0x07, - 0x06,0x02,0x0d,0x06,0x06,0x06,0x0e,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04, - 0x04,0x04,0x05,0x06,0x06,0x06,0x00,0x00,0x00,0x0e,0x00,0x00,0x08,0x00,0x10, - 0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01,0x86,0x00, - 0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba,0xf8,0xbb, - 0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00,0xc4,0xff, - 0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00,0x13,0x09, - 0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07,0xb2,0xff, - 0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf,0xe7,0x08, - 0x00,0xf0,0x02,0x00 -}; diff --git a/external/minhook/src/hook.c b/external/minhook/src/hook.c deleted file mode 100644 index a1975893..00000000 --- a/external/minhook/src/hook.c +++ /dev/null @@ -1,906 +0,0 @@ -/* - * MinHook - The Minimalistic API Hooking Library for x64/x86 - * Copyright (C) 2009-2017 Tsuda Kageyu. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include - -#include "../include/MinHook.h" -#include "buffer.h" -#include "trampoline.h" - -#ifndef ARRAYSIZE - #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) -#endif - -// Initial capacity of the HOOK_ENTRY buffer. -#define INITIAL_HOOK_CAPACITY 32 - -// Initial capacity of the thread IDs buffer. -#define INITIAL_THREAD_CAPACITY 128 - -// Special hook position values. -#define INVALID_HOOK_POS UINT_MAX -#define ALL_HOOKS_POS UINT_MAX - -// Freeze() action argument defines. -#define ACTION_DISABLE 0 -#define ACTION_ENABLE 1 -#define ACTION_APPLY_QUEUED 2 - -// Thread access rights for suspending/resuming threads. -#define THREAD_ACCESS \ - (THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION | THREAD_SET_CONTEXT) - -// Hook information. -typedef struct _HOOK_ENTRY -{ - LPVOID pTarget; // Address of the target function. - LPVOID pDetour; // Address of the detour or relay function. - LPVOID pTrampoline; // Address of the trampoline function. - UINT8 backup[8]; // Original prologue of the target function. - - UINT8 patchAbove : 1; // Uses the hot patch area. - UINT8 isEnabled : 1; // Enabled. - UINT8 queueEnable : 1; // Queued for enabling/disabling when != isEnabled. - - UINT nIP : 4; // Count of the instruction boundaries. - UINT8 oldIPs[8]; // Instruction boundaries of the target function. - UINT8 newIPs[8]; // Instruction boundaries of the trampoline function. -} HOOK_ENTRY, *PHOOK_ENTRY; - -// Suspended threads for Freeze()/Unfreeze(). -typedef struct _FROZEN_THREADS -{ - LPDWORD pItems; // Data heap - UINT capacity; // Size of allocated data heap, items - UINT size; // Actual number of data items -} FROZEN_THREADS, *PFROZEN_THREADS; - -//------------------------------------------------------------------------- -// Global Variables: -//------------------------------------------------------------------------- - -// Spin lock flag for EnterSpinLock()/LeaveSpinLock(). -volatile LONG g_isLocked = FALSE; - -// Private heap handle. If not NULL, this library is initialized. -HANDLE g_hHeap = NULL; - -// Hook entries. -struct -{ - PHOOK_ENTRY pItems; // Data heap - UINT capacity; // Size of allocated data heap, items - UINT size; // Actual number of data items -} g_hooks; - -//------------------------------------------------------------------------- -// Returns INVALID_HOOK_POS if not found. -static UINT FindHookEntry(LPVOID pTarget) -{ - UINT i; - for (i = 0; i < g_hooks.size; ++i) - { - if ((ULONG_PTR)pTarget == (ULONG_PTR)g_hooks.pItems[i].pTarget) - return i; - } - - return INVALID_HOOK_POS; -} - -//------------------------------------------------------------------------- -static PHOOK_ENTRY AddHookEntry() -{ - if (g_hooks.pItems == NULL) - { - g_hooks.capacity = INITIAL_HOOK_CAPACITY; - g_hooks.pItems = (PHOOK_ENTRY)HeapAlloc( - g_hHeap, 0, g_hooks.capacity * sizeof(HOOK_ENTRY)); - if (g_hooks.pItems == NULL) - return NULL; - } - else if (g_hooks.size >= g_hooks.capacity) - { - PHOOK_ENTRY p = (PHOOK_ENTRY)HeapReAlloc( - g_hHeap, 0, g_hooks.pItems, (g_hooks.capacity * 2) * sizeof(HOOK_ENTRY)); - if (p == NULL) - return NULL; - - g_hooks.capacity *= 2; - g_hooks.pItems = p; - } - - return &g_hooks.pItems[g_hooks.size++]; -} - -//------------------------------------------------------------------------- -static void DeleteHookEntry(UINT pos) -{ - if (pos < g_hooks.size - 1) - g_hooks.pItems[pos] = g_hooks.pItems[g_hooks.size - 1]; - - g_hooks.size--; - - if (g_hooks.capacity / 2 >= INITIAL_HOOK_CAPACITY && g_hooks.capacity / 2 >= g_hooks.size) - { - PHOOK_ENTRY p = (PHOOK_ENTRY)HeapReAlloc( - g_hHeap, 0, g_hooks.pItems, (g_hooks.capacity / 2) * sizeof(HOOK_ENTRY)); - if (p == NULL) - return; - - g_hooks.capacity /= 2; - g_hooks.pItems = p; - } -} - -//------------------------------------------------------------------------- -static DWORD_PTR FindOldIP(PHOOK_ENTRY pHook, DWORD_PTR ip) -{ - UINT i; - - if (pHook->patchAbove && ip == ((DWORD_PTR)pHook->pTarget - sizeof(JMP_REL))) - return (DWORD_PTR)pHook->pTarget; - - for (i = 0; i < pHook->nIP; ++i) - { - if (ip == ((DWORD_PTR)pHook->pTrampoline + pHook->newIPs[i])) - return (DWORD_PTR)pHook->pTarget + pHook->oldIPs[i]; - } - -#if defined(_M_X64) || defined(__x86_64__) - // Check relay function. - if (ip == (DWORD_PTR)pHook->pDetour) - return (DWORD_PTR)pHook->pTarget; -#endif - - return 0; -} - -//------------------------------------------------------------------------- -static DWORD_PTR FindNewIP(PHOOK_ENTRY pHook, DWORD_PTR ip) -{ - UINT i; - for (i = 0; i < pHook->nIP; ++i) - { - if (ip == ((DWORD_PTR)pHook->pTarget + pHook->oldIPs[i])) - return (DWORD_PTR)pHook->pTrampoline + pHook->newIPs[i]; - } - - return 0; -} - -//------------------------------------------------------------------------- -static void ProcessThreadIPs(HANDLE hThread, UINT pos, UINT action) -{ - // If the thread suspended in the overwritten area, - // move IP to the proper address. - - CONTEXT c; -#if defined(_M_X64) || defined(__x86_64__) - DWORD64 *pIP = &c.Rip; -#else - DWORD *pIP = &c.Eip; -#endif - UINT count; - - c.ContextFlags = CONTEXT_CONTROL; - if (!GetThreadContext(hThread, &c)) - return; - - if (pos == ALL_HOOKS_POS) - { - pos = 0; - count = g_hooks.size; - } - else - { - count = pos + 1; - } - - for (; pos < count; ++pos) - { - PHOOK_ENTRY pHook = &g_hooks.pItems[pos]; - BOOL enable; - DWORD_PTR ip; - - switch (action) - { - case ACTION_DISABLE: - enable = FALSE; - break; - - case ACTION_ENABLE: - enable = TRUE; - break; - - default: // ACTION_APPLY_QUEUED - enable = pHook->queueEnable; - break; - } - if (pHook->isEnabled == enable) - continue; - - if (enable) - ip = FindNewIP(pHook, *pIP); - else - ip = FindOldIP(pHook, *pIP); - - if (ip != 0) - { - *pIP = ip; - SetThreadContext(hThread, &c); - } - } -} - -//------------------------------------------------------------------------- -static VOID EnumerateThreads(PFROZEN_THREADS pThreads) -{ - HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); - if (hSnapshot != INVALID_HANDLE_VALUE) - { - THREADENTRY32 te; - te.dwSize = sizeof(THREADENTRY32); - if (Thread32First(hSnapshot, &te)) - { - do - { - if (te.dwSize >= (FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(DWORD)) - && te.th32OwnerProcessID == GetCurrentProcessId() - && te.th32ThreadID != GetCurrentThreadId()) - { - if (pThreads->pItems == NULL) - { - pThreads->capacity = INITIAL_THREAD_CAPACITY; - pThreads->pItems - = (LPDWORD)HeapAlloc(g_hHeap, 0, pThreads->capacity * sizeof(DWORD)); - if (pThreads->pItems == NULL) - break; - } - else if (pThreads->size >= pThreads->capacity) - { - LPDWORD p = (LPDWORD)HeapReAlloc( - g_hHeap, 0, pThreads->pItems, (pThreads->capacity * 2) * sizeof(DWORD)); - if (p == NULL) - { - HeapFree(g_hHeap, 0, pThreads->pItems); - pThreads->pItems = NULL; - break; - } - - pThreads->capacity *= 2; - pThreads->pItems = p; - } - pThreads->pItems[pThreads->size++] = te.th32ThreadID; - } - - te.dwSize = sizeof(THREADENTRY32); - } while (Thread32Next(hSnapshot, &te)); - } - CloseHandle(hSnapshot); - } -} - -//------------------------------------------------------------------------- -static MH_STATUS Freeze(PFROZEN_THREADS pThreads, UINT pos, UINT action) -{ - pThreads->pItems = NULL; - pThreads->capacity = 0; - pThreads->size = 0; - EnumerateThreads(pThreads); - - MH_STATUS status = MH_OK; - - if (pThreads->pItems != NULL) - { - UINT i; - for (i = 0; i < pThreads->size; ++i) - { - HANDLE hThread = OpenThread(THREAD_ACCESS, FALSE, pThreads->pItems[i]); - if (hThread != NULL) - { - SuspendThread(hThread); - ProcessThreadIPs(hThread, pos, action); - CloseHandle(hThread); - } - } - } - else - { - status = MH_ERROR_MEMORY_ALLOC; - } - - return status; -} - -//------------------------------------------------------------------------- -static VOID Unfreeze(PFROZEN_THREADS pThreads) -{ - UINT i; - for (i = 0; i < pThreads->size; ++i) - { - HANDLE hThread = OpenThread(THREAD_ACCESS, FALSE, pThreads->pItems[i]); - if (hThread != NULL) - { - ResumeThread(hThread); - CloseHandle(hThread); - } - } - - HeapFree(g_hHeap, 0, pThreads->pItems); -} - -//------------------------------------------------------------------------- -static MH_STATUS EnableHookLL(UINT pos, BOOL enable) -{ - PHOOK_ENTRY pHook = &g_hooks.pItems[pos]; - DWORD oldProtect; - SIZE_T patchSize = sizeof(JMP_REL); - LPBYTE pPatchTarget = (LPBYTE)pHook->pTarget; - - if (pHook->patchAbove) - { - pPatchTarget -= sizeof(JMP_REL); - patchSize += sizeof(JMP_REL_SHORT); - } - - if (!VirtualProtect(pPatchTarget, patchSize, PAGE_EXECUTE_READWRITE, &oldProtect)) - return MH_ERROR_MEMORY_PROTECT; - - if (enable) - { - PJMP_REL pJmp = (PJMP_REL)pPatchTarget; - pJmp->opcode = 0xE9; - pJmp->operand = (UINT32)((LPBYTE)pHook->pDetour - (pPatchTarget + sizeof(JMP_REL))); - - if (pHook->patchAbove) - { - PJMP_REL_SHORT pShortJmp = (PJMP_REL_SHORT)pHook->pTarget; - pShortJmp->opcode = 0xEB; - pShortJmp->operand = (UINT8)(0 - (sizeof(JMP_REL_SHORT) + sizeof(JMP_REL))); - } - } - else - { - if (pHook->patchAbove) - memcpy(pPatchTarget, pHook->backup, sizeof(JMP_REL) + sizeof(JMP_REL_SHORT)); - else - memcpy(pPatchTarget, pHook->backup, sizeof(JMP_REL)); - } - - VirtualProtect(pPatchTarget, patchSize, oldProtect, &oldProtect); - - // Just-in-case measure. - FlushInstructionCache(GetCurrentProcess(), pPatchTarget, patchSize); - - pHook->isEnabled = enable; - pHook->queueEnable = enable; - - return MH_OK; -} - -//------------------------------------------------------------------------- -static MH_STATUS EnableAllHooksLL(BOOL enable) -{ - MH_STATUS status = MH_OK; - UINT i, first = INVALID_HOOK_POS; - - for (i = 0; i < g_hooks.size; ++i) - { - if (g_hooks.pItems[i].isEnabled != enable) - { - first = i; - break; - } - } - - if (first != INVALID_HOOK_POS) - { - FROZEN_THREADS threads; - status = Freeze(&threads, ALL_HOOKS_POS, enable ? ACTION_ENABLE : ACTION_DISABLE); - if (status == MH_OK) - { - for (i = first; i < g_hooks.size; ++i) - { - if (g_hooks.pItems[i].isEnabled != enable) - { - status = EnableHookLL(i, enable); - if (status != MH_OK) - break; - } - } - - Unfreeze(&threads); - } - } - - return status; -} - -//------------------------------------------------------------------------- -static VOID EnterSpinLock(VOID) -{ - SIZE_T spinCount = 0; - - // Wait until the flag is FALSE. - while (InterlockedCompareExchange(&g_isLocked, TRUE, FALSE) != FALSE) - { - // No need to generate a memory barrier here, since InterlockedCompareExchange() - // generates a full memory barrier itself. - - // Prevent the loop from being too busy. - if (spinCount < 32) - Sleep(0); - else - Sleep(1); - - spinCount++; - } -} - -//------------------------------------------------------------------------- -static VOID LeaveSpinLock(VOID) -{ - // No need to generate a memory barrier here, since InterlockedExchange() - // generates a full memory barrier itself. - - InterlockedExchange(&g_isLocked, FALSE); -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_Initialize(VOID) -{ - MH_STATUS status = MH_OK; - - EnterSpinLock(); - - if (g_hHeap == NULL) - { - g_hHeap = HeapCreate(0, 0, 0); - if (g_hHeap != NULL) - { - // Initialize the internal function buffer. - InitializeBuffer(); - } - else - { - status = MH_ERROR_MEMORY_ALLOC; - } - } - else - { - status = MH_ERROR_ALREADY_INITIALIZED; - } - - LeaveSpinLock(); - - return status; -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_Uninitialize(VOID) -{ - MH_STATUS status = MH_OK; - - EnterSpinLock(); - - if (g_hHeap != NULL) - { - status = EnableAllHooksLL(FALSE); - if (status == MH_OK) - { - // Free the internal function buffer. - - // HeapFree is actually not required, but some tools detect a false - // memory leak without HeapFree. - - UninitializeBuffer(); - - HeapFree(g_hHeap, 0, g_hooks.pItems); - HeapDestroy(g_hHeap); - - g_hHeap = NULL; - - g_hooks.pItems = NULL; - g_hooks.capacity = 0; - g_hooks.size = 0; - } - } - else - { - status = MH_ERROR_NOT_INITIALIZED; - } - - LeaveSpinLock(); - - return status; -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal) -{ - MH_STATUS status = MH_OK; - - EnterSpinLock(); - - if (g_hHeap != NULL) - { - if (IsExecutableAddress(pTarget) && IsExecutableAddress(pDetour)) - { - UINT pos = FindHookEntry(pTarget); - if (pos == INVALID_HOOK_POS) - { - LPVOID pBuffer = AllocateBuffer(pTarget); - if (pBuffer != NULL) - { - TRAMPOLINE ct; - - ct.pTarget = pTarget; - ct.pDetour = pDetour; - ct.pTrampoline = pBuffer; - if (CreateTrampolineFunction(&ct)) - { - PHOOK_ENTRY pHook = AddHookEntry(); - if (pHook != NULL) - { - pHook->pTarget = ct.pTarget; -#if defined(_M_X64) || defined(__x86_64__) - pHook->pDetour = ct.pRelay; -#else - pHook->pDetour = ct.pDetour; -#endif - pHook->pTrampoline = ct.pTrampoline; - pHook->patchAbove = ct.patchAbove; - pHook->isEnabled = FALSE; - pHook->queueEnable = FALSE; - pHook->nIP = ct.nIP; - memcpy(pHook->oldIPs, ct.oldIPs, ARRAYSIZE(ct.oldIPs)); - memcpy(pHook->newIPs, ct.newIPs, ARRAYSIZE(ct.newIPs)); - - // Back up the target function. - - if (ct.patchAbove) - { - memcpy( - pHook->backup, - (LPBYTE)pTarget - sizeof(JMP_REL), - sizeof(JMP_REL) + sizeof(JMP_REL_SHORT)); - } - else - { - memcpy(pHook->backup, pTarget, sizeof(JMP_REL)); - } - - if (ppOriginal != NULL) - *ppOriginal = pHook->pTrampoline; - } - else - { - status = MH_ERROR_MEMORY_ALLOC; - } - } - else - { - status = MH_ERROR_UNSUPPORTED_FUNCTION; - } - - if (status != MH_OK) - { - FreeBuffer(pBuffer); - } - } - else - { - status = MH_ERROR_MEMORY_ALLOC; - } - } - else - { - status = MH_ERROR_ALREADY_CREATED; - } - } - else - { - status = MH_ERROR_NOT_EXECUTABLE; - } - } - else - { - status = MH_ERROR_NOT_INITIALIZED; - } - - LeaveSpinLock(); - - return status; -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget) -{ - MH_STATUS status = MH_OK; - - EnterSpinLock(); - - if (g_hHeap != NULL) - { - UINT pos = FindHookEntry(pTarget); - if (pos != INVALID_HOOK_POS) - { - if (g_hooks.pItems[pos].isEnabled) - { - FROZEN_THREADS threads; - status = Freeze(&threads, pos, ACTION_DISABLE); - if (status == MH_OK) - { - status = EnableHookLL(pos, FALSE); - - Unfreeze(&threads); - } - } - - if (status == MH_OK) - { - FreeBuffer(g_hooks.pItems[pos].pTrampoline); - DeleteHookEntry(pos); - } - } - else - { - status = MH_ERROR_NOT_CREATED; - } - } - else - { - status = MH_ERROR_NOT_INITIALIZED; - } - - LeaveSpinLock(); - - return status; -} - -//------------------------------------------------------------------------- -static MH_STATUS EnableHook(LPVOID pTarget, BOOL enable) -{ - MH_STATUS status = MH_OK; - - EnterSpinLock(); - - if (g_hHeap != NULL) - { - if (pTarget == MH_ALL_HOOKS) - { - status = EnableAllHooksLL(enable); - } - else - { - UINT pos = FindHookEntry(pTarget); - if (pos != INVALID_HOOK_POS) - { - if (g_hooks.pItems[pos].isEnabled != enable) - { - FROZEN_THREADS threads; - status = Freeze(&threads, pos, ACTION_ENABLE); - if (status == MH_OK) - { - status = EnableHookLL(pos, enable); - - Unfreeze(&threads); - } - } - else - { - status = enable ? MH_ERROR_ENABLED : MH_ERROR_DISABLED; - } - } - else - { - status = MH_ERROR_NOT_CREATED; - } - } - } - else - { - status = MH_ERROR_NOT_INITIALIZED; - } - - LeaveSpinLock(); - - return status; -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget) -{ - return EnableHook(pTarget, TRUE); -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget) -{ - return EnableHook(pTarget, FALSE); -} - -//------------------------------------------------------------------------- -static MH_STATUS QueueHook(LPVOID pTarget, BOOL queueEnable) -{ - MH_STATUS status = MH_OK; - - EnterSpinLock(); - - if (g_hHeap != NULL) - { - if (pTarget == MH_ALL_HOOKS) - { - UINT i; - for (i = 0; i < g_hooks.size; ++i) - g_hooks.pItems[i].queueEnable = queueEnable; - } - else - { - UINT pos = FindHookEntry(pTarget); - if (pos != INVALID_HOOK_POS) - { - g_hooks.pItems[pos].queueEnable = queueEnable; - } - else - { - status = MH_ERROR_NOT_CREATED; - } - } - } - else - { - status = MH_ERROR_NOT_INITIALIZED; - } - - LeaveSpinLock(); - - return status; -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget) -{ - return QueueHook(pTarget, TRUE); -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget) -{ - return QueueHook(pTarget, FALSE); -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_ApplyQueued(VOID) -{ - MH_STATUS status = MH_OK; - UINT i, first = INVALID_HOOK_POS; - - EnterSpinLock(); - - if (g_hHeap != NULL) - { - for (i = 0; i < g_hooks.size; ++i) - { - if (g_hooks.pItems[i].isEnabled != g_hooks.pItems[i].queueEnable) - { - first = i; - break; - } - } - - if (first != INVALID_HOOK_POS) - { - FROZEN_THREADS threads; - status = Freeze(&threads, ALL_HOOKS_POS, ACTION_APPLY_QUEUED); - if (status == MH_OK) - { - for (i = first; i < g_hooks.size; ++i) - { - PHOOK_ENTRY pHook = &g_hooks.pItems[i]; - if (pHook->isEnabled != pHook->queueEnable) - { - status = EnableHookLL(i, pHook->queueEnable); - if (status != MH_OK) - break; - } - } - - Unfreeze(&threads); - } - } - } - else - { - status = MH_ERROR_NOT_INITIALIZED; - } - - LeaveSpinLock(); - - return status; -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_CreateHookApiEx( - LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, - LPVOID *ppOriginal, LPVOID *ppTarget) -{ - HMODULE hModule; - LPVOID pTarget; - - hModule = GetModuleHandleW(pszModule); - if (hModule == NULL) - return MH_ERROR_MODULE_NOT_FOUND; - - pTarget = (LPVOID)GetProcAddress(hModule, pszProcName); - if (pTarget == NULL) - return MH_ERROR_FUNCTION_NOT_FOUND; - - if(ppTarget != NULL) - *ppTarget = pTarget; - - return MH_CreateHook(pTarget, pDetour, ppOriginal); -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_CreateHookApi( - LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal) -{ - return MH_CreateHookApiEx(pszModule, pszProcName, pDetour, ppOriginal, NULL); -} - -//------------------------------------------------------------------------- -const char * WINAPI MH_StatusToString(MH_STATUS status) -{ -#define MH_ST2STR(x) \ - case x: \ - return #x; - - switch (status) { - MH_ST2STR(MH_UNKNOWN) - MH_ST2STR(MH_OK) - MH_ST2STR(MH_ERROR_ALREADY_INITIALIZED) - MH_ST2STR(MH_ERROR_NOT_INITIALIZED) - MH_ST2STR(MH_ERROR_ALREADY_CREATED) - MH_ST2STR(MH_ERROR_NOT_CREATED) - MH_ST2STR(MH_ERROR_ENABLED) - MH_ST2STR(MH_ERROR_DISABLED) - MH_ST2STR(MH_ERROR_NOT_EXECUTABLE) - MH_ST2STR(MH_ERROR_UNSUPPORTED_FUNCTION) - MH_ST2STR(MH_ERROR_MEMORY_ALLOC) - MH_ST2STR(MH_ERROR_MEMORY_PROTECT) - MH_ST2STR(MH_ERROR_MODULE_NOT_FOUND) - MH_ST2STR(MH_ERROR_FUNCTION_NOT_FOUND) - } - -#undef MH_ST2STR - - return "(unknown)"; -} diff --git a/external/minhook/src/trampoline.c b/external/minhook/src/trampoline.c deleted file mode 100644 index 617baf3f..00000000 --- a/external/minhook/src/trampoline.c +++ /dev/null @@ -1,320 +0,0 @@ -/* - * MinHook - The Minimalistic API Hooking Library for x64/x86 - * Copyright (C) 2009-2017 Tsuda Kageyu. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include - -#ifdef _MSC_VER - #include -#endif - -#ifndef ARRAYSIZE - #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) -#endif - -#if defined(_M_X64) || defined(__x86_64__) - #include "./hde/hde64.h" - typedef hde64s HDE; - #define HDE_DISASM(code, hs) hde64_disasm(code, hs) -#else - #include "./hde/hde32.h" - typedef hde32s HDE; - #define HDE_DISASM(code, hs) hde32_disasm(code, hs) -#endif - -#include "trampoline.h" -#include "buffer.h" - -// Maximum size of a trampoline function. -#if defined(_M_X64) || defined(__x86_64__) - #define TRAMPOLINE_MAX_SIZE (MEMORY_SLOT_SIZE - sizeof(JMP_ABS)) -#else - #define TRAMPOLINE_MAX_SIZE MEMORY_SLOT_SIZE -#endif - -//------------------------------------------------------------------------- -static BOOL IsCodePadding(LPBYTE pInst, UINT size) -{ - UINT i; - - if (pInst[0] != 0x00 && pInst[0] != 0x90 && pInst[0] != 0xCC) - return FALSE; - - for (i = 1; i < size; ++i) - { - if (pInst[i] != pInst[0]) - return FALSE; - } - return TRUE; -} - -//------------------------------------------------------------------------- -BOOL CreateTrampolineFunction(PTRAMPOLINE ct) -{ -#if defined(_M_X64) || defined(__x86_64__) - CALL_ABS call = { - 0xFF, 0x15, 0x00000002, // FF15 00000002: CALL [RIP+8] - 0xEB, 0x08, // EB 08: JMP +10 - 0x0000000000000000ULL // Absolute destination address - }; - JMP_ABS jmp = { - 0xFF, 0x25, 0x00000000, // FF25 00000000: JMP [RIP+6] - 0x0000000000000000ULL // Absolute destination address - }; - JCC_ABS jcc = { - 0x70, 0x0E, // 7* 0E: J** +16 - 0xFF, 0x25, 0x00000000, // FF25 00000000: JMP [RIP+6] - 0x0000000000000000ULL // Absolute destination address - }; -#else - CALL_REL call = { - 0xE8, // E8 xxxxxxxx: CALL +5+xxxxxxxx - 0x00000000 // Relative destination address - }; - JMP_REL jmp = { - 0xE9, // E9 xxxxxxxx: JMP +5+xxxxxxxx - 0x00000000 // Relative destination address - }; - JCC_REL jcc = { - 0x0F, 0x80, // 0F8* xxxxxxxx: J** +6+xxxxxxxx - 0x00000000 // Relative destination address - }; -#endif - - UINT8 oldPos = 0; - UINT8 newPos = 0; - ULONG_PTR jmpDest = 0; // Destination address of an internal jump. - BOOL finished = FALSE; // Is the function completed? -#if defined(_M_X64) || defined(__x86_64__) - UINT8 instBuf[16]; -#endif - - ct->patchAbove = FALSE; - ct->nIP = 0; - - do - { - HDE hs; - UINT copySize; - LPVOID pCopySrc; - ULONG_PTR pOldInst = (ULONG_PTR)ct->pTarget + oldPos; - ULONG_PTR pNewInst = (ULONG_PTR)ct->pTrampoline + newPos; - - copySize = HDE_DISASM((LPVOID)pOldInst, &hs); - if (hs.flags & F_ERROR) - return FALSE; - - pCopySrc = (LPVOID)pOldInst; - if (oldPos >= sizeof(JMP_REL)) - { - // The trampoline function is long enough. - // Complete the function with the jump to the target function. -#if defined(_M_X64) || defined(__x86_64__) - jmp.address = pOldInst; -#else - jmp.operand = (UINT32)(pOldInst - (pNewInst + sizeof(jmp))); -#endif - pCopySrc = &jmp; - copySize = sizeof(jmp); - - finished = TRUE; - } -#if defined(_M_X64) || defined(__x86_64__) - else if ((hs.modrm & 0xC7) == 0x05) - { - // Instructions using RIP relative addressing. (ModR/M = 00???101B) - - // Modify the RIP relative address. - PUINT32 pRelAddr; - - // Avoid using memcpy to reduce the footprint. -#ifndef _MSC_VER - memcpy(instBuf, (LPBYTE)pOldInst, copySize); -#else - __movsb(instBuf, (LPBYTE)pOldInst, copySize); -#endif - pCopySrc = instBuf; - - // Relative address is stored at (instruction length - immediate value length - 4). - pRelAddr = (PUINT32)(instBuf + hs.len - ((hs.flags & 0x3C) >> 2) - 4); - *pRelAddr - = (UINT32)((pOldInst + hs.len + (INT32)hs.disp.disp32) - (pNewInst + hs.len)); - - // Complete the function if JMP (FF /4). - if (hs.opcode == 0xFF && hs.modrm_reg == 4) - finished = TRUE; - } -#endif - else if (hs.opcode == 0xE8) - { - // Direct relative CALL - ULONG_PTR dest = pOldInst + hs.len + (INT32)hs.imm.imm32; -#if defined(_M_X64) || defined(__x86_64__) - call.address = dest; -#else - call.operand = (UINT32)(dest - (pNewInst + sizeof(call))); -#endif - pCopySrc = &call; - copySize = sizeof(call); - } - else if ((hs.opcode & 0xFD) == 0xE9) - { - // Direct relative JMP (EB or E9) - ULONG_PTR dest = pOldInst + hs.len; - - if (hs.opcode == 0xEB) // isShort jmp - dest += (INT8)hs.imm.imm8; - else - dest += (INT32)hs.imm.imm32; - - // Simply copy an internal jump. - if ((ULONG_PTR)ct->pTarget <= dest - && dest < ((ULONG_PTR)ct->pTarget + sizeof(JMP_REL))) - { - if (jmpDest < dest) - jmpDest = dest; - } - else - { -#if defined(_M_X64) || defined(__x86_64__) - jmp.address = dest; -#else - jmp.operand = (UINT32)(dest - (pNewInst + sizeof(jmp))); -#endif - pCopySrc = &jmp; - copySize = sizeof(jmp); - - // Exit the function if it is not in the branch. - finished = (pOldInst >= jmpDest); - } - } - else if ((hs.opcode & 0xF0) == 0x70 - || (hs.opcode & 0xFC) == 0xE0 - || (hs.opcode2 & 0xF0) == 0x80) - { - // Direct relative Jcc - ULONG_PTR dest = pOldInst + hs.len; - - if ((hs.opcode & 0xF0) == 0x70 // Jcc - || (hs.opcode & 0xFC) == 0xE0) // LOOPNZ/LOOPZ/LOOP/JECXZ - dest += (INT8)hs.imm.imm8; - else - dest += (INT32)hs.imm.imm32; - - // Simply copy an internal jump. - if ((ULONG_PTR)ct->pTarget <= dest - && dest < ((ULONG_PTR)ct->pTarget + sizeof(JMP_REL))) - { - if (jmpDest < dest) - jmpDest = dest; - } - else if ((hs.opcode & 0xFC) == 0xE0) - { - // LOOPNZ/LOOPZ/LOOP/JCXZ/JECXZ to the outside are not supported. - return FALSE; - } - else - { - UINT8 cond = ((hs.opcode != 0x0F ? hs.opcode : hs.opcode2) & 0x0F); -#if defined(_M_X64) || defined(__x86_64__) - // Invert the condition in x64 mode to simplify the conditional jump logic. - jcc.opcode = 0x71 ^ cond; - jcc.address = dest; -#else - jcc.opcode1 = 0x80 | cond; - jcc.operand = (UINT32)(dest - (pNewInst + sizeof(jcc))); -#endif - pCopySrc = &jcc; - copySize = sizeof(jcc); - } - } - else if ((hs.opcode & 0xFE) == 0xC2) - { - // RET (C2 or C3) - - // Complete the function if not in a branch. - finished = (pOldInst >= jmpDest); - } - - // Can't alter the instruction length in a branch. - if (pOldInst < jmpDest && copySize != hs.len) - return FALSE; - - // Trampoline function is too large. - if ((newPos + copySize) > TRAMPOLINE_MAX_SIZE) - return FALSE; - - // Trampoline function has too many instructions. - if (ct->nIP >= ARRAYSIZE(ct->oldIPs)) - return FALSE; - - ct->oldIPs[ct->nIP] = oldPos; - ct->newIPs[ct->nIP] = newPos; - ct->nIP++; - - // Avoid using memcpy to reduce the footprint. -#ifndef _MSC_VER - memcpy((LPBYTE)ct->pTrampoline + newPos, pCopySrc, copySize); -#else - __movsb((LPBYTE)ct->pTrampoline + newPos, (LPBYTE)pCopySrc, copySize); -#endif - newPos += copySize; - oldPos += hs.len; - } - while (!finished); - - // Is there enough place for a long jump? - if (oldPos < sizeof(JMP_REL) - && !IsCodePadding((LPBYTE)ct->pTarget + oldPos, sizeof(JMP_REL) - oldPos)) - { - // Is there enough place for a short jump? - if (oldPos < sizeof(JMP_REL_SHORT) - && !IsCodePadding((LPBYTE)ct->pTarget + oldPos, sizeof(JMP_REL_SHORT) - oldPos)) - { - return FALSE; - } - - // Can we place the long jump above the function? - if (!IsExecutableAddress((LPBYTE)ct->pTarget - sizeof(JMP_REL))) - return FALSE; - - if (!IsCodePadding((LPBYTE)ct->pTarget - sizeof(JMP_REL), sizeof(JMP_REL))) - return FALSE; - - ct->patchAbove = TRUE; - } - -#if defined(_M_X64) || defined(__x86_64__) - // Create a relay function. - jmp.address = (ULONG_PTR)ct->pDetour; - - ct->pRelay = (LPBYTE)ct->pTrampoline + newPos; - memcpy(ct->pRelay, &jmp, sizeof(jmp)); -#endif - - return TRUE; -} diff --git a/external/minhook/src/trampoline.h b/external/minhook/src/trampoline.h deleted file mode 100644 index bdffdac0..00000000 --- a/external/minhook/src/trampoline.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * MinHook - The Minimalistic API Hooking Library for x64/x86 - * Copyright (C) 2009-2017 Tsuda Kageyu. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#pragma pack(push, 1) - -// Structs for writing x86/x64 instructions. - -// 8-bit relative jump. -typedef struct _JMP_REL_SHORT -{ - UINT8 opcode; // EB xx: JMP +2+xx - UINT8 operand; -} JMP_REL_SHORT, *PJMP_REL_SHORT; - -// 32-bit direct relative jump/call. -typedef struct _JMP_REL -{ - UINT8 opcode; // E9/E8 xxxxxxxx: JMP/CALL +5+xxxxxxxx - UINT32 operand; // Relative destination address -} JMP_REL, *PJMP_REL, CALL_REL; - -// 64-bit indirect absolute jump. -typedef struct _JMP_ABS -{ - UINT8 opcode0; // FF25 00000000: JMP [+6] - UINT8 opcode1; - UINT32 dummy; - UINT64 address; // Absolute destination address -} JMP_ABS, *PJMP_ABS; - -// 64-bit indirect absolute call. -typedef struct _CALL_ABS -{ - UINT8 opcode0; // FF15 00000002: CALL [+6] - UINT8 opcode1; - UINT32 dummy0; - UINT8 dummy1; // EB 08: JMP +10 - UINT8 dummy2; - UINT64 address; // Absolute destination address -} CALL_ABS; - -// 32-bit direct relative conditional jumps. -typedef struct _JCC_REL -{ - UINT8 opcode0; // 0F8* xxxxxxxx: J** +6+xxxxxxxx - UINT8 opcode1; - UINT32 operand; // Relative destination address -} JCC_REL; - -// 64bit indirect absolute conditional jumps that x64 lacks. -typedef struct _JCC_ABS -{ - UINT8 opcode; // 7* 0E: J** +16 - UINT8 dummy0; - UINT8 dummy1; // FF25 00000000: JMP [+6] - UINT8 dummy2; - UINT32 dummy3; - UINT64 address; // Absolute destination address -} JCC_ABS; - -#pragma pack(pop) - -typedef struct _TRAMPOLINE -{ - LPVOID pTarget; // [In] Address of the target function. - LPVOID pDetour; // [In] Address of the detour function. - LPVOID pTrampoline; // [In] Buffer address for the trampoline and relay function. - -#if defined(_M_X64) || defined(__x86_64__) - LPVOID pRelay; // [Out] Address of the relay function. -#endif - BOOL patchAbove; // [Out] Should use the hot patch area? - UINT nIP; // [Out] Number of the instruction boundaries. - UINT8 oldIPs[8]; // [Out] Instruction boundaries of the target function. - UINT8 newIPs[8]; // [Out] Instruction boundaries of the trampoline function. -} TRAMPOLINE, *PTRAMPOLINE; - -BOOL CreateTrampolineFunction(PTRAMPOLINE ct); diff --git a/license/lzham/LICENSE.txt b/license/lzham/LICENSE.txt new file mode 100644 index 00000000..94cf6adc --- /dev/null +++ b/license/lzham/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2009-2011 Richard Geldreich, Jr. + +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. diff --git a/license/minhook/AUTHORS.txt b/license/minhook/AUTHORS.txt deleted file mode 100644 index ebef1a6c..00000000 --- a/license/minhook/AUTHORS.txt +++ /dev/null @@ -1,8 +0,0 @@ -Tsuda Kageyu - Creator, maintainer - -Michael Maltsev - Added "Queue" functions. A lot of bug fixes. - -Andrey Unis - Rewrote the hook engine in plain C. diff --git a/license/minhook/LICENSE.txt b/license/minhook/LICENSE.txt deleted file mode 100644 index 74dea272..00000000 --- a/license/minhook/LICENSE.txt +++ /dev/null @@ -1,81 +0,0 @@ -MinHook - The Minimalistic API Hooking Library for x64/x86 -Copyright (C) 2009-2017 Tsuda Kageyu. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER -OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -================================================================================ -Portions of this software are Copyright (c) 2008-2009, Vyacheslav Patkov. -================================================================================ -Hacker Disassembler Engine 32 C -Copyright (c) 2008-2009, Vyacheslav Patkov. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -------------------------------------------------------------------------------- -Hacker Disassembler Engine 64 C -Copyright (c) 2008-2009, Vyacheslav Patkov. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/r5dedicated/console.cpp b/r5dedicated/console.cpp deleted file mode 100644 index 6cdc996a..00000000 --- a/r5dedicated/console.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include "pch.h" -#include "hooks.h" -#include "opcodes.h" -#include "console.h" - -//############################################################################# -// INITIALIZATION -//############################################################################# - -void SetupConsole() -{ - /////////////////////////////////////////////////////////////////////////// - // Create the console window - if (AllocConsole() == FALSE) - { - OutputDebugString("Failed to create console window!\n"); - return; - } - - /////////////////////////////////////////////////////////////////////////// - // Set the window title - FILE* sBuildTxt; - CHAR sBuildBuf[1024] = { 0 }; - - fopen_s(&sBuildTxt, "build.txt", "r"); - if (sBuildTxt) - { - while (fgets(sBuildBuf, sizeof(sBuildBuf), sBuildTxt) != NULL) - { - fclose(sBuildTxt); - } - } - SetConsoleTitle(sBuildBuf); - - /////////////////////////////////////////////////////////////////////////// - // Open input/output streams - FILE* fDummy; - freopen_s(&fDummy, "CONIN$", "r", stdin); - freopen_s(&fDummy, "CONOUT$", "w", stdout); - freopen_s(&fDummy, "CONOUT$", "w", stderr); - - /////////////////////////////////////////////////////////////////////////// - // Create a worker thread to process console commands - DWORD threadId0; - DWORD __stdcall ProcessConsoleWorker(LPVOID); - HANDLE hThread0 = CreateThread(NULL, 0, ProcessConsoleWorker, NULL, 0, &threadId0); - - if (hThread0) - { - printf("THREAD ID: %ld\n\n", threadId0); - CloseHandle(hThread0); - } -} - -//############################################################################# -// WORKER THREAD -//############################################################################# - -DWORD __stdcall ProcessConsoleWorker(LPVOID) -{ - // Loop forever - while (true) - { - std::string sCommand; - - /////////////////////////////////////////////////////////////////////// - // Get the user input on the debug console - printf(">"); - std::getline(std::cin, sCommand); - - /////////////////////////////////////////////////////////////////////// - // Engine toggles - if (sCommand == "toggle net") { Hooks::ToggleNetTrace(); continue; } - if (sCommand == "toggle dev") { Hooks::ToggleDevCommands(); continue; } - if (sCommand == "toggle fal") { g_bReturnAllFalse = !g_bReturnAllFalse; continue; } - /////////////////////////////////////////////////////////////////////// - // Debug toggles - if (sCommand == "pattern test") { PrintHAddress(); /*PrintOAddress();*/ continue; } - if (sCommand == "console test") { g_bDebugConsole = !g_bDebugConsole; continue; } - /////////////////////////////////////////////////////////////////////// - // Exec toggles - if (sCommand == "1") { addr_CommandExecute(NULL, "exec autoexec_dev"); } - if (sCommand == "2") { g_bDebugLoading = !g_bDebugLoading; continue; } - if (sCommand == "3") { SetCHostState(); continue; } // TEST - - /////////////////////////////////////////////////////////////////////// - // Execute the command in the r5 SQVM - addr_CommandExecute(NULL, sCommand.c_str()); - sCommand.clear(); - - /////////////////////////////////////////////////////////////////////// - // Sleep and loop - Sleep(50); - } - - return 0; -} \ No newline at end of file diff --git a/r5dedicated/console.h b/r5dedicated/console.h deleted file mode 100644 index 19d16a6b..00000000 --- a/r5dedicated/console.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once -#include "pch.h" - -void SetupConsole(); -void ToggleDevCommands(); diff --git a/r5dedicated/csourceappsystemgroup.cpp b/r5dedicated/csourceappsystemgroup.cpp deleted file mode 100644 index bffa7cc3..00000000 --- a/r5dedicated/csourceappsystemgroup.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -namespace Hooks -{ - CSourceAppSystemGroup_CreateFn originalCSourceAppSystemGroup_Create = nullptr; -} -auto g_bIsDedicated = (uint8_t*)0x162C61208; - -//----------------------------------------------------------------------------- -// Purpose: sets 'EbisuSDK' globals required in certain engine callbacks. -//----------------------------------------------------------------------------- -void HEbisuSDK_Init() -{ - auto ofs000 = (uint8_t*)0x1634F1690; - auto ofs001 = (uint8_t*)0x1634F16B0; - auto ofs002 = (uint8_t*)0x1634F1695; - auto ofs003 = (uint8_t*)0x1634F30D8; - auto ofs004 = (uint8_t*)0x1634F31D8; - - *(char*)(ofs000) = (char)0x1; // <-- | 1st EbisuSDK boolean to be checked. - *(char*)(ofs001) = (char)0x1; // <-- | 2nd EbisuSDK boolean to be checked. - *(char*)(ofs002) = (char)0x1; // <-- | 3rd EbisuSDK boolean to be checked. - *(char*)(ofs003) = (char)0x1; // <-- | Gets tested on listenserver for certain ConCommands. - *(char*)(ofs004) = (char)0x0; // <-- | TODO: enforces Necleus cvars when not equal to NULL. -} - -//----------------------------------------------------------------------------- -// Purpose: hook 'SourceAppSystemGroup::Create' and set m_bIsDedicated to true. -//----------------------------------------------------------------------------- -char __fastcall Hooks::CSourceAppSystemGroup_Create(__int64 a1) -{ - *g_bIsDedicated = 1; // HAS TO BE HERE!!! - HEbisuSDK_Init(); - return originalCSourceAppSystemGroup_Create(a1); -} \ No newline at end of file diff --git a/r5dedicated/cvengineserver.cpp b/r5dedicated/cvengineserver.cpp deleted file mode 100644 index 450639f4..00000000 --- a/r5dedicated/cvengineserver.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -namespace Hooks -{ - IsPersistenceDataAvailableFn originalIsPersistenceDataAvailable = nullptr; -} - -//----------------------------------------------------------------------------- -// Sets the persistence var in the playerstruct to ready for each client -//----------------------------------------------------------------------------- -bool Hooks::IsPersistenceDataAvailable(__int64 thisptr, int client) -{ - static bool isPersistenceVarSet[256]; - - // TODO: Maybe not hardcode - std::uintptr_t playerStructBase = 0x16073B200; - std::uintptr_t playerStructSize = 0x4A4C0; - std::uintptr_t persistenceVar = 0x5BC; - - std::uintptr_t targetPlayerStruct = playerStructBase + client * playerStructSize; - - *(char*)(targetPlayerStruct + persistenceVar) = (char)0x5; - - if (!isPersistenceVarSet[client]) - { - printf("\n"); - printf("##################################################\n"); - printf("] SETTING PERSISTENCE VAR FOR CLIENT #%d\n", client); - printf("##################################################\n"); - printf("\n"); - isPersistenceVarSet[client] = true; - } - - return originalIsPersistenceDataAvailable(thisptr, client); -} \ No newline at end of file diff --git a/r5dedicated/dllmain.cpp b/r5dedicated/dllmain.cpp deleted file mode 100644 index 4dc7cc24..00000000 --- a/r5dedicated/dllmain.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "pch.h" -#include "dllmain.h" -#include "hooks.h" -#include "console.h" - -//############################################################################# -// INITIALIZATION -//############################################################################# - -void InitializeR5Dedicated() -{ - SetupConsole(); - Hooks::InstallHooks(); - Hooks::DedicatedPatch(); - printf("+-----------------------------------------------------------------------------+\n"); - printf("| R5 DEDICATED SERVER --------------------------------------------------- |\n"); - printf("+-----------------------------------------------------------------------------+\n"); - printf("\n"); -} - -void TerminateR5Dedicated() -{ - FreeConsole(); - Hooks::RemoveHooks(); -} - -//############################################################################# -// ENTRYPOINT -//############################################################################# - -BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) -{ - - switch (dwReason) - { - case DLL_PROCESS_ATTACH: - { - InitializeR5Dedicated(); - break; - } - - case DLL_PROCESS_DETACH: - { - TerminateR5Dedicated(); - break; - } - } - - return TRUE; -} diff --git a/r5dedicated/dllmain.h b/r5dedicated/dllmain.h deleted file mode 100644 index 7ec14cc2..00000000 --- a/r5dedicated/dllmain.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once -__declspec(dllexport) void DummyExport() -{ - // Required for detours. -} \ No newline at end of file diff --git a/r5dedicated/enums.h b/r5dedicated/enums.h deleted file mode 100644 index f3ff58b5..00000000 --- a/r5dedicated/enums.h +++ /dev/null @@ -1,422 +0,0 @@ -#pragma once - -/* Enumerations */ -enum class D3D11DeviceVTbl : short -{ - // IUnknown - QueryInterface = 0, - AddRef = 1, - Release = 2, - - // ID3D11Device - CreateBuffer = 3, - CreateTexture1D = 4, - CreateTexture2D = 5, - CreateTexture3D = 6, - CreateShaderResourceView = 7, - CreateUnorderedAccessView = 8, - CreateRenderTargetView = 9, - CreateDepthStencilView = 10, - CreateInputLayout = 11, - CreateVertexShader = 12, - CreateGeometryShader = 13, - CreateGeometryShaderWithStreamOutput = 14, - CreatePixelShader = 15, - CreateHullShader = 16, - CreateDomainShader = 17, - CreateComputeShader = 18, - CreateClassLinkage = 19, - CreateBlendState = 20, - CreateDepthStencilState = 21, - CreateRasterizerState = 22, - CreateSamplerState = 23, - CreateQuery = 24, - CreatePredicate = 25, - CreateCounter = 26, - CreateDeferredContext = 27, - OpenSharedResource = 28, - CheckFormatSupport = 29, - CheckMultisampleQualityLevels = 30, - CheckCounterInfo = 31, - CheckCounter = 32, - CheckFeatureSupport = 33, - GetPrivateData = 34, - SetPrivateData = 35, - SetPrivateDataInterface = 36, - GetFeatureLevel = 37, - GetCreationFlags = 38, - GetDeviceRemovedReason = 39, - GetImmediateContext = 40, - SetExceptionMode = 41, - GetExceptionMode = 42, -}; - -enum class DXGISwapChainVTbl : short -{ - // IUnknown - QueryInterface = 0, - AddRef = 1, - Release = 2, - - // IDXGIObject - SetPrivateData = 3, - SetPrivateDataInterface = 4, - GetPrivateData = 5, - GetParent = 6, - - // IDXGIDeviceSubObject - GetDevice = 7, - - // IDXGISwapChain - Present = 8, - GetBuffer = 9, - SetFullscreenState = 10, - GetFullscreenState = 11, - GetDesc = 12, - ResizeBuffers = 13, - ResizeTarget = 14, - GetContainingOutput = 15, - GetFrameStatistics = 16, - GetLastPresentCount = 17, -}; - -#define MAX_SPLITSCREEN_CLIENT_BITS 2 -#define MAX_SPLITSCREEN_CLIENTS ( 1 << MAX_SPLITSCREEN_CLIENT_BITS ) // 4 - -enum -{ - MAX_JOYSTICKS = MAX_SPLITSCREEN_CLIENTS, - MOUSE_BUTTON_COUNT = 5, -}; - -enum JoystickAxis_t -{ - JOY_AXIS_X = 0, - JOY_AXIS_Y, - JOY_AXIS_Z, - JOY_AXIS_R, - JOY_AXIS_U, - JOY_AXIS_V, - MAX_JOYSTICK_AXES, -}; - -enum -{ - JOYSTICK_MAX_BUTTON_COUNT = 32, - JOYSTICK_POV_BUTTON_COUNT = 4, - JOYSTICK_AXIS_BUTTON_COUNT = MAX_JOYSTICK_AXES * 2, -}; - -#define JOYSTICK_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_BUTTON + ((_joystick) * JOYSTICK_MAX_BUTTON_COUNT) + (_button) ) -#define JOYSTICK_POV_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_POV_BUTTON + ((_joystick) * JOYSTICK_POV_BUTTON_COUNT) + (_button) ) -#define JOYSTICK_AXIS_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_AXIS_BUTTON + ((_joystick) * JOYSTICK_AXIS_BUTTON_COUNT) + (_button) ) - -#define JOYSTICK_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_BUTTON_INTERNAL( _joystick, _button ) ) -#define JOYSTICK_POV_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_POV_BUTTON_INTERNAL( _joystick, _button ) ) -#define JOYSTICK_AXIS_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_AXIS_BUTTON_INTERNAL( _joystick, _button ) ) - -enum ButtonCode_t -{ - BUTTON_CODE_INVALID = -1, - BUTTON_CODE_NONE = 0, - - KEY_FIRST = 0, - - KEY_NONE = KEY_FIRST, - KEY_0, - KEY_1, - KEY_2, - KEY_3, - KEY_4, - KEY_5, - KEY_6, - KEY_7, - KEY_8, - KEY_9, - KEY_A, - KEY_B, - KEY_C, - KEY_D, - KEY_E, - KEY_F, - KEY_G, - KEY_H, - KEY_I, - KEY_J, - KEY_K, - KEY_L, - KEY_M, - KEY_N, - KEY_O, - KEY_P, - KEY_Q, - KEY_R, - KEY_S, - KEY_T, - KEY_U, - KEY_V, - KEY_W, - KEY_X, - KEY_Y, - KEY_Z, - KEY_PAD_0, - KEY_PAD_1, - KEY_PAD_2, - KEY_PAD_3, - KEY_PAD_4, - KEY_PAD_5, - KEY_PAD_6, - KEY_PAD_7, - KEY_PAD_8, - KEY_PAD_9, - KEY_PAD_DIVIDE, - KEY_PAD_MULTIPLY, - KEY_PAD_MINUS, - KEY_PAD_PLUS, - KEY_PAD_ENTER, - KEY_PAD_DECIMAL, - KEY_LBRACKET, - KEY_RBRACKET, - KEY_SEMICOLON, - KEY_APOSTROPHE, - KEY_BACKQUOTE, - KEY_COMMA, - KEY_PERIOD, - KEY_SLASH, - KEY_BACKSLASH, - KEY_MINUS, - KEY_EQUAL, - KEY_ENTER, - KEY_SPACE, - KEY_BACKSPACE, - KEY_TAB, - KEY_CAPSLOCK, - KEY_NUMLOCK, - KEY_ESCAPE, - KEY_SCROLLLOCK, - KEY_INSERT, - KEY_DELETE, - KEY_HOME, - KEY_END, - KEY_PAGEUP, - KEY_PAGEDOWN, - KEY_BREAK, - KEY_LSHIFT, - KEY_RSHIFT, - KEY_LALT, - KEY_RALT, - KEY_LCONTROL, - KEY_RCONTROL, - KEY_LWIN, - KEY_RWIN, - KEY_APP, - KEY_UP, - KEY_LEFT, - KEY_DOWN, - KEY_RIGHT, - KEY_F1, - KEY_F2, - KEY_F3, - KEY_F4, - KEY_F5, - KEY_F6, - KEY_F7, - KEY_F8, - KEY_F9, - KEY_F10, - KEY_F11, - KEY_F12, - KEY_CAPSLOCKTOGGLE, - KEY_NUMLOCKTOGGLE, - KEY_SCROLLLOCKTOGGLE, - - KEY_LAST = KEY_SCROLLLOCKTOGGLE, - KEY_COUNT = KEY_LAST - KEY_FIRST + 1, - - // Mouse - MOUSE_FIRST = KEY_LAST + 1, - - MOUSE_LEFT = MOUSE_FIRST, - MOUSE_RIGHT, - MOUSE_MIDDLE, - MOUSE_4, - MOUSE_5, - MOUSE_WHEEL_UP, // A fake button which is 'pressed' and 'released' when the wheel is moved up - MOUSE_WHEEL_DOWN, // A fake button which is 'pressed' and 'released' when the wheel is moved down - - MOUSE_LAST = MOUSE_WHEEL_DOWN, - MOUSE_COUNT = MOUSE_LAST - MOUSE_FIRST + 1, - - // Joystick - JOYSTICK_FIRST = MOUSE_LAST + 1, - - JOYSTICK_FIRST_BUTTON = JOYSTICK_FIRST, - JOYSTICK_LAST_BUTTON = JOYSTICK_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_MAX_BUTTON_COUNT - 1), - JOYSTICK_FIRST_POV_BUTTON, - JOYSTICK_LAST_POV_BUTTON = JOYSTICK_POV_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_POV_BUTTON_COUNT - 1), - JOYSTICK_FIRST_AXIS_BUTTON, - JOYSTICK_LAST_AXIS_BUTTON = JOYSTICK_AXIS_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_AXIS_BUTTON_COUNT - 1), - - JOYSTICK_LAST = JOYSTICK_LAST_AXIS_BUTTON, - - BUTTON_CODE_LAST, - BUTTON_CODE_COUNT = BUTTON_CODE_LAST - KEY_FIRST + 1, - - // Helpers for XBox 360 - KEY_XBUTTON_UP = JOYSTICK_FIRST_POV_BUTTON, // POV buttons - KEY_XBUTTON_RIGHT, - KEY_XBUTTON_DOWN, - KEY_XBUTTON_LEFT, - - KEY_XBUTTON_A = JOYSTICK_FIRST_BUTTON, // Buttons - KEY_XBUTTON_B, - KEY_XBUTTON_X, - KEY_XBUTTON_Y, - KEY_XBUTTON_LEFT_SHOULDER, - KEY_XBUTTON_RIGHT_SHOULDER, - KEY_XBUTTON_BACK, - KEY_XBUTTON_START, - KEY_XBUTTON_STICK1, - KEY_XBUTTON_STICK2, - KEY_XBUTTON_INACTIVE_START, - - KEY_XSTICK1_RIGHT = JOYSTICK_FIRST_AXIS_BUTTON, // XAXIS POSITIVE - KEY_XSTICK1_LEFT, // XAXIS NEGATIVE - KEY_XSTICK1_DOWN, // YAXIS POSITIVE - KEY_XSTICK1_UP, // YAXIS NEGATIVE - KEY_XBUTTON_LTRIGGER, // ZAXIS POSITIVE - KEY_XBUTTON_RTRIGGER, // ZAXIS NEGATIVE - KEY_XSTICK2_RIGHT, // UAXIS POSITIVE - KEY_XSTICK2_LEFT, // UAXIS NEGATIVE - KEY_XSTICK2_DOWN, // VAXIS POSITIVE - KEY_XSTICK2_UP, // VAXIS NEGATIVE -}; - -// Buttons are not confirmed to be the same. They have been always the same throughout the source engine. Lets hope they did not change them. - -enum KeyValuesTypes -{ - TYPE_NONE = 0x0, - TYPE_STRING = 0x1, - TYPE_INT = 0x2, - TYPE_FLOAT = 0x3, - TYPE_PTR = 0x4, - TYPE_WSTRING = 0x5, - TYPE_COLOR = 0x6, - TYPE_UINT64 = 0x7, - TYPE_COMPILED_INT_BYTE = 0x8, - TYPE_COMPILED_INT_0 = 0x9, - TYPE_COMPILED_INT_1 = 0xA, - TYPE_NUMTYPES = 0xB, -}; - -enum ClientFrameStage_t -{ - FRAME_UNDEFINED = -1, // (haven't run any frames yet) - FRAME_START, - - // A network packet is being recieved - FRAME_NET_UPDATE_START, - // Data has been received and we're going to start calling PostDataUpdate - FRAME_NET_UPDATE_POSTDATAUPDATE_START, - // Data has been received and we've called PostDataUpdate on all data recipients - FRAME_NET_UPDATE_POSTDATAUPDATE_END, - // We've received all packets, we can now do interpolation, prediction, etc.. - FRAME_NET_UPDATE_END, - - // We're about to start rendering the scene - FRAME_RENDER_START, - // We've finished rendering the scene. - FRAME_RENDER_END, - - FRAME_NET_FULL_FRAME_UPDATE_ON_REMOVE -}; - -enum HostStates_t -{ - HS_NEW_GAME = 0x0, - HS_LOAD_GAME = 0x1, - HS_CHANGE_LEVEL_SP = 0x2, - HS_CHANGE_LEVEL_MP = 0x3, - HS_RUN = 0x4, - HS_GAME_SHUTDOWN = 0x5, - HS_SHUTDOWN = 0x6, - HS_RESTART = 0x7, -}; - -enum SIGNONSTATE -{ - SIGNONSTATE_NONE = 0, // no state yet; about to connect - SIGNONSTATE_CHALLENGE = 1, // client challenging server; all OOB packets - SIGNONSTATE_CONNECTED = 2, // client is connected to server; netchans ready - SIGNONSTATE_NEW = 3, // just got serverinfo and string tables - SIGNONSTATE_PRESPAWN = 4, // received signon buffers - SIGNONSTATE_GETTING_DATA = 5, // getting persistence data I assume? - SIGNONSTATE_SPAWN = 6, // ready to receive entity packets - SIGNONSTATE_FIRST_SNAP = 7, // ??? - SIGNONSTATE_FULL = 8, // we are fully connected; first non-delta packet received - SIGNONSTATE_CHANGELEVEL = 9, // server is changing level; please wait -}; - -enum FileWarningLevel_t -{ - FILESYSTEM_WARNING = -1, - FILESYSTEM_WARNING_QUIET = 0, - FILESYSTEM_WARNING_REPORTUNCLOSED, - FILESYSTEM_WARNING_REPORTUSAGE, - FILESYSTEM_WARNING_REPORTALLACCESSES, - FILESYSTEM_WARNING_REPORTALLACCESSES_READ, - FILESYSTEM_WARNING_REPORTALLACCESSES_READWRITE, - FILESYSTEM_WARNING_REPORTALLACCESSES_ASYNC -}; - -#define FCVAR_NONE 0 - -// Command to ConVars and ConCommands -// ConVar Systems -#define FCVAR_UNREGISTERED (1<<0) // If this is set, don't add to linked list, etc. -#define FCVAR_DEVELOPMENTONLY (1<<1) // Hidden in released products. Flag is removed automatically if ALLOW_DEVELOPMENT_CVARS is defined. -#define FCVAR_GAMEDLL (1<<2) // defined by the game DLL -#define FCVAR_CLIENTDLL (1<<3) // defined by the client DLL -#define FCVAR_HIDDEN (1<<4) // Hidden. Doesn't appear in find or auto complete. Like DEVELOPMENTONLY, but can't be compiled out. - -// ConVar only -#define FCVAR_PROTECTED (1<<5) // It's a server cvar, but we don't send the data since it's a password, etc. Sends 1 if it's not bland/zero, 0 otherwise as value -#define FCVAR_SPONLY (1<<6) // This cvar cannot be changed by clients connected to a multiplayer server. -#define FCVAR_ARCHIVE (1<<7) // set to cause it to be saved to vars.rc -#define FCVAR_NOTIFY (1<<8) // notifies players when changed -#define FCVAR_USERINFO (1<<9) // changes the client's info string - -#define FCVAR_PRINTABLEONLY (1<<10) // This cvar's string cannot contain unprintable characters ( e.g., used for player name etc ). - -#define FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS (1<<10) // When on concommands this allows remote clients to execute this cmd on the server. - // We are changing the default behavior of concommands to disallow execution by remote clients without - // this flag due to the number existing concommands that can lag or crash the server when clients abuse them. - -#define FCVAR_UNLOGGED (1<<11) // If this is a FCVAR_SERVER, don't log changes to the log file / console if we are creating a log -#define FCVAR_NEVER_AS_STRING (1<<12) // never try to print that cvar - -// It's a ConVar that's shared between the client and the server. -// At signon, the values of all such ConVars are sent from the server to the client (skipped for local -// client, of course ) -// If a change is requested it must come from the console (i.e., no remote client changes) -// If a value is changed while a server is active, it's replicated to all connected clients -#define FCVAR_REPLICATED (1<<13) // server setting enforced on clients, TODO rename to FCAR_SERVER at some time -#define FCVAR_CHEAT (1<<14) // Only useable in singleplayer / debug / multiplayer & sv_cheats -#define FCVAR_SS (1<<15) // causes varnameN where N == 2 through max splitscreen slots for mod to be autogenerated -#define FCVAR_DEMO (1<<16) // record this cvar when starting a demo file -#define FCVAR_DONTRECORD (1<<17) // don't record these command in demofiles -#define FCVAR_SS_ADDED (1<<18) // This is one of the "added" FCVAR_SS variables for the splitscreen players -#define FCVAR_RELEASE (1<<19) // Cvars tagged with this are the only cvars avaliable to customers -#define FCVAR_RELOAD_MATERIALS (1<<20) // If this cvar changes, it forces a material reload -#define FCVAR_RELOAD_TEXTURES (1<<21) // If this cvar changes, if forces a texture reload - -#define FCVAR_NOT_CONNECTED (1<<22) // cvar cannot be changed by a client that is connected to a server -#define FCVAR_MATERIAL_SYSTEM_THREAD (1<<23) // Indicates this cvar is read from the material system thread -#define FCVAR_ARCHIVE_GAMECONSOLE (1<<24) // cvar written to config.cfg on the Xbox - -#define FCVAR_SERVER_CAN_EXECUTE (1<<28)// the server is allowed to execute this command on clients via ClientCommand/NET_StringCmd/CBaseClientState::ProcessStringCmd. -#define FCVAR_SERVER_CANNOT_QUERY (1<<29)// If this is set, then the server is not allowed to query this cvar's value (via IServerPluginHelpers::StartQueryCvarValue). -#define FCVAR_CLIENTCMD_CAN_EXECUTE (1<<30) // IVEngineClient::ClientCmd is allowed to execute this command. - -#define MAX_PLAYERS 128 // Max R5 players. diff --git a/r5dedicated/gameclasses.cpp b/r5dedicated/gameclasses.cpp deleted file mode 100644 index 9f9cf1e4..00000000 --- a/r5dedicated/gameclasses.cpp +++ /dev/null @@ -1,470 +0,0 @@ -#include "pch.h" -#include "enums.h" -#include "gameclasses.h" - -// Need this for a re-factor later. -// Interface* interfaces = *reinterpret_cast(0x167F4FA48); - -// for (Interface* current = interfaces; current; current = reinterpret_cast(current->NextInterfacePtr)) -// { -// printf("%s: %p\n", current->InterfaceName, current->InterfacePtr); -// } - -namespace GameGlobals -{ - bool IsInitialized = false; - CHostState* HostState = nullptr; - CInputSystem* InputSystem = nullptr; - CCVar* Cvar = nullptr; - CClient* Client = nullptr; - BanList* BanSystem = new BanList(); - - CKeyValuesSystem* KeyValuesSystem = nullptr; - KeyValues** PlaylistKeyValues = nullptr; - - std::vector allPlaylists = { "none" }; - - namespace CustomCommandVariations - { - - void Kick_Callback(CCommand* cmd) - { - std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); - if (argSize < 2) // Do we atleast have 2 arguments? - return; - - CCommand& cmdReference = *cmd; // Get reference. - const char* firstArg = cmdReference[1]; // Get first arg. - - for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. - { - CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance. - if (!client) - continue; - - if (!client->GetNetChan()) // Netchan valid? - continue; - - void* clientNamePtr = (void**)(((std::uintptr_t)client->GetNetChan()) + 0x1A8D); // Get client name from netchan. - std::string clientName((char*)clientNamePtr, 32); // Get full name. - - if (clientName.empty()) // Empty name? - continue; - - if (strcmp(firstArg, clientName.c_str()) != 0) // Our wanted name? - continue; - - DisconnectClient(client, "Kicked from Server", 0, 1); // Disconnect client. - } - } - - void KickID_Callback(CCommand* cmd) - { - static auto HasOnlyDigits = [](const std::string& string) - { - for (const char& character : string) - { - if (std::isdigit(character) == 0) - return false; - } - return true; - }; - - std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); - if (argSize < 2) // Do we atleast have 2 arguments? - return; - - CCommand& cmdReference = *cmd; // Get reference. - std::string firstArg = cmdReference[1]; // Get first arg. - - try - { - bool onlyDigits = HasOnlyDigits(firstArg); // Only has digits? - for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. - { - CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance. - if (!client) - continue; - - if (!client->GetNetChan()) // Netchan valid? - continue; - - std::string finalIPAddress = "null"; // If this stays null they modified the packet somehow. - MemoryAddress ipAddressField = MemoryAddress(((std::uintptr_t)client->GetNetChan()) + 0x1AC0); // Get client ip from netchan. - if (ipAddressField) - { - std::stringstream ss; - ss << std::to_string(ipAddressField.GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x1).GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x2).GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x3).GetValue()); - - finalIPAddress = ss.str(); - } - - if (onlyDigits) - { - std::int64_t ID = static_cast(std::stoll(firstArg)); - if (ID > MAX_PLAYERS) // Is it a possible originID? - { - std::int64_t originID = client->m_iOriginID; - if (originID != ID) // See if they match. - continue; - } - else // If its not try by userID. - { - std::int64_t clientID = static_cast(client->m_iUserID + 1); // Get UserID + 1. - if (clientID != ID) // See if they match. - continue; - } - - DisconnectClient(client, "Kicked from Server", 0, 1); // Disconnect client. - } - else - { - if (firstArg.compare(finalIPAddress) != NULL) // Do the string equal? - continue; - - DisconnectClient(client, "Kicked from Server", 0, 1); // Disconnect client. - } - } - } - catch (std::exception& e) - { - std::cout << "Kick UID asked for a userID or originID :( You can get the userID with the 'status' command. Error: " << e.what() << std::endl; - return; - } - } - - void Unban_Callback(CCommand* cmd) - { - static auto HasOnlyDigits = [](const std::string& string) - { - for (const char& character : string) - { - if (std::isdigit(character) == 0) - return false; - } - return true; - }; - - std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); - if (argSize < 2) // Do we atleast have 2 arguments? - return; - - CCommand& cmdReference = *cmd; // Get reference. - - try - { - const char* firstArg = cmdReference[1]; - if (HasOnlyDigits(firstArg)) // Check if we have an ip address or origin ID. - { - GameGlobals::BanSystem->DeleteEntry("noIP", std::stoll(firstArg)); // Delete ban entry. - GameGlobals::BanSystem->Save(); // Save modified vector to file. - } - else - { - GameGlobals::BanSystem->DeleteEntry(firstArg, 1); // Delete ban entry. - GameGlobals::BanSystem->Save(); // Save modified vector to file. - } - } - catch (std::exception& e) - { - std::cout << "Unban Error: " << e.what() << std::endl; - return; - } - } - - void ReloadBanList_Callback(CCommand* cmd) - { - GameGlobals::BanSystem->Load(); // Reload banlist. - } - - void Ban_Callback(CCommand* cmd) - { - std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); - if (argSize < 2) // Do we atleast have 2 arguments? - return; - - CCommand& cmdReference = *cmd; // Get reference. - const char* firstArg = cmdReference[1]; // Get first arg. - - for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. - { - CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance. - if (!client) - continue; - - if (!client->GetNetChan()) // Netchan valid? - continue; - - void* clientNamePtr = (void**)(((std::uintptr_t)client->GetNetChan()) + 0x1A8D); // Get client name from netchan. - std::string clientName((char*)clientNamePtr, 32); // Get full name. - - if (clientName.empty()) // Empty name? - continue; - - if (strcmp(firstArg, clientName.c_str()) != 0) // Our wanted name? - continue; - - std::string finalIPAddress = "null"; // If this stays null they modified the packet somehow. - MemoryAddress ipAddressField = MemoryAddress(((std::uintptr_t)client->GetNetChan()) + 0x1AC0); // Get client ip from netchan. - if (ipAddressField && ipAddressField.GetValue() != 0x0) - { - std::stringstream ss; - ss << std::to_string(ipAddressField.GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x1).GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x2).GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x3).GetValue()); - - finalIPAddress = ss.str(); - } - - GameGlobals::BanSystem->AddEntry(finalIPAddress, client->m_iOriginID); // Add ban entry. - GameGlobals::BanSystem->Save(); // Save ban list. - DisconnectClient(client, "Banned from Server", 0, 1); // Disconnect client. - } - } - - void BanID_Callback(CCommand* cmd) - { - static auto HasOnlyDigits = [](const std::string& string) - { - for (const char& character : string) - { - if (std::isdigit(character) == 0) - return false; - } - return true; - }; - - std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); - if (argSize < 2) // Do we atleast have 2 arguments? - return; - - CCommand& cmdReference = *cmd; // Get reference. - std::string firstArg = cmdReference[1]; - - try - { - bool onlyDigits = HasOnlyDigits(firstArg); // Only has digits? - for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. - { - CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance. - if (!client) - continue; - - if (!client->GetNetChan()) // Netchan valid? - continue; - - std::string finalIPAddress = "null"; // If this stays null they modified the packet somehow. - MemoryAddress ipAddressField = MemoryAddress(((std::uintptr_t)client->GetNetChan()) + 0x1AC0); // Get client ip from netchan. - if (ipAddressField) - { - std::stringstream ss; - ss << std::to_string(ipAddressField.GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x1).GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x2).GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x3).GetValue()); - - finalIPAddress = ss.str(); - } - - if (onlyDigits) - { - std::int64_t ID = static_cast(std::stoll(firstArg)); - if (ID > MAX_PLAYERS) // Is it a possible originID? - { - std::int64_t originID = client->m_iOriginID; - if (originID != ID) // See if they match. - continue; - } - else // If its not try by userID. - { - std::int64_t clientID = static_cast(client->m_iUserID + 1); // Get UserID + 1. - if (clientID != ID) // See if they match. - continue; - } - - GameGlobals::BanSystem->AddEntry(finalIPAddress, client->m_iOriginID); // Add ban entry. - GameGlobals::BanSystem->Save(); // Save ban list. - DisconnectClient(client, "Banned from Server", 0, 1); // Disconnect client. - } - else - { - if (firstArg.compare(finalIPAddress) != NULL) // Do the string equal? - continue; - - GameGlobals::BanSystem->AddEntry(finalIPAddress, client->m_iOriginID); // Add ban entry. - GameGlobals::BanSystem->Save(); // Save ban list. - DisconnectClient(client, "Banned from Server", 0, 1); // Disconnect client. - } - } - } - catch (std::exception& e) - { - std::cout << "Banid Error: " << e.what() << std::endl; - return; - } - } - } - - void NullHostNames() - { - const char* hostnameArray[] = - { - "pin_telemetry_hostname", - "assetdownloads_hostname", - "users_hostname", - "persistence_hostname", - "speechtotexttoken_hostname", - "communities_hostname", - "persistenceDef_hostname", - "party_hostname", - "speechtotext_hostname", - "serverReports_hostname", - "subscription_hostname", - "steamlink_hostname", - "staticfile_hostname", - "matchmaking_hostname", - "skill_hostname", - "publication_hostname", - "stats_hostname" - }; - - for (int i = 0; i < 17; i++) - { - const char* name = hostnameArray[i]; - Cvar->FindVar(name)->m_pzsCurrentValue = "0.0.0.0"; - } - } - - void InitGameGlobals() - { - HostState = reinterpret_cast(0x141736120); // Get CHostState from memory. - InputSystem = *reinterpret_cast(0x14D40B380); // Get IInputSystem from memory. - Cvar = *reinterpret_cast(0x14D40B348); // Get CCVar from memory. - //KeyValuesSystem = reinterpret_cast(0x141F105C0); // Get CKeyValuesSystem from memory. - //PlaylistKeyValues = reinterpret_cast(0x16705B980); // Get the KeyValue for the playlist file. - //Client = reinterpret_cast(0x16073B200); - - //NullHostNames(); // Null all hostnames. - //InitAllCommandVariations(); // Initialize our custom ConVars. - //*(char*)addr_m_bRestrictServerCommands = true; // Restrict commands. - //void* disconnect = Cvar->FindCommand("disconnect"); - //*(std::int32_t*)((std::uintptr_t)disconnect + 0x38) |= FCVAR_SERVER_CAN_EXECUTE; // Make sure server is not restricted to this. - - //std::thread t1(InitPlaylist); // Start thread to grab playlists. - //t1.detach(); // Detach thread from current one. - - IsInitialized = true; - } - - void InitPlaylist() - { - while (true) - { - if ((*PlaylistKeyValues)) - { - KeyValues* playlists = (*PlaylistKeyValues)->FindKey("Playlists", false); // Find playlists key. - if (playlists) - { - allPlaylists.clear(); - for (KeyValues* dat = playlists->m_pSub; dat != nullptr; dat = dat->m_pPeer) // Parse through all sub keys. - { - allPlaylists.push_back(dat->GetName()); // Get all playlist names. - } - - break; // Break if playlist got filled. - } - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - } - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - } - } - - void InitAllCommandVariations() - { - void* KickConCommand = CreateCustomConCommand("kick", "Kick a client from the Server via name. | Usage: kick (name).", 0, CustomCommandVariations::Kick_Callback, nullptr); - void* KickIDConCommand = CreateCustomConCommand("kickid", "Kick a client from the Server via userID or originID | Usage: kickid (originID/userID)", 0, CustomCommandVariations::KickID_Callback, nullptr); - void* UnbanConCommand = CreateCustomConCommand("unban", "Unbans a client from the Server via IP or originID | Usage: unban (originID/ipAddress)", 0, CustomCommandVariations::Unban_Callback, nullptr); - void* ReloadBanListConCommand = CreateCustomConCommand("reloadbanlist", "Reloads the ban list from disk.", 0, CustomCommandVariations::ReloadBanList_Callback, nullptr); - void* BanConCommand = CreateCustomConCommand("ban", "Bans a client from the Server via name. | Usage: ban (name)", 0, CustomCommandVariations::Ban_Callback, nullptr); - void* BanIDConCommand = CreateCustomConCommand("banid", "Bans a client from the Server via originID, userID or IP | Usage: banid (originID/ipAddress/userID)", 0, CustomCommandVariations::BanID_Callback, nullptr); - } - - void* CreateCustomConCommand(const char* name, const char* helpString, int flags, void* callback, void* callbackAfterExecution) - { - static MemoryAddress ConCommandVtable = MemoryAddress(0x14136BD70); - static MemoryAddress NullSub = MemoryAddress(0x1401B3280); - static MemoryAddress CallbackCompletion = MemoryAddress(0x1401E3990); - static MemoryAddress RegisterConCommand = MemoryAddress(0x14046F470); - - void* command = reinterpret_cast(addr_MemAlloc_Wrapper(0x68)); // Allocate new memory with StdMemAlloc else we crash. - memset(command, 0, 0x68); // Set all to null. - std::uintptr_t commandPtr = reinterpret_cast(command); // To ptr. - - *(void**)commandPtr = ConCommandVtable.RCast(); // 0x0 to ConCommand vtable. - *(const char**)(commandPtr + 0x18) = name; // 0x18 to ConCommand Name. - *(const char**)(commandPtr + 0x20) = helpString; // 0x20 to ConCommand help string. - *(std::int32_t*)(commandPtr + 0x38) = flags; // 0x38 to ConCommand Flags. - *(void**)(commandPtr + 0x40) = NullSub.RCast(); // 0x40 Nullsub since every concommand has it. - *(void**)(commandPtr + 0x50) = callback; // 0x50 has function callback. - *(std::int32_t*)(commandPtr + 0x60) = 2; // 0x60 Set to use callback and newcommand callback. - - if (callbackAfterExecution) // Do we wanna have a callback after execution? - { - *(void**)(commandPtr + 0x58) = callbackAfterExecution; // 0x58 to our callback after execution. - } - else - { - *(void**)(commandPtr + 0x58) = CallbackCompletion.RCast(); // 0x58 nullsub. - } - - RegisterConCommand.RCast()((void*)commandPtr); // Register command in ConVarAccessor. - - return command; - } - - ConVar* CreateCustomConVar(const char* name, const char* defaultValue, int flags, const char* helpString, bool bMin, float fMin, bool bMax, float fMax, void* callback, void* unk) - { - static MemoryAddress ConVarVtable = MemoryAddress(0x14046FB50).Offset(0x12).ResolveRelativeAddress(); // Get vtable ptr for ConVar table. - static MemoryAddress ICvarVtable = MemoryAddress(0x14046FB50).Offset(0x29).ResolveRelativeAddress(); // Get vtable ptr for ICvar table. - static MemoryAddress CreateConVar = MemoryAddress(0x140470540); // Get CreateConvar address. - - ConVar* allocatedConvar = reinterpret_cast(addr_MemAlloc_Wrapper(0xA0)); // Allocate new memory with StdMemAlloc else we crash. - memset(allocatedConvar, 0, 0xA0); // Set all to null. - std::uintptr_t cvarPtr = reinterpret_cast(allocatedConvar); // To ptr. - - *(void**)(cvarPtr + 0x40) = ICvarVtable.RCast(); // 0x40 to ICvar table. - *(void**)cvarPtr = ConVarVtable.RCast(); // 0x0 to ConVar vtable. - - CreateConVar.RCast() // Call to create ConVar. - (allocatedConvar, name, defaultValue, flags, helpString, bMin, fMin, bMax, fMax, callback, unk); - - return allocatedConvar; // Return allocated ConVar. - } - - void DisconnectClient(CClient* client, const char* reason, unsigned __int8 unk1, char unk2) - { - if (!client) // Client valid? - return; - - if (std::strlen(reason) == NULL) // Is reason null? - return; - - if (!client->GetNetChan()) - return; - - addr_NetChan_Shutdown(client->GetNetChan(), reason, unk1, unk2); // Shutdown netchan. - client->GetNetChan() = nullptr; // Null netchan. - MemoryAddress(0x140302FD0).RCast()(client); // Reset CClient instance for client. - } -} - -#pragma region KeyValues -const char* KeyValues::GetName() -{ - return GameGlobals::KeyValuesSystem->GetStringForSymbol(MAKE_3_BYTES_FROM_1_AND_2(m_iKeyNameCaseSensitive, m_iKeyNameCaseSensitive2)); -} -#pragma endregion \ No newline at end of file diff --git a/r5dedicated/gameclasses.h b/r5dedicated/gameclasses.h deleted file mode 100644 index 39ebf687..00000000 --- a/r5dedicated/gameclasses.h +++ /dev/null @@ -1,487 +0,0 @@ -#pragma once -#include "pch.h" -#include "hooks.h" -#include "enums.h" -#include "banlist.h" - -///////////////////////////////////////////////////////////////////////////// -// Classes and Structs -class CInputSystem -{ -public: - void EnableInput(bool bEnabled)// @0x14039F100 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void(__thiscall*)(CInputSystem*, bool); - (*reinterpret_cast(this))[10](this, bEnabled); - } - - void EnableMessagePump(bool bEnabled) // @0x14039F110 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void(__thiscall*)(CInputSystem*, bool); - (*reinterpret_cast(this))[11](this, bEnabled); - } - - bool IsButtonDown(ButtonCode_t Button) // @0x1403A0140 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = bool(__thiscall*)(CInputSystem*, ButtonCode_t); - return (*reinterpret_cast(this))[13](this, Button); - } - -private: - char pad_0000[16]; //0x0000 -public: - bool m_bEnabled; //0x0010 IsInputEnabled variable. - bool m_bPumpEnabled; //0x0011 EnabledMessagePump variable. -}; - -typedef int HKeySymbol; - -#define MAKE_3_BYTES_FROM_1_AND_2( x1, x2 ) (( (( std::uint16_t )x2) << 8 ) | (std::uint8_t)(x1)) - -class CKeyValuesSystem // VTABLE @ 0x1413AA1E8 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM -{ -public: - - void RegisterSizeofKeyValues(__int64 size) //@0x1413AA1F0 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void(__thiscall*)(CKeyValuesSystem*, __int64); - (*reinterpret_cast(this))[0](this, size); - } - - void* AllocKeyValuesMemory(__int64 size) // @0x1413AA1F8 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void* (__thiscall*)(CKeyValuesSystem*, __int64); - return (*reinterpret_cast(this))[1](this, size); - } - - void FreeKeyValuesMemory(void* pMem) // @0x1413AA200 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void(__thiscall*)(CKeyValuesSystem*, void*); - (*reinterpret_cast(this))[2](this, pMem); - } - - HKeySymbol GetSymbolForString(const char* name, bool bCreate) // @0x1413AA208 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = HKeySymbol(__thiscall*)(CKeyValuesSystem*, const char*, bool); - return (*reinterpret_cast(this))[3](this, name, bCreate); - } - - const char* GetStringForSymbol(HKeySymbol symbol) // @0x1413AA210 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = const char* (__thiscall*)(CKeyValuesSystem*, HKeySymbol); - return (*reinterpret_cast(this))[4](this, symbol); - } - - // void __fastcall CKeyValuesSystem::FreeKeyValuesMemory(CKeyValuesSystem* this_arg, void* ptr_mem_arg) - // { - // __int64* v2; // rax - // __int64 v4; // rax - // __int64* v5; // rax - // - // v2 = qword_14D40B538; - // if (!qword_14D40B538) - // { - // v2 = sub_140462930(); - // qword_14D40B538 = v2; - // } - // v4 = (*(*v2 + 48))(v2, ptr_mem_arg); - // if (v4 > 0) - // CKeyValuesSystem::m_pMemPool -= v4; - // v5 = qword_14D40B538; - // if (!qword_14D40B538) - // { - // v5 = sub_140462930(); - // qword_14D40B538 = v5; - // } - // (*(*v5 + 40))(v5, ptr_mem_arg); - // } - - // GetMemPool return a global variable called m_pMemPool it gets modified by AllocKeyValuesMemory and FreeKeyValuesMemory above you can see where the find it in FreeKeyValuesMemory. - void* GetMemPool() // @0x1413AA228 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - return reinterpret_cast(0x14D412768); // May need to dereference is once more not sure right now. - } - - void SetKeyValuesExpressionSymbol(const char* name, bool bValue) // @0x1413AA230 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void(__thiscall*)(CKeyValuesSystem*, const char*, bool); - (*reinterpret_cast(this))[8](this, name, bValue); - } - - bool GetKeyValuesExpressionSymbol(const char* name) // @0x1413AA238 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = bool(__thiscall*)(CKeyValuesSystem*, const char*); - return (*reinterpret_cast(this))[9](this, name); - } - - HKeySymbol GetSymbolForStringCaseSensitive(HKeySymbol& hCaseInsensitiveSymbol, const char* name, bool bCreate) // @0x1413AA240 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = HKeySymbol(__thiscall*)(CKeyValuesSystem*, HKeySymbol&, const char*, bool); - return (*reinterpret_cast(this))[10](this, hCaseInsensitiveSymbol, name, bCreate); - } - - // Datatypes aren't accurate. But full fill the actual byte distance. -public: - void* vtable; // 0x0000 - __int64 m_iMaxKeyValuesSize; // 0x0008 -private: - char gap10[240]; // 0x0010 -public: - __int32 m_KvConditionalSymbolTable; // 0x0100 -private: - char gap104[4]; // 0x0104 -public: - __int64 field_108; // 0x0108 -private: - char gap110[32]; // 0x0110 -public: - int m_mutex; // 0x0130 -}; - -class KeyValues -{ -public: - - KeyValues* FindKey(const char* keyName, bool bCreate) - { - static auto func = reinterpret_cast(addr_KeyValues_FindKey); - return func(this, keyName, bCreate); - } - - const char* GetName(); - - int GetInt(const char* keyName, int defaultValue) - { - KeyValues* dat = FindKey(keyName, false); - - if (!dat) - return defaultValue; - - switch (dat->m_iDataType) - { - case TYPE_STRING: - return atoi(dat->m_sValue); - case TYPE_FLOAT: - return static_cast(m_flValue()); - case TYPE_WSTRING: - return _wtoi(dat->m_wsValue); - case TYPE_UINT64: - return 0; - default: - return dat->m_iValue(); - } - - return defaultValue; - } - - void SetInt(const char* keyName, int iValue) - { - KeyValues* dat = FindKey(keyName, true); - if (dat) - { - dat->m_iValue() = iValue; - dat->m_iDataType = TYPE_INT; - } - } - - void SetFloat(const char* keyName, float flValue) - { - KeyValues* dat = FindKey(keyName, true); - if (dat) - { - dat->m_flValue() = flValue; - dat->m_iDataType = TYPE_FLOAT; - } - } - - // Compiler makes it so m_Value shares the offset spot with m_flValue that why we cast it like this. - float& m_flValue() - { - static __int32 offset = 0x18; - return *(float*)((std::uintptr_t)this + offset); - } - - int& m_iValue() - { - static __int32 offset = 0x18; - return *(int*)((std::uintptr_t)this + offset); - } - -public: - unsigned __int32 m_iKeyName : 24; // 0x0000 - unsigned __int32 m_iKeyNameCaseSensitive : 8; // 0x0003 - char* m_sValue; // 0x0008 - wchar_t* m_wsValue; // 0x0010 - int m_Value; // 0x0018 -private: - char gap1C[12]; // 0x0020 -public: - char m_iDataType; // 0x0028 - unsigned __int16 m_iKeyNameCaseSensitive2; // 0x002A - KeyValues* m_pPeer; // 0x0030 - KeyValues* m_pSub; // 0x0038 - KeyValues* m_pChain; // 0x0040 -}; - -struct Vector3 // Implement the proper class of this at some point. -{ - float x; // 0x0000 - float y; // 0x0004 - float z; // 0x0008 -}; - -struct QAngle // Implement the proper class of this at some point. -{ - float pitch; //0x0000 - float yaw; // 0x0004 - float roll; // 0x0008 -}; - -class CHostState -{ -public: - int m_iCurrentState; //0x0000 - int m_iNextState; //0x0004 - Vector3 m_vecLocation; //0x0008 - QAngle m_angLocation; //0x0014 - char m_levelName[64]; //0x0020 - char m_mapGroupName[256]; //0x0060 - char m_landMarkName[256]; //0x0160 - float m_flShortFrameTime; //0x0260 - bool m_bActiveGame; //0x0264 - bool m_bRememberLocation; //0x0265 - bool m_bBackgroundLevel; //0x0266 - bool m_bWaitingForConnection; //0x0267 - bool m_bSplitScreenConnect; //0x0268 - bool m_bGameHasShutDownAndFlushedMemory; //0x0269 - bool m_bWorkshopMapDownloadPending; //0x026A -}; - -class CHLClient -{ -public: - void FrameStageNotify(ClientFrameStage_t curStage) // @0x1405C0740 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void(__thiscall*)(CHLClient*, ClientFrameStage_t); - (*reinterpret_cast(this))[58](this, curStage); /* 48 83 EC 28 89 15 ? ? ? ? */ - } -}; - -class CClient -{ -public: - inline CClient* GetClientInstance(int index) - { - return (CClient*)(std::uintptr_t)(0x16073B200 + (index * (std::uintptr_t)0x4A4C0)); - } - - void*& GetNetChan() - { - return m_nNetChannel; - } -private: - char pad_0000[16]; //0x0000 -public: - int m_iUserID; //0x0010 -private: - char pad_0014[908]; //0x0014 -public: - void* m_nNetChannel; //0x03A0 -private: - char pad_03A8[8]; //0x03A8 -public: - int m_iSignonstate; //0x03B0 -private: - char pad_03B4[4]; //0x03B4 -public: - std::int64_t m_iOriginID; //0x03B8 -private: - char pad_03C0[303360]; //0x03C0 -}; - -class CCommand -{ -private: - enum - { - COMMAND_MAX_ARGC = 64, - COMMAND_MAX_LENGTH = 512, - }; - -public: - CCommand() = delete; - - inline int MaxCommandLength() - { - return COMMAND_MAX_LENGTH - 1; - } - - inline int64_t ArgC() const - { - return m_nArgc; - } - - inline const char** ArgV() const - { - return m_nArgc ? (const char**)m_ppArgv : NULL; - } - - inline const char* ArgS() const - { - return m_nArgv0Size ? &m_pArgSBuffer[m_nArgv0Size] : ""; - } - - inline const char* GetCommandString() const - { - return m_nArgc ? m_pArgSBuffer : ""; - } - - inline const char* Arg(int nIndex) const - { - // FIXME: Many command handlers appear to not be particularly careful - // about checking for valid argc range. For now, we're going to - // do the extra check and return an empty string if it's out of range - if (nIndex < 0 || nIndex >= m_nArgc) - return ""; - return m_ppArgv[nIndex]; - } - - inline const char* operator[](int nIndex) const - { - return Arg(nIndex); - } - -private: - std::int64_t m_nArgc; - std::int64_t m_nArgv0Size; - char m_pArgSBuffer[COMMAND_MAX_LENGTH]; - char m_pArgvBuffer[COMMAND_MAX_LENGTH]; - const char* m_ppArgv[COMMAND_MAX_ARGC]; -}; - -class ConCommandBase -{ -public: - void* m_pConCommandBaseVTable; //0x0000 - ConCommandBase* m_pNext; //0x0008 - bool m_bRegistered; //0x0010 -private: - char pad_0011[7]; //0x0011 -public: - const char* m_pszName; //0x0018 - const char* m_pszHelpString; //0x0020 -private: - char pad_0028[16]; //0x0028 -public: - __int32 m_nFlags; //0x0038 -private: - char pad_003C[4]; //0x003C -}; //Size: 0x0038 - -class ConVar -{ -public: - ConCommandBase m_ConCommandBase; // 0x0000 - void* m_pConVarVTable; //0x0040 - ConVar* m_pParent; //0x0048 - const char* n_pszDefaultValue; //0x0050 - const char* m_pzsCurrentValue; //0x0058 - __int64 m_iStringLength; //0x0060 - float m_flValue; //0x0068 - int m_iValue; //0x006C - bool m_bHasMin; //0x0070 - float m_flMinValue; //0x0074 - bool m_bHasMax; //0x0078 - float m_flMaxValue; //0x007C - char pad_0080[32]; //0x0080 -}; //Size: 0x00A0 - -class CCVarIteratorInternal // Fully reversed table, just look at the virtual function table and rename the function. -{ -public: - virtual void SetFirst(void) = 0; //0 - virtual void Next(void) = 0; //1 - virtual bool IsValid(void) = 0; //2 - virtual ConCommandBase* Get(void) = 0; //3 -}; - -class CCVar -{ -public: - ConCommandBase* FindCommandBase(const char* szCommandName) // @0x1405983A0 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = ConCommandBase*(__thiscall*)(CCVar*, const char*); - return (*reinterpret_cast(this))[14](this, szCommandName); - } - - ConVar* FindVar(const char* szVarName) // @0x1405983B0 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = ConVar*(__thiscall*)(CCVar*, const char*); - return (*reinterpret_cast(this))[16](this, szVarName); - } - - void* /*Implement ConCommand class.*/ FindCommand(const char* szCommandName) // @0x1405983F0 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void*(__thiscall*)(CCVar*, const char*); - return (*reinterpret_cast(this))[18](this, szCommandName); - } - - CCVarIteratorInternal* FactoryInternalIterator() // @0x140597C10 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = CCVarIteratorInternal*(__thiscall*)(CCVar*); - return (*reinterpret_cast(this))[41](this); - } - - std::unordered_map DumpToMap() - { - std::stringstream ss; - CCVarIteratorInternal* itint = FactoryInternalIterator(); // Allocatd new InternalIterator. - - std::unordered_map allConVars; - - for (itint->SetFirst(); itint->IsValid(); itint->Next()) // Loop through all instances. - { - ConCommandBase* command = itint->Get(); - const char* commandName = command->m_pszName; - allConVars[commandName] = command; - } - - return allConVars; - } -}; - -struct Interface -{ - __int64 (*InterfacePtr)(void); - const char* InterfaceName; - __int64* NextInterfacePtr; -}; - -///////////////////////////////////////////////////////////////////////////// -// Initialize Game Globals -namespace GameGlobals -{ - // Class Instances - extern CHostState* HostState; - extern CInputSystem* InputSystem; - extern CCVar* Cvar; - extern KeyValues** PlaylistKeyValues; - extern CKeyValuesSystem* KeyValuesSystem; - extern CClient* Client; - extern BanList* BanSystem; - - // Var - ConVar* CreateCustomConVar(const char* name, const char* defaultValue, int flags, const char* helpString, bool bMin, float fMin, bool bMax, float fMax, void* callback, void* unk); - void* CreateCustomConCommand(const char* name, const char* helpString, int flags, void* callback, void* callbackAfterExecution); - - // Init - void InitGameGlobals(); - void InitAllCommandVariations(); - void InitPlaylist(); - - extern std::vector allPlaylists; - extern bool IsInitialized; - - // Utility - void DisconnectClient(CClient* client, const char* reason, unsigned __int8 unk1, char unk2); -} \ No newline at end of file diff --git a/r5dedicated/hooks.cpp b/r5dedicated/hooks.cpp deleted file mode 100644 index ad488c56..00000000 --- a/r5dedicated/hooks.cpp +++ /dev/null @@ -1,165 +0,0 @@ -#include "pch.h" -#include "hooks.h" -#include "opcodes.h" -#include "gameclasses.h" - -void Hooks::InstallHooks() -{ - /////////////////////////////////////////////////////////////////////////////// - // Initialize Minhook - MH_Initialize(); - - /////////////////////////////////////////////////////////////////////////////// - // Hook SourceAppSystemGroup functions - MH_CreateHook(addr_CSourceAppSystemGroup_Create, &Hooks::CSourceAppSystemGroup_Create, reinterpret_cast(&originalCSourceAppSystemGroup_Create)); - - /////////////////////////////////////////////////////////////////////////////// - // Hook Squirrel functions - MH_CreateHook(addr_SQVM_Print, &Hooks::SQVM_Print, NULL); - MH_CreateHook(addr_SQVM_LoadRson, &Hooks::SQVM_LoadRson, reinterpret_cast(&originalSQVM_LoadRson)); - MH_CreateHook(addr_SQVM_LoadScript, &Hooks::SQVM_LoadScript, reinterpret_cast(&originalSQVM_LoadScript)); - - /////////////////////////////////////////////////////////////////////////////// - // Hook Game Functions - // MH_CreateHook(addr_CHLClient_FrameStageNotify, &Hooks::FrameStageNotify, reinterpret_cast(&originalFrameStageNotify)); - MH_CreateHook(addr_CVEngineServer_IsPersistenceDataAvailable, &Hooks::IsPersistenceDataAvailable, reinterpret_cast(&originalIsPersistenceDataAvailable)); - - /////////////////////////////////////////////////////////////////////////////// - // Hook Netchan functions - MH_CreateHook(addr_NET_ReceiveDatagram, &Hooks::NET_ReceiveDatagram, reinterpret_cast(&originalNET_ReceiveDatagram)); - MH_CreateHook(addr_NET_SendDatagram, &Hooks::NET_SendDatagram, reinterpret_cast(&originalNET_SendDatagram)); - - /////////////////////////////////////////////////////////////////////////////// - // Hook ConVar | ConCommand functions. - MH_CreateHook(addr_ConVar_IsFlagSet, &Hooks::ConVar_IsFlagSet, NULL); - MH_CreateHook(addr_ConCommand_IsFlagSet, &Hooks::ConCommand_IsFlagSet, NULL); - - /////////////////////////////////////////////////////////////////////////////// - // Hook Utility functions - MH_CreateHook(addr_MSG_EngineError, &Hooks::MSG_EngineError, reinterpret_cast(&originalMSG_EngineError)); - - /////////////////////////////////////////////////////////////////////////////// - // Enable SourceAppSystemGroup hooks - MH_EnableHook(addr_CSourceAppSystemGroup_Create); - - /////////////////////////////////////////////////////////////////////////////// - // Enable Squirrel hooks - MH_EnableHook(addr_SQVM_Print); - MH_EnableHook(addr_SQVM_LoadRson); - MH_EnableHook(addr_SQVM_LoadScript); - - /////////////////////////////////////////////////////////////////////////////// - // Enable Game hooks - MH_EnableHook(addr_CHLClient_FrameStageNotify); - MH_EnableHook(addr_CVEngineServer_IsPersistenceDataAvailable); - - /////////////////////////////////////////////////////////////////////////////// - // Enable ConVar | ConCommand hooks - MH_EnableHook(addr_ConVar_IsFlagSet); - MH_EnableHook(addr_ConCommand_IsFlagSet); - - /////////////////////////////////////////////////////////////////////////////// - // Enabled Utility hooks - MH_EnableHook(addr_MSG_EngineError); - - /////////////////////////////////////////////////////////////////////////////// - // Set global variables - GameGlobals::InitGameGlobals(); -} - -void Hooks::RemoveHooks() -{ - /////////////////////////////////////////////////////////////////////////////// - // Hook SourceAppSystemGroup functions - MH_RemoveHook(addr_CSourceAppSystemGroup_Create); - - /////////////////////////////////////////////////////////////////////////////// - // Unhook Squirrel functions - MH_RemoveHook(addr_SQVM_Print); - MH_RemoveHook(addr_SQVM_LoadRson); - MH_RemoveHook(addr_SQVM_LoadScript); - - /////////////////////////////////////////////////////////////////////////////// - // Unhook Game Functions - MH_RemoveHook(addr_CHLClient_FrameStageNotify); - MH_RemoveHook(addr_CVEngineServer_IsPersistenceDataAvailable); - - /////////////////////////////////////////////////////////////////////////////// - // Unhook Netchan functions - MH_RemoveHook(addr_NET_ReceiveDatagram); - MH_RemoveHook(addr_NET_SendDatagram); - - /////////////////////////////////////////////////////////////////////////////// - // Unhook ConVar | ConCommand functions. - MH_RemoveHook(addr_ConVar_IsFlagSet); - MH_RemoveHook(addr_ConCommand_IsFlagSet); - - /////////////////////////////////////////////////////////////////////////////// - // Unhook Utility functions - MH_RemoveHook(addr_MSG_EngineError); - - /////////////////////////////////////////////////////////////////////////////// - // Reset Minhook - MH_Uninitialize(); -} - -//################################################################################# -// TOGGLES -//################################################################################# - -void Hooks::ToggleDevCommands() -{ - static bool bDev = true;; - - if (!bDev) - { - MH_EnableHook(addr_ConVar_IsFlagSet); - MH_EnableHook(addr_ConCommand_IsFlagSet); - printf("\n"); - printf("+--------------------------------------------------------+\n"); - printf("|>>>>>>>>>>>>>| DEVONLY COMMANDS ACTIVATED |<<<<<<<<<<<<<|\n"); - printf("+--------------------------------------------------------+\n"); - printf("\n"); - - } - else - { - MH_DisableHook(addr_ConVar_IsFlagSet); - MH_DisableHook(addr_ConCommand_IsFlagSet); - printf("\n"); - printf("+--------------------------------------------------------+\n"); - printf("|>>>>>>>>>>>>| DEVONLY COMMANDS DEACTIVATED |<<<<<<<<<<<<|\n"); - printf("+--------------------------------------------------------+\n"); - printf("\n"); - } - - bDev = !bDev; -} - -void Hooks::ToggleNetTrace() -{ - static bool bNet = true; - - if (!bNet) - { - MH_EnableHook(addr_NET_ReceiveDatagram); - MH_EnableHook(addr_NET_SendDatagram); - printf("\n"); - printf("+--------------------------------------------------------+\n"); - printf("|>>>>>>>>>>>>>| NETCHANNEL TRACE ACTIVATED |<<<<<<<<<<<<<|\n"); - printf("+--------------------------------------------------------+\n"); - printf("\n"); - } - else - { - MH_DisableHook(addr_NET_ReceiveDatagram); - MH_DisableHook(addr_NET_SendDatagram); - printf("\n"); - printf("+--------------------------------------------------------+\n"); - printf("|>>>>>>>>>>>>| NETCHANNEL TRACE DEACTIVATED |<<<<<<<<<<<<|\n"); - printf("+--------------------------------------------------------+\n"); - printf("\n"); - } - - bNet = !bNet; -} diff --git a/r5dedicated/hooks.h b/r5dedicated/hooks.h deleted file mode 100644 index 360bddc7..00000000 --- a/r5dedicated/hooks.h +++ /dev/null @@ -1,169 +0,0 @@ -#pragma once -#include "pch.h" -// Define the signatures or offsets to be searched and hooked -namespace -{ - Module r5_patterns = Module("r5apex.exe"); // Create module class instance. - -#pragma region CSourceAppSystemGroup - FUNC_AT_ADDRESS(addr_CSourceAppSystemGroup_Create, char(*)(__int64), r5_patterns.PatternSearch("48 89 5C 24 ? 48 89 74 24 ? 57 48 83 EC 20 48 8B F9 E8 ? ? ? ? 33 C9").GetPtr()); -#pragma endregion - -#pragma region Console - /*0x140202090*/ - FUNC_AT_ADDRESS(addr_CommandExecute, void(*)(void*, const char*), r5_patterns.PatternSearch("48 89 5C 24 ? 57 48 83 EC 20 48 8D 0D ? ? ? ? 41 8B D8").GetPtr()); - - /*0x14046FE90*/ - FUNC_AT_ADDRESS(addr_ConVar_IsFlagSet, bool(*)(int**, int), r5_patterns.PatternSearch("48 8B 41 48 85 50 38").GetPtr()); - - /*0x14046F490*/ - FUNC_AT_ADDRESS(addr_ConCommand_IsFlagSet, bool(*)(int*, int), r5_patterns.PatternSearch("85 51 38 0F 95 C0 C3").GetPtr()); -#pragma endregion - -#pragma region Squirrel - /*0x141057FD0*/ - FUNC_AT_ADDRESS(addr_SQVM_Print, void*, r5_patterns.PatternSearch("83 F8 01 48 8D 3D ? ? ? ?").OffsetSelf(0x3).FollowNearCallSelf(0x3, 0x7).GetPtr()); - - //DWORD64 p_SQVM_LoadScript = FindPattern("r5apex.exe", (const unsigned char*)"\x48\x89\x5C\x24\x10\x48\x89\x74\x24\x18\x48\x89\x7C\x24\x20\x48\x89\x4C\x24\x08\x55\x41\x54\x41\x55\x41\x56\x41\x57\x48\x8D\x6C", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); // For S0 and S1 - - /*0x141055630*/ - // For anything S2 and above (current S8 - FUNC_AT_ADDRESS(addr_SQVM_LoadScript, bool(*)(void*, const char*, const char*, int), r5_patterns.PatternSearch("48 8B C4 48 89 48 08 55 41 56 48 8D 68").GetPtr()); - - /*0x140C957E0*/ - FUNC_AT_ADDRESS(addr_SQVM_LoadRson, int(*)(const char*), r5_patterns.PatternSearch("4C 8B DC 49 89 5B 08 57 48 81 EC A0 00 00 00 33").GetPtr()); -#pragma endregion - -#pragma region NetChannel - /*0x1402655F0*/ - FUNC_AT_ADDRESS(addr_NET_ReceiveDatagram, bool(*)(int, void*, bool), r5_patterns.PatternSearch("48 89 74 24 18 48 89 7C 24 20 55 41 54 41 55 41 56 41 57 48 8D AC 24 50 EB").GetPtr()); - - /*0x1402662D0*/ - FUNC_AT_ADDRESS(addr_NET_SendDatagram, int(*)(SOCKET, const char*, int, int), r5_patterns.PatternSearch("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 41 56 41 57 48 81 EC ? 05 ? ?").GetPtr()); - - /*0x14025F190*/ - FUNC_AT_ADDRESS(addr_NetChan_Shutdown, void(*)(void*, const char*, unsigned __int8, char), r5_patterns.StringSearch("Disconnect by server.\n").FindPatternSelf("E8 ? ? ? ? 4C 89 B3 ? ? ? ?", MemoryAddress::Direction::DOWN).FollowNearCallSelf().GetPtr()); -#pragma endregion - -#pragma region CHLClient - /*0x1405C0740*/ - FUNC_AT_ADDRESS(addr_CHLClient_FrameStageNotify, void(*)(void* rcx, int curStage), r5_patterns.PatternSearch("48 83 EC 28 89 15 ?? ?? ?? ??").GetPtr()); -#pragma endregion - -#pragma region CClientState - /*0x1418223E4*/ - FUNC_AT_ADDRESS(addr_m_bRestrictServerCommands, void*, r5_patterns.StringSearch("DevShotGenerator_Init()").FindPatternSelf("88 05", MemoryAddress::Direction::UP).ResolveRelativeAddressSelf(0x2).OffsetSelf(0x2).GetPtr()); -#pragma endregion - -#pragma region CVEngineServer - /*0x140315CF0*/ - FUNC_AT_ADDRESS(addr_CVEngineServer_IsPersistenceDataAvailable, bool(*)(__int64, int), r5_patterns.PatternSearch("3B 15 ?? ?? ?? ?? 7D 33").GetPtr()); -#pragma endregion - -#pragma region Utility - /*0x140295600*/ - FUNC_AT_ADDRESS(addr_MSG_EngineError, int(*)(char*, va_list), r5_patterns.PatternSearch("48 89 5C 24 08 48 89 74 24 10 57 48 81 EC 30 08 00 00 48 8B DA").GetPtr()); - - /*0x1401B31C0*/ - FUNC_AT_ADDRESS(addr_MemAlloc_Wrapper, void* (*)(__int64), r5_patterns.StringSearch("ConversionModeMenu").FindPatternSelf("E8 ? ? ? ? 48", MemoryAddress::Direction::UP).FollowNearCallSelf().GetPtr()); -#pragma endregion - // Un-used atm. - // DWORD64 p_KeyValues_FindKey = /*1404744E0*/ reinterpret_cast(PatternScan("r5apex.exe", "40 56 57 41 57 48 81 EC ?? ?? ?? ?? 45")); - -#pragma region KeyValues -/*0x1404744E0*/ - FUNC_AT_ADDRESS(addr_KeyValues_FindKey, void* (*)(void*, const char*, bool), r5_patterns.PatternSearch("40 56 57 41 57 48 81 EC ?? ?? ?? ?? 45").GetPtr()); -#pragma endregion - - - void PrintHAddress() // Test the sigscan results - { - std::cout << "+--------------------------------------------------------+" << std::endl; - PRINT_ADDRESS("CSourceAppSystemGroup::Create", addr_CSourceAppSystemGroup_Create); - PRINT_ADDRESS("CommandExecute", addr_CommandExecute); - PRINT_ADDRESS("ConVar_IsFlagSet", addr_ConVar_IsFlagSet); - PRINT_ADDRESS("ConCommand_IsFlagSet", addr_ConCommand_IsFlagSet); - PRINT_ADDRESS("SQVM_Print", addr_SQVM_Print); - PRINT_ADDRESS("SQVM_LoadScript", addr_SQVM_LoadScript); - PRINT_ADDRESS("SQVM_LoadRson", addr_SQVM_LoadRson); - PRINT_ADDRESS("NET_ReceiveDatagram", addr_NET_ReceiveDatagram); - PRINT_ADDRESS("NET_SendDatagram ", addr_NET_SendDatagram); - PRINT_ADDRESS("NetChan_Shutdown ", addr_NetChan_Shutdown); - PRINT_ADDRESS("CHLClient::FrameStageNotify", addr_CHLClient_FrameStageNotify); - PRINT_ADDRESS("CVEngineServer::IsPersistenceDataAvailable", addr_CVEngineServer_IsPersistenceDataAvailable); - PRINT_ADDRESS("MSG_EngineError", addr_MSG_EngineError); - PRINT_ADDRESS("MemAlloc_Wrapper", addr_MemAlloc_Wrapper); - PRINT_ADDRESS("KeyValues_FindKey", addr_KeyValues_FindKey); - std::cout << "+--------------------------------------------------------+" << std::endl; - // TODO implement error handling when sigscan fails or result is 0 - } -} - -inline bool g_bDebugLoading = false; -inline bool g_bReturnAllFalse = false; -inline bool g_bDebugConsole = false; - -namespace Hooks -{ -#pragma region CSourceAppSystemGroup - char __fastcall CSourceAppSystemGroup_Create(__int64 a1); - - using CSourceAppSystemGroup_CreateFn = char(*)(__int64); - extern CSourceAppSystemGroup_CreateFn originalCSourceAppSystemGroup_Create; -#pragma endregion - -#pragma region CHLClient -// void __fastcall FrameStageNotify(CHLClient* rcx, ClientFrameStage_t curStage); - -// using FrameStageNotifyFn = void(__fastcall*)(CHLClient*, ClientFrameStage_t); -// extern FrameStageNotifyFn originalFrameStageNotify; -#pragma endregion - -#pragma region Squirrel - void* SQVM_Print(void* sqvm, char* fmt, ...); - __int64 SQVM_LoadRson(const char* rson_name); - bool SQVM_LoadScript(void* sqvm, const char* script_path, const char* script_name, int flag); - - using SQVM_LoadRsonFn = __int64(*)(const char*); - extern SQVM_LoadRsonFn originalSQVM_LoadRson; - - using SQVM_LoadScriptFn = bool(*)(void*, const char*, const char*, int); - extern SQVM_LoadScriptFn originalSQVM_LoadScript; -#pragma endregion - -#pragma region CVEngineServer - bool IsPersistenceDataAvailable(__int64 thisptr, int client); - - using IsPersistenceDataAvailableFn = bool(*)(__int64, int); - extern IsPersistenceDataAvailableFn originalIsPersistenceDataAvailable; -#pragma endregion - -#pragma region NetChannel - bool NET_ReceiveDatagram(int sock, void* inpacket, bool raw); - unsigned int NET_SendDatagram(SOCKET s, const char* buf, int len, int flags); - - using NET_ReceiveDatagramFn = bool(*)(int, void*, bool); - extern NET_ReceiveDatagramFn originalNET_ReceiveDatagram; - - using NET_SendDatagramFn = unsigned int(*)(SOCKET, const char*, int, int); - extern NET_SendDatagramFn originalNET_SendDatagram; -#pragma endregion - -#pragma region ConVar - bool ConVar_IsFlagSet(int** cvar, int flag); - bool ConCommand_IsFlagSet(int* cmd, int flag); -#pragma endregion - -#pragma region Utility - int MSG_EngineError(char* fmt, va_list args); - - using MSG_EngineErrorFn = int(*)(char*, va_list); - extern MSG_EngineErrorFn originalMSG_EngineError; -#pragma endregion - - void InstallHooks(); - void RemoveHooks(); - void ToggleNetTrace(); - void ToggleDevCommands(); - void DedicatedPatch(); -} \ No newline at end of file diff --git a/r5dedicated/iconvar.cpp b/r5dedicated/iconvar.cpp deleted file mode 100644 index 75002dff..00000000 --- a/r5dedicated/iconvar.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -//----------------------------------------------------------------------------- -// Purpose: test each ConVar query before setting the cvar -// Input : **cvar - flag -// Output : true if change is not permitted, false if permitted -//----------------------------------------------------------------------------- - -bool Hooks::ConVar_IsFlagSet(int** cvar, int flag) -{ - int real_flags = *(*(cvar + (72 / (sizeof(void*)))) + (56 / sizeof(int))); - if (g_bDebugConsole) - { - printf("--------------------------------------------------\n"); - printf(" Flaged: %08X\n", real_flags); - } - // Mask off FCVAR_CHEATS and FCVAR_DEVELOPMENTONLY - real_flags &= 0xFFFFBFFD; - if (g_bDebugConsole) - { - printf(" Masked: %08X\n", real_flags); - printf(" Verify: %08X\n", flag); - printf("--------------------------------------------------\n"); - } - if (flag & 0x80000) { return true; } - - if (!g_bReturnAllFalse) - { - return (real_flags & flag) != 0; - } - else - { - return false; - } -} - -//----------------------------------------------------------------------------- -// Purpose: test each ConCommand query before execution -// Input : *cmd - flag -// Output : true if execution is not permitted, false if permitted -//----------------------------------------------------------------------------- - -bool Hooks::ConCommand_IsFlagSet(int* cmd, int flag) -{ - int real_flags = *((cmd + (56 / sizeof(int)))); - if (g_bDebugConsole) - { - printf("--------------------------------------------------\n"); - printf(" Flaged: %08X\n", real_flags); - } - // Mask off FCVAR_CHEATS and FCVAR_DEVELOPMENTONLY - real_flags &= 0xFFFFBFFD; - if (g_bDebugConsole) - { - printf(" Masked: %08X\n", real_flags); - printf(" Verify: %08X\n", flag); - printf("--------------------------------------------------\n"); - } - if (flag & 0x80000) { return true; } - - if (!g_bReturnAllFalse) - { - return(real_flags & flag) != 0; - } - else - { - return false; - } -} \ No newline at end of file diff --git a/r5dedicated/msgbox.cpp b/r5dedicated/msgbox.cpp deleted file mode 100644 index 58a7727b..00000000 --- a/r5dedicated/msgbox.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -namespace Hooks -{ - MSG_EngineErrorFn originalMSG_EngineError = nullptr; -} - -//----------------------------------------------------------------------------- -// Engine Error message box -//----------------------------------------------------------------------------- - -int Hooks::MSG_EngineError(char* fmt, va_list args) -{ - printf("\nENGINE ERROR #####################################\n"); - vprintf(fmt, args); - - return originalMSG_EngineError(fmt, args); -} \ No newline at end of file diff --git a/r5dedicated/net.cpp b/r5dedicated/net.cpp deleted file mode 100644 index 84d2cd50..00000000 --- a/r5dedicated/net.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -namespace Hooks -{ - NET_ReceiveDatagramFn originalNET_ReceiveDatagram = nullptr; - NET_SendDatagramFn originalNET_SendDatagram = nullptr; -} - -typedef unsigned __int64 QWORD; - -struct __declspec(align(8)) netpacket_t -{ - DWORD family_maybe; - sockaddr_in sin; - WORD sin_port; - BYTE gap16; - BYTE byte17; - DWORD source; - double received; - unsigned __int8* data; - QWORD label; - BYTE byte38; - QWORD qword40; - QWORD qword48; - BYTE gap50[8]; - QWORD qword58; - QWORD qword60; - QWORD qword68; - int less_than_12; - DWORD wiresize; - BYTE gap78[8]; - QWORD qword80; -}; - -//----------------------------------------------------------------------------- -// Purpose: hook and log the receive datagram -//----------------------------------------------------------------------------- -bool Hooks::NET_ReceiveDatagram(int sock, void* inpacket, bool raw) -{ - bool result = originalNET_ReceiveDatagram(sock, inpacket, raw); - if (result) - { - int i = NULL; - netpacket_t* pkt = (netpacket_t*)inpacket; - - /////////////////////////////////////////////////////////////////////////// - // Log received packet data - HexDump("[+] NET_ReceiveDatagram", 0, &pkt->data[i], pkt->wiresize); - } - - return result; -} - -//----------------------------------------------------------------------------- -// Purpose: hook and log send datagram -//----------------------------------------------------------------------------- -unsigned int Hooks::NET_SendDatagram(SOCKET s, const char* buf, int len, int flags) -{ - unsigned int result = originalNET_SendDatagram(s, buf, len, flags); - if (result) - { - /////////////////////////////////////////////////////////////////////////// - // Log transmitted packet data - HexDump("[+] NET_SendDatagram", 0, buf, len); - } - - return result; -} \ No newline at end of file diff --git a/r5dedicated/opcodes.h b/r5dedicated/opcodes.h deleted file mode 100644 index 9ba0943c..00000000 --- a/r5dedicated/opcodes.h +++ /dev/null @@ -1,117 +0,0 @@ -#pragma once - -inline HANDLE GameProcess = GetCurrentProcess(); -void SetCHostState(); - -namespace -{ - Module r5_op = Module("r5apex.exe"); // Create module class instance. - -#pragma region Origin - /*0x14032EEA0*/ - MemoryAddress Origin_Init = r5_op.PatternSearch("48 83 EC 28 80 3D ? ? ? 23 ? 0F 85 ? 02 ?"); - - /*0x140330290*/ - MemoryAddress Origin_SetState = r5_op.PatternSearch("48 81 EC 58 04 ? ? 80 3D ? ? ? ? ? 0F 84"); -#pragma endregion - -#pragma region Engine - /*0x14043FB90*/ - MemoryAddress dst002 = r5_op.PatternSearch("48 89 4C 24 08 56 41 55 48 81 EC 68 03 ? ? 4C"); - - /*0x14022A4A0*/ - MemoryAddress dst004 = r5_op.PatternSearch("48 83 EC 38 0F 29 74 24 20 48 89 5C 24 40 48 8B"); - - /*0x140238DA0*/ - MemoryAddress Host_NewGame = r5_op.PatternSearch("48 8B C4 ? 41 54 41 ? 48 81 EC ? ? ? ? F2"); -#pragma endregion - -#pragma region NetChannel - /*0x14030D000*/ - MemoryAddress CServer_Auth = r5_op.PatternSearch("40 55 57 41 55 41 57 48 8D AC 24 ? ? ? ?"); -#pragma endregion - -#pragma region FairFight - /*0x140303AE0*/ - MemoryAddress FairFight_Init = r5_op.PatternSearch("40 53 48 83 EC 20 8B 81 B0 03 ? ? 48 8B D9 C6"); -#pragma endregion - - - // TODO: create patterns instead and rename to function names. - // Renderer - MemoryAddress r0 = 0x00000001402FE280; // - MemoryAddress gCShaderGlue__Init = 0x00000001403B3A50; // - MemoryAddress gMatSync = 0x00000001403DEE90; // - MemoryAddress gCMaterialSystem__MatsysMode_Init = 0x00000001403BD120; // - MemoryAddress r4 = 0x0000000140404380; // - MemoryAddress r5 = 0x000000014040D850; // - MemoryAddress r6 = 0x0000000140413260; // - MemoryAddress r7 = 0x00000001404093F0; // - MemoryAddress r8 = 0x00000001403D2E60; // - MemoryAddress d3d11init = 0x000000014043CDF0; // - - // Engine - MemoryAddress gHost_Init_0 = 0x0000000140236E40; // main Host_Init()? - MemoryAddress e1 = 0x0000000140FB2F10; // also used by CServerGameDLL - MemoryAddress addr_CEngine_Frame = 0x00000001402970E0; - MemoryAddress e3 = 0x0000000140231C00; - MemoryAddress e4 = 0x0000000140BE1970; - MemoryAddress e5 = 0x0000000140DBBAF0; - MemoryAddress e6 = 0x0000000140DBE610; - MemoryAddress e7 = 0x000000014044AFA0; - MemoryAddress e8 = 0x000000014027EC50; // RenderFrame? - MemoryAddress gCEngineAPI__Init = 0x0000000140342FB0; // - MemoryAddress gCEngineAPI__ModInit = 0x0000000140343DE0; // - MemoryAddress gCEngineAPI__Connect = 0x0000000140342BA0; // - MemoryAddress gCEngineAPI__OnStartup = 0x0000000140343860; // - MemoryAddress gCSourceAppSystemGroup__Create = 0x000000014044AFA0; // - MemoryAddress gCShaderSystem__Init = 0x00000001403DF870; // - MemoryAddress gInitMaterialSystem = 0x000000014024B390; // - MemoryAddress gCVideoMode_Common__DrawStartupGraphic = 0x000000014027F0F0; // - MemoryAddress gShaderDispatch = 0x00000001403EE5C0; - MemoryAddress gShaderCreate = 0x00000001403ECD00; // - MemoryAddress gTextureCreate = 0x00000001403EDCD0; - - MemoryAddress gCShaderSystem__9 = 0x00000001403DFC30; - MemoryAddress gBSP_LUMP_INIT = 0x00000001402546F0; // BSP. - - - MemoryAddress e9 = 0x00000001404066E0; - MemoryAddress e10 = 0x00000001403B49E0; // CMaterialGlue? - - // SERVER - MemoryAddress gHost_Init_1 = 0x0000000140237B00; // server Host_Init()? - MemoryAddress s1 = 0x0000000140231C00; // _Host_RunFrame() with inlined CFrameTimer::MarkFrame()? - MemoryAddress s2 = 0x00000001402312A0; // server HeartBeat? (baseserver.cpp) - MemoryAddress s3 = 0x0000000140FB36D0; // TEMP?? - - // CLIENT - MemoryAddress gHost_Init_2 = 0x0000000140236640; // client Host_Init()? - MemoryAddress gCGame__CreateGameWindow = 0x0000000140299100; // - MemoryAddress c2 = 0x00000001403F4360; // 1403DF870 --> 1403F4360 - MemoryAddress c3 = 0x00000001403F8A80; // 1403DF870 --> 1403F8A40 - MemoryAddress gCHLClient__1000 = 0x00000001405C27B0; // CHLClient + 1000 - MemoryAddress gCHLClient__HudMessage = 0x00000001405BAC00; // CHudMessage - MemoryAddress c6 = 0x00000001403CA2D0; // - - // VGUI - MemoryAddress gCEngineVGui__Init = 0x0000000140282E40; // jumptable - MemoryAddress gCEngineVGui__OnLevelLoadingStarted = 0x00000001402830D0; - MemoryAddress SCR_BeginLoadingPlaque = 0x000000014023E870; - - - void PrintOAddress() // Test the sigscan results - { - std::cout << "+--------------------------------------------------------+" << std::endl; - PRINT_ADDRESS("Origin_Init", Origin_Init.GetPtr()); - PRINT_ADDRESS("Origin_SetState", Origin_SetState.GetPtr()); - PRINT_ADDRESS("dst002", dst002.GetPtr()); - PRINT_ADDRESS("dst004", dst004.GetPtr()); - PRINT_ADDRESS("Host_NewGame", Host_NewGame.GetPtr()); - PRINT_ADDRESS("CServer_Auth", CServer_Auth.GetPtr()); - PRINT_ADDRESS("FairFight_Init", FairFight_Init.GetPtr()); - std::cout << "+--------------------------------------------------------+" << std::endl; - - // TODO implement error handling when sigscan fails or result is 0 - } -} \ No newline at end of file diff --git a/r5dedicated/pch.cpp b/r5dedicated/pch.cpp deleted file mode 100644 index 17305716..00000000 --- a/r5dedicated/pch.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "pch.h" \ No newline at end of file diff --git a/r5dedicated/pch.h b/r5dedicated/pch.h deleted file mode 100644 index 9a8b7cc6..00000000 --- a/r5dedicated/pch.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once -#pragma message("[DEDICATED] pre-compiling headers.\n") - -#define WIN32_LEAN_AND_MEAN // Prevent winsock2 redefinition. -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -// Our headers - -#include "spdlog.h" -#include "sinks/basic_file_sink.h" -#include "sinks/stdout_sinks.h" -#include "sinks/ostream_sink.h" -#include "utility.h" -#include "httplib.h" -#include "json.hpp" - -#include "address.h" - -#pragma once - -#define FUNC_AT_ADDRESS(name, funcbody, addr) \ - using _##name = funcbody; \ - _##name name = (funcbody)addr \ - -#define PRINT_ADDRESS(name, address) std::cout << name << ": " << std::hex << std::uppercase << address << std::endl; diff --git a/r5dedicated/r5dedicated.def b/r5dedicated/r5dedicated.def deleted file mode 100644 index adb0127b..00000000 --- a/r5dedicated/r5dedicated.def +++ /dev/null @@ -1,4 +0,0 @@ -LIBRARY dedicated - -EXPORTS - DummyExport @1 \ No newline at end of file diff --git a/r5dedicated/r5dedicated.vcxproj b/r5dedicated/r5dedicated.vcxproj deleted file mode 100644 index 7ef975cb..00000000 --- a/r5dedicated/r5dedicated.vcxproj +++ /dev/null @@ -1,318 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 16.0 - Win32Proj - {71988d92-343c-49ab-b52b-0ae0e83b0401} - r5dedicated - 10.0 - - - - DynamicLibrary - true - v142 - Unicode - - - DynamicLibrary - false - v142 - true - Unicode - - - DynamicLibrary - true - v142 - MultiByte - - - DynamicLibrary - false - v142 - true - MultiByte - - - - - - - - - - - - - - - - - - - - - true - dedicated - - - false - dedicated - - - true - dedicated - $(VC_IncludePath);$(WindowsSDK_IncludePath);$(SolutionDir)external\minhook\include;$(SolutionDir)external\spdlog\include;$(SolutionDir)shared\include;$(SolutionDir)r5dedicated;$(IncludePath) - $(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(SolutionDir)external\minhook\lib\$(Configuration);$(LibraryPath) - $(SolutionDir)bin\$(Configuration)\ - $(SolutionDir)build\$(ProjectName)\$(Configuration)\ - - - false - dedicated - $(VC_IncludePath);$(WindowsSDK_IncludePath);$(SolutionDir)external\minhook\include;$(SolutionDir)external\spdlog\include;$(SolutionDir)shared\include;$(SolutionDir)r5dedicated;$(IncludePath) - $(SolutionDir)external\minhook\lib\$(Configuration);$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(LibraryPath) - $(SolutionDir)bin\$(Configuration)\ - $(SolutionDir)build\$(ProjectName)\$(Configuration)\ - - - - Level3 - true - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - Default - Default - - - Console - true - - - - - Level3 - true - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - Default - Default - - - Console - true - true - true - - - - - Level3 - true - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpp17 - stdc17 - Use - pch.h - $(IntDir)$(TargetName).pch - MultiThreadedDebug - - - Console - true - Minhook.x64.lib - r5dedicated.def - - - if not EXIST $(SolutionDir)external\minhook\lib\$(Configuration)\Minhook.x64.lib ( -"$(DevEnvDir)devenv" "$(SolutionDir)apex.sln" /Rebuild $(Configuration) /project "$(SolutionDir)external\minhook\libMinHook.vcxproj" -) - - - - - Level3 - true - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpp17 - stdc17 - Disabled - Speed - false - true - Use - pch.h - $(IntDir)$(TargetName).pch - - - Console - true - true - true - Minhook.x64.lib - r5dedicated.def - - - if not EXIST $(SolutionDir)external\minhook\lib\$(Configuration)\Minhook.x64.lib ( -"$(DevEnvDir)devenv" "$(SolutionDir)apex.sln" /Rebuild $(Configuration) /project "$(SolutionDir)external\minhook\libMinHook.vcxproj" -) - - - - - - - - - - - - - - - Create - Create - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/r5dedicated/r5dedicated.vcxproj.filters b/r5dedicated/r5dedicated.vcxproj.filters deleted file mode 100644 index 43adb38d..00000000 --- a/r5dedicated/r5dedicated.vcxproj.filters +++ /dev/null @@ -1,450 +0,0 @@ - - - - - {b5a2491a-0f04-4b55-bc24-bbdd9cd6fc2e} - - - {ef65ed91-551f-4f5d-b2a9-efc26e1af0f6} - - - {22b58a18-d1d7-4e3e-b5de-d9338f3177d3} - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd - - - {a0165b58-06b1-4b6a-b5d1-5d643517ad14} - - - {35b40ed1-12bd-4bcf-9c05-5a42a0096619} - - - {a7199092-e8a9-49fa-97e1-b2d0ea21001b} - - - {bd5aef1a-dad8-45a0-85f2-82bc8f86bed8} - - - {c947552e-2623-4672-b3fa-3b50fe819628} - - - {19dd0a10-5185-4b03-9466-693b86a17dff} - - - {cb271201-f518-4dc4-9ab4-5d7f6a54c175} - - - {12259d0c-172a-4a26-a1d4-5d784f4c3d25} - - - {13bdf72b-f7c0-4658-9899-932f31d03da7} - - - {eaefe9b7-d14d-48b6-878a-53a5ada7454b} - - - {245e8064-9b24-4631-9326-340dfb761fde} - - - {485b5648-149f-4664-a961-be9cd520e9e3} - - - {31cdde4d-3641-497c-9b34-20d3d7c89d87} - - - {338a4fb7-7519-4628-9206-679d33824965} - - - {06affed3-5a59-4b95-88ca-72d92c91909b} - - - {05e6e9a7-801b-49b0-9c5a-21c4868befb7} - - - {cc424eef-0c7a-4fb0-9d84-30bf8db2e253} - - - {74afa89f-72af-4e13-aa90-70f7a1957154} - - - {9381fa63-cf89-4980-8e5a-bf6e43cb2283} - - - {7fd080e8-390a-430b-a94c-e19c5792bf10} - - - {e7e154b6-398e-42f9-bfb9-e80cd306254e} - - - {3c89e0ef-e415-4a91-86b7-2a04a5f03340} - - - - - shared\include - - - shared\include - - - shared\include - - - shared\include - - - core\include - - - core\include - - - core\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include\cfg - - - shared\libraries\spdlog\include\cfg - - - shared\libraries\spdlog\include\cfg - - - shared\libraries\spdlog\include\cfg - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\fmt - - - shared\libraries\spdlog\include\fmt - - - shared\libraries\spdlog\include\fmt - - - shared\libraries\spdlog\include\fmt - - - shared\libraries\spdlog\include\fmt - - - shared\libraries\spdlog\include\fmt\bundled - - - shared\libraries\spdlog\include\fmt\bundled - - - shared\libraries\spdlog\include\fmt\bundled - - - shared\libraries\spdlog\include\fmt\bundled - - - shared\libraries\spdlog\include\fmt\bundled - - - shared\libraries\spdlog\include\fmt\bundled - - - shared\libraries\spdlog\include\fmt\bundled - - - shared\libraries\spdlog\include\fmt\bundled - - - shared\libraries\spdlog\include\fmt\bundled - - - shared\libraries\spdlog\include\fmt\bundled - - - shared\libraries\spdlog\include\fmt\bundled - - - shared\libraries\spdlog\include\fmt\bundled - - - shared\libraries\spdlog\include\fmt\bundled - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\minhook\include - - - hooks\include - - - hooks\include - - - r5-sdk\include - - - r5-sdk\include - - - - - core - - - core - - - core - - - hooks\netchannel - - - hooks\cvengineserver - - - hooks\squirrel - - - hooks\other - - - shared - - - hooks\iconvar - - - hooks - - - hooks - - - hooks\csourceappsystemgroup - - - r5-sdk\src - - - - - core\resource - - - \ No newline at end of file diff --git a/r5dedicated/sqvm.cpp b/r5dedicated/sqvm.cpp deleted file mode 100644 index 137dd52f..00000000 --- a/r5dedicated/sqvm.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -namespace Hooks -{ - SQVM_LoadRsonFn originalSQVM_LoadRson = nullptr; - SQVM_LoadScriptFn originalSQVM_LoadScript = nullptr; -} - -//--------------------------------------------------------------------------------- -// Purpose: prints the output of each VM to the console -//--------------------------------------------------------------------------------- -void* Hooks::SQVM_Print(void* sqvm, char* fmt, ...) -{ - int vmIdx = *(int*)((std::uintptr_t)sqvm + 0x18); - - static char buf[1024]; - static std::string vmType[3] = { "Script(S):", "Script(C):", "Script(U):" }; - - static auto wconsole = spdlog::stdout_logger_mt("sqvm_wconsole"); // windows console - - std::string vmStr = vmType[vmIdx].c_str(); - - wconsole->set_pattern("[%S.%e] %v"); - wconsole->set_level(spdlog::level::debug); - - va_list args; - va_start(args, fmt); - - vsnprintf(buf, sizeof(buf), fmt, args); - - buf[sizeof(buf) - 1] = 0; - va_end(args); - - vmStr.append(buf); - wconsole->debug(vmStr); - - return NULL; -} - -//--------------------------------------------------------------------------------- -// Purpose: loads the include file from the mods directory -//--------------------------------------------------------------------------------- -__int64 Hooks::SQVM_LoadRson(const char* rson_name) -{ - char filepath[MAX_PATH] = { 0 }; - sprintf_s(filepath, MAX_PATH, "platform\\%s", rson_name); - - /////////////////////////////////////////////////////////////////////////////// - // Flip forward slashes in filepath to windows-style backslash - for (int i = 0; i < strlen(filepath); i++) - { - if (filepath[i] == '/') - { - filepath[i] = '\\'; - } - } - - /////////////////////////////////////////////////////////////////////////////// - // Returns the new path if the rson exists on the disk - if (FileExists(filepath) && originalSQVM_LoadRson(rson_name)) - { - printf("\n"); - printf("##################################################\n"); - printf("] '%s'\n", filepath); - printf("##################################################\n"); - printf("\n"); - - return originalSQVM_LoadRson(filepath); - } - - printf("\n"); - printf("##################################################\n"); - printf("] '%s'\n", rson_name); - printf("##################################################\n"); - printf("\n"); - - return originalSQVM_LoadRson(rson_name); -} - -//--------------------------------------------------------------------------------- -// Purpose: loads the script file from the mods directory -//--------------------------------------------------------------------------------- -bool Hooks::SQVM_LoadScript(void* sqvm, const char* script_path, const char* script_name, int flag) -{ - char filepath[MAX_PATH] = { 0 }; - sprintf_s(filepath, MAX_PATH, "platform\\%s", script_path); - - /////////////////////////////////////////////////////////////////////////////// - // Flip forward slashes in filepath to windows-style backslash - for (int i = 0; i < strlen(filepath); i++) - { - if (filepath[i] == '/') - { - filepath[i] = '\\'; - } - } - if (g_bDebugLoading) - { - printf(" [+] Loading SQVM Script '%s' ...\n", filepath); - } - /////////////////////////////////////////////////////////////////////////////// - // Returns true if the script exists on the disk - if (FileExists(filepath) && originalSQVM_LoadScript(sqvm, filepath, script_name, flag)) - { - return true; - } - if (g_bDebugLoading) - { - printf(" [!] FAILED. Try SP / VPK for '%s'\n", filepath); - } - - return originalSQVM_LoadScript(sqvm, script_path, script_name, flag); -} \ No newline at end of file diff --git a/r5dev/bsplib/bsplib.cpp b/r5dev/bsplib/bsplib.cpp new file mode 100644 index 00000000..1e84ec60 --- /dev/null +++ b/r5dev/bsplib/bsplib.cpp @@ -0,0 +1,339 @@ +#include "core/stdafx.h" +#include "tier0/cvar.h" +#include "common/psuedodefs.h" +#include "bsplib/bsplib.h" + +//----------------------------------------------------------------------------- +// Purpose: calculates the view frustum culling data per static prop +//----------------------------------------------------------------------------- +__int64 __fastcall HCalcPropStaticFrustumCulling(__int64 a1, __int64 a2, unsigned int a3, unsigned int a4, __int64 a5, __int64 a6, __int64 a7) +{ + //float v9; // xmm6_4 + //char v10; // r13 + //float v11; // xmm7_4 + //__int64 v12; // rsi + //__int64 v13; // rdx + //__int64 v14; // rbx + //unsigned __int8 v15; // r8 + //const char* v16; // rax + //int v17; // eax + //char v18; // dl + //bool v19; // cc + //char v20; // al + //__int16 v21; // ax + //__int64 v22; // rax + //__m128 v23; // xmm9 + //__m128 v24; // xmm10 + //__m128 v25; // xmm11 + //unsigned __int64 v26; // rbx + //__m128 v27; // xmm0 + //__m128 v28; // xmm8 + //__int64 v29; // rax + //__int64 v30; // rcx + //__int64 v31; // xmm1_8 + //int v32; // er8 + //__m128 v33; // xmm0 + //__int64 v34; // rcx + //unsigned __int64 v35; // rdx + //__int64 v36; // rax + //__int64 v37; // rax + //unsigned __int64 v38; // rcx + //__int64 v39; // rax + //unsigned __int64 v40; // rcx + //__int64 v41; // rax + //float v42; // xmm2_4 + //__int64 v43; // rbx + //float v44; // xmm2_4 + //float v45; // xmm4_4 + //__m128i v46; // xmm1 + //__m128i v47; // xmm2 + //__m128i v48; // xmm3 + //unsigned int v49; // eax + //float v50; // xmm0_4 + //float v51; // xmm2_4 + //float v52; // xmm1_4 + //float v53; // xmm2_4 + //unsigned __int8 v54; // si + //__int64 v55; // rax + //__int64 v56; // rcx + //int v57; // eax + //__int64 v58; // rcx + //int v59; // edx + //__int64 v60; // rax + //__int64 v61; // r13 + //int v62; // eax + //unsigned __int64 v63; // r13 + //__int64 v64; // rdx + //int v65; // er14 + //unsigned __int64 v66; // rdi + //__int64 v67; // r13 + //__int64 v68; // r15 + //void** v69; // rbx + //__int64 v70; // rcx + //__int64 result; // rax + //__m128 v72; // [rsp+38h] [rbp-D0h] BYREF + //__int64 v73; // [rsp+48h] [rbp-C0h] + //__int64 v74; // [rsp+50h] [rbp-B8h] + //__m128 v75; // [rsp+58h] [rbp-B0h] BYREF + //__int64 v76; // [rsp+68h] [rbp-A0h] + //__int64 v77; // [rsp+78h] [rbp-90h] + //__m128 v78[3]; // [rsp+88h] [rbp-80h] BYREF + //char Destination[376]; // [rsp+B8h] [rbp-50h] BYREF + //__int64 v80; // [rsp+278h] [rbp+170h] + //int v83; // [rsp+288h] [rbp+180h] + //int v84; // [rsp+290h] [rbp+188h] + //__int64 v85; // [rsp+298h] [rbp+190h] + //__int64 v86; // [rsp+2A8h] [rbp+1A0h] + + //v9 = 1.0; + //v10 = a4; + //*(_QWORD*)(a1 + 20) = *(_QWORD*)a5; + //*(_DWORD*)(a1 + 28) = *(_DWORD*)(a5 + 8); + //*(_DWORD*)(a1 + 8) = a4; + //v11 = *(float*)(a5 + 24); + //v12 = a4 >> 1; + //*(float*)(a1 + 12) = 1.0 / (float)(v11 * v11); + //v13 = *(unsigned __int16*)(a7 + 320); + //*(_WORD*)a1 = v13; + //v14 = (*(__int64(__fastcall**)(__int64, __int64, _QWORD))(*(_QWORD*)qword_14D40B328 + 104i64))(qword_14D40B328, v13, 0i64); + //v85 = v14; + //if ((*(_BYTE*)(v14 + 156) & 0x10) == 0 && dword_1696A9D20 < 100) + // ++dword_1696A9D20; + //v15 = *(_BYTE*)(a5 + 30); + //if (v15 > 2u && (unsigned __int8)(v15 - 6) > 2u) + //{ + // v16 = (const char*)(*((__int64(__fastcall**)(void**, __int64))g_CModelLoader + 4))(&g_CModelLoader, a7); + // strncpy_s(Destination, v16, 0x104ui64); + // v15 = 0; + //} + //v17 = *(unsigned __int8*)(a5 + 32); + //*(_BYTE*)(a1 + 4) = v17; + //v18 = v17; + //v19 = v17 < *(_DWORD*)(v14 + 228); + //*(_BYTE*)(a1 + 5) = v15; + //if (!v19) + // v18 = 0; + //*(_BYTE*)(a1 + 4) = v18; + //v20 = *(_BYTE*)(a5 + 31); + //if ((v20 & 4) != 0) + //{ + // v15 |= 0x40u; + // *(_BYTE*)(a1 + 5) = v15; + // v20 = *(_BYTE*)(a5 + 31); + //} + //if ((v20 & 8) != 0) + //{ + // v15 |= 0x20u; + // *(_BYTE*)(a1 + 5) = v15; + // v20 = *(_BYTE*)(a5 + 31); + //} + //if ((v20 & 0x30) != 16) + // *(_BYTE*)(a1 + 5) = v15 | 0x10; + //v21 = 0; + //if (*(_WORD*)(a5 + 34) != 0xFFFF) + // v21 = *(_WORD*)(a5 + 34); + //*(_WORD*)(a1 + 2) = v21; + //sub_1404365A0(v78, a5, a5 + 12, 0); + //v22 = qword_141744EA8; + //v23 = v78[0]; + //v24 = v78[1]; + //v25 = v78[2]; + //v26 = (unsigned __int64)(unsigned int)v12 << 6; + //*(__m128*)(v26 + qword_141744EA8) = v78[0]; + //*(__m128*)(v26 + v22 + 16) = v24; + //*(__m128*)(v26 + v22 + 32) = v25; + //v27 = _mm_mul_ps(_mm_cvtepi32_ps(_mm_unpacklo_epi16(_mm_unpacklo_epi8(_mm_cvtsi32_si128(*(_DWORD*)(a5 + 52)), (__m128i)0i64), (__m128i)0i64)), (__m128)xmmword_1415BD270); + //v75 = v27; + //*(double*)v27.m128_u64 = _mm_castsi128_ps(sub_140270130(&v75)); + //v28 = v27; + //*(__m128*)(v26 + qword_141744EA8 + 48) = v27; + //((void(__fastcall*)(void***, __int64, __int64*, char*))off_141731448[10])(&off_141731448, a7, (long long*)&v72, (char*)&v72 + 12); + //sub_14028F170((unsigned int)&v75, (unsigned int)&v75.m128_u32[3], (__m128*)v78, (__m128i*)&v72, (__m128i*)&v72 + 12); + //v29 = qword_141744EA0; + //v30 = 3 * v12; + //v31 = v76; + //*(__m128*)(qword_141744EA0 + 8 * v30) = v75; + //*(_QWORD*)(v29 + 8 * v30 + 16) = v31; + //if ((v10 & 1) != 0) + //{ + // v32 = dword_141744EBC; + // v33 = v72; + // *(_DWORD*)a2 = *(_DWORD*)(a6 + 48); + // *(_DWORD*)(a2 + 4) = *(_DWORD*)(a6 + 52); + // *(_QWORD*)(a2 + 8) = 0i64; + // v34 = 3i64 * (unsigned int)(v32 + v12); + // v35 = (unsigned __int64)(unsigned int)(v32 + v12) << 6; + // v36 = qword_141744EA0; + // *(__m128*)(qword_141744EA0 + 8 * v34) = v33; + // *(_QWORD*)(v36 + 8 * v34 + 16) = v73; + // v37 = qword_141744EA8; + // v38 = (unsigned __int64)(unsigned int)(v12 + 2 * v32) << 6; + // *(__m128*)(v35 + qword_141744EA8) = v23; + // *(__m128*)(v35 + v37 + 16) = v24; + // *(__m128*)(v35 + v37 + 32) = v25; + // *(__m128*)(v35 + qword_141744EA8 + 48) = v28; + // v39 = qword_141744EA8; + // *(__m128*)(v38 + qword_141744EA8) = v23; + // *(__m128*)(v38 + v39 + 16) = v24; + // *(__m128*)(v38 + v39 + 32) = v25; + // *(__m128*)(v38 + qword_141744EA8 + 48) = v28; + // v40 = (unsigned __int64)(unsigned int)(v32 + v12 + 2 * v32) << 6; + // *(__m128*)(v40 + qword_141744EA8 + 48) = v28; + // v41 = qword_141744EA8; + // *(__m128*)(v40 + qword_141744EA8) = *(__m128*)a6; + // *(__m128*)(v40 + v41 + 16) = *(__m128*)(a6 + 16); + // *(__m128*)(v40 + v41 + 32) = *(__m128*)(a6 + 32); + //} + //v42 = *(float*)(a5 + 36); + //v43 = v85; + //if (v42 <= 0.0) + //{ + // if ((*(_DWORD*)(v85 + 156) & 0x800) != 0) + // { + // v42 = 227023.36; + // } + // else + // { + // v44 = *(float*)(v85 + 364); + // if (v44 <= 0.0) + // v42 = fmaxf( + // (float)((float)(sqrtf( + // (float)((float)((float)(*(float*)&v72 - *((float*)&v72 + 3)) * (float)(*(float*)&v72 - *((float*)&v72 + 3))) + // + (float)((float)(*((float*)&v72 + 1) - *(float*)&v73) * (float)(*((float*)&v72 + 1) - *(float*)&v73))) + // + (float)((float)(*((float*)&v72 + 2) - *((float*)&v73 + 1)) * (float)(*((float*)&v72 + 2) - *((float*)&v73 + 1)))) + // * 0.5) + // * v11) + // * g_pCvar->FindVar("model_defaultFadeDistScale")->m_pParent->m_flValue, + // g_pCvar->FindVar("model_defaultFadeDistMin")->m_pParent->m_flValue); + // else + // v42 = v44 * v11; + // } + //} + //v45 = fmaxf(v42, 100.0); + //*(float*)(a1 + 16) = v45 * v45; + //v46 = (__m128i) * (unsigned int*)(v85 + 368); + //if (*(float*)v46.m128i_i32 <= 0.0) + //{ + // LOWORD(v49) = 0; + //} + //else + //{ + // *(float*)v46.m128i_i32 = *(float*)v46.m128i_i32 * *(float*)v46.m128i_i32; + // v47 = v46; + // *(float*)v47.m128i_i32 = fmaxf(*(float*)v46.m128i_i32, 1.0004883); + // v48 = v47; + // *(float*)v48.m128i_i32 = fminf(*(float*)v47.m128i_i32, 4293918700.0); + // v49 = (unsigned int)(_mm_cvtsi128_si32(v48) - 1065351168) >> 12; + //} + //*(_WORD*)(a1 + 6) = v49; + //v50 = *(float*)(qword_141744EA0 + 24 * ((unsigned __int64)*(unsigned int*)(a1 + 8) >> 1) + 8) + // - *(float*)(qword_141744EA0 + 24 * ((unsigned __int64)*(unsigned int*)(a1 + 8) >> 1) + 20); + //v51 = *(float*)(qword_141744EA0 + 24 * ((unsigned __int64)*(unsigned int*)(a1 + 8) >> 1) + 4) + // - *(float*)(qword_141744EA0 + 24 * ((unsigned __int64)*(unsigned int*)(a1 + 8) >> 1) + 16); + //v52 = *(float*)(qword_141744EA0 + 24 * ((unsigned __int64)*(unsigned int*)(a1 + 8) >> 1)) + // - *(float*)(qword_141744EA0 + 24 * ((unsigned __int64)*(unsigned int*)(a1 + 8) >> 1) + 12); + //v53 = (float)((float)(v51 * v51) + (float)(v52 * v52)) + (float)(v50 * v50); + //if (v45 >= 227023.363449684) + // v9 = g_pCvar->FindVar("staticProp_no_fade_scalar")->m_pParent->m_flValue; + //v54 = 0; + //*(float*)(qword_141744E88 + 8i64 * a3) = v9 * (float)(1.0 / (float)(v53 * g_pCvar->FindVar("staticProp_gather_size_weight")->m_pParent->m_flValue)); + //v55 = qword_141744E88; + //*(_BYTE*)(qword_141744E88 + 8i64 * a3 + 4) &= 0xFEu; + //*(_BYTE*)(v55 + 8i64 * a3 + 4) |= v45 >= 227023.363449684; + //v56 = *(_QWORD*)(*(__int64(__fastcall**)(__int64, _QWORD))(*(_QWORD*)qword_14D40B328 + 160i64))(qword_14D40B328, *(unsigned __int16*)(a7 + 320)); + //v57 = *(unsigned __int16*)(a5 + 32); + //v77 = v56; + //v58 = v85 + *(int*)(v85 + 232) + 2i64 * v57 * *(_DWORD*)(v85 + 224); + //v59 = 0; + //v86 = v58; + //v84 = 0; + //if (*(int*)(v85 + 236) <= 0) + // return 0i64; + //v60 = 0i64; + //v74 = 0i64; + //do + //{ + // v61 = v60 + *(int*)(v43 + 240); + // v62 = 0; + // v63 = v43 + v61; + // v83 = 0; + // v75.m128_u64[0] = v63; + // if (*(int*)(v63 + 4) > 0) + // { + // v64 = 0i64; + // v80 = 0i64; + // do + // { + // v65 = 0; + // v66 = v63 + v64 + *(int*)(v63 + 12); + // if (*(int*)(v66 + 76) > 0) + // { + // v67 = v77; + // v68 = 0i64; + // do + // { + // v69 = *(void***)(v67 + 8i64 * *(__int16*)(v58 + 2i64 * *(int*)(v68 + *(int*)(v66 + 80) + v66))); + // if (!(*((unsigned __int8(__fastcall**)(void**)) * v69 + 86))(v69)) + // { + // if (!(*((unsigned __int8(__fastcall**)(void**)) * v69 + 32))(v69) && (*((unsigned __int8(__fastcall**)(void**)) * v69 + 31))(v69)) + // { + // v70 = 0i64; + // if (dword_141744EE8) + // { + // while (off_141744E70[v70 + 16] != v69) + // { + // v70 = (unsigned int)(v70 + 1); + // if ((unsigned int)v70 >= dword_141744EE8) + // goto LABEL_42; + // } + // } + // else + // { + // LABEL_42: + // off_141744E70[(unsigned int)dword_141744EE8++ + 16] = v69; + // } + // } + // if ((*((unsigned __int8(__fastcall**)(void**)) * v69 + 21))(v69) && (*((unsigned int(__fastcall**)(void**, __int64)) * v69 + 18))(v69, 1i64)) + // *(_BYTE*)(a1 + 5) |= 0x80u; + // if (!(*((unsigned __int8(__fastcall**)(void**)) * v69 + 48))(v69) && (*((unsigned int(__fastcall**)(void**, __int64)) * v69 + 18))(v69, 21844i64)) + // v54 |= 2u; + // v54 |= (*((unsigned __int8(__fastcall**)(void**)) * v69 + 48))(v69) != 0; + // } + // v58 = v86; + // ++v65; + // v68 += 92i64; + // } while (v65 < *(_DWORD*)(v66 + 76)); + // v63 = v75.m128_u64[0]; + // v62 = v83; + // v64 = v80; + // } + // ++v62; + // v64 += 136i64; + // v83 = v62; + // v80 = v64; + // } while (v62 < *(_DWORD*)(v63 + 4)); + // v43 = v85; + // v59 = v84; + // } + // ++v59; + // v60 = v74 + 16; + // v84 = v59; + // v74 += 16i64; + //} while (v59 < *(_DWORD*)(v43 + 236)); + //result = v54; + //if (v54) + // *(_BYTE*)(a1 + 5) &= 0x7Fu; + //return result; + +return NULL; +} + +void BspLib_Attach() +{ + DetourAttach((LPVOID*)&CalcPropStaticFrustumCulling, &HCalcPropStaticFrustumCulling); +} + +void BspLib_Detach() +{ + DetourDetach((LPVOID*)&CalcPropStaticFrustumCulling, &HCalcPropStaticFrustumCulling); +} diff --git a/r5dev/bsplib/bsplib.h b/r5dev/bsplib/bsplib.h new file mode 100644 index 00000000..d5f5f57c --- /dev/null +++ b/r5dev/bsplib/bsplib.h @@ -0,0 +1,45 @@ +#pragma once + +namespace +{ + //static auto g_CModelLoader = ADDRESS(0x14173B210).RCast(); + + //static auto dword_1696A9D20 = ADDRESS(0x14D40B328).RCast(); + //static auto dword_141744EE8 = ADDRESS(0x141744EE8).RCast(); + //static auto dword_141744EBC = ADDRESS(0x141744EBC).RCast(); + + //static auto qword_14D40B328 = ADDRESS(0x14D40B328).RCast(); + //static auto qword_141744EA8 = ADDRESS(0x141744EA8).RCast(); + //static auto qword_141744EA0 = ADDRESS(0x141744EA0).RCast(); + //static auto qword_141744E88 = ADDRESS(0x141744E88).RCast(); + + //static auto off_141744E70 = ADDRESS(0x141744E70).RCast(); + //static auto off_141731448 = ADDRESS(0x141744EA8).RCast(); + + //__m128 xmmword_1415BD270 = _mm_castsi128_ps(_mm_set_epi32(0x3B808081, 0x3B808081, 0x3B808081, 0x3B808081)); // xmmword_1415BD270 + + //static auto sub_1404365A0 = ADDRESS(0x1404365A0).RCast(); // Prototype is most likely incorrect: 'local variable allocation has failed, the output may be wrong!' + //static auto sub_140270130 = ADDRESS(0x140270130).RCast<__m128 (*)(__m128*)>(); + //static auto sub_14028F170 = ADDRESS(0x14028F170).RCast(); + + ADDRESS p_CalcPropStaticFrustumCulling = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x8B\xC4\x44\x89\x40\x18\x48\x89\x50\x10\x55", "xxxxxxxxxxxx"); + __int64 (*CalcPropStaticFrustumCulling)(__int64 a1, __int64 a2, unsigned int a3, unsigned int a4, __int64 a5, __int64 a6, __int64 a7) = (__int64 (*)(__int64 a1, __int64 a2, unsigned int a3, unsigned int a4, __int64 a5, __int64 a6, __int64 a7))p_CalcPropStaticFrustumCulling.GetPtr(); /*48 8B C4 44 89 40 18 48 89 50 10 55*/ +} + +__int64 __fastcall HCalcPropStaticFrustumCulling(__int64 a1, __int64 a2, unsigned int a3, unsigned int a4, __int64 a5, __int64 a6, __int64 a7); + +void BspLib_Attach(); +void BspLib_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +class HBspLib : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: CalcPropStaticFrustumCulling : 0x" << std::hex << std::uppercase << p_CalcPropStaticFrustumCulling.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HBspLib); diff --git a/r5dev/client/IVEngineClient.cpp b/r5dev/client/IVEngineClient.cpp new file mode 100644 index 00000000..c9151c73 --- /dev/null +++ b/r5dev/client/IVEngineClient.cpp @@ -0,0 +1,13 @@ +//=============================================================================// +// +// Purpose: Interface the engine exposes to the game DLL +// +//=============================================================================// + +#include "core/stdafx.h" +#include "tier0/basetypes.h" +#include "client/IVEngineClient.h" + +//#ifdef GAMEDLL_S3 +bool* m_bRestrictServerCommands = reinterpret_cast(g_mGameDll.StringSearch("DevShotGenerator_Init()").FindPatternSelf("88 05", ADDRESS::Direction::UP).ResolveRelativeAddressSelf(0x2).OffsetSelf(0x2).GetPtr()); +//#endif diff --git a/r5dev/client/IVEngineClient.h b/r5dev/client/IVEngineClient.h new file mode 100644 index 00000000..445b4672 --- /dev/null +++ b/r5dev/client/IVEngineClient.h @@ -0,0 +1,25 @@ +#pragma once + +namespace +{ + /* ==== CVENGINECLIENT ================================================================================================================================================== */ + ADDRESS p_IVEngineClient_CommandExecute = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x08\x57\x48\x83\xEC\x20\x48\x8D\x0D\x27\x61\xa5\x1E\x41\x8B\xD8", "xxxx?xxxxxxxx????xxx"); + void (*IVEngineClient_CommandExecute)(void* self, const char* cmd) = (void (*)(void*, const char*))p_IVEngineClient_CommandExecute.GetPtr(); /*48 89 5C 24 ?? 57 48 83 EC 20 48 8D 0D ?? ?? ?? ?? 41 8B D8*/ +} + +/////////////////////////////////////////////////////////////////////////////// +extern bool* m_bRestrictServerCommands; + +/////////////////////////////////////////////////////////////////////////////// +class HVEngineClient : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: IVEngineClient::CommandExecute : 0x" << std::hex << std::uppercase << p_IVEngineClient_CommandExecute.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: m_bRestrictServerCommands : 0x" << std::hex << std::uppercase << m_bRestrictServerCommands << std::setw(0) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HVEngineClient); \ No newline at end of file diff --git a/r5dev/client/cdll_engine_int.cpp b/r5dev/client/cdll_engine_int.cpp new file mode 100644 index 00000000..bfd36335 --- /dev/null +++ b/r5dev/client/cdll_engine_int.cpp @@ -0,0 +1,133 @@ +#include "core/stdafx.h" +/*****************************************************************************/ +#include "tier0/basetypes.h" +#include "tier0/IConVar.h" +#include "tier0/cvar.h" +#include "client/IVEngineClient.h" +#include "client/client.h" +#include "client/cdll_engine_int.h" +#include "public/include/bansystem.h" +#include "engine/net_chan.h" +#include "vpc/keyvalues.h" +/*****************************************************************************/ + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void __fastcall HFrameStageNotify(CHLClient* rcx, ClientFrameStage_t frameStage) +{ + switch (frameStage) + { + case ClientFrameStage_t::FRAME_START: // FrameStageNotify gets called every frame by CEngine::Frame with the stage being FRAME_START. We can use this to check/set global variables. + { + static bool bInitialized = false; + if (!bInitialized) + { + IConVar_ClearHostNames(); + ConCommand_InitConCommand(); + CKeyValueSystem_Init(); + + IVEngineClient_CommandExecute(NULL, "exec autoexec.cfg"); + IVEngineClient_CommandExecute(NULL, "exec autoexec_server.cfg"); + IVEngineClient_CommandExecute(NULL, "exec autoexec_client.cfg"); + + *(bool*)m_bRestrictServerCommands = true; // Restrict commands. + void* disconnect = g_pCvar->FindCommand("disconnect"); + *(std::int32_t*)((std::uintptr_t)disconnect + 0x38) |= FCVAR_SERVER_CAN_EXECUTE; // Make sure server is not restricted to this. + + if (net_userandomkey->m_pParent->m_iValue == 1) + { + HNET_GenerateKey(); + } + g_pCvar->FindVar("net_usesocketsforloopback")->m_pParent->m_iValue = 1; + + bInitialized = true; + } + break; + } + case ClientFrameStage_t::FRAME_NET_UPDATE_POSTDATAUPDATE_END: + { + if (g_pBanSystem->IsRefuseListValid()) + { + for (int i = 0; i < g_pBanSystem->vsvrefuseList.size(); i++) // Loop through vector. + { + for (int c = 0; c < MAX_PLAYERS; c++) // Loop through all possible client instances. + { + CClient* client = g_pClient->GetClientInstance(c); // Get client instance. + if (!client) + { + continue; + } + + if (!client->GetNetChan()) // Netchan valid? + { + continue; + } + + int clientID = g_pClient->m_iUserID + 1; // Get UserID + 1. + if (clientID != g_pBanSystem->vsvrefuseList[i].second) // See if they match. + { + continue; + } + + NET_DisconnectClient(g_pClient, c, g_pBanSystem->vsvrefuseList[i].first.c_str(), 0, 1); + g_pBanSystem->DeleteConnectionRefuse(clientID); + break; + } + } + } + PatchNetVarConVar(); + break; + } + default: + { + break; + } + } + + CHLClient_FrameStageNotify(rcx, (int)frameStage); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void PatchNetVarConVar() +{ + CHAR sConvarPtr[] = "\x72\x3a\x73\x76\x72\x75\x73\x7a\x7a\x03\x04"; + PCHAR curr = sConvarPtr; + while (*curr) + { + *curr ^= 'B'; + ++curr; + } + + std::int64_t nCvarAddr = 0; + std::stringstream ss; + ss << std::hex << std::string(sConvarPtr); + ss >> nCvarAddr; + void* pCvar = reinterpret_cast(nCvarAddr); + + if (*reinterpret_cast(pCvar) == 144) + { + std::uint8_t padding[] = + { + 0x48, 0x8B, 0x45, 0x58, 0xC7, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + void* pCallback = nullptr; + VirtualAlloc(pCallback, 10, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + memcpy(pCallback, (void*)padding, 9); + reinterpret_cast(pCallback)(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +void CHLClient_Attach() +{ + DetourAttach((LPVOID*)&CHLClient_FrameStageNotify, &HFrameStageNotify); +} + +void CHLClient_Detach() +{ + DetourDetach((LPVOID*)&CHLClient_FrameStageNotify, &HFrameStageNotify); +} diff --git a/r5dev/client/cdll_engine_int.h b/r5dev/client/cdll_engine_int.h new file mode 100644 index 00000000..d56bd275 --- /dev/null +++ b/r5dev/client/cdll_engine_int.h @@ -0,0 +1,74 @@ +#pragma once +#include "tier0/basetypes.h" + + +enum class ClientFrameStage_t : int +{ + FRAME_UNDEFINED = -1, // (haven't run any frames yet) + FRAME_START, + + // A network packet is being recieved + FRAME_NET_UPDATE_START, + // Data has been received and we're going to start calling PostDataUpdate + FRAME_NET_UPDATE_POSTDATAUPDATE_START, + // Data has been received and we've called PostDataUpdate on all data recipients + FRAME_NET_UPDATE_POSTDATAUPDATE_END, + // We've received all packets, we can now do interpolation, prediction, etc.. + FRAME_NET_UPDATE_END, + + // We're about to start rendering the scene + FRAME_RENDER_START, + // We've finished rendering the scene. + FRAME_RENDER_END, + + FRAME_NET_FULL_FRAME_UPDATE_ON_REMOVE +}; + +class CHLClient +{ +public: + void FrameStageNotify(ClientFrameStage_t curStage) // @0x1405C0740 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = void(__thiscall*)(CHLClient*, ClientFrameStage_t); + (*reinterpret_cast(this))[58](this, curStage); /*48 83 EC 28 89 15 ?? ?? ?? ??*/ + } +}; + +namespace +{ + /* ==== CHLCLIENT ======================================================================================================================================================= */ +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) + ADDRESS p_CHLClient_FrameStageNotify = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x83\xEC\x38\x89\x15\x00\x00\x00\x00", "xxxxxx????"); + void (*CHLClient_FrameStageNotify)(void* rcx, int curStage) = (void(*)(void*, int))p_CHLClient_FrameStageNotify.GetPtr(); /*48 83 EC 38 89 15 ?? ?? ?? ??*/ + + ADDRESS p_CHLClient_PostInit = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x83\x3D\x00\x00\x00\x00\x00\x48\x8D\x05\x00\x00\x00\x00\x48\x89\x05\x00\x00\x00\x00\x48\x8D\x05\x00\x00\x00\x00\x48\x89\x05\x00\x00\x00\x00\x48\x8D\x05\x00\x00\x00\x00\x48\x89\x05\x00\x00\x00\x00\x48\x8D\x05\x00\x00\x00\x00\x48\x89\x05\x00\x00\x00\x00\x48\x8D\x05\x00\x00\x00\x00\x48\x89\x05\x00\x00\x00\x00\x48\x8D\x05\x00\x00\x00\x00\x48\x89\x05\x00\x00\x00\x00\x48\x8D\x05\x00\x00\x00\x00\x48\x89\x05\x00\x00\x00\x00\x48\x8D\x05\x00\x00\x00\x00\x48\x89\x05\x00\x00\x00\x00\x48\x8D\x05\x00\x00\x00\x00", "xxx?????xxx????xxx????xxx????xxx????xxx????xxx????xxx????xxx????xxx????xxx????xxx????xxx????xxx????xxx????xxx????xxx????xxx????"); + void* (*CHLClient_PostInit)() = (void* (*)())p_CHLClient_PostInit.GetPtr(); /*48 83 3D ? ? ? ? ? 48 8D 05 ? ? ? ? 48 89 05 ? ? ? ? 48 8D 05 ? ? ? ? 48 89 05 ? ? ? ? 48 8D 05 ? ? ? ? 48 89 05 ? ? ? ? 48 8D 05 ? ? ? ? 48 89 05 ? ? ? ? 48 8D 05 ? ? ? ? 48 89 05 ? ? ? ? 48 8D 05 ? ? ? ? 48 89 05 ? ? ? ? 48 8D 05 ? ? ? ? 48 89 05 ? ? ? ? 48 8D 05 ? ? ? ? 48 89 05 ? ? ? ? 48 8D 05 ? ? ? ?*/ +#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) + ADDRESS p_CHLClient_FrameStageNotify = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x83\xEC\x28\x89\x15\x00\x00\x00\x00", "xxxxxx????"); + void (*CHLClient_FrameStageNotify)(void* rcx, int curStage) = (void(*)(void*, int))p_CHLClient_FrameStageNotify.GetPtr(); /*48 83 EC 28 89 15 ?? ?? ?? ??*/ + + ADDRESS p_CHLClient_PostInit = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x83\xEC\x28\x48\x83\x3D\x00\x00\x00\x00\x00\x48\x8D\x05\x00\x00\x00\x00", "xxxxxxx?????xxx????"); + void* (*CHLClient_PostInit)() = (void* (*)())p_CHLClient_PostInit.GetPtr(); /*48 83 EC 28 48 83 3D ? ? ? ? ? 48 8D 05 ? ? ? ?*/ +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +void __fastcall HFrameStageNotify(CHLClient* rcx, ClientFrameStage_t curStage); +void PatchNetVarConVar(); + +void CHLClient_Attach(); +void CHLClient_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +class HDll_Engine_Int : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: CHLClient::FrameStageNotify : 0x" << std::hex << std::uppercase << p_CHLClient_FrameStageNotify.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: CHLClient::PostInit : 0x" << std::hex << std::uppercase << p_CHLClient_PostInit.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HDll_Engine_Int); diff --git a/r5dev/client/client.cpp b/r5dev/client/client.cpp new file mode 100644 index 00000000..49d3d39e --- /dev/null +++ b/r5dev/client/client.cpp @@ -0,0 +1,5 @@ +#include "core/stdafx.h" +#include "client/client.h" + +/////////////////////////////////////////////////////////////////////////////// +CClient* g_pClient = reinterpret_cast(p_IVEngineServer_PersistenceAvailable.FindPatternSelf("48 8D 0D", ADDRESS::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr()); diff --git a/r5dev/client/client.h b/r5dev/client/client.h new file mode 100644 index 00000000..9fb97ec2 --- /dev/null +++ b/r5dev/client/client.h @@ -0,0 +1,69 @@ +#pragma once +#include "tier0/basetypes.h" +#include "server/IVEngineServer.h" + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CClient; + +/////////////////////////////////////////////////////////////////////////////// +extern CClient* g_pClient; + +namespace +{ +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) + const std::uintptr_t g_dwCClientSize = 0x4A440; + const std::uintptr_t g_dwPersistenceVar = 0x5B4; + const std::uintptr_t g_dwCClientPadding = 303232; +#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) + const std::uintptr_t g_dwCClientSize = 0x4A4C0; + const std::uintptr_t g_dwPersistenceVar = 0x5BC; + const std::uintptr_t g_dwCClientPadding = 303360; +#endif +} + +class CClient +{ +public: + inline CClient* GetClientInstance(int nIndex) + { + return (CClient*)(std::uintptr_t)(g_pClient + (nIndex * g_dwCClientSize)); + } + + void*& GetNetChan() + { + return m_nNetChannel; + } +private: + char pad_0000[16]; //0x0000 +public: + int m_iUserID; //0x0010 +private: + char pad_0014[908]; //0x0014 +public: + void* m_nNetChannel; //0x03A0 +private: + char pad_03A8[8]; //0x03A8 +public: + int m_iSignonstate; //0x03B0 +private: + char pad_03B4[4]; //0x03B4 +public: + std::int64_t m_iOriginID; //0x03B8 +private: + char pad_03C0[g_dwCClientPadding]; //0x03C0 +}; + +/////////////////////////////////////////////////////////////////////////////// +class HClient : public IDetour +{ + virtual void debugp() + { + std::cout << "| VAR: g_pClient : 0x" << std::hex << std::uppercase << g_pClient << std::setw(0) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HClient); diff --git a/r5dedicated/opcodes.cpp b/r5dev/common/opcodes.cpp similarity index 75% rename from r5dedicated/opcodes.cpp rename to r5dev/common/opcodes.cpp index 32d3239d..77d7a77d 100644 --- a/r5dedicated/opcodes.cpp +++ b/r5dev/common/opcodes.cpp @@ -1,14 +1,12 @@ -#include "pch.h" -#include "hooks.h" -#include "enums.h" -#include "opcodes.h" -#include "gameclasses.h" +#include "core/stdafx.h" +#include "common/opcodes.h" -/*----------------------------------------------------------------------------- - * _opcodes.cpp - *-----------------------------------------------------------------------------*/ + /*----------------------------------------------------------------------------- + * _opcodes.cpp + *-----------------------------------------------------------------------------*/ -void Hooks::DedicatedPatch() +#ifdef DEDICATED +void Dedicated_Init() { *(uintptr_t*)0x14D415040 = 0x1417304E8; *(uintptr_t*)0x14B37C3C0 = 0x141F10CA0; @@ -40,10 +38,10 @@ void Hooks::DedicatedPatch() gCEngineAPI__OnStartup.Offset(0x5E).Patch({ 0xE9, 0xC6, 0x01, 0x00, 0x00 }); // JNE --> JNP | Skip Video Mode initialization code. gCEngineAPI__Connect.Offset(0xDD).Patch({ 0x90, 0x90, 0x90 }); // CAL --> NOP | NOP call to texture and material preloading. gCEngineAPI__Connect.Offset(0xF1).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 }); // CAL --> NOP | NOP call to texture and material preloading. - gCEngineAPI__Connect.Offset(0x1C6).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90}); // CAL --> NOP | NOP call to texture and material preloading. + gCEngineAPI__Connect.Offset(0x1C6).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 }); // CAL --> NOP | NOP call to texture and material preloading. //gCEngineAPI__ModInit.Offset(0x3DD).Patch({ 0xE9, 0xB5, 0x00, 0x00, 0x00, 0x00 }); // JNE --> JNP | Skip CreateWindow Initialization code. gCEngineAPI__ModInit.Offset(0x44C).Patch({ 0xEB, 0x49 }); // JNZ --> JMP | Skip CreateGameWindow validation code. - //gCEngineAPI__ModInit.Offset(0x3DD).Patch({ 0xEB, 0x6D }); // JE --> JMP | Skip CreateGameWindow initialization code. + //gCEngineAPI__ModInit.Offset(0x3DD).Patch({ 0xEB, 0x6D }); // JE --> JMP | Skip CreateGameWindow initialization code. //------------------------------------------------------------------------- // CENGINEVGUI @@ -59,7 +57,7 @@ void Hooks::DedicatedPatch() //------------------------------------------------------------------------- // CHLClIENT //------------------------------------------------------------------------- - gCHLClient__1000.Patch({ 0xC3 }); // FUN --> RET | Return early in 'gCHLClient::unnamed' to prevent infinite loop. + gCHLClient__1000.Patch({ 0xC3 }); // FUN --> RET | Return early in 'gCHLClient::unnamed' to prevent infinite loop. gCHLClient__HudMessage.Patch({ 0xC3 }); // FUN --> RET | Return early from 'CHudMessage' call. //------------------------------------------------------------------------- @@ -82,7 +80,7 @@ void Hooks::DedicatedPatch() //------------------------------------------------------------------------- // CSHADERSYSTEM //------------------------------------------------------------------------- - //gCShaderSystem_Init.Patch({ 0xC3 }); // FUN --> RET | Return early in 'CShaderSystem::Init' to prevent initialization. + //gCShaderSystem_Init.Patch({ 0xC3 }); // FUN --> RET | Return early in 'CShaderSystem::Init' to prevent initialization. gCShaderSystem__9.Offset(0x3).Patch({ 0xE9, 0x95, 0x03, 0x00, 0x00 }); // Unnecessary CShaderSystem call? //------------------------------------------------------------------------- @@ -102,7 +100,7 @@ void Hooks::DedicatedPatch() gHost_Init_1.Offset(0x621).Patch({ 0xEB, 0x0C }); // JNE --> JMP | Skip client.dll Init_PostVideo() validation code. gHost_Init_1.Offset(0x658).Patch({ 0xE9, 0x8C, 0x00, 0x00, 0x00 }); // JE --> JMP | Skip NULL call as client is never initialized. gHost_Init_1.Offset(0x6E9).Patch({ 0xE9, 0xB0, 0x00, 0x00, 0x00 }); // JNE --> JMP | Skip shader preloading as cvar can't be checked due to client being NULL. - //gHost_Init_2.Offset(0x5D8).Patch({ 0xEB, 0x05 }); // JE --> JMP | Render? + //gHost_Init_2.Offset(0x5D8).Patch({ 0xEB, 0x05 }); // JE --> JMP | Render? //------------------------------------------------------------------------- // RUNTIME: _HOST_RUNFRAME @@ -110,6 +108,11 @@ void Hooks::DedicatedPatch() //s1.Offset(0x1C6).Patch({ 0xE9, 0xAD, 0x11, 0x00, 0x00 }); // JNE --> JMP | Return early in _Host_RunFrame() for debugging perposes. //s1.Offset(0x1010).Patch({ 0xEB, 0x14 }); // JNE --> JMP | Return early in _Host_RunFrame() for debugging perposes. + //------------------------------------------------------------------------- + // RUNTIME: HOST_NEWGAME + //------------------------------------------------------------------------- + Host_NewGame.Offset(0x637).Patch({ 0xE9, 0xC1, 0x00, 0x00, 0x00 }); // JNE --> JMP | Prevent connect localhost from being executed in Host_NewGame. + //------------------------------------------------------------------------- // RUNTIME: EBISUSDK //------------------------------------------------------------------------- @@ -163,35 +166,92 @@ void Hooks::DedicatedPatch() gShaderDispatch.Offset(0x62).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 }); // CAL --> NOP | Prevent memory allocation and population for shader assets. // UNKNOWN ---------------------------------------------------------------- - MemoryAddress t8 = 0x00000001403C0480; + ADDRESS t8 = 0x00000001403C0480; t8.Patch({ 0xC3 }); // Return from unknown call during ChangeLevel. [LATE] - MemoryAddress t9 = 0x00000001403EE420; + ADDRESS t9 = 0x00000001403EE420; t9.Patch({ 0xC3 }); // Return from unknown call during ChangeLevel. [EARLY] //------------------------------------------------------------------------- // RUNTIME BLOCK //------------------------------------------------------------------------- - MemoryAddress t0 = 0x00000001401D71E0; + ADDRESS t0 = 0x00000001401D71E0; t0.Patch({ 0xC3 }); - MemoryAddress t1 = 0x0000000140456B50; + ADDRESS t1 = 0x0000000140456B50; t1.Offset(0x292).Patch({ 0xE9, 0xEE, 0x00, 0x00, 0x00 }); - MemoryAddress t2 = 0x0000000140238DA0; + ADDRESS t2 = 0x0000000140238DA0; t2.Offset(0x4E0).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 }); - MemoryAddress t3 = 0x0000000140312D80; + ADDRESS t3 = 0x0000000140312D80; //t3.Offset(0xB3).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 }); - MemoryAddress t4 = 0x0000000140312D80; // Patch Additional shader preloading. + ADDRESS t4 = 0x0000000140312D80; // Patch Additional shader preloading. //t4.Offset(0xB3).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 }); - MemoryAddress t5 = 0x00000001403BBFD0; + ADDRESS t5 = 0x00000001403BBFD0; t5.Offset(0x7D8).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 }); //------------------------------------------------------------------------- // END RUNTIME BLOCK //------------------------------------------------------------------------- } +#endif // DEDICATED -// TEST -void SetCHostState() +void RuntimePtc_Init() /* .TEXT */ { - static std::string ServerMap = std::string(); - ServerMap = "mp_rr_canyonlands_64k_x_64k"; - strncpy_s(GameGlobals::HostState->m_levelName, ServerMap.c_str(), 64); // Copy new map into hoststate levelname. 64 is size of m_levelname. - GameGlobals::HostState->m_iNextState = HostStates_t::HS_NEW_GAME; // Force CHostState::FrameUpdate to start a server. -} \ No newline at end of file +#ifdef DEDICATED + //------------------------------------------------------------------------- + // JNZ --> JMP | Prevent OriginSDK from initializing on the server + Origin_Init.Offset(0x0B).Patch({ 0xE9, 0x63, 0x02, 0x00, 0x00, 0x00 }); + Origin_SetState.Offset(0x0E).Patch({ 0xE9, 0xCB, 0x03, 0x00, 0x00, 0x00 }); +#endif // DEDICATED + //------------------------------------------------------------------------- + // JNE --> JMP | Allow games to be loaded without the optional texture streaming file + //WriteProcessMemory(GameProcess, LPVOID(dst002 + 0x8E5), "\xEB\x19", 2, NULL); + //------------------------------------------------------------------------- + // JNE --> JMP | Prevent connect command from crashing by invalid call to UI function + dst003.Offset(0x1D6).Patch({ 0xEB, 0x27 }); + //------------------------------------------------------------------------- + // JA --> JMP | Prevent FairFight anti-cheat from initializing on the + FairFight_Init.Offset(0x61).Patch({ 0xE9, 0xED, 0x00, 0x00, 0x00, 0x00 }); +} + +void RuntimePtc_Toggle() /* .TEXT */ +{ + static bool g_nop = true; + + if (g_nop) + { + //------------------------------------------------------------------------- + // CALL --> NOP | Allow some maps to be loaded by nopping out a call in LoadProp function + //WriteProcessMemory(GameProcess, LPVOID(dst007 + 0x5E8), "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90", 11, NULL); + + dst007.Offset(0x5E8).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 }); + + + //------------------------------------------------------------------------- + // CALL --> NOP | Disable the viewmodel rendered to avoid a crash from a certain entity in desertlands_mu1 + //WriteProcessMemory(GameProcess, LPVOID(dst008 + 0x67), "\x90\x90\x90\x90\x90", 5, NULL); + dst008.Offset(0x67).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 }); + + + printf("\n"); + printf("+--------------------------------------------------------+\n"); + printf("|>>>>>>>>>>>>>>| TEXT OPCODES OVERWRITTEN |<<<<<<<<<<<<<<|\n"); + printf("+--------------------------------------------------------+\n"); + printf("\n"); + } + else + { + //------------------------------------------------------------------------- + // NOP --> CALL | Recover function DST007 + //WriteProcessMemory(GameProcess, LPVOID(dst007 + 0x5E8), "\x48\x8B\x03\xFF\x90\xB0\x02\x00\x00\x84\xC0", 11, NULL); + + dst007.Offset(0x5E8).Patch({ 0x48, 0x8B, 0x03, 0xFF, 0x90, 0xB0, 0x02, 0x00, 0x00, 0x84, 0xC0 }); + //------------------------------------------------------------------------- + // NOP --> CALL | Recover function DST008 + //WriteProcessMemory(GameProcess, LPVOID(dst008 + 0x67), "\xE8\x54\xD8\xFF\xFF", 5, NULL); + dst008.Offset(0x67).Patch({ 0xE8, 0x54, 0xD8, 0xFF, 0xFF }); + + printf("\n"); + printf("+--------------------------------------------------------+\n"); + printf("|>>>>>>>>>>>>>>>| TEXT OPCODES RECOVERED |<<<<<<<<<<<<<<<|\n"); + printf("+--------------------------------------------------------+\n"); + printf("\n"); + } + g_nop = !g_nop; +} diff --git a/r5dev/common/opcodes.h b/r5dev/common/opcodes.h new file mode 100644 index 00000000..538102d9 --- /dev/null +++ b/r5dev/common/opcodes.h @@ -0,0 +1,119 @@ +#pragma once +#include +#include +#include "public/include/utility.h" + +void Dedicated_Init(); +void RuntimePtc_Init(); +void RuntimePtc_Toggle(); + +namespace +{ + ///* -------------- ORIGIN ------------------------------------------------------------------------------------------------------------------------------------------------ */ + ADDRESS Origin_Init = /*0x14032EEA0*/ FindPatternSIMD("r5apex.exe", (const unsigned char*)"\x48\x83\xEC\x28\x80\x3D\x00\x00\x00\x23\x00\x0F\x85\x00\x02\x00", "xxxxxx???xxxx?xx"); + ADDRESS Origin_SetState = /*0x140330290*/ FindPatternSIMD("r5apex.exe", (const unsigned char*)"\x48\x81\xEC\x58\x04\x00\x00\x80\x3D\x00\x00\x00\x00\x00\x0F\x84", "xxxxxxxxx????xxx"); + + ///* -------------- ENGINE ------------------------------------------------------------------------------------------------------------------------------------------------ */ + //ADDRESS dst002 = /*0x14043FB90*/ FindPatternSIMD("r5apex.exe", (const unsigned char*)"\x48\x89\x4C\x24\x08\x56\x41\x55\x48\x81\xEC\x68\x03\x00\x00\x4C", "xxxx?xxxxxxxxxxx"); + ADDRESS dst003 = /*0x14022A4A0*/ FindPatternSIMD("r5apex.exe", (const unsigned char*)"\x48\x83\xEC\x38\x0F\x29\x74\x24\x20\x48\x89\x5C\x24\x40\x48\x8B", "xxxxxxxxxxxxxxxx"); + ADDRESS Host_NewGame = /*0x140238DA0*/ FindPatternSIMD("r5apex.exe", (const unsigned char*)"\x48\x8B\xC4\x00\x41\x54\x41\x00\x48\x81\xEC\x00\x00\x00\x00\xF2", "xxx?xxx?xxx??xxx"); + + ///* -------------- NETCHAN ----------------------------------------------------------------------------------------------------------------------------------------------- */ + //ADDRESS CServer_Auth = /*0x14030D000*/ FindPatternSIMD("r5apex.exe", (const unsigned char*)"\x40\x55\x57\x41\x55\x41\x57\x48\x8D\xAC\x24\x28\xFF\xFF\xFF\x48", "xxxxxxxxxxxxxxxx"); + + ///* -------------- FAIRFIGHT --------------------------------------------------------------------------------------------------------------------------------------------- */ + ADDRESS FairFight_Init = /*0x140303AE0*/ FindPatternSIMD("r5apex.exe", (const unsigned char*)"\x40\x53\x48\x83\xEC\x20\x8B\x81\xB0\x03\x00\x00\x48\x8B\xD9\xC6", "xxxxxxxxxxxxxxxx"); + + ///* -------------- OTHER ------------------------------------------------------------------------------------------------------------------------------------------------- */ + ADDRESS dst007 = /*0x14028F3B0*/ FindPatternSIMD("r5apex.exe", (const unsigned char*)"\x48\x8B\xC4\x44\x89\x40\x18\x48\x89\x50\x10\x55\x53\x56\x57\x41", "xxxxxxxxxxxxxxxx"); + ADDRESS dst008 = /*0x140E3E110*/ FindPatternSIMD("r5apex.exe", (const unsigned char*)"\x48\x83\xEC\x78\x48\x8B\x84\x24\x00\x00\x00\x00\x4D\x8B\xD8\x00", "xxxxxxxx????xxx?"); + + //ADDRESS dst009 = FindPatternSIMD("r5apex.exe", (const unsigned char*)"\x48\x8B\xC4\x55\x41\x54\x41\x55\x41\x56\x41\x57\x48\x8B\xEC\x48\x83\xEC\x60", "xxxxxxxxxxxxxxxxxxx"); + + ///* -------------- ------- ----------------------------------------------------------------------------------------------------------------------------------------------- */ + + namespace + { + // TODO: create patterns instead and rename to function names. + // Renderer + ADDRESS r0 = 0x00000001402FE280; // + ADDRESS gCShaderGlue__Init = 0x00000001403B3A50; // + ADDRESS gMatSync = 0x00000001403DEE90; // + ADDRESS gCMaterialSystem__MatsysMode_Init = 0x00000001403BD120; // + ADDRESS r4 = 0x0000000140404380; // + ADDRESS r5 = 0x000000014040D850; // + ADDRESS r6 = 0x0000000140413260; // + ADDRESS r7 = 0x00000001404093F0; // + ADDRESS r8 = 0x00000001403D2E60; // + ADDRESS d3d11init = 0x000000014043CDF0; // + + // Engine + ADDRESS gHost_Init_0 = 0x0000000140236E40; // main Host_Init()? + ADDRESS e1 = 0x0000000140FB2F10; // also used by CServerGameDLL + ADDRESS addr_CEngine_Frame = 0x00000001402970E0; + ADDRESS e3 = 0x0000000140231C00; + ADDRESS e4 = 0x0000000140BE1970; + ADDRESS e5 = 0x0000000140DBBAF0; + ADDRESS e6 = 0x0000000140DBE610; + ADDRESS e7 = 0x000000014044AFA0; + ADDRESS e8 = 0x000000014027EC50; // RenderFrame? + ADDRESS gCEngineAPI__Init = 0x0000000140342FB0; // + ADDRESS gCEngineAPI__ModInit = 0x0000000140343DE0; // + ADDRESS gCEngineAPI__Connect = 0x0000000140342BA0; // + ADDRESS gCEngineAPI__OnStartup = 0x0000000140343860; // + ADDRESS gCSourceAppSystemGroup__Create = 0x000000014044AFA0; // + ADDRESS gCShaderSystem__Init = 0x00000001403DF870; // + ADDRESS gInitMaterialSystem = 0x000000014024B390; // + ADDRESS gCVideoMode_Common__DrawStartupGraphic = 0x000000014027F0F0; // + ADDRESS gShaderDispatch = 0x00000001403EE5C0; + ADDRESS gShaderCreate = 0x00000001403ECD00; // + ADDRESS gTextureCreate = 0x00000001403EDCD0; + + ADDRESS gCShaderSystem__9 = 0x00000001403DFC30; + ADDRESS gBSP_LUMP_INIT = 0x00000001402546F0; // BSP. + + + ADDRESS e9 = 0x00000001404066E0; + ADDRESS e10 = 0x00000001403B49E0; // CMaterialGlue? + + // SERVER + ADDRESS gHost_Init_1 = 0x0000000140237B00; // server Host_Init()? + ADDRESS s1 = 0x0000000140231C00; // _Host_RunFrame() with inlined CFrameTimer::MarkFrame()? + ADDRESS s2 = 0x00000001402312A0; // server HeartBeat? (baseserver.cpp) + ADDRESS s3 = 0x0000000140FB36D0; // TEMP?? + + // CLIENT + ADDRESS gHost_Init_2 = 0x0000000140236640; // client Host_Init()? + ADDRESS gCGame__CreateGameWindow = 0x0000000140299100; // + ADDRESS c2 = 0x00000001403F4360; // 1403DF870 --> 1403F4360 + ADDRESS c3 = 0x00000001403F8A80; // 1403DF870 --> 1403F8A40 + ADDRESS gCHLClient__1000 = 0x00000001405C27B0; // CHLClient + 1000 + ADDRESS gCHLClient__HudMessage = 0x00000001405BAC00; // CHudMessage + ADDRESS c6 = 0x00000001403CA2D0; // + + // VGUI + ADDRESS gCEngineVGui__Init = 0x0000000140282E40; // jumptable + ADDRESS gCEngineVGui__OnLevelLoadingStarted = 0x00000001402830D0; + ADDRESS SCR_BeginLoadingPlaque = 0x000000014023E870; + } + + + void PrintOAddress() // Test the sigscan results + { + std::cout << "+--------------------------------------------------------+" << std::endl; + std::cout << "| Origin_Init : " << std::hex << std::uppercase << Origin_Init.GetPtr() << std::setw(20) << " |" << std::endl; + std::cout << "| Origin_SetState : " << std::hex << std::uppercase << Origin_SetState.GetPtr() << std::setw(20) << " |" << std::endl; + std::cout << "+--------------------------------------------------------+" << std::endl; + //std::cout << "| dst002 : " << std::hex << std::uppercase << dst002.GetPtr() << std::setw(20) << " |" << std::endl; + std::cout << "| dst003 : " << std::hex << std::uppercase << dst003.GetPtr() << std::setw(20) << " |" << std::endl; + //std::cout << "| Host_NewGame : " << std::hex << std::uppercase << Host_NewGame.GetPtr() << std::setw(20) << " |" << std::endl; + std::cout << "+--------------------------------------------------------+" << std::endl; + //std::cout << "| CServer_Auth : " << std::hex << std::uppercase << CServer_Auth.GetPtr() << std::setw(20) << " |" << std::endl; + std::cout << "+--------------------------------------------------------+" << std::endl; + std::cout << "| FairFight_Init : " << std::hex << std::uppercase << FairFight_Init.GetPtr() << std::setw(20) << " |" << std::endl; + std::cout << "+--------------------------------------------------------+" << std::endl; + std::cout << "| dst007 : " << std::hex << std::uppercase << dst007.GetPtr() << std::setw(20) << " |" << std::endl; + std::cout << "| dst008 : " << std::hex << std::uppercase << dst008.GetPtr() << std::setw(20) << " |" << std::endl; + std::cout << "+--------------------------------------------------------+" << std::endl; + } +} diff --git a/r5dev/common/protocol.h b/r5dev/common/protocol.h new file mode 100644 index 00000000..44c94d9d --- /dev/null +++ b/r5dev/common/protocol.h @@ -0,0 +1,19 @@ +#pragma once + +/*----------------------------------------------------------------------------- + * _protocol.h + *-----------------------------------------------------------------------------*/ + +enum class SIGNONSTATE : int +{ + SIGNONSTATE_NONE = 0, // no state yet; about to connect. + SIGNONSTATE_CHALLENGE = 1, // client challenging server; all OOB packets. + SIGNONSTATE_CONNECTED = 2, // client is connected to server; netchans ready. + SIGNONSTATE_NEW = 3, // just got serverinfo and string tables. + SIGNONSTATE_PRESPAWN = 4, // received signon buffers. + SIGNONSTATE_GETTING_DATA = 5, // getting persistence data. + SIGNONSTATE_SPAWN = 6, // ready to receive entity packets. + SIGNONSTATE_FIRST_SNAP = 7, // received baseline snapshot. + SIGNONSTATE_FULL = 8, // we are fully connected; first non-delta packet received. + SIGNONSTATE_CHANGELEVEL = 9, // server is changing level; please wait. +}; diff --git a/r5dev/common/psuedodefs.h b/r5dev/common/psuedodefs.h new file mode 100644 index 00000000..0a7a19f3 --- /dev/null +++ b/r5dev/common/psuedodefs.h @@ -0,0 +1,462 @@ +/* + + This file contains definitions used in the Hex-Rays decompiler output. + It has type definitions and convenience macros to make the + output more readable. + + Copyright (c) 2007-2020 Hex-Rays + +*/ + +#ifndef HEXRAYS_DEFS_H +#define HEXRAYS_DEFS_H + +#if defined(__GNUC__) + typedef long long ll; + typedef unsigned long long ull; + #define __int64 long long + #define __int32 int + #define __int16 short + #define __int8 char + #define MAKELL(num) num ## LL + #define FMT_64 "ll" +#elif defined(_MSC_VER) + typedef __int64 ll; + typedef unsigned __int64 ull; + #define MAKELL(num) num ## i64 + #define FMT_64 "I64" +#elif defined (__BORLANDC__) + typedef __int64 ll; + typedef unsigned __int64 ull; + #define MAKELL(num) num ## i64 + #define FMT_64 "L" +#else + #error "unknown compiler" +#endif +typedef unsigned int uint; +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned long ulong; + +typedef char int8; +typedef signed char sint8; +typedef unsigned char uint8; +typedef short int16; +typedef signed short sint16; +typedef unsigned short uint16; +typedef int int32; +typedef signed int sint32; +typedef unsigned int uint32; +typedef ll int64; +typedef ll sint64; +typedef ull uint64; + +// Partially defined types. They are used when the decompiler does not know +// anything about the type except its size. +#define _BYTE uint8 +#define _WORD uint16 +#define _DWORD uint32 +#define _QWORD uint64 +#if !defined(_MSC_VER) +#define _LONGLONG __int128 +#endif + +// Non-standard boolean types. They are used when the decompiler cannot use +// the standard "bool" type because of the size mistmatch but the possible +// values are only 0 and 1. See also 'BOOL' type below. +typedef int8 _BOOL1; +typedef int16 _BOOL2; +typedef int32 _BOOL4; +typedef int64 _BOOL8; + +#ifndef _WINDOWS_ +typedef int8 BYTE; +typedef int16 WORD; +typedef int32 DWORD; +typedef int32 LONG; +typedef int BOOL; // uppercase BOOL is usually 4 bytes +#endif +typedef int64 QWORD; +#ifndef __cplusplus +typedef int bool; // we want to use bool in our C programs +#endif + +#define __pure // pure function: + // when given the same arguments, always returns the same value + // has no side effects + +// Non-returning function +#if defined(__GNUC__) +#define __noreturn __attribute__((noreturn)) +#else +#define __noreturn __declspec(noreturn) +#endif + + +#ifndef NULL +#define NULL 0 +#endif + +// Some convenience macros to make partial accesses nicer +#define LAST_IND(x,part_type) (sizeof(x)/sizeof(part_type) - 1) +#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN +# define LOW_IND(x,part_type) LAST_IND(x,part_type) +# define HIGH_IND(x,part_type) 0 +#else +# define HIGH_IND(x,part_type) LAST_IND(x,part_type) +# define LOW_IND(x,part_type) 0 +#endif +// first unsigned macros: +#define BYTEn(x, n) (*((_BYTE*)&(x)+n)) +#define WORDn(x, n) (*((_WORD*)&(x)+n)) +#define DWORDn(x, n) (*((_DWORD*)&(x)+n)) + +#define LOBYTE(x) BYTEn(x,LOW_IND(x,_BYTE)) +#define LOWORD(x) WORDn(x,LOW_IND(x,_WORD)) +#define LODWORD(x) DWORDn(x,LOW_IND(x,_DWORD)) +#define HIBYTE(x) BYTEn(x,HIGH_IND(x,_BYTE)) +#define HIWORD(x) WORDn(x,HIGH_IND(x,_WORD)) +#define HIDWORD(x) DWORDn(x,HIGH_IND(x,_DWORD)) +#define BYTE1(x) BYTEn(x, 1) // byte 1 (counting from 0) +#define BYTE2(x) BYTEn(x, 2) +#define BYTE3(x) BYTEn(x, 3) +#define BYTE4(x) BYTEn(x, 4) +#define BYTE5(x) BYTEn(x, 5) +#define BYTE6(x) BYTEn(x, 6) +#define BYTE7(x) BYTEn(x, 7) +#define BYTE8(x) BYTEn(x, 8) +#define BYTE9(x) BYTEn(x, 9) +#define BYTE10(x) BYTEn(x, 10) +#define BYTE11(x) BYTEn(x, 11) +#define BYTE12(x) BYTEn(x, 12) +#define BYTE13(x) BYTEn(x, 13) +#define BYTE14(x) BYTEn(x, 14) +#define BYTE15(x) BYTEn(x, 15) +#define WORD1(x) WORDn(x, 1) +#define WORD2(x) WORDn(x, 2) // third word of the object, unsigned +#define WORD3(x) WORDn(x, 3) +#define WORD4(x) WORDn(x, 4) +#define WORD5(x) WORDn(x, 5) +#define WORD6(x) WORDn(x, 6) +#define WORD7(x) WORDn(x, 7) + +// now signed macros (the same but with sign extension) +#define SBYTEn(x, n) (*((int8*)&(x)+n)) +#define SWORDn(x, n) (*((int16*)&(x)+n)) +#define SDWORDn(x, n) (*((int32*)&(x)+n)) + +#define SLOBYTE(x) SBYTEn(x,LOW_IND(x,int8)) +#define SLOWORD(x) SWORDn(x,LOW_IND(x,int16)) +#define SLODWORD(x) SDWORDn(x,LOW_IND(x,int32)) +#define SHIBYTE(x) SBYTEn(x,HIGH_IND(x,int8)) +#define SHIWORD(x) SWORDn(x,HIGH_IND(x,int16)) +#define SHIDWORD(x) SDWORDn(x,HIGH_IND(x,int32)) +#define SBYTE1(x) SBYTEn(x, 1) +#define SBYTE2(x) SBYTEn(x, 2) +#define SBYTE3(x) SBYTEn(x, 3) +#define SBYTE4(x) SBYTEn(x, 4) +#define SBYTE5(x) SBYTEn(x, 5) +#define SBYTE6(x) SBYTEn(x, 6) +#define SBYTE7(x) SBYTEn(x, 7) +#define SBYTE8(x) SBYTEn(x, 8) +#define SBYTE9(x) SBYTEn(x, 9) +#define SBYTE10(x) SBYTEn(x, 10) +#define SBYTE11(x) SBYTEn(x, 11) +#define SBYTE12(x) SBYTEn(x, 12) +#define SBYTE13(x) SBYTEn(x, 13) +#define SBYTE14(x) SBYTEn(x, 14) +#define SBYTE15(x) SBYTEn(x, 15) +#define SWORD1(x) SWORDn(x, 1) +#define SWORD2(x) SWORDn(x, 2) +#define SWORD3(x) SWORDn(x, 3) +#define SWORD4(x) SWORDn(x, 4) +#define SWORD5(x) SWORDn(x, 5) +#define SWORD6(x) SWORDn(x, 6) +#define SWORD7(x) SWORDn(x, 7) + +// Generate a pair of operands. S stands for 'signed' +#define __SPAIR16__(high, low) (((int16) (high) << 8) | (uint8) (low)) +#define __SPAIR32__(high, low) (((int32) (high) << 16) | (uint16)(low)) +#define __SPAIR64__(high, low) (((int64) (high) << 32) | (uint32)(low)) +#define __SPAIR128__(high, low) (((int128) (high) << 64) | (uint64)(low)) +#define __PAIR16__(high, low) (((uint16) (high) << 8) | (uint8) (low)) +#define __PAIR32__(high, low) (((uint32) (high) << 16) | (uint16)(low)) +#define __PAIR64__(high, low) (((uint64) (high) << 32) | (uint32)(low)) +#define __PAIR128__(high, low) (((uint128)(high) << 64) | (uint64)(low)) + +// Helper functions to represent some assembly instructions. + +#ifdef __cplusplus + +// compile time assertion +#define __CASSERT_N0__(l) COMPILE_TIME_ASSERT_ ## l +#define __CASSERT_N1__(l) __CASSERT_N0__(l) +#define CASSERT(cnd) typedef char __CASSERT_N1__(__LINE__) [(cnd) ? 1 : -1] + +// check that unsigned multiplication does not overflow +template bool is_mul_ok(T count, T elsize) +{ + CASSERT(T(-1) > 0); // make sure T is unsigned + if ( elsize == 0 || count == 0 ) + return true; + return count <= T(-1) / elsize; +} + +// multiplication that saturates (yields the biggest value) instead of overflowing +// such a construct is useful in "operator new[]" +template bool saturated_mul(T count, T elsize) +{ + return is_mul_ok(count, elsize) ? count * elsize : T(-1); +} + +#include // for size_t + +// memcpy() with determined behavoir: it always copies +// from the start to the end of the buffer +// note: it copies byte by byte, so it is not equivalent to, for example, rep movsd +inline void *qmemcpy(void *dst, const void *src, size_t cnt) +{ + char *out = (char *)dst; + const char *in = (const char *)src; + while ( cnt > 0 ) + { + *out++ = *in++; + --cnt; + } + return dst; +} + +// rotate left +template T __ROL__(T value, int count) +{ + const uint nbits = sizeof(T) * 8; + + if ( count > 0 ) + { + count %= nbits; + T high = value >> (nbits - count); + if ( T(-1) < 0 ) // signed value + high &= ~((T(-1) << count)); + value <<= count; + value |= high; + } + else + { + count = -count % nbits; + T low = value << (nbits - count); + value >>= count; + value |= low; + } + return value; +} + +inline uint8 __ROL1__(uint8 value, int count) { return __ROL__((uint8)value, count); } +inline uint16 __ROL2__(uint16 value, int count) { return __ROL__((uint16)value, count); } +inline uint32 __ROL4__(uint32 value, int count) { return __ROL__((uint32)value, count); } +inline uint64 __ROL8__(uint64 value, int count) { return __ROL__((uint64)value, count); } +inline uint8 __ROR1__(uint8 value, int count) { return __ROL__((uint8)value, -count); } +inline uint16 __ROR2__(uint16 value, int count) { return __ROL__((uint16)value, -count); } +inline uint32 __ROR4__(uint32 value, int count) { return __ROL__((uint32)value, -count); } +inline uint64 __ROR8__(uint64 value, int count) { return __ROL__((uint64)value, -count); } + +// the carry flag of a left shift +template int8 __MKCSHL__(T value, uint count) +{ + const uint nbits = sizeof(T) * 8; + count %= nbits; + + return (value >> (nbits-count)) & 1; +} + +// the carry flag of a right shift +template int8 __MKCSHR__(T value, uint count) +{ + return (value >> (count-1)) & 1; +} + +// sign flag +template int8 __SETS__(T x) +{ + if ( sizeof(T) == 1 ) + return int8(x) < 0; + if ( sizeof(T) == 2 ) + return int16(x) < 0; + if ( sizeof(T) == 4 ) + return int32(x) < 0; + return int64(x) < 0; +} + +// overflow flag of subtraction (x-y) +template int8 __OFSUB__(T x, U y) +{ + if ( sizeof(T) < sizeof(U) ) + { + U x2 = x; + int8 sx = __SETS__(x2); + return (sx ^ __SETS__(y)) & (sx ^ __SETS__(U(x2-y))); + } + else + { + T y2 = y; + int8 sx = __SETS__(x); + return (sx ^ __SETS__(y2)) & (sx ^ __SETS__(T(x-y2))); + } +} + +// overflow flag of addition (x+y) +template int8 __OFADD__(T x, U y) +{ + if ( sizeof(T) < sizeof(U) ) + { + U x2 = x; + int8 sx = __SETS__(x2); + return ((1 ^ sx) ^ __SETS__(y)) & (sx ^ __SETS__(U(x2+y))); + } + else + { + T y2 = y; + int8 sx = __SETS__(x); + return ((1 ^ sx) ^ __SETS__(y2)) & (sx ^ __SETS__(T(x+y2))); + } +} + +// https://en.wikipedia.org/wiki/Carry_flag#Carry_flag_vs._borrow_flag +#if defined(__ARM__) || defined(__PPC__) +#define SUB_WITH_CARRY 1 +#else +#define SUB_WITH_CARRY 0 +#endif + +// carry flag of subtraction (x-y) +template int8 __CFSUB__(T x, U y) +{ + int size = sizeof(T) > sizeof(U) ? sizeof(T) : sizeof(U); + bool res; + if ( size == 1 ) + res = uint8(x) < uint8(y); + else if ( size == 2 ) + res = uint16(x) < uint16(y); + else if ( size == 4 ) + res = uint32(x) < uint32(y); + else + res = uint64(x) < uint64(y); +#if SUB_WITH_CARRY + res = !res; +#endif + return res; +} + +// carry flag of addition (x+y) +template int8 __CFADD__(T x, U y) +{ + int size = sizeof(T) > sizeof(U) ? sizeof(T) : sizeof(U); + if ( size == 1 ) + return uint8(x) > uint8(x+y); + if ( size == 2 ) + return uint16(x) > uint16(x+y); + if ( size == 4 ) + return uint32(x) > uint32(x+y); + return uint64(x) > uint64(x+y); +} + +// carry flag of subtraction with carry +template int8 __CFSUB__(T x, U y, int8 cf) +{ +#if SUB_WITH_CARRY + cf = !cf; +#endif + return __CFADD__(y, cf) ^ __CFSUB(x, y + cf); +} + +// overflow flag of subtraction with carry +template int8 __OFSUB__(T x, U y, int8 cf) +{ +#if SUB_WITH_CARRY + cf = !cf; +#endif + return __OFADD__(y, cf) ^ __OFSUB(x, y + cf); +} + +inline uint8 abs8(int8 x) { return x >= 0 ? x : -x; } +inline uint16 abs16(int16 x) { return x >= 0 ? x : -x; } +inline uint32 abs32(int32 x) { return x >= 0 ? x : -x; } +inline uint64 abs64(int64 x) { return x >= 0 ? x : -x; } +//inline uint128 abs128(int128 x) { return x >= 0 ? x : -x; } + +#include // for memcpy +#include // for enable_if + +template +inline typename std::enable_if::type __coerce(F f) +{ + T t; + memcpy(&t, &f, sizeof(T)); + return t; +} +#define COERCE_FLOAT(v) __coerce(v) +#define COERCE_DOUBLE(v) __coerce(v) +#define COERCE_LONG_DOUBLE(v) __coerce(v) +#define COERCE_UNSIGNED_INT(v) __coerce(v) +#define COERCE_UNSIGNED_INT64(v) __coerce(v) + +#else // C++ +// For C, we just provide macros, they are not quite correct. +#define __ROL__(x, y) __rotl__(x, y) // Rotate left +#define __ROR__(x, y) __rotr__(x, y) // Rotate right +#define __CFSHL__(x, y) invalid_operation // Generate carry flag for (x<>y) +#define __CFADD__(x, y) invalid_operation // Generate carry flag for (x+y) +#define __CFSUB__(x, y) invalid_operation // Generate carry flag for (x-y) +#define __OFADD__(x, y) invalid_operation // Generate overflow flag for (x+y) +#define __OFSUB__(x, y) invalid_operation // Generate overflow flag for (x-y) + +#define abs8(x) (int8) ((int8) (x) >= 0 ? (x) : -(x)) +#define abs16(x) (int16) ((int16) (x) >= 0 ? (x) : -(x)) +#define abs32(x) (int32) ((int32) (x) >= 0 ? (x) : -(x)) +#define abs64(x) (int64) ((int64) (x) >= 0 ? (x) : -(x)) +#define abs128(x) (int128)((int128)(x) >= 0 ? (x) : -(x)) + +#endif // C++ + +#if defined(__MIPS__) +// traps for MIPS arithmetic operation +void __noreturn __integer_oveflow(void); // SIGFPE/FPE_INTOVF +void __noreturn __divide_by_zero(void); // SIGFPE/FPE_INTDIV +void __noreturn __trap(uint16 trapcode); // SIGTRAP +void __noreturn __break(uint16 code, uint16 subcode); +#endif + +// No definition for rcl/rcr because the carry flag is unknown +#define __RCL__(x, y) invalid_operation // Rotate left thru carry +#define __RCR__(x, y) invalid_operation // Rotate right thru carry +#define __MKCRCL__(x, y) invalid_operation // Generate carry flag for a RCL +#define __MKCRCR__(x, y) invalid_operation // Generate carry flag for a RCR +#define __SETP__(x, y) invalid_operation // Generate parity flag for (x-y) + +// In the decompilation listing there are some objects declared as _UNKNOWN +// because we could not determine their types. Since the C compiler does not +// accept void item declarations, we replace them by anything of our choice, +// for example a char: + +#define _UNKNOWN char + +#ifdef _MSC_VER +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#endif + +// The ADJ() macro is used for shifted pointers. +// While compilers do not understand it, it makes the code more readable. +// A shifted pointer is declared like this, for example: +// char *__shifted(mystruct,8) p; +// It means: while 'p' points to 'char', it also points to the middle of 'mystruct'. +// More precisely, it is at the offset of 8 bytes from the beginning of 'mystruct'. +// +// The ADJ() macro performs the necessary adjustment. +// The __parentof() and __deltaof() functions are made up, they do not exist. +// __parentof() returns the parent structure type. +// __deltaof() returns the shift amount. + +#define ADJ(p) (__parentof(p) *)(p-__deltaof(p)) + +#endif // HEXRAYS_DEFS_H diff --git a/r5dev/src/dllmain.cpp b/r5dev/core/dllmain.cpp similarity index 52% rename from r5dev/src/dllmain.cpp rename to r5dev/core/dllmain.cpp index 2e25f83c..fd6f8e8e 100644 --- a/r5dev/src/dllmain.cpp +++ b/r5dev/core/dllmain.cpp @@ -1,34 +1,43 @@ -#include "pch.h" -#include "r5dev.h" -#include "id3dx.h" -#include "input.h" -#include "hooks.h" -#include "opcptc.h" -#include "console.h" +#include "core/stdafx.h" +#include "core/r5dev.h" +#include "core/init.h" +/*****************************************************************************/ +#ifndef DEDICATED +#include "windows/id3dx.h" +#include "windows/input.h" +#endif // !DEDICATED +#include "windows/console.h" //############################################################################# // INITIALIZATION //############################################################################# -void InitializeR5Dev() +void R5Dev_Init() { - SetupConsole(); - Hooks::InstallHooks(); - InstallOpcodes(); - g_GuiConfig.Load(); // Load gui config. - SetupDXSwapChain(); + Console_Init(); + Systems_Init(); + +#ifndef DEDICATED + Input_Init(); + DirectX_Init(); +#endif // !DEDICATED spdlog::get("console")->set_pattern("%v"); spdlog::info("+-----------------------------------------------------------------------------+\n"); - spdlog::info("| R5 DEV -- INITIALIZED ------------------------------------------------- |\n"); + spdlog::info("| R5 DEVELOPER CONSOLE -- INITIALIZED ----------------------------------- |\n"); spdlog::info("+-----------------------------------------------------------------------------+\n"); - spdlog::get("console")->set_pattern("[%I:%M:%S:%e] [%L] %v"); + spdlog::get("console")->set_pattern("[%S.%e] %v"); } -void TerminateR5Dev() +void R5Dev_Shutdown() { - RemoveDXHooks(); - Hooks::RemoveHooks(); + Systems_Shutdown(); + +#ifndef DEDICATED + Input_Shutdown(); + DirectX_Shutdown(); +#endif // !DEDICATED + FreeConsole(); } @@ -36,20 +45,19 @@ void TerminateR5Dev() // ENTRYPOINT //############################################################################# -BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) +BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) { - switch (dwReason) { case DLL_PROCESS_ATTACH: { - InitializeR5Dev(); + R5Dev_Init(); break; } case DLL_PROCESS_DETACH: { - TerminateR5Dev(); + R5Dev_Shutdown(); break; } } diff --git a/r5dev/core/init.cpp b/r5dev/core/init.cpp new file mode 100644 index 00000000..7c7bdad1 --- /dev/null +++ b/r5dev/core/init.cpp @@ -0,0 +1,182 @@ +//=============================================================================// +// +// Purpose: Main systems initialization file +// +//=============================================================================// + +#include "core/stdafx.h" +#include "core/init.h" +#include "common/opcodes.h" +#include "tier0/ConCommand.h" +#include "tier0/completion.h" +#include "tier0/cvar.h" +#include "tier0/IConVar.h" +#include "vpc/IAppSystem.h" +#include "vpc/keyvalues.h" +#include "vpc/basefilesystem.h" +#include "vpc/keyvalues.h" +#include "launcher/IApplication.h" +#include "ebisusdk/EbisuSDK.h" +#include "vphysics/QHull.h" +#include "bsplib/bsplib.h" +#ifndef DEDICATED +#include "vgui/CEngineVGui.h" +#include "vgui/vgui_fpspanel.h" +#include "vguimatsurface/MatSystemSurface.h" +#include "client/cdll_engine_int.h" +#endif // !DEDICATED +#include "client/client.h" +#include "client/IVEngineClient.h" +#include "server/server.h" +#include "server/IVEngineServer.h" +#include "squirrel/sqinit.h" +#include "squirrel/sqapi.h" +#include "squirrel/sqvm.h" +#include "rtech/stryder.h" +#include "engine/baseclient.h" +#include "engine/host_state.h" +#include "engine/net_chan.h" +#include "engine/sys_dll.h" +#include "engine/sys_dll2.h" +#include "engine/sys_utils.h" +#ifndef DEDICATED +#include "inputsystem/inputsystem.h" +#include "windows/id3dx.h" +#endif // !DEDICATED + + + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// ██╗███╗ ██╗██╗████████╗██╗ █████╗ ██╗ ██╗███████╗ █████╗ ████████╗██╗ ██████╗ ███╗ ██╗ +// ██║████╗ ██║██║╚══██╔══╝██║██╔══██╗██║ ██║╚══███╔╝██╔══██╗╚══██╔══╝██║██╔═══██╗████╗ ██║ +// ██║██╔██╗ ██║██║ ██║ ██║███████║██║ ██║ ███╔╝ ███████║ ██║ ██║██║ ██║██╔██╗ ██║ +// ██║██║╚██╗██║██║ ██║ ██║██╔══██║██║ ██║ ███╔╝ ██╔══██║ ██║ ██║██║ ██║██║╚██╗██║ +// ██║██║ ╚████║██║ ██║ ██║██║ ██║███████╗██║███████╗██║ ██║ ██║ ██║╚██████╔╝██║ ╚████║ +// ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +void Systems_Init() +{ + // Begin the detour transaction to hook the the process + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + + // Hook functions + IApplication_Attach(); + CBaseClient_Attach(); + CBaseFileSystem_Attach(); + + //QHull_Attach(); + //BspLib_Attach(); + +#ifndef DEDICATED + CEngineVGui_Attach(); + CFPSPanel_Attach(); + CHLClient_Attach(); +#endif // !DEDICATED + + CServer_Attach(); + +#ifdef DEDICATED + CHostState_Attach(); // Dedicated only for now until backwards compatible with S1. +#endif // DEDICATED + + CNetChan_Attach(); + ConCommand_Attach(); + IConVar_Attach(); + CKeyValueSystem_Attach(); + IVEngineServer_Attach(); + SQAPI_Attach(); + SQVM_Attach(); + SysDll_Attach(); + SysUtils_Attach(); + + // Patch instructions + RuntimePtc_Init(); + + // Commit the transaction + if (DetourTransactionCommit() != NO_ERROR) + { + // Failed to hook into the process, terminate + TerminateProcess(GetCurrentProcess(), 0xBAD0C0DE); + } + + IConVar_InitConVar(); + +#ifdef DEDICATED + Dedicated_Init(); +#endif // DEDICATED + +} + +////////////////////////////////////////////////////////////////////////// +// +// ███████╗██╗ ██╗██╗ ██╗████████╗██████╗ ██████╗ ██╗ ██╗███╗ ██╗ +// ██╔════╝██║ ██║██║ ██║╚══██╔══╝██╔══██╗██╔═══██╗██║ ██║████╗ ██║ +// ███████╗███████║██║ ██║ ██║ ██║ ██║██║ ██║██║ █╗ ██║██╔██╗ ██║ +// ╚════██║██╔══██║██║ ██║ ██║ ██║ ██║██║ ██║██║███╗██║██║╚██╗██║ +// ███████║██║ ██║╚██████╔╝ ██║ ██████╔╝╚██████╔╝╚███╔███╔╝██║ ╚████║ +// ╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══╝╚══╝ ╚═╝ ╚═══╝ +// +////////////////////////////////////////////////////////////////////////// + +void Systems_Shutdown() +{ + // Begin the detour transaction to unhook the the process + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + + // Unhook functions + IApplication_Detach(); + CBaseClient_Detach(); + CBaseFileSystem_Detach(); + + //QHull_Detach(); + //BspLib_Detach(); + +#ifndef DEDICATED + CEngineVGui_Detach(); + CFPSPanel_Detach(); + CHLClient_Detach(); +#endif // !DEDICATED + CServer_Detach(); + +#ifdef DEDICATED + CHostState_Detach(); // Dedicated only for now until backwards compatible with S1. +#endif // DEDICATED + + CNetChan_Detach(); + ConCommand_Detach(); + IConVar_Detach(); + CKeyValueSystem_Detach(); + IVEngineServer_Detach(); + SQAPI_Detach(); + SQVM_Detach(); + SysDll_Detach(); + SysUtils_Detach(); + + // Commit the transaction + DetourTransactionCommit(); +} + +////////////////////////////////////////////////////////// +// +// ██████╗ ███████╗███████╗██╗ ██╗██╗ ████████╗███████╗ +// ██╔══██╗██╔════╝██╔════╝██║ ██║██║ ╚══██╔══╝██╔════╝ +// ██████╔╝█████╗ ███████╗██║ ██║██║ ██║ ███████╗ +// ██╔══██╗██╔══╝ ╚════██║██║ ██║██║ ██║ ╚════██║ +// ██║ ██║███████╗███████║╚██████╔╝███████╗██║ ███████║ +// ╚═╝ ╚═╝╚══════╝╚══════╝ ╚═════╝ ╚══════╝╚═╝ ╚══════╝ +// +////////////////////////////////////////////////////////// + +void PrintHAddress() // Test the sigscan results +{ + std::cout << "+----------------------------------------------------------------+" << std::endl; + for (IDetour* pdetour : vdetour) + { + pdetour->debugp(); + } +} diff --git a/r5dev/core/init.h b/r5dev/core/init.h new file mode 100644 index 00000000..8ddabd80 --- /dev/null +++ b/r5dev/core/init.h @@ -0,0 +1,10 @@ +#pragma once +namespace +{ + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /* ==== ------- ========================================================================================================================================================= */ +} + +void Systems_Init(); +void Systems_Shutdown(); +void PrintHAddress(); diff --git a/r5dev/core/logdef.h b/r5dev/core/logdef.h new file mode 100644 index 00000000..7a58613e --- /dev/null +++ b/r5dev/core/logdef.h @@ -0,0 +1,23 @@ +#pragma once + +//------------------------------------------------------------------------- +// NETCHAN | +inline auto g_spd_netchan_logger = spdlog::basic_logger_mt("netchan_logger", "platform\\logs\\net_trace.log"); +static std::ostringstream g_spd_net_p_oss; +static auto g_spd_net_p_ostream_sink = std::make_shared(g_spd_net_p_oss); +//------------------------------------------------------------------------- +// FILESYSTEM | +static std::ostringstream fs_oss; +static auto fs_ostream_sink = std::make_shared(fs_oss); +//------------------------------------------------------------------------- +// SQUIRREL PRINTF | +inline std::ostringstream g_spd_sqvm_p_oss; +inline auto g_spd_sqvm_p_ostream_sink = std::make_shared(g_spd_sqvm_p_oss); +//------------------------------------------------------------------------- +// SQUIRREL WARNF | +inline std::ostringstream g_spd_sqvm_w_oss; +inline auto g_spd_sqvm_w_ostream_sink = std::make_shared(g_spd_sqvm_w_oss); +//------------------------------------------------------------------------- +// SYSTEM PRINTF | +inline std::ostringstream g_spd_sys_w_oss; +inline auto g_spd_sys_p_ostream_sink = std::make_shared(g_spd_sys_w_oss); diff --git a/r5dev/include/r5dev.h b/r5dev/core/r5dev.h similarity index 100% rename from r5dev/include/r5dev.h rename to r5dev/core/r5dev.h diff --git a/r5dev/resource.h b/r5dev/core/resource.h similarity index 67% rename from r5dev/resource.h rename to r5dev/core/resource.h index f1b308d1..9efea4f3 100644 --- a/r5dev/resource.h +++ b/r5dev/core/resource.h @@ -1,12 +1,15 @@ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. -// Used by BuildVersion.rc +// Used by r5dev.rc +// +#define PNG 256 +#define IDB_PNG1 101 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_RESOURCE_VALUE 102 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 diff --git a/r5dev/core/stdafx.cpp b/r5dev/core/stdafx.cpp new file mode 100644 index 00000000..1f4c90b9 --- /dev/null +++ b/r5dev/core/stdafx.cpp @@ -0,0 +1 @@ +#include "core/stdafx.h" diff --git a/r5dev/core/stdafx.h b/r5dev/core/stdafx.h new file mode 100644 index 00000000..c910af11 --- /dev/null +++ b/r5dev/core/stdafx.h @@ -0,0 +1,56 @@ +#pragma once +#pragma message("Pre-compiling headers.\n") + +#define WIN32_LEAN_AND_MEAN // Prevent winsock2 redefinition. +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(DEDICATED) +#include +#endif // !DEDICATED + +#include "thirdparty/detours/include/detours.h" +#include "thirdparty/detours/include/idetour.h" + +#if !defined(DEDICATED) +#include "thirdparty/imgui/include/imgui.h" +#include "thirdparty/imgui/include/imgui_utility.h" +#include "thirdparty/imgui/include/imgui_stdlib.h" +#include "thirdparty/imgui/include/imgui_impl_dx11.h" +#include "thirdparty/imgui/include/imgui_impl_win32.h" +#endif // !DEDICATED + +#include "thirdparty/spdlog/include/spdlog.h" +#include "thirdparty/spdlog/include/sinks/basic_file_sink.h" +#include "thirdparty/spdlog/include/sinks/stdout_sinks.h" +#include "thirdparty/spdlog/include/sinks/ostream_sink.h" +#include "public/include/utility.h" +#include "public/include/memaddr.h" +#include "public/include/httplib.h" +#include "public/include/json.hpp" + +#ifndef SDKLAUNCHER +namespace +{ + MODULE g_mGameDll = MODULE("r5apex.exe"); + MODULE g_mRadVideoToolsDll = MODULE("bink2w64.dll"); + MODULE g_mRadAudioDecoderDll = MODULE("binkawin64.dll"); + MODULE g_mRadAudioSystemDll = MODULE("mileswin64.dll"); +} +#endif // SDKLAUNCHER diff --git a/r5dev/cpp.hint b/r5dev/cpp.hint new file mode 100644 index 00000000..15955c79 --- /dev/null +++ b/r5dev/cpp.hint @@ -0,0 +1,9 @@ +// Hint files help the Visual Studio IDE interpret Visual C++ identifiers +// such as names of functions and macros. +// For more information see https://go.microsoft.com/fwlink/?linkid=865984 +#define CIMGUI_API +#define NULL +#define NULL ((void *)0) +#define IM_FMTARGS(FMT) __attribute__((format(printf, FMT, FMT+1))) +#define IM_FMTARGS(FMT) __attribute__((format(gnu_printf, FMT, FMT+1))) +#define IM_FMTARGS(FMT) diff --git a/r5dev/dedicated.vcxproj b/r5dev/dedicated.vcxproj new file mode 100644 index 00000000..b31728a8 --- /dev/null +++ b/r5dev/dedicated.vcxproj @@ -0,0 +1,377 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {ed2c50b3-7c2c-4e44-988e-daa059f72b9c} + dedicated + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + DynamicLibrary + true + v143 + MultiByte + Static + + + DynamicLibrary + false + v143 + true + MultiByte + Static + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + $(SolutionDir)r5dev\;$(IncludePath); + $(SolutionDir)r5dev\thirdparty\detours\libs;$(SolutionDir)r5dev\thirdparty\lzham\libs;$(LibraryPath); + $(SolutionDir)bin\$(Configuration)\ + $(SolutionDir)build\$(ProjectName)\$(Configuration)\ + + + false + $(SolutionDir)r5dev\;$(IncludePath); + $(SolutionDir)r5dev\thirdparty\detours\libs;$(SolutionDir)r5dev\thirdparty\lzham\libs;$(LibraryPath); + $(SolutionDir)bin\$(Configuration)\ + $(SolutionDir)build\$(ProjectName)\$(Configuration)\ + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + /D DEDICATED %(AdditionalOptions) + Use + core\stdafx.h + stdcpp17 + stdc17 + + + Console + true + detours.lib;lzhamlib_x64D.lib;lzhamcomp_x64D.lib;lzhamdecomp_x64D.lib;d3d11.lib;bcrypt.lib;%(AdditionalDependencies) + + + del "..\..\..\$(ProjectName)" && copy /Y "$(TargetPath)" "..\..\..\ + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + /D DEDICATED %(AdditionalOptions) + Use + core\stdafx.h + stdcpp17 + true + Speed + stdc17 + + + Console + true + true + true + detours.lib;lzhamlib_x64.lib;lzhamcomp_x64.lib;lzhamdecomp_x64.lib;d3d11.lib;bcrypt.lib;%(AdditionalDependencies) + + + del "..\..\..\$(ProjectName)" && copy /Y "$(TargetPath)" "..\..\..\ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/r5dev/dedicated.vcxproj.filters b/r5dev/dedicated.vcxproj.filters new file mode 100644 index 00000000..a1381cad --- /dev/null +++ b/r5dev/dedicated.vcxproj.filters @@ -0,0 +1,687 @@ + + + + + {f97c7e9b-cc5e-4ec1-8c2a-19cf941b5c90} + + + {c9b133cf-3943-49c9-abb0-c6f9e83a696c} + + + {f280958c-f362-4918-a1f3-ca66f4bf1ec6} + + + {00cd526c-e897-4d88-86e0-03612cfda456} + + + {495a53fc-5055-4449-b42c-b7edc5e83466} + + + {08dd9239-516c-4b9f-8064-28bd127d9806} + + + {a6ab59cc-8df3-4fbd-b94b-2ec6e83feea9} + + + {009673c6-839f-48d7-b2c6-eca9a6e606b1} + + + {08acc045-15dd-469f-b717-35f1dd35c07c} + + + {8786ae3f-91d3-4eb8-89b7-e2135d143faa} + + + {e88d652e-34a0-44a0-a886-8861315c5b4f} + + + {9dc16eaa-182b-42ea-9524-8cc8421829b6} + + + {3792464a-e366-4bbb-86fa-35631e3748f2} + + + {a7be4dd8-3e52-4053-afa5-26faf833e5b7} + + + {3db73603-e800-4763-98dc-f3bf4c674261} + + + {2b79f12d-1506-42e6-94a9-560891c0ff8c} + + + {1a595a55-8bcf-4c0f-8456-4669b8692ce5} + + + {7c06597f-4eda-4695-9d39-d7719ead774e} + + + {a0f19f1c-dfba-49b4-a9d1-2f7c353bf0d8} + + + {6c7f8ba7-fc42-49a3-b22d-c23098e75c0c} + + + {903e9591-e043-4d78-970e-8246198f4d7f} + + + {4f304fe8-8e16-44c3-a173-cb47fb009e23} + + + {2dd28355-d167-479b-8437-855a2186cbb8} + + + {86d45699-fa61-431d-920e-bfdb013b1918} + + + {d4c6d6c0-05da-43ab-a6fc-f61ce721f870} + + + {8aacb674-0b73-41fa-94f0-98ba718289a9} + + + {bbb7a170-37ae-4c76-b99a-8ebb924818ed} + + + {e1897f61-953e-4173-9c75-e59ca33929ea} + + + {e7189077-ca26-4a4e-bb60-eec7e5a1bafc} + + + {24c9de5d-3333-4464-acb2-41f950a879b8} + + + {db372a17-c0de-4a8b-8aa8-9b3129cc5955} + + + {8288ba1a-7609-42ef-af3b-850727635a99} + + + + + sdk\client + + + sdk\client + + + sdk\common + + + sdk\common + + + core + + + core + + + core + + + core + + + sdk\ebisusdk + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\launcher + + + sdk\mathlib + + + sdk\networksystem + + + sdk\networksystem + + + sdk\public\include + + + sdk\public\include + + + sdk\public\include + + + sdk\public\include + + + sdk\public\include + + + sdk\rtech + + + sdk\rtech + + + sdk\server + + + sdk\squirrel + + + sdk\squirrel + + + thirdparty\detours\include + + + thirdparty\detours\include + + + thirdparty\detours\include + + + thirdparty\detours\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\fmt + + + thirdparty\spdlog\include\fmt + + + thirdparty\spdlog\include\fmt + + + thirdparty\spdlog\include\fmt + + + thirdparty\spdlog\include\fmt + + + thirdparty\spdlog\include\fmt\bundled + + + thirdparty\spdlog\include\fmt\bundled + + + thirdparty\spdlog\include\fmt\bundled + + + thirdparty\spdlog\include\fmt\bundled + + + thirdparty\spdlog\include\fmt\bundled + + + thirdparty\spdlog\include\fmt\bundled + + + thirdparty\spdlog\include\fmt\bundled + + + thirdparty\spdlog\include\fmt\bundled + + + thirdparty\spdlog\include\fmt\bundled + + + thirdparty\spdlog\include\fmt\bundled + + + thirdparty\spdlog\include\fmt\bundled + + + thirdparty\spdlog\include\fmt\bundled + + + thirdparty\spdlog\include\fmt\bundled + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\cfg + + + thirdparty\spdlog\include\cfg + + + thirdparty\spdlog\include\cfg + + + thirdparty\spdlog\include\cfg + + + sdk\tier0 + + + sdk\tier0 + + + sdk\tier0 + + + sdk\tier0 + + + sdk\tier0 + + + sdk\vpc + + + sdk\vpc + + + sdk\vpc + + + sdk\vpc + + + sdk\vphysics + + + windows + + + sdk\networksystem + + + sdk\mathlib + + + sdk\mathlib + + + sdk\squirrel + + + sdk\server + + + sdk\tier0 + + + sdk\mathlib + + + sdk\vpklib + + + sdk\public\include + + + thirdparty\lzham\include + + + thirdparty\lzham\include + + + sdk\mathlib + + + sdk\bsplib + + + sdk\common + + + + + sdk\client + + + sdk\client + + + sdk\common + + + core + + + core + + + core + + + sdk\ebisusdk + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\launcher + + + sdk\public + + + sdk\public + + + sdk\rtech + + + sdk\rtech + + + sdk\server + + + sdk\squirrel + + + sdk\squirrel + + + sdk\tier0 + + + sdk\tier0 + + + sdk\tier0 + + + sdk\tier0 + + + sdk\vpc + + + sdk\vpc + + + sdk\vpc + + + sdk\vphysics + + + windows + + + sdk\networksystem + + + sdk\mathlib + + + sdk\squirrel + + + sdk\server + + + sdk\vpklib + + + sdk\public + + + sdk\bsplib + + + sdk\mathlib + + + sdk\mathlib + + + + + + \ No newline at end of file diff --git a/r5dev/ebisusdk/EbisuSDK.cpp b/r5dev/ebisusdk/EbisuSDK.cpp new file mode 100644 index 00000000..4e865c3a --- /dev/null +++ b/r5dev/ebisusdk/EbisuSDK.cpp @@ -0,0 +1,26 @@ +#include "core/stdafx.h" +#include "ebisusdk/EbisuSDK.h" +#include "client/client.h" +#include "engine/sys_utils.h" + +//----------------------------------------------------------------------------- +// Purpose: sets the EbisuSDK globals for dedicated to satisfy command callbacks +//----------------------------------------------------------------------------- +void HEbisuSDK_Init() +{ +#ifdef DEDICATED + *(char*)g_bEbisuSDKInitialized.GetPtr() = (char)0x1; // <- 1st EbisuSDK + *(char*)g_bEbisuSDKCvarInitialized.GetPtr() = (char)0x1; // <- 2nd EbisuSDK + *(char*)g_qEbisuSDKCvarInitialized.GetPtr() = (char)0x1; // <- 3rd EbisuSDK +#endif // DEDICATED +} + +void EbisuSDK_Attach() +{ + // +} + +void EbisuSDK_Detach() +{ + // +} diff --git a/r5dev/ebisusdk/EbisuSDK.h b/r5dev/ebisusdk/EbisuSDK.h new file mode 100644 index 00000000..41157b8e --- /dev/null +++ b/r5dev/ebisusdk/EbisuSDK.h @@ -0,0 +1,64 @@ +#pragma once +#include "tier0/basetypes.h" +#include "tier0/completion.h" +#include "public/include/utility.h" + +namespace +{ +#ifdef DEDICATED + ADDRESS p_EbisuSDK_Init_Tier0 = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x83\xEC\x28\x80\x3D\x00\x00\x00\x00\x00\x0F\x85\x00\x02\x00\x00\x48\x89\x5C\x24\x20", "xxxxxx????xxx?xxxxxxxx").GetPtr(); + void(*EbisuSDK_Init_Tier0) = (void(*))p_EbisuSDK_Init_Tier0.GetPtr(); /*48 83 EC 28 80 3D ?? ?? ?? ?? 00 0F 85 ?? 02 00 00 48 89 5C 24 20*/ + + ADDRESS p_EbisuSDK_CVar_Init = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x57\x48\x83\xEC\x40\x83\x3D", "xxxxxxxx"); + void(*EbisuSDK_CVar_Init) = (void(*))p_EbisuSDK_CVar_Init.GetPtr(); /*40 57 48 83 EC 40 83 3D*/ +#else + ADDRESS p_OriginGetErrorDescription = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x53\x48\x83\xEC\x20\x8B\xD9\x48\x8D\x15\x00\x00\x00\x00", "xxxxxxxxxxx????").GetPtr(); + void(*OriginGetErrorDescription) = (void(*))p_OriginGetErrorDescription.GetPtr(); /*40 53 48 83 EC 20 8B D9 48 8D 15 ?? ?? ?? ??*/ +#endif +} + +namespace +{ +#ifdef DEDICATED +#if defined (GAMEDLL_S1) + ADDRESS g_bEbisuSDKInitialized = p_EbisuSDK_Init_Tier0.FindPatternSelf("80 3D ?? ?? ?? ?? 00", ADDRESS::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x7).GetPtr(); + ADDRESS g_bEbisuSDKCvarInitialized = p_Map_Callback.FindPatternSelf("80 3D 8F 7C 1E 22 00", ADDRESS::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x7).GetPtr(); + ADDRESS g_qEbisuSDKCvarInitialized = p_EbisuSDK_CVar_Init.FindPatternSelf("4C 89 05 C4 2B 0E 22", ADDRESS::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr(); +#elif defined (GAMEDLL_S2) + ADDRESS g_bEbisuSDKInitialized = p_EbisuSDK_Init_Tier0.FindPatternSelf("80 3D ?? ?? ?? ?? 00", ADDRESS::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x7).GetPtr(); + ADDRESS g_bEbisuSDKCvarInitialized = p_Map_Callback.FindPatternSelf("80 3D 43 2D 41 22 00", ADDRESS::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x7).GetPtr(); + ADDRESS g_qEbisuSDKCvarInitialized = p_EbisuSDK_CVar_Init.FindPatternSelf("4C 89 05 74 2D 32 22", ADDRESS::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr(); +#elif defined (GAMEDLL_S3) + ADDRESS g_bEbisuSDKInitialized = p_EbisuSDK_Init_Tier0.FindPatternSelf("80 3D ?? ?? ?? ?? 00", ADDRESS::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x7).GetPtr(); + ADDRESS g_bEbisuSDKCvarInitialized = p_Map_Callback.FindPatternSelf("80 3D 23 54 2B 23 00", ADDRESS::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x7).GetPtr(); + ADDRESS g_qEbisuSDKCvarInitialized = p_EbisuSDK_CVar_Init.FindPatternSelf("4C 89 05 B4 2C 1C 23", ADDRESS::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr(); +#endif // GAMEDLL_* +#endif // DEDICATED +} +/////////////////////////////////////////////////////////////////////////////// +void HEbisuSDK_Init(); + +void EbisuSDK_Attach(); +void EbisuSDK_Detach(); + + +/////////////////////////////////////////////////////////////////////////////// +class HEbisuSDK : public IDetour +{ + virtual void debugp() + { +#ifdef DEDICATED + std::cout << "| FUN: EbisuSDK_Init_Tier0 : 0x" << std::hex << std::uppercase << p_EbisuSDK_Init_Tier0.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: EbisuSDK_CVar_Init : 0x" << std::hex << std::uppercase << p_EbisuSDK_CVar_Init.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: g_bEbisuSDKInitialized : 0x" << std::hex << std::uppercase << g_bEbisuSDKInitialized.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: g_bEbisuSDKCvarInitialized : 0x" << std::hex << std::uppercase << g_bEbisuSDKCvarInitialized.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: g_qEbisuSDKCvarInitialized : 0x" << std::hex << std::uppercase << g_qEbisuSDKCvarInitialized.GetPtr() << std::setw(npad) << " |" << std::endl; +#else + std::cout << "| FUN: OriginGetErrorDescription : 0x" << std::hex << std::uppercase << p_OriginGetErrorDescription.GetPtr() << std::setw(npad) << " |" << std::endl; +#endif // DEDICATED + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HEbisuSDK); diff --git a/r5dev/ebisusdk/EbisuTypes.h b/r5dev/ebisusdk/EbisuTypes.h new file mode 100644 index 00000000..aea65b49 --- /dev/null +++ b/r5dev/ebisusdk/EbisuTypes.h @@ -0,0 +1,137 @@ +#pragma once + +//----------------------------------------------------------------------------- +// General errors +//----------------------------------------------------------------------------- +#define ORIGIN_ERROR 0xA0000000 +#define ORIGIN_WARNING 0x40000000 +#define ORIGIN_ERROR_AREA_GENERAL 0x00000000 +#define ORIGIN_ERROR_AREA_SDK (1<<16) +#define ORIGIN_ERROR_AREA_CORE (2<<16) +#define ORIGIN_ERROR_AREA_IGO (3<<16) +#define ORIGIN_ERROR_AREA_FRIENDS (4<<16) +#define ORIGIN_ERROR_AREA_PRESENCE (5<<16) +#define ORIGIN_ERROR_AREA_COMMERCE (6<<16) +#define ORIGIN_ERROR_AREA_ACHIEVEMENTS (7<<16) +#define ORIGIN_ERROR_AREA_LSX (8<<16) +#define ORIGIN_ERROR_AREA_PROXY (9<<16) +#define ORIGIN_ERROR_LEVEL_SHIFT 24 +#define ORIGIN_ERROR_LEVEL_MASK 0x0F000000 +#define ORIGIN_LEVEL_0 (0<<24) +#define ORIGIN_LEVEL_1 (1<<24) +#define ORIGIN_LEVEL_2 (2<<24) +#define ORIGIN_LEVEL_3 (3<<24) +#define ORIGIN_LEVEL_4 (4<<24) + +//----------------------------------------------------------------------------- +// Origin errors +//----------------------------------------------------------------------------- +#define ORIGIN_SUCCESS 0 +#define ORIGIN_PENDING 1 +#define ORIGIN_ERROR_GENERAL -1 +#define ORIGIN_ERROR_INVALID_HANDLE (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_GENERAL + 0) +#define ORIGIN_ERROR_OUT_OF_MEMORY (ORIGIN_ERROR + ORIGIN_LEVEL_0 + ORIGIN_ERROR_AREA_GENERAL + 1) +#define ORIGIN_ERROR_NOT_IMPLEMENTED (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_GENERAL + 2) +#define ORIGIN_ERROR_INVALID_USER (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 3) +#define ORIGIN_ERROR_INVALID_ARGUMENT (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 4) +#define ORIGIN_ERROR_NO_CALLBACK_SPECIFIED (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 5) +#define ORIGIN_ERROR_BUFFER_TOO_SMALL (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 6) +#define ORIGIN_ERROR_TOO_MANY_VALUES_IN_LIST (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 7) +#define ORIGIN_ERROR_NOT_FOUND (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 8) +#define ORIGIN_ERROR_INVALID_PERSONA (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 9) +#define ORIGIN_ERROR_NO_NETWORK (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 10) +#define ORIGIN_ERROR_NO_SERVICE (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 11) +#define ORIGIN_ERROR_NOT_LOGGED_IN (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 12) +#define ORIGIN_ERROR_MANDATORY_ORIGIN_UPDATE_PENDING (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 13) +#define ORIGIN_ERROR_ACCOUNT_IN_USE (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 14) +#define ORIGIN_ERROR_TOO_MANY_INSTANCES (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_GENERAL + 15) +#define ORIGIN_ERROR_ALREADY_EXISTS (ORIGIN_ERROR + ORIGIN_LEVEL_3 + ORIGIN_ERROR_AREA_GENERAL + 16) +#define ORIGIN_ERROR_INVALID_OPERATION (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 17) +#define ORIGIN_ERROR_AGE_RESTRICTED (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 18) +#define ORIGIN_ERROR_BANNED (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 19) +#define ORIGIN_ERROR_NOT_READY (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 20) + +//----------------------------------------------------------------------------- +// SDK errors +//----------------------------------------------------------------------------- +#define ORIGIN_ERROR_SDK_NOT_INITIALIZED (ORIGIN_ERROR + ORIGIN_LEVEL_0 + ORIGIN_ERROR_AREA_SDK + 0) +#define ORIGIN_ERROR_SDK_INVALID_ALLOCATOR_DEALLOCATOR_COMBINATION (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_SDK + 1) +#define ORIGIN_ERROR_SDK_IS_RUNNING (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_SDK + 2) +#define ORIGIN_ERROR_SDK_NOT_ALL_RESOURCES_RELEASED (ORIGIN_ERROR + ORIGIN_LEVEL_3 + ORIGIN_ERROR_AREA_SDK + 3) +#define ORIGIN_ERROR_SDK_INVALID_RESOURCE (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_SDK + 4) +#define ORIGIN_ERROR_SDK_INTERNAL_ERROR (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_SDK + 5) +#define ORIGIN_ERROR_SDK_INTERNAL_BUFFER_TOO_SMALL (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_SDK + 6) + +//----------------------------------------------------------------------------- +// SDK warnings +//----------------------------------------------------------------------------- +#define ORIGIN_WARNING_SDK_ALREADY_INITIALIZED (ORIGIN_WARNING + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_SDK + 1) +#define ORIGIN_WARNING_SDK_STILL_RUNNING (ORIGIN_WARNING + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_SDK + 2) +#define ORIGIN_WARNING_SDK_ENUMERATOR_IN_USE (ORIGIN_WARNING + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_SDK + 3) +#define ORIGIN_WARNING_SDK_ENUMERATOR_TERMINATED (ORIGIN_WARNING + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_SDK + 4) + +//----------------------------------------------------------------------------- +// Core errors +//----------------------------------------------------------------------------- +#define ORIGIN_ERROR_CORE_NOTLOADED (ORIGIN_ERROR + ORIGIN_LEVEL_0 + ORIGIN_ERROR_AREA_CORE + 0) +#define ORIGIN_ERROR_CORE_LOGIN_FAILED (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_CORE + 1) +#define ORIGIN_ERROR_CORE_AUTHENTICATION_FAILED (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_CORE + 2 +#define ORIGIN_ERROR_CORE_SEND_FAILED (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_CORE + 4) +#define ORIGIN_ERROR_CORE_RECEIVE_FAILED (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_CORE + 5) +#define ORIGIN_ERROR_CORE_RESOURCE_NOT_FOUND (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_CORE + 6) +#define ORIGIN_ERROR_CORE_INCOMPATIBLE_VERSION (ORIGIN_ERROR + ORIGIN_LEVEL_0 + ORIGIN_ERROR_AREA_CORE + 7) +#define ORIGIN_ERROR_CORE_NOT_INSTALLED (ORIGIN_ERROR + ORIGIN_LEVEL_0 + ORIGIN_ERROR_AREA_CORE + 8) + +//----------------------------------------------------------------------------- +// In-game overlay errors +//----------------------------------------------------------------------------- +#define ORIGIN_WARNING_IGO_NOTLOADED (ORIGIN_WARNING + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_IGO + 0) +#define ORIGIN_WARNING_IGO_SUPPORT_NOTLOADED (ORIGIN_WARNING + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_IGO + 1) +#define ORIGIN_ERROR_IGO_ILLEGAL_ANCHOR_POINT (ORIGIN_ERROR + ORIGIN_LEVEL_3 + ORIGIN_ERROR_AREA_IGO + 2) +#define ORIGIN_ERROR_IGO_ILLEGAL_DOCK_POINT (ORIGIN_ERROR + ORIGIN_LEVEL_3 + ORIGIN_ERROR_AREA_IGO + 3) +#define ORIGIN_ERROR_IGO_NOT_AVAILABLE (ORIGIN_ERROR + ORIGIN_LEVEL_3 + ORIGIN_ERROR_AREA_IGO + 4) + +//----------------------------------------------------------------------------- +// Presence errors +//----------------------------------------------------------------------------- +#define ORIGIN_ERROR_NO_MULTIPLAYER_ID (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PRESENCE + 0) + +//----------------------------------------------------------------------------- +// LSX errors +//----------------------------------------------------------------------------- +#define ORIGIN_ERROR_LSX_INVALID_RESPONSE (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_LSX + 0) +#define ORIGIN_ERROR_LSX_NO_RESPONSE (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_LSX + 1) +#define ORIGIN_ERROR_LSX_INVALID_REQUEST (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_LSX + 2) + +//----------------------------------------------------------------------------- +// Commerce errors +//----------------------------------------------------------------------------- +#define ORIGIN_ERROR_COMMERCE_NO_SUCH_STORE (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_COMMERCE + 0) +#define ORIGIN_ERROR_COMMERCE_NO_SUCH_CATALOG (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_COMMERCE + 1) +#define ORIGIN_ERROR_COMMERCE_INVALID_REPLY (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_COMMERCE + 2) +#define ORIGIN_ERROR_COMMERCE_NO_CATEGORIES (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_COMMERCE + 3) +#define ORIGIN_ERROR_COMMERCE_NO_PRODUCTS (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_COMMERCE + 4) +#define ORIGIN_ERROR_COMMERCE_UNDERAGE_USER (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_COMMERCE + 5) +#define ORIGIN_ERROR_COMMERCE_DEPRECATED_STORE (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_COMMERCE + 6) +#define ORIGIN_ERROR_PROXY (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 0) +#define ORIGIN_SUCCESS_PROXY_OK (ORIGIN_WARNING + ORIGIN_LEVEL_4 + ORIGIN_ERROR_AREA_PROXY + 200) +#define ORIGIN_SUCCESS_PROXY_CREATED (ORIGIN_WARNING + ORIGIN_LEVEL_4 + ORIGIN_ERROR_AREA_PROXY + 201) +#define ORIGIN_SUCCESS_PROXY_ACCEPTED (ORIGIN_WARNING + ORIGIN_LEVEL_4 + ORIGIN_ERROR_AREA_PROXY + 202) +#define ORIGIN_SUCCESS_PROXY_NON_AUTH_INFO (ORIGIN_WARNING + ORIGIN_LEVEL_4 + ORIGIN_ERROR_AREA_PROXY + 203) +#define ORIGIN_SUCCESS_PROXY_NO_CONTENT (ORIGIN_WARNING + ORIGIN_LEVEL_4 + ORIGIN_ERROR_AREA_PROXY + 204) +#define ORIGIN_SUCCESS_RESET_CONTENT (ORIGIN_WARNING + ORIGIN_LEVEL_4 + ORIGIN_ERROR_AREA_PROXY + 205) +#define ORIGIN_SUCCESS_PARTIAL_CONTENT (ORIGIN_WARNING + ORIGIN_LEVEL_4 + ORIGIN_ERROR_AREA_PROXY + 206) +#define ORIGIN_ERROR_PROXY_BAD_REQUEST (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 400) +#define ORIGIN_ERROR_PROXY_UNAUTHORIZED (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 401) +#define ORIGIN_ERROR_PROXY_PAYMENT_REQUIRED (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 402) +#define ORIGIN_ERROR_PROXY_FORBIDDEN (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 403) +#define ORIGIN_ERROR_PROXY_NOT_FOUND (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 404) +#define ORIGIN_ERROR_PROXY_METHOD_NOT_ALLOWED (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 405) +#define ORIGIN_ERROR_PROXY_NOT_ACCEPTABLE (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 406) +#define ORIGIN_ERROR_PROXY_REQUEST_TIMEOUT (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 408) +#define ORIGIN_ERROR_PROXY_CONFLICT (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 409) +#define ORIGIN_ERROR_PROXY_INTERNAL_ERROR (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 500) +#define ORIGIN_ERROR_PROXY_NOT_IMPLEMENTED (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 501) +#define ORIGIN_ERROR_PROXY_BAD_GATEWAY (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 502) +#define ORIGIN_ERROR_PROXY_SERVICE_UNAVAILABLE (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 503) +#define ORIGIN_ERROR_PROXY_GATEWAY_TIMEOUT (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 504) diff --git a/r5dev/engine/baseclient.cpp b/r5dev/engine/baseclient.cpp new file mode 100644 index 00000000..2fe8abbb --- /dev/null +++ b/r5dev/engine/baseclient.cpp @@ -0,0 +1,20 @@ +#include "core/stdafx.h" +#include "engine/baseclient.h" + +//--------------------------------------------------------------------------------- +// Purpose: throw away any residual garbage in the channel +//--------------------------------------------------------------------------------- +std::int64_t* HCBaseClient_Clear(std::int64_t client) +{ + return CBaseClient_Clear(client); +} + +/////////////////////////////////////////////////////////////////////////////// +void CBaseClient_Attach() +{ + DetourAttach((LPVOID*)&CBaseClient_Clear, &HCBaseClient_Clear); +} +void CBaseClient_Detach() +{ + DetourDetach((LPVOID*)&CBaseClient_Clear, &HCBaseClient_Clear); +} diff --git a/r5dev/engine/baseclient.h b/r5dev/engine/baseclient.h new file mode 100644 index 00000000..cb929954 --- /dev/null +++ b/r5dev/engine/baseclient.h @@ -0,0 +1,27 @@ +#pragma once + +namespace +{ + /* ==== CBASECLIENT ===================================================================================================================================================== */ + ADDRESS p_CBaseClient_Clear = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x53\x41\x56\x41\x57\x48\x83\xEC\x20\x48\x8B\xD9\x48\x89\x74", "xxxxxxxxxxxxxxxx"); + std::int64_t* (*CBaseClient_Clear)(std::int64_t client) = (std::int64_t * (*)(std::int64_t))p_CBaseClient_Clear.GetPtr(); /*40 53 41 56 41 57 48 83 EC 20 48 8B D9 48 89 74*/ +} + +/////////////////////////////////////////////////////////////////////////////// +std::int64_t* HCBaseClient_Clear(std::int64_t client); + +void CBaseClient_Attach(); +void CBaseClient_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +class HBaseClient : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: CBaseClient::Clear : 0x" << std::hex << std::uppercase << p_CBaseClient_Clear.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HBaseClient); diff --git a/r5dev/engine/baseclientstate.cpp b/r5dev/engine/baseclientstate.cpp new file mode 100644 index 00000000..9d9576b4 --- /dev/null +++ b/r5dev/engine/baseclientstate.cpp @@ -0,0 +1,9 @@ +//===========================================================================// +// +// Purpose: +// +//===========================================================================// + +#include "core/stdafx.h" +#include "engine/baseclientstate.h" +//TODO diff --git a/r5dev/engine/baseclientstate.h b/r5dev/engine/baseclientstate.h new file mode 100644 index 00000000..3f44032f --- /dev/null +++ b/r5dev/engine/baseclientstate.h @@ -0,0 +1,31 @@ +#pragma once +#include "tier0/basetypes.h" + +namespace +{ + /* ==== CCLIENTSTATE ==================================================================================================================================================== */ +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) + //ADDRESS p_CClientState__CheckForResend = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x00\x56\x57\x41\x57\x00\x81\xEC\x20\x04\x00\x00\x45\x0F\xB6\xF9\x00\x00\x00\x00\x8B\xF1\x48", "xxxx?xxxx?xxxx?xxxxx????xxx"); /*48 89 5C 24 ?? 56 57 41 57 ?? 81 EC 20 04 ?? 00 45 0F B6 F9 ?? ?? ?? ?? 8B F1 48*/ + //void (*CClientState__CheckForResend)(std::int64_t a1, const char* a2, std::int64_t a3, char a4, int a5, std::uint8_t* a6) = (void(*)(std::int64_t, const char*, std::int64_t, char, int, std::uint8_t*))p_CClientState__CheckForResend.GetPtr(); +#elif defined (GAMEDLL_S2) + //ADDRESS p_CClientState__CheckForResend = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x00\x48\x89\x74\x24\x00\x48\x89\x7C\x24\x00\x41\x56\x48\x81\xEC\x00\x00\x00\x00\x45\x0F\xB6", "xxxx?xxxx?xxxx?xxxxx????xxx"); /*48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 56 48 81 EC ?? ?? ?? ?? 45 0F B6*/ + //void (*CClientState__CheckForResend)(std::int64_t a1, const char* a2, std::int64_t a3, char a4, int a5, std::uint8_t* a6) = (void(*)(std::int64_t, const char*, std::int64_t, char, int, std::uint8_t*))p_CClientState__CheckForResend.GetPtr(); +#elif defined (GAMEDLL_S3) + //ADDRESS p_CClientState__CheckForResend = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x00\x48\x89\x74\x24\x00\x48\x89\x7C\x24\x00\x41\x56\x48\x81\xEC\x00\x00\x00\x00\x48\x8B\x32", "xxxx?xxxx?xxxx?xxxxx????xxx"); /*48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 56 48 81 EC ?? ?? ?? ?? 48 8B 32*/ + //void (*CClientState__CheckForResend)(std::int64_t a1, const char* a2, std::int64_t a3, char a4, int a5, std::uint8_t* a6) = (void(*)(std::int64_t, const char*, std::int64_t, char, int, std::uint8_t*))p_CClientState__CheckForResend.GetPtr(); +#endif +} + + +/////////////////////////////////////////////////////////////////////////////// +class HClientState : public IDetour +{ + virtual void debugp() + { + //std::cout << "| FUN: CClientState::CheckForResend : 0x" << std::hex << std::uppercase << p_CClientState__CheckForResend.GetPtr() << std::setw(npad) << " |" << std::endl; + //std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HClientState); diff --git a/r5dev/engine/host_state.cpp b/r5dev/engine/host_state.cpp new file mode 100644 index 00000000..b8cdcde9 --- /dev/null +++ b/r5dev/engine/host_state.cpp @@ -0,0 +1,244 @@ +#include "core/stdafx.h" +#include "engine/sys_utils.h" +#include "engine/host_state.h" +#include "engine/net_chan.h" +#include "tier0/cvar.h" +#include "client/IVEngineClient.h" + +//----------------------------------------------------------------------------- +// Purpose: state machine's main processing loop +//----------------------------------------------------------------------------- +void HCHostState_FrameUpdate(void* rcx, void* rdx, float time) +{ + static auto setjmpFn = ADDRESS(0x141205460).RCast(); + static auto host_abortserver = ADDRESS(0x14B37C700).RCast(); + static auto CHostState_InitFn = ADDRESS(0x14023E7D0).RCast(); + static auto g_ServerAbortServer = ADDRESS(0x14B37CA22).RCast(); + static auto State_RunFn = ADDRESS(0x14023E870).RCast(); + static auto Cbuf_ExecuteFn = ADDRESS(0x14020D5C0).RCast(); + static auto g_ServerGameClients = ADDRESS(0x14B383428).RCast(); + static auto SV_InitGameDLLFn = ADDRESS(0x140308B90).RCast(); + static auto g_CModelLoader = ADDRESS(0x14173B210).RCast(); + static auto CModelLoader_Map_IsValidFn = ADDRESS(0x1402562F0).RCast(); + static auto Host_NewGameFn = ADDRESS(0x140238DA0).RCast(); + static auto Host_Game_ShutdownFn = ADDRESS(0x14023EDA0).RCast(); + static auto src_drawloading = ADDRESS(0x14B37D96B).RCast(); + static auto scr_engineevent_loadingstarted = ADDRESS(0x1666ED024).RCast(); + static auto gfExtendedError = ADDRESS(0x14B383391).RCast(); + static auto g_CEngineVGui = ADDRESS(0x141741310).RCast(); + static auto g_ServerDLL = ADDRESS(0x141732048).RCast(); + static auto Host_ChangelevelFn = ADDRESS(0x1402387B0).RCast(); + static auto CL_EndMovieFn = ADDRESS(0x1402C03D0).RCast(); + static auto SendOfflineRequestToStryderFn = ADDRESS(0x14033D380).RCast(); + static auto CEngine = ADDRESS(0X141741BA0).RCast(); + + static bool bInitialized = false; + if (!bInitialized) + { + IConVar_ClearHostNames(); + ConCommand_InitConCommand(); + + IVEngineClient_CommandExecute(NULL, "exec autoexec.cfg"); + IVEngineClient_CommandExecute(NULL, "exec autoexec_server.cfg"); +#ifndef DEDICATED + IVEngineClient_CommandExecute(NULL, "exec autoexec_client.cfg"); +#endif // !DEDICATED + + *(bool*)m_bRestrictServerCommands = true; // Restrict commands. + void* disconnect = g_pCvar->FindCommand("disconnect"); + *(std::int32_t*)((std::uintptr_t)disconnect + 0x38) |= FCVAR_SERVER_CAN_EXECUTE; // Make sure server is not restricted to this. + + if (net_userandomkey->m_pParent->m_iValue == 1) + { + HNET_GenerateKey(); + } + + g_pCvar->FindVar("net_usesocketsforloopback")->m_pParent->m_iValue = 1; + + bInitialized = true; + } + + HostStates_t oldState{}; + void* placeHolder = nullptr; + if (setjmpFn(*host_abortserver, placeHolder)) + { + CHostState_InitFn(g_pHostState); + return; + } + else + { + *g_ServerAbortServer = true; + + do + { + Cbuf_ExecuteFn(); + oldState = g_pHostState->m_iCurrentState; + switch (g_pHostState->m_iCurrentState) + { + case HostStates_t::HS_NEW_GAME: + { + DevMsg(eDLL_T::ENGINE, "CHostState::FrameUpdate | CASE:HS_NEW_GAME | Loading level: '%s'\n", g_pHostState->m_levelName); + + // Inlined CHostState::State_NewGame + g_pHostState->m_bSplitScreenConnect = false; + if (!g_ServerGameClients) // Init Game if it ain't valid. + { + SV_InitGameDLLFn(); + } + + if (!CModelLoader_Map_IsValidFn(g_CModelLoader, g_pHostState->m_levelName) // Check if map is valid and if we can start a new game. + || !Host_NewGameFn(g_pHostState->m_levelName, nullptr, g_pHostState->m_bBackgroundLevel, g_pHostState->m_bSplitScreenConnect, nullptr) || !g_ServerGameClients) + { + DevMsg(eDLL_T::ENGINE, "CHostState::FrameUpdate | CASE:HS_NEW_GAME | Error: Map not valid.\n"); + // Inlined SCR_EndLoadingPlaque + if (*src_drawloading) + { + *scr_engineevent_loadingstarted = 0; + using HideLoadingPlaqueFn = void(*)(void*); + (*reinterpret_cast(g_CEngineVGui))[36](g_CEngineVGui); // (*((void(__fastcall**)(void**))g_CEngineVGui + 36))(&g_CEngineVGui);// HideLoadingPlaque + } + else if (*gfExtendedError) + { + using ShowErrorMessageFn = void(*)(void*); + (*reinterpret_cast(g_CEngineVGui))[35](g_CEngineVGui); // (*((void(__fastcall**)(void**))g_CEngineVGui + 35))(&g_CEngineVGui);// ShowErrorMessage + } + // End Inline SCR_EndLoadingPlaque + + // Inlined CHostState::GameShutdown + if (g_pHostState->m_bActiveGame) + { + using GameShutdownFn = void(*)(void*); + (*reinterpret_cast(g_ServerDLL))[9](g_ServerDLL); // (*(void(__fastcall**)(void*))(*(_QWORD*)g_ServerDLL + 72i64))(g_ServerDLL);// GameShutdown + g_pHostState->m_bActiveGame = 0; + } + // End Inline CHostState::GameShutdown + } + + // Seems useless so nope. + // if (g_CHLClient) + // (*(void(__fastcall**)(__int64, _QWORD))(*(_QWORD*)g_CHLClient + 1000i64))(g_CHLClient, 0i64); + + g_pHostState->m_iCurrentState = HostStates_t::HS_RUN; // Set current state to run. + + // If our next state isn't a shutdown or its a forced shutdown then set next state to run. + if (g_pHostState->m_iNextState != HostStates_t::HS_SHUTDOWN || !g_pCvar->FindVar("host_hasIrreversibleShutdown")->m_pParent->m_iValue) + { + g_pHostState->m_iNextState = HostStates_t::HS_RUN; + } + + // End Inline CHostState::State_NewGame + break; + } + case HostStates_t::HS_CHANGE_LEVEL_SP: + { + g_pHostState->m_flShortFrameTime = 1.5; // Set frame time. + + DevMsg(eDLL_T::ENGINE, "CHostState::FrameUpdate | CASE:HS_CHANGE_LEVEL_SP | Changing singleplayer level to: '%s'\n", g_pHostState->m_levelName); + + if (CModelLoader_Map_IsValidFn(g_CModelLoader, g_pHostState->m_levelName)) // Check if map is valid and if we can start a new game. + { + Host_ChangelevelFn(true, g_pHostState->m_levelName, g_pHostState->m_mapGroupName); // Call change level as singleplayer level. + } + else + { + DevMsg(eDLL_T::ENGINE, "CHostState::FrameUpdate | CASE:HS_CHANGE_LEVEL_SP | Error: Unable to find map: '%s'\n", g_pHostState->m_levelName); + } + + // Seems useless so nope. + // if (g_CHLClient) + // (*(void(__fastcall**)(__int64, _QWORD))(*(_QWORD*)g_CHLClient + 1000i64))(g_CHLClient, 0i64); + + g_pHostState->m_iCurrentState = HostStates_t::HS_RUN; // Set current state to run. + + // If our next state isn't a shutdown or its a forced shutdown then set next state to run. + if (g_pHostState->m_iNextState != HostStates_t::HS_SHUTDOWN || !g_pCvar->FindVar("host_hasIrreversibleShutdown")->m_pParent->m_iValue) + { + g_pHostState->m_iNextState = HostStates_t::HS_RUN; + } + + break; + } + case HostStates_t::HS_CHANGE_LEVEL_MP: + { + g_pHostState->m_flShortFrameTime = 0.5; // Set frame time. + using LevelShutdownFn = void(__thiscall*)(void*); + (*reinterpret_cast(*g_ServerDLL))[8](g_ServerDLL); // (*(void (__fastcall **)(void *))(*(_QWORD *)server_dll_var + 64i64))(server_dll_var);// LevelShutdown + + DevMsg(eDLL_T::ENGINE, "CHostState::FrameUpdate | CASE:HS_CHANGE_LEVEL_MP | Changing multiplayer level to: '%s'\n", g_pHostState->m_levelName); + + if (CModelLoader_Map_IsValidFn(g_CModelLoader, g_pHostState->m_levelName)) // Check if map is valid and if we can start a new game. + { + using EnabledProgressBarForNextLoadFn = void(*)(void*); + (*reinterpret_cast(g_CEngineVGui))[31](g_CEngineVGui); // (*((void(__fastcall**)(void**))g_CEngineVGUI + 31))(&g_CEngineVGUI);// EnabledProgressBarForNextLoad + Host_ChangelevelFn(false, g_pHostState->m_levelName, g_pHostState->m_mapGroupName); // Call change level as multiplayer level. + } + else + { + DevMsg(eDLL_T::ENGINE, "CHostState::FrameUpdate | CASE:HS_CHANGE_LEVEL_MP | Error: Unable to find map: '%s'\n", g_pHostState->m_levelName); + } + + // Seems useless so nope. + // // if (g_CHLClient) + // (*(void(__fastcall**)(__int64, _QWORD))(*(_QWORD*)g_CHLClient + 1000i64))(g_CHLClient, 0i64); + + g_pHostState->m_iCurrentState = HostStates_t::HS_RUN; // Set current state to run. + + // If our next state isn't a shutdown or its a forced shutdown then set next state to run. + if (g_pHostState->m_iNextState != HostStates_t::HS_SHUTDOWN || !g_pCvar->FindVar("host_hasIrreversibleShutdown")->m_pParent->m_iValue) + { + g_pHostState->m_iNextState = HostStates_t::HS_RUN; + } + + break; + } + case HostStates_t::HS_RUN: + { + State_RunFn(&g_pHostState->m_iCurrentState, nullptr, time); + break; + } + case HostStates_t::HS_GAME_SHUTDOWN: + { + DevMsg(eDLL_T::ENGINE, "CHostState::FrameUpdate | CASE:HS_GAME_SHUTDOWN | Shutdown game\n"); + Host_Game_ShutdownFn(g_pHostState); + break; + } + case HostStates_t::HS_RESTART: + { + DevMsg(eDLL_T::ENGINE, "CHostState::FrameUpdate | CASE:HS_RESTART | Restarting client\n"); + CL_EndMovieFn(); + SendOfflineRequestToStryderFn(); // We have hostnames nulled anyway. + *(std::int32_t*)((std::uintptr_t)CEngine + 0xC) = 3; //g_CEngine.vtable->SetNextState(&g_CEngine, DLL_RESTART); + break; + } + case HostStates_t::HS_SHUTDOWN: + { + DevMsg(eDLL_T::ENGINE, "CHostState::FrameUpdate | CASE:HS_SHUTDOWN | Shutdown client\n"); + CL_EndMovieFn(); + SendOfflineRequestToStryderFn(); // We have hostnames nulled anyway. + *(std::int32_t*)((std::uintptr_t)CEngine + 0xC) = 2; //g_CEngine.vtable->SetNextState(&g_CEngine, DLL_CLOSE); + break; + } + default: + { + break; + } + } + + } while ((oldState != HostStates_t::HS_RUN || g_pHostState->m_iNextState == HostStates_t::HS_LOAD_GAME && g_pCvar->FindVar("g_single_frame_shutdown_for_reload_cvar")->m_pParent->m_iValue) + && oldState != HostStates_t::HS_SHUTDOWN + && oldState != HostStates_t::HS_RESTART); + + } +} + +void CHostState_Attach() +{ + DetourAttach((LPVOID*)&CHostState_FrameUpdate, &HCHostState_FrameUpdate); +} + +void CHostState_Detach() +{ + DetourDetach((LPVOID*)&CHostState_FrameUpdate, &HCHostState_FrameUpdate); +} + +CHostState* g_pHostState = reinterpret_cast(p_CHostState_FrameUpdate.FindPatternSelf("48 8D ?? ?? ?? ?? 01", ADDRESS::Direction::DOWN, 100).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr());; diff --git a/r5dev/engine/host_state.h b/r5dev/engine/host_state.h new file mode 100644 index 00000000..a16cb06f --- /dev/null +++ b/r5dev/engine/host_state.h @@ -0,0 +1,65 @@ +#pragma once +#include "mathlib/vector.h" + +enum class HostStates_t : int +{ + HS_NEW_GAME = 0x0, + HS_LOAD_GAME = 0x1, + HS_CHANGE_LEVEL_SP = 0x2, + HS_CHANGE_LEVEL_MP = 0x3, + HS_RUN = 0x4, + HS_GAME_SHUTDOWN = 0x5, + HS_SHUTDOWN = 0x6, + HS_RESTART = 0x7, +}; + +class CHostState +{ +public: + HostStates_t m_iCurrentState; //0x0000 + HostStates_t m_iNextState; //0x0004 + Vector3 m_vecLocation; //0x0008 + QAngle m_angLocation; //0x0014 + char m_levelName[64]; //0x0020 + char m_mapGroupName[256]; //0x0060 + char m_landMarkName[256]; //0x0160 + float m_flShortFrameTime; //0x0260 + bool m_bActiveGame; //0x0264 + bool m_bRememberLocation; //0x0265 + bool m_bBackgroundLevel; //0x0266 + bool m_bWaitingForConnection; //0x0267 + bool m_bSplitScreenConnect; //0x0268 + bool m_bGameHasShutDownAndFlushedMemory; //0x0269 + bool m_bWorkshopMapDownloadPending; //0x026A +}; + +namespace +{ + /* ==== CHOSTSTATE ====================================================================================================================================================== */ + ADDRESS p_CHostState_FrameUpdate = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x08\x48\x89\x6C\x24\x20\xF3\x0F\x11\x54\x24\x18", "xxxxxxxxxxxxxxxx"); + void (*CHostState_FrameUpdate)(void* rcx, void* rdx, float time) = (void(*)(void*, void*, float))p_CHostState_FrameUpdate.GetPtr(); /*48 89 5C 24 08 48 89 6C 24 20 F3 0F 11 54 24 18*/ +} + +/////////////////////////////////////////////////////////////////////////////// +void HCHostState_FrameUpdate(void* rcx, void* rdx, float time); + +void CHostState_Attach(); +void CHostState_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +extern CHostState* g_pHostState; + + +/////////////////////////////////////////////////////////////////////////////// +class HHostState : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: CHostState::FrameUpdate : 0x" << std::hex << std::uppercase << p_CHostState_FrameUpdate.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: g_pHostState : 0x" << std::hex << std::uppercase << g_pHostState << std::setw(0) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HHostState); diff --git a/r5dev/engine/net_chan.cpp b/r5dev/engine/net_chan.cpp new file mode 100644 index 00000000..659c48e0 --- /dev/null +++ b/r5dev/engine/net_chan.cpp @@ -0,0 +1,176 @@ +//=============================================================================// +// +// Purpose: Netchannel system utilities +// +//=============================================================================// + +#include "core/stdafx.h" +#include "core/logdef.h" +#include "tier0/cvar.h" +#include "tier0/completion.h" +#include "engine/sys_utils.h" +#include "engine/net_chan.h" +#include "engine/baseclient.h" +#include "server/IVEngineServer.h" + +#ifndef DEDICATED +#include "gameui/IConsole.h" +#endif // !DEDICATED + +//----------------------------------------------------------------------------- +// Purpose: shutdown netchannel +//----------------------------------------------------------------------------- +void NET_ShutDown(void* thisptr, const char* szReason, std::uint8_t a1, char a2) +{ + DownloadPlaylists_Callback(); // Re-load playlist from disk after getting disconnected from the server. + NET_ShutDown(thisptr, szReason, a1, a2); +} + +//----------------------------------------------------------------------------- +// Purpose: hook and log the receive datagram +//----------------------------------------------------------------------------- +bool HNET_ReceiveDatagram(int iSocket, netpacket_s* pInpacket, bool bRaw) +{ + bool result = NET_ReceiveDatagram(iSocket, pInpacket, bRaw); + if (result) + { + // Log received packet data + HexDump("[+] NET_ReceiveDatagram", 0, &pInpacket->data[NULL], pInpacket->wiresize); + } + return result; +} + +//----------------------------------------------------------------------------- +// Purpose: hook and log the send datagram +//----------------------------------------------------------------------------- +void* HNET_SendDatagram(SOCKET s, const char* szPayload, int iLenght, int nFlags) +{ + void* result = NET_SendDatagram(s, szPayload, iLenght, nFlags); + if (result) + { + // Log transmitted packet data + HexDump("[+] NET_SendDatagram", 0, szPayload, iLenght); + } + return result; +} + +//----------------------------------------------------------------------------- +// Purpose: sets the user specified encryption key +//----------------------------------------------------------------------------- +void HNET_SetKey(std::string svNetKey) +{ + g_szNetKey.clear(); + g_szNetKey = svNetKey; + + DevMsg(eDLL_T::ENGINE, "______________________________________________________________\n"); + DevMsg(eDLL_T::ENGINE, "] NET_KEY ----------------------------------------------------\n"); + DevMsg(eDLL_T::ENGINE, "] BASE64: '%s'\n", g_szNetKey.c_str()); + DevMsg(eDLL_T::ENGINE, "--------------------------------------------------------------\n"); + + NET_SetKey(g_pNetKey, g_szNetKey.c_str()); +} + +//----------------------------------------------------------------------------- +// Purpose: calculates and sets the encryption key +//----------------------------------------------------------------------------- +void HNET_GenerateKey() +{ + g_szNetKey.clear(); + net_userandomkey->m_pParent->m_iValue = 1; + + BCRYPT_ALG_HANDLE hAlgorithm; + if (BCryptOpenAlgorithmProvider(&hAlgorithm, L"RNG", 0, 0) < 0) + { + DevMsg(eDLL_T::ENGINE, "Failed to open rng algorithm\n"); + return; + } + unsigned char pBuffer[0x10u]; + if (BCryptGenRandom(hAlgorithm, pBuffer, 0x10u, 0) < 0) + { + DevMsg(eDLL_T::ENGINE, "Failed to generate random data\n"); + return; + } + + for (int i = 0; i < 0x10u; i++) + { + g_szNetKey += pBuffer[i]; + } + + g_szNetKey = Base64Encode(g_szNetKey); + + DevMsg(eDLL_T::ENGINE, "______________________________________________________________\n"); + DevMsg(eDLL_T::ENGINE, "] NET_KEY ----------------------------------------------------\n"); + DevMsg(eDLL_T::ENGINE, "] BASE64: '%s'\n", g_szNetKey.c_str()); + DevMsg(eDLL_T::ENGINE, "--------------------------------------------------------------\n"); + + NET_SetKey(g_pNetKey, g_szNetKey.c_str()); +} + +//----------------------------------------------------------------------------- +// Purpose: hook and log the client's signonstate to the console +//----------------------------------------------------------------------------- +void HNET_PrintFunc(const char* fmt, ...) +{ + static char buf[1024]; + + va_list args; + va_start(args, fmt); + + vsnprintf(buf, sizeof(buf), fmt, args); + + buf[sizeof(buf) -1] = 0; + va_end(args); + + DevMsg(eDLL_T::CLIENT, "%s\n", buf); +} + +//----------------------------------------------------------------------------- +// Purpose: disconnect the client and shutdown netchannel +//----------------------------------------------------------------------------- +void NET_DisconnectClient(CClient* pClient, int nIndex, const char* szReason, uint8_t unk1, char unk2) +{ + if (!pClient) // Client valid? + { + return; + } + if (std::strlen(szReason) == NULL) // Is reason null? + { + return; + } + if (!pClient->GetNetChan()) + { + return; + } + + NET_Shutdown(pClient->GetNetChan(), szReason, unk1, unk2); // Shutdown netchan. + pClient->GetNetChan() = nullptr; // Null netchan. + CBaseClient_Clear((std::int64_t)pClient); // Reset CClient instance for client. + g_bIsPersistenceVarSet[nIndex] = false; // Reset Persistence var. +} + +/////////////////////////////////////////////////////////////////////////////// +void CNetChan_Attach() +{ + DetourAttach((LPVOID*)&NET_PrintFunc, &HNET_PrintFunc); +} + +void CNetChan_Detach() +{ + DetourDetach((LPVOID*)&NET_PrintFunc, &HNET_PrintFunc); +} + +void CNetChan_Trace_Attach() +{ + DetourAttach((LPVOID*)&NET_ReceiveDatagram, &HNET_ReceiveDatagram); + DetourAttach((LPVOID*)&NET_SendDatagram, &HNET_SendDatagram); +} + +void CNetChan_Trace_Detach() +{ + DetourDetach((LPVOID*)&NET_ReceiveDatagram, &HNET_ReceiveDatagram); + DetourDetach((LPVOID*)&NET_SendDatagram, &HNET_SendDatagram); +} + +/////////////////////////////////////////////////////////////////////////////// +std::string g_szNetKey = "WDNWLmJYQ2ZlM0VoTid3Yg=="; +std::uintptr_t g_pNetKey = g_mGameDll.StringSearch("client:NetEncryption_NewKey").FindPatternSelf("48 8D ? ? ? ? ? 48 3B", ADDRESS::Direction::UP, 150).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr(); diff --git a/r5dev/engine/net_chan.h b/r5dev/engine/net_chan.h new file mode 100644 index 00000000..c3523431 --- /dev/null +++ b/r5dev/engine/net_chan.h @@ -0,0 +1,95 @@ +#pragma once +#include "core/stdafx.h" +#include "tier0/basetypes.h" +#include "common/protocol.h" +#include "client/client.h" + +typedef struct netpacket_s netpacket_t; +typedef struct __declspec(align(8)) netpacket_s +{ + DWORD family_maybe; + sockaddr_in sin; + WORD sin_port; + char gap16; + char byte17; + DWORD source; + double received; + std::uint8_t* data; + std::uint64_t label; + BYTE byte38; + std::uint64_t qword40; + std::uint64_t qword48; + BYTE gap50[8]; + std::uint64_t qword58; + std::uint64_t qword60; + std::uint64_t qword68; + int less_than_12; + DWORD wiresize; + BYTE gap78[8]; + struct netpacket_s *pNext; +} netpacket_t; + +namespace +{ + /* ==== CNETCHAN ======================================================================================================================================================== */ +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) || defined (GAMEDLL_S2) + ADDRESS p_NET_Init = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x08\x48\x89\x6C\x24\x10\x48\x89\x74\x24\x18\x48\x89\x7C\x24\x20\x41\x54\x41\x56\x41\x57\x48\x81\xEC\xC0\x01\x00", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + void*(*NET_Init)(char a1) = (void* (*)(char))p_NET_Init.GetPtr(); /*48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 48 89 7C 24 20 41 54 41 56 41 57 48 81 EC C0 01 00*/ + + ADDRESS p_NET_Shutdown = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x6C\x24\x18\x56\x57\x41\x56\x48\x83\xEC\x30\x83\xB9\xD8", "xxxxxxxxxxxxxxxx"); + void (*NET_Shutdown)(void* thisptr, const char* a0, std::uint8_t a1, char a2) = (void (*)(void*, const char*, std::uint8_t, char))p_NET_Shutdown.GetPtr(); /*48 89 6C 24 18 56 57 41 56 48 83 EC 30 83 B9 D8*/ +#elif defined (GAMEDLL_S3) + ADDRESS p_NET_Init = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x08\x48\x89\x6C\x24\x10\x48\x89\x74\x24\x18\x48\x89\x7C\x24\x20\x41\x54\x41\x56\x41\x57\x48\x81\xEC\xF0\x01\x00", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + void* (*NET_Init)(char a1) = (void* (*)(char))p_NET_Init.GetPtr(); /*48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 48 89 7C 24 20 41 54 41 56 41 57 48 81 EC F0 01 00*/ + + ADDRESS p_NET_Shutdown = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x6C\x24\x18\x56\x57\x41\x56\x48\x83\xEC\x30\x83\xB9\xD0", "xxxxxxxxxxxxxxxx"); + void (*NET_Shutdown)(void* thisptr, const char* szReason, std::uint8_t a1, char a2) = (void (*)(void*, const char*, std::uint8_t, char))p_NET_Shutdown.GetPtr(); /*48 89 6C 24 18 56 57 41 56 48 83 EC 30 83 B9 D0*/ +#endif + ADDRESS p_NET_SetKey = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x08\x48\x89\x6C\x24\x10\x48\x89\x74\x24\x18\x57\x48\x83\xEC\x20\x48\x8B\xF9\x41\xB8", "xxxxxxxxxxxxxxxxxxxxxxxxx"); + void (*NET_SetKey)(std::uintptr_t pKey, const char* szHash) = (void (*)(std::uintptr_t, const char*))p_NET_SetKey.GetPtr(); /*48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 48 83 EC 20 48 8B F9 41 B8*/ + + ADDRESS p_NET_ReceiveDatagram = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x74\x24\x18\x48\x89\x7C\x24\x20\x55\x41\x54\x41\x55\x41\x56\x41\x57\x48\x8D\xAC\x24\x50\xEB", "xxxxxxxxxxxxxxxxxxxxxxxxx"); + bool (*NET_ReceiveDatagram)(int iSocket, netpacket_s* pInpacket, bool bRaw) = (bool (*)(int, netpacket_s*, bool))p_NET_ReceiveDatagram.GetPtr(); /*E8 ?? ?? ?? ?? 84 C0 75 35 48 8B D3*/ + + ADDRESS p_NET_SendDatagram = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x08\x48\x89\x6C\x24\x10\x48\x89\x74\x24\x18\x57\x41\x56\x41\x57\x48\x81\xEC\x00\x05\x00\x00", "xxxxxxxxxxxxxxxxxxxxxxx?xxx"); + void*(*NET_SendDatagram)(SOCKET s, const char* szPayload, int iLenght, int nFlags) = (void*(*)(SOCKET, const char*, int, int))p_NET_SendDatagram.GetPtr(); /*48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 41 56 41 57 48 81 EC ?? 05 00 00*/ + + ADDRESS p_NET_PrintFunc = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x54\x24\x10\x4C\x89\x44\x24\x18\x4C\x89\x4C\x24\x20\xC3\x48", "xxxxxxxxxxxxxxxxx"); + void (*NET_PrintFunc)(const char* fmt) = (void(*)(const char*))p_NET_PrintFunc.GetPtr(); /*48 89 54 24 10 4C 89 44 24 18 4C 89 4C 24 20 C3 48*/ +} + +/////////////////////////////////////////////////////////////////////////////// +bool HNET_ReceiveDatagram(int iSocket, netpacket_s* pInpacket, bool bRaw); +void* HNET_SendDatagram(SOCKET s, const char* szPayload, int iLenght, int nFlags); +void HNET_SetKey(std::string svNetKey); +void HNET_GenerateKey(); +void HNET_PrintFunc(const char* fmt, ...); +void NET_DisconnectClient(CClient* pClient, int nIndex, const char* szReason, std::uint8_t unk1, char unk2); + +void CNetChan_Attach(); +void CNetChan_Detach(); +void CNetChan_Trace_Attach(); +void CNetChan_Trace_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +extern std::string g_szNetKey; +extern std::uintptr_t g_pNetKey; + +/////////////////////////////////////////////////////////////////////////////// +class HNetChan : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: NET_Init : 0x" << std::hex << std::uppercase << p_NET_Init.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: NET_Shutdown : 0x" << std::hex << std::uppercase << p_NET_Shutdown.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: NET_SetKey : 0x" << std::hex << std::uppercase << p_NET_SetKey.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: NET_ReceiveDatagram : 0x" << std::hex << std::uppercase << p_NET_ReceiveDatagram.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: NET_SendDatagram : 0x" << std::hex << std::uppercase << p_NET_SendDatagram.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: NET_PrintFunc : 0x" << std::hex << std::uppercase << p_NET_PrintFunc.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: g_pNetKey : 0x" << std::hex << std::uppercase << g_pNetKey << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HNetChan); diff --git a/r5dev/engine/sys_dll.cpp b/r5dev/engine/sys_dll.cpp new file mode 100644 index 00000000..d22d7f43 --- /dev/null +++ b/r5dev/engine/sys_dll.cpp @@ -0,0 +1,26 @@ +#include "core/stdafx.h" +#include "engine/sys_dll.h" + +//----------------------------------------------------------------------------- +// Sys_Error_Internal +// +//----------------------------------------------------------------------------- +int HSys_Error_Internal(char* fmt, va_list args) +{ + printf("\n_____________________________________________________________\n"); + printf("] ENGINE ERROR ################################################\n"); + vprintf(fmt, args); + + /////////////////////////////////////////////////////////////////////////// + return Sys_Error_Internal(fmt, args); +} + +void SysDll_Attach() +{ + DetourAttach((LPVOID*)&Sys_Error_Internal, &HSys_Error_Internal); +} + +void SysDll_Detach() +{ + DetourDetach((LPVOID*)&Sys_Error_Internal, &HSys_Error_Internal); +} diff --git a/r5dev/engine/sys_dll.h b/r5dev/engine/sys_dll.h new file mode 100644 index 00000000..eaa4df4d --- /dev/null +++ b/r5dev/engine/sys_dll.h @@ -0,0 +1,27 @@ +#pragma once + +namespace +{ + /* ==== UTILITY ========================================================================================================================================================= */ + ADDRESS p_Sys_Error_Internal = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x08\x48\x89\x74\x24\x10\x57\x48\x81\xEC\x30\x08\x00\x00\x48\x8B\xDA\x48\x8B\xF9\xE8\x00\x00\x00\xFF\x33\xF6\x48", "xxxxxxxxxxxxxxxxxxxxxxxxx???xxxx"); + int (*Sys_Error_Internal)(char* fmt, va_list args) = (int (*)(char*, va_list))p_Sys_Error_Internal.GetPtr(); /*48 89 5C 24 08 48 89 74 24 10 57 48 81 EC 30 08 00 00 48 8B DA 48 8B F9 E8 ?? ?? ?? FF 33 F6 48*/ +} + +/////////////////////////////////////////////////////////////////////////////// +int HSys_Error_Internal(char* fmt, va_list args); + +void SysDll_Attach(); +void SysDll_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +class HSys_Dll : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: Sys_Error_Internal : 0x" << std::hex << std::uppercase << p_Sys_Error_Internal.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HSys_Dll); \ No newline at end of file diff --git a/r5dev/engine/sys_dll2.cpp b/r5dev/engine/sys_dll2.cpp new file mode 100644 index 00000000..03f87c71 --- /dev/null +++ b/r5dev/engine/sys_dll2.cpp @@ -0,0 +1,3 @@ +#include "core/stdafx.h" +#include "engine/sys_dll.h" +#include "engine/sys_dll2.h" diff --git a/r5dev/engine/sys_dll2.h b/r5dev/engine/sys_dll2.h new file mode 100644 index 00000000..eedac0c1 --- /dev/null +++ b/r5dev/engine/sys_dll2.h @@ -0,0 +1,40 @@ +#pragma once +#include "tier0/basetypes.h" +#include "tier0/interface.h" + +class CEngineAPI +{ + // TODO [ AMOS ]: +}; + +namespace +{ + ADDRESS p_CEngineAPI_Connect = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x83\xEC\x28\x48\x8B\x05\x00\x00\x00\x00\x48\x8D\x0D\x00\x00\x00\x00\x48\x85\xC0\x48\x89\x15", "xxxxxxx????xxx????xxxxxx"); + bool (*CEngineAPI_Connect)(CEngineAPI* thisptr, CreateInterfaceFn factory) = (bool (*)(CEngineAPI*, CreateInterfaceFn))p_CEngineAPI_Connect.GetPtr(); /*48 83 EC 28 48 8B 05 ? ? ? ? 48 8D 0D ? ? ? ? 48 85 C0 48 89 15 ? ? ? ?*/ +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) + ADDRESS p_PakFile_Init = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x00\x48\x89\x6C\x24\x00\x44\x88\x44\x24\x00\x56\x57\x41\x54\x41\x56\x41\x57\x48\x83\xEC\x20", "xxxx?xxxx?xxxx?xxxxxxxxxxxx"); + int (*PakFile_Init)(char* buffer, char* source, char vpk_file) = (int (*)(char*, char*, char))p_PakFile_Init.GetPtr(); /*48 89 5C 24 ?? 48 89 6C 24 ?? 44 88 44 24 ?? 56 57 41 54 41 56 41 57 48 83 EC 20*/ + + ADDRESS g_pMapVPKCache = p_PakFile_Init.FindPatternSelf("4C 8D 35 ?? ?? ?? ?? 44", ADDRESS::Direction::DOWN, 250).OffsetSelf(0x3).ResolveRelativeAddressSelf().GetPtr(); +#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) + ADDRESS p_PakFile_Init = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x44\x88\x44\x24\x00\x53\x55\x56\x57", "xxxx?xxxx"); + void (*PakFile_Init)(char* buffer, char* source, char vpk_file) = (void (*)(char*, char*, char))p_PakFile_Init.GetPtr(); /*44 88 44 24 ?? 53 55 56 57*/ + + ADDRESS g_pMapVPKCache = p_PakFile_Init.FindPatternSelf("48 8D 1D ?? ?? ?? ?? 4C", ADDRESS::Direction::DOWN, 250).OffsetSelf(0x3).ResolveRelativeAddressSelf().GetPtr(); +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +class HSys_Dll2 : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: CEngineAPI_Connect : 0x" << std::hex << std::uppercase << p_CEngineAPI_Connect.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: PakFile_Init : 0x" << std::hex << std::uppercase << p_PakFile_Init.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: g_pMapVPKCache : 0x" << std::hex << std::uppercase << g_pMapVPKCache.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HSys_Dll2); \ No newline at end of file diff --git a/r5dev/engine/sys_utils.cpp b/r5dev/engine/sys_utils.cpp new file mode 100644 index 00000000..85c136f3 --- /dev/null +++ b/r5dev/engine/sys_utils.cpp @@ -0,0 +1,123 @@ +#include "core/stdafx.h" +#include "core/logdef.h" +#include "engine/sys_utils.h" +#include "vgui/CEngineVGui.h" +#include "gameui/IConsole.h" + +//----------------------------------------------------------------------------- +// Sys_Error +// +//----------------------------------------------------------------------------- +void HSys_Error(char* fmt, ...) +{ + static char buf[1024]; + + va_list args; + va_start(args, fmt); + + vsnprintf(buf, sizeof(buf), fmt, args); + + buf[sizeof(buf) -1] = 0; + va_end(args); + + DevMsg(eDLL_T::ENGINE, "%s\n", buf); + Sys_Error(buf); +} + +//----------------------------------------------------------------------------- +// Sys_Print +// +//----------------------------------------------------------------------------- +void DevMsg(eDLL_T idx, const char* fmt, ...) +{ + int vmIdx = (int)idx; + static bool initialized = false; + static char buf[1024]; + + static std::string vmType[7] = { "Native(S):", "Native(C):", "Native(U):", "Native(E):", "Native(F):", "Native(R):", "Native(M):" }; + + static auto iconsole = spdlog::stdout_logger_mt("sys_print_iconsole"); // in-game console. + static auto wconsole = spdlog::stdout_logger_mt("sys_print_wconsole"); // windows console. + static auto sqlogger = spdlog::basic_logger_mt("sys_print_logger", "platform\\logs\\sys_print.log"); // file logger. + + std::string vmStr = vmType[vmIdx].c_str(); + + g_spd_sys_w_oss.str(""); + g_spd_sys_w_oss.clear(); + + if (!initialized) + { + iconsole = std::make_shared("sys_print_ostream", g_spd_sys_p_ostream_sink); + iconsole->set_pattern("[%S.%e] %v"); + iconsole->set_level(spdlog::level::debug); + wconsole->set_pattern("[%S.%e] %v"); + wconsole->set_level(spdlog::level::debug); + sqlogger->set_pattern("[%S.%e] %v"); + sqlogger->set_level(spdlog::level::debug); + initialized = true; + } + + va_list args; + va_start(args, fmt); + + vsnprintf(buf, sizeof(buf), fmt, args); + + buf[sizeof(buf) - 1] = 0; + va_end(args); + + vmStr.append(buf); + + iconsole->debug(vmStr); + wconsole->debug(vmStr); + sqlogger->debug(vmStr); + +#ifndef DEDICATED + std::string s = g_spd_sys_w_oss.str(); + const char* c = s.c_str(); + + g_pLogSystem.AddLog((LogType_t)eDLL_T::ENGINE, s); + Items.push_back(Strdup((const char*)c)); +#endif // !DEDICATED +} + +//----------------------------------------------------------------------------- +// Sys_LoadAssetHelper +// +//----------------------------------------------------------------------------- +void* HSys_LoadAssetHelper(const CHAR* lpFileName, std::int64_t a2, LARGE_INTEGER* a3) +{ + std::string mod_file; + std::string base_file = lpFileName; + const std::string mod_dir = "paks\\Win32\\"; + const std::string base_dir = "paks\\Win64\\"; + + if (strstr(lpFileName, base_dir.c_str())) + { + base_file.erase(0, 11); // Erase 'base_dir'. + mod_file = mod_dir + base_file; // Prepend 'mod_dir'. + + if (FileExists(mod_file.c_str())) + { + // Load decompressed pak files from 'mod_dir'. + DevMsg(eDLL_T::RTECH, "Loading pak: '%s'\n", mod_file.c_str()); + return Sys_LoadAssetHelper(mod_file.c_str(), a2, a3); + } + } + if (strstr(lpFileName, base_dir.c_str())) + { + DevMsg(eDLL_T::RTECH, "Loading pak: '%s'\n", lpFileName); + } + return Sys_LoadAssetHelper(lpFileName, a2, a3); +} + +void SysUtils_Attach() +{ + DetourAttach((LPVOID*)&Sys_Error, &HSys_Error); + DetourAttach((LPVOID*)&Sys_LoadAssetHelper, &HSys_LoadAssetHelper); +} + +void SysUtils_Detach() +{ + DetourDetach((LPVOID*)&Sys_Error, &HSys_Error); + DetourDetach((LPVOID*)&Sys_LoadAssetHelper, &HSys_LoadAssetHelper); +} diff --git a/r5dev/engine/sys_utils.h b/r5dev/engine/sys_utils.h new file mode 100644 index 00000000..5bc41fb2 --- /dev/null +++ b/r5dev/engine/sys_utils.h @@ -0,0 +1,53 @@ +#pragma once +#include "tier0/basetypes.h" + +namespace +{ + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ADDRESS p_Sys_Error = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x4C\x24\x08\x48\x89\x54\x24\x10\x4C\x89\x44\x24\x18\x4C\x89\x4C\x24\x20\x53\x55\x41\x54\x41\x56\xB8\x58\x10\x00\x00\xE8", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + void (*Sys_Error)(char* fmt, ...) = (void (*)(char* fmt, ...))p_Sys_Error.GetPtr(); /*48 89 4C 24 08 48 89 54 24 10 4C 89 44 24 18 4C 89 4C 24 20 53 55 41 54 41 56 B8 58 10 00 00 E8*/ + + ADDRESS p_Sys_LoadAssetHelper = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x74\x24\x10\x48\x89\x7C\x24\x18\x41\x56\x48\x83\xEC\x40\x33", "xxxxxxxxxxxxxxxxx"); + void*(*Sys_LoadAssetHelper)(const CHAR* lpFileName, std::int64_t a2, LARGE_INTEGER* a3) = (void*(*)(const CHAR*, std::int64_t, LARGE_INTEGER*))p_Sys_LoadAssetHelper.GetPtr();/*48 89 74 24 10 48 89 7C 24 18 41 56 48 83 EC 40 33*/ +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) || defined (GAMEDLL_S2) + ADDRESS p_MemAlloc_Wrapper = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x53\x48\x83\xEC\x20\x48\x8B\x05\x00\x00\x00\x00\x48\x8B\xD9\x48\x85\xC0\x75\x0C\xE8\x16", "xxxxxxxxx????xxxxxxxxxx"); + void* (*MemAlloc_Wrapper)(std::int64_t size) = (void* (*)(std::int64_t))p_MemAlloc_Wrapper.GetPtr(); /*40 53 48 83 EC 20 48 8B 05 ?? ?? ?? ?? 48 8B D9 48 85 C0 75 0C E8 16*/ +#elif defined (GAMEDLL_S3) + ADDRESS p_MemAlloc_Wrapper = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x53\x48\x83\xEC\x20\x48\x8B\x05\x6B\x83\x25\x0D\x48\x8B\xD9", "xxxxxxxxxxxxxxxx"); + void* (*MemAlloc_Wrapper)(std::int64_t size) = (void* (*)(std::int64_t))p_MemAlloc_Wrapper.GetPtr(); /*40 53 48 83 EC 20 48 8B 05 6B 83 25 0D 48 8B D9*/ +#endif + /* ==== ------- ========================================================================================================================================================= */ +} + +enum class eDLL_T : int +{ + SERVER = 0, // Game DLL + CLIENT = 1, // Game DLL + UI = 2, // Game DLL + ENGINE = 3, // Wrapper + FS = 4, // File System + RTECH = 5, // RTech API + MS = 6 // Material System +}; + +/////////////////////////////////////////////////////////////////////////////// +void HSys_Error(char* fmt, ...); +void DevMsg(eDLL_T idx, const char* fmt, ...); + +void SysUtils_Attach(); +void SysUtils_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +class HSys_Utils : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: Sys_Error : 0x" << std::hex << std::uppercase << p_Sys_Error.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: Sys_LoadAssetHelper : 0x" << std::hex << std::uppercase << p_Sys_LoadAssetHelper.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: MemAlloc_Wrapper : 0x" << std::hex << std::uppercase << p_MemAlloc_Wrapper.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HSys_Utils); diff --git a/r5dev/gameui/IBrowser.cpp b/r5dev/gameui/IBrowser.cpp new file mode 100644 index 00000000..2cb83b5a --- /dev/null +++ b/r5dev/gameui/IBrowser.cpp @@ -0,0 +1,712 @@ +#include "core/stdafx.h" +#include "core/init.h" +#include "core/resource.h" +#include "tier0/IConVar.h" +#include "tier0/cvar.h" +#include "tier0/completion.h" +#include "windows/id3dx.h" +#include "windows/console.h" +#include "engine/net_chan.h" +#include "engine/sys_utils.h" +#include "engine/host_state.h" +#include "server/server.h" +#include "client/IVEngineClient.h" +#include "networksystem/serverlisting.h" +#include "networksystem/r5net.h" +#include "vpc/keyvalues.h" +#include "squirrel/sqinit.h" +#include "gameui/IBrowser.h" + +/****************************************************************************** +------------------------------------------------------------------------------- +File : IBrowser.cpp +Date : 09:06:2021 +Author : Sal +Purpose: Implements the in-game server browser frontend +------------------------------------------------------------------------------- +History: +- 09:06:2021 21:07 : Created by Sal +- 25:07:2021 14:26 : Implement private servers connect dialog and password field + +******************************************************************************/ + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +IBrowser::IBrowser() +{ + memset(m_chServerConnStringBuffer, 0, sizeof(m_chServerConnStringBuffer)); + + std::string path = "stbsp"; + for (const auto& entry : std::filesystem::directory_iterator(path)) + { + std::string filename = entry.path().string(); + int slashPos = filename.rfind("\\", std::string::npos); + filename = filename.substr(static_cast, std::allocator>::size_type>(slashPos) + 1, std::string::npos); + filename = filename.substr(0, filename.size() - 6); + + auto it = mapArray.find(filename); // Find MapName in mapArray. + if (it != mapArray.end()) + { + m_vszMapsList.push_back(it->second); + } + else + { + m_vszMapsList.push_back(filename); + } + } + + m_szMatchmakingHostName = r5net_matchmaking_hostname->m_pzsCurrentValue; + static std::thread hostingServerRequestThread([this]() + { + while (true) + { + UpdateHostingStatus(); + std::this_thread::sleep_for(std::chrono::milliseconds(5000)); + } + }); + + hostingServerRequestThread.detach(); + + /* Obtain handle to module */ + static HGLOBAL rcData = NULL; + HMODULE handle; + GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR)"unnamed", &handle); + HRSRC rc = FindResource(handle, MAKEINTRESOURCE(IDB_PNG1), MAKEINTRESOURCE(PNG)); + /* Obtain assets from 'rsrc' */ + if (rc != NULL) + { rcData = LoadResource(handle, rc); } + else { assert(rc == NULL); } + if (rcData != NULL) { m_vucLockedIconBlob = (std::vector*)LockResource(rcData); } + else { assert(rcData == NULL); } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +IBrowser::~IBrowser() +{ + //delete r5net; +} + +//----------------------------------------------------------------------------- +// Purpose: updates the hoster's status +//----------------------------------------------------------------------------- +void IBrowser::UpdateHostingStatus() +{ + if (!g_pHostState || !g_pCvar) + { + return; + } + + eHostingStatus = g_pHostState->m_bActiveGame ? EHostStatus::HOSTING : EHostStatus::NOT_HOSTING; // Are we hosting a server? + switch (eHostingStatus) + { + case EHostStatus::NOT_HOSTING: + { + m_szHostRequestMessage.clear(); + m_iv4HostRequestMessageColor = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); + break; + } + case EHostStatus::HOSTING: + { + if (eServerVisibility == EServerVisibility::OFFLINE) + { + break; + } + + if (*g_nRemoteFunctionCallsChecksum == NULL) // Check if script checksum is valid yet. + { + break; + } + + switch (eServerVisibility) + { + + case EServerVisibility::HIDDEN: + m_Server.bHidden = true; + break; + case EServerVisibility::PUBLIC: + m_Server.bHidden = false; + break; + default: + break; + } + + SendHostingPostRequest(); + break; + } + default: + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: refreshes the server browser list with available servers +//----------------------------------------------------------------------------- +void IBrowser::RefreshServerList() +{ + static bool bThreadLocked = false; + + m_vServerList.clear(); + m_szServerListMessage.clear(); + + if (!bThreadLocked) + { + std::thread t([this]() + { + DevMsg(eDLL_T::CLIENT, "Refreshing server list with string '%s'\n", r5net_matchmaking_hostname->m_pzsCurrentValue); + bThreadLocked = true; + m_vServerList = g_pR5net->GetServersList(m_szServerListMessage); + bThreadLocked = false; + }); + + t.detach(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: sends the hosting POST request to the comp server +//----------------------------------------------------------------------------- +void IBrowser::SendHostingPostRequest() +{ + m_szHostToken = std::string(); + DevMsg(eDLL_T::CLIENT, "Sending PostServerHost request\n"); + bool result = g_pR5net->PostServerHost(m_szHostRequestMessage, m_szHostToken, + ServerListing{ + m_Server.svServerName, + std::string(g_pHostState->m_levelName), + "", + g_pCvar->FindVar("hostport")->m_pzsCurrentValue, + g_pCvar->FindVar("mp_gamemode")->m_pzsCurrentValue, + m_Server.bHidden, + std::to_string(*g_nRemoteFunctionCallsChecksum), + + std::string(), + g_szNetKey.c_str() + } + ); + + if (result) + { + m_iv4HostRequestMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f); + std::stringstream msg; + msg << "Broadcasting! "; + if (!m_szHostToken.empty()) + { + msg << "Share the following token for clients to connect: "; + } + m_szHostRequestMessage = msg.str().c_str(); + } + else + { + m_iv4HostRequestMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + } +} + +//----------------------------------------------------------------------------- +// Purpose: draws the compmenu +//----------------------------------------------------------------------------- +void IBrowser::CompMenu() +{ + ImGui::BeginTabBar("CompMenu"); + if (ImGui::TabItemButton("Server Browser")) + { + SetSection(ESection::SERVER_BROWSER); + } + if (ImGui::TabItemButton("Host Server")) + { + SetSection(ESection::HOST_SERVER); + } + if (ImGui::TabItemButton("Settings")) + { + SetSection(ESection::SETTINGS); + } + ImGui::EndTabBar(); +} + +//----------------------------------------------------------------------------- +// Purpose: draws the server browser section +//----------------------------------------------------------------------------- +void IBrowser::ServerBrowserSection() +{ + ImGui::BeginGroup(); + m_imServerBrowserFilter.Draw(); + ImGui::SameLine(); + if (ImGui::Button("Refresh List")) + { + RefreshServerList(); + } + ImGui::EndGroup(); + ImGui::TextColored(ImVec4(1.00f, 0.00f, 0.00f, 1.00f), m_szServerListMessage.c_str()); + ImGui::Separator(); + + const float FooterHeight = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); + ImGui::BeginChild("ServerListChild", { 0, -FooterHeight }, true, ImGuiWindowFlags_AlwaysVerticalScrollbar); + if (ImGui::BeginTable("##ServerBrowser_ServerList", 5, ImGuiTableFlags_Resizable)) + { + ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch, 20); + ImGui::TableSetupColumn("Map", ImGuiTableColumnFlags_WidthStretch, 25); + ImGui::TableSetupColumn("Port", ImGuiTableColumnFlags_WidthStretch, 5); + ImGui::TableSetupColumn("Playlist", ImGuiTableColumnFlags_WidthStretch, 5); + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 5); + ImGui::TableHeadersRow(); + + for (ServerListing& server : m_vServerList) + { + const char* name = server.svServerName.c_str(); + const char* map = server.svMapName.c_str(); + const char* port = server.svPort.c_str(); + const char* playlist = server.svPlaylist.c_str(); + + if (m_imServerBrowserFilter.PassFilter(name) + || m_imServerBrowserFilter.PassFilter(map) + || m_imServerBrowserFilter.PassFilter(port)) + { + ImGui::TableNextColumn(); + ImGui::Text(name); + + ImGui::TableNextColumn(); + ImGui::Text(map); + + ImGui::TableNextColumn(); + ImGui::Text(port); + + ImGui::TableNextColumn(); + ImGui::Text(playlist); + + ImGui::TableNextColumn(); + std::string selectButtonText = "Connect##"; + selectButtonText += (server.svServerName + server.svIpAddress + server.svMapName); + + if (ImGui::Button(selectButtonText.c_str())) + { + ConnectToServer(server.svIpAddress, server.svPort, server.svEncryptionKey); + } + } + + } + ImGui::EndTable(); + } + ImGui::EndChild(); + + ImGui::Separator(); + ImGui::PushItemWidth(ImGui::GetWindowContentRegionWidth() / 4); + { + ImGui::InputTextWithHint("##ServerBrowser_ServerConnString", "Enter IP address or \"localhost\"", m_chServerConnStringBuffer, IM_ARRAYSIZE(m_chServerConnStringBuffer)); + + ImGui::SameLine(); + ImGui::InputTextWithHint("##ServerBrowser_ServerEncKey", "Enter encryption key", m_chServerEncKeyBuffer, IM_ARRAYSIZE(m_chServerEncKeyBuffer)); + + ImGui::SameLine(); + if (ImGui::Button("Connect##ServerBrowser_ConnectByIp", ImVec2(ImGui::GetWindowContentRegionWidth() / 4.2, 18.5))) + { + ConnectToServer(m_chServerConnStringBuffer, m_chServerEncKeyBuffer); + } + + ImGui::SameLine(); + if (ImGui::Button("Private Servers##ServerBrowser_HiddenServersButton", ImVec2(ImGui::GetWindowContentRegionWidth() / 4.2, 18.5))) + { + ImGui::OpenPopup("Connect to Private Server##HiddenServersConnectModal"); + } + HiddenServersModal(); + } + ImGui::PopItemWidth(); +} + +//----------------------------------------------------------------------------- +// Purpose: draws the hidden private server modal +//----------------------------------------------------------------------------- +void IBrowser::HiddenServersModal() +{ + bool modalOpen = true; + if (ImGui::BeginPopupModal("Connect to Private Server##HiddenServersConnectModal", &modalOpen)) + { + ImGui::SetWindowSize(ImVec2(400.f, 200.f), ImGuiCond_Always); + + if (!m_idLockedIcon) + { + bool ret = LoadTextureBuffer((unsigned char*)m_vucLockedIconBlob, 0x1000 /*TODO [ AMOS ]: Calculate size dynamically*/, &m_idLockedIcon, &m_nLockedIconWidth, &m_nLockedIconHeight); + IM_ASSERT(ret); + } + + ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.00f, 0.00f, 0.00f, 0.00f)); // Override the style color for child bg. + + ImGui::BeginChild("##HiddenServersConnectModal_IconParent", ImVec2(m_nLockedIconWidth, m_nLockedIconHeight)); + ImGui::Image(m_idLockedIcon, ImVec2(m_nLockedIconWidth, m_nLockedIconHeight)); // Display texture. + ImGui::EndChild(); + + ImGui::PopStyleColor(); // Pop the override for the child bg. + + ImGui::SameLine(); + ImGui::Text("Enter the token to connect"); + + ImGui::PushItemWidth(ImGui::GetWindowContentRegionWidth()); // Override item width. + ImGui::InputTextWithHint("##HiddenServersConnectModal_TokenInput", "Token", &m_szHiddenServerToken); + ImGui::PopItemWidth(); + + ImGui::Dummy(ImVec2(ImGui::GetWindowContentRegionWidth(), 19.f)); // Place a dummy, basically making space inserting a blank element. + + ImGui::TextColored(m_ivHiddenServerMessageColor, m_szHiddenServerRequestMessage.c_str()); + ImGui::Separator(); + + if (ImGui::Button("Connect##HiddenServersConnectModal_ConnectButton", ImVec2(ImGui::GetWindowContentRegionWidth() / 2, 24))) + { + m_szHiddenServerRequestMessage.clear(); + ServerListing server; + bool result = g_pR5net->GetServerByToken(server, m_szHiddenServerRequestMessage, m_szHiddenServerToken); // Send token connect request. + if (!server.svServerName.empty()) + { + ConnectToServer(server.svIpAddress, server.svPort, server.svEncryptionKey); // Connect to the server + m_szHiddenServerRequestMessage = "Found Server: " + server.svServerName; + m_ivHiddenServerMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f); + ImGui::CloseCurrentPopup(); + } + else + { + m_szHiddenServerRequestMessage = "Error: " + m_szHiddenServerRequestMessage; + m_ivHiddenServerMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + } + } + + ImGui::SameLine(); + if (ImGui::Button("Close##HiddenServersConnectModal_CloseButton", ImVec2(ImGui::GetWindowContentRegionWidth() / 2, 24))) + { + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: draws the host section +//----------------------------------------------------------------------------- +void IBrowser::HostServerSection() +{ + static std::string szServerNameErr = ""; + + ImGui::InputTextWithHint("##ServerHost_ServerName", "Server Name (Required)", &m_Server.svServerName); + ImGui::Spacing(); + + if (ImGui::BeginCombo("Playlist##ServerHost_PlaylistBox", m_Server.svPlaylist.c_str())) + { + for (auto& item : g_szAllPlaylists) + { + if (ImGui::Selectable(item.c_str(), item == m_Server.svPlaylist)) + { + m_Server.svPlaylist = item; + } + } + ImGui::EndCombo(); + } + + if (ImGui::BeginCombo("Map##ServerHost_MapListBox", m_Server.svMapName.c_str())) + { + for (auto& item : m_vszMapsList) + { + if (ImGui::Selectable(item.c_str(), item == m_Server.svMapName)) + { + m_Server.svMapName = item; + for (auto it = mapArray.begin(); it != mapArray.end(); ++it) + { + if (it->second.compare(m_Server.svMapName) == NULL) + { + m_Server.svMapName = it->first; + } + } + } + } + ImGui::EndCombo(); + } + + ImGui::Checkbox("Load Global Ban List##ServerHost_CheckCompBanDBCheckbox", &g_bCheckCompBanDB); + ImGui::Spacing(); + + ImGui::SameLine(); + ImGui::Text("Server Visiblity"); + + if (ImGui::SameLine(); ImGui::RadioButton("Offline##ServerHost_ServerChoice1", eServerVisibility == EServerVisibility::OFFLINE)) + { + eServerVisibility = EServerVisibility::OFFLINE; + } + if (ImGui::SameLine(); ImGui::RadioButton("Hidden##ServerHost_ServerChoice2", eServerVisibility == EServerVisibility::HIDDEN)) + { + eServerVisibility = EServerVisibility::HIDDEN; + } + if (ImGui::SameLine(); ImGui::RadioButton("Public##ServerHost_ServerChoice2", eServerVisibility == EServerVisibility::PUBLIC)) + { + eServerVisibility = EServerVisibility::PUBLIC; + } + + ImGui::Spacing(); + ImGui::Separator(); + + if (!g_pHostState->m_bActiveGame) + { + if (ImGui::Button("Start Server##ServerHost_StartServerButton", ImVec2(ImGui::GetWindowSize().x, 32))) + { + szServerNameErr.clear(); + if (!m_Server.svServerName.empty() && !m_Server.svPlaylist.empty() && !m_Server.svMapName.empty()) + { + DevMsg(eDLL_T::ENGINE, "Starting Server with name '%s', map '%s' and playlist '%s'\n", m_Server.svServerName.c_str(), m_Server.svMapName.c_str(), m_Server.svPlaylist.c_str()); + szServerNameErr = std::string(); + UpdateHostingStatus(); + + /* + * Playlist gets parsed in two instances, first in LoadPlaylist all the neccessary values. + * Then when you would normally call launchplaylist which calls StartPlaylist it would cmd call mp_gamemode which parses the gamemode specific part of the playlist.. + */ + KeyValues_LoadPlaylist(m_Server.svPlaylist.c_str()); + std::stringstream cgmd; + cgmd << "mp_gamemode " << m_Server.svPlaylist; + ProcessCommand(cgmd.str().c_str()); + + // This is to avoid a race condition. + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + std::stringstream cmd; + cmd << "map " << m_Server.svMapName; + ProcessCommand(cmd.str().c_str()); + } + else + { + if (m_Server.svServerName.empty()) + { + szServerNameErr = "No Server Name assigned."; + } + else if (m_Server.svPlaylist.empty()) + { + szServerNameErr = "No Playlist assigned."; + } + else if (m_Server.svMapName.empty()) + { + szServerNameErr = "'levelname' was empty."; + } + } + } + } + + if (ImGui::Button("Force Start##ServerHost_ForceStart", ImVec2(ImGui::GetWindowSize().x, 32))) + { + szServerNameErr.clear(); + if (!m_Server.svPlaylist.empty() && !m_Server.svMapName.empty()) + { + DevMsg(eDLL_T::ENGINE, "Starting Server with map '%s' and playlist '%s'\n", m_Server.svMapName.c_str(), m_Server.svPlaylist.c_str()); + szServerNameErr = std::string(); + UpdateHostingStatus(); + + /* + * Playlist gets parsed in two instances, first in LoadPlaylist all the neccessary values. + * Then when you would normally call launchplaylist which calls StartPlaylist it would cmd call mp_gamemode which parses the gamemode specific part of the playlist.. + */ + KeyValues_LoadPlaylist(m_Server.svPlaylist.c_str()); + std::stringstream cgmd; + cgmd << "mp_gamemode " << m_Server.svPlaylist; + ProcessCommand(cgmd.str().c_str()); + + // This is to avoid a race condition. + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + std::stringstream cmd; + cmd << "map " << m_Server.svMapName; + ProcessCommand(cmd.str().c_str()); + } + else + { + if (m_Server.svPlaylist.empty()) + { + szServerNameErr = "No Playlist assigned."; + } + else if (m_Server.svMapName.empty()) + { + szServerNameErr = "'levelname' was empty."; + } + } + } + + ImGui::TextColored(ImVec4(1.00f, 0.00f, 0.00f, 1.00f), szServerNameErr.c_str()); + ImGui::TextColored(m_iv4HostRequestMessageColor, m_szHostRequestMessage.c_str()); + if (!m_szHostToken.empty()) + { + ImGui::InputText("##ServerHost_HostToken", &m_szHostToken, ImGuiInputTextFlags_ReadOnly); + } + + if (g_pHostState->m_bActiveGame) + { + if (ImGui::Button("Reload Scripts##ServerHost_ReloadServerButton", ImVec2(ImGui::GetWindowSize().x, 32))) + { + DevMsg(eDLL_T::ENGINE, "Recompiling scripts\n"); + ProcessCommand("reparse_weapons"); + ProcessCommand("reload"); + } + + if (ImGui::Button("Change Level##ServerHost_ChangeLevel", ImVec2(ImGui::GetWindowSize().x, 32))) + { + if (!m_Server.svMapName.empty()) + { + strncpy_s(g_pHostState->m_levelName, m_Server.svMapName.c_str(), 64); // Copy new map into hoststate levelname. 64 is size of m_levelname. + g_pHostState->m_iNextState = HostStates_t::HS_CHANGE_LEVEL_MP; // Force CHostState::FrameUpdate to change the level. + } + else + { + szServerNameErr = "Failed to change level: 'levelname' was empty."; + } + } + + if (ImGui::Button("Stop Server##ServerHost_StopServerButton", ImVec2(ImGui::GetWindowSize().x, 32))) + { + ProcessCommand("LeaveMatch"); // TODO: use script callback instead. + g_pHostState->m_iNextState = HostStates_t::HS_GAME_SHUTDOWN; // Force CHostState::FrameUpdate to shutdown the server for dedicated. + } + } + else + { + if (ImGui::Button("Reload Playlist from Disk##ServerHost_ReloadPlaylist", ImVec2(ImGui::GetWindowSize().x, 32))) + { + DownloadPlaylists_Callback(); + CKeyValueSystem_InitPlaylist(); // Re-Init playlist. + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: draws the settings section +//----------------------------------------------------------------------------- +void IBrowser::SettingsSection() +{ + ImGui::InputTextWithHint("Hostname##MatchmakingServerString", "Matchmaking Server String", &m_szMatchmakingHostName); + if (ImGui::Button("Update Hostname")) + { + r5net_matchmaking_hostname->m_pzsCurrentValue = m_szMatchmakingHostName.c_str(); + if (g_pR5net) + { + delete g_pR5net; + g_pR5net = new R5Net::Client(r5net_matchmaking_hostname->m_pzsCurrentValue); + } + } + ImGui::InputText("Netkey##SettingsSection_EncKey", (char*)g_szNetKey.c_str(), ImGuiInputTextFlags_ReadOnly); + if (ImGui::Button("Regenerate Encryption Key##SettingsSection_RegenEncKey")) + { + RegenerateEncryptionKey(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: draws the main browser frontend +//----------------------------------------------------------------------------- +void IBrowser::Draw(const char* title, bool* bDraw) +{ + if (!m_bThemeSet) + { + SetStyleVar(); + m_bThemeSet = true; + } + if (!ImGui::Begin(title, bDraw)) + { + ImGui::End(); + return; + } + if (*bDraw == NULL) + { + g_bShowBrowser = false; + } + + ImGui::SetNextWindowSize(ImVec2(840, 600), ImGuiCond_FirstUseEver); + ImGui::SetWindowPos(ImVec2(-500, 50), ImGuiCond_FirstUseEver); + + ImGui::Begin(title, NULL, ImGuiWindowFlags_NoScrollbar); + { + CompMenu(); + + switch (eCurrentSection) + { + case ESection::SERVER_BROWSER: + ServerBrowserSection(); + break; + case ESection::HOST_SERVER: + HostServerSection(); + break; + case ESection::SETTINGS: + SettingsSection(); + break; + default: + break; + } + } + ImGui::End(); +} + +//----------------------------------------------------------------------------- +// Purpose: executes submitted commands in a separate thread +//----------------------------------------------------------------------------- +void IBrowser::ProcessCommand(const char* command_line) +{ + std::thread t(IVEngineClient_CommandExecute, this, command_line); + t.detach(); // Detach from render thread. + + // This is to avoid a race condition. + std::this_thread::sleep_for(std::chrono::milliseconds(1)); +} + +//----------------------------------------------------------------------------- +// Purpose: connects to specified server +//----------------------------------------------------------------------------- +void IBrowser::ConnectToServer(const std::string ip, const std::string port, const std::string encKey) +{ + if (!encKey.empty()) + { + ChangeEncryptionKeyTo(encKey); + } + + std::stringstream cmd; + cmd << "connect " << ip << ":" << port; + ProcessCommand(cmd.str().c_str()); +} + +//----------------------------------------------------------------------------- +// Purpose: connects to specified server +//----------------------------------------------------------------------------- +void IBrowser::ConnectToServer(const std::string connString, const std::string encKey) +{ + if (!encKey.empty()) + { + ChangeEncryptionKeyTo(encKey); + } + + std::stringstream cmd; + cmd << "connect " << connString; + ProcessCommand(cmd.str().c_str()); +} + +//----------------------------------------------------------------------------- +// Purpose: regenerates encryption key +//----------------------------------------------------------------------------- +void IBrowser::RegenerateEncryptionKey() +{ + HNET_GenerateKey(); +} + +//----------------------------------------------------------------------------- +// Purpose: changes encryption key to specified one +//----------------------------------------------------------------------------- +void IBrowser::ChangeEncryptionKeyTo(const std::string str) +{ + HNET_SetKey(str); +} + +//############################################################################# +// ENTRYPOINT +//############################################################################# + +IBrowser* g_pServerBrowser = nullptr; +void DrawBrowser(bool* bDraw) +{ + static IBrowser browser; + static bool AssignPtr = []() + { + g_pServerBrowser = &browser; + return true; + } (); + browser.Draw("Server Browser", bDraw); +} diff --git a/r5dev/gameui/IBrowser.h b/r5dev/gameui/IBrowser.h new file mode 100644 index 00000000..6ff873e3 --- /dev/null +++ b/r5dev/gameui/IBrowser.h @@ -0,0 +1,173 @@ +#pragma once +#ifndef DEDICATED +#include "networksystem/serverlisting.h" +#include "networksystem/r5net.h" + +void DrawBrowser(bool* bDraw); + +class IBrowser +{ +private: + bool m_bThemeSet = false; +public: + IBrowser(); + ~IBrowser(); + + //////////////////// + // Enums // + //////////////////// + enum class ESection + { + SERVER_BROWSER, + HOST_SERVER, + SETTINGS + } eCurrentSection = ESection::SERVER_BROWSER; + + enum class EHostStatus + { + NOT_HOSTING, + HOSTING + } eHostingStatus = EHostStatus::NOT_HOSTING; + + enum class EServerVisibility + { + OFFLINE, + HIDDEN, + PUBLIC + } eServerVisibility = EServerVisibility::OFFLINE; + + //////////////////// + // Server Browser // + //////////////////// +public: + std::vector m_vServerList; + ImGuiTextFilter m_imServerBrowserFilter; + char m_chServerConnStringBuffer[256] = { 0 }; + char m_chServerEncKeyBuffer[30] = { 0 }; + std::string m_szServerListMessage = std::string(); + + std::map mapArray = + { + { "mp_rr_canyonlands_64k_x_64k", "King's Canyon Season 0" }, + { "mp_rr_desertlands_64k_x_64k", "World's Edge Season 3" }, + { "mp_rr_canyonlands_mu1", "King's Canyon Season 2" }, + { "mp_rr_canyonlands_mu1_night", "King's Canyon Season 2 After Dark" }, + { "mp_rr_desertlands_64k_x_64k_nx", "World's Edge Season 3 After Dark" }, + { "mp_lobby", "Lobby Season 3" }, + { "mp_rr_canyonlands_staging", "King's Canyon Firing Range" } + }; + + //////////////////// + // Settings // + //////////////////// + std::string m_szMatchmakingHostName; + + //////////////////// + // Host Server // + //////////////////// + ServerListing m_Server; + std::vector m_vszMapsList; + std::string m_szHostRequestMessage = ""; + std::string m_szHostToken = ""; + ImVec4 m_iv4HostRequestMessageColor = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); + + //////////////////// + // Private Server // + //////////////////// + std::string m_szHiddenServerToken = ""; + std::string m_szHiddenServerRequestMessage = ""; + ImVec4 m_ivHiddenServerMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f); + + /* Texture */ + ID3D11ShaderResourceView* m_idLockedIcon = nullptr; + int m_nLockedIconWidth = 0; + int m_nLockedIconHeight = 0; + std::vector* m_vucLockedIconBlob; + + void SetSection(ESection section) + { + eCurrentSection = section; + } + + //////////////////// + // Style // + //////////////////// + void SetStyleVar() + { + ImGuiStyle& style = ImGui::GetStyle(); + ImVec4* colors = style.Colors; + + colors[ImGuiCol_Text] = ImVec4(0.81f, 0.81f, 0.81f, 1.00f); + colors[ImGuiCol_TextDisabled] = ImVec4(0.56f, 0.56f, 0.56f, 1.00f); + colors[ImGuiCol_WindowBg] = ImVec4(0.27f, 0.27f, 0.27f, 1.00f); + colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); + colors[ImGuiCol_PopupBg] = ImVec4(0.27f, 0.27f, 0.27f, 1.00f); + colors[ImGuiCol_Border] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f); + colors[ImGuiCol_BorderShadow] = ImVec4(0.04f, 0.04f, 0.04f, 0.64f); + colors[ImGuiCol_FrameBg] = ImVec4(0.13f, 0.13f, 0.13f, 1.00f); + colors[ImGuiCol_FrameBgHovered] = ImVec4(0.19f, 0.19f, 0.19f, 1.00f); + colors[ImGuiCol_FrameBgActive] = ImVec4(0.24f, 0.24f, 0.24f, 1.00f); + colors[ImGuiCol_TitleBg] = ImVec4(0.22f, 0.22f, 0.22f, 1.00f); + colors[ImGuiCol_TitleBgActive] = ImVec4(0.27f, 0.27f, 0.27f, 1.00f); + colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); + colors[ImGuiCol_MenuBarBg] = ImVec4(0.22f, 0.22f, 0.22f, 1.00f); + colors[ImGuiCol_ScrollbarBg] = ImVec4(0.10f, 0.10f, 0.10f, 1.00f); + colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f); + colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.53f, 0.53f, 0.53f, 1.00f); + colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.63f, 0.63f, 0.63f, 1.00f); + colors[ImGuiCol_CheckMark] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f); + colors[ImGuiCol_SliderGrab] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f); + colors[ImGuiCol_SliderGrabActive] = ImVec4(0.53f, 0.53f, 0.53f, 1.00f); + colors[ImGuiCol_Button] = ImVec4(0.35f, 0.35f, 0.35f, 1.00f); + colors[ImGuiCol_ButtonHovered] = ImVec4(0.45f, 0.45f, 0.45f, 1.00f); + colors[ImGuiCol_ButtonActive] = ImVec4(0.52f, 0.52f, 0.52f, 1.00f); + colors[ImGuiCol_Header] = ImVec4(0.35f, 0.35f, 0.35f, 1.00f); + colors[ImGuiCol_HeaderHovered] = ImVec4(0.45f, 0.45f, 0.45f, 1.00f); + colors[ImGuiCol_HeaderActive] = ImVec4(0.53f, 0.53f, 0.53f, 1.00f); + colors[ImGuiCol_Separator] = ImVec4(0.53f, 0.53f, 0.57f, 1.00f); + colors[ImGuiCol_SeparatorHovered] = ImVec4(0.53f, 0.53f, 0.53f, 1.00f); + colors[ImGuiCol_SeparatorActive] = ImVec4(0.63f, 0.63f, 0.63f, 1.00f); + colors[ImGuiCol_ResizeGrip] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f); + colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.52f, 0.52f, 0.52f, 1.00f); + colors[ImGuiCol_ResizeGripActive] = ImVec4(0.63f, 0.63f, 0.63f, 1.00f); + colors[ImGuiCol_Tab] = ImVec4(0.18f, 0.18f, 0.18f, 1.00f); + colors[ImGuiCol_TabHovered] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); + colors[ImGuiCol_TabActive] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); + + style.WindowBorderSize = 0.0f; + style.FrameBorderSize = 1.0f; + style.ChildBorderSize = 1.0f; + style.PopupBorderSize = 1.0f; + style.TabBorderSize = 1.0f; + + style.WindowRounding = 2.5f; + style.FrameRounding = 0.0f; + style.ChildRounding = 0.0f; + style.PopupRounding = 0.0f; + style.TabRounding = 1.0f; + style.ScrollbarRounding = 1.0f; + + style.ItemSpacing = ImVec2(4, 4); + style.WindowPadding = ImVec2(5, 5); + } + + void RefreshServerList(); + void SendHostingPostRequest(); + void CompMenu(); + void ServerBrowserSection(); + void SettingsSection(); + void HiddenServersModal(); + void HostServerSection(); + void Draw(const char* title, bool* bDraw); + void UpdateHostingStatus(); + void ProcessCommand(const char* command_line); + + void RegenerateEncryptionKey(); + void ChangeEncryptionKeyTo(const std::string str); + + void ConnectToServer(const std::string ip, const std::string port, const std::string encKey); + void ConnectToServer(const std::string connString, const std::string encKey); +}; + +extern IBrowser* g_pServerBrowser; +#endif diff --git a/r5dev/gameui/IConsole.cpp b/r5dev/gameui/IConsole.cpp new file mode 100644 index 00000000..211ade66 --- /dev/null +++ b/r5dev/gameui/IConsole.cpp @@ -0,0 +1,408 @@ +#include "core/stdafx.h" +#include "core/init.h" +#include "tier0/cvar.h" +#include "windows/id3dx.h" +#include "windows/console.h" +#include "gameui/IConsole.h" +#include "client/IVEngineClient.h" + +/****************************************************************************** +------------------------------------------------------------------------------- +File : IConsole.cpp +Date : 18:07:2021 +Author : Kawe Mazidjatari +Purpose: Implements the in-game console frontend +------------------------------------------------------------------------------- +History: +- 15:06:2021 | 14:56 : Created by Kawe Mazidjatari +- 07:08:2021 | 15:22 : Multithread 'CommandExecute' operations to prevent deadlock in render thread +- 07:08:2021 | 15:25 : Fix a race condition that occured when detaching the 'CommandExecute' thread + +******************************************************************************/ + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CConsole::CConsole() +{ + ClearLog(); + memset(m_szInputBuf, 0, sizeof(m_szInputBuf)); + + m_nHistoryPos = -1; + m_bAutoScroll = true; + m_bScrollToBottom = false; + m_bThemeSet = false; + + m_ivCommands.push_back("CLEAR"); + m_ivCommands.push_back("HELP"); + m_ivCommands.push_back("HISTORY"); + AddLog("[DEBUG] THREAD ID: %ld\n", g_dThreadId); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CConsole::~CConsole() +{ + ClearLog(); + for (int i = 0; i < m_ivHistory.Size; i++) + { + free(m_ivHistory[i]); + } +} + +//----------------------------------------------------------------------------- +// Purpose: draws the console frontend +//----------------------------------------------------------------------------- +void CConsole::Draw(const char* title, bool* bDraw) +{ + bool copy_to_clipboard = false; + static auto iConsoleConfig = &g_pImGuiConfig->IConsole_Config; + static auto iBrowserConfig = &g_pImGuiConfig->IBrowser_Config; + + if (!m_bThemeSet) + { + SetStyleVar(); + m_bThemeSet = true; + } + if (iConsoleConfig->m_bAutoClear && Items.Size > iConsoleConfig->m_nAutoClearLimit) // Check if Auto-Clear is enabled and if its above our limit. + { + ClearLog(); + m_ivHistory.clear(); + } + + //ImGui::ShowStyleEditor(); + + ImGui::SetNextWindowSize(ImVec2(1000, 600), ImGuiCond_FirstUseEver); + ImGui::SetWindowPos(ImVec2(-1000, 50), ImGuiCond_FirstUseEver); + + if (!ImGui::Begin(title, bDraw)) + { + ImGui::End(); + return; + } + if (*bDraw == NULL) + { + g_bShowConsole = false; + } + + // Reserve enough left-over height and width for 1 separator + 1 input text + const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); + const float footer_width_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetWindowWidth(); + + /////////////////////////////////////////////////////////////////////// + ImGui::Separator(); + if (ImGui::BeginPopup("Options")) + { + ImGui::Checkbox("Auto-Scroll", &m_bAutoScroll); + + if (ImGui::Checkbox("Auto-Clear", &iConsoleConfig->m_bAutoClear)) + { + g_pImGuiConfig->Save(); + } + + ImGui::SameLine(); + ImGui::PushItemWidth(100); + + if (ImGui::InputInt("Limit##AutoClearAfterCertainIndexCGameConsole", &iConsoleConfig->m_nAutoClearLimit)) + { + g_pImGuiConfig->Save(); + } + + ImGui::PopItemWidth(); + + if (ImGui::SmallButton("Clear")) + { + ClearLog(); + } + + ImGui::SameLine(); + copy_to_clipboard = ImGui::SmallButton("Copy"); + + ImGui::Text("Console Hotkey:"); + ImGui::SameLine(); + + if (ImGui::Hotkey("##OpenIConsoleBind0", &iConsoleConfig->m_nBind0, ImVec2(80, 80))) + { + g_pImGuiConfig->Save(); + } + + ImGui::Text("Browser Hotkey:"); + ImGui::SameLine(); + + if (ImGui::Hotkey("##OpenIBrowserBind0", &iBrowserConfig->m_nBind0, ImVec2(80, 80))) + { + g_pImGuiConfig->Save(); + } + + ImGui::EndPopup(); + } + if (ImGui::Button("Options")) + { + ImGui::OpenPopup("Options"); + } + ImGui::SameLine(); + m_itFilter.Draw("Filter [\"-incl,-excl\"] [\"error\"]", footer_width_to_reserve - 500); + ImGui::Separator(); + + /////////////////////////////////////////////////////////////////////// + ImGui::BeginChild("ScrollingRegion", ImVec2(0, -footer_height_to_reserve), true, ImGuiWindowFlags_AlwaysVerticalScrollbar); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 4.f, 6.f }); + if (copy_to_clipboard) + { + ImGui::LogToClipboard(); + } + for (int i = 0; i < Items.Size; i++) + { + const char* item = Items[i]; + if (!m_itFilter.PassFilter(item)) + { + continue; + } + /////////////////////////////////////////////////////////////////// + ImVec4 color; + bool has_color = false; + + /////////////////////////////////////////////////////////////////// + // General + if (strstr(item, "[INFO]")) { color = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); has_color = true; } + if (strstr(item, "[ERROR]")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } + if (strstr(item, "[DEBUG]")) { color = ImVec4(0.00f, 0.30f, 1.00f, 1.00f); has_color = true; } + if (strstr(item, "[WARNING]")) { color = ImVec4(1.00f, 1.00f, 0.00f, 0.80f); has_color = true; } + if (strncmp(item, "# ", 2) == 0) { color = ImVec4(1.00f, 0.80f, 0.60f, 1.00f); has_color = true; } + + /////////////////////////////////////////////////////////////////// + // Script virtual machines per game dll + if (strstr(item, "Script(S):")) { color = ImVec4(0.59f, 0.58f, 0.73f, 1.00f); has_color = true; } + if (strstr(item, "Script(C):")) { color = ImVec4(0.59f, 0.58f, 0.63f, 1.00f); has_color = true; } + if (strstr(item, "Script(U):")) { color = ImVec4(0.59f, 0.48f, 0.53f, 1.00f); has_color = true; } + + /////////////////////////////////////////////////////////////////// + // Native per game dll + if (strstr(item, "Native(S):")) { color = ImVec4(0.59f, 0.58f, 0.73f, 1.00f); has_color = true; } + if (strstr(item, "Native(C):")) { color = ImVec4(0.59f, 0.58f, 0.63f, 1.00f); has_color = true; } + if (strstr(item, "Native(U):")) { color = ImVec4(0.59f, 0.48f, 0.53f, 1.00f); has_color = true; } + + /////////////////////////////////////////////////////////////////// + // Native per sys dll + if (strstr(item, "Native(E):")) { color = ImVec4(0.70f, 0.70f, 0.70f, 1.00f); has_color = true; } + if (strstr(item, "Native(F):")) { color = ImVec4(0.32f, 0.64f, 0.72f, 1.00f); has_color = true; } + if (strstr(item, "Native(R):")) { color = ImVec4(0.36f, 0.70f, 0.35f, 1.00f); has_color = true; } + if (strstr(item, "Native(M):")) { color = ImVec4(0.75f, 0.41f, 0.67f, 1.00f); has_color = true; } + + /////////////////////////////////////////////////////////////////// + // Callbacks + //if (strstr(item, "CodeCallback_")) { color = ImVec4(0.00f, 0.30f, 1.00f, 1.00f); has_color = true; } + + /////////////////////////////////////////////////////////////////// + // Squirrel VM script errors + if (strstr(item, ".gnut")) { color = ImVec4(1.00f, 1.00f, 1.00f, 0.60f); has_color = true; } + if (strstr(item, ".nut")) { color = ImVec4(1.00f, 1.00f, 1.00f, 0.60f); has_color = true; } + if (strstr(item, "[CLIENT]")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } + if (strstr(item, "[SERVER]")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } + if (strstr(item, "[UI]")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } + if (strstr(item, "SCRIPT ERROR")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } + if (strstr(item, "SCRIPT COMPILE")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } + if (strstr(item, ".gnut #")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } + if (strstr(item, ".nut #")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } + if (strstr(item, "): -> ")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } + + /////////////////////////////////////////////////////////////////// + // Squirrel VM script debug + if (strstr(item, "CALLSTACK")) { color = ImVec4(1.00f, 1.00f, 0.00f, 0.80f); has_color = true; } + if (strstr(item, "LOCALS")) { color = ImVec4(1.00f, 1.00f, 0.00f, 0.80f); has_color = true; } + if (strstr(item, "*FUNCTION")) { color = ImVec4(1.00f, 1.00f, 0.00f, 0.80f); has_color = true; } + if (strstr(item, "DIAGPRINTS")) { color = ImVec4(1.00f, 1.00f, 0.00f, 0.80f); has_color = true; } + if (strstr(item, " File : ")) { color = ImVec4(0.00f, 0.30f, 1.00f, 1.00f); has_color = true; } + if (strstr(item, "<><>GRX<><>")) { color = ImVec4(0.00f, 0.30f, 1.00f, 1.00f); has_color = true; } + + /////////////////////////////////////////////////////////////////// + // Filters + //if (strstr(item, ") -> ")) { color = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); has_color = true; } + + if (has_color) { ImGui::PushStyleColor(ImGuiCol_Text, color); } + ImGui::TextWrapped(item); + if (has_color) { ImGui::PopStyleColor(); } + } + if (copy_to_clipboard) { ImGui::LogFinish(); } + + if (m_bScrollToBottom || (m_bAutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())) { ImGui::SetScrollHereY(1.0f); } + m_bScrollToBottom = false; + + /////////////////////////////////////////////////////////////////////// + ImGui::PopStyleVar(); + ImGui::EndChild(); + ImGui::Separator(); + + /////////////////////////////////////////////////////////////////////// + // Console + bool reclaim_focus = false; + ImGui::PushItemWidth(footer_width_to_reserve - 80); + if (ImGui::IsWindowAppearing()) { ImGui::SetKeyboardFocusHere(); } + ImGuiInputTextFlags input_text_flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory; + + if (ImGui::InputText("##input", m_szInputBuf, IM_ARRAYSIZE(m_szInputBuf), input_text_flags, &TextEditCallbackStub, (void*)this)) + { + char* s = m_szInputBuf; + const char* replace = ""; + if (strstr(m_szInputBuf, "`")) { strcpy_s(s, sizeof(replace), replace); } + Strtrim(s); + if (s[0]) { ProcessCommand(s); } + strcpy_s(s, sizeof(replace), replace); + reclaim_focus = true; + } + + ImGui::SameLine(); + if (ImGui::Button("Submit")) + { + char* s = m_szInputBuf; + const char* replace = ""; + if (s[0]) { ProcessCommand(s); } + strcpy_s(s, sizeof(replace), replace); + reclaim_focus = true; + } + + // Auto-focus on window apparition. + ImGui::SetItemDefaultFocus(); + + // Auto focus previous widget. + if (reclaim_focus) + { + ImGui::SetKeyboardFocusHere(-1); + } + + ImGui::End(); +} + +//----------------------------------------------------------------------------- +// Purpose: executes submitted commands in a separate thread +//----------------------------------------------------------------------------- +void CConsole::ProcessCommand(const char* command_line) +{ + AddLog("# %s\n", command_line); + + std::thread t(IVEngineClient_CommandExecute, this, command_line); + t.detach(); // Detach from render thread. + + // This is to avoid a race condition. + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + + m_nHistoryPos = -1; + for (int i = m_ivHistory.Size - 1; i >= 0; i--) + { + if (Stricmp(m_ivHistory[i], command_line) == 0) + { + delete m_ivHistory[i]; + m_ivHistory.erase(m_ivHistory.begin() + i); + break; + } + } + + m_ivHistory.push_back(Strdup(command_line)); + if (Stricmp(command_line, "CLEAR") == 0) + { + ClearLog(); + } + else if (Stricmp(command_line, "HELP") == 0) + { + AddLog("Frontend commands:"); + for (int i = 0; i < m_ivCommands.Size; i++) + { + AddLog("- %s", m_ivCommands[i]); + } + + AddLog("Log types:"); + AddLog("Script(S): = Server (Script VM)"); + AddLog("Script(C): = Client (Script VM)"); + AddLog("Script(U): = UI (Script VM)"); + + AddLog("Native(S): = Server dll (Code)"); + AddLog("Native(C): = Client dll (code)"); + AddLog("Native(U): = UI dll (code)"); + + AddLog("Native(E): = Engine dll (code)"); + AddLog("Native(F): = FileSystem dll (code)"); + AddLog("Native(R): = RTech dll (code)"); + AddLog("Native(M): = MaterialSystem dll (code)"); + } + else if (Stricmp(command_line, "HISTORY") == 0) + { + int first = m_ivHistory.Size - 10; + for (int i = first > 0 ? first : 0; i < m_ivHistory.Size; i++) + { + AddLog("%3d: %s\n", i, m_ivHistory[i]); + } + } + + m_bScrollToBottom = true; +} + +//----------------------------------------------------------------------------- +// Purpose: text edit callback +//----------------------------------------------------------------------------- +int CConsole::TextEditCallback(ImGuiInputTextCallbackData* data) +{ + switch (data->EventFlag) + { + case ImGuiInputTextFlags_CallbackCompletion: + { + // Locate beginning of current word. + const char* word_end = data->Buf + data->CursorPos; + const char* word_start = word_end; + while (word_start > data->Buf) + { + const char c = word_start[-1]; + if (c == ' ' || c == '\t' || c == ',' || c == ';') + { + break; + } + word_start--; + } + break; + } + case ImGuiInputTextFlags_CallbackHistory: + { + const int prev_history_pos = m_nHistoryPos; + if (data->EventKey == ImGuiKey_UpArrow) + { + if (m_nHistoryPos == -1) { m_nHistoryPos = m_ivHistory.Size - 1; } + else if (m_nHistoryPos > 0) { m_nHistoryPos--; } + } + else if (data->EventKey == ImGuiKey_DownArrow) + { + if (m_nHistoryPos != -1) + { + if (++m_nHistoryPos >= m_ivHistory.Size) + { + m_nHistoryPos = -1; + } + } + } + if (prev_history_pos != m_nHistoryPos) + { + const char* history_str = (m_nHistoryPos >= 0) ? m_ivHistory[m_nHistoryPos] : ""; + data->DeleteChars(0, data->BufTextLen); + data->InsertChars(0, history_str); + } + } + } + return 0; +} + +//############################################################################# +// ENTRYPOINT +//############################################################################# + +CConsole* g_GameConsole = nullptr; +void DrawConsole(bool* bDraw) +{ + static CConsole console; + static bool AssignPtr = []() + { + g_GameConsole = &console; + return true; + } (); + console.Draw("Console", bDraw); +} + +/////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/include/CGameConsole.h b/r5dev/gameui/IConsole.h similarity index 84% rename from r5dev/include/CGameConsole.h rename to r5dev/gameui/IConsole.h index 764f6839..3d9e9b96 100644 --- a/r5dev/include/CGameConsole.h +++ b/r5dev/gameui/IConsole.h @@ -1,40 +1,42 @@ #pragma once -#include "gui_utility.h" +#ifndef DEDICATED + +/////////////////////////////////////////////////////////////////////////////// +// Initialization +void DrawConsole(bool* bDraw); /////////////////////////////////////////////////////////////////////////////// // Globals -extern ImVector Items; +inline ImVector Items; -class CGameConsole +class CConsole { private: /////////////////////////////////////////////////////////////////////////// - char InputBuf[256] = { 0 }; - ImVector Commands; - ImVector History; - int HistoryPos = -1; - ImGuiTextFilter Filter; - bool AutoScroll = true; - bool ScrollToBottom = false; - bool ThemeSet = false; + char m_szInputBuf[256] = { 0 }; + ImVector m_ivCommands; + ImVector m_ivHistory; + int m_nHistoryPos = -1; + ImGuiTextFilter m_itFilter; + bool m_bAutoScroll = true; + bool m_bScrollToBottom = false; + bool m_bThemeSet = false; public: /////////////////////////////////////////////////////////////////////////// - CGameConsole(); - ~CGameConsole(); + CConsole(); + ~CConsole(); - void Draw(const char* title); + void Draw(const char* title, bool* bDraw); void ProcessCommand(const char* command_line); - void ExecCommand(const char* command_line); int TextEditCallback(ImGuiInputTextCallbackData* data); - bool ShouldPrintToCommandPrompt() { return g_GuiConfig.CGameConsoleConfig.printCmd; }; /////////////////////////////////////////////////////////////////////////// // History static int TextEditCallbackStub(ImGuiInputTextCallbackData* data) { - CGameConsole* console = (CGameConsole*)data->UserData; + CConsole* console = (CConsole*)data->UserData; return console->TextEditCallback(data); } @@ -42,9 +44,9 @@ public: // Utility void ClearLog() { + for (int i = 0; i < Items.Size; i++) { free(Items[i]); } Items.clear(); } - void AddLog(const char* fmt, ...) IM_FMTARGS(2) { char buf[1024]; @@ -118,4 +120,5 @@ public: } }; -extern CGameConsole* g_GameConsole; \ No newline at end of file +extern CConsole* g_GameConsole; +#endif // !DEDICATED diff --git a/r5dev/include/CCompanion.h b/r5dev/include/CCompanion.h deleted file mode 100644 index 93713b1a..00000000 --- a/r5dev/include/CCompanion.h +++ /dev/null @@ -1,163 +0,0 @@ -#pragma once -#include "gui_utility.h" -#include "r5net.h" - -class CCompanion -{ -private: - bool ThemeSet = false; -public: - CCompanion(); - ~CCompanion(); - - //////////////////// - // Enums // - //////////////////// - enum class ESection { - ServerBrowser, - HostServer, - Settings - } CurrentSection = ESection::ServerBrowser; - - enum class EHostStatus { - NotHosting, - Hosting - } HostingStatus = EHostStatus::NotHosting; - - enum class EServerVisibility { - Offline, - Hidden, - Public - } ServerVisibility = EServerVisibility::Offline; - - //////////////////// - // Server Browser // - //////////////////// -private: - R5Net::Client* r5net = nullptr; -public: - R5Net::Client* GetR5Net() { return r5net; } - - std::vector ServerList; - ImGuiTextFilter ServerBrowserFilter; - char ServerConnStringBuffer[256] = { 0 }; - char ServerEncKeyBuffer[30] = { 0 }; - std::string ServerListMessage = std::string(); - - //////////////////// - // Settings // - //////////////////// - std::string MatchmakingServerStringBuffer; - - //////////////////// - // Host Server // - //////////////////// - ServerListing MyServer; - std::vector MapsList; - std::string HostRequestMessage = ""; - std::string HostToken = ""; - ImVec4 HostRequestMessageColor = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - bool StartAsDedi = false; - bool OverridePlaylist = false; - - //////////////////// - // Private Server // - //////////////////// - std::string HiddenServerToken = ""; - std::string HiddenServerRequestMessage = ""; - ImVec4 HiddenServerMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f); - - /* Texture */ - ID3D11ShaderResourceView* ApexLockIcon = nullptr; - int ApexLockIconWidth = 48; - int ApexLockIconHeight = 48; - - void SetSection(ESection section) - { - CurrentSection = section; - } - - //////////////////// - // Style // - //////////////////// - void SetStyleVar() - { - ImGuiStyle& style = ImGui::GetStyle(); - ImVec4* colors = style.Colors; - - colors[ImGuiCol_Text] = ImVec4(0.81f, 0.81f, 0.81f, 1.00f); - colors[ImGuiCol_TextDisabled] = ImVec4(0.56f, 0.56f, 0.56f, 1.00f); - colors[ImGuiCol_WindowBg] = ImVec4(0.27f, 0.27f, 0.27f, 1.00f); - colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - colors[ImGuiCol_PopupBg] = ImVec4(0.27f, 0.27f, 0.27f, 1.00f); - colors[ImGuiCol_Border] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f); - colors[ImGuiCol_BorderShadow] = ImVec4(0.04f, 0.04f, 0.04f, 0.64f); - colors[ImGuiCol_FrameBg] = ImVec4(0.13f, 0.13f, 0.13f, 1.00f); - colors[ImGuiCol_FrameBgHovered] = ImVec4(0.19f, 0.19f, 0.19f, 1.00f); - colors[ImGuiCol_FrameBgActive] = ImVec4(0.24f, 0.24f, 0.24f, 1.00f); - colors[ImGuiCol_TitleBg] = ImVec4(0.22f, 0.22f, 0.22f, 1.00f); - colors[ImGuiCol_TitleBgActive] = ImVec4(0.27f, 0.27f, 0.27f, 1.00f); - colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - colors[ImGuiCol_MenuBarBg] = ImVec4(0.22f, 0.22f, 0.22f, 1.00f); - colors[ImGuiCol_ScrollbarBg] = ImVec4(0.10f, 0.10f, 0.10f, 1.00f); - colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f); - colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.53f, 0.53f, 0.53f, 1.00f); - colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.63f, 0.63f, 0.63f, 1.00f); - colors[ImGuiCol_CheckMark] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f); - colors[ImGuiCol_SliderGrab] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f); - colors[ImGuiCol_SliderGrabActive] = ImVec4(0.53f, 0.53f, 0.53f, 1.00f); - colors[ImGuiCol_Button] = ImVec4(0.35f, 0.35f, 0.35f, 1.00f); - colors[ImGuiCol_ButtonHovered] = ImVec4(0.45f, 0.45f, 0.45f, 1.00f); - colors[ImGuiCol_ButtonActive] = ImVec4(0.52f, 0.52f, 0.52f, 1.00f); - colors[ImGuiCol_Header] = ImVec4(0.35f, 0.35f, 0.35f, 1.00f); - colors[ImGuiCol_HeaderHovered] = ImVec4(0.45f, 0.45f, 0.45f, 1.00f); - colors[ImGuiCol_HeaderActive] = ImVec4(0.53f, 0.53f, 0.53f, 1.00f); - colors[ImGuiCol_Separator] = ImVec4(0.53f, 0.53f, 0.57f, 1.00f); - colors[ImGuiCol_SeparatorHovered] = ImVec4(0.53f, 0.53f, 0.53f, 1.00f); - colors[ImGuiCol_SeparatorActive] = ImVec4(0.63f, 0.63f, 0.63f, 1.00f); - colors[ImGuiCol_ResizeGrip] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f); - colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.52f, 0.52f, 0.52f, 1.00f); - colors[ImGuiCol_ResizeGripActive] = ImVec4(0.63f, 0.63f, 0.63f, 1.00f); - colors[ImGuiCol_Tab] = ImVec4(0.18f, 0.18f, 0.18f, 1.00f); - colors[ImGuiCol_TabHovered] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); - colors[ImGuiCol_TabActive] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); - - style.WindowBorderSize = 0.0f; - style.FrameBorderSize = 1.0f; - style.ChildBorderSize = 1.0f; - style.PopupBorderSize = 1.0f; - style.TabBorderSize = 1.0f; - - style.WindowRounding = 2.5f; - style.FrameRounding = 0.0f; - style.ChildRounding = 0.0f; - style.PopupRounding = 0.0f; - style.TabRounding = 1.0f; - style.ScrollbarRounding = 1.0f; - - style.ItemSpacing = ImVec2(4, 4); - style.WindowPadding = ImVec2(5, 5); - } - - void RefreshServerList(); - void SendHostingPostRequest(); - void CompMenu(); - void ServerBrowserSection(); - void SettingsSection(); - void HiddenServersModal(); - void HostServerSection(); - void Draw(const char* title); - void UpdateHostingStatus(); - void ProcessCommand(const char* command_line); - void ExecCommand(const char* command_line); - - void RegenerateEncryptionKey(); - void ChangeEncryptionKeyTo(const std::string str); - - void ConnectToServer(const std::string ip, const std::string port, const std::string encKey); - void ConnectToServer(const std::string connString, const std::string encKey); -}; - -extern CCompanion* g_ServerBrowser; - -extern bool g_CheckCompBanDB; \ No newline at end of file diff --git a/r5dev/include/console.h b/r5dev/include/console.h deleted file mode 100644 index 6e5f6aed..00000000 --- a/r5dev/include/console.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -///////////////////////////////////////////////////////////////////////////// -// Initialization -void SetupConsole(); \ No newline at end of file diff --git a/r5dev/include/enums.h b/r5dev/include/enums.h deleted file mode 100644 index afe89dda..00000000 --- a/r5dev/include/enums.h +++ /dev/null @@ -1,790 +0,0 @@ -#pragma once - -/* Enumerations */ -enum class D3D11DeviceVTbl : short -{ - // IUnknown - QueryInterface = 0, - AddRef = 1, - Release = 2, - - // ID3D11Device - CreateBuffer = 3, - CreateTexture1D = 4, - CreateTexture2D = 5, - CreateTexture3D = 6, - CreateShaderResourceView = 7, - CreateUnorderedAccessView = 8, - CreateRenderTargetView = 9, - CreateDepthStencilView = 10, - CreateInputLayout = 11, - CreateVertexShader = 12, - CreateGeometryShader = 13, - CreateGeometryShaderWithStreamOutput = 14, - CreatePixelShader = 15, - CreateHullShader = 16, - CreateDomainShader = 17, - CreateComputeShader = 18, - CreateClassLinkage = 19, - CreateBlendState = 20, - CreateDepthStencilState = 21, - CreateRasterizerState = 22, - CreateSamplerState = 23, - CreateQuery = 24, - CreatePredicate = 25, - CreateCounter = 26, - CreateDeferredContext = 27, - OpenSharedResource = 28, - CheckFormatSupport = 29, - CheckMultisampleQualityLevels = 30, - CheckCounterInfo = 31, - CheckCounter = 32, - CheckFeatureSupport = 33, - GetPrivateData = 34, - SetPrivateData = 35, - SetPrivateDataInterface = 36, - GetFeatureLevel = 37, - GetCreationFlags = 38, - GetDeviceRemovedReason = 39, - GetImmediateContext = 40, - SetExceptionMode = 41, - GetExceptionMode = 42, -}; - -enum class DXGISwapChainVTbl : short -{ - // IUnknown - QueryInterface = 0, - AddRef = 1, - Release = 2, - - // IDXGIObject - SetPrivateData = 3, - SetPrivateDataInterface = 4, - GetPrivateData = 5, - GetParent = 6, - - // IDXGIDeviceSubObject - GetDevice = 7, - - // IDXGISwapChain - Present = 8, - GetBuffer = 9, - SetFullscreenState = 10, - GetFullscreenState = 11, - GetDesc = 12, - ResizeBuffers = 13, - ResizeTarget = 14, - GetContainingOutput = 15, - GetFrameStatistics = 16, - GetLastPresentCount = 17, -}; - -#define MAX_SPLITSCREEN_CLIENT_BITS 2 -#define MAX_SPLITSCREEN_CLIENTS ( 1 << MAX_SPLITSCREEN_CLIENT_BITS ) // 4 - -enum -{ - MAX_JOYSTICKS = MAX_SPLITSCREEN_CLIENTS, - MOUSE_BUTTON_COUNT = 5, -}; - -enum JoystickAxis_t -{ - JOY_AXIS_X = 0, - JOY_AXIS_Y, - JOY_AXIS_Z, - JOY_AXIS_R, - JOY_AXIS_U, - JOY_AXIS_V, - MAX_JOYSTICK_AXES, -}; - -enum -{ - JOYSTICK_MAX_BUTTON_COUNT = 32, - JOYSTICK_POV_BUTTON_COUNT = 4, - JOYSTICK_AXIS_BUTTON_COUNT = MAX_JOYSTICK_AXES * 2, -}; - -#define JOYSTICK_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_BUTTON + ((_joystick) * JOYSTICK_MAX_BUTTON_COUNT) + (_button) ) -#define JOYSTICK_POV_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_POV_BUTTON + ((_joystick) * JOYSTICK_POV_BUTTON_COUNT) + (_button) ) -#define JOYSTICK_AXIS_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_AXIS_BUTTON + ((_joystick) * JOYSTICK_AXIS_BUTTON_COUNT) + (_button) ) - -#define JOYSTICK_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_BUTTON_INTERNAL( _joystick, _button ) ) -#define JOYSTICK_POV_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_POV_BUTTON_INTERNAL( _joystick, _button ) ) -#define JOYSTICK_AXIS_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_AXIS_BUTTON_INTERNAL( _joystick, _button ) ) - -enum ButtonCode_t -{ - BUTTON_CODE_INVALID = -1, - BUTTON_CODE_NONE = 0, - - KEY_FIRST = 0, - - KEY_NONE = KEY_FIRST, - KEY_0, - KEY_1, - KEY_2, - KEY_3, - KEY_4, - KEY_5, - KEY_6, - KEY_7, - KEY_8, - KEY_9, - KEY_A, - KEY_B, - KEY_C, - KEY_D, - KEY_E, - KEY_F, - KEY_G, - KEY_H, - KEY_I, - KEY_J, - KEY_K, - KEY_L, - KEY_M, - KEY_N, - KEY_O, - KEY_P, - KEY_Q, - KEY_R, - KEY_S, - KEY_T, - KEY_U, - KEY_V, - KEY_W, - KEY_X, - KEY_Y, - KEY_Z, - KEY_PAD_0, - KEY_PAD_1, - KEY_PAD_2, - KEY_PAD_3, - KEY_PAD_4, - KEY_PAD_5, - KEY_PAD_6, - KEY_PAD_7, - KEY_PAD_8, - KEY_PAD_9, - KEY_PAD_DIVIDE, - KEY_PAD_MULTIPLY, - KEY_PAD_MINUS, - KEY_PAD_PLUS, - KEY_PAD_ENTER, - KEY_PAD_DECIMAL, - KEY_LBRACKET, - KEY_RBRACKET, - KEY_SEMICOLON, - KEY_APOSTROPHE, - KEY_BACKQUOTE, - KEY_COMMA, - KEY_PERIOD, - KEY_SLASH, - KEY_BACKSLASH, - KEY_MINUS, - KEY_EQUAL, - KEY_ENTER, - KEY_SPACE, - KEY_BACKSPACE, - KEY_TAB, - KEY_CAPSLOCK, - KEY_NUMLOCK, - KEY_ESCAPE, - KEY_SCROLLLOCK, - KEY_INSERT, - KEY_DELETE, - KEY_HOME, - KEY_END, - KEY_PAGEUP, - KEY_PAGEDOWN, - KEY_BREAK, - KEY_LSHIFT, - KEY_RSHIFT, - KEY_LALT, - KEY_RALT, - KEY_LCONTROL, - KEY_RCONTROL, - KEY_LWIN, - KEY_RWIN, - KEY_APP, - KEY_UP, - KEY_LEFT, - KEY_DOWN, - KEY_RIGHT, - KEY_F1, - KEY_F2, - KEY_F3, - KEY_F4, - KEY_F5, - KEY_F6, - KEY_F7, - KEY_F8, - KEY_F9, - KEY_F10, - KEY_F11, - KEY_F12, - KEY_CAPSLOCKTOGGLE, - KEY_NUMLOCKTOGGLE, - KEY_SCROLLLOCKTOGGLE, - - KEY_LAST = KEY_SCROLLLOCKTOGGLE, - KEY_COUNT = KEY_LAST - KEY_FIRST + 1, - - // Mouse - MOUSE_FIRST = KEY_LAST + 1, - - MOUSE_LEFT = MOUSE_FIRST, - MOUSE_RIGHT, - MOUSE_MIDDLE, - MOUSE_4, - MOUSE_5, - MOUSE_WHEEL_UP, // A fake button which is 'pressed' and 'released' when the wheel is moved up - MOUSE_WHEEL_DOWN, // A fake button which is 'pressed' and 'released' when the wheel is moved down - - MOUSE_LAST = MOUSE_WHEEL_DOWN, - MOUSE_COUNT = MOUSE_LAST - MOUSE_FIRST + 1, - - // Joystick - JOYSTICK_FIRST = MOUSE_LAST + 1, - - JOYSTICK_FIRST_BUTTON = JOYSTICK_FIRST, - JOYSTICK_LAST_BUTTON = JOYSTICK_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_MAX_BUTTON_COUNT - 1), - JOYSTICK_FIRST_POV_BUTTON, - JOYSTICK_LAST_POV_BUTTON = JOYSTICK_POV_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_POV_BUTTON_COUNT - 1), - JOYSTICK_FIRST_AXIS_BUTTON, - JOYSTICK_LAST_AXIS_BUTTON = JOYSTICK_AXIS_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_AXIS_BUTTON_COUNT - 1), - - JOYSTICK_LAST = JOYSTICK_LAST_AXIS_BUTTON, - - BUTTON_CODE_LAST, - BUTTON_CODE_COUNT = BUTTON_CODE_LAST - KEY_FIRST + 1, - - // Helpers for XBox 360 - KEY_XBUTTON_UP = JOYSTICK_FIRST_POV_BUTTON, // POV buttons - KEY_XBUTTON_RIGHT, - KEY_XBUTTON_DOWN, - KEY_XBUTTON_LEFT, - - KEY_XBUTTON_A = JOYSTICK_FIRST_BUTTON, // Buttons - KEY_XBUTTON_B, - KEY_XBUTTON_X, - KEY_XBUTTON_Y, - KEY_XBUTTON_LEFT_SHOULDER, - KEY_XBUTTON_RIGHT_SHOULDER, - KEY_XBUTTON_BACK, - KEY_XBUTTON_START, - KEY_XBUTTON_STICK1, - KEY_XBUTTON_STICK2, - KEY_XBUTTON_INACTIVE_START, - - KEY_XSTICK1_RIGHT = JOYSTICK_FIRST_AXIS_BUTTON, // XAXIS POSITIVE - KEY_XSTICK1_LEFT, // XAXIS NEGATIVE - KEY_XSTICK1_DOWN, // YAXIS POSITIVE - KEY_XSTICK1_UP, // YAXIS NEGATIVE - KEY_XBUTTON_LTRIGGER, // ZAXIS POSITIVE - KEY_XBUTTON_RTRIGGER, // ZAXIS NEGATIVE - KEY_XSTICK2_RIGHT, // UAXIS POSITIVE - KEY_XSTICK2_LEFT, // UAXIS NEGATIVE - KEY_XSTICK2_DOWN, // VAXIS POSITIVE - KEY_XSTICK2_UP, // VAXIS NEGATIVE -}; - -// Buttons are not confirmed to be the same. They have been always the same throughout the source engine. Lets hope they did not change them. - -enum KeyValuesTypes -{ - TYPE_NONE = 0x0, - TYPE_STRING = 0x1, - TYPE_INT = 0x2, - TYPE_FLOAT = 0x3, - TYPE_PTR = 0x4, - TYPE_WSTRING = 0x5, - TYPE_COLOR = 0x6, - TYPE_UINT64 = 0x7, - TYPE_COMPILED_INT_BYTE = 0x8, - TYPE_COMPILED_INT_0 = 0x9, - TYPE_COMPILED_INT_1 = 0xA, - TYPE_NUMTYPES = 0xB, -}; - -enum ClientFrameStage_t -{ - FRAME_UNDEFINED = -1, // (haven't run any frames yet) - FRAME_START, - - // A network packet is being recieved - FRAME_NET_UPDATE_START, - // Data has been received and we're going to start calling PostDataUpdate - FRAME_NET_UPDATE_POSTDATAUPDATE_START, - // Data has been received and we've called PostDataUpdate on all data recipients - FRAME_NET_UPDATE_POSTDATAUPDATE_END, - // We've received all packets, we can now do interpolation, prediction, etc.. - FRAME_NET_UPDATE_END, - - // We're about to start rendering the scene - FRAME_RENDER_START, - // We've finished rendering the scene. - FRAME_RENDER_END, - - FRAME_NET_FULL_FRAME_UPDATE_ON_REMOVE -}; - -enum class HostStates_t : int -{ - HS_NEW_GAME = 0x0, - HS_LOAD_GAME = 0x1, - HS_CHANGE_LEVEL_SP = 0x2, - HS_CHANGE_LEVEL_MP = 0x3, - HS_RUN = 0x4, - HS_GAME_SHUTDOWN = 0x5, - HS_SHUTDOWN = 0x6, - HS_RESTART = 0x7, -}; - -enum SIGNONSTATE -{ - SIGNONSTATE_NONE = 0, // no state yet; about to connect - SIGNONSTATE_CHALLENGE = 1, // client challenging server; all OOB packets - SIGNONSTATE_CONNECTED = 2, // client is connected to server; netchans ready - SIGNONSTATE_NEW = 3, // just got serverinfo and string tables - SIGNONSTATE_PRESPAWN = 4, // received signon buffers - SIGNONSTATE_GETTING_DATA = 5, // getting persistence data I assume? - SIGNONSTATE_SPAWN = 6, // ready to receive entity packets - SIGNONSTATE_FIRST_SNAP = 7, // ??? - SIGNONSTATE_FULL = 8, // we are fully connected; first non-delta packet received - SIGNONSTATE_CHANGELEVEL = 9, // server is changing level; please wait -}; - -enum FileWarningLevel_t -{ - FILESYSTEM_WARNING = -1, - FILESYSTEM_WARNING_QUIET = 0, - FILESYSTEM_WARNING_REPORTUNCLOSED, - FILESYSTEM_WARNING_REPORTUSAGE, - FILESYSTEM_WARNING_REPORTALLACCESSES, - FILESYSTEM_WARNING_REPORTALLACCESSES_READ, - FILESYSTEM_WARNING_REPORTALLACCESSES_READWRITE, - FILESYSTEM_WARNING_REPORTALLACCESSES_ASYNC -}; - -#define FCVAR_NONE 0 - -// Command to ConVars and ConCommands -// ConVar Systems -#define FCVAR_UNREGISTERED (1<<0) // If this is set, don't add to linked list, etc. -#define FCVAR_DEVELOPMENTONLY (1<<1) // Hidden in released products. Flag is removed automatically if ALLOW_DEVELOPMENT_CVARS is defined. -#define FCVAR_GAMEDLL (1<<2) // defined by the game DLL -#define FCVAR_CLIENTDLL (1<<3) // defined by the client DLL -#define FCVAR_HIDDEN (1<<4) // Hidden. Doesn't appear in find or auto complete. Like DEVELOPMENTONLY, but can't be compiled out. - -// ConVar only -#define FCVAR_PROTECTED (1<<5) // It's a server cvar, but we don't send the data since it's a password, etc. Sends 1 if it's not bland/zero, 0 otherwise as value -#define FCVAR_SPONLY (1<<6) // This cvar cannot be changed by clients connected to a multiplayer server. -#define FCVAR_ARCHIVE (1<<7) // set to cause it to be saved to vars.rc -#define FCVAR_NOTIFY (1<<8) // notifies players when changed -#define FCVAR_USERINFO (1<<9) // changes the client's info string - -#define FCVAR_PRINTABLEONLY (1<<10) // This cvar's string cannot contain unprintable characters ( e.g., used for player name etc ). - -#define FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS (1<<10) // When on concommands this allows remote clients to execute this cmd on the server. - // We are changing the default behavior of concommands to disallow execution by remote clients without - // this flag due to the number existing concommands that can lag or crash the server when clients abuse them. - -#define FCVAR_UNLOGGED (1<<11) // If this is a FCVAR_SERVER, don't log changes to the log file / console if we are creating a log -#define FCVAR_NEVER_AS_STRING (1<<12) // never try to print that cvar - -// It's a ConVar that's shared between the client and the server. -// At signon, the values of all such ConVars are sent from the server to the client (skipped for local -// client, of course ) -// If a change is requested it must come from the console (i.e., no remote client changes) -// If a value is changed while a server is active, it's replicated to all connected clients -#define FCVAR_REPLICATED (1<<13) // server setting enforced on clients, TODO rename to FCAR_SERVER at some time -#define FCVAR_CHEAT (1<<14) // Only useable in singleplayer / debug / multiplayer & sv_cheats -#define FCVAR_SS (1<<15) // causes varnameN where N == 2 through max splitscreen slots for mod to be autogenerated -#define FCVAR_DEMO (1<<16) // record this cvar when starting a demo file -#define FCVAR_DONTRECORD (1<<17) // don't record these command in demofiles -#define FCVAR_SS_ADDED (1<<18) // This is one of the "added" FCVAR_SS variables for the splitscreen players -#define FCVAR_RELEASE (1<<19) // Cvars tagged with this are the only cvars avaliable to customers -#define FCVAR_RELOAD_MATERIALS (1<<20) // If this cvar changes, it forces a material reload -#define FCVAR_RELOAD_TEXTURES (1<<21) // If this cvar changes, if forces a texture reload - -#define FCVAR_NOT_CONNECTED (1<<22) // cvar cannot be changed by a client that is connected to a server -#define FCVAR_MATERIAL_SYSTEM_THREAD (1<<23) // Indicates this cvar is read from the material system thread -#define FCVAR_ARCHIVE_GAMECONSOLE (1<<24) // cvar written to config.cfg on the Xbox - -#define FCVAR_SERVER_CAN_EXECUTE (1<<28)// the server is allowed to execute this command on clients via ClientCommand/NET_StringCmd/CBaseClientState::ProcessStringCmd. -#define FCVAR_SERVER_CANNOT_QUERY (1<<29)// If this is set, then the server is not allowed to query this cvar's value (via IServerPluginHelpers::StartQueryCvarValue). -#define FCVAR_CLIENTCMD_CAN_EXECUTE (1<<30) // IVEngineClient::ClientCmd is allowed to execute this command. - -#define MAX_PLAYERS 128 // Max R5 players. - -/// \ingroup error -#define ORIGIN_ERROR 0xA0000000 ///< Add this to your error code to get an error. Bit 29 is set to distinguish Origin error codes from system error codes. - -/// \ingroup error -#define ORIGIN_WARNING 0x40000000 ///< Add this to your warning code to get a warning. Bit 29 is set to distinguish Origin warning codes from system warnings codes. - -/// \ingroup error -#define ORIGIN_ERROR_AREA_GENERAL 0x00000000 ///< Add this to your error to get a general error. - -/// \ingroup error -#define ORIGIN_ERROR_AREA_SDK (1<<16) ///< Add this to your error to get an SDK error. - -/// \ingroup error -#define ORIGIN_ERROR_AREA_CORE (2<<16) ///< Add this to your error to get a core error. - -/// \ingroup error -#define ORIGIN_ERROR_AREA_IGO (3<<16) ///< Add this to your error to get an IGO error. - -/// \ingroup error -#define ORIGIN_ERROR_AREA_FRIENDS (4<<16) ///< Add this to your error to get a friends error. - -/// \ingroup error -#define ORIGIN_ERROR_AREA_PRESENCE (5<<16) ///< Add this to your error to get a presence error. - -/// \ingroup error -#define ORIGIN_ERROR_AREA_COMMERCE (6<<16) ///< Add this to your error to get a commerce error. - -/// \ingroup error -#define ORIGIN_ERROR_AREA_ACHIEVEMENTS (7<<16) ///< Add this to your error to get a achievement error. - -/// \ingroup error -#define ORIGIN_ERROR_AREA_LSX (8<<16) ///< Add this to your error to get an LSX error. - -/// \ingroup error -#define ORIGIN_ERROR_AREA_PROXY (9<<16) ///< Add this to your error to get an Origin Proxy error. - - -/// \ingroup error -#define ORIGIN_ERROR_LEVEL_SHIFT 24 ///< [A description is required for this define] - -/// \ingroup error -#define ORIGIN_ERROR_LEVEL_MASK 0x0F000000 ///< The error level mask. - -/// \ingroup error -#define ORIGIN_LEVEL_0 (0<<24) ///< A severe error. - -/// \ingroup error -#define ORIGIN_LEVEL_1 (1<<24) ///< A major error. - -/// \ingroup error -#define ORIGIN_LEVEL_2 (2<<24) ///< A minor error. - -/// \ingroup error -#define ORIGIN_LEVEL_3 (3<<24) ///< A trivial error. - -/// \ingroup error -#define ORIGIN_LEVEL_4 (4<<24) ///< Every error. - -/// @} - -#ifndef DOXYGEN_SHOULD_SKIP_THIS - -/// $errors - -#endif /* DOXYGEN_SHOULD_SKIP_THIS */ - - - -/// \name Origin Error Codes -/// These defines specify Origin-specific errors. -/// @{ - -/// \ingroup error -#define ORIGIN_SUCCESS 0 ///< The operation succeeded. - -/// \ingroup error -#define ORIGIN_PENDING 1 ///< The operation is still waiting to complete. - -// General error codes - -/// \ingroup error -#define ORIGIN_ERROR_GENERAL -1 ///< An unspecified error has occured. - -/// \ingroup error -#define ORIGIN_ERROR_INVALID_HANDLE (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_GENERAL + 0) ///< The provided handle is invalid. - -/// \ingroup error -#define ORIGIN_ERROR_OUT_OF_MEMORY (ORIGIN_ERROR + ORIGIN_LEVEL_0 + ORIGIN_ERROR_AREA_GENERAL + 1) ///< Failed to allocate memory. - -/// \ingroup error -#define ORIGIN_ERROR_NOT_IMPLEMENTED (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_GENERAL + 2) ///< The function is not implemented. - -/// \ingroup error -#define ORIGIN_ERROR_INVALID_USER (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 3) ///< The specified user is not valid in this context, or the userId is invalid. - -/// \ingroup error -#define ORIGIN_ERROR_INVALID_ARGUMENT (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 4) ///< One or more arguments are invalid. - -/// \ingroup error -#define ORIGIN_ERROR_NO_CALLBACK_SPECIFIED (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 5) ///< The asynchronous operation expected a callback, but no callback was specified. - -/// \ingroup error -#define ORIGIN_ERROR_BUFFER_TOO_SMALL (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 6) ///< The provided buffer doesn't have enough space to contain the requested data. - -/// \ingroup error -#define ORIGIN_ERROR_TOO_MANY_VALUES_IN_LIST (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 7) ///< We are currently only supporting one item in the list. - -/// \ingroup error -#define ORIGIN_ERROR_NOT_FOUND (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 8) ///< The requested item was not found. - -/// \ingroup error -#define ORIGIN_ERROR_INVALID_PERSONA (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 9) ///< The specified persona is not valid in this context, or the personaId is invalid. - -/// \ingroup error -#define ORIGIN_ERROR_NO_NETWORK (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 10) ///< No internet connection available. - -/// \ingroup error -#define ORIGIN_ERROR_NO_SERVICE (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 11) ///< Origin services are unavailable. - -/// \ingroup error -#define ORIGIN_ERROR_NOT_LOGGED_IN (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 12) ///< The user isn't logged in. No valid session is available. - -/// \ingroup error -#define ORIGIN_ERROR_MANDATORY_ORIGIN_UPDATE_PENDING (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 13) ///< There is a mandatory update pending for Origin, this will prevent origin from going online. - -/// \ingroup error -#define ORIGIN_ERROR_ACCOUNT_IN_USE (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 14) ///< The account is currently in use by another Origin instance. - -/// \ingroup error -#define ORIGIN_ERROR_TOO_MANY_INSTANCES (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_GENERAL + 15) ///< Too many instances of the OriginSDK created. - -/// \ingroup error -#define ORIGIN_ERROR_ALREADY_EXISTS (ORIGIN_ERROR + ORIGIN_LEVEL_3 + ORIGIN_ERROR_AREA_GENERAL + 16) ///< The item already exists in the list. - -/// \ingroup error -#define ORIGIN_ERROR_INVALID_OPERATION (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 17) ///< The requested operation cannot be performed. - -/// \ingroup error -#define ORIGIN_ERROR_AGE_RESTRICTED (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 18) ///< The item has age restrictions. - -/// \ingroup error -#define ORIGIN_ERROR_BANNED (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 19) ///< The user is banned. - -/// \ingroup error -#define ORIGIN_ERROR_NOT_READY (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 20) ///< The item is not ready. - -/// @} - -/// \name SDK Error Codes -/// These defines specify Origin SDK-specific errors. -/// @{ - -// Sdk error codes - -/// \ingroup error -#define ORIGIN_ERROR_SDK_NOT_INITIALIZED (ORIGIN_ERROR + ORIGIN_LEVEL_0 + ORIGIN_ERROR_AREA_SDK + 0) ///< The Origin SDK was not running. - -/// \ingroup error -#define ORIGIN_ERROR_SDK_INVALID_ALLOCATOR_DEALLOCATOR_COMBINATION (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_SDK + 1) ///< Make sure that you provide both an allocator and a deallocator. - -/// \ingroup error -#define ORIGIN_ERROR_SDK_IS_RUNNING (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_SDK + 2) ///< The Origin SDK is running. This operation should only be done before the SDK is initialized or after the SDK is shutdown. - -/// \ingroup error -#define ORIGIN_ERROR_SDK_NOT_ALL_RESOURCES_RELEASED (ORIGIN_ERROR + ORIGIN_LEVEL_3 + ORIGIN_ERROR_AREA_SDK + 3) ///< The game is still holding on to the resource handles. Call #OriginDestroyHandle on resources that are no longer needed. - -/// \ingroup error -#define ORIGIN_ERROR_SDK_INVALID_RESOURCE (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_SDK + 4) ///< The resource in the resource map is invalid. Memory corruption? - -/// \ingroup error -#define ORIGIN_ERROR_SDK_INTERNAL_ERROR (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_SDK + 5) ///< The SDK experienced an internal error. - -/// \ingroup error -#define ORIGIN_ERROR_SDK_INTERNAL_BUFFER_TOO_SMALL (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_SDK + 6) ///< The internal buffer that the SDK is using is not big enough to receive the response. Inform OriginSDK Support. - -/// @} - -/// \name SDK Warning Codes -/// These defines specify Origin SDK-specific warnings. -/// @{ - -/// \ingroup error -#define ORIGIN_WARNING_SDK_ALREADY_INITIALIZED (ORIGIN_WARNING + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_SDK + 1) ///< The Origin SDK is already initialized. - -/// \ingroup error -#define ORIGIN_WARNING_SDK_STILL_RUNNING (ORIGIN_WARNING + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_SDK + 2) ///< The Origin SDK is still running. - -/// \ingroup error -#define ORIGIN_WARNING_SDK_ENUMERATOR_IN_USE (ORIGIN_WARNING + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_SDK + 3) ///< The Enumerator associated with the handle was in use. - -/// \ingroup error -#define ORIGIN_WARNING_SDK_ENUMERATOR_TERMINATED (ORIGIN_WARNING + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_SDK + 4) ///< The Enumerator associated with the handle was not finished. - -/// @} - -/// \name Core Error Codes -/// These defines specify Origin Core-specific errors. -/// @{ - -/// \ingroup error -#define ORIGIN_ERROR_CORE_NOTLOADED (ORIGIN_ERROR + ORIGIN_LEVEL_0 + ORIGIN_ERROR_AREA_CORE + 0) ///< The Origin desktop application is not loaded. - -/// \ingroup error -#define ORIGIN_ERROR_CORE_LOGIN_FAILED (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_CORE + 1) ///< Origin couldn't authenticate with the Origin Servers. - -/// \ingroup error -#define ORIGIN_ERROR_CORE_AUTHENTICATION_FAILED (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_CORE + 2) ///< Origin seems to be running, but the LSX Authentication Challenge failed. No communication with Core is possible. - -/// \ingroup error -#define ORIGIN_ERROR_CORE_SEND_FAILED (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_CORE + 4) ///< Sending data to Origin failed. - -/// \ingroup error -#define ORIGIN_ERROR_CORE_RECEIVE_FAILED (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_CORE + 5) ///< Receiving data from Origin failed. - -/// \ingroup error -#define ORIGIN_ERROR_CORE_RESOURCE_NOT_FOUND (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_CORE + 6) ///< The requested resource could not be located. - -/// \ingroup error -#define ORIGIN_ERROR_CORE_INCOMPATIBLE_VERSION (ORIGIN_ERROR + ORIGIN_LEVEL_0 + ORIGIN_ERROR_AREA_CORE + 7) ///< The Origin version is too old to work with this SDK version. - -/// \ingroup error -#define ORIGIN_ERROR_CORE_NOT_INSTALLED (ORIGIN_ERROR + ORIGIN_LEVEL_0 + ORIGIN_ERROR_AREA_CORE + 8) ///< The Origin installation couldn't be found. - -/// @} - -/// \name IGO Error and Warning Codes -/// These defines specify In-game Overlay errors and warnings. -/// @{ - -/// \ingroup error -#define ORIGIN_WARNING_IGO_NOTLOADED (ORIGIN_WARNING + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_IGO + 0) ///< The IGO could not be loaded, so SDK functionality is degraded. - -/// \ingroup error -#define ORIGIN_WARNING_IGO_SUPPORT_NOTLOADED (ORIGIN_WARNING + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_IGO + 1) ///< IGO support is not loaded, so SDK functionality is degraded. - -/// \ingroup error -#define ORIGIN_ERROR_IGO_ILLEGAL_ANCHOR_POINT (ORIGIN_ERROR + ORIGIN_LEVEL_3 + ORIGIN_ERROR_AREA_IGO + 2) ///< The combination of anchor point bits doesn't resolve to a proper dialog anchor point. - -/// \ingroup error -#define ORIGIN_ERROR_IGO_ILLEGAL_DOCK_POINT (ORIGIN_ERROR + ORIGIN_LEVEL_3 + ORIGIN_ERROR_AREA_IGO + 3) ///< The combination of dock point bits doesn't resolve to a proper dock point. - -/// \ingroup error -#define ORIGIN_ERROR_IGO_NOT_AVAILABLE (ORIGIN_ERROR + ORIGIN_LEVEL_3 + ORIGIN_ERROR_AREA_IGO + 4) ///< The IGO is not available. - -/// @} - -/// \name Presence Error Codes -/// These defines specify Origin Presence errors. -/// @{ - -/// \ingroup error -#define ORIGIN_ERROR_NO_MULTIPLAYER_ID (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PRESENCE + 0) ///< It is not possible to set the presence to JOINABLE when no multiplayer Id is defined on the offer. - -/// @} - -/// \name Friends Error Codes -/// These defines specify Origin Friends errors. -/// @{ - -/// \ingroup error -#define ORIGIN_ERROR_LSX_INVALID_RESPONSE (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_LSX + 0) ///< The LSX Decoder didn't expect this response. - -/// \ingroup error -#define ORIGIN_ERROR_LSX_NO_RESPONSE (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_LSX + 1) ///< The LSX server didn't respond within the set timeout. - -/// \ingroup error -#define ORIGIN_ERROR_LSX_INVALID_REQUEST (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_LSX + 2) ///< The LSX Decoder didn't expect this request. - -/// @} - -/// \name Commerce Error Codes -/// These defines specify Origin Commerce errors. -/// @{ - -/// \ingroup error -#define ORIGIN_ERROR_COMMERCE_NO_SUCH_STORE (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_COMMERCE + 0) ///< The store could not be found. - -/// \ingroup error -#define ORIGIN_ERROR_COMMERCE_NO_SUCH_CATALOG (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_COMMERCE + 1) ///< The catalog could not be found. - -/// \ingroup error -#define ORIGIN_ERROR_COMMERCE_INVALID_REPLY (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_COMMERCE + 2) ///< The server reply was not understood. - -/// \ingroup error -#define ORIGIN_ERROR_COMMERCE_NO_CATEGORIES (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_COMMERCE + 3) ///< No categories were found. - -/// \ingroup error -#define ORIGIN_ERROR_COMMERCE_NO_PRODUCTS (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_COMMERCE + 4) ///< No products were found. - -/// \ingroup error -#define ORIGIN_ERROR_COMMERCE_UNDERAGE_USER (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_COMMERCE + 5) ///< The user is under age and is blocked to perform this action. - -/// \ingroup error -#define ORIGIN_ERROR_COMMERCE_DEPRECATED_STORE (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_COMMERCE + 6) ///< The user's OS is deprecated and is blocked to perform this action. - - -/// @} - - -/// \name Origin Proxy Error Codes. -/// These defines specify Origin Proxy errors. -/// @{ -/// \ingroup error -#define ORIGIN_ERROR_PROXY (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 0) ///< Base proxy error. You shouldn't get this error. - -/// \ingroup error -#define ORIGIN_SUCCESS_PROXY_OK (ORIGIN_WARNING + ORIGIN_LEVEL_4 + ORIGIN_ERROR_AREA_PROXY + 200) ///< Server success: OK. - -/// \ingroup error -#define ORIGIN_SUCCESS_PROXY_CREATED (ORIGIN_WARNING + ORIGIN_LEVEL_4 + ORIGIN_ERROR_AREA_PROXY + 201) ///< Server success: Created. - -/// \ingroup error -#define ORIGIN_SUCCESS_PROXY_ACCEPTED (ORIGIN_WARNING + ORIGIN_LEVEL_4 + ORIGIN_ERROR_AREA_PROXY + 202) ///< Server success: Accepted. - -/// \ingroup error -#define ORIGIN_SUCCESS_PROXY_NON_AUTH_INFO (ORIGIN_WARNING + ORIGIN_LEVEL_4 + ORIGIN_ERROR_AREA_PROXY + 203) ///< Server success: Non-Authoritative Information. - -/// \ingroup error -#define ORIGIN_SUCCESS_PROXY_NO_CONTENT (ORIGIN_WARNING + ORIGIN_LEVEL_4 + ORIGIN_ERROR_AREA_PROXY + 204) ///< Server success: No Content. - -/// \ingroup error -#define ORIGIN_SUCCESS_RESET_CONTENT (ORIGIN_WARNING + ORIGIN_LEVEL_4 + ORIGIN_ERROR_AREA_PROXY + 205) ///< Server success: Reset Content. - -/// \ingroup error -#define ORIGIN_SUCCESS_PARTIAL_CONTENT (ORIGIN_WARNING + ORIGIN_LEVEL_4 + ORIGIN_ERROR_AREA_PROXY + 206) ///< Server success: Partial Content. - -/// \ingroup error -#define ORIGIN_ERROR_PROXY_BAD_REQUEST (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 400) ///< Server error: Bad Request - -/// \ingroup error -#define ORIGIN_ERROR_PROXY_UNAUTHORIZED (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 401) ///< Server error: Unauthorized. - -/// \ingroup error -#define ORIGIN_ERROR_PROXY_PAYMENT_REQUIRED (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 402) ///< Server error: Payment Required. - -/// \ingroup error -#define ORIGIN_ERROR_PROXY_FORBIDDEN (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 403) ///< Server error: Forbidden. - -/// \ingroup error -#define ORIGIN_ERROR_PROXY_NOT_FOUND (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 404) ///< Server error: Not found. - -/// \ingroup error -#define ORIGIN_ERROR_PROXY_METHOD_NOT_ALLOWED (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 405) ///< Server error: Method not Allowed. - -/// \ingroup error -#define ORIGIN_ERROR_PROXY_NOT_ACCEPTABLE (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 406) ///< Server error: Not Acceptable. - -/// \ingroup error -#define ORIGIN_ERROR_PROXY_REQUEST_TIMEOUT (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 408) ///< Server error: Request Timeout. - -/// \ingroup error -#define ORIGIN_ERROR_PROXY_CONFLICT (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 409) ///< Server error: Conflict. - -/// \ingroup error -#define ORIGIN_ERROR_PROXY_INTERNAL_ERROR (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 500) ///< Server error: Internal Server Error. - -/// \ingroup error -#define ORIGIN_ERROR_PROXY_NOT_IMPLEMENTED (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 501) ///< Server error: Not Implemented. - -/// \ingroup error -#define ORIGIN_ERROR_PROXY_BAD_GATEWAY (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 502) ///< Server error: Bad Gateway. - -/// \ingroup error -#define ORIGIN_ERROR_PROXY_SERVICE_UNAVAILABLE (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 503) ///< Server error: Service Unavailable. - -/// \ingroup error -#define ORIGIN_ERROR_PROXY_GATEWAY_TIMEOUT (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 504) ///< Server error: Gateway Timeout. diff --git a/r5dev/include/gameclasses.h b/r5dev/include/gameclasses.h deleted file mode 100644 index 61c39cd4..00000000 --- a/r5dev/include/gameclasses.h +++ /dev/null @@ -1,521 +0,0 @@ -#pragma once -#include "patterns.h" -#include "banlist.h" - -///////////////////////////////////////////////////////////////////////////// -// Classes and Structs - -class CInputSystem -{ -public: - void EnableInput(bool bEnabled)// @0x14039F100 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void(__thiscall*)(CInputSystem*, bool); - (*reinterpret_cast(this))[10](this, bEnabled); - } - - void EnableMessagePump(bool bEnabled) // @0x14039F110 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void(__thiscall*)(CInputSystem*, bool); - (*reinterpret_cast(this))[11](this, bEnabled); - } - - bool IsButtonDown(ButtonCode_t Button) // @0x1403A0140 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = bool(__thiscall*)(CInputSystem*, ButtonCode_t); - return (*reinterpret_cast(this))[13](this, Button); - } - -private: - char pad_0000[16]; //0x0000 -public: - bool m_bEnabled; //0x0010 IsInputEnabled variable. - bool m_bPumpEnabled; //0x0011 EnabledMessagePump variable. -}; - -typedef int HKeySymbol; - -#define MAKE_3_BYTES_FROM_1_AND_2( x1, x2 ) (( (( std::uint16_t )x2) << 8 ) | (std::uint8_t)(x1)) - -class CKeyValuesSystem // VTABLE @ 0x1413AA1E8 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM -{ -public: - - void RegisterSizeofKeyValues(std::int64_t size) //@0x1413AA1F0 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void(__thiscall*)(CKeyValuesSystem*, std::int64_t); - (*reinterpret_cast(this))[0](this, size); - } - - void* AllocKeyValuesMemory(std::int64_t size) // @0x1413AA1F8 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void* (__thiscall*)(CKeyValuesSystem*, std::int64_t); - return (*reinterpret_cast(this))[1](this, size); - } - - void FreeKeyValuesMemory(void* pMem) // @0x1413AA200 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void(__thiscall*)(CKeyValuesSystem*, void*); - (*reinterpret_cast(this))[2](this, pMem); - } - - HKeySymbol GetSymbolForString(const char* name, bool bCreate) // @0x1413AA208 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = HKeySymbol(__thiscall*)(CKeyValuesSystem*, const char*, bool); - return (*reinterpret_cast(this))[3](this, name, bCreate); - } - - const char* GetStringForSymbol(HKeySymbol symbol) // @0x1413AA210 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = const char* (__thiscall*)(CKeyValuesSystem*, HKeySymbol); - return (*reinterpret_cast(this))[4](this, symbol); - } - - // void __fastcall CKeyValuesSystem::FreeKeyValuesMemory(CKeyValuesSystem* this_arg, void* ptr_mem_arg) - // { - // __int64* v2; // rax - // __int64 v4; // rax - // __int64* v5; // rax - // - // v2 = qword_14D40B538; - // if (!qword_14D40B538) - // { - // v2 = sub_140462930(); - // qword_14D40B538 = v2; - // } - // v4 = (*(*v2 + 48))(v2, ptr_mem_arg); - // if (v4 > 0) - // CKeyValuesSystem::m_pMemPool -= v4; - // v5 = qword_14D40B538; - // if (!qword_14D40B538) - // { - // v5 = sub_140462930(); - // qword_14D40B538 = v5; - // } - // (*(*v5 + 40))(v5, ptr_mem_arg); - // } - - // GetMemPool return a global variable called m_pMemPool it gets modified by AllocKeyValuesMemory and FreeKeyValuesMemory above you can see where the find it in FreeKeyValuesMemory. - void* GetMemPool() // @0x1413AA228 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - return reinterpret_cast(0x14D412768); // May need to dereference is once more not sure right now. - } - - void SetKeyValuesExpressionSymbol(const char* name, bool bValue) // @0x1413AA230 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void(__thiscall*)(CKeyValuesSystem*, const char*, bool); - (*reinterpret_cast(this))[8](this, name, bValue); - } - - bool GetKeyValuesExpressionSymbol(const char* name) // @0x1413AA238 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = bool(__thiscall*)(CKeyValuesSystem*, const char*); - return (*reinterpret_cast(this))[9](this, name); - } - - HKeySymbol GetSymbolForStringCaseSensitive(HKeySymbol& hCaseInsensitiveSymbol, const char* name, bool bCreate) // @0x1413AA240 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = HKeySymbol(__thiscall*)(CKeyValuesSystem*, HKeySymbol&, const char*, bool); - return (*reinterpret_cast(this))[10](this, hCaseInsensitiveSymbol, name, bCreate); - } - - // Datatypes aren't accurate. But full fill the actual byte distance. -public: - void* vtable; // 0x0000 - std::int64_t m_iMaxKeyValuesSize; // 0x0008 -private: - char gap10[240]; // 0x0010 -public: - int m_KvConditionalSymbolTable; // 0x0100 -private: - char gap104[4]; // 0x0104 -public: - std::int64_t field_108; // 0x0108 -private: - char gap110[32]; // 0x0110 -public: - int m_mutex; // 0x0130 -}; - -class KeyValues -{ -public: - - KeyValues* FindKey(const char* keyName, bool bCreate) - { - static auto func = reinterpret_cast(addr_KeyValues_FindKey); - return func(this, keyName, bCreate); - } - - const char* GetName(); - - int GetInt(const char* keyName, int defaultValue) - { - KeyValues* dat = FindKey(keyName, false); - - if (!dat) - return defaultValue; - - switch (dat->m_iDataType) - { - case TYPE_STRING: - return atoi(dat->m_sValue); - case TYPE_FLOAT: - return static_cast(m_flValue()); - case TYPE_WSTRING: - return _wtoi(dat->m_wsValue); - case TYPE_UINT64: - return 0; - default: - return dat->m_iValue(); - } - - return defaultValue; - } - - void SetInt(const char* keyName, int iValue) - { - KeyValues* dat = FindKey(keyName, true); - if (dat) - { - dat->m_iValue() = iValue; - dat->m_iDataType = TYPE_INT; - } - } - - void SetFloat(const char* keyName, float flValue) - { - KeyValues* dat = FindKey(keyName, true); - if (dat) - { - dat->m_flValue() = flValue; - dat->m_iDataType = TYPE_FLOAT; - } - } - - // Compiler makes it so m_Value shares the offset spot with m_flValue that why we cast it like this. - float& m_flValue() - { - static __int32 offset = 0x18; - return *(float*)((std::uintptr_t)this + offset); - } - - int& m_iValue() - { - static __int32 offset = 0x18; - return *(int*)((std::uintptr_t)this + offset); - } - -public: - unsigned __int32 m_iKeyName : 24; // 0x0000 - unsigned __int32 m_iKeyNameCaseSensitive : 8; // 0x0003 - char* m_sValue; // 0x0008 - wchar_t* m_wsValue; // 0x0010 - int m_Value; // 0x0018 -private: - char gap1C[12]; // 0x0020 -public: - char m_iDataType; // 0x0028 - unsigned __int16 m_iKeyNameCaseSensitive2; // 0x002A - KeyValues* m_pPeer; // 0x0030 - KeyValues* m_pSub; // 0x0038 - KeyValues* m_pChain; // 0x0040 -}; - -struct Vector3 // Implement the proper class of this at some point. -{ - float x; // 0x0000 - float y; // 0x0004 - float z; // 0x0008 -}; - -struct QAngle // Implement the proper class of this at some point. -{ - float pitch; //0x0000 - float yaw; // 0x0004 - float roll; // 0x0008 -}; - -class CHostState -{ -public: - HostStates_t m_iCurrentState; //0x0000 - HostStates_t m_iNextState; //0x0004 - Vector3 m_vecLocation; //0x0008 - QAngle m_angLocation; //0x0014 - char m_levelName[64]; //0x0020 - char m_mapGroupName[256]; //0x0060 - char m_landMarkName[256]; //0x0160 - float m_flShortFrameTime; //0x0260 - bool m_bActiveGame; //0x0264 - bool m_bRememberLocation; //0x0265 - bool m_bBackgroundLevel; //0x0266 - bool m_bWaitingForConnection; //0x0267 - bool m_bSplitScreenConnect; //0x0268 - bool m_bGameHasShutDownAndFlushedMemory; //0x0269 - bool m_bWorkshopMapDownloadPending; //0x026A -}; - -class CHLClient -{ -public: - void FrameStageNotify(ClientFrameStage_t curStage) // @0x1405C0740 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void(__thiscall*)(CHLClient*, ClientFrameStage_t); - (*reinterpret_cast(this))[58](this, curStage); /* 48 83 EC 28 89 15 ? ? ? ? */ - } -}; - -class CClient -{ -public: - inline CClient* GetClientInstance(int index) - { - return (CClient*)(std::uintptr_t)(0x16073B200 + (index * (std::uintptr_t)0x4A4C0)); - } - - void*& GetNetChan() - { - return m_nNetChannel; - } -private: - char pad_0000[16]; //0x0000 -public: - int m_iUserID; //0x0010 -private: - char pad_0014[908]; //0x0014 -public: - void* m_nNetChannel; //0x03A0 -private: - char pad_03A8[8]; //0x03A8 -public: - int m_iSignonstate; //0x03B0 -private: - char pad_03B4[4]; //0x03B4 -public: - std::int64_t m_iOriginID; //0x03B8 -private: - char pad_03C0[303360]; //0x03C0 -}; - -class CCommand -{ -private: - enum - { - COMMAND_MAX_ARGC = 64, - COMMAND_MAX_LENGTH = 512, - }; - -public: - CCommand() = delete; - - inline int MaxCommandLength() - { - return COMMAND_MAX_LENGTH - 1; - } - - inline std::int64_t ArgC() const - { - return m_nArgc; - } - - inline const char** ArgV() const - { - return m_nArgc ? (const char**)m_ppArgv : NULL; - } - - inline const char* ArgS() const - { - return m_nArgv0Size ? &m_pArgSBuffer[m_nArgv0Size] : ""; - } - - inline const char* GetCommandString() const - { - return m_nArgc ? m_pArgSBuffer : ""; - } - - inline const char* Arg(int nIndex) const - { - // FIXME: Many command handlers appear to not be particularly careful - // about checking for valid argc range. For now, we're going to - // do the extra check and return an empty string if it's out of range - if (nIndex < 0 || nIndex >= m_nArgc) - return ""; - return m_ppArgv[nIndex]; - } - - inline const char* operator[](int nIndex) const - { - return Arg(nIndex); - } - -private: - std::int64_t m_nArgc; - std::int64_t m_nArgv0Size; - char m_pArgSBuffer[COMMAND_MAX_LENGTH]; - char m_pArgvBuffer[COMMAND_MAX_LENGTH]; - const char* m_ppArgv[COMMAND_MAX_ARGC]; -}; - -class ConCommandBase -{ -public: - void* m_pConCommandBaseVTable; //0x0000 - ConCommandBase* m_pNext; //0x0008 - bool m_bRegistered; //0x0010 -private: - char pad_0011[7]; //0x0011 -public: - const char* m_pszName; //0x0018 - const char* m_pszHelpString; //0x0020 -private: - char pad_0028[16]; //0x0028 -public: - int m_nFlags; //0x0038 -private: - char pad_003C[4]; //0x003C -}; //Size: 0x0038 - -class ConVar -{ -public: - ConCommandBase m_ConCommandBase; // 0x0000 - void* m_pConVarVTable; //0x0040 - ConVar* m_pParent; //0x0048 - const char* n_pszDefaultValue; //0x0050 - const char* m_pzsCurrentValue; //0x0058 - std::int64_t m_iStringLength; //0x0060 - float m_flValue; //0x0068 - int m_iValue; //0x006C - bool m_bHasMin; //0x0070 - float m_flMinValue; //0x0074 - bool m_bHasMax; //0x0078 - float m_flMaxValue; //0x007C - char pad_0080[32]; //0x0080 -}; //Size: 0x00A0 - -class CCVarIteratorInternal // Fully reversed table, just look at the virtual function table and rename the function. -{ -public: - virtual void SetFirst(void) = 0; //0 - virtual void Next(void) = 0; //1 - virtual bool IsValid(void) = 0; //2 - virtual ConCommandBase* Get(void) = 0; //3 -}; - -class CCVar -{ -public: - ConCommandBase* FindCommandBase(const char* szCommandName) // @0x1405983A0 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = ConCommandBase*(__thiscall*)(CCVar*, const char*); - return (*reinterpret_cast(this))[14](this, szCommandName); - } - - ConVar* FindVar(const char* szVarName) // @0x1405983B0 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = ConVar*(__thiscall*)(CCVar*, const char*); - return (*reinterpret_cast(this))[16](this, szVarName); - } - - void* /*Implement ConCommand class.*/ FindCommand(const char* szCommandName) // @0x1405983F0 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void*(__thiscall*)(CCVar*, const char*); - return (*reinterpret_cast(this))[18](this, szCommandName); - } - - CCVarIteratorInternal* FactoryInternalIterator() // @0x140597C10 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = CCVarIteratorInternal*(__thiscall*)(CCVar*); - return (*reinterpret_cast(this))[41](this); - } - - std::unordered_map DumpToMap() - { - std::stringstream ss; - CCVarIteratorInternal* itint = FactoryInternalIterator(); // Allocatd new InternalIterator. - - std::unordered_map allConVars; - - for (itint->SetFirst(); itint->IsValid(); itint->Next()) // Loop through all instances. - { - ConCommandBase* command = itint->Get(); - const char* commandName = command->m_pszName; - allConVars[commandName] = command; - } - - return allConVars; - } -}; - -struct Interface -{ - std::int64_t(*InterfacePtr)(void); - const char* InterfaceName; - std::int64_t* NextInterfacePtr; -}; - -struct SQFuncRegistration -{ - const char* scriptName; // 00 - const char* nativeName; // 08 - const char* helpString; // 10 - const char* retValType; // 18 - const char* argTypes; // 20 - std::int16_t unk28; // 28 - std::int16_t padding1; // 2A - std::int32_t unk2c; // 2C - std::int64_t unk30; // 30 - std::int32_t unk38; // 38 - std::int32_t padding2; // 3C - std::int64_t unk40; // 40 - std::int64_t unk48; // 48 - std::int64_t unk50; // 50 - std::int32_t unk58; // 58 - std::int32_t padding3; // 5C - void* funcPtr; // 60 - - SQFuncRegistration() - { - memset(this, 0, sizeof(SQFuncRegistration)); - this->padding2 = 6; - } -}; - -///////////////////////////////////////////////////////////////////////////// -// Initialize Game Globals - -namespace GameGlobals -{ - // Class Instances - extern CHostState* HostState; - extern CInputSystem* InputSystem; - extern CCVar* Cvar; - extern KeyValues** PlaylistKeyValues; - extern CKeyValuesSystem* KeyValuesSystem; - extern CClient* Client; - extern BanList* BanSystem; - - // Var - ConVar* CreateCustomConVar(const char* name, const char* defaultValue, int flags, const char* helpString, bool bMin, float fMin, bool bMax, float fMax, void* callback, void* unk); - void* CreateCustomConCommand(const char* name, const char* helpString, int flags, void* callback, void* callbackAfterExecution); - - // Script - void Script_RegisterFunction(void* sqvm, const char* name, const char* helpString, const char* retValType, const char* argTypes, void* funcPtr); - void RegisterUIScriptFunctions(void* sqvm); - void RegisterClientScriptFunctions(void* sqvm); - void RegisterServerScriptFunctions(void* sqvm); - - // Init - void InitGameGlobals(); - void InitAllCommandVariations(); - void InitPlaylist(); - - - extern std::vector allPlaylists; - extern bool IsInitialized; - - // Utility - void DisconnectClient(CClient* client, const char* reason, unsigned __int8 unk1, char unk2); -} \ No newline at end of file diff --git a/r5dev/include/gui_utility.h b/r5dev/include/gui_utility.h deleted file mode 100644 index 2f1db191..00000000 --- a/r5dev/include/gui_utility.h +++ /dev/null @@ -1,88 +0,0 @@ -#pragma once - -///////////////////////////////////////////////////////////////////////////// -// Internals -int Stricmp(const char* s1, const char* s2); -int Strnicmp(const char* s1, const char* s2, int n); -char* Strdup(const char* s); -void Strtrim(char* s); - -class GuiConfig -{ -public: - struct - { - int bind1 = VK_OEM_3; - int bind2 = VK_INSERT; - int autoClearLimit = 300; - bool autoClear = true; - bool printCmd = false; - } CGameConsoleConfig; - - struct - { - int bind1 = VK_HOME; - int bind2 = VK_F10; - } CCompanionConfig; - - void Load() - { - spdlog::debug("Loading the Gui Config..\n"); - std::filesystem::path path = std::filesystem::current_path() /= "gui.config"; // Get current path + gui.config - - nlohmann::json in; - try - { - std::ifstream configFile(path, std::ios::binary); // Parse config file. - configFile >> in; - configFile.close(); - - if (!in.is_null()) - { - if (!in["config"].is_null()) - { - // CGameConsole - CGameConsoleConfig.bind1 = in["config"]["CGameConsole"]["bind1"].get(); - CGameConsoleConfig.bind2 = in["config"]["CGameConsole"]["bind2"].get(); - CGameConsoleConfig.autoClearLimit = in["config"]["CGameConsole"]["autoClearLimit"].get(); - CGameConsoleConfig.autoClear = in["config"]["CGameConsole"]["autoClear"].get(); - CGameConsoleConfig.printCmd = in["config"]["CGameConsole"]["printCmd"].get(); - - // CCompanion - CCompanionConfig.bind1 = in["config"]["CCompanion"]["bind1"].get(); - CCompanionConfig.bind2 = in["config"]["CCompanion"]["bind2"].get(); - } - } - } - catch (const std::exception& ex) - { - spdlog::critical("Gui Config loading failed. Perhaps re-create it by messing with Options in CGameConsole. Reason: {}\n", ex.what()); - return; - } - } - - void Save() - { - nlohmann::json out; - - // CGameConsole - out["config"]["CGameConsole"]["bind1"] = CGameConsoleConfig.bind1; - out["config"]["CGameConsole"]["bind2"] = CGameConsoleConfig.bind2; - out["config"]["CGameConsole"]["autoClearLimit"] = CGameConsoleConfig.autoClearLimit; - out["config"]["CGameConsole"]["autoClear"] = CGameConsoleConfig.autoClear; - out["config"]["CGameConsole"]["printCmd"] = CGameConsoleConfig.printCmd; - - // CCompanion - out["config"]["CCompanion"]["bind1"] = CCompanionConfig.bind1; - out["config"]["CCompanion"]["bind2"] = CCompanionConfig.bind2; - - std::filesystem::path path = std::filesystem::current_path() /= "gui.config"; // Get current path + gui.config - std::ofstream outFile(path, std::ios::out | std::ios::trunc); // Write config file.. - - outFile << out.dump(4); // Dump it into config file.. - - outFile.close(); // Close the file handle. - }; -}; - -extern GuiConfig g_GuiConfig; diff --git a/r5dev/include/hooks.h b/r5dev/include/hooks.h deleted file mode 100644 index a6f7f1a2..00000000 --- a/r5dev/include/hooks.h +++ /dev/null @@ -1,155 +0,0 @@ -#pragma once -#include "patterns.h" -#include "structs.h" -#include "hooks.h" -#include "gameclasses.h" -#include "CCompanion.h" -#include "CGameConsole.h" - -inline bool g_bDebugLoading = false; -inline bool g_bReturnAllFalse = false; -inline bool g_bDebugConsole = false; -extern bool g_bBlockInput; - -namespace Hooks -{ -#pragma region CHLClient - void __fastcall FrameStageNotify(CHLClient* rcx, ClientFrameStage_t curStage); - - using FrameStageNotifyFn = void(__fastcall*)(CHLClient*, ClientFrameStage_t); - extern FrameStageNotifyFn originalFrameStageNotify; -#pragma endregion - -#pragma region Squirrel - void* SQVM_Print(void* sqvm, char* fmt, ...); - __int64 SQVM_Warning(void* sqvm, int a2, int a3, int* stringSize, void** string); - __int64 SQVM_LoadRson(const char* rson_name); - bool SQVM_LoadScript(void* sqvm, const char* script_path, const char* script_name, int flag); - void SQVM_RegisterOriginFuncs(void* sqvm); - void SQVM_RegisterCreatePlayerTasklist(void* sqvm); - - using SQVM_WarningFn = __int64(*)(void*, int, int, int*, void**); - extern SQVM_WarningFn originalSQVM_Warning; - - using SQVM_LoadRsonFn = __int64(*)(const char*); - extern SQVM_LoadRsonFn originalSQVM_LoadRson; - - using SQVM_LoadScriptFn = bool(*)(void*, const char*, const char*, int); - extern SQVM_LoadScriptFn originalSQVM_LoadScript; - - using SQVM_RegisterOriginFuncsFn = void(*)(void*); - extern SQVM_RegisterOriginFuncsFn originalSQVM_RegisterOriginFuncs; - - using SQVM_RegisterCreatePlayerTasklistFn = void(*)(void*); - extern SQVM_RegisterCreatePlayerTasklistFn originalSQVM_RegisterCreatePlayerTasklist; -#pragma endregion - -#pragma region CServer - void* ConnectClient(void* thisptr, void* packet); - - using ConnectClientFn = void* (*)(void*, void*); - extern ConnectClientFn originalConnectClient; -#pragma endregion - -#pragma region CVEngineServer - bool IsPersistenceDataAvailable(__int64 thisptr, int client); - - using IsPersistenceDataAvailableFn = bool(*)(__int64, int); - extern IsPersistenceDataAvailableFn originalIsPersistenceDataAvailable; -#pragma endregion - -#pragma region NetChannel - bool NET_ReceiveDatagram(int sock, void* inpacket, bool raw); - unsigned int NET_SendDatagram(SOCKET s, const char* buf, int len, int flags); - void NET_PrintFunc(const char* fmt, ...); - void NetChan_Shutdown(void* rcx, const char* reason, unsigned __int8 unk1, char unk2); - - using NET_PrintFuncFn = void(*)(const char* fmt, ...); - extern NET_PrintFuncFn originalNET_PrintFunc; - - using NET_ReceiveDatagramFn = bool(*)(int, void*, bool); - extern NET_ReceiveDatagramFn originalNET_ReceiveDatagram; - - using NET_SendDatagramFn = unsigned int(*)(SOCKET, const char*, int, int); - extern NET_SendDatagramFn originalNET_SendDatagram; - using NetChan_ShutDown = void(*)(void*, const char*, unsigned __int8, char); - extern NetChan_ShutDown originalNetChan_ShutDown; -#pragma endregion - -#pragma region ConVar - bool ConVar_IsFlagSet(ConVar* cvar, int flag); - bool ConCommand_IsFlagSet(ConCommandBase* cmd, int flag); - - using ConVar_IsFlagSetFn = bool(*)(ConVar*, int); - extern ConVar_IsFlagSetFn originalConVar_IsFlagSet; - - using ConCommand_IsFlagSetFn = bool(*)(ConCommandBase*, int); - extern ConCommand_IsFlagSetFn originalConCommand_IsFlagSet; -#pragma endregion - -#pragma region CMatSystemSurface - void LockCursor(void* thisptr); - - using LockCursorFn = void(*)(void*); - extern LockCursorFn originalLockCursor; -#pragma endregion - -#pragma region WinAPI - BOOL WINAPI GetCursorPos(LPPOINT lpPoint); - BOOL WINAPI SetCursorPos(int X, int Y); - BOOL WINAPI ClipCursor(const RECT* lpRect); - BOOL WINAPI ShowCursor(BOOL bShow); - - using GetCursorPosFn = BOOL(WINAPI*)(LPPOINT); - extern GetCursorPosFn originalGetCursorPos; - - using SetCursorPosFn = BOOL(WINAPI*)(int, int); - extern SetCursorPosFn originalSetCursorPos; - - using ClipCursorFn = BOOL(WINAPI*)(const RECT*); - extern ClipCursorFn originalClipCursor; - - using ShowCursorFn = BOOL(WINAPI*)(BOOL); - extern ShowCursorFn originalShowCursor; -#pragma endregion - -#pragma region HostState - void FrameUpdate(void* rcx, void* rdx, float time); - - using FrameUpdateFn = void(*)(void*, void*, float); - extern FrameUpdateFn originalFrameUpdate; -#pragma endregion - -#pragma region CEngineVGui - int CEngineVGui_Paint(void* thisptr, int mode); - - using CEngineVGui_PaintFn = int(*)(void*, int); - extern CEngineVGui_PaintFn originalCEngineVGui_Paint; -#pragma endregion - -#pragma region OriginSDK - const char* OriginGetErrorDescription(std::uint32_t originCode); - - using OriginGetErrorDescriptionWrapperFn = const char* (*)(std::uint32_t); - extern OriginGetErrorDescriptionWrapperFn originalOriginGetErrorDescriptionWrapper; -#pragma endregion - -#pragma region Other - int MSG_EngineError(char* fmt, va_list args); - bool LoadPlaylist(const char* playlist); - - using MSG_EngineErrorFn = int(*)(char*, va_list); - extern MSG_EngineErrorFn originalMSG_EngineError; - - using LoadPlaylistFn = bool(*)(const char*); - extern LoadPlaylistFn originalLoadPlaylist; - -#pragma endregion - - void InstallHooks(); - void RemoveHooks(); - void ToggleNetTrace(); - extern bool bToggledNetTrace; - void ToggleDevCommands(); - extern bool bToggledDevFlags; -} \ No newline at end of file diff --git a/r5dev/include/id3dx.h b/r5dev/include/id3dx.h deleted file mode 100644 index cab22d1e..00000000 --- a/r5dev/include/id3dx.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -///////////////////////////////////////////////////////////////////////////// -// Initialization -void SetupDXSwapChain(); - -///////////////////////////////////////////////////////////////////////////// -// Internals -void PrintDXAddress(); -void InstallDXHooks(); -void RemoveDXHooks(); - -///////////////////////////////////////////////////////////////////////////// -// Handlers -extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); -extern HRESULT __stdcall Present(IDXGISwapChain* pSwapChain, UINT nSyncInterval, UINT nFlags); -extern HRESULT __stdcall GetResizeBuffers(IDXGISwapChain* pSwapChain, UINT nBufferCount, UINT nWidth, UINT nHeight, DXGI_FORMAT dxFormat, UINT nSwapChainFlags); - -///////////////////////////////////////////////////////////////////////////// -// Globals -extern DWORD g_dThreadId; -extern bool g_bShowConsole; -extern bool g_bShowBrowser; - -///////////////////////////////////////////////////////////////////////////// -// Utils -bool LoadTextureFromByteArray(unsigned char* image_data, const int& image_width, const int& image_height, ID3D11ShaderResourceView** out_srv); \ No newline at end of file diff --git a/r5dev/include/input.h b/r5dev/include/input.h deleted file mode 100644 index 7b9637ef..00000000 --- a/r5dev/include/input.h +++ /dev/null @@ -1 +0,0 @@ -#pragma once \ No newline at end of file diff --git a/r5dev/include/logsystem.h b/r5dev/include/logsystem.h deleted file mode 100644 index 3611ecc3..00000000 --- a/r5dev/include/logsystem.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -enum class LogType_t : int -{ - SCRIPT_SERVER, - SCRIPT_CLIENT, - SCRIPT_UI, - SCRIPT_WARNING, - NATIVE -}; - -struct Log -{ - Log(const std::string Message, const int Ticks, const LogType_t Type) - { - this->Message = Message; - this->Ticks = Ticks; - this->Type = Type; - } - std::string Message = ""; - int Ticks = 1024; - LogType_t Type = LogType_t::NATIVE; -}; - -class LogSystem -{ -public: - void AddLog(LogType_t type, std::string text); - void Update(); - -private: - std::array GetLogColorForType(LogType_t type); - std::vector m_vLogs; -}; - -extern LogSystem g_LogSystem; \ No newline at end of file diff --git a/r5dev/include/opcptc.h b/r5dev/include/opcptc.h deleted file mode 100644 index fc0715e2..00000000 --- a/r5dev/include/opcptc.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -void InstallOpcodes(); -inline HANDLE GameProcess = GetCurrentProcess(); - -namespace -{ - Module r5_op = Module("r5apex.exe"); // Create module class instance. - -#pragma region Origin - /*0x14032EEA0*/ - MemoryAddress Origin_Init = r5_op.PatternSearch("48 83 EC 28 80 3D ? ? ? 23 ? 0F 85 ? 02 ?"); - - /*0x140330290*/ - MemoryAddress Origin_SetState = r5_op.PatternSearch("48 81 EC 58 04 ? ? 80 3D ? ? ? ? ? 0F 84"); -#pragma endregion - -#pragma region Engine - /*0x14043FB90*/ - MemoryAddress dst002 = r5_op.PatternSearch("48 89 4C 24 08 56 41 55 48 81 EC 68 03 ? ? 4C"); - - /*0x14022A4A0*/ - MemoryAddress dst004 = r5_op.PatternSearch("48 83 EC 38 0F 29 74 24 20 48 89 5C 24 40 48 8B"); - - /*0x140238DA0*/ - MemoryAddress Host_NewGame = r5_op.PatternSearch("48 8B C4 ? 41 54 41 ? 48 81 EC ? ? ? ? F2"); -#pragma endregion - -#pragma region NetChannel - /*0x14030D000*/ - MemoryAddress CServer_Auth = r5_op.PatternSearch("40 55 57 41 55 41 57 48 8D AC 24 ? ? ? ?"); - /*0x1413336F0*/ - MemoryAddress NetChan_EncKey_DefaultAssign = r5_op.PatternSearch("E8 ? ? ? ? 48 8D 1D ? ? ? ? 4C 39 3D ? ? ? ?"); -#pragma endregion - -#pragma region FairFight - /*0x140303AE0*/ - MemoryAddress FairFight_Init = r5_op.PatternSearch("40 53 48 83 EC 20 8B 81 B0 03 ? ? 48 8B D9 C6"); -#pragma endregion - -#pragma region Squirrel - /*0x14105CCA0*/ - MemoryAddress Squirrel_CompileError = r5_op.StringSearch("%s SCRIPT COMPILE ERROR: %s").FindPatternSelf("48 89 5C", MemoryAddress::Direction::UP); -#pragma endregion - - void PrintOAddress() // Test the sigscan results - { - std::cout << "+--------------------------------------------------------+" << std::endl; - PRINT_ADDRESS("Origin_Init", Origin_Init.GetPtr()); - PRINT_ADDRESS("Origin_SetState", Origin_SetState.GetPtr()); - PRINT_ADDRESS("dst002", dst002.GetPtr()); - PRINT_ADDRESS("dst004", dst004.GetPtr()); - PRINT_ADDRESS("Host_NewGame", Host_NewGame.GetPtr()); - PRINT_ADDRESS("CServer_Auth", CServer_Auth.GetPtr()); - PRINT_ADDRESS("NetChan_EncKey_DefaultAssign", NetChan_EncKey_DefaultAssign.GetPtr()); - PRINT_ADDRESS("FairFight_Init", FairFight_Init.GetPtr()); - PRINT_ADDRESS("Squirrel_CompileError", Squirrel_CompileError.GetPtr()); - std::cout << "+--------------------------------------------------------+" << std::endl; - } -} diff --git a/r5dev/include/patterns.h b/r5dev/include/patterns.h deleted file mode 100644 index d2a20430..00000000 --- a/r5dev/include/patterns.h +++ /dev/null @@ -1,210 +0,0 @@ -#pragma once - -// Define the signatures or offsets to be searched and hooked -namespace -{ - Module r5_patterns = Module("r5apex.exe"); // Create module class instance. - -#pragma region Console - /*0x140202090*/ - FUNC_AT_ADDRESS(addr_CommandExecute, void(*)(void*, const char*), r5_patterns.PatternSearch("48 89 5C 24 ? 57 48 83 EC 20 48 8D 0D ? ? ? ? 41 8B D8").GetPtr()); - - /*0x14046FE90*/ - FUNC_AT_ADDRESS(addr_ConVar_IsFlagSet, bool(*)(int**, int), r5_patterns.PatternSearch("48 8B 41 48 85 50 38").GetPtr()); - - /*0x14046F490*/ - FUNC_AT_ADDRESS(addr_ConCommand_IsFlagSet, bool(*)(int*, int), r5_patterns.PatternSearch("85 51 38 0F 95 C0 C3").GetPtr()); - - /*0x140279CE0*/ - FUNC_AT_ADDRESS(addr_downloadPlaylists_Callback, void(*)(), r5_patterns.PatternSearch("33 C9 C6 05 ? ? ? ? ? E9 ? ? ? ?").GetPtr()); -#pragma endregion - -#pragma region Squirrel - /*0x141057FD0*/ - FUNC_AT_ADDRESS(addr_SQVM_Print, void*, r5_patterns.PatternSearch("83 F8 01 48 8D 3D ? ? ? ?").OffsetSelf(0x3).FollowNearCallSelf(0x3, 0x7).GetPtr()); - - /*0x14105F950*/ - FUNC_AT_ADDRESS(addr_SQVM_Warning, __int64(*)(__int64, int, int, const char*, std::size_t*), r5_patterns.PatternSearch("E8 ? ? ? ? 85 C0 0F 99 C3").FollowNearCallSelf().GetPtr()); - - /*0x140B1E55*/ - FUNC_AT_ADDRESS(addr_SQVM_Warning_ReturnAddr, void*, r5_patterns.PatternSearch("E8 ? ? ? ? 85 C0 0F 99 C3").OffsetSelf(0x5).GetPtr()); - - /*0x141061A50*/ - FUNC_AT_ADDRESS(addr_sq_pushstring, void(*)(void*, char*, __int64), r5_patterns.PatternSearch("E8 ? ? ? ? 8D 55 FE").FollowNearCall().GetPtr()); - - /*0x141061CD0*/ - FUNC_AT_ADDRESS(addr_sq_pushbool, void(*)(void*, int), r5_patterns.PatternSearch("E8 ? ? ? ? 41 0F B6 17").FollowNearCall().GetPtr()); - - /*0x141061C70*/ - FUNC_AT_ADDRESS(addr_sq_pushinteger, void(*)(void*, int), r5_patterns.PatternSearch("E8 ? ? ? ? 41 0F B7 17").FollowNearCall().GetPtr()); - - /*0x141062040*/ - FUNC_AT_ADDRESS(addr_sq_newarray, void(*)(void*, int), r5_patterns.PatternSearch("E8 ? ? ? ? 49 63 F5").FollowNearCall().GetPtr()); - - /*0x1410621F0*/ - FUNC_AT_ADDRESS(addr_sq_arrayappend, __int64(*)(void*, int), r5_patterns.PatternSearch("E8 ? ? ? ? 49 63 46 38").FollowNearCall().GetPtr()); - - /*0x141061FB0*/ - FUNC_AT_ADDRESS(addr_sq_newtable, void(*)(void*), r5_patterns.PatternSearch("E8 ? ? ? ? 83 C6 04").FollowNearCall().GetPtr()); - - /*0x141064250*/ - FUNC_AT_ADDRESS(addr_sq_newslot, __int64(*)(void*, int), r5_patterns.PatternSearch("E8 ? ? ? ? 49 63 46 18").FollowNearCall().GetPtr()); - - //DWORD64 p_SQVM_LoadScript = FindPattern("r5apex.exe", (const unsigned char*)"\x48\x89\x5C\x24\x10\x48\x89\x74\x24\x18\x48\x89\x7C\x24\x20\x48\x89\x4C\x24\x08\x55\x41\x54\x41\x55\x41\x56\x41\x57\x48\x8D\x6C", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); // For S0 and S1 - - /*0x141055630*/ - // For anything S2 and above (current S8 - FUNC_AT_ADDRESS(addr_SQVM_LoadScript, bool(*)(void*, const char*, const char*, int), r5_patterns.PatternSearch("48 8B C4 48 89 48 08 55 41 56 48 8D 68").GetPtr()); - - /*0x140C957E0*/ - FUNC_AT_ADDRESS(addr_SQVM_LoadRson, int(*)(const char*), r5_patterns.PatternSearch("4C 8B DC 49 89 5B 08 57 48 81 EC A0 00 00 00 33").GetPtr()); - - /*0x140834A00*/ - FUNC_AT_ADDRESS(addr_SQVM_RegisterOriginFuncs, void(*)(void*), r5_patterns.PatternSearch("E8 ? ? ? ? 48 8B 0D ? ? ? ? 48 8D 15 ? ? ? ? E8 ? ? ? ? 48 8B 05 ? ? ? ? C7 05 ? ? ? ? ? ? ? ?").FollowNearCall().GetPtr()); - - /*0x140C06B20*/ - FUNC_AT_ADDRESS(addr_SQVM_RegisterCreatePlayerTasklist, void(*)(void*), r5_patterns.PatternSearch("E8 ? ? ? ? 48 8B 0D ? ? ? ? E8 ? ? ? ? 48 8B CB").FollowNearCall().GetPtr()); -#pragma endregion - -#pragma region NetChannel - /*0x1402655F0*/ - FUNC_AT_ADDRESS(addr_NET_PrintFunc, bool(*)(int, void*, bool), r5_patterns.PatternSearch("48 89 54 24 10 4C 89 44 24 18 4C 89 4C 24 20 C3 48").GetPtr()); - - /*0x1402655F0*/ - FUNC_AT_ADDRESS(addr_NET_ReceiveDatagram, bool(*)(int, void*, bool), r5_patterns.PatternSearch("48 89 74 24 18 48 89 7C 24 20 55 41 54 41 55 41 56 41 57 48 8D AC 24 50 EB").GetPtr()); - - /*0x1402662D0*/ - FUNC_AT_ADDRESS(addr_NET_SendDatagram, int(*)(SOCKET, const char*, int, int), r5_patterns.PatternSearch("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 41 56 41 57 48 81 EC ? 05 ? ?").GetPtr()); - - /*0x14025F190*/ - FUNC_AT_ADDRESS(addr_NetChan_Shutdown, void(*)(void*, const char*, unsigned __int8, char), r5_patterns.StringSearch("Disconnect by server.\n").FindPatternSelf("E8 ? ? ? ? 4C 89 B3 ? ? ? ?", MemoryAddress::Direction::DOWN).FollowNearCallSelf().GetPtr()); - - /*0x160686DC0*/ - MemoryAddress addr_NetChan_EncKeyPtr = r5_patterns.StringSearch("client:NetEncryption_NewKey").FindPatternSelf("48 8D ? ? ? ? ? 48 3B", MemoryAddress::Direction::UP, 150).ResolveRelativeAddressSelf(0x3, 0x7); - char* addr_NetChan_EncKey = addr_NetChan_EncKeyPtr.Offset(0x12D0).RCast(); - - /*0x140263E70*/ - FUNC_AT_ADDRESS(addr_NetChan_SetEncKey, void(*)(uintptr_t, const char*), MemoryAddress(0x140263E70).GetPtr()); - -#pragma endregion - -#pragma region CServer - /*0x140310230*/ - FUNC_AT_ADDRESS(addr_CServer_RejectConnection, void(*)(void*, unsigned int, void*, const char*), r5_patterns.StringSearch("#CONNECTION_FAILED_RESERVATION_TIMEOUT").FindPatternSelf("E8", MemoryAddress::Direction::DOWN).FollowNearCallSelf().GetPtr()); - - /*0x14030D000*/ - FUNC_AT_ADDRESS(addr_CServer_ConnectClient, void*(*)(void*, void*), r5_patterns.StringSearch("dedi.connect.fail.total:1|c\n").FindPatternSelf("E8", MemoryAddress::Direction::UP).FollowNearCallSelf().GetPtr()); -#pragma endregion - -#pragma region CHLClient - /*0x1405C0740*/ - FUNC_AT_ADDRESS(addr_CHLClient_FrameStageNotify, void(*)(void*, int), r5_patterns.PatternSearch("48 83 EC 28 89 15 ?? ?? ?? ??").GetPtr()); -#pragma endregion - -#pragma region CClient - /*0x140302FD0*/ - FUNC_AT_ADDRESS(addr_CClient_Clear, void(*)(__int64), r5_patterns.StringSearch("Disconnect by server.\n").FindPatternSelf("40", MemoryAddress::Direction::UP).GetPtr()); -#pragma endregion - -#pragma region CClientState - /*0x1418223E4*/ - FUNC_AT_ADDRESS(addr_m_bRestrictServerCommands, void*, r5_patterns.StringSearch("DevShotGenerator_Init()").FindPatternSelf("88 05", MemoryAddress::Direction::UP).ResolveRelativeAddressSelf(0x2).OffsetSelf(0x2).GetPtr()); -#pragma endregion - -#pragma region CVEngineServer - /*0x140315CF0*/ - FUNC_AT_ADDRESS(addr_CVEngineServer_IsPersistenceDataAvailable, bool(*)(__int64, int), r5_patterns.PatternSearch("3B 15 ?? ?? ?? ?? 7D 33").GetPtr()); -#pragma endregion - -#pragma region CBaseFileSystem - /*0x14038BE20*/ - FUNC_AT_ADDRESS(addr_CBaseFileSystem_FileSystemWarning, void(*)(void*, FileWarningLevel_t, const char*, ...), r5_patterns.PatternSearch("E8 ? ? ? ? 33 C0 80 3B 2A").FollowNearCallSelf().GetPtr()); -#pragma endregion - -#pragma region CMatSystemSurface - /*0x140548A00*/ - FUNC_AT_ADDRESS(addr_CMatSystemSurface_LockCursor, void(*)(void*), MemoryAddress(0x140548A00).GetPtr()); // Maybe sigscan this via RTTI. - - /*0x1405489C0*/ - FUNC_AT_ADDRESS(addr_CMatSystemSurface_UnlockCursor, void(*)(void*), MemoryAddress(0x1405489C0).GetPtr()); // Maybe sigscan this via RTTI. - - /*0x140547900*/ - FUNC_AT_ADDRESS(addr_CMatSystemSurface_DrawColoredText, void(*)(void*, int, int, int, int, int, int, int, int, const char*, ...), MemoryAddress(0x140547900).GetPtr()); -#pragma region Utility - /*0x140295600*/ - FUNC_AT_ADDRESS(addr_MSG_EngineError, int(*)(char*, va_list), r5_patterns.StringSearch("Engine Error").FindPatternSelf("48 89 ? ? ? 48 89", MemoryAddress::Direction::UP, 500).GetPtr()); - - /*0x1401B31C0*/ - FUNC_AT_ADDRESS(addr_MemAlloc_Wrapper, void*(*)(__int64), r5_patterns.StringSearch("ConversionModeMenu").FindPatternSelf("E8 ? ? ? ? 48", MemoryAddress::Direction::UP).FollowNearCallSelf().GetPtr()); - - /*0x14B37DE80 has current loaded playlist name*/ - /*0x1402790C0*/ - FUNC_AT_ADDRESS(addr_LoadPlaylist, bool(*)(const char*), r5_patterns.PatternSearch("E8 ? ? ? ? 80 3D ? ? ? ? ? 74 0C").FollowNearCallSelf().GetPtr()); - - /*0x1671060C0*/ - FUNC_AT_ADDRESS(addr_MapVPKCache, void*, r5_patterns.StringSearch("PrecacheMTVF").FindPatternSelf("48 8D 1D ? ? ? ? 4C", MemoryAddress::Direction::UP, 900).OffsetSelf(0x3).ResolveRelativeAddressSelf().GetPtr()); - - /*0x140278C50*/ - FUNC_AT_ADDRESS(addr_mp_gamemode_Callback, bool(*)(const char*), r5_patterns.StringSearch("Failed to load playlist data\n").FindPatternSelf("E8 ? ? ? ? B0 01", MemoryAddress::Direction::DOWN, 200).FollowNearCallSelf().GetPtr()); -#pragma endregion - -#pragma region KeyValues - /*0x1404744E0*/ - FUNC_AT_ADDRESS(addr_KeyValues_FindKey, void*(*)(void*, const char*, bool), r5_patterns.PatternSearch("40 56 57 41 57 48 81 EC ?? ?? ?? ?? 45").GetPtr()); -#pragma endregion - -#pragma region CEngineVGui - /*0x140283FD0*/ - FUNC_AT_ADDRESS(addr_CEngineVGui_Paint, int(*)(void*, int), r5_patterns.PatternSearch("41 55 41 56 48 83 EC 78 44 8B EA").GetPtr()); -#pragma endregion - -#pragma region OriginSDK - /*0x1410A2D70*/ - FUNC_AT_ADDRESS(addr_OriginGetErrorDescription, const char*(*)(std::int32_t), r5_patterns.StringSearch("OriginGetErrorDescription entered").FindPatternSelf("40 53", MemoryAddress::Direction::UP).GetPtr()); -#pragma endregion - - - void PrintHAddress() // Test the sigscan results - { - std::cout << "+--------------------------------------------------------+" << std::endl; - PRINT_ADDRESS("CommandExecute", addr_CommandExecute); - PRINT_ADDRESS("ConVar_IsFlagSet", addr_ConVar_IsFlagSet); - PRINT_ADDRESS("ConCommand_IsFlagSet", addr_ConCommand_IsFlagSet); - PRINT_ADDRESS("Downloadplaylists_Callback", addr_downloadPlaylists_Callback); - PRINT_ADDRESS("MP_gamemode_Callback", addr_mp_gamemode_Callback); - PRINT_ADDRESS("SQVM_Print", addr_SQVM_Print); - PRINT_ADDRESS("SQVM_LoadScript", addr_SQVM_LoadScript); - PRINT_ADDRESS("SQVM_LoadRson", addr_SQVM_LoadRson); - PRINT_ADDRESS("SQVM_Warning", addr_SQVM_Warning); - PRINT_ADDRESS("SQVM_Warning_ReturnAddr", addr_SQVM_Warning_ReturnAddr); - PRINT_ADDRESS("SQVM_RegisterOriginFuncs", addr_SQVM_RegisterOriginFuncs); - PRINT_ADDRESS("SQVM_RegisterCreatePlayerTasklist", addr_SQVM_RegisterCreatePlayerTasklist); - PRINT_ADDRESS("sq_arrayappend", addr_sq_arrayappend); - PRINT_ADDRESS("sq_newarray", addr_sq_newarray); - PRINT_ADDRESS("sq_newslot", addr_sq_newslot); - PRINT_ADDRESS("sq_newtable", addr_sq_newtable); - PRINT_ADDRESS("sq_pushbool", addr_sq_pushbool); - PRINT_ADDRESS("sq_pushinteger", addr_sq_pushinteger); - PRINT_ADDRESS("sq_pushstring", addr_sq_pushstring); - PRINT_ADDRESS("NET_PrintFunc", addr_NET_PrintFunc); - PRINT_ADDRESS("NET_ReceiveDatagram", addr_NET_ReceiveDatagram); - PRINT_ADDRESS("NET_SendDatagram ", addr_NET_SendDatagram); - PRINT_ADDRESS("CClientState::m_bRestrictServerCommands", addr_m_bRestrictServerCommands); - PRINT_ADDRESS("CClient::Clear", addr_CClient_Clear); - PRINT_ADDRESS("INetChannel::Shutdown", addr_NetChan_Shutdown); - PRINT_ADDRESS("INetChannel::SetEncryptionKey", addr_NetChan_SetEncKey); - PRINT_ADDRESS("INetChannel::EncryptionKey", addr_NetChan_EncKey); - PRINT_ADDRESS("CHLClient::FrameStageNotify", addr_CHLClient_FrameStageNotify); - PRINT_ADDRESS("CVEngineServer::IsPersistenceDataAvailable", addr_CVEngineServer_IsPersistenceDataAvailable); - PRINT_ADDRESS("CServer::ConnectClient", addr_CServer_ConnectClient); - PRINT_ADDRESS("CServer::RejectConnection", addr_CServer_RejectConnection); - PRINT_ADDRESS("CBaseFileSystem::FileSystemWarning", addr_CBaseFileSystem_FileSystemWarning); - PRINT_ADDRESS("MSG_EngineError", addr_MSG_EngineError); - PRINT_ADDRESS("LoadPlaylist", addr_LoadPlaylist); - PRINT_ADDRESS("MapVPKCache", addr_MapVPKCache); - PRINT_ADDRESS("MemAlloc_Wrapper", addr_MemAlloc_Wrapper); - PRINT_ADDRESS("KeyValues::FindKey", addr_KeyValues_FindKey); - std::cout << "+--------------------------------------------------------+" << std::endl; - // TODO implement error handling when sigscan fails or result is 0 - } -} diff --git a/r5dev/include/pch.h b/r5dev/include/pch.h deleted file mode 100644 index de793c13..00000000 --- a/r5dev/include/pch.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once -#pragma message("[DEV] pre-compiling headers.\n") - -#define WIN32_LEAN_AND_MEAN // Prevent winsock2 redefinition. -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Our headers - -#include "imgui.h" -#include "imgui_stdlib.h" -#include "imgui_impl_dx11.h" -#include "imgui_impl_win32.h" -#include "spdlog.h" -#include "sinks/basic_file_sink.h" -#include "sinks/stdout_sinks.h" -#include "sinks/ostream_sink.h" -#include "utility.h" -#include "httplib.h" -#include "json.hpp" - -#include "address.h" -#include "enums.h" - -#define FUNC_AT_ADDRESS(name, funcbody, addr) \ - using _##name = funcbody; \ - _##name name = (funcbody)addr \ - -#define PRINT_ADDRESS(name, address) std::cout << name << ": " << std::hex << std::uppercase << address << std::endl; \ No newline at end of file diff --git a/r5dev/include/squirrel.h b/r5dev/include/squirrel.h deleted file mode 100644 index 568fb824..00000000 --- a/r5dev/include/squirrel.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -char* sq_getstring(void* sqvm, int i); -int sq_getinteger(void* sqvm, int i); - -void sq_pushbool(void* sqvm, int val); - -void sq_pushstring(void* sqvm, char* string, int len); -void sq_pushstring(void* sqvm, const char* string, int len); - -void sq_pushinteger(void* sqvm, int val); - -void sq_newarray(void* sqvm, int size); -void sq_arrayappend(void* sqvm, int idx); - -void sq_newtable(void* sqvm); -void sq_newslot(void* sqvm, int idx); \ No newline at end of file diff --git a/r5dev/include/structs.h b/r5dev/include/structs.h deleted file mode 100644 index f15e396d..00000000 --- a/r5dev/include/structs.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -/* Structures */ -typedef unsigned __int64 QWORD; - -struct __declspec(align(8)) netpacket_t -{ - DWORD family_maybe; - sockaddr_in sin; - WORD sin_port; - BYTE gap16; - BYTE byte17; - DWORD source; - double received; - unsigned __int8* data; - QWORD label; - BYTE byte38; - QWORD qword40; - QWORD qword48; - BYTE gap50[8]; - QWORD qword58; - QWORD qword60; - QWORD qword68; - int less_than_12; - DWORD wiresize; - BYTE gap78[8]; - QWORD qword80; -}; diff --git a/r5dev/inputsystem/ButtonCode.h b/r5dev/inputsystem/ButtonCode.h new file mode 100644 index 00000000..f622d024 --- /dev/null +++ b/r5dev/inputsystem/ButtonCode.h @@ -0,0 +1,215 @@ +#pragma once +#include "tier0/basetypes.h" + +#define JOYSTICK_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_BUTTON + ((_joystick) * JOYSTICK_MAX_BUTTON_COUNT) + (_button) ) +#define JOYSTICK_POV_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_POV_BUTTON + ((_joystick) * JOYSTICK_POV_BUTTON_COUNT) + (_button) ) +#define JOYSTICK_AXIS_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_AXIS_BUTTON + ((_joystick) * JOYSTICK_AXIS_BUTTON_COUNT) + (_button) ) + +#define JOYSTICK_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_BUTTON_INTERNAL( _joystick, _button ) ) +#define JOYSTICK_POV_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_POV_BUTTON_INTERNAL( _joystick, _button ) ) +#define JOYSTICK_AXIS_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_AXIS_BUTTON_INTERNAL( _joystick, _button ) ) + +// Buttons are not confirmed to be the same. They have been always the same throughout the source engine. Lets hope they did not change them. +enum +{ + MAX_JOYSTICKS = MAX_SPLITSCREEN_CLIENTS, + MOUSE_BUTTON_COUNT = 5, +}; + +enum JoystickAxis_t +{ + JOY_AXIS_X = 0, + JOY_AXIS_Y, + JOY_AXIS_Z, + JOY_AXIS_R, + JOY_AXIS_U, + JOY_AXIS_V, + MAX_JOYSTICK_AXES, +}; + +enum +{ + JOYSTICK_MAX_BUTTON_COUNT = 32, + JOYSTICK_POV_BUTTON_COUNT = 4, + JOYSTICK_AXIS_BUTTON_COUNT = MAX_JOYSTICK_AXES * 2, +}; + +//----------------------------------------------------------------------------- +// Button enum. "Buttons" are binary-state input devices (mouse buttons, keyboard keys) +//----------------------------------------------------------------------------- +enum ButtonCode_t +{ + BUTTON_CODE_INVALID = -1, + BUTTON_CODE_NONE = 0, + + KEY_FIRST = 0, + + KEY_NONE = KEY_FIRST, + KEY_0, + KEY_1, + KEY_2, + KEY_3, + KEY_4, + KEY_5, + KEY_6, + KEY_7, + KEY_8, + KEY_9, + KEY_A, + KEY_B, + KEY_C, + KEY_D, + KEY_E, + KEY_F, + KEY_G, + KEY_H, + KEY_I, + KEY_J, + KEY_K, + KEY_L, + KEY_M, + KEY_N, + KEY_O, + KEY_P, + KEY_Q, + KEY_R, + KEY_S, + KEY_T, + KEY_U, + KEY_V, + KEY_W, + KEY_X, + KEY_Y, + KEY_Z, + KEY_PAD_0, + KEY_PAD_1, + KEY_PAD_2, + KEY_PAD_3, + KEY_PAD_4, + KEY_PAD_5, + KEY_PAD_6, + KEY_PAD_7, + KEY_PAD_8, + KEY_PAD_9, + KEY_PAD_DIVIDE, + KEY_PAD_MULTIPLY, + KEY_PAD_MINUS, + KEY_PAD_PLUS, + KEY_PAD_ENTER, + KEY_PAD_DECIMAL, + KEY_LBRACKET, + KEY_RBRACKET, + KEY_SEMICOLON, + KEY_APOSTROPHE, + KEY_BACKQUOTE, + KEY_COMMA, + KEY_PERIOD, + KEY_SLASH, + KEY_BACKSLASH, + KEY_MINUS, + KEY_EQUAL, + KEY_ENTER, + KEY_SPACE, + KEY_BACKSPACE, + KEY_TAB, + KEY_CAPSLOCK, + KEY_NUMLOCK, + KEY_ESCAPE, + KEY_SCROLLLOCK, + KEY_INSERT, + KEY_DELETE, + KEY_HOME, + KEY_END, + KEY_PAGEUP, + KEY_PAGEDOWN, + KEY_BREAK, + KEY_LSHIFT, + KEY_RSHIFT, + KEY_LALT, + KEY_RALT, + KEY_LCONTROL, + KEY_RCONTROL, + KEY_LWIN, + KEY_RWIN, + KEY_APP, + KEY_UP, + KEY_LEFT, + KEY_DOWN, + KEY_RIGHT, + KEY_F1, + KEY_F2, + KEY_F3, + KEY_F4, + KEY_F5, + KEY_F6, + KEY_F7, + KEY_F8, + KEY_F9, + KEY_F10, + KEY_F11, + KEY_F12, + KEY_CAPSLOCKTOGGLE, + KEY_NUMLOCKTOGGLE, + KEY_SCROLLLOCKTOGGLE, + + KEY_LAST = KEY_SCROLLLOCKTOGGLE, + KEY_COUNT = KEY_LAST - KEY_FIRST + 1, + + // Mouse + MOUSE_FIRST = KEY_LAST + 1, + + MOUSE_LEFT = MOUSE_FIRST, + MOUSE_RIGHT, + MOUSE_MIDDLE, + MOUSE_4, + MOUSE_5, + MOUSE_WHEEL_UP, // A fake button which is 'pressed' and 'released' when the wheel is moved up + MOUSE_WHEEL_DOWN, // A fake button which is 'pressed' and 'released' when the wheel is moved down + + MOUSE_LAST = MOUSE_WHEEL_DOWN, + MOUSE_COUNT = MOUSE_LAST - MOUSE_FIRST + 1, + + // Joystick + JOYSTICK_FIRST = MOUSE_LAST + 1, + + JOYSTICK_FIRST_BUTTON = JOYSTICK_FIRST, + JOYSTICK_LAST_BUTTON = JOYSTICK_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_MAX_BUTTON_COUNT - 1), + JOYSTICK_FIRST_POV_BUTTON, + JOYSTICK_LAST_POV_BUTTON = JOYSTICK_POV_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_POV_BUTTON_COUNT - 1), + JOYSTICK_FIRST_AXIS_BUTTON, + JOYSTICK_LAST_AXIS_BUTTON = JOYSTICK_AXIS_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_AXIS_BUTTON_COUNT - 1), + + JOYSTICK_LAST = JOYSTICK_LAST_AXIS_BUTTON, + + BUTTON_CODE_LAST, + BUTTON_CODE_COUNT = BUTTON_CODE_LAST - KEY_FIRST + 1, + + // Helpers for XBox 360 + KEY_XBUTTON_UP = JOYSTICK_FIRST_POV_BUTTON, // POV buttons + KEY_XBUTTON_RIGHT, + KEY_XBUTTON_DOWN, + KEY_XBUTTON_LEFT, + + KEY_XBUTTON_A = JOYSTICK_FIRST_BUTTON, // Buttons + KEY_XBUTTON_B, + KEY_XBUTTON_X, + KEY_XBUTTON_Y, + KEY_XBUTTON_LEFT_SHOULDER, + KEY_XBUTTON_RIGHT_SHOULDER, + KEY_XBUTTON_BACK, + KEY_XBUTTON_START, + KEY_XBUTTON_STICK1, + KEY_XBUTTON_STICK2, + KEY_XBUTTON_INACTIVE_START, + + KEY_XSTICK1_RIGHT = JOYSTICK_FIRST_AXIS_BUTTON, // XAXIS POSITIVE + KEY_XSTICK1_LEFT, // XAXIS NEGATIVE + KEY_XSTICK1_DOWN, // YAXIS POSITIVE + KEY_XSTICK1_UP, // YAXIS NEGATIVE + KEY_XBUTTON_LTRIGGER, // ZAXIS POSITIVE + KEY_XBUTTON_RTRIGGER, // ZAXIS NEGATIVE + KEY_XSTICK2_RIGHT, // UAXIS POSITIVE + KEY_XSTICK2_LEFT, // UAXIS NEGATIVE + KEY_XSTICK2_DOWN, // VAXIS POSITIVE + KEY_XSTICK2_UP, // VAXIS NEGATIVE +}; \ No newline at end of file diff --git a/r5dev/inputsystem/inputsystem.cpp b/r5dev/inputsystem/inputsystem.cpp new file mode 100644 index 00000000..02911bde --- /dev/null +++ b/r5dev/inputsystem/inputsystem.cpp @@ -0,0 +1,6 @@ +#include "core/stdafx.h" +#include "vpc/IAppSystem.h" +#include "inputsystem/inputsystem.h" + +/////////////////////////////////////////////////////////////////////////////// +CInputSystem* g_pInputSystem = reinterpret_cast(p_IAppSystem_LoadLibrary.FindPatternSelf("48 89 05", ADDRESS::Direction::DOWN, 40).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr()); diff --git a/r5dev/inputsystem/inputsystem.h b/r5dev/inputsystem/inputsystem.h new file mode 100644 index 00000000..96fb5f3c --- /dev/null +++ b/r5dev/inputsystem/inputsystem.h @@ -0,0 +1,47 @@ +#pragma once +#include "core/stdafx.h" +#include "inputsystem/ButtonCode.h" + +class CInputSystem +{ +public: + void EnableInput(bool bEnabled)// @0x14039F100 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = void(__thiscall*)(CInputSystem*, bool); + (*reinterpret_cast(this))[10](this, bEnabled); + } + + void EnableMessagePump(bool bEnabled) // @0x14039F110 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = void(__thiscall*)(CInputSystem*, bool); + (*reinterpret_cast(this))[11](this, bEnabled); + } + + bool IsButtonDown(ButtonCode_t Button) // @0x1403A0140 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = bool(__thiscall*)(CInputSystem*, ButtonCode_t); + return (*reinterpret_cast(this))[13](this, Button); + } + +private: + char pad_0000[16]; //0x0000 +public: + bool m_bEnabled; //0x0010 IsInputEnabled variable. + bool m_bPumpEnabled; //0x0011 EnabledMessagePump variable. +}; + +/////////////////////////////////////////////////////////////////////////////// +extern CInputSystem* g_pInputSystem +; +/////////////////////////////////////////////////////////////////////////////// +class HInputSystem : public IDetour +{ + virtual void debugp() + { + std::cout << "| VAR: g_pInputSystem : 0x" << std::hex << std::uppercase << g_pInputSystem << std::setw(0) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HInputSystem); diff --git a/r5dev/launcher/IApplication.cpp b/r5dev/launcher/IApplication.cpp new file mode 100644 index 00000000..5d5f5ee0 --- /dev/null +++ b/r5dev/launcher/IApplication.cpp @@ -0,0 +1,32 @@ +#include "core/stdafx.h" +#include "launcher/IApplication.h" +#include "ebisusdk/EbisuSDK.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void* HIApplication_Main(void* a1, void* a2) +{ + HEbisuSDK_Init(); + return IAppSystem_Main(a1, a2); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool HIApplication_Create(void* a1) +{ + return IAppSystem_Create(a1); +} + +void IApplication_Attach() +{ + DetourAttach((LPVOID*)&IAppSystem_Main, &HIApplication_Main); + DetourAttach((LPVOID*)&IAppSystem_Create, &HIApplication_Create); +} + +void IApplication_Detach() +{ + DetourDetach((LPVOID*)&IAppSystem_Main, &HIApplication_Main); + DetourDetach((LPVOID*)&IAppSystem_Create, &HIApplication_Create); +} diff --git a/r5dev/launcher/IApplication.h b/r5dev/launcher/IApplication.h new file mode 100644 index 00000000..598f04d8 --- /dev/null +++ b/r5dev/launcher/IApplication.h @@ -0,0 +1,37 @@ +#pragma once +#include "tier0/basetypes.h" + +namespace +{ +#if defined (GAMEDLL_S1) || defined (GAMEDLL_S1) + +#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) + /* ==== CAPPSYSTEMGROUP ================================================================================================================================================= */ + ADDRESS p_IAppSystem_Main = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x53\x48\x83\xEC\x20\x80\xB9\x00\x00\x00\x00\x00\xBB\x00\x00\x00\x00", "xxxxxxxx?????x????"); + void* (*IAppSystem_Main)(void* a1, void* a2) = (void* (*)(void*, void*))p_IAppSystem_Main.GetPtr(); /*40 53 48 83 EC 20 80 B9 ?? ?? ?? ?? ?? BB ?? ?? ?? ??*/ + + ADDRESS p_IAppSystem_Create = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x8B\xC4\x55\x41\x54\x41\x55\x41\x56\x41\x57\x48\x8B\xEC\x48\x83\xEC\x60", "xxxxxxxxxxxxxxxxxxx"); + bool (*IAppSystem_Create)(void* a1) = (bool(*)(void*))p_IAppSystem_Create.GetPtr(); /*48 8B C4 55 41 54 41 55 41 56 41 57 48 8B EC 48 83 EC 60*/ +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +void* HIApplication_Main(void* a1, void* a2); +bool HIApplication_Create(void* a1); + +void IApplication_Attach(); +void IApplication_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +class HApplication : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: IAppSystem::Main : 0x" << std::hex << std::uppercase << p_IAppSystem_Main.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: IAppSystem::Create : 0x" << std::hex << std::uppercase << p_IAppSystem_Create.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HApplication); diff --git a/r5dev/mathlib/IceKey.H b/r5dev/mathlib/IceKey.H new file mode 100644 index 00000000..f8641d06 --- /dev/null +++ b/r5dev/mathlib/IceKey.H @@ -0,0 +1,62 @@ +// Purpose: Header file for the C++ ICE encryption class. +// Taken from public domain code, as written by Matthew Kwan - July 1996 +// http://www.darkside.com.au/ice/ + +#ifndef _IceKey_H +#define _IceKey_H + +/* +The IceKey class is used for encrypting and decrypting 64-bit blocks of data +with the ICE (Information Concealment Engine) encryption algorithm. + +The constructor creates a new IceKey object that can be used to encrypt and decrypt data. +The level of encryption determines the size of the key, and hence its speed. +Level 0 uses the Thin-ICE variant, which is an 8-round cipher taking an 8-byte key. +This is the fastest option, and is generally considered to be at least as secure as DES, +although it is not yet certain whether it is as secure as its key size. + +For levels n greater than zero, a 16n-round cipher is used, taking 8n-byte keys. +Although not as fast as level 0, these are very very secure. + +Before an IceKey can be used to encrypt data, its key schedule must be set with the set() member function. +The length of the key required is determined by the level, as described above. + +The member functions encrypt() and decrypt() encrypt and decrypt respectively data +in blocks of eight chracters, using the specified key. + +Two functions keySize() and blockSize() are provided +which return the key and block size respectively, measured in bytes. +The key size is determined by the level, while the block size is always 8. + +The destructor zeroes out and frees up all memory associated with the key. +*/ + +class IceSubkey; + +class IceKey { + public: + IceKey (int n); + ~IceKey (); + + void set (const unsigned char *key); + + void encrypt (const unsigned char *plaintext, + unsigned char *ciphertext) const; + + void decrypt (const unsigned char *ciphertext, + unsigned char *plaintext) const; + + int keySize () const; + + int blockSize () const; + + private: + void scheduleBuild (unsigned short *k, int n, + const int *keyrot); + + int _size; + int _rounds; + IceSubkey *_keysched; +}; + +#endif diff --git a/r5dev/mathlib/IceKey.cpp b/r5dev/mathlib/IceKey.cpp new file mode 100644 index 00000000..6ba93eda --- /dev/null +++ b/r5dev/mathlib/IceKey.cpp @@ -0,0 +1,392 @@ +// Purpose: C++ implementation of the ICE encryption algorithm. +// Taken from public domain code, as written by Matthew Kwan - July 1996 +// http://www.darkside.com.au/ice/ +#include "core/stdafx.h" +#include "mathlib/IceKey.H" +#if !defined(_STATIC_LINKED) || defined(_SHARED_LIB) + +#pragma warning(disable: 4244) + + + /* Structure of a single round subkey */ +class IceSubkey { + public: + unsigned long val[3]; +}; + + + /* The S-boxes */ +static unsigned long ice_sbox[4][1024]; +static int ice_sboxes_initialised = 0; + + + /* Modulo values for the S-boxes */ +static const int ice_smod[4][4] = { + {333, 313, 505, 369}, + {379, 375, 319, 391}, + {361, 445, 451, 397}, + {397, 425, 395, 505}}; + + /* XOR values for the S-boxes */ +static const int ice_sxor[4][4] = { + {0x83, 0x85, 0x9b, 0xcd}, + {0xcc, 0xa7, 0xad, 0x41}, + {0x4b, 0x2e, 0xd4, 0x33}, + {0xea, 0xcb, 0x2e, 0x04}}; + + /* Permutation values for the P-box */ +static const unsigned long ice_pbox[32] = { + 0x00000001, 0x00000080, 0x00000400, 0x00002000, + 0x00080000, 0x00200000, 0x01000000, 0x40000000, + 0x00000008, 0x00000020, 0x00000100, 0x00004000, + 0x00010000, 0x00800000, 0x04000000, 0x20000000, + 0x00000004, 0x00000010, 0x00000200, 0x00008000, + 0x00020000, 0x00400000, 0x08000000, 0x10000000, + 0x00000002, 0x00000040, 0x00000800, 0x00001000, + 0x00040000, 0x00100000, 0x02000000, 0x80000000}; + + /* The key rotation schedule */ +static const int ice_keyrot[16] = { + 0, 1, 2, 3, 2, 1, 3, 0, + 1, 3, 2, 0, 3, 1, 0, 2}; + + +/* + * 8-bit Galois Field multiplication of a by b, modulo m. + * Just like arithmetic multiplication, except that additions and + * subtractions are replaced by XOR. + */ + +static unsigned int +gf_mult ( + unsigned int a, + unsigned int b, + unsigned int m +) { + unsigned int res = 0; + + while (b) { + if (b & 1) + res ^= a; + + a <<= 1; + b >>= 1; + + if (a >= 256) + a ^= m; + } + + return (res); +} + + +/* + * Galois Field exponentiation. + * Raise the base to the power of 7, modulo m. + */ + +static unsigned long +gf_exp7 ( + unsigned int b, + unsigned int m +) { + unsigned int x; + + if (b == 0) + return (0); + + x = gf_mult (b, b, m); + x = gf_mult (b, x, m); + x = gf_mult (x, x, m); + return (gf_mult (b, x, m)); +} + + +/* + * Carry out the ICE 32-bit P-box permutation. + */ + +static unsigned long +ice_perm32 ( + unsigned long x +) { + unsigned long res = 0; + const unsigned long *pbox = ice_pbox; + + while (x) { + if (x & 1) + res |= *pbox; + pbox++; + x >>= 1; + } + + return (res); +} + + +/* + * Initialise the ICE S-boxes. + * This only has to be done once. + */ + +static void +ice_sboxes_init (void) +{ + int i; + + for (i=0; i<1024; i++) { + int col = (i >> 1) & 0xff; + int row = (i & 0x1) | ((i & 0x200) >> 8); + unsigned long x; + + x = gf_exp7 (col ^ ice_sxor[0][row], ice_smod[0][row]) << 24; + ice_sbox[0][i] = ice_perm32 (x); + + x = gf_exp7 (col ^ ice_sxor[1][row], ice_smod[1][row]) << 16; + ice_sbox[1][i] = ice_perm32 (x); + + x = gf_exp7 (col ^ ice_sxor[2][row], ice_smod[2][row]) << 8; + ice_sbox[2][i] = ice_perm32 (x); + + x = gf_exp7 (col ^ ice_sxor[3][row], ice_smod[3][row]); + ice_sbox[3][i] = ice_perm32 (x); + } +} + + +/* + * Create a new ICE key. + */ + +IceKey::IceKey (int n) +{ + if (!ice_sboxes_initialised) { + ice_sboxes_init (); + ice_sboxes_initialised = 1; + } + + if (n < 1) { + _size = 1; + _rounds = 8; + } else { + _size = n; + _rounds = n * 16; + } + + _keysched = new IceSubkey[_rounds]; +} + + +/* + * Destroy an ICE key. + */ + +IceKey::~IceKey () +{ + int i, j; + + for (i=0; i<_rounds; i++) + for (j=0; j<3; j++) + _keysched[i].val[j] = 0; + + _rounds = _size = 0; + + delete[] _keysched; +} + + +/* + * The single round ICE f function. + */ + +static unsigned long +ice_f ( + unsigned long p, + const IceSubkey *sk +) { + unsigned long tl, tr; /* Expanded 40-bit values */ + unsigned long al, ar; /* Salted expanded 40-bit values */ + + /* Left half expansion */ + tl = ((p >> 16) & 0x3ff) | (((p >> 14) | (p << 18)) & 0xffc00); + + /* Right half expansion */ + tr = (p & 0x3ff) | ((p << 2) & 0xffc00); + + /* Perform the salt permutation */ + // al = (tr & sk->val[2]) | (tl & ~sk->val[2]); + // ar = (tl & sk->val[2]) | (tr & ~sk->val[2]); + al = sk->val[2] & (tl ^ tr); + ar = al ^ tr; + al ^= tl; + + al ^= sk->val[0]; /* XOR with the subkey */ + ar ^= sk->val[1]; + + /* S-box lookup and permutation */ + return (ice_sbox[0][al >> 10] | ice_sbox[1][al & 0x3ff] + | ice_sbox[2][ar >> 10] | ice_sbox[3][ar & 0x3ff]); +} + + +/* + * Encrypt a block of 8 bytes of data with the given ICE key. + */ + +void +IceKey::encrypt ( + const unsigned char *ptext, + unsigned char *ctext +) const +{ + int i; + unsigned long l, r; + + l = (((unsigned long) ptext[0]) << 24) + | (((unsigned long) ptext[1]) << 16) + | (((unsigned long) ptext[2]) << 8) | ptext[3]; + r = (((unsigned long) ptext[4]) << 24) + | (((unsigned long) ptext[5]) << 16) + | (((unsigned long) ptext[6]) << 8) | ptext[7]; + + for (i = 0; i < _rounds; i += 2) { + l ^= ice_f (r, &_keysched[i]); + r ^= ice_f (l, &_keysched[i + 1]); + } + + for (i = 0; i < 4; i++) { + ctext[3 - i] = r & 0xff; + ctext[7 - i] = l & 0xff; + + r >>= 8; + l >>= 8; + } +} + + +/* + * Decrypt a block of 8 bytes of data with the given ICE key. + */ + +void +IceKey::decrypt ( + const unsigned char *ctext, + unsigned char *ptext +) const +{ + int i; + unsigned long l, r; + + l = (((unsigned long) ctext[0]) << 24) + | (((unsigned long) ctext[1]) << 16) + | (((unsigned long) ctext[2]) << 8) | ctext[3]; + r = (((unsigned long) ctext[4]) << 24) + | (((unsigned long) ctext[5]) << 16) + | (((unsigned long) ctext[6]) << 8) | ctext[7]; + + for (i = _rounds - 1; i > 0; i -= 2) { + l ^= ice_f (r, &_keysched[i]); + r ^= ice_f (l, &_keysched[i - 1]); + } + + for (i = 0; i < 4; i++) { + ptext[3 - i] = r & 0xff; + ptext[7 - i] = l & 0xff; + + r >>= 8; + l >>= 8; + } +} + + +/* + * Set 8 rounds [n, n+7] of the key schedule of an ICE key. + */ + +void +IceKey::scheduleBuild ( + unsigned short *kb, + int n, + const int *keyrot +) { + int i; + + for (i=0; i<8; i++) { + int j; + int kr = keyrot[i]; + IceSubkey *isk = &_keysched[n + i]; + + for (j=0; j<3; j++) + isk->val[j] = 0; + + for (j=0; j<15; j++) { + int k; + unsigned long *curr_sk = &isk->val[j % 3]; + + for (k=0; k<4; k++) { + unsigned short *curr_kb = &kb[(kr + k) & 3]; + int bit = *curr_kb & 1; + + *curr_sk = (*curr_sk << 1) | bit; + *curr_kb = (*curr_kb >> 1) | ((bit ^ 1) << 15); + } + } + } +} + + +/* + * Set the key schedule of an ICE key. + */ + +void +IceKey::set ( + const unsigned char *key +) { + int i; + + if (_rounds == 8) { + unsigned short kb[4]; + + for (i=0; i<4; i++) + kb[3 - i] = (key[i*2] << 8) | key[i*2 + 1]; + + scheduleBuild (kb, 0, ice_keyrot); + return; + } + + for (i=0; i<_size; i++) { + int j; + unsigned short kb[4]; + + for (j=0; j<4; j++) + kb[3 - j] = (key[i*8 + j*2] << 8) | key[i*8 + j*2 + 1]; + + scheduleBuild (kb, i*8, ice_keyrot); + scheduleBuild (kb, _rounds - 8 - i*8, &ice_keyrot[8]); + } +} + + +/* + * Return the key size, in bytes. + */ + +int +IceKey::keySize () const +{ + return (_size * 8); +} + + +/* + * Return the block size, in bytes. + */ + +int +IceKey::blockSize () const +{ + return (8); +} + +#endif // !_STATIC_LINKED || _SHARED_LIB diff --git a/r5dev/mathlib/adler32.cpp b/r5dev/mathlib/adler32.cpp new file mode 100644 index 00000000..82aac397 --- /dev/null +++ b/r5dev/mathlib/adler32.cpp @@ -0,0 +1,47 @@ +#include "core/stdafx.h" +#include "mathlib/adler32.h" + +// Mark Adler's compact Adler32 hashing algorithm +// Originally from the public domain stb.h header. +uint32_t adler32::update(uint32_t adler, const void* ptr, size_t buf_len) +{ + if (!ptr) + { + return NULL; + } + + const uint8_t* buffer = static_cast(ptr); + + const unsigned long ADLER_MOD = 65521; + unsigned long s1 = adler & 0xffff, s2 = adler >> 16; + size_t blocklen; + unsigned long i; + + blocklen = buf_len % 5552; + while (buf_len) + { + for (i = 0; i + 7 < blocklen; i += 8) + { + s1 += buffer[0], s2 += s1; + s1 += buffer[1], s2 += s1; + s1 += buffer[2], s2 += s1; + s1 += buffer[3], s2 += s1; + s1 += buffer[4], s2 += s1; + s1 += buffer[5], s2 += s1; + s1 += buffer[6], s2 += s1; + s1 += buffer[7], s2 += s1; + + buffer += 8; + } + + for (; i < blocklen; ++i) + { + s1 += *buffer++, s2 += s1; + } + + s1 %= ADLER_MOD, s2 %= ADLER_MOD; + buf_len -= blocklen; + blocklen = 5552; + } + return (s2 << 16) + s1; +} diff --git a/r5dev/mathlib/adler32.h b/r5dev/mathlib/adler32.h new file mode 100644 index 00000000..71556647 --- /dev/null +++ b/r5dev/mathlib/adler32.h @@ -0,0 +1,7 @@ +#pragma once + +class adler32 +{ +public: + static uint32_t update(uint32_t adler, const void* ptr, size_t buf_len); +}; diff --git a/r5dev/mathlib/crc32.cpp b/r5dev/mathlib/crc32.cpp new file mode 100644 index 00000000..63cf181f --- /dev/null +++ b/r5dev/mathlib/crc32.cpp @@ -0,0 +1,22 @@ +#include "core/stdafx.h" +#include "mathlib/crc32.h" + +// Karl Malbrain's compact CRC-32, with pre and post conditioning. +// See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": +// http://www.geocities.com/malbrain/ +uint32_t crc32::update(uint32_t crc, const uint8_t* ptr, size_t buf_len) +{ + if (!ptr) + { + return NULL; + } + + crc = ~crc; + while (buf_len--) + { + uint8_t b = *ptr++; + crc = (crc >> 4) ^ s_crc32[(crc & 0xF) ^ (b & 0xF)]; + crc = (crc >> 4) ^ s_crc32[(crc & 0xF) ^ (b >> 4)]; + } + return ~crc; +} diff --git a/r5dev/mathlib/crc32.h b/r5dev/mathlib/crc32.h new file mode 100644 index 00000000..1466ef9c --- /dev/null +++ b/r5dev/mathlib/crc32.h @@ -0,0 +1,16 @@ +#pragma once +#include + +namespace +{ +} +class crc32 +{ + static inline uint32_t s_crc32[16] = + { + 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c + }; +public: + static uint32_t update(uint32_t crc, const uint8_t* ptr, size_t buf_len); +}; \ No newline at end of file diff --git a/r5dev/mathlib/parallel_for.h b/r5dev/mathlib/parallel_for.h new file mode 100644 index 00000000..ac766efd --- /dev/null +++ b/r5dev/mathlib/parallel_for.h @@ -0,0 +1,57 @@ +#include +#include +#include +#include + +/// @param[in] nb_elements : size of your for loop +/// @param[in] functor(start, end) : +/// your function processing a sub chunk of the for loop. +/// "start" is the first index to process (included) until the index "end" +/// (excluded) +/// @code +/// for(int i = start; i < end; ++i) +/// computation(i); +/// @endcode +/// @param use_threads : enable / disable threads. +/// +/// +static +void parallel_for(unsigned nb_elements, + std::function functor, + bool use_threads = true) +{ + // ------- + unsigned nb_threads_hint = std::thread::hardware_concurrency(); + unsigned nb_threads = nb_threads_hint == 0 ? 8 : (nb_threads_hint); + + unsigned batch_size = nb_elements / nb_threads; + unsigned batch_remainder = nb_elements % nb_threads; + + std::vector< std::thread > my_threads(nb_threads); + + if( use_threads ) + { + // Multithread execution + for(unsigned i = 0; i < nb_threads; ++i) + { + int start = i * batch_size; + my_threads[i] = std::thread(functor, start, start+batch_size); + } + } + else + { + // Single thread execution (for easy debugging) + for(unsigned i = 0; i < nb_threads; ++i){ + int start = i * batch_size; + functor( start, start+batch_size ); + } + } + + // Deform the elements left + int start = nb_threads * batch_size; + functor( start, start+batch_remainder); + + // Wait for the other thread to finish their task + if( use_threads ) + std::for_each(my_threads.begin(), my_threads.end(), std::mem_fn(&std::thread::join)); +} diff --git a/r5dev/mathlib/vector.h b/r5dev/mathlib/vector.h new file mode 100644 index 00000000..235b7288 --- /dev/null +++ b/r5dev/mathlib/vector.h @@ -0,0 +1,26 @@ +#pragma once + +typedef float vec_t; +typedef float vec3_t[3]; + +/*----------------------------------------------------------------------------- + * _vector.h + *-----------------------------------------------------------------------------*/ + +class Vector3 // TODO [ AMOS ]: Reverse class +{ +public: + // Members + vec_t x, // 0x0000 + y, // 0x0004 + z; // 0x0008 +}; + +class QAngle // TODO [ AMOS ]: Reverse class +{ +public: + // Members + vec_t x, // 0x0000 + y, // 0x0004 + z; // 0x0008 +}; diff --git a/r5dev/networksystem/r5net.cpp b/r5dev/networksystem/r5net.cpp new file mode 100644 index 00000000..4f814233 --- /dev/null +++ b/r5dev/networksystem/r5net.cpp @@ -0,0 +1,328 @@ +// r5net.cpp : Defines the functions for the static library. +// + +#include "core/stdafx.h" +#include "tier0/basetypes.h" +#include "tier0/cvar.h" +#include "engine/sys_utils.h" +#include "networksystem/r5net.h" + +//----------------------------------------------------------------------------- +// Purpose: returns the sdk version string. +//----------------------------------------------------------------------------- +std::string R5Net::Client::GetSDKVersion() +{ + return SDK_VERSION; +} + +//----------------------------------------------------------------------------- +// Purpose: returns a vector of hosted servers. +//----------------------------------------------------------------------------- +std::vector R5Net::Client::GetServersList(std::string& svOutMessage) +{ + std::vector vslList{}; + + nlohmann::json jsReqBody = nlohmann::json::object(); + jsReqBody["version"] = GetSDKVersion(); + + std::string reqBodyStr = jsReqBody.dump(); + + if (r5net_show_debug->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::ENGINE, "Sending GetServerList post.\n"); + } + + httplib::Result htResults = m_HttpClient.Post("/servers", jsReqBody.dump().c_str(), jsReqBody.dump().length(), "application/json"); + + if (r5net_show_debug->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::ENGINE, "GetServerList replied with '%d'.\n", htResults->status); + } + + if (htResults && htResults->status == 200) // STATUS_OK + { + nlohmann::json jsResultBody = nlohmann::json::parse(htResults->body); + if (jsResultBody["success"].is_boolean() && jsResultBody["success"].get()) + { + for (auto &obj : jsResultBody["servers"]) + { + vslList.push_back( + ServerListing{ + obj.value("name",""), + obj.value("map", ""), + obj.value("ip", ""), + obj.value("port", ""), + obj.value("gamemode", ""), + obj.value("hidden", "false") == "true", + obj.value("remote_checksum", ""), + obj.value("version", GetSDKVersion()), + obj.value("encKey", "") + } + ); + } + } + else + { + if (jsResultBody["err"].is_string()) + { + svOutMessage = jsResultBody["err"].get(); + } + else + { + svOutMessage = "An unknown error occured!"; + } + } + } + else + { + if (htResults) + { + if (!htResults->body.empty()) + { + nlohmann::json jsResultBody = nlohmann::json::parse(htResults->body); + + if (jsResultBody["err"].is_string()) + { + svOutMessage = jsResultBody["err"].get(); + } + else + { + svOutMessage = std::string("Failed to reach comp-server ") + std::to_string(htResults->status); + } + + return vslList; + } + + svOutMessage = std::string("Failed to reach comp-server ") + std::to_string(htResults->status); + return vslList; + } + + svOutMessage = "Failed to reach comp-server. Unknown error code."; + return vslList; + } + + return vslList; +} + +//----------------------------------------------------------------------------- +// Purpose: Sends host server POST request. +// Input : &svOutMessage - +// &svOutToken - +// &slServerListing - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool R5Net::Client::PostServerHost(std::string& svOutMessage, std::string& svOutToken, const ServerListing& slServerListing) +{ + nlohmann::json jsRequestBody = nlohmann::json::object(); + jsRequestBody["name"] = slServerListing.svServerName; + jsRequestBody["map"] = slServerListing.svMapName; + jsRequestBody["port"] = slServerListing.svPort; + jsRequestBody["remote_checksum"] = slServerListing.svRemoteChecksum; + jsRequestBody["version"] = GetSDKVersion(); + jsRequestBody["gamemode"] = slServerListing.svPlaylist; + jsRequestBody["encKey"] = slServerListing.svEncryptionKey; + jsRequestBody["hidden"] = slServerListing.bHidden; + + std::string svRequestBody = jsRequestBody.dump(); + + if (r5net_show_debug->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::ENGINE, "Sending PostServerHost post '%s'.\n", svRequestBody.c_str()); + } + + httplib::Result htResults = m_HttpClient.Post("/servers/add", svRequestBody.c_str(), svRequestBody.length(), "application/json"); + + if (r5net_show_debug->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::ENGINE, "PostServerHost replied with '%d'.\n", htResults->status); + } + + if (htResults && htResults->status == 200) // STATUS_OK + { + nlohmann::json jsResultBody = nlohmann::json::parse(htResults->body); + if (jsResultBody["success"].is_boolean() && jsResultBody["success"].get()) + { + if (jsResultBody["token"].is_string()) + { + svOutToken = jsResultBody["token"].get(); + } + else + { + svOutToken = std::string(); + } + + return true; + } + else + { + if (jsResultBody["err"].is_string()) + { + svOutMessage = jsResultBody["err"].get(); + } + else + { + svOutMessage = "An unknown error occured!"; + } + return false; + } + } + else + { + if (htResults) + { + if (!htResults->body.empty()) + { + nlohmann::json jsResultBody = nlohmann::json::parse(htResults->body); + + if (jsResultBody["err"].is_string()) + { + svOutMessage = jsResultBody["err"].get(); + } + else + { + svOutMessage = std::string("Failed to reach comp-server ") + std::to_string(htResults->status); + } + + svOutToken = std::string(); + return false; + } + + svOutToken = std::string(); + svOutMessage = std::string("Failed to reach comp-server ") + std::to_string(htResults->status); + return false; + } + + svOutToken = std::string(); + svOutMessage = "Failed to reach comp-server. Unknown error code."; + return false; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Gets the server by token string. +// Input : &slOutServer - +// &svOutMessage - +// svToken - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool R5Net::Client::GetServerByToken(ServerListing& slOutServer, std::string& svOutMessage, const std::string svToken) +{ + nlohmann::json jsRequestBody = nlohmann::json::object(); + + jsRequestBody["token"] = svToken; + + if (r5net_show_debug->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::ENGINE, "Sending GetServerByToken post.\n"); + } + + httplib::Result htResults = m_HttpClient.Post("/server/byToken", jsRequestBody.dump().c_str(), jsRequestBody.dump().length(), "application/json"); + + if (r5net_show_debug->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::ENGINE, "GetServerByToken replied with '%d'\n", htResults->status); + } + + if (htResults && htResults->status == 200) // STATUS_OK + { + if (!htResults->body.empty()) + { + nlohmann::json jsResultBody = nlohmann::json::parse(htResults->body); + + if (htResults && jsResultBody["success"].is_boolean() && jsResultBody["success"]) + { + slOutServer = ServerListing{ + jsResultBody["server"].value("name",""), + jsResultBody["server"].value("map", ""), + jsResultBody["server"].value("ip", ""), + jsResultBody["server"].value("port", ""), + jsResultBody["server"].value("gamemode", ""), + jsResultBody["server"].value("hidden", "false") == "true", + jsResultBody["server"].value("remote_checksum", ""), + jsResultBody["server"].value("version", GetSDKVersion()), + jsResultBody["server"].value("encKey", "") + }; + return true; + } + else + { + if (jsResultBody["err"].is_string()) + { + svOutMessage = jsResultBody["err"].get(); + } + else + { + svOutMessage = ""; + } + + slOutServer = ServerListing{}; + return false; + } + } + } + else + { + if (htResults) + { + if (!htResults->body.empty()) + { + nlohmann::json jsResultBody = nlohmann::json::parse(htResults->body); + + if (jsResultBody["err"].is_string()) + { + svOutMessage = jsResultBody["err"].get(); + } + else + { + svOutMessage = std::string("Failed to reach comp-server ") + std::to_string(htResults->status); + } + + return false; + } + + svOutMessage = std::string("Failed to reach comp-server ") + std::to_string(htResults->status); + return false; + } + + svOutMessage = "Failed to reach comp-server. Unknown error code."; + slOutServer = ServerListing{}; + return false; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Checks if client is banned on the comp server. +// Input : svIpAddress - +// nOriginID - +// &svOutErrCl - +// Output : Returns true if banned, false if not banned. +//----------------------------------------------------------------------------- +bool R5Net::Client::GetClientIsBanned(const std::string svIpAddress, std::int64_t nOriginID, std::string& svOutErrCl) +{ + nlohmann::json jsRequestBody = nlohmann::json::object(); + jsRequestBody["ip"] = svIpAddress; + jsRequestBody["orid"] = nOriginID; + + httplib::Result htResults = m_HttpClient.Post("/banlist/isBanned", jsRequestBody.dump().c_str(), jsRequestBody.dump().length(), "application/json"); + + if (htResults && htResults->status == 200) + { + nlohmann::json jsResultBody = nlohmann::json::parse(htResults->body); + + if (jsResultBody["success"].is_boolean() && jsResultBody["success"].get()) + { + if (jsResultBody["isBanned"].is_boolean() && jsResultBody["isBanned"].get()) + { + svOutErrCl = jsResultBody.value("errCl", "Generic error (code:gen). Contact R5Reloaded developers."); + return true; + } + } + } + return false; +} +/////////////////////////////////////////////////////////////////////////////// +R5Net::Client* g_pR5net(new R5Net::Client("r5a-comp-sv.herokuapp.com")); diff --git a/r5dev/networksystem/r5net.h b/r5dev/networksystem/r5net.h new file mode 100644 index 00000000..072412a2 --- /dev/null +++ b/r5dev/networksystem/r5net.h @@ -0,0 +1,27 @@ +#pragma once +#include "serverlisting.h" + +namespace R5Net +{ + class Client + { + public: + Client(std::string serverString) : m_HttpClient(serverString.c_str()) + { + m_HttpClient.set_connection_timeout(10); + } + + std::vector GetServersList(std::string& svOutMessage); + bool PostServerHost(std::string& svOutMessage, std::string& svOutToken, const ServerListing& slServerListing); + bool GetServerByToken(ServerListing& slOutServer, std::string& svOutMessage, const std::string svToken); + bool GetClientIsBanned(std::string svIpAddress, std::int64_t nOriginID, std::string& svOutErrCl); + std::string GetSDKVersion(); + + Client* pR5net = nullptr; + Client* GetR5Net() { return pR5net; } + + private: + httplib::Client m_HttpClient; + }; +} +extern R5Net::Client* g_pR5net; diff --git a/r5dev/networksystem/serverlisting.h b/r5dev/networksystem/serverlisting.h new file mode 100644 index 00000000..c0d1d760 --- /dev/null +++ b/r5dev/networksystem/serverlisting.h @@ -0,0 +1,14 @@ +#pragma once + +struct ServerListing +{ + std::string svServerName; + std::string svMapName = "mp_rr_canyonlands_staging"; + std::string svIpAddress; + std::string svPort; + std::string svPlaylist = "survival_dev"; + bool bHidden{}; + std::string svRemoteChecksum; + std::string svVersion; + std::string svEncryptionKey; +}; diff --git a/r5dev/networksystem/sm_protocol.h b/r5dev/networksystem/sm_protocol.h new file mode 100644 index 00000000..370c50fe --- /dev/null +++ b/r5dev/networksystem/sm_protocol.h @@ -0,0 +1,39 @@ +#pragma once +//============================================================================= +enum +{ + CONNECTIONLESS_HEADER = 0xffffffff, +}; + +//============================================================================= +// Connectionless messages +enum +{ + c2s_connect = 1, + + c2s_num_messages +}; + +enum +{ + s2c_connect_accept = 1, + s2c_connect_reject, + + s2c_num_messages +}; + +//============================================================================= +enum NetworkMessageGroup_t +{ + net_group_networksystem = 0 +}; + +//============================================================================= +// Networksystem internal messages for use during valid connection +enum SystemNetworkMessageType_t +{ + net_nop = 0, // nop command used for padding + net_disconnect = 1, // disconnect (last message in connection) + + net_num_messages +}; diff --git a/r5dev/public/bansystem.cpp b/r5dev/public/bansystem.cpp new file mode 100644 index 00000000..6460679d --- /dev/null +++ b/r5dev/public/bansystem.cpp @@ -0,0 +1,193 @@ +#include "core/stdafx.h" +#include "public/include/bansystem.h" + +CBanSystem::CBanSystem() +{ + Load(); +} + +void CBanSystem::operator[](std::pair pair) +{ + AddEntry(pair.first, pair.second); +} + +//----------------------------------------------------------------------------- +// Purpose: loads and parses the banlist +//----------------------------------------------------------------------------- +void CBanSystem::Load() +{ + std::filesystem::path path = std::filesystem::current_path() /= "platform\\cfg\\banlist.cfg"; // Get current path + banlist.config + + nlohmann::json jsIn; + std::ifstream banFile(path, std::ios::in); // Parse ban list. + + int nTotalBans = 0; + + if (banFile.good() && banFile) // Check if it parsed. + { + banFile >> jsIn; // Into json. + banFile.close(); // Close file. + + if (!jsIn.is_null()) // Check if json is valid + { + if (!jsIn["totalBans"].is_null()) // Is the totalBans field populated? + { + nTotalBans = jsIn["totalBans"].get(); // Get the totalBans field. + } + } + + for (int i = 0; i < nTotalBans; i++) // Loop through total bans. + { + nlohmann::json jsEntry = jsIn[std::to_string(i).c_str()]; // Get Entry for current ban. + if (jsEntry.is_null()) // Check if entry is valid. + { + continue; + } + + + std::int64_t nOriginID = jsEntry["originID"].get(); // Get originID field from entry. + std::string svIpAddress = jsEntry["ipAddress"].get(); // Get ipAddress field from entry. + + vsvBanList.push_back(std::make_pair(svIpAddress, nOriginID)); // Push back into vector. + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: saves the banlist +//----------------------------------------------------------------------------- +void CBanSystem::Save() +{ + nlohmann::json jsOut; + + for (int i = 0; i < vsvBanList.size(); i++) + { + jsOut["totalBans"] = vsvBanList.size(); // Populate totalBans field. + jsOut[std::to_string(i).c_str()]["ipAddress"] = vsvBanList[i].first; // Populate ipAddress field for this entry. + jsOut[std::to_string(i).c_str()]["originID"] = vsvBanList[i].second; // Populate originID field for this entry. + } + + std::filesystem::path path = std::filesystem::current_path() /= "platform\\cfg\\banlist.cfg"; // Get current path + banlist.config + std::ofstream outFile(path, std::ios::out | std::ios::trunc); // Write config file.. + + outFile << jsOut.dump(4); // Dump it into config file.. + outFile.close(); // Close the file handle. +} + +//----------------------------------------------------------------------------- +// Purpose: adds a banned player entry to the banlist +//----------------------------------------------------------------------------- +void CBanSystem::AddEntry(std::string svIpAddress, std::int64_t nOriginID) +{ + if (!svIpAddress.empty() && nOriginID > 0) // Check if args are valid. + { + vsvBanList.push_back(std::make_pair(svIpAddress, nOriginID)); // Push it back into the vector. + } +} + +//----------------------------------------------------------------------------- +// Purpose: deletes an entry in the banlist +//----------------------------------------------------------------------------- +void CBanSystem::DeleteEntry(std::string svIpAddress, std::int64_t nOriginID) +{ + for (int i = 0; i < vsvBanList.size(); i++) // Loop through vector. + { + if (svIpAddress.compare(vsvBanList[i].first) == NULL || nOriginID == vsvBanList[i].second) // Do any entries match our vector? + { + vsvBanList.erase(vsvBanList.begin() + i); // If so erase that vector element. + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: adds a connect refuse entry to the refuselist +//----------------------------------------------------------------------------- +void CBanSystem::AddConnectionRefuse(std::string svError, int nUserID) +{ + if (vsvrefuseList.empty()) + { + vsvrefuseList.push_back(std::make_pair(svError, nUserID)); + } + else + { + for (int i = 0; i < vsvrefuseList.size(); i++) // Loop through vector. + { + if (vsvrefuseList[i].second != nUserID) // Do any entries match our vector? + { + vsvrefuseList.push_back(std::make_pair(svError, nUserID)); // Push it back into the vector. + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: deletes an entry in the refuselist +//----------------------------------------------------------------------------- +void CBanSystem::DeleteConnectionRefuse(int nUserID) +{ + for (int i = 0; i < vsvrefuseList.size(); i++) // Loop through vector. + { + if (vsvrefuseList[i].second == nUserID) // Do any entries match our vector? + { + vsvrefuseList.erase(vsvrefuseList.begin() + i); // If so erase that vector element. + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: checks if specified ip address or necleus id is banned +// Input : svIpAddress - +// nOriginID - +// Output : true if banned, false if not banned +//----------------------------------------------------------------------------- +bool CBanSystem::IsBanned(std::string svIpAddress, std::int64_t nOriginID) +{ + for (int i = 0; i < vsvBanList.size(); i++) + { + std::string ipAddress = vsvBanList[i].first; // Get first pair entry. + std::int64_t originID = vsvBanList[i].second; // Get second pair entry. + + if (ipAddress.empty()) // Check if ip is empty. + { + continue; + } + + + if (originID <= 0) // Is originID below 0? + { + continue; + } + + + if (ipAddress.compare(svIpAddress) == NULL) // Do they match? + { + return true; + } + + + if (nOriginID == originID) // Do they match? + { + return true; + } + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: checks if refuselist is valid +//----------------------------------------------------------------------------- +bool CBanSystem::IsRefuseListValid() +{ + return !vsvrefuseList.empty(); +} + +//----------------------------------------------------------------------------- +// Purpose: checks if banlist is valid +//----------------------------------------------------------------------------- +bool CBanSystem::IsBanListValid() +{ + return !vsvBanList.empty(); +} +/////////////////////////////////////////////////////////////////////////////// +CBanSystem* g_pBanSystem = new CBanSystem();; diff --git a/r5dev/public/binstream.cpp b/r5dev/public/binstream.cpp new file mode 100644 index 00000000..f376553a --- /dev/null +++ b/r5dev/public/binstream.cpp @@ -0,0 +1,171 @@ +#include "core/stdafx.h" +#include "engine/sys_utils.h" +#include "public/include/binstream.h" + +//----------------------------------------------------------------------------- +// Purpose: CIOStream constructor +//----------------------------------------------------------------------------- +CIOStream::CIOStream() +{ + eCurrentMode = eStreamFileMode::NONE; +} + +//----------------------------------------------------------------------------- +// Purpose: CIOStream destructor +//----------------------------------------------------------------------------- +CIOStream::~CIOStream() +{ + if (writer.is_open()) + { + writer.close(); + } + + if (reader.is_open()) + { + reader.close(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: opens the file in specified mode +// Input : fileFullPath - mode +// Output : true if operation is successfull +//----------------------------------------------------------------------------- +bool CIOStream::open(std::string svFileFullPath, eStreamFileMode eMode) +{ + svFilePath = svFileFullPath; + + if (eMode == eStreamFileMode::WRITE) + { + eCurrentMode = eMode; + + // check if we had a previously opened file to close it + if (writer.is_open()) + { + writer.close(); + } + + writer.open(svFilePath.c_str(), std::ios::binary); + if (!writer.is_open()) + { + DevMsg(eDLL_T::FS, "Error opening file '%s' for write operation!\n", svFilePath.c_str()); + eCurrentMode = eStreamFileMode::NONE; + } + } + // Read mode + else if (eMode == eStreamFileMode::READ) + { + eCurrentMode = eMode; + + // check if we had a previously opened file to close it + if (reader.is_open()) + { + reader.close(); + } + + reader.open(svFilePath.c_str(), std::ios::binary); + if (!reader.is_open()) + { + DevMsg(eDLL_T::FS, "Error opening file '%s' for read operation!\n", svFilePath.c_str()); + eCurrentMode = eStreamFileMode::NONE; + } + } + + // if the mode is still the NONE -> we failed + return eCurrentMode == eStreamFileMode::NONE ? false : true; +} + +//----------------------------------------------------------------------------- +// Purpose: closes the file +//----------------------------------------------------------------------------- +void CIOStream::close() +{ + if (eCurrentMode == eStreamFileMode::WRITE) + { + writer.close(); + } + else if (eCurrentMode == eStreamFileMode::READ) + { + reader.close(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: checks if we are able to read the file +//----------------------------------------------------------------------------- +bool CIOStream::checkReadabilityStatus() +{ + if (eCurrentMode != eStreamFileMode::READ) + { + DevMsg(eDLL_T::FS, "Error: StreamFileMode doesn't match required mode for read operation.\n"); + return false; + } + + // check if we hit the end of the file. + if (reader.eof()) + { + DevMsg(eDLL_T::FS, "Error: trying to read past EOF.\n"); + reader.close(); + eCurrentMode = eStreamFileMode::NONE; + return false; + } + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: checks if we are able to write to file +//----------------------------------------------------------------------------- +bool CIOStream::checkWritabilityStatus() +{ + if (eCurrentMode != eStreamFileMode::WRITE) + { + DevMsg(eDLL_T::FS, "Error: StreamFileMode doesn't match required mode for write operation.\n"); + return false; + } + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: checks if we hit the end of file +//----------------------------------------------------------------------------- +bool CIOStream::eof() +{ + return reader.eof(); +} + +//----------------------------------------------------------------------------- +// Purpose: reads a string from the file and returns it +//----------------------------------------------------------------------------- +std::string CIOStream::readString() +{ + if (checkReadabilityStatus()) + { + char c; + std::string result = ""; + while (!reader.eof() && (c = readR()) != '\0') + { + result += c; + } + + return result; + } + return ""; +} + +//----------------------------------------------------------------------------- +// Purpose: writes a string to the file +//----------------------------------------------------------------------------- +void CIOStream::writeString(std::string str) +{ + if (!checkWritabilityStatus()) + { + return; + } + + str += '\0'; // null-terminate the string. + + char* text = (char*)(str.c_str()); + size_t size = str.size(); + + writer.write((const char*)text, size); +} diff --git a/r5dev/public/include/bansystem.h b/r5dev/public/include/bansystem.h new file mode 100644 index 00000000..c293ad63 --- /dev/null +++ b/r5dev/public/include/bansystem.h @@ -0,0 +1,24 @@ +#pragma once +#include "public/include/json.hpp" + +class CBanSystem +{ +public: + CBanSystem(); + void operator[](std::pair pair); + void Load(); + void Save(); + void AddEntry(std::string svIpAddress, std::int64_t nOriginID); + void DeleteEntry(std::string svIpAddress, std::int64_t nOriginID); + void AddConnectionRefuse(std::string svError, int nUserID); + void DeleteConnectionRefuse(int nUserID); + bool IsBanned(std::string svIpAddress, std::int64_t nOriginID); + bool IsRefuseListValid(); + bool IsBanListValid(); + + std::vector> vsvrefuseList = {};; +private: + std::vector> vsvBanList = {}; +}; + +extern CBanSystem* g_pBanSystem; diff --git a/r5dev/public/include/binstream.h b/r5dev/public/include/binstream.h new file mode 100644 index 00000000..1607d97f --- /dev/null +++ b/r5dev/public/include/binstream.h @@ -0,0 +1,68 @@ +#pragma once + +enum class eStreamFileMode +{ + NONE = 0, + READ, + WRITE +}; + +class CIOStream +{ + std::ofstream writer; // Output file stream. + std::ifstream reader; // Input file stream. + std::string svFilePath = ""; // Filepath. + eStreamFileMode eCurrentMode = eStreamFileMode::NONE; // Current active mode. + +public: + CIOStream(); + ~CIOStream(); + + bool open(std::string fileFullPath, eStreamFileMode mode); + void close(); + + bool checkWritabilityStatus(); + bool checkReadabilityStatus(); + + bool eof(); + + //----------------------------------------------------------------------------- + // Purpose: reads any value from the file (for strings use 'readString(...)' instead) + //----------------------------------------------------------------------------- + template + void read(T& value) // Template functions have to be in the header! + { + if (checkReadabilityStatus()) + { + reader.read((char*)&value, sizeof(value)); + } + } + + //----------------------------------------------------------------------------- + // Purpose: reads any value from the file and returns it (for strings use 'readString(...)' instead) + //----------------------------------------------------------------------------- + template + T readR() // Template functions have to be in the header! + { + checkReadabilityStatus(); + + T value; + reader.read((char*)&value, sizeof(value)); + return value; + } + std::string readString(); + + //----------------------------------------------------------------------------- + // Purpose: writes any value to the file (for strings use 'writeString(...)' instead) + //----------------------------------------------------------------------------- + template + void write(T& value) // Template functions have to be in the header! + { + if (!checkWritabilityStatus()) + { + return; + } + writer.write((const char*)&value, sizeof(value)); + } + void writeString(std::string str); +}; diff --git a/shared/include/httplib.h b/r5dev/public/include/httplib.h similarity index 100% rename from shared/include/httplib.h rename to r5dev/public/include/httplib.h diff --git a/shared/include/json.hpp b/r5dev/public/include/json.hpp similarity index 100% rename from shared/include/json.hpp rename to r5dev/public/include/json.hpp diff --git a/shared/include/address.h b/r5dev/public/include/memaddr.h similarity index 76% rename from shared/include/address.h rename to r5dev/public/include/memaddr.h index b3cc6c22..0d7cf4b4 100644 --- a/shared/include/address.h +++ b/r5dev/public/include/memaddr.h @@ -1,6 +1,7 @@ #pragma once +#include "public/include/utility.h" -class MemoryAddress +class ADDRESS { public: @@ -15,9 +16,9 @@ public: return ptr; } - MemoryAddress() = default; - MemoryAddress(std::uintptr_t ptr) : ptr(ptr) {} - MemoryAddress(void* ptr) : ptr(std::uintptr_t(ptr)) {} + ADDRESS() = default; + ADDRESS(std::uintptr_t ptr) : ptr(ptr) {} + ADDRESS(void* ptr) : ptr(std::uintptr_t(ptr)) {} operator std::uintptr_t() const { @@ -34,12 +35,12 @@ public: return ptr != NULL; } - bool operator!= (const MemoryAddress& addr) const + bool operator!= (const ADDRESS& addr) const { return ptr != addr.ptr; } - bool operator== (const MemoryAddress& addr) const + bool operator== (const ADDRESS& addr) const { return ptr == addr.ptr; } @@ -64,18 +65,18 @@ public: return *reinterpret_cast(ptr); } - MemoryAddress Offset(std::ptrdiff_t offset) + ADDRESS Offset(std::ptrdiff_t offset) { - return MemoryAddress(ptr + offset); + return ADDRESS(ptr + offset); } - MemoryAddress OffsetSelf(std::ptrdiff_t offset) + ADDRESS OffsetSelf(std::ptrdiff_t offset) { ptr += offset; return *this; } - MemoryAddress Deref(int deref = 1) + ADDRESS Deref(int deref = 1) { std::uintptr_t reference = ptr; @@ -85,10 +86,10 @@ public: reference = *reinterpret_cast(reference); } - return MemoryAddress(reference); + return ADDRESS(reference); } - MemoryAddress DerefSelf(int deref = 1) + ADDRESS DerefSelf(int deref = 1) { while (deref--) { @@ -105,7 +106,7 @@ public: for (auto [byteAtCurrentAddress, i] = std::tuple{ std::uint8_t(), (std::size_t)0 }; i < opcodeArray.size(); i++, reference++) // Loop forward in the ptr class member. { - byteAtCurrentAddress = *reinterpret_cast(reference); // Get byte at current address. + byteAtCurrentAddress = *reinterpret_cast(reference); // Get byte at current Address. if (byteAtCurrentAddress != opcodeArray[i]) // If byte at ptr doesn't equal in the byte array return false. return false; @@ -128,14 +129,14 @@ public: for (int i = 0; i < opcodes.size(); i++) { - *(std::uint8_t*)(ptr + i) = opcodes[i]; // Write opcodes to address. + *(std::uint8_t*)(ptr + i) = opcodes[i]; // Write opcodes to Address. } dwSize = opcodes.size(); VirtualProtect((void*)ptr, dwSize, oldProt, &oldProt); // Restore protection. } - MemoryAddress FindPatternSelf(const std::string pattern, const Direction searchDirect, const int opCodesToScan = 100, const std::ptrdiff_t occurence = 1) + ADDRESS FindPatternSelf(const std::string pattern, const Direction searchDirect, const int opCodesToScan = 100, const std::ptrdiff_t occurence = 1) { static auto PatternToBytes = [](const std::string pattern) { @@ -206,7 +207,7 @@ public: return *this; } - MemoryAddress FindPattern(const std::string pattern, const Direction searchDirect, const int opCodesToScan = 100, const std::ptrdiff_t occurence = 1) + ADDRESS FindPattern(const std::string pattern, const Direction searchDirect, const int opCodesToScan = 100, const std::ptrdiff_t occurence = 1) { static auto PatternToBytes = [](const std::string pattern) { @@ -266,60 +267,60 @@ public: occurences++; if (occurence == occurences) { - return MemoryAddress(&*(ScanBytes + memOffset)); + return ADDRESS(&*(ScanBytes + memOffset)); } } } - return MemoryAddress(); + return ADDRESS(); } - MemoryAddress FollowNearCall(std::ptrdiff_t opcodeOffset = 0x1, std::ptrdiff_t nextInstructionOffset = 0x5) + ADDRESS FollowNearCall(std::ptrdiff_t opcodeOffset = 0x1, std::ptrdiff_t nextInstructionOffset = 0x5) { return ResolveRelativeAddress(opcodeOffset, nextInstructionOffset); } - MemoryAddress FollowNearCallSelf(std::ptrdiff_t opcodeOffset = 0x1, std::ptrdiff_t nextInstructionOffset = 0x5) + ADDRESS FollowNearCallSelf(std::ptrdiff_t opcodeOffset = 0x1, std::ptrdiff_t nextInstructionOffset = 0x5) { return ResolveRelativeAddressSelf(opcodeOffset, nextInstructionOffset); } - MemoryAddress ResolveRelativeAddressSelf(std::ptrdiff_t registerOffset = 0x0, std::ptrdiff_t nextInstructionOffset = 0x4) + ADDRESS ResolveRelativeAddressSelf(std::ptrdiff_t registerOffset = 0x0, std::ptrdiff_t nextInstructionOffset = 0x4) { // Skip register. std::uintptr_t skipRegister = ptr + registerOffset; - // Get 4-byte long relative address. + // Get 4-byte long relative Address. std::int32_t relativeAddress = *reinterpret_cast(skipRegister); // Get location of next instruction. std::uintptr_t nextInstruction = ptr + nextInstructionOffset; - // Get function location via adding relative address to next instruction. + // Get function location via adding relative Address to next instruction. ptr = nextInstruction + relativeAddress; return *this; } - MemoryAddress ResolveRelativeAddress(std::ptrdiff_t registerOffset = 0x0, std::ptrdiff_t nextInstructionOffset = 0x4) + ADDRESS ResolveRelativeAddress(std::ptrdiff_t registerOffset = 0x0, std::ptrdiff_t nextInstructionOffset = 0x4) { // Skip register. std::uintptr_t skipRegister = ptr + registerOffset; - // Get 4-byte long relative address. + // Get 4-byte long relative Address. std::int32_t relativeAddress = *reinterpret_cast(skipRegister); // Get location of next instruction. std::uintptr_t nextInstruction = ptr + nextInstructionOffset; - // Get function location via adding relative address to next instruction. - return MemoryAddress(nextInstruction + relativeAddress); + // Get function location via adding relative Address to next instruction. + return ADDRESS(nextInstruction + relativeAddress); } private: std::uintptr_t ptr = 0; }; -class Module +class MODULE { public: @@ -334,7 +335,7 @@ public: } std::string sectionName = std::string(); // Name of section. - std::uintptr_t sectionStartAddress = 0; // Start memory address of section. + std::uintptr_t sectionStartAddress = 0; // Start memory Address of section. DWORD sectionSize = 0; // Size of section. }; @@ -349,8 +350,8 @@ public: return ModuleSections(); } - Module() = default; - Module(std::string moduleName) : moduleName(moduleName) + MODULE() = default; + MODULE(std::string moduleName) : moduleName(moduleName) { const MODULEINFO mInfo = GetModuleInfo(moduleName.c_str()); // Get module info. sizeOfModule = (DWORD64)mInfo.SizeOfImage; // Grab the module size. @@ -368,7 +369,7 @@ public: } } - MemoryAddress PatternSearch(const std::string pattern, const std::ptrdiff_t patternOccurence = 1) + ADDRESS PatternSearch(const std::string pattern, const std::ptrdiff_t patternOccurence = 1) { static auto PatternToBytes = [](const std::string pattern) { @@ -401,7 +402,7 @@ public: ModuleSections textSection = GetSectionByName(".text"); // Get the .text section. if (!textSection.IsSectionValid()) - return MemoryAddress(); + return ADDRESS(); const std::vector PatternBytes = PatternToBytes(pattern); // Convert our pattern to a byte array. const std::pair BytesInfo = std::make_pair(PatternBytes.size(), PatternBytes.data()); // Get the size and data of our bytes. @@ -430,62 +431,119 @@ public: { occurencesFound++; // Increment occurences found counter. if (patternOccurence == occurencesFound) // Is it the occurence we want? - return MemoryAddress(&StartOfCodeSection[i]); // If yes return it. + return ADDRESS(&StartOfCodeSection[i]); // If yes return it. latestOccurence = &StartOfCodeSection[i]; // Stash latest occurence. } } - return MemoryAddress(latestOccurence); + return ADDRESS(latestOccurence); } - MemoryAddress GetExportedFunction(const std::string functionName) + ADDRESS FindPatternSIMD(std::uint8_t* szPattern, const char* szMask) + { + + ModuleSections mInfo = GetSectionByName(".text"); // Get the .text section. + if (!mInfo.IsSectionValid()) { return ADDRESS(); } + + DWORD64 base = (DWORD64)mInfo.sectionStartAddress; + DWORD64 size = (DWORD64)mInfo.sectionSize; + + unsigned char* pData = (unsigned char*)base; + unsigned int length = (unsigned int)size; + + const unsigned char* end = pData + length - strlen(szMask); + int num_masks = (int)ceil((float)strlen(szMask) / (float)16); + int masks[32]; // 32*16 = enough masks for 512 bytes. + memset(masks, 0, num_masks * sizeof(int)); + for (int64_t i = 0; i < num_masks; ++i) + { + for (int64_t j = strnlen(szMask + i * 16, 16) - 1; j >= 0; --j) + { + if (szMask[i * 16 + j] == 'x') + { + masks[i] |= 1 << j; + } + } + } + __m128i xmm1 = _mm_loadu_si128((const __m128i*) szPattern); + __m128i xmm2, xmm3, msks; + for (; pData != end; _mm_prefetch((const char*)(++pData + 64), _MM_HINT_NTA)) + { + if (szPattern[0] == pData[0]) + { + xmm2 = _mm_loadu_si128((const __m128i*) pData); + msks = _mm_cmpeq_epi8(xmm1, xmm2); + if ((_mm_movemask_epi8(msks) & masks[0]) == masks[0]) + { + for (DWORD64 i = 1; i < num_masks; ++i) + { + xmm2 = _mm_loadu_si128((const __m128i*) (pData + i * 16)); + xmm3 = _mm_loadu_si128((const __m128i*) (szPattern + i * 16)); + msks = _mm_cmpeq_epi8(xmm2, xmm3); + if ((_mm_movemask_epi8(msks) & masks[i]) == masks[i]) + { + if ((i + 1) == num_masks) + { + return (ADDRESS)pData; + } + } + else goto cont; + } + return (ADDRESS)(&*(pData)); + } + }cont:; + } + return ADDRESS(); + } + + ADDRESS GetExportedFunction(const std::string functionName) { if (!dosHeader || dosHeader->e_magic != IMAGE_DOS_SIGNATURE) // Is dosHeader valid? - return MemoryAddress(); + return ADDRESS(); if (!ntHeaders || ntHeaders->Signature != IMAGE_NT_SIGNATURE) // Is ntHeader valid? - return MemoryAddress(); + return ADDRESS(); - // Get the location of IMAGE_EXPORT_DIRECTORY for this module by adding the IMAGE_DIRECTORY_ENTRY_EXPORT relative virtual address onto our module base address. + // Get the location of IMAGE_EXPORT_DIRECTORY for this module by adding the IMAGE_DIRECTORY_ENTRY_EXPORT relative virtual Address onto our module base Address. IMAGE_EXPORT_DIRECTORY* ImageExportDirectory = reinterpret_cast(moduleBase + ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); if (!ImageExportDirectory) - return MemoryAddress(); + return ADDRESS(); // Are there any exported functions? if (!ImageExportDirectory->NumberOfFunctions) - return MemoryAddress(); + return ADDRESS(); - // Get the location of the functions via adding the relative virtual address from the struct into our module base address. + // Get the location of the functions via adding the relative virtual Address from the struct into our module base Address. DWORD* AddressOfFunctionsPtr = reinterpret_cast(moduleBase + ImageExportDirectory->AddressOfFunctions); if (!AddressOfFunctionsPtr) - return MemoryAddress(); + return ADDRESS(); - // Get the names of the functions via adding the relative virtual address from the struct into our module base address. + // Get the names of the functions via adding the relative virtual Address from the struct into our module base Address. DWORD* AddressOfNamePtr = reinterpret_cast(moduleBase + ImageExportDirectory->AddressOfNames); if (!AddressOfNamePtr) - return MemoryAddress(); + return ADDRESS(); - // Get the ordinals of the functions via adding the relative virtual address from the struct into our module base address. + // Get the ordinals of the functions via adding the relative virtual Address from the struct into our module base Address. DWORD* AddressOfOrdinalsPtr = reinterpret_cast(moduleBase + ImageExportDirectory->AddressOfNameOrdinals); if (!AddressOfOrdinalsPtr) - return MemoryAddress(); + return ADDRESS(); for (std::size_t i = 0; i < ImageExportDirectory->NumberOfFunctions; i++) // Iterate through all the functions. { - // Get virtual relative address of the function name. Then add module base address to get the actual location. + // Get virtual relative Address of the function name. Then add module base Address to get the actual location. std::string ExportFunctionName = reinterpret_cast(reinterpret_cast(moduleBase + AddressOfNamePtr[i])); if (ExportFunctionName.compare(functionName) == 0) // Is this our wanted exported function? { - // Get the function ordinal. Then grab the relative virtual address of our wanted function. Then add module base address so we get the actual location. - return MemoryAddress(moduleBase + AddressOfFunctionsPtr[reinterpret_cast(AddressOfOrdinalsPtr)[i]]); // Return as MemoryAddress class. + // Get the function ordinal. Then grab the relative virtual Address of our wanted function. Then add module base Address so we get the actual location. + return ADDRESS(moduleBase + AddressOfFunctionsPtr[reinterpret_cast(AddressOfOrdinalsPtr)[i]]); // Return as Address class. } } - return MemoryAddress(); + return ADDRESS(); } - MemoryAddress FindAddressForString(const std::string string, bool nullTerminator) + ADDRESS FindAddressForString(const std::string string, bool nullTerminator) { static auto StringToBytes = [](const std::string string, bool nullTerminator) { @@ -507,7 +565,7 @@ public: ModuleSections rdataSection = GetSectionByName(".rdata"); // .Get rdata section, we only loop through here because most important strings are in the .rdata section. if (!rdataSection.IsSectionValid()) - return MemoryAddress(); + return ADDRESS(); std::vector stringBytes = StringToBytes(string, nullTerminator); // Convert our string to a byte array. const std::pair BytesInfo = std::make_pair(stringBytes.size(), stringBytes.data()); // Get the size and data of our bytes. @@ -531,14 +589,14 @@ public: if (FoundAddress) { - return MemoryAddress(&StartOfRdata[i]); + return ADDRESS(&StartOfRdata[i]); } } - return MemoryAddress(); + return ADDRESS(); } - MemoryAddress StringSearch(const std::string string, const std::ptrdiff_t occurence = 1, bool nullTerminator = false) + ADDRESS StringSearch(const std::string string, const std::ptrdiff_t occurence = 1, bool nullTerminator = false) { static auto PatternToBytes = [](const std::string pattern) { @@ -571,11 +629,11 @@ public: ModuleSections textSection = GetSectionByName(".text"); // Get the .text section. if (!textSection.IsSectionValid()) - return MemoryAddress(); + return ADDRESS(); - MemoryAddress stringAddress = FindAddressForString(string, nullTerminator); // Get address for the string in the .rdata section. + ADDRESS stringAddress = FindAddressForString(string, nullTerminator); // Get Address for the string in the .rdata section. if (!stringAddress) - return MemoryAddress(); + return ADDRESS(); std::uint8_t* latestOccurence = nullptr; std::ptrdiff_t occurencesFound = 0; @@ -587,25 +645,25 @@ public: byte byte = StartOfCodeSection[i]; if (byte == 0x8D) // is it a LEA instruction? { - MemoryAddress skipOpCode = MemoryAddress((std::uintptr_t)&StartOfCodeSection[i]).OffsetSelf(0x2); // Skip next 2 opcodes, those being the instruction and then the register. + ADDRESS skipOpCode = ADDRESS((std::uintptr_t)&StartOfCodeSection[i]).OffsetSelf(0x2); // Skip next 2 opcodes, those being the instruction and then the register. - std::int32_t relativeAddress = skipOpCode.GetValue(); // Get 4-byte long string relative address + std::int32_t relativeAddress = skipOpCode.GetValue(); // Get 4-byte long string relative Address std::uintptr_t nextInstruction = skipOpCode.Offset(0x4).GetPtr(); // Get location of next instruction. - MemoryAddress potentialLocation = MemoryAddress(nextInstruction + relativeAddress); // Get potential string location. + ADDRESS potentialLocation = ADDRESS(nextInstruction + relativeAddress); // Get potential string location. if (potentialLocation == stringAddress) { occurencesFound++; // Increment occurences found counter. if (occurence == occurencesFound) // Is it the occurence we want? - return MemoryAddress(&StartOfCodeSection[i]); // If yes return it. + return ADDRESS(&StartOfCodeSection[i]); // If yes return it. latestOccurence = &StartOfCodeSection[i]; // Stash latest occurence. } } } - return MemoryAddress(latestOccurence); + return ADDRESS(latestOccurence); } std::uintptr_t GetModuleBase() diff --git a/r5dev/public/include/stb_image.h b/r5dev/public/include/stb_image.h new file mode 100644 index 00000000..d60371b9 --- /dev/null +++ b/r5dev/public/include/stb_image.h @@ -0,0 +1,7897 @@ +/* stb_image - v2.27 - public domain image loader - http://nothings.org/stb + no warranty implied; use at your own risk + + Do this: + #define STB_IMAGE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define STB_IMAGE_IMPLEMENTATION + #include "stb_image.h" + + You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. + And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free + + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8/16-bit-per-channel + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels, 8/16 bit-per-channel) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + PNM (PPM and PGM binary only) + + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) + + Full documentation under "DOCUMENTATION" below. + + +LICENSE + + See end of file for license information. + +RECENT REVISION HISTORY: + + 2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes + 2.26 (2020-07-13) many minor fixes + 2.25 (2020-02-02) fix warnings + 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically + 2.23 (2019-08-11) fix clang static analysis warning + 2.22 (2019-03-04) gif fixes, fix warnings + 2.21 (2019-02-25) fix typo in comment + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings + 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes + 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 + RGB-format JPEG; remove white matting in PSD; + allocate large structures on the stack; + correct channel count for PNG & BMP + 2.10 (2016-01-22) avoid warning introduced in 2.09 + 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED + + See end of file for full revision history. + + + ============================ Contributors ========================= + + Image formats Extensions, features + Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) + Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) + Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) + Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) + Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) + Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) + Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) + github:urraka (animated gif) Junggon Kim (PNM comments) + Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) + socks-the-fox (16-bit PNG) + Jeremy Sawicki (handle all ImageNet JPGs) + Optimizations & bugfixes Mikhail Morozov (1-bit BMP) + Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) + Arseny Kapoulkine Simon Breuss (16-bit PNM) + John-Mark Allen + Carmelo J Fdez-Aguera + + Bug & warning fixes + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski + Phil Jordan Dave Moore Roy Eltham + Hayaki Saito Nathan Reed Won Chun + Luke Graham Johan Duparc Nick Verigakis the Horde3D community + Thomas Ruf Ronny Chevalier github:rlyeh + Janez Zemva John Bartholomew Michal Cichon github:romigrou + Jonathan Blow Ken Hamada Tero Hanninen github:svdijk + Eugene Golushkov Laurent Gomila Cort Stratton github:snagar + Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex + Cass Everitt Ryamond Barbiero github:grim210 + Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw + Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus + Josh Tobin Matthew Gregan github:poppolopoppo + Julian Raschke Gregory Mullen Christian Floisand github:darealshinji + Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 + Brad Weinberger Matvey Cherevko github:mosra + Luca Sas Alexander Veselov Zack Middleton [reserved] + Ryan C. Gordon [reserved] [reserved] + DO NOT ADD YOUR NAME HERE + + Jacko Dirks + + To add your name to the credits, pick a random blank space in the middle and fill it. + 80% of merge conflicts on stb PRs are due to people adding their name at the end + of the credits. +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// DOCUMENTATION +// +// Limitations: +// - no 12-bit-per-channel JPEG +// - no JPEGs with arithmetic coding +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below for HDR usage): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *channels_in_file -- outputs # of image components in image file +// int desired_channels -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data, or NULL on an allocation failure or if the image is +// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'desired_channels' if desired_channels is non-zero, or +// *channels_in_file otherwise. If desired_channels is non-zero, +// *channels_in_file has the number of components that _would_ have been +// output otherwise. E.g. if you set desired_channels to 4, you will always +// get RGBA output, but you can check *channels_in_file to see if it's trivially +// opaque because e.g. there were only 3 channels in the source image. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *channels_in_file will be unchanged. The function +// stbi_failure_reason() can be queried for an extremely brief, end-user +// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS +// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// To query the width, height and component count of an image without having to +// decode the full file, you can use the stbi_info family of functions: +// +// int x,y,n,ok; +// ok = stbi_info(filename, &x, &y, &n); +// // returns ok=1 and sets x, y, n if image is a supported format, +// // 0 otherwise. +// +// Note that stb_image pervasively uses ints in its public API for sizes, +// including sizes of memory buffers. This is now part of the API and thus +// hard to change without causing breakage. As a result, the various image +// loaders all have certain limits on image size; these differ somewhat +// by format but generally boil down to either just under 2GB or just under +// 1GB. When the decoded image would be larger than this, stb_image decoding +// will fail. +// +// Additionally, stb_image will reject image files that have any of their +// dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS, +// which defaults to 2**24 = 16777216 pixels. Due to the above memory limit, +// the only way to have an image with such dimensions load correctly +// is for it to have a rather extreme aspect ratio. Either way, the +// assumption here is that such larger images are likely to be malformed +// or malicious. If you do need to load an image with individual dimensions +// larger than that, and it still fits in the overall size limit, you can +// #define STBI_MAX_DIMENSIONS on your own to be something larger. +// +// =========================================================================== +// +// UNICODE: +// +// If compiling for Windows and you wish to use Unicode filenames, compile +// with +// #define STBI_WINDOWS_UTF8 +// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert +// Windows wchar_t filenames to utf8. +// +// =========================================================================== +// +// Philosophy +// +// stb libraries are designed with the following priorities: +// +// 1. easy to use +// 2. easy to maintain +// 3. good performance +// +// Sometimes I let "good performance" creep up in priority over "easy to maintain", +// and for best performance I may provide less-easy-to-use APIs that give higher +// performance, in addition to the easy-to-use ones. Nevertheless, it's important +// to keep in mind that from the standpoint of you, a client of this library, +// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. +// +// Some secondary priorities arise directly from the first two, some of which +// provide more explicit reasons why performance can't be emphasized. +// +// - Portable ("ease of use") +// - Small source code footprint ("easy to maintain") +// - No dependencies ("ease of use") +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). +// +// =========================================================================== +// +// SIMD support +// +// The JPEG decoder will try to automatically use SIMD kernels on x86 when +// supported by the compiler. For ARM Neon support, you must explicitly +// request it. +// +// (The old do-it-yourself SIMD API is no longer supported in the current +// code.) +// +// On x86, SSE2 will automatically be used when available based on a run-time +// test; if not, the generic C versions are used as a fall-back. On ARM targets, +// the typical path is to have separate builds for NEON and non-NEON devices +// (at least this is true for iOS and Android). Therefore, the NEON support is +// toggled by a build flag: define STBI_NEON to get NEON loops. +// +// If for some reason you do not want to use any of SIMD code, or if +// you have issues compiling it, you can disable it entirely by +// defining STBI_NO_SIMD. +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image supports loading HDR images in general, and currently the Radiance +// .HDR file format specifically. You can still load any file through the existing +// interface; if you attempt to load an HDR file, it will be automatically remapped +// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// iPhone PNG support: +// +// We optionally support converting iPhone-formatted PNGs (which store +// premultiplied BGRA) back to RGB, even though they're internally encoded +// differently. To enable this conversion, call +// stbi_convert_iphone_png_to_rgb(1). +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// +// =========================================================================== +// +// ADDITIONAL CONFIGURATION +// +// - You can suppress implementation of any of the decoders to reduce +// your code footprint by #defining one or more of the following +// symbols before creating the implementation. +// +// STBI_NO_JPEG +// STBI_NO_PNG +// STBI_NO_BMP +// STBI_NO_PSD +// STBI_NO_TGA +// STBI_NO_GIF +// STBI_NO_HDR +// STBI_NO_PIC +// STBI_NO_PNM (.ppm and .pgm) +// +// - You can request *only* certain decoders and suppress all other ones +// (this will be more forward-compatible, as addition of new decoders +// doesn't require you to disable them explicitly): +// +// STBI_ONLY_JPEG +// STBI_ONLY_PNG +// STBI_ONLY_BMP +// STBI_ONLY_PSD +// STBI_ONLY_TGA +// STBI_ONLY_GIF +// STBI_ONLY_HDR +// STBI_ONLY_PIC +// STBI_ONLY_PNM (.ppm and .pgm) +// +// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still +// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB +// +// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater +// than that size (in either width or height) without further processing. +// This is to let programs in the wild set an upper bound to prevent +// denial-of-service attacks on untrusted data, as one could generate a +// valid image of gigantic dimensions and force stb_image to allocate a +// huge block of memory and spend disproportionate time decoding it. By +// default this is set to (1 << 24), which is 16777216, but that's still +// very big. + +#ifndef STBI_NO_STDIO +#include +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for desired_channels + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +#include +typedef unsigned char stbi_uc; +typedef unsigned short stbi_us; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef STBIDEF +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +//////////////////////////////////// +// +// 8-bits-per-channel interface +// + +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + +#ifdef STBI_WINDOWS_UTF8 +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif + +//////////////////////////////////// +// +// 16-bits-per-channel interface +// + +STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif + +//////////////////////////////////// +// +// float-per-channel interface +// +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + + #ifndef STBI_NO_STDIO + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); + #endif +#endif + +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif // STBI_NO_HDR + +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_LINEAR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// on most compilers (and ALL modern mainstream compilers) this is threadsafe +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit (char const *filename); +STBIDEF int stbi_is_16_bit_from_file(FILE *f); +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// as above, but only applies to images loaded on the thread that calls the function +// this function is only available if your compiler supports thread-local variables; +// calling it will fail to link if your compiler doesn't +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply); +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert); +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include +#include // ptrdiff_t on osx +#include +#include +#include + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include // ldexp, pow +#endif + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_ASSERT +#include +#define STBI_ASSERT(x) assert(x) +#endif + +#ifdef __cplusplus +#define STBI_EXTERN extern "C" +#else +#define STBI_EXTERN extern +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + +#ifndef STBI_NO_THREAD_LOCALS + #if defined(__cplusplus) && __cplusplus >= 201103L + #define STBI_THREAD_LOCAL thread_local + #elif defined(__GNUC__) && __GNUC__ < 5 + #define STBI_THREAD_LOCAL __thread + #elif defined(_MSC_VER) + #define STBI_THREAD_LOCAL __declspec(thread) + #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) + #define STBI_THREAD_LOCAL _Thread_local + #endif + + #ifndef STBI_THREAD_LOCAL + #if defined(__GNUC__) + #define STBI_THREAD_LOCAL __thread + #endif + #endif +#endif + +#ifdef _MSC_VER +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (-(y) & 31))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// which in turn means it gets to use SSE2 everywhere. This is unfortunate, +// but previous attempts to provide the SSE2 functions with runtime +// detection caused numerous issues. The way architecture extensions are +// exposed in GCC/Clang is, sadly, not really suited for one-file libs. +// New behavior: if compiled with -msse2, we use SSE2 without any +// detection; if not, we don't use it at all. +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) +#define STBI_SSE2 +#include + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#endif + +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + // If we're even attempting to compile this on GCC/Clang, that means + // -msse2 is on, which means the compiler is allowed to use SSE2 + // instructions at will, and so are we. + return 1; +} +#endif + +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include +#ifdef _MSC_VER +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name +#else +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +#ifndef STBI_MAX_DIMENSIONS +#define STBI_MAX_DIMENSIONS (1 << 24) +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + int callback_already_read; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + int ch; + fseek((FILE*) user, n, SEEK_CUR); + ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ + if (ch != EOF) { + ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ + } +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user) || ferror((FILE *) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +enum +{ + STBI_ORDER_RGB, + STBI_ORDER_BGR +}; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} stbi__result_info; + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__png_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__psd_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__pnm_is16(stbi__context *s); +#endif + +static +#ifdef STBI_THREAD_LOCAL +STBI_THREAD_LOCAL +#endif +const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +#ifndef STBI_NO_FAILURE_STRINGS +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} +#endif + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stb_image uses ints pervasively, including for offset calculations. +// therefore the largest decoded image size we can support with the +// current code, even on 64-bit targets, is INT_MAX. this is not a +// significant limitation for the intended use case. +// +// we do, however, need to make sure our size calculations don't +// overflow. hence a few helper functions for size calculations that +// multiply integers together, making sure that they're non-negative +// and no overflow occurs. + +// return 1 if the sum is valid, 0 on overflow. +// negative terms are considered invalid. +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; +} + +// returns 1 if the product is valid, 0 on overflow. +// negative factors are considered invalid. +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX/b; +} + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} +#endif + +// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); +} +#endif + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// mallocs with size overflow checking +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} +#endif + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) +{ + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return stbi__malloc(a*b*c*d + add); +} +#endif + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load_global = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_global = flag_true_if_should_flip; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global +#else +static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; + +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_local = flag_true_if_should_flip; + stbi__vertically_flip_on_load_set = 1; +} + +#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ + ? stbi__vertically_flip_on_load_local \ + : stbi__vertically_flip_on_load_global) +#endif // STBI_THREAD_LOCAL + +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; + + // test the formats with a very explicit header first (at least a FOURCC + // or distinctive magic number first) + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); + #else + STBI_NOTUSED(bpc); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); + #endif + + // then the formats that can end up attempting to load with just 1 or 2 + // bytes matching expectations; these are prone to false positives, so + // try them later + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp, ri); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi_uc *reduced; + + reduced = (stbi_uc *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + + STBI_FREE(orig); + return reduced; +} + +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; + + enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); + if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + + STBI_FREE(orig); + return enlarged; +} + +static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) +{ + int row; + size_t bytes_per_row = (size_t)w * bytes_per_pixel; + stbi_uc temp[2048]; + stbi_uc *bytes = (stbi_uc *)image; + + for (row = 0; row < (h>>1); row++) { + stbi_uc *row0 = bytes + row*bytes_per_row; + stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; + // swap row0 with row1 + size_t bytes_left = bytes_per_row; + while (bytes_left) { + size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; + } + } +} + +#ifndef STBI_NO_GIF +static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) +{ + int slice; + int slice_size = w * h * bytes_per_pixel; + + stbi_uc *bytes = (stbi_uc *)image; + for (slice = 0; slice < z; ++slice) { + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; + } +} +#endif + +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 8) { + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } + + // @TODO: move stbi__convert_format to here + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); + } + + return (unsigned char *) result; +} + +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 16) { + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } + + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); + } + + return (stbi__uint16 *) result; +} + +#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); + } +} +#endif + +#ifndef STBI_NO_STDIO + +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) +STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); +#endif + +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) + return 0; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f,x,y,comp,req_comp); + fclose(f); + return result; +} + + +#endif //!STBI_NO_STDIO + +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_mem(&s,buffer,len); + + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); + if (stbi__vertically_flip_on_load) { + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + } + + return result; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + long pos = ftell(f); + int res; + stbi__context s; + stbi__start_file(&s,f); + res = stbi__hdr_test(&s); + fseek(f, pos, SEEK_SET); + return res; + #else + STBI_NOTUSED(f); + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; + #endif +} + +#ifndef STBI_NO_LINEAR +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) +// nothing +#else +static void stbi__skip(stbi__context *s, int n) +{ + if (n == 0) return; // already there! + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) +// nothing +#else +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} +#endif + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + z += (stbi__uint32)stbi__get16le(s) << 16; + return z; +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) +{ + return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + stbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output; + if (!data) return NULL; + output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + } + if (n < comp) { + for (i=0; i < x*y; ++i) { + output[i*comp + n] = data[i*comp + n]/255.0f; + } + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output; + if (!data) return NULL; + output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi__uint16 dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + int jfif; + int app14_color_transform; // Adobe APP14 tag + int rgb; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0; + unsigned int code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (stbi_uc) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (~0U << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + unsigned int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); + + sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & (sgn - 1)); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static const stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + diff = t ? stbi__extend_receive(j, t) : 0; + + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * (1 << j->succ_low)); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * (1 << shift)); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift)); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) * 4096) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0]*4; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); // consume repeated 0xff fill bytes + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4, sixteen = (p != 0); + int t = q & 15,i; + if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); + L -= (sixteen ? 129 : 65); + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len","Corrupt JPEG"); + else + return stbi__err("bad APP len","Corrupt JPEG"); + } + L -= 2; + + if (m == 0xE0 && L >= 5) { // JFIF APP0 segment + static const unsigned char tag[5] = {'J','F','I','F','\0'}; + int ok = 1; + int i; + for (i=0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment + static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; + int ok = 1; + int i; + for (i=0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 6; + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s); // color transform + L -= 6; + } + } + + stbi__skip(z->s, L); + return 1; + } + + return stbi__err("unknown marker","Corrupt JPEG"); +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) +{ + int i; + for (i=0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + c = stbi__get8(s); + if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + z->rgb = 0; + for (i=0; i < s->img_n; ++i) { + static const unsigned char rgb[3] = { 'R', 'G', 'B' }; + z->img_comp[i].id = stbi__get8(s); + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios + // and I've never seen a non-corrupted JPEG file actually use them + for (i=0; i < s->img_n; ++i) { + if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG"); + if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG"); + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + // these sizes can't be more than 17 bits + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + if (z->progressive) { + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->jfif = 0; + z->app14_color_transform = -1; // valid values are 0,1,2 + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + if (x == 255) { + j->marker = stbi__get8(j->s); + break; + } + } + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + } else { + if (!stbi__process_marker(j, m)) return 0; + } + m = stbi__get_marker(j); + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + stbi__free_jpeg_components(j, j->s->img_n, 0); +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +// fast 0..255 * 0..255 => 0..255 rounded multiplication +static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) +{ + unsigned int t = x*y + 128; + return (stbi_uc) ((t + (t >>8)) >> 8); +} + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n, is_rgb; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; + + is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); + + if (z->s->img_n == 3 && n < 3 && !is_rgb) + decode_n = 1; + else + decode_n = z->s->img_n; + + // nothing to do if no components requested; check this now to avoid + // accessing uninitialized coutput[0] later + if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; } + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + if (is_rgb) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) { // CMYK + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], m); + out[1] = stbi__blinn_8x8(coutput[1][i], m); + out[2] = stbi__blinn_8x8(coutput[2][i], m); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) { // YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], m); + out[1] = stbi__blinn_8x8(255 - out[1], m); + out[2] = stbi__blinn_8x8(255 - out[2], m); + out += n; + } + } else { // YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + if (is_rgb) { + if (n == 1) + for (i=0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i=0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } + } + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output + return output; + } +} + +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + unsigned char* result; + stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__errpuc("outofmem", "Out of memory"); + STBI_NOTUSED(ri); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x,y,comp,req_comp); + STBI_FREE(j); + return result; +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__err("outofmem", "Out of memory"); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); + stbi__rewind(s); + STBI_FREE(j); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + int result; + stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + if (!j) return stbi__err("outofmem", "Out of memory"); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) +#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[STBI__ZNSYMS]; + stbi__uint16 value[STBI__ZNSYMS]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static int stbi__zeof(stbi__zbuf *z) +{ + return (z->zbuffer >= z->zbuffer_end); +} + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + return stbi__zeof(z) ? 0 : *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + if (z->code_buffer >= (1U << z->num_bits)) { + z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ + return; + } + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s >= 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere! + if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) { + if (stbi__zeof(a)) { + return -1; /* report error for unexpected end of data. */ + } + stbi__fill_bits(a); + } + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + unsigned int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (unsigned int) (z->zout - z->zout_start); + limit = old_limit = (unsigned) (z->zout_end - z->zout_start); + if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); + while (cur + n > limit) { + if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); + limit *= 2; + } + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static const int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static const int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static const int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < ntot) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) { + c = stbi__zreceive(a,3)+3; + } else if (c == 18) { + c = stbi__zreceive(a,7)+11; + } else { + return stbi__err("bad codelengths", "Corrupt PNG"); + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); + n += c; + } + } + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncompressed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] = +{ + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 +}; +static const stbi_uc stbi__zdefault_distance[32] = +{ + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +}; +/* +Init algorithm: +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} +*/ + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; + int depth; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + int bytes = (depth == 16? 2 : 1); + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n*bytes; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; // copy it into a local for later + + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, + // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), + // so just check for raw_len < img_len always. + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *prior; + int filter = *raw++; + + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else if (depth == 16) { + if (img_n != out_n) { + cur[filter_bytes] = 255; // first pixel top byte + cur[filter_bytes+1] = 255; // first pixel bottom byte + } + raw += filter_bytes; + cur += output_bytes; + prior += output_bytes; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*filter_bytes; + #define STBI__CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; + } + #undef STBI__CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define STBI__CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ + for (k=0; k < filter_bytes; ++k) + switch (filter) { + STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; + } + #undef STBI__CASE + + // the loop above sets the high byte of the pixels' alpha, but for + // 16 bit png files we also need the low byte set. we'll do that here. + if (depth == 16) { + cur = a->out + stride*j; // start at the beginning of the row again + for (i=0; i < x; ++i,cur+=output_bytes) { + cur[filter_bytes+1] = 255; + } + } + } + } + + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + // insert alpha = 255 + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } else { + STBI_ASSERT(img_n == 3); + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; + } + } + } + } + } else if (depth == 16) { + // force the image data from big-endian to platform-native. + // this is done in a separate pass due to the decoding relying + // on the data being untouched, but could probably be done + // per-line during decode if care is taken. + stbi_uc *cur = a->out; + stbi__uint16 *cur16 = (stbi__uint16*)cur; + + for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { + *cur16 = (cur[0] << 8) | cur[1]; + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + if (!final) return stbi__err("outofmem", "Out of memory"); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16*) z->out; + + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load_global = 0; +static int stbi__de_iphone_flag_global = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_global = flag_true_if_should_convert; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global +#define stbi__de_iphone_flag stbi__de_iphone_flag_global +#else +static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; +static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; + +STBIDEF void stbi__unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; + stbi__unpremultiply_on_load_set = 1; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_local = flag_true_if_should_convert; + stbi__de_iphone_flag_set = 1; +} + +#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \ + ? stbi__unpremultiply_on_load_local \ + : stbi__unpremultiply_on_load_global) +#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \ + ? stbi__de_iphone_flag_local \ + : stbi__de_iphone_flag_global) +#endif // STBI_THREAD_LOCAL + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + stbi_uc half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = ( t * 255 + half) / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]={0}; + stbi__uint16 tc16[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); + s->img_y = stbi__get32be(s); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (scan == STBI__SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; + } + STBI_FREE(z->expanded); z->expanded = NULL; + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) +{ + void *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth <= 8) + ri->bits_per_channel = 8; + else if (p->depth == 16) + ri->bits_per_channel = 16; + else + return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp, ri); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} + +static int stbi__png_is16(stbi__context *s) +{ + stbi__png p; + p.s = s; + if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) + return 0; + if (p.depth != 16) { + stbi__rewind(p.s); + return 0; + } + return 1; +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) { n += 16; z >>= 16; } + if (z >= 0x00100) { n += 8; z >>= 8; } + if (z >= 0x00010) { n += 4; z >>= 4; } + if (z >= 0x00004) { n += 2; z >>= 2; } + if (z >= 0x00002) { n += 1;/* >>= 1;*/ } + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +// extract an arbitrarily-aligned N-bit value (N=bits) +// from v, and then make it 8-bits long and fractionally +// extend it to full full range. +static int stbi__shiftsigned(unsigned int v, int shift, int bits) +{ + static unsigned int mul_table[9] = { + 0, + 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, + 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, + }; + static unsigned int shift_table[9] = { + 0, 0,0,1,0,2,4,6,0, + }; + if (shift < 0) + v <<= -shift; + else + v >>= shift; + STBI_ASSERT(v < 256); + v >>= (8-bits); + STBI_ASSERT(bits >= 0 && bits <= 8); + return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; +} + +typedef struct +{ + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; + int extra_read; +} stbi__bmp_data; + +static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress) +{ + // BI_BITFIELDS specifies masks explicitly, don't override + if (compress == 3) + return 1; + + if (compress == 0) { + if (info->bpp == 16) { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } else if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + // otherwise, use defaults, which is all-0 + info->mr = info->mg = info->mb = info->ma = 0; + } + return 1; + } + return 0; // error +} + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + info->extra_read = 14; + + if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); + + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes + if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + if (compress == 0) { + stbi__bmp_set_mask_defaults(info, compress); + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->extra_read += 12; + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + // V4/V5 header + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs + stbi__bmp_set_mask_defaults(info, compress); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + } + return (void *) 1; +} + + +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + STBI_NOTUSED(ri); + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - info.extra_read - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - info.extra_read - info.hsz) >> 2; + } + if (psize == 0) { + if (info.offset != s->callback_already_read + (s->img_buffer - s->img_buffer_original)) { + return stbi__errpuc("bad offset", "Corrupt BMP"); + } + } + + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); + + out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 1) width = (s->img_x + 7) >> 3; + else if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + if (info.bpp == 1) { + for (j=0; j < (int) s->img_y; ++j) { + int bit_offset = 7, v = stbi__get8(s); + for (i=0; i < (int) s->img_x; ++i) { + int color = (v>>bit_offset)&0x1; + out[z++] = pal[color][0]; + out[z++] = pal[color][1]; + out[z++] = pal[color][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + if((--bit_offset) < 0) { + bit_offset = 7; + v = stbi__get8(s); + } + } + stbi__skip(s, pad); + } + } else { + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, info.offset - info.extra_read - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + unsigned int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i]; p1[i] = p2[i]; p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if (is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // fallthrough + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fallthrough + case 32: return bits_per_pixel/8; + default: return 0; + } +} + +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + +errorEnd: + stbi__rewind(s); + return res; +} + +// read 16bit value and convert to 24bit RGB +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = (stbi__uint16)stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (stbi_uc)((r * 255)/31); + out[1] = (stbi_uc)((g * 255)/31); + out[2] = (stbi_uc)((b * 255)/31); + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16=0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4] = {0}; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin); // @TODO + STBI_NOTUSED(tga_y_origin); // @TODO + + if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); + + tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + if (tga_palette_len == 0) { /* you have to have at least one entry! */ + STBI_FREE(tga_data); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + STBI_NOTUSED(tga_palette_start); + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) +{ + int count, nleft, len; + + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + + return 1; +} + +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + int pixelCount; + int channelCount, compression; + int channel, i; + int bitdepth; + int w,h; + stbi_uc *out; + STBI_NOTUSED(ri); + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); + + // Create the destination image. + + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *) stbi__malloc(4 * w*h); + + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + if (channel >= channelCount) { + // Fill this channel with default data. + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } else { + stbi_uc *p = out+channel; + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } + } else { + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out+channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + } + + // remove weird white matte from PSD + if (channelCount >= 4) { + if (ri->bits_per_channel == 16) { + for (i=0; i < w*h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); + } + } + } else { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } + } + } + } + + // convert to desired output format + if (req_comp && req_comp != 4) { + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) +{ + stbi_uc *result; + int i, x,y, internal_comp; + STBI_NOTUSED(ri); + + if (!comp) comp = &internal_comp; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + + if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + if (!result) return stbi__errpuc("outofmem", "Out of memory"); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + stbi_uc *background; // The current "background" as far as a gif is concerned + stbi_uc *history; + int flags, bgindex, ratio, transparent, eflags; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[8192]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; + int delay; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!g) return stbi__err("outofmem", "Out of memory"); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); + stbi__rewind( s ); + return 0; + } + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + int idx; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + idx = g->cur_x + g->cur_y; + p = &g->out[idx]; + g->history[idx / 4] = 1; + + c = &g->color_table[g->codes[code].suffix * 4]; + if (c[3] > 128) { // don't render transparent pixels; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) { + return stbi__errpuc("no clear code", "Corrupt GIF"); + } + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 8192) { + return stbi__errpuc("too many codes", "Corrupt GIF"); + } + + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +// two back is the image from two frames ago, used for a very specific disposal format +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) +{ + int dispose; + int first_frame; + int pi; + int pcount; + STBI_NOTUSED(req_comp); + + // on first frame, any non-written pixels get the background colour (non-transparent) + first_frame = 0; + if (g->out == 0) { + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) + return stbi__errpuc("too large", "GIF image is too large"); + pcount = g->w * g->h; + g->out = (stbi_uc *) stbi__malloc(4 * pcount); + g->background = (stbi_uc *) stbi__malloc(4 * pcount); + g->history = (stbi_uc *) stbi__malloc(pcount); + if (!g->out || !g->background || !g->history) + return stbi__errpuc("outofmem", "Out of memory"); + + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; + // background colour is only used for pixels that are not rendered first frame, after that "background" + // color refers to the color that was there the previous frame. + memset(g->out, 0x00, 4 * pcount); + memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) + memset(g->history, 0x00, pcount); // pixels that were affected previous frame + first_frame = 1; + } else { + // second frame - how do we dispose of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; + + if ((dispose == 3) && (two_back == 0)) { + dispose = 2; // if I don't have an image to revert back to, default to the old background + } + + if (dispose == 3) { // use previous graphic + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + } + } + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + } + } + } else { + // This is a non-disposal case eithe way, so just + // leave the pixels as is, and they will become the new background + // 1: do not dispose + // 0: not specified. + } + + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); + } + + // clear my history; + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + + for (;;) { + int tag = stbi__get8(s); + switch (tag) { + case 0x2C: /* Image Descriptor */ + { + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + // if the width of the specified rectangle is 0, that means + // we may not see *any* pixels or the image is malformed; + // to make sure this is caught, move the current y down to + // max_y (which is what out_gif_code checks). + if (w == 0) + g->cur_y = g->max_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (!o) return NULL; + + // if this was the first frame, + pcount = g->w * g->h; + if (first_frame && (g->bgindex > 0)) { + // if first frame, any pixel not drawn to gets the background color + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi] == 0) { + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + } + } + } + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + int ext = stbi__get8(s); + if (ext == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. + + // unset old transparent + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 255; + } + if (g->eflags & 0x01) { + g->transparent = stbi__get8(s); + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 0; + } + } else { + // don't need transparent + stbi__skip(s, 1); + g->transparent = -1; + } + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) { + stbi__skip(s, len); + } + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } +} + +static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays) +{ + STBI_FREE(g->out); + STBI_FREE(g->history); + STBI_FREE(g->background); + + if (out) STBI_FREE(out); + if (delays && *delays) STBI_FREE(*delays); + return stbi__errpuc("outofmem", "Out of memory"); +} + +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + if (stbi__gif_test(s)) { + int layers = 0; + stbi_uc *u = 0; + stbi_uc *out = 0; + stbi_uc *two_back = 0; + stbi__gif g; + int stride; + int out_size = 0; + int delays_size = 0; + + STBI_NOTUSED(out_size); + STBI_NOTUSED(delays_size); + + memset(&g, 0, sizeof(g)); + if (delays) { + *delays = 0; + } + + do { + u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + + if (u) { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; + + if (out) { + void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); + if (!tmp) + return stbi__load_gif_main_outofmem(&g, out, delays); + else { + out = (stbi_uc*) tmp; + out_size = layers * stride; + } + + if (delays) { + int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + if (!new_delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + *delays = new_delays; + delays_size = layers * sizeof(int); + } + } else { + out = (stbi_uc*)stbi__malloc( layers * stride ); + if (!out) + return stbi__load_gif_main_outofmem(&g, out, delays); + out_size = layers * stride; + if (delays) { + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + if (!*delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + delays_size = layers * sizeof(int); + } + } + memcpy( out + ((layers - 1) * stride), u, stride ); + if (layers >= 2) { + two_back = out - 2 * stride; + } + + if (delays) { + (*delays)[layers - 1U] = g.delay; + } + } + } while (u != 0); + + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + + // do the final conversion after loading everything; + if (req_comp && req_comp != 4) + out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); + + *z = layers; + return out; + } else { + return stbi__errpuc("not GIF", "Image was not as a gif type."); + } +} + +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *u = 0; + stbi__gif g; + memset(&g, 0, sizeof(g)); + STBI_NOTUSED(ri); + + u = stbi__gif_load_next(s, &g, comp, req_comp, 0); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + + // moved conversion to after successful load so that the same + // can be done for multiple frames. + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } else if (g.out) { + // if there was an error and we allocated an image buffer, free it! + STBI_FREE(g.out); + } + + // free buffers needed for multiple frame loading; + STBI_FREE(g.history); + STBI_FREE(g.background); + + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s, const char *signature) +{ + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + stbi__rewind(s); + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); + stbi__rewind(s); + if(!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + const char *headerToken; + STBI_NOTUSED(ri); + + // Check identifier + headerToken = stbi__hdr_gettoken(s,buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); + + // Read data + hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } + + for (k = 0; k < 4; ++k) { + int nleft; + i = 0; + while ((nleft = width - i) > 0) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + if (scanline) + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int dummy; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (stbi__hdr_test(s) == 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + if (p == NULL) { + stbi__rewind( s ); + return 0; + } + if (x) *x = s->img_x; + if (y) *y = s->img_y; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount, dummy, depth; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 8 && depth != 16) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi__psd_is16(stbi__context *s) +{ + int channelCount, depth; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + STBI_NOTUSED(stbi__get32be(s)); + STBI_NOTUSED(stbi__get32be(s)); + depth = stbi__get16be(s); + if (depth != 16) { + stbi__rewind( s ); + return 0; + } + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained,dummy; + stbi__pic_packet packets[10]; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + STBI_NOTUSED(ri); + + ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n); + if (ri->bits_per_channel == 0) + return 0; + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + + if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0)) + return stbi__errpuc("too large", "PNM too large"); + + out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8)); + + if (req_comp && req_comp != s->img_n) { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv, dummy; + char c, p, t; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + stbi__rewind(s); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind(s); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + if (maxv > 65535) + return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images"); + else if (maxv > 255) + return 16; + else + return 8; +} + +static int stbi__pnm_is16(stbi__context *s) +{ + if (stbi__pnm_info(s, NULL, NULL, NULL) == 16) + return 1; + return 0; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +static int stbi__is_16_main(stbi__context *s) +{ + #ifndef STBI_NO_PNG + if (stbi__png_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_is16(s)) return 1; + #endif + return 0; +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} + +STBIDEF int stbi_is_16_bit(char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_is_16_bit_from_file(f); + fclose(f); + return result; +} + +STBIDEF int stbi_is_16_bit_from_file(FILE *f) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__is_16_main(&s); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__is_16_main(&s); +} + +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__is_16_main(&s); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug + 1-bit BMP + *_is_16_bit api + avoid warnings + 2.16 (2017-07-23) all functions have 16-bit variants; + STBI_NO_STDIO works again; + compilation fixes; + fix rounding in unpremultiply; + optimize vertical flip; + disable raw_len validation; + documentation fixes + 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; + warning fixes; disable run-time SSE detection on gcc; + uniform handling of optional "return" values; + thread-safe initialization of zlib tables + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) allocate large structures on the stack + remove white matting for transparent PSD + fix reported channel count for PNG & BMP + re-enable SSE2 in non-gcc 64-bit + support RGB-formatted JPEG + read 16-bit PNGs (only as 8-bit) + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +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. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +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 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. +------------------------------------------------------------------------------ +*/ diff --git a/r5dev/public/include/utility.h b/r5dev/public/include/utility.h new file mode 100644 index 00000000..fa4ec9ee --- /dev/null +++ b/r5dev/public/include/utility.h @@ -0,0 +1,20 @@ +#pragma once +#include + +///////////////////////////////////////////////////////////////////////////// +// Internals +BOOL FileExists(LPCTSTR szPath); +MODULEINFO GetModuleInfo(const char* szModule); +DWORD64 FindPatternV1(const char* szModule, const unsigned char* szPattern, const char* szMask); +DWORD64 FindPatternSIMD(const char* szModule, const unsigned char* szPattern, const char* szMask); + +///////////////////////////////////////////////////////////////////////////// +// Utility +void DbgPrint(LPCSTR sFormat, ...); +void HexDump(const char* szHeader, int nFunc, const void* pData, int nSize); +std::string Base64Encode(const std::string& in); +std::string Base64Decode(const std::string& in); +bool StringReplace(std::string& str, const std::string& from, const std::string& to); +std::string CreateDirectories(std::string svFilePath); + +///////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/public/utility.cpp b/r5dev/public/utility.cpp new file mode 100644 index 00000000..d92099b0 --- /dev/null +++ b/r5dev/public/utility.cpp @@ -0,0 +1,298 @@ +/*----------------------------------------------------------------------------- + * _utility + *-----------------------------------------------------------------------------*/ + +#include "core/stdafx.h" +#include "core/logdef.h" +#include "public/include/utility.h" + +/////////////////////////////////////////////////////////////////////////////// +// For checking if a specific file exists. +BOOL FileExists(LPCTSTR szPath) +{ + DWORD dwAttrib = GetFileAttributes(szPath); + + return (dwAttrib != INVALID_FILE_ATTRIBUTES && + !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); +} + +/////////////////////////////////////////////////////////////////////////////// +// For getting information about the executing module. +MODULEINFO GetModuleInfo(const char* szModule) +{ + MODULEINFO modinfo = { 0 }; + HMODULE hModule = GetModuleHandle(szModule); + if (hModule == 0) + { + return modinfo; + } + GetModuleInformation(GetCurrentProcess(), hModule, &modinfo, sizeof(MODULEINFO)); + return modinfo; +} + +/////////////////////////////////////////////////////////////////////////////// +// For finding a byte pattern in memory of the process. +BOOL Compare(const unsigned char* pData, const unsigned char* szPattern, const char* szMask) +{ + for (; *szMask; ++szMask, ++pData, ++szPattern) + { + if (*szMask == 'x' && *pData != *szPattern) + { + return false; + } + } + return (*szMask) == NULL; +} +DWORD64 FindPatternV1(const char* szModule, const unsigned char* szPattern, const char* szMask) +{ + MODULEINFO mInfo = GetModuleInfo(szModule); + DWORD64 dwAddress = (DWORD64)mInfo.lpBaseOfDll; + DWORD64 dwLen = (DWORD64)mInfo.SizeOfImage; + + size_t maskLen = strlen(szMask); + for (int i = 0; i < dwLen - maskLen; i++) + { + if (Compare((unsigned char*)(dwAddress + i), szPattern, szMask)) + { + return (dwAddress + i); + } + } + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +// For finding a pattern in memory of the process with SIMD. +DWORD64 FindPatternSIMD(const char* szModule, const unsigned char* szPattern, const char* szMask) +{ + MODULEINFO mInfo = GetModuleInfo(szModule); + DWORD64 dwBase = (DWORD64)mInfo.lpBaseOfDll; + DWORD64 dwSize = (DWORD64)mInfo.SizeOfImage; + + unsigned char* pData = (unsigned char*)dwBase; + unsigned int length = (unsigned int)dwSize; + + const unsigned char* end = pData + length - strlen(szMask); + int num_masks = (int)ceil((float)strlen(szMask) / (float)16); + int masks[32]; // 32*16 = enough masks for 512 bytes. + memset(masks, 0, num_masks * sizeof(int)); + for (int64_t i = 0; i < num_masks; ++i) + { + for (int64_t j = strnlen(szMask + i * 16, 16) - 1; j >= 0; --j) + { + if (szMask[i * 16 + j] == 'x') + { + masks[i] |= 1 << j; + } + } + } + __m128i xmm1 = _mm_loadu_si128((const __m128i*) szPattern); + __m128i xmm2, xmm3, msks; + for (; pData != end; _mm_prefetch((const char*)(++pData + 64), _MM_HINT_NTA)) + { + if (szPattern[0] == pData[0]) + { + xmm2 = _mm_loadu_si128((const __m128i*) pData); + msks = _mm_cmpeq_epi8(xmm1, xmm2); + if ((_mm_movemask_epi8(msks) & masks[0]) == masks[0]) + { + for (DWORD64 i = 1; i < num_masks; ++i) + { + xmm2 = _mm_loadu_si128((const __m128i*) (pData + i * 16)); + xmm3 = _mm_loadu_si128((const __m128i*) (szPattern + i * 16)); + msks = _mm_cmpeq_epi8(xmm2, xmm3); + if ((_mm_movemask_epi8(msks) & masks[i]) == masks[i]) + { + if ((i + 1) == num_masks) + { + return (DWORD64)pData; + } + } + else goto cont; + } + return (DWORD64)pData; + } + }cont:; + } + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +// For printing output to the debugger. +void DbgPrint(LPCSTR sFormat, ...) +{ + CHAR sBuffer[512] = { 0 }; + va_list sArgs = {}; + + // Get the variable arg pointer. + va_start(sArgs, sFormat); + + // Format print the string. + int length = vsnprintf(sBuffer, sizeof(sBuffer), sFormat, sArgs); + va_end(sArgs); + + // Output the string to the debugger. + OutputDebugString(sBuffer); +} + +/////////////////////////////////////////////////////////////////////////////// +// For dumping data from a buffer to a file on the disk +void HexDump(const char* szHeader, int nFunc, const void* pData, int nSize) +{ + static unsigned char szAscii[17] = {}; + static std::atomic i = {}, j = {}, k = {}; + static std::shared_ptr logger = spdlog::get("default_logger"); + + // Loop until the function returned to the first caller. + while (k == 1) { /*Sleep(75);*/ } + + k = 1; + szAscii[16] = '\0'; + + // Add new loggers here to replace the placeholder. + if (nFunc == 0) { logger = g_spd_netchan_logger; } + + // Add timestamp. + logger->set_level(spdlog::level::trace); + logger->set_pattern("%v [%H:%M:%S.%f]\n"); + logger->trace("---------------------------------------------------------"); + + // Disable EOL and create block header. + logger->set_pattern("%v"); + logger->trace("{:s} ---- LEN BYTES: {}\n:\n", szHeader, nSize); + logger->trace("-------- 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF\n"); + + // Output the buffer to the file. + for (i = 0; i < nSize; i++) + { + if (i % nSize == 0) { logger->trace(" 0x{:04X} ", i); } + logger->trace("{:02x} ", ((unsigned char*)pData)[i]); + + if (((unsigned char*)pData)[i] >= ' ' && ((unsigned char*)pData)[i] <= '~') { szAscii[i % 16] = ((unsigned char*)pData)[i]; } + else { szAscii[i % 16] = '.'; } + + if ((i + 1) % 8 == 0 || i + 1 == nSize) + { + logger->trace(" "); + + if ((i + 1) % 16 == 0) + { + if (i + 1 == nSize) + { + logger->trace("{:s}\n", szAscii); + logger->trace("---------------------------------------------------------------------------\n"); + logger->trace("\n"); + } + else + { + i++; + logger->trace("{:s}\n ", szAscii); + logger->trace("0x{:04X} ", i--); + } + } + else if (i + 1 == nSize) + { + szAscii[(i + 1) % 16] = '\0'; + if ((i + 1) % 16 <= 8) + { + logger->trace(" "); + } + for (j = (i + 1) % 16; j < 16; j++) + { + logger->trace(" "); + } + logger->trace("{:s}\n", szAscii); + logger->trace("---------------------------------------------------------------------------\n"); + logger->trace("\n"); + } + } + } + k = 0; + /////////////////////////////////////////////////////////////////////////// +} + +/////////////////////////////////////////////////////////////////////////////// +// For encoding data in base64. +std::string Base64Encode(const std::string& in) +{ + std::string results; + int val = 0, valb = -6; + + for (unsigned char c : in) + { + val = (val << 8) + c; + valb += 8; + while (valb >= 0) + { + results.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(val >> valb) & 0x3F]); + valb -= 6; + } + } + if (valb > -6) + { + results.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[((val << 8) >> (valb + 8)) & 0x3F]); + } + while (results.size() % 4) + { + results.push_back('='); + } + return results; +} + +/////////////////////////////////////////////////////////////////////////////// +// For decoding data in base64. +std::string Base64Decode(const std::string& in) +{ + std::string results; + int val = 0, valb = -8; + + std::vector T(256, -1); + for (int i = 0; i < 64; i++) + { + T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i; + } + + for (unsigned char c : in) + { + if (T[c] == -1) + { + break; + } + val = (val << 6) + T[c]; + valb += 6; + if (valb >= 0) + { + results.push_back(char((val >> valb) & 0xFF)); + valb -= 8; + } + } + return results; +} + +/////////////////////////////////////////////////////////////////////////////// +// For replacing parts of a given string. +bool StringReplace(std::string& str, const std::string& from, const std::string& to) +{ + size_t start_pos = str.find(from); + if (start_pos == std::string::npos) + { + return false; + } + + str.replace(start_pos, from.length(), to); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// For creating directories for output streams. +std::string CreateDirectories(std::string svFilePath) +{ + std::filesystem::path fspPathOut(svFilePath); + std::string results = fspPathOut.u8string(); + + StringReplace(svFilePath, "\\ \\", "\\"); + fspPathOut = fspPathOut.parent_path(); + + std::filesystem::create_directories(fspPathOut); + + return results; +} diff --git a/r5dev/r5dev.def b/r5dev/r5dev.def index fadeb105..3438df9f 100644 --- a/r5dev/r5dev.def +++ b/r5dev/r5dev.def @@ -1,4 +1,4 @@ -LIBRARY r5detours +LIBRARY r5apexsdkd EXPORTS - DummyExport @1 \ No newline at end of file + DummyExport @1 diff --git a/r5launcher/Apex.props b/r5dev/r5dev.props similarity index 100% rename from r5launcher/Apex.props rename to r5dev/r5dev.props diff --git a/r5dev/r5dev.vcxproj b/r5dev/r5dev.vcxproj index b26f9ccf..f6378d8b 100644 --- a/r5dev/r5dev.vcxproj +++ b/r5dev/r5dev.vcxproj @@ -1,4 +1,4 @@ - + @@ -18,6 +18,283 @@ x64 + + + + + + + + + + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 16.0 Win32Proj @@ -30,29 +307,30 @@ DynamicLibrary true - v142 + v143 Unicode DynamicLibrary false - v142 + v143 true Unicode DynamicLibrary true - v142 + v143 MultiByte + Static DynamicLibrary false - v142 + v143 true MultiByte - false + Static @@ -71,7 +349,7 @@ - + @@ -82,22 +360,21 @@ true - $(SolutionDir)r5net\include\r5;$(SolutionDir)external\minhook\include;$(SolutionDir)external\imgui\include;$(SolutionDir)external\spdlog\include;$(SolutionDir)shared\include;$(SolutionDir)r5dev\include;$(IncludePath) - $(SolutionDir)external\minhook\lib\$(Configuration);$(SolutionDir)r5net\lib\$(Configuration);$(LibraryPath) + $(SolutionDir)r5dev\;$(IncludePath);$(DXSDK_DIR)Include + $(SolutionDir)r5dev\thirdparty\detours\libs;$(SolutionDir)r5dev\thirdparty\lzham\libs;$(LibraryPath);$(DXSDK_DIR)Lib\x64 + r5apexvtxd $(SolutionDir)bin\$(Configuration)\ - r5detours $(SolutionDir)build\$(ProjectName)\$(Configuration)\ + $(VC_ReferencesPath_x64); false - $(SolutionDir)r5net\include\r5;$(SolutionDir)external\minhook\include;$(SolutionDir)external\imgui\include;$(SolutionDir)external\spdlog\include;$(SolutionDir)shared\include;$(SolutionDir)r5dev\include;$(IncludePath) - $(SolutionDir)external\minhook\lib\$(Configuration);$(SolutionDir)r5net\lib\$(Configuration);$(LibraryPath) + $(SolutionDir)r5dev\;$(IncludePath);$(DXSDK_DIR)Include + $(SolutionDir)r5dev\thirdparty\detours\libs;$(SolutionDir)r5dev\thirdparty\lzham\libs;$(LibraryPath);$(DXSDK_DIR)Lib\x64 + r5apexsdkd + $(VC_ReferencesPath_x64); $(SolutionDir)bin\$(Configuration)\ $(SolutionDir)build\$(ProjectName)\$(Configuration)\ - r5detours - - - false @@ -142,31 +419,22 @@ _DEBUG;R5DEV_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true Use - pch.h - stdc17 + core\stdafx.h + + stdcpp17 - MultiThreadedDebug + stdc17 Windows true false r5dev.def - Minhook.x64.lib;r5net.lib;bcrypt.lib;%(AdditionalDependencies) + detours.lib;lzhamlib_x64D.lib;lzhamcomp_x64D.lib;lzhamdecomp_x64D.lib;d3d11.lib;bcrypt.lib;%(AdditionalDependencies) - del "$(SolutionDir)bin\$(Configuration)\r5dev.dll" -rename "$(TargetPath)" "r5dev.dll" + copy /Y "$(TargetPath)" "..\..\..\bin\$(TargetFileName)" - - if not EXIST $(SolutionDir)external\minhook\lib\$(Configuration)\Minhook.x64.lib ( -"$(DevEnvDir)devenv" "$(SolutionDir)apex.sln" /Rebuild $(Configuration) /project "$(SolutionDir)external\minhook\libMinHook.vcxproj" -); - -if not EXIST $(SolutionDir)r5net\lib\$(Configuration)\r5net.lib ( -"$(DevEnvDir)devenv" "$(SolutionDir)apex.sln" /Rebuild $(Configuration) /project "$(SolutionDir)r5net\r5net.vcxproj" -); - @@ -177,14 +445,19 @@ if not EXIST $(SolutionDir)r5net\lib\$(Configuration)\r5net.lib ( NDEBUG;R5DEV_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true Use - pch.h + core\stdafx.h + Speed + + + true + true + AnySuitable stdc17 stdcpp17 - AnySuitable - Speed - true true - MultiThreadedDLL + + + true Windows @@ -193,249 +466,12 @@ if not EXIST $(SolutionDir)r5net\lib\$(Configuration)\r5net.lib ( true false r5dev.def - Minhook.x64.lib;r5net.lib;bcrypt.lib;%(AdditionalDependencies) + detours.lib;lzhamlib_x64.lib;lzhamcomp_x64.lib;lzhamdecomp_x64.lib;d3d11.lib;bcrypt.lib;%(AdditionalDependencies) - - + copy /Y "$(TargetPath)" "..\..\..\$(TargetFileName)" && del "..\..\..\r5apexsdkd64.dll" && rename "..\..\..\$(TargetFileName)" "r5apexsdkd64.dll" - - if not EXIST $(SolutionDir)external\minhook\lib\$(Configuration)\Minhook.x64.lib ( -"$(DevEnvDir)devenv" "$(SolutionDir)apex.sln" /Rebuild $(Configuration) /project "$(SolutionDir)external\minhook\libMinHook.vcxproj" -); - -if not EXIST $(SolutionDir)r5net\lib\$(Configuration)\r5net.lib ( -"$(DevEnvDir)devenv" "$(SolutionDir)apex.sln" /Rebuild $(Configuration) /project "$(SolutionDir)r5net\r5net.vcxproj" -); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NotUsing - NotUsing - - - NotUsing - NotUsing - - - NotUsing - NotUsing - - - NotUsing - NotUsing - - - NotUsing - NotUsing - - - NotUsing - NotUsing - - - NotUsing - NotUsing - - - NotUsing - NotUsing - - - - - - Use - pch.h - - - Use - pch.h - - - Use - pch.h - - - - - - - - - - - - - - - - - Use - pch.h - - - Use - pch.h - - - - - Use - pch.h - - - Use - pch.h - - - Create - pch.h - Create - pch.h - - - Use - pch.h - - - - - - - - {f04be619-0326-4ff1-b06b-fbe882e04d5e} - - diff --git a/r5dev/r5dev.vcxproj.filters b/r5dev/r5dev.vcxproj.filters index 7d03082e..d2e7e5bf 100644 --- a/r5dev/r5dev.vcxproj.filters +++ b/r5dev/r5dev.vcxproj.filters @@ -2,611 +2,843 @@ - {927ea852-3616-4fc4-8b32-781f65853a6b} + {5857edb8-da8e-491a-96a1-00a4fcfe3d38} - - {b421eb0e-1e3d-4a7a-a8d8-f9b397911702} + + {8de4910c-a79a-4e5b-8da1-d6da52425492} - - {c2ad07a8-ef40-4faa-b86c-19b31bf3209d} + + {d2a0971d-1de6-47f7-8a77-4246bc0cda3c} - - {633d6e7a-c709-4d64-a134-b383d43c8c8e} + + {c00eb6b9-50b2-4fc3-9e24-d8f7ff601e0e} - - {7cef4f92-94d9-43af-bea5-c6963d68fff2} + + {8caa20bd-7dea-4cb9-a69f-e90498e96a93} - - {c72b9789-72e9-4657-8a42-8712aaf8690e} + + {c069ad8a-c3be-4e61-afbf-f3eee3846d83} - - {757af774-e575-4623-8582-71efb0ae53a4} + + {0b1b90e6-79f6-4b19-8f5e-aac20d346094} - - {c18fb898-adc3-4aa8-902c-4777bbc76e5b} + + {fb47caac-589a-490f-9a7d-00f53add78cc} - - {7b737633-f8a6-464b-868c-c63854984082} + + {4afffd57-c78e-420f-a0a7-863b04e64812} - - {f832e462-9066-45a9-8f58-f865d5c24de3} + + {86b2ad0f-8709-48fa-8e92-0e9b8053ffce} - - {af02cb01-8e1d-49f9-b3da-cff2cf7dc7a1} + + {09875779-c73f-4138-9ed5-14af2b9d8337} - - {9d3b7c40-958a-4f6e-9ab6-72c3caea9591} + + {1dd9c7b9-1abc-412a-8e85-928c0d017beb} - - {6faf53e7-9be1-439f-92f9-16ab96c005b7} + + {525505fd-c8bb-453c-8cce-e11123499ae9} - - {e38cd6c5-b355-4bb5-bf65-bfd51fef296b} + + {10a04e38-fd13-476b-92ea-ebbe60803ef6} - - {30005d10-4213-441c-b18b-4dd47fcb8812} + + {7977cef5-28c7-40ca-be44-394d9f1b75de} - - {e901ef66-ddeb-4aff-9d7e-786d74a791e9} + + {3d8e58ec-0228-40ca-8ec8-66791a71bc08} - - {da82c6eb-abba-4b52-84bf-9f3758a169bf} + + {4759136c-9b08-4e32-91a9-a71c62f019c1} - - {6a6d7e33-2ce0-4b53-bcf6-41dc6dacdc5f} + + {c54c9382-0c06-4046-9e91-fb2a8019a22b} - - {e449786f-692c-4d6c-a292-b0720bf9e2a9} + + {0ac635a8-3c25-4b4b-b382-1bb8bc882c55} - - {46b49d69-ea42-467c-86d8-00611f2718ff} + + {db621634-f890-4852-b8dc-d540dffd433a} - - {2ec9179b-7320-47da-b170-f021d0279310} + + {2fb19b52-f018-4e95-8eca-ed230132ce0b} - - {83391c47-4a5b-4231-a9ef-29e119c5bb69} + + {d0a016cc-d3bc-449b-be29-29289dc550cb} - - {c27e3539-0070-40fa-b262-0ecd39f47919} + + {47bf6838-c9bf-4429-b55a-9a00339c630f} - - {afb847fc-853f-4e6f-bde6-91bf345369b4} + + {510de005-f0c8-48b9-b274-2424ccf8f7c7} - - {9f8f15a7-6e69-4a79-b644-a92ad815c600} + + {7cdf9989-f142-4f8a-a5ee-595fba1a354f} - - {25c5fe7f-23c0-4d44-b92d-ea4b127eec05} + + {ce08331e-176d-4071-8564-2cbda5a09f34} - - {0420f99f-468e-4ba5-abe3-960cc332b636} + + {a37df87a-dfda-47ab-a1be-1e49bdfecf44} - - {a40dcf17-855b-4965-b58d-6c7d24edc627} + + {a7469cda-54f2-4efc-8a58-6105c74845f9} - - {89bf5311-5686-4077-9b79-c6306dc1bd91} + + {f7c791f6-2cda-4702-88bd-458ea662e7ef} - - {3a4ca756-24d2-47e8-af7f-0e8a75933c4b} + + {956f49fb-0d6a-47d6-96a0-9d616e47e5f2} - - {58dbc5f6-cf3c-4d71-80c8-caefffb39beb} + + {698f4add-abac-46e5-a3f8-55d3a230b369} - - {0136e8e8-91ef-45c1-8661-476b5c20fc48} + + {942b8ea5-ce53-4e1e-ad7a-845991aaead6} - - {1979149f-6402-4985-b900-25a91f1168ac} + + {b0696621-8658-4918-b0f2-ba20acc26829} - - {10edfee7-8c10-41de-b8f3-424826d2614a} + + {cbe60970-f348-4a8b-8cee-d4cfebbe0d99} - - {02d83321-09fe-4a60-86d9-b1e8d5e165f4} + + {14a61eec-93ec-4e7c-b0bf-2ce23c3b782c} - - {a2663195-c4f2-4d5f-8d65-cfed54976e4c} + + {64bc6e43-c5e7-474f-be64-31df89e0034b} - - {10a22c13-763e-4054-bf6a-8f4b61697520} + + {d914384a-56bc-4829-977b-5900f01b5612} - - {04423080-121a-4982-8fd5-1d1d17157cbd} + + {da0b06b6-feab-44a9-bf9e-afb9e103eceb} + + + {7995c0ea-4972-4c52-854a-bd94cbdc6be9} + + + {336e3141-0276-4cd5-a836-585eef681b7b} - + + sdk\client + + + sdk\client + + + sdk\client + + + sdk\ebisusdk + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\gameui + + + sdk\gameui + + + sdk\public + + + sdk\public + + + sdk\rtech + + + sdk\rtech + + + sdk\inputsystem + + + sdk\launcher + + + sdk\server + + + sdk\squirrel + + + sdk\squirrel + + + thirdparty\imgui + + + thirdparty\imgui + + + thirdparty\imgui + + + thirdparty\imgui + + + thirdparty\imgui + + + thirdparty\imgui + + + thirdparty\imgui + + + thirdparty\imgui + + + sdk\tier0 + + + sdk\tier0 + + + sdk\tier0 + + + sdk\tier0 + + + sdk\vgui + + + sdk\vgui + + + sdk\vguimatsurface + + + sdk\vpc + + + sdk\vpc + + + sdk\vpc + + + sdk\vphysics + + + windows + + + windows + + + windows + + core - + core - - shared\libraries\imgui - - - shared\libraries\imgui - - - shared\libraries\imgui - - - shared\libraries\imgui - - - shared\libraries\imgui - - - shared\libraries\imgui - - - shared\libraries\imgui - - - shared - - - hooks\src\chlclient - - - hooks\src\squirrel - - - hooks\src\cvengineserver - - - hooks\src\other - - - hooks\src\netchannel - - - hooks\src\iconvar - - - hooks\src\winapi - - - hooks\src - - - r5-sdk\src - - + core - - core + + thirdparty\imgui - - r5-sdk\src + + sdk\common - - shared\libraries\imgui + + sdk\mathlib - - gui\interface + + sdk\networksystem - - gui\interface + + sdk\squirrel - - gui + + sdk\server - - gui + + sdk\vpklib - - hooks\src\netchannel + + sdk\public - - hooks\src\other + + sdk\bsplib - - hooks\src\cserver + + sdk\mathlib - - r5-sdk\src - - - hooks\src\hoststate - - - hooks\src\cmatsystemsurface - - - hooks\src\cenginevgui - - - r5-sdk\src - - - hooks\src\originsdk + + sdk\mathlib - - shared\libraries\imgui\include + + sdk\client - - shared\libraries\imgui\include + + sdk\client - - shared\libraries\imgui\include + + sdk\client - - shared\libraries\imgui\include + + sdk\common - - shared\libraries\imgui\include + + sdk\ebisusdk - - shared\libraries\imgui\include + + sdk\engine - - shared\libraries\imgui\include + + sdk\engine - - shared\libraries\imgui\include + + sdk\engine - - shared\libraries\spdlog\include + + sdk\engine - - shared\libraries\spdlog\include + + sdk\engine - - shared\libraries\spdlog\include + + sdk\engine - - shared\libraries\spdlog\include + + sdk\engine - - shared\libraries\spdlog\include + + sdk\gameui - - shared\libraries\spdlog\include + + sdk\gameui - - shared\libraries\spdlog\include + + sdk\public\include - - shared\libraries\spdlog\include + + sdk\public\include - - shared\libraries\spdlog\include + + sdk\public\include - - shared\libraries\spdlog\include + + sdk\public\include - - shared\libraries\spdlog\include + + sdk\public\include - - shared\libraries\spdlog\include + + sdk\rtech - - shared\libraries\spdlog\include + + sdk\rtech - - shared\libraries\spdlog\include + + sdk\inputsystem - - shared\libraries\spdlog\include + + sdk\inputsystem - - shared\libraries\spdlog\include + + sdk\launcher - - shared\libraries\spdlog\include\cfg + + sdk\mathlib - - shared\libraries\spdlog\include\cfg + + sdk\networksystem - - shared\libraries\spdlog\include\cfg + + sdk\server - - shared\libraries\spdlog\include\cfg + + sdk\squirrel - - shared\libraries\spdlog\include\details + + sdk\squirrel - - shared\libraries\spdlog\include\details + + thirdparty\detours\include - - shared\libraries\spdlog\include\details + + thirdparty\detours\include - - shared\libraries\spdlog\include\details + + thirdparty\detours\include - - shared\libraries\spdlog\include\details + + thirdparty\detours\include - - shared\libraries\spdlog\include\details + + thirdparty\imgui\include - - shared\libraries\spdlog\include\details + + thirdparty\imgui\include - - shared\libraries\spdlog\include\details + + thirdparty\imgui\include - - shared\libraries\spdlog\include\details + + thirdparty\imgui\include - - shared\libraries\spdlog\include\details + + thirdparty\imgui\include - - shared\libraries\spdlog\include\details + + thirdparty\imgui\include - - shared\libraries\spdlog\include\details + + thirdparty\imgui\include - - shared\libraries\spdlog\include\details + + thirdparty\imgui\include - - shared\libraries\spdlog\include\details + + thirdparty\imgui\include - - shared\libraries\spdlog\include\details + + thirdparty\imgui\include - - shared\libraries\spdlog\include\details + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\details + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\details + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\details + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\details + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\details + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\details + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\details + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\details + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\details + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\fmt + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\fmt + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\fmt + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\fmt + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\fmt + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\fmt\bundled + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\fmt\bundled + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\fmt\bundled + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\fmt\bundled + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\fmt\bundled + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\fmt\bundled + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\fmt\bundled + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\fmt\bundled + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\fmt\bundled + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\fmt\bundled + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\fmt\bundled + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\fmt\bundled + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\fmt\bundled + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\fmt - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\fmt - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\fmt - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\fmt - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\fmt - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\fmt\bundled - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\fmt\bundled - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\fmt\bundled - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\fmt\bundled - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\fmt\bundled - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\fmt\bundled - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\fmt\bundled - - shared\include + + thirdparty\spdlog\include\fmt\bundled - - shared\include + + thirdparty\spdlog\include\fmt\bundled - - shared\include + + thirdparty\spdlog\include\fmt\bundled - - shared\include + + thirdparty\spdlog\include\fmt\bundled - - core\include + + thirdparty\spdlog\include\fmt\bundled - - shared\libraries\minhook\include + + thirdparty\spdlog\include\fmt\bundled - - hooks\include + + thirdparty\spdlog\include\details - - gui\include + + thirdparty\spdlog\include\details - - gui\include + + thirdparty\spdlog\include\details - - r5-sdk\include + + thirdparty\spdlog\include\details - - core\include + + thirdparty\spdlog\include\details - + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\cfg + + + thirdparty\spdlog\include\cfg + + + thirdparty\spdlog\include\cfg + + + thirdparty\spdlog\include\cfg + + + sdk\tier0 + + + sdk\tier0 + + + sdk\tier0 + + + sdk\tier0 + + + sdk\tier0 + + + sdk\vgui + + + sdk\vgui + + + sdk\vgui + + + sdk\vguimatsurface + + + sdk\vpc + + + sdk\vpc + + + sdk\vpc + + + sdk\vpc + + + sdk\vphysics + + + windows + + + windows + + + windows + + core - - r5-sdk\include + + core - - r5-sdk\include + + core - - r5-sdk\include + + core - - r5-sdk\include + + sdk\common - - core\include + + sdk\mathlib - - shared\libraries\imgui\include + + sdk\networksystem - - gui\include + + sdk\networksystem - - gui\include + + sdk\mathlib - - - shared\include + + sdk\public\include - - r5-sdk\include + + sdk\squirrel - - r5-sdk\include + + core + + + sdk\server + + + sdk\tier0 + + + sdk\mathlib + + + sdk\vpklib + + + sdk\public\include + + + thirdparty\lzham\include + + + thirdparty\lzham\include + + + sdk\mathlib + + + sdk\bsplib + + + sdk\common - - core\resource - + + + + + sdk\resource\png + + + + + sdk\resource + \ No newline at end of file diff --git a/r5dev/resource/ico/sdklauncher_dbg.ico b/r5dev/resource/ico/sdklauncher_dbg.ico new file mode 100644 index 0000000000000000000000000000000000000000..15e17194413a0a5d5f4463d8d11b6e3630472d77 GIT binary patch literal 274208 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 G-~Jm%($miX literal 0 HcmV?d00001 diff --git a/r5dev/resource/ico/sdklauncher_rel.ico b/r5dev/resource/ico/sdklauncher_rel.ico new file mode 100644 index 0000000000000000000000000000000000000000..94f2fae8c67c7876f24ff41202d8602affc57454 GIT binary patch literal 269547 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

  • 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%0A>$ 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 RZQGJq4aZ+%HrH5J{12VwOh5nt literal 0 HcmV?d00001 diff --git a/r5dev/thirdparty/lzham/libs/lzhamdecomp_x64.lib b/r5dev/thirdparty/lzham/libs/lzhamdecomp_x64.lib new file mode 100644 index 0000000000000000000000000000000000000000..5772956d7b82f44798e3f1aa3c6ff1372b4a4dc1 GIT binary patch literal 599642 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�wPd$k-dxjM16|GrcS(n` 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%0D?Y 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

    rF`vHAa!)H-*-?UwnyOV*T( zR2yTsNB*!<{z$$2GXJQu?Z07v=ql(Br+&1`^&FG0sobA*m$t)v`1?}iv9_a(gDMAQ z;QNW3`;YntW&B6wpp1-l(ART5=uo*MX{nTvmXx9SaKF~BN4j7>?NT|2RS&xcdd%ZC z7EV|BvB-mZ|JXX8RE{*0CAP4-U%COZ#S*CdH7W;nzfR?Sw7L2^q#r5KQ!q#1Gv&@* zAW_W;Y~u^c>>LC0CO*xL-}dy!>5VEst}^;@;`PwmxfXnMmC6=Bl#6`&Ce!_~cqVzwB(x0d6dSL$ol`p0JH>31#sf?I- zb|&@=J23D<<=>=o5%r@qE(q)&sq!4vqaU7((m$>81uTEc(I2;elFG9+UPo0PQu#-b zi)DPgEdS<3PGa6Kh4#?!&Alqmranpc#6jJLs63DQ9Z~w5R1WgLAEp0Cl)f~yM@IQ& zDqDW|?#(FuTPg?TuhKnqP`|Y*2j%aN()UE^7iCK!p}tw7vgL>GURK#+8=UveoFGu< z0`x7q@%t(-R2lkt8czjd>-kW;h4#Iua!~(sLh%;#b5u4x`o29%-=Xq(dXFNeSg7$= zyIUxy+Ncxq%++`+@X7fqTU?6!>!(rrS5*#tHv0rg6!OV@l>_~cqx3%w>CtEBgyJ}D zlgfclpAE^V?7YMB<>5)U%l;Yb zI&4@xc6YLWW{mX#X@8h6e_R90^5ZSvmc?5yrDe&y2LGbG4`(4yiks!x_^-+jq@OLZ z>8rB$K2cVW(X!w8q5LbCd9hmd-meT9?bge@xJAyLZZn36;jqtu5dHj9*7|$oRZ*NWZ_wDEl+~47N z4ZaD-YtX;8N}!wR#cNi6s>Wv)Yaq=Sl^>JH!2ECV-1^iXM{?ZRUW|ZoeV)csfnOG= zJXF>Z%Xf*&?Iu>C6-&Xp- zquIb22HJVO%8jWj5?A)KpzgtFgx%E zZo71qS5bd{D7J$BLY3F69&NdL^vCmGtMW1_cXlT3jb4=}MlO((u4m<+Uv2>!>iGb6 z3sqi0{p(Ts->SS^^;k11aFc}cSE{^}`e7RLn2%xqFqJKaLcJe}(m$@U-GA`Q_@m`d z$o}yvUΖ>PQr}6lbCSLsSl8-rJ(|cc>iH??9CP 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­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%0AO{Q%&tPC7`XxqFQGwp3C770MT8e4ersbMeW?H#v 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≥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≫Zk$Zu_Pu7v? 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 literal 0 HcmV?d00001 diff --git a/external/spdlog/include/async.h b/r5dev/thirdparty/spdlog/include/async.h similarity index 95% rename from external/spdlog/include/async.h rename to r5dev/thirdparty/spdlog/include/async.h index 98e8c25a..65e0f3ba 100644 --- a/external/spdlog/include/async.h +++ b/r5dev/thirdparty/spdlog/include/async.h @@ -14,9 +14,9 @@ // This is because each message in the queue holds a shared_ptr to the // originating logger. -#include -#include

    -#include
    +#include +#include +#include #include #include diff --git a/external/spdlog/include/async_logger-inl.h b/r5dev/thirdparty/spdlog/include/async_logger-inl.h similarity index 93% rename from external/spdlog/include/async_logger-inl.h rename to r5dev/thirdparty/spdlog/include/async_logger-inl.h index 81ffb6f2..2c95584b 100644 --- a/external/spdlog/include/async_logger-inl.h +++ b/r5dev/thirdparty/spdlog/include/async_logger-inl.h @@ -4,11 +4,11 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include +#include #endif -#include -#include
    +#include +#include #include #include diff --git a/external/spdlog/include/async_logger.h b/r5dev/thirdparty/spdlog/include/async_logger.h similarity index 97% rename from external/spdlog/include/async_logger.h rename to r5dev/thirdparty/spdlog/include/async_logger.h index a67b1562..b43f1aff 100644 --- a/external/spdlog/include/async_logger.h +++ b/r5dev/thirdparty/spdlog/include/async_logger.h @@ -14,7 +14,7 @@ // Upon destruction, logs all remaining messages in the queue before // destructing.. -#include +#include namespace spdlog { diff --git a/external/spdlog/include/cfg/argv.h b/r5dev/thirdparty/spdlog/include/cfg/argv.h similarity index 91% rename from external/spdlog/include/cfg/argv.h rename to r5dev/thirdparty/spdlog/include/cfg/argv.h index 81e69564..c6a93fc8 100644 --- a/external/spdlog/include/cfg/argv.h +++ b/r5dev/thirdparty/spdlog/include/cfg/argv.h @@ -2,8 +2,8 @@ // Distributed under the MIT License (http://opensource.org/licenses/MIT) #pragma once -#include -#include
    +#include +#include // // Init log levels using each argv entry that starts with "SPDLOG_LEVEL=" diff --git a/external/spdlog/include/cfg/env.h b/r5dev/thirdparty/spdlog/include/cfg/env.h similarity index 84% rename from external/spdlog/include/cfg/env.h rename to r5dev/thirdparty/spdlog/include/cfg/env.h index 0faf4f96..dcf1b8e0 100644 --- a/external/spdlog/include/cfg/env.h +++ b/r5dev/thirdparty/spdlog/include/cfg/env.h @@ -2,9 +2,9 @@ // Distributed under the MIT License (http://opensource.org/licenses/MIT) #pragma once -#include -#include
    -#include
    +#include +#include +#include // // Init levels and patterns from env variables SPDLOG_LEVEL diff --git a/external/spdlog/include/cfg/helpers-inl.h b/r5dev/thirdparty/spdlog/include/cfg/helpers-inl.h similarity index 93% rename from external/spdlog/include/cfg/helpers-inl.h rename to r5dev/thirdparty/spdlog/include/cfg/helpers-inl.h index 8e480650..254bd0ae 100644 --- a/external/spdlog/include/cfg/helpers-inl.h +++ b/r5dev/thirdparty/spdlog/include/cfg/helpers-inl.h @@ -4,12 +4,12 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include +#include #endif -#include -#include
    -#include
    +#include +#include +#include #include #include diff --git a/external/spdlog/include/cfg/helpers.h b/r5dev/thirdparty/spdlog/include/cfg/helpers.h similarity index 93% rename from external/spdlog/include/cfg/helpers.h rename to r5dev/thirdparty/spdlog/include/cfg/helpers.h index be44817a..96f4625f 100644 --- a/external/spdlog/include/cfg/helpers.h +++ b/r5dev/thirdparty/spdlog/include/cfg/helpers.h @@ -3,7 +3,7 @@ #pragma once -#include +#include #include namespace spdlog { diff --git a/external/spdlog/include/common-inl.h b/r5dev/thirdparty/spdlog/include/common-inl.h similarity index 97% rename from external/spdlog/include/common-inl.h rename to r5dev/thirdparty/spdlog/include/common-inl.h index 0c629451..fd498447 100644 --- a/external/spdlog/include/common-inl.h +++ b/r5dev/thirdparty/spdlog/include/common-inl.h @@ -4,7 +4,7 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include +#include #endif #include diff --git a/external/spdlog/include/common.h b/r5dev/thirdparty/spdlog/include/common.h similarity index 98% rename from external/spdlog/include/common.h rename to r5dev/thirdparty/spdlog/include/common.h index 9c50d1d5..66e05cf9 100644 --- a/external/spdlog/include/common.h +++ b/r5dev/thirdparty/spdlog/include/common.h @@ -3,8 +3,8 @@ #pragma once -#include -#include
    +#include +#include #include #include @@ -33,7 +33,7 @@ # define SPDLOG_INLINE inline #endif // #ifdef SPDLOG_COMPILED_LIB -#include +#include #if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) # include diff --git a/external/spdlog/include/details/backtracer-inl.h b/r5dev/thirdparty/spdlog/include/details/backtracer-inl.h similarity index 96% rename from external/spdlog/include/details/backtracer-inl.h rename to r5dev/thirdparty/spdlog/include/details/backtracer-inl.h index a184091b..7e838779 100644 --- a/external/spdlog/include/details/backtracer-inl.h +++ b/r5dev/thirdparty/spdlog/include/details/backtracer-inl.h @@ -4,7 +4,7 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include
    +#include #endif namespace spdlog { namespace details { diff --git a/external/spdlog/include/details/backtracer.h b/r5dev/thirdparty/spdlog/include/details/backtracer.h similarity index 89% rename from external/spdlog/include/details/backtracer.h rename to r5dev/thirdparty/spdlog/include/details/backtracer.h index 986b496c..ab449917 100644 --- a/external/spdlog/include/details/backtracer.h +++ b/r5dev/thirdparty/spdlog/include/details/backtracer.h @@ -3,8 +3,8 @@ #pragma once -#include
    -#include
    +#include +#include #include #include diff --git a/external/spdlog/include/details/circular_q.h b/r5dev/thirdparty/spdlog/include/details/circular_q.h similarity index 100% rename from external/spdlog/include/details/circular_q.h rename to r5dev/thirdparty/spdlog/include/details/circular_q.h diff --git a/external/spdlog/include/details/console_globals.h b/r5dev/thirdparty/spdlog/include/details/console_globals.h similarity index 90% rename from external/spdlog/include/details/console_globals.h rename to r5dev/thirdparty/spdlog/include/details/console_globals.h index e8cc76ed..a801112a 100644 --- a/external/spdlog/include/details/console_globals.h +++ b/r5dev/thirdparty/spdlog/include/details/console_globals.h @@ -3,7 +3,7 @@ #pragma once -#include
    +#include #include namespace spdlog { diff --git a/external/spdlog/include/details/file_helper-inl.h b/r5dev/thirdparty/spdlog/include/details/file_helper-inl.h similarity index 95% rename from external/spdlog/include/details/file_helper-inl.h rename to r5dev/thirdparty/spdlog/include/details/file_helper-inl.h index e6bd6966..2aff8925 100644 --- a/external/spdlog/include/details/file_helper-inl.h +++ b/r5dev/thirdparty/spdlog/include/details/file_helper-inl.h @@ -4,11 +4,11 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include
    +#include #endif -#include
    -#include +#include +#include #include #include diff --git a/external/spdlog/include/details/file_helper.h b/r5dev/thirdparty/spdlog/include/details/file_helper.h similarity index 97% rename from external/spdlog/include/details/file_helper.h rename to r5dev/thirdparty/spdlog/include/details/file_helper.h index d3a01cce..8b469977 100644 --- a/external/spdlog/include/details/file_helper.h +++ b/r5dev/thirdparty/spdlog/include/details/file_helper.h @@ -3,7 +3,7 @@ #pragma once -#include +#include #include namespace spdlog { diff --git a/external/spdlog/include/details/fmt_helper.h b/r5dev/thirdparty/spdlog/include/details/fmt_helper.h similarity index 97% rename from external/spdlog/include/details/fmt_helper.h rename to r5dev/thirdparty/spdlog/include/details/fmt_helper.h index 53045dbf..0b433247 100644 --- a/external/spdlog/include/details/fmt_helper.h +++ b/r5dev/thirdparty/spdlog/include/details/fmt_helper.h @@ -5,8 +5,8 @@ #include #include #include -#include -#include +#include +#include // Some fmt helpers to efficiently format and pad ints and strings namespace spdlog { diff --git a/external/spdlog/include/details/log_msg-inl.h b/r5dev/thirdparty/spdlog/include/details/log_msg-inl.h similarity index 90% rename from external/spdlog/include/details/log_msg-inl.h rename to r5dev/thirdparty/spdlog/include/details/log_msg-inl.h index 43c07712..fd632d24 100644 --- a/external/spdlog/include/details/log_msg-inl.h +++ b/r5dev/thirdparty/spdlog/include/details/log_msg-inl.h @@ -4,10 +4,10 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include
    +#include #endif -#include
    +#include namespace spdlog { namespace details { diff --git a/external/spdlog/include/details/log_msg.h b/r5dev/thirdparty/spdlog/include/details/log_msg.h similarity index 96% rename from external/spdlog/include/details/log_msg.h rename to r5dev/thirdparty/spdlog/include/details/log_msg.h index 0ec1d668..6b3884aa 100644 --- a/external/spdlog/include/details/log_msg.h +++ b/r5dev/thirdparty/spdlog/include/details/log_msg.h @@ -3,7 +3,7 @@ #pragma once -#include +#include #include namespace spdlog { diff --git a/external/spdlog/include/details/log_msg_buffer-inl.h b/r5dev/thirdparty/spdlog/include/details/log_msg_buffer-inl.h similarity index 96% rename from external/spdlog/include/details/log_msg_buffer-inl.h rename to r5dev/thirdparty/spdlog/include/details/log_msg_buffer-inl.h index eaac84d2..c2852764 100644 --- a/external/spdlog/include/details/log_msg_buffer-inl.h +++ b/r5dev/thirdparty/spdlog/include/details/log_msg_buffer-inl.h @@ -4,7 +4,7 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include
    +#include #endif namespace spdlog { diff --git a/external/spdlog/include/details/log_msg_buffer.h b/r5dev/thirdparty/spdlog/include/details/log_msg_buffer.h similarity index 94% rename from external/spdlog/include/details/log_msg_buffer.h rename to r5dev/thirdparty/spdlog/include/details/log_msg_buffer.h index 1a46a337..a886e5ef 100644 --- a/external/spdlog/include/details/log_msg_buffer.h +++ b/r5dev/thirdparty/spdlog/include/details/log_msg_buffer.h @@ -3,7 +3,7 @@ #pragma once -#include
    +#include namespace spdlog { namespace details { diff --git a/external/spdlog/include/details/mpmc_blocking_q.h b/r5dev/thirdparty/spdlog/include/details/mpmc_blocking_q.h similarity index 98% rename from external/spdlog/include/details/mpmc_blocking_q.h rename to r5dev/thirdparty/spdlog/include/details/mpmc_blocking_q.h index 16643320..e5a1cb76 100644 --- a/external/spdlog/include/details/mpmc_blocking_q.h +++ b/r5dev/thirdparty/spdlog/include/details/mpmc_blocking_q.h @@ -10,7 +10,7 @@ // dequeue_for(..) - will block until the queue is not empty or timeout have // passed. -#include
    +#include #include #include diff --git a/external/spdlog/include/details/null_mutex.h b/r5dev/thirdparty/spdlog/include/details/null_mutex.h similarity index 100% rename from external/spdlog/include/details/null_mutex.h rename to r5dev/thirdparty/spdlog/include/details/null_mutex.h diff --git a/external/spdlog/include/details/os-inl.h b/r5dev/thirdparty/spdlog/include/details/os-inl.h similarity index 99% rename from external/spdlog/include/details/os-inl.h rename to r5dev/thirdparty/spdlog/include/details/os-inl.h index 4c642711..80023274 100644 --- a/external/spdlog/include/details/os-inl.h +++ b/r5dev/thirdparty/spdlog/include/details/os-inl.h @@ -4,10 +4,10 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include
    +#include #endif -#include +#include #include #include @@ -25,7 +25,7 @@ #include // _get_osfhandle and _isatty support #include // _get_pid support -#include
    +#include #ifdef __MINGW32__ #include diff --git a/external/spdlog/include/details/os.h b/r5dev/thirdparty/spdlog/include/details/os.h similarity index 98% rename from external/spdlog/include/details/os.h rename to r5dev/thirdparty/spdlog/include/details/os.h index 7e32c345..56c307ba 100644 --- a/external/spdlog/include/details/os.h +++ b/r5dev/thirdparty/spdlog/include/details/os.h @@ -3,7 +3,7 @@ #pragma once -#include +#include #include // std::time_t namespace spdlog { diff --git a/external/spdlog/include/details/periodic_worker-inl.h b/r5dev/thirdparty/spdlog/include/details/periodic_worker-inl.h similarity index 94% rename from external/spdlog/include/details/periodic_worker-inl.h rename to r5dev/thirdparty/spdlog/include/details/periodic_worker-inl.h index 101d9901..6df6e3db 100644 --- a/external/spdlog/include/details/periodic_worker-inl.h +++ b/r5dev/thirdparty/spdlog/include/details/periodic_worker-inl.h @@ -4,7 +4,7 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include
    +#include #endif namespace spdlog { diff --git a/external/spdlog/include/details/periodic_worker.h b/r5dev/thirdparty/spdlog/include/details/periodic_worker.h similarity index 100% rename from external/spdlog/include/details/periodic_worker.h rename to r5dev/thirdparty/spdlog/include/details/periodic_worker.h diff --git a/external/spdlog/include/details/registry-inl.h b/r5dev/thirdparty/spdlog/include/details/registry-inl.h similarity index 95% rename from external/spdlog/include/details/registry-inl.h rename to r5dev/thirdparty/spdlog/include/details/registry-inl.h index 0fd34ce7..0f3b63e5 100644 --- a/external/spdlog/include/details/registry-inl.h +++ b/r5dev/thirdparty/spdlog/include/details/registry-inl.h @@ -4,20 +4,20 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include
    +#include #endif -#include -#include
    -#include -#include +#include +#include +#include +#include #ifndef SPDLOG_DISABLE_DEFAULT_LOGGER // support for the default stdout color logger #ifdef _WIN32 -#include +#include #else -#include +#include #endif #endif // SPDLOG_DISABLE_DEFAULT_LOGGER diff --git a/external/spdlog/include/details/registry.h b/r5dev/thirdparty/spdlog/include/details/registry.h similarity index 98% rename from external/spdlog/include/details/registry.h rename to r5dev/thirdparty/spdlog/include/details/registry.h index 00bde16a..7a274c20 100644 --- a/external/spdlog/include/details/registry.h +++ b/r5dev/thirdparty/spdlog/include/details/registry.h @@ -8,7 +8,7 @@ // If user requests a non existing logger, nullptr will be returned // This class is thread safe -#include +#include #include #include diff --git a/external/spdlog/include/details/synchronous_factory.h b/r5dev/thirdparty/spdlog/include/details/synchronous_factory.h similarity index 100% rename from external/spdlog/include/details/synchronous_factory.h rename to r5dev/thirdparty/spdlog/include/details/synchronous_factory.h diff --git a/external/spdlog/include/details/tcp_client-windows.h b/r5dev/thirdparty/spdlog/include/details/tcp_client-windows.h similarity index 97% rename from external/spdlog/include/details/tcp_client-windows.h rename to r5dev/thirdparty/spdlog/include/details/tcp_client-windows.h index d3d372ab..ec237d3b 100644 --- a/external/spdlog/include/details/tcp_client-windows.h +++ b/r5dev/thirdparty/spdlog/include/details/tcp_client-windows.h @@ -5,8 +5,8 @@ #define WIN32_LEAN_AND_MEAN // tcp client helper -#include -#include
    +#include +#include #include #include diff --git a/external/spdlog/include/details/tcp_client.h b/r5dev/thirdparty/spdlog/include/details/tcp_client.h similarity index 97% rename from external/spdlog/include/details/tcp_client.h rename to r5dev/thirdparty/spdlog/include/details/tcp_client.h index ba574aea..ff83512e 100644 --- a/external/spdlog/include/details/tcp_client.h +++ b/r5dev/thirdparty/spdlog/include/details/tcp_client.h @@ -8,8 +8,8 @@ #endif // tcp client helper -#include -#include
    +#include +#include #include #include diff --git a/external/spdlog/include/details/thread_pool-inl.h b/r5dev/thirdparty/spdlog/include/details/thread_pool-inl.h similarity index 96% rename from external/spdlog/include/details/thread_pool-inl.h rename to r5dev/thirdparty/spdlog/include/details/thread_pool-inl.h index 02bf6b2b..5ce306b6 100644 --- a/external/spdlog/include/details/thread_pool-inl.h +++ b/r5dev/thirdparty/spdlog/include/details/thread_pool-inl.h @@ -4,10 +4,10 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include
    +#include #endif -#include +#include #include namespace spdlog { diff --git a/external/spdlog/include/details/thread_pool.h b/r5dev/thirdparty/spdlog/include/details/thread_pool.h similarity index 94% rename from external/spdlog/include/details/thread_pool.h rename to r5dev/thirdparty/spdlog/include/details/thread_pool.h index 7cc0ab70..304a59d5 100644 --- a/external/spdlog/include/details/thread_pool.h +++ b/r5dev/thirdparty/spdlog/include/details/thread_pool.h @@ -3,9 +3,9 @@ #pragma once -#include
    -#include
    -#include
    +#include +#include +#include #include #include diff --git a/external/spdlog/include/details/windows_include.h b/r5dev/thirdparty/spdlog/include/details/windows_include.h similarity index 100% rename from external/spdlog/include/details/windows_include.h rename to r5dev/thirdparty/spdlog/include/details/windows_include.h diff --git a/external/spdlog/include/fmt/bin_to_hex.h b/r5dev/thirdparty/spdlog/include/fmt/bin_to_hex.h similarity index 100% rename from external/spdlog/include/fmt/bin_to_hex.h rename to r5dev/thirdparty/spdlog/include/fmt/bin_to_hex.h diff --git a/external/spdlog/include/fmt/bundled/args.h b/r5dev/thirdparty/spdlog/include/fmt/bundled/args.h similarity index 100% rename from external/spdlog/include/fmt/bundled/args.h rename to r5dev/thirdparty/spdlog/include/fmt/bundled/args.h diff --git a/external/spdlog/include/fmt/bundled/chrono.h b/r5dev/thirdparty/spdlog/include/fmt/bundled/chrono.h similarity index 100% rename from external/spdlog/include/fmt/bundled/chrono.h rename to r5dev/thirdparty/spdlog/include/fmt/bundled/chrono.h diff --git a/external/spdlog/include/fmt/bundled/color.h b/r5dev/thirdparty/spdlog/include/fmt/bundled/color.h similarity index 100% rename from external/spdlog/include/fmt/bundled/color.h rename to r5dev/thirdparty/spdlog/include/fmt/bundled/color.h diff --git a/external/spdlog/include/fmt/bundled/compile.h b/r5dev/thirdparty/spdlog/include/fmt/bundled/compile.h similarity index 100% rename from external/spdlog/include/fmt/bundled/compile.h rename to r5dev/thirdparty/spdlog/include/fmt/bundled/compile.h diff --git a/external/spdlog/include/fmt/bundled/core.h b/r5dev/thirdparty/spdlog/include/fmt/bundled/core.h similarity index 100% rename from external/spdlog/include/fmt/bundled/core.h rename to r5dev/thirdparty/spdlog/include/fmt/bundled/core.h diff --git a/external/spdlog/include/fmt/bundled/format-inl.h b/r5dev/thirdparty/spdlog/include/fmt/bundled/format-inl.h similarity index 100% rename from external/spdlog/include/fmt/bundled/format-inl.h rename to r5dev/thirdparty/spdlog/include/fmt/bundled/format-inl.h diff --git a/external/spdlog/include/fmt/bundled/format.h b/r5dev/thirdparty/spdlog/include/fmt/bundled/format.h similarity index 100% rename from external/spdlog/include/fmt/bundled/format.h rename to r5dev/thirdparty/spdlog/include/fmt/bundled/format.h diff --git a/external/spdlog/include/fmt/bundled/locale.h b/r5dev/thirdparty/spdlog/include/fmt/bundled/locale.h similarity index 100% rename from external/spdlog/include/fmt/bundled/locale.h rename to r5dev/thirdparty/spdlog/include/fmt/bundled/locale.h diff --git a/external/spdlog/include/fmt/bundled/os.h b/r5dev/thirdparty/spdlog/include/fmt/bundled/os.h similarity index 100% rename from external/spdlog/include/fmt/bundled/os.h rename to r5dev/thirdparty/spdlog/include/fmt/bundled/os.h diff --git a/external/spdlog/include/fmt/bundled/ostream.h b/r5dev/thirdparty/spdlog/include/fmt/bundled/ostream.h similarity index 100% rename from external/spdlog/include/fmt/bundled/ostream.h rename to r5dev/thirdparty/spdlog/include/fmt/bundled/ostream.h diff --git a/external/spdlog/include/fmt/bundled/printf.h b/r5dev/thirdparty/spdlog/include/fmt/bundled/printf.h similarity index 100% rename from external/spdlog/include/fmt/bundled/printf.h rename to r5dev/thirdparty/spdlog/include/fmt/bundled/printf.h diff --git a/external/spdlog/include/fmt/bundled/ranges.h b/r5dev/thirdparty/spdlog/include/fmt/bundled/ranges.h similarity index 100% rename from external/spdlog/include/fmt/bundled/ranges.h rename to r5dev/thirdparty/spdlog/include/fmt/bundled/ranges.h diff --git a/external/spdlog/include/fmt/bundled/xchar.h b/r5dev/thirdparty/spdlog/include/fmt/bundled/xchar.h similarity index 100% rename from external/spdlog/include/fmt/bundled/xchar.h rename to r5dev/thirdparty/spdlog/include/fmt/bundled/xchar.h diff --git a/external/spdlog/include/fmt/chrono.h b/r5dev/thirdparty/spdlog/include/fmt/chrono.h similarity index 100% rename from external/spdlog/include/fmt/chrono.h rename to r5dev/thirdparty/spdlog/include/fmt/chrono.h diff --git a/external/spdlog/include/fmt/fmt.h b/r5dev/thirdparty/spdlog/include/fmt/fmt.h similarity index 74% rename from external/spdlog/include/fmt/fmt.h rename to r5dev/thirdparty/spdlog/include/fmt/fmt.h index 6779d6bb..5d3b0ba8 100644 --- a/external/spdlog/include/fmt/fmt.h +++ b/r5dev/thirdparty/spdlog/include/fmt/fmt.h @@ -19,10 +19,10 @@ #endif // enable the 'n' flag in for backward compatibility with fmt 6.x #define FMT_DEPRECATED_N_SPECIFIER -#include -#include +#include +#include #else // SPDLOG_FMT_EXTERNAL is defined - use external fmtlib -#include -#include +#include +#include #endif diff --git a/external/spdlog/include/fmt/ostr.h b/r5dev/thirdparty/spdlog/include/fmt/ostr.h similarity index 100% rename from external/spdlog/include/fmt/ostr.h rename to r5dev/thirdparty/spdlog/include/fmt/ostr.h diff --git a/external/spdlog/include/fmt/xchar.h b/r5dev/thirdparty/spdlog/include/fmt/xchar.h similarity index 100% rename from external/spdlog/include/fmt/xchar.h rename to r5dev/thirdparty/spdlog/include/fmt/xchar.h diff --git a/external/spdlog/include/formatter.h b/r5dev/thirdparty/spdlog/include/formatter.h similarity index 79% rename from external/spdlog/include/formatter.h rename to r5dev/thirdparty/spdlog/include/formatter.h index d80ea810..09228b60 100644 --- a/external/spdlog/include/formatter.h +++ b/r5dev/thirdparty/spdlog/include/formatter.h @@ -3,8 +3,8 @@ #pragma once -#include -#include
    +#include +#include namespace spdlog { diff --git a/external/spdlog/include/fwd.h b/r5dev/thirdparty/spdlog/include/fwd.h similarity index 100% rename from external/spdlog/include/fwd.h rename to r5dev/thirdparty/spdlog/include/fwd.h diff --git a/external/spdlog/include/logger-inl.h b/r5dev/thirdparty/spdlog/include/logger-inl.h similarity index 97% rename from external/spdlog/include/logger-inl.h rename to r5dev/thirdparty/spdlog/include/logger-inl.h index f3014826..be81eed5 100644 --- a/external/spdlog/include/logger-inl.h +++ b/r5dev/thirdparty/spdlog/include/logger-inl.h @@ -7,9 +7,9 @@ #include #endif -#include -#include
    -#include +#include +#include +#include #include diff --git a/external/spdlog/include/logger.h b/r5dev/thirdparty/spdlog/include/logger.h similarity index 98% rename from external/spdlog/include/logger.h rename to r5dev/thirdparty/spdlog/include/logger.h index 77934dc8..3178d3d1 100644 --- a/external/spdlog/include/logger.h +++ b/r5dev/thirdparty/spdlog/include/logger.h @@ -14,9 +14,9 @@ // The use of private formatter per sink provides the opportunity to cache some // formatted data, and support for different format per sink. -#include -#include
    -#include
    +#include +#include +#include #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT # include
    diff --git a/external/spdlog/include/pattern_formatter-inl.h b/r5dev/thirdparty/spdlog/include/pattern_formatter-inl.h similarity index 99% rename from external/spdlog/include/pattern_formatter-inl.h rename to r5dev/thirdparty/spdlog/include/pattern_formatter-inl.h index 01752d40..56fa2f80 100644 --- a/external/spdlog/include/pattern_formatter-inl.h +++ b/r5dev/thirdparty/spdlog/include/pattern_formatter-inl.h @@ -7,11 +7,11 @@ #include #endif -#include
    -#include
    -#include
    -#include -#include +#include +#include +#include +#include +#include #include #include diff --git a/external/spdlog/include/pattern_formatter.h b/r5dev/thirdparty/spdlog/include/pattern_formatter.h similarity index 94% rename from external/spdlog/include/pattern_formatter.h rename to r5dev/thirdparty/spdlog/include/pattern_formatter.h index aad1a265..1f07fd8b 100644 --- a/external/spdlog/include/pattern_formatter.h +++ b/r5dev/thirdparty/spdlog/include/pattern_formatter.h @@ -3,10 +3,10 @@ #pragma once -#include -#include
    -#include
    -#include +#include +#include +#include +#include #include #include diff --git a/external/spdlog/include/sinks/android_sink.h b/r5dev/thirdparty/spdlog/include/sinks/android_sink.h similarity index 91% rename from external/spdlog/include/sinks/android_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/android_sink.h index 7e359b64..15706340 100644 --- a/external/spdlog/include/sinks/android_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/android_sink.h @@ -5,11 +5,11 @@ #ifdef __ANDROID__ -#include
    -#include
    -#include
    -#include -#include
    +#include +#include +#include +#include +#include #include #include diff --git a/external/spdlog/include/sinks/ansicolor_sink-inl.h b/r5dev/thirdparty/spdlog/include/sinks/ansicolor_sink-inl.h similarity index 96% rename from external/spdlog/include/sinks/ansicolor_sink-inl.h rename to r5dev/thirdparty/spdlog/include/sinks/ansicolor_sink-inl.h index 06d70f3f..40723e22 100644 --- a/external/spdlog/include/sinks/ansicolor_sink-inl.h +++ b/r5dev/thirdparty/spdlog/include/sinks/ansicolor_sink-inl.h @@ -4,11 +4,11 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include +#include #endif -#include -#include
    +#include +#include namespace spdlog { namespace sinks { diff --git a/external/spdlog/include/sinks/ansicolor_sink.h b/r5dev/thirdparty/spdlog/include/sinks/ansicolor_sink.h similarity index 95% rename from external/spdlog/include/sinks/ansicolor_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/ansicolor_sink.h index 7d20d1d8..fa53c742 100644 --- a/external/spdlog/include/sinks/ansicolor_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/ansicolor_sink.h @@ -3,9 +3,10 @@ #pragma once -#include
    -#include
    -#include +#include +#include +#include + #include #include #include diff --git a/external/spdlog/include/sinks/base_sink-inl.h b/r5dev/thirdparty/spdlog/include/sinks/base_sink-inl.h similarity index 91% rename from external/spdlog/include/sinks/base_sink-inl.h rename to r5dev/thirdparty/spdlog/include/sinks/base_sink-inl.h index d8ca467a..9c01965b 100644 --- a/external/spdlog/include/sinks/base_sink-inl.h +++ b/r5dev/thirdparty/spdlog/include/sinks/base_sink-inl.h @@ -4,11 +4,11 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include +#include #endif -#include -#include +#include +#include #include diff --git a/external/spdlog/include/sinks/base_sink.h b/r5dev/thirdparty/spdlog/include/sinks/base_sink.h similarity index 90% rename from external/spdlog/include/sinks/base_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/base_sink.h index d8bac0b5..ff3b46ed 100644 --- a/external/spdlog/include/sinks/base_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/base_sink.h @@ -9,9 +9,9 @@ // implementers.. // -#include -#include
    -#include +#include +#include +#include namespace spdlog { namespace sinks { diff --git a/external/spdlog/include/sinks/basic_file_sink-inl.h b/r5dev/thirdparty/spdlog/include/sinks/basic_file_sink-inl.h similarity index 85% rename from external/spdlog/include/sinks/basic_file_sink-inl.h rename to r5dev/thirdparty/spdlog/include/sinks/basic_file_sink-inl.h index de5997f0..0a50f777 100644 --- a/external/spdlog/include/sinks/basic_file_sink-inl.h +++ b/r5dev/thirdparty/spdlog/include/sinks/basic_file_sink-inl.h @@ -4,11 +4,11 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include +#include #endif -#include -#include
    +#include +#include namespace spdlog { namespace sinks { diff --git a/external/spdlog/include/sinks/basic_file_sink.h b/r5dev/thirdparty/spdlog/include/sinks/basic_file_sink.h similarity index 86% rename from external/spdlog/include/sinks/basic_file_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/basic_file_sink.h index 30f0365d..06e91706 100644 --- a/external/spdlog/include/sinks/basic_file_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/basic_file_sink.h @@ -3,10 +3,10 @@ #pragma once -#include
    -#include
    -#include -#include
    +#include +#include +#include +#include #include #include diff --git a/external/spdlog/include/sinks/daily_file_sink.h b/r5dev/thirdparty/spdlog/include/sinks/daily_file_sink.h similarity index 94% rename from external/spdlog/include/sinks/daily_file_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/daily_file_sink.h index 0bdfd824..5261c5e5 100644 --- a/external/spdlog/include/sinks/daily_file_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/daily_file_sink.h @@ -3,15 +3,15 @@ #pragma once -#include -#include
    -#include
    -#include -#include -#include -#include
    -#include
    -#include
    +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include diff --git a/external/spdlog/include/sinks/dist_sink.h b/r5dev/thirdparty/spdlog/include/sinks/dist_sink.h similarity index 91% rename from external/spdlog/include/sinks/dist_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/dist_sink.h index 1053ac6d..76a1fbe9 100644 --- a/external/spdlog/include/sinks/dist_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/dist_sink.h @@ -3,10 +3,10 @@ #pragma once -#include "base_sink.h" -#include
    -#include
    -#include +#include "thirdparty/spdlog/include/base_sink.h" +#include +#include +#include #include #include diff --git a/external/spdlog/include/sinks/dup_filter_sink.h b/r5dev/thirdparty/spdlog/include/sinks/dup_filter_sink.h similarity index 94% rename from external/spdlog/include/sinks/dup_filter_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/dup_filter_sink.h index 836f5cb2..0e42ceea 100644 --- a/external/spdlog/include/sinks/dup_filter_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/dup_filter_sink.h @@ -3,9 +3,9 @@ #pragma once -#include "dist_sink.h" -#include
    -#include
    +#include "thirdparty/spdlog/include/dist_sink.h" +#include +#include #include #include diff --git a/external/spdlog/include/sinks/hourly_file_sink.h b/r5dev/thirdparty/spdlog/include/sinks/hourly_file_sink.h similarity index 92% rename from external/spdlog/include/sinks/hourly_file_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/hourly_file_sink.h index bf0c914d..79356721 100644 --- a/external/spdlog/include/sinks/hourly_file_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/hourly_file_sink.h @@ -3,14 +3,14 @@ #pragma once -#include -#include
    -#include
    -#include -#include -#include
    -#include
    -#include
    +#include +#include +#include +#include +#include +#include +#include +#include #include #include diff --git a/external/spdlog/include/sinks/mongo_sink.h b/r5dev/thirdparty/spdlog/include/sinks/mongo_sink.h similarity index 92% rename from external/spdlog/include/sinks/mongo_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/mongo_sink.h index 0047b15e..b01eee10 100644 --- a/external/spdlog/include/sinks/mongo_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/mongo_sink.h @@ -10,10 +10,10 @@ // http://mongocxx.org/mongocxx-v3/installation/ // -#include "spdlog/common.h" -#include "spdlog/details/log_msg.h" -#include "spdlog/sinks/base_sink.h" -#include
    +#include "thirdparty/spdlog/include/spdlog/common.h" +#include "thirdparty/spdlog/include/spdlog/details/log_msg.h" +#include "thirdparty/spdlog/include/spdlog/sinks/base_sink.h" +#include #include #include diff --git a/external/spdlog/include/sinks/msvc_sink.h b/r5dev/thirdparty/spdlog/include/sinks/msvc_sink.h similarity index 90% rename from external/spdlog/include/sinks/msvc_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/msvc_sink.h index 797f2fcd..e235bd65 100644 --- a/external/spdlog/include/sinks/msvc_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/msvc_sink.h @@ -5,8 +5,8 @@ #if defined(_WIN32) -#include
    -#include +#include +#include #include #include diff --git a/external/spdlog/include/sinks/null_sink.h b/r5dev/thirdparty/spdlog/include/sinks/null_sink.h similarity index 86% rename from external/spdlog/include/sinks/null_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/null_sink.h index 5eaf9e3f..0c8b144c 100644 --- a/external/spdlog/include/sinks/null_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/null_sink.h @@ -3,9 +3,9 @@ #pragma once -#include
    -#include -#include
    +#include +#include +#include #include diff --git a/external/spdlog/include/sinks/ostream_sink.h b/r5dev/thirdparty/spdlog/include/sinks/ostream_sink.h similarity index 91% rename from external/spdlog/include/sinks/ostream_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/ostream_sink.h index d798568a..660bb0c3 100644 --- a/external/spdlog/include/sinks/ostream_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/ostream_sink.h @@ -3,8 +3,8 @@ #pragma once -#include
    -#include +#include +#include #include #include diff --git a/external/spdlog/include/sinks/qt_sinks.h b/r5dev/thirdparty/spdlog/include/sinks/qt_sinks.h similarity index 95% rename from external/spdlog/include/sinks/qt_sinks.h rename to r5dev/thirdparty/spdlog/include/sinks/qt_sinks.h index c2d02506..326f68a4 100644 --- a/external/spdlog/include/sinks/qt_sinks.h +++ b/r5dev/thirdparty/spdlog/include/sinks/qt_sinks.h @@ -8,10 +8,10 @@ // etc) Building and using requires Qt library. // -#include "spdlog/common.h" -#include "spdlog/details/log_msg.h" -#include "spdlog/details/synchronous_factory.h" -#include "spdlog/sinks/base_sink.h" +#include "thirdparty/spdlog/include/spdlog/common.h" +#include "thirdparty/spdlog/include/spdlog/details/log_msg.h" +#include "thirdparty/spdlog/include/spdlog/details/synchronous_factory.h" +#include "thirdparty/spdlog/include/spdlog/sinks/base_sink.h" #include #include diff --git a/external/spdlog/include/sinks/ringbuffer_sink.h b/r5dev/thirdparty/spdlog/include/sinks/ringbuffer_sink.h similarity index 88% rename from external/spdlog/include/sinks/ringbuffer_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/ringbuffer_sink.h index 1ee3f691..1864be0e 100644 --- a/external/spdlog/include/sinks/ringbuffer_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/ringbuffer_sink.h @@ -3,10 +3,10 @@ #pragma once -#include "spdlog/sinks/base_sink.h" -#include "spdlog/details/circular_q.h" -#include "spdlog/details/log_msg_buffer.h" -#include "spdlog/details/null_mutex.h" +#include "thirdparty/spdlog/include/spdlog/sinks/base_sink.h" +#include "thirdparty/spdlog/include/spdlog/details/circular_q.h" +#include "thirdparty/spdlog/include/spdlog/details/log_msg_buffer.h" +#include "thirdparty/spdlog/include/spdlog/details/null_mutex.h" #include #include diff --git a/external/spdlog/include/sinks/rotating_file_sink-inl.h b/r5dev/thirdparty/spdlog/include/sinks/rotating_file_sink-inl.h similarity index 93% rename from external/spdlog/include/sinks/rotating_file_sink-inl.h rename to r5dev/thirdparty/spdlog/include/sinks/rotating_file_sink-inl.h index 94622d9a..d0bbdeab 100644 --- a/external/spdlog/include/sinks/rotating_file_sink-inl.h +++ b/r5dev/thirdparty/spdlog/include/sinks/rotating_file_sink-inl.h @@ -4,14 +4,14 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include +#include #endif -#include +#include -#include
    -#include
    -#include +#include +#include +#include #include #include diff --git a/external/spdlog/include/sinks/rotating_file_sink.h b/r5dev/thirdparty/spdlog/include/sinks/rotating_file_sink.h similarity index 90% rename from external/spdlog/include/sinks/rotating_file_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/rotating_file_sink.h index 097454a0..b2e73902 100644 --- a/external/spdlog/include/sinks/rotating_file_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/rotating_file_sink.h @@ -3,10 +3,10 @@ #pragma once -#include -#include
    -#include
    -#include
    +#include +#include +#include +#include #include #include diff --git a/external/spdlog/include/sinks/sink-inl.h b/r5dev/thirdparty/spdlog/include/sinks/sink-inl.h similarity index 87% rename from external/spdlog/include/sinks/sink-inl.h rename to r5dev/thirdparty/spdlog/include/sinks/sink-inl.h index 31615c4b..47170213 100644 --- a/external/spdlog/include/sinks/sink-inl.h +++ b/r5dev/thirdparty/spdlog/include/sinks/sink-inl.h @@ -4,10 +4,10 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include +#include #endif -#include +#include SPDLOG_INLINE bool spdlog::sinks::sink::should_log(spdlog::level::level_enum msg_level) const { diff --git a/external/spdlog/include/sinks/sink.h b/r5dev/thirdparty/spdlog/include/sinks/sink.h similarity index 88% rename from external/spdlog/include/sinks/sink.h rename to r5dev/thirdparty/spdlog/include/sinks/sink.h index 20d607ae..4f5939b8 100644 --- a/external/spdlog/include/sinks/sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/sink.h @@ -3,8 +3,8 @@ #pragma once -#include
    -#include +#include +#include namespace spdlog { diff --git a/external/spdlog/include/sinks/stdout_color_sinks-inl.h b/r5dev/thirdparty/spdlog/include/sinks/stdout_color_sinks-inl.h similarity index 94% rename from external/spdlog/include/sinks/stdout_color_sinks-inl.h rename to r5dev/thirdparty/spdlog/include/sinks/stdout_color_sinks-inl.h index ac4845d3..87663d8c 100644 --- a/external/spdlog/include/sinks/stdout_color_sinks-inl.h +++ b/r5dev/thirdparty/spdlog/include/sinks/stdout_color_sinks-inl.h @@ -4,7 +4,7 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include +#include #endif #include diff --git a/external/spdlog/include/sinks/stdout_color_sinks.h b/r5dev/thirdparty/spdlog/include/sinks/stdout_color_sinks.h similarity index 85% rename from external/spdlog/include/sinks/stdout_color_sinks.h rename to r5dev/thirdparty/spdlog/include/sinks/stdout_color_sinks.h index 5130631a..1fac52cc 100644 --- a/external/spdlog/include/sinks/stdout_color_sinks.h +++ b/r5dev/thirdparty/spdlog/include/sinks/stdout_color_sinks.h @@ -4,12 +4,12 @@ #pragma once #ifdef _WIN32 -#include +#include #else -#include +#include #endif -#include
    +#include namespace spdlog { namespace sinks { @@ -41,5 +41,5 @@ std::shared_ptr stderr_color_st(const std::string &logger_name, color_mo } // namespace spdlog #ifdef SPDLOG_HEADER_ONLY -#include "stdout_color_sinks-inl.h" +#include "thirdparty/spdlog/include/stdout_color_sinks-inl.h" #endif diff --git a/external/spdlog/include/sinks/stdout_sinks-inl.h b/r5dev/thirdparty/spdlog/include/sinks/stdout_sinks-inl.h similarity index 94% rename from external/spdlog/include/sinks/stdout_sinks-inl.h rename to r5dev/thirdparty/spdlog/include/sinks/stdout_sinks-inl.h index 7458e541..9a36e0b2 100644 --- a/external/spdlog/include/sinks/stdout_sinks-inl.h +++ b/r5dev/thirdparty/spdlog/include/sinks/stdout_sinks-inl.h @@ -4,17 +4,17 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include +#include #endif -#include
    -#include +#include +#include #include #ifdef _WIN32 // under windows using fwrite to non-binary stream results in \r\r\n (see issue #1675) // so instead we use ::FileWrite -#include
    +#include #ifndef _USING_V110_SDK71_ // fileapi.h doesnt exist in winxp #include // WriteFile (..) diff --git a/external/spdlog/include/sinks/stdout_sinks.h b/r5dev/thirdparty/spdlog/include/sinks/stdout_sinks.h similarity index 90% rename from external/spdlog/include/sinks/stdout_sinks.h rename to r5dev/thirdparty/spdlog/include/sinks/stdout_sinks.h index 33c7d84f..4e9f542a 100644 --- a/external/spdlog/include/sinks/stdout_sinks.h +++ b/r5dev/thirdparty/spdlog/include/sinks/stdout_sinks.h @@ -3,13 +3,13 @@ #pragma once -#include
    -#include
    -#include +#include +#include +#include #include #ifdef _WIN32 -#include
    +#include #endif namespace spdlog { diff --git a/external/spdlog/include/sinks/syslog_sink.h b/r5dev/thirdparty/spdlog/include/sinks/syslog_sink.h similarity index 95% rename from external/spdlog/include/sinks/syslog_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/syslog_sink.h index 206cec43..24d9c4b5 100644 --- a/external/spdlog/include/sinks/syslog_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/syslog_sink.h @@ -3,9 +3,9 @@ #pragma once -#include -#include
    -#include
    +#include +#include +#include #include #include diff --git a/external/spdlog/include/sinks/systemd_sink.h b/r5dev/thirdparty/spdlog/include/sinks/systemd_sink.h similarity index 94% rename from external/spdlog/include/sinks/systemd_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/systemd_sink.h index c92d7076..c17ccfb3 100644 --- a/external/spdlog/include/sinks/systemd_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/systemd_sink.h @@ -3,9 +3,9 @@ #pragma once -#include -#include
    -#include
    +#include +#include +#include #include #ifndef SD_JOURNAL_SUPPRESS_LOCATION diff --git a/external/spdlog/include/sinks/tcp_sink.h b/r5dev/thirdparty/spdlog/include/sinks/tcp_sink.h similarity index 87% rename from external/spdlog/include/sinks/tcp_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/tcp_sink.h index a336ff41..953347e3 100644 --- a/external/spdlog/include/sinks/tcp_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/tcp_sink.h @@ -3,13 +3,13 @@ #pragma once -#include -#include -#include
    +#include +#include +#include #ifdef _WIN32 -#include
    +#include #else -#include
    +#include #endif #include diff --git a/external/spdlog/include/sinks/win_eventlog_sink.h b/r5dev/thirdparty/spdlog/include/sinks/win_eventlog_sink.h similarity index 97% rename from external/spdlog/include/sinks/win_eventlog_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/win_eventlog_sink.h index 1fc57486..eb7c1357 100644 --- a/external/spdlog/include/sinks/win_eventlog_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/win_eventlog_sink.h @@ -30,11 +30,11 @@ Windows Registry Editor Version 5.00 #pragma once -#include
    -#include +#include +#include -#include
    -#include +#include +#include #include #include diff --git a/external/spdlog/include/sinks/wincolor_sink-inl.h b/r5dev/thirdparty/spdlog/include/sinks/wincolor_sink-inl.h similarity index 96% rename from external/spdlog/include/sinks/wincolor_sink-inl.h rename to r5dev/thirdparty/spdlog/include/sinks/wincolor_sink-inl.h index 422e74ac..4a40b218 100644 --- a/external/spdlog/include/sinks/wincolor_sink-inl.h +++ b/r5dev/thirdparty/spdlog/include/sinks/wincolor_sink-inl.h @@ -4,14 +4,14 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include +#include #endif -#include
    +#include #include -#include -#include +#include +#include namespace spdlog { namespace sinks { diff --git a/external/spdlog/include/sinks/wincolor_sink.h b/r5dev/thirdparty/spdlog/include/sinks/wincolor_sink.h similarity index 92% rename from external/spdlog/include/sinks/wincolor_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/wincolor_sink.h index 34ee1ef2..43c669e8 100644 --- a/external/spdlog/include/sinks/wincolor_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/wincolor_sink.h @@ -3,10 +3,10 @@ #pragma once -#include -#include
    -#include
    -#include +#include +#include +#include +#include #include #include diff --git a/external/spdlog/include/spdlog-inl.h b/r5dev/thirdparty/spdlog/include/spdlog-inl.h similarity index 95% rename from external/spdlog/include/spdlog-inl.h rename to r5dev/thirdparty/spdlog/include/spdlog-inl.h index 797bbdae..4eb2fc32 100644 --- a/external/spdlog/include/spdlog-inl.h +++ b/r5dev/thirdparty/spdlog/include/spdlog-inl.h @@ -4,11 +4,11 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include +#include #endif -#include -#include +#include +#include namespace spdlog { diff --git a/external/spdlog/include/spdlog.h b/r5dev/thirdparty/spdlog/include/spdlog.h similarity index 97% rename from external/spdlog/include/spdlog.h rename to r5dev/thirdparty/spdlog/include/spdlog.h index 7abda7fb..077705d2 100644 --- a/external/spdlog/include/spdlog.h +++ b/r5dev/thirdparty/spdlog/include/spdlog.h @@ -9,11 +9,11 @@ #pragma once -#include -#include
    -#include -#include -#include
    +#include +#include +#include +#include +#include #include #include diff --git a/external/spdlog/include/stopwatch.h b/r5dev/thirdparty/spdlog/include/stopwatch.h similarity index 97% rename from external/spdlog/include/stopwatch.h rename to r5dev/thirdparty/spdlog/include/stopwatch.h index ca36aad6..da2621da 100644 --- a/external/spdlog/include/stopwatch.h +++ b/r5dev/thirdparty/spdlog/include/stopwatch.h @@ -3,7 +3,7 @@ #pragma once -#include +#include // Stopwatch support for spdlog (using std::chrono::steady_clock). // Displays elapsed seconds since construction as double. diff --git a/external/spdlog/include/tweakme.h b/r5dev/thirdparty/spdlog/include/tweakme.h similarity index 100% rename from external/spdlog/include/tweakme.h rename to r5dev/thirdparty/spdlog/include/tweakme.h diff --git a/external/spdlog/include/version.h b/r5dev/thirdparty/spdlog/include/version.h similarity index 100% rename from external/spdlog/include/version.h rename to r5dev/thirdparty/spdlog/include/version.h diff --git a/r5dev/tier0/ConCommand.cpp b/r5dev/tier0/ConCommand.cpp new file mode 100644 index 00000000..f3793445 --- /dev/null +++ b/r5dev/tier0/ConCommand.cpp @@ -0,0 +1,118 @@ +#include "core/stdafx.h" +#include "tier0/cvar.h" +#include "tier0/completion.h" +#include "tier0/ConCommand.h" +#include "client/client.h" +#include "engine/sys_utils.h" + +//----------------------------------------------------------------------------- +// Purpose: test each ConCommand query before execution +// Input : *cmd - flag +// Output : true if execution is not permitted, false if permitted +//----------------------------------------------------------------------------- +bool HConCommand_IsFlagSet(ConCommandBase* pCommandBase, int nFlag) +{ + if (cm_debug_cmdquery->m_pParent->m_iValue > 0) + { + printf("--------------------------------------------------\n"); + printf(" Flaged: %08X\n", pCommandBase->m_nFlags); + } + // Mask off FCVAR_CHEATS and FCVAR_DEVELOPMENTONLY + pCommandBase->m_nFlags &= 0xFFFFBFFD; + if (cm_debug_cmdquery->m_pParent->m_iValue > 0) + { + printf(" Masked: %08X\n", pCommandBase->m_nFlags); + printf(" Verify: %08X\n", nFlag); + printf("--------------------------------------------------\n"); + } + if (nFlag & 0x80000 && cm_return_false_cmdquery_all->m_pParent->m_iValue <= 0) + { + return ConCommand_IsFlagSet(pCommandBase, nFlag); + } + if (cm_return_false_cmdquery_all->m_pParent->m_iValue > 0) + { + // Returning false on all queries may cause problems. + return false; + } + if (cm_return_false_cmdquery_dev_cheat->m_pParent->m_iValue > 0) + { + // Return false on every FCVAR_DEVELOPMENTONLY || FCVAR_CHEAT query. + return (pCommandBase->m_nFlags & nFlag) != 0; + } + // Default behaviour. + return ConCommand_IsFlagSet(pCommandBase, nFlag); +} + +//----------------------------------------------------------------------------- +// Purpose: register ConCommand's +//----------------------------------------------------------------------------- +void* ConCommand_RegisterCommand(const char* szName, const char* szHelpString, int nFlags, void* pCallback, void* pCommandCompletionCallback) +{ + void* pCommand = reinterpret_cast(MemAlloc_Wrapper(0x68)); // Allocate new memory with StdMemAlloc else we crash. + memset(pCommand, 0, 0x68); // Set all to null. + std::uintptr_t pCommandBase = reinterpret_cast(pCommand); // To ptr. + + *(void**)pCommandBase = g_pConCommandVtable.RCast(); // 0x00 to ConCommand vtable. + *(const char**)(pCommandBase + 0x18) = szName; // 0x18 to ConCommand Name. + *(const char**)(pCommandBase + 0x20) = szHelpString; // 0x20 to ConCommand help string. + *(std::int32_t*)(pCommandBase + 0x38) = nFlags; // 0x38 to ConCommand Flags. + *(void**)(pCommandBase + 0x40) = p_ConCommand_NullSub.RCast(); // 0x40 Nullsub since every concommand has it. + *(void**)(pCommandBase + 0x50) = pCallback; // 0x50 has function callback. + *(std::int32_t*)(pCommandBase + 0x60) = 2; // 0x60 Set to use callback and newcommand callback. + + if (pCommandCompletionCallback) // callback after execution desired? + { + *(void**)(pCommandBase + 0x58) = pCommandCompletionCallback; // 0x58 to our callback after execution. + } + else + { + *(void**)(pCommandBase + 0x58) = p_ConCommand_CallbackCompletion.RCast(); // 0x58 nullsub. + } + + p_ConCommand_RegisterConCommand.RCast()((void*)pCommandBase); // Register command in ConVarAccessor. + + return pCommand; +} + +//----------------------------------------------------------------------------- +// Purpose: ConCommand definitions to be registered +//----------------------------------------------------------------------------- +void ConCommand_InitConCommand() +{ + //------------------------------------------------------------------------- + // SERVER DLL | + void* sv_kick = ConCommand_RegisterCommand("sv_kick", "Kick a client from the server by name. | Usage: kick .", 0, _Kick_f_CompletionFunc, nullptr); + void* sv_kickid = ConCommand_RegisterCommand("sv_kickid", "Kick a client from the server by UserID or OriginID | Usage: kickid /.", 0, _KickID_f_CompletionFunc, nullptr); + void* sv_ban = ConCommand_RegisterCommand("sv_ban", "Bans a client from the server by name. | Usage: ban .", 0, _Ban_f_CompletionFunc, nullptr); + void* sv_banid = ConCommand_RegisterCommand("sv_banid", "Bans a client from the server by OriginID, UserID or IPAddress | Usage: banid //.", 0, _BanID_f_CompletionFunc, nullptr); + void* sv_unban = ConCommand_RegisterCommand("sv_unban", "Unbans a client from the Server by IPAddress or OriginID | Usage: unban /.", 0, _Unban_f_CompletionFunc, nullptr); + void* sv_reloadbanlist = ConCommand_RegisterCommand("sv_reloadbanlist", "Reloads the ban list from the disk.", 0, _ReloadBanList_f_CompletionFunc, nullptr); +#ifndef DEDICATED + //------------------------------------------------------------------------- + // CLIENT DLL | + void* cl_showconsole = ConCommand_RegisterCommand("cl_showconsole", "Opens the game console.", 0, _CGameConsole_f_CompletionFunc, nullptr); + void* cl_showbrowser = ConCommand_RegisterCommand("cl_showbrowser", "Opens the server browser.", 0, _CCompanion_f_CompletionFunc, nullptr); +#endif // !DEDICATED + //------------------------------------------------------------------------- + // FILESYSTEM API | + void* fs_decompress_pak = ConCommand_RegisterCommand("fs_decompress_pak", "Decompresses user specified 'vpk_dir' file.", 0, _VPK_Decompress_f_CompletionFunc, nullptr); + //------------------------------------------------------------------------- + // RTECH API | + void* rtech_toguid = ConCommand_RegisterCommand("rtech_toguid", "Calculates the GUID from input data.", 0, _RTech_GenerateGUID_f_CompletionFunc, nullptr); + void* rtech_decompress = ConCommand_RegisterCommand("rtech_decompress", "Decompresses user specified 'RPak' file.", 0, _RTech_Decompress_f_CompletionFunc, nullptr); + //------------------------------------------------------------------------- + // NETCHANNEL | + void* net_toggletrace = ConCommand_RegisterCommand("net_toggletrace", "Logs the sending and receiving datagram to a file on the disk.", 0, _NET_TraceNetChan_f_CompletionFunc, nullptr); + void* net_setkey = ConCommand_RegisterCommand("net_setkey", "Sets user specified base64 net key.", 0, _NET_SetKey_f_CompletionFunc, nullptr); + void* net_generatekey = ConCommand_RegisterCommand("net_generatekey", "Generates and sets a random base64 net key.", 0, _NET_GenerateKey_f_CompletionFunc, nullptr); +} + +void ConCommand_Attach() +{ + DetourAttach((LPVOID*)&ConCommand_IsFlagSet, &HConCommand_IsFlagSet); +} + +void ConCommand_Detach() +{ + DetourDetach((LPVOID*)&ConCommand_IsFlagSet, &HConCommand_IsFlagSet); +} diff --git a/r5dev/tier0/ConCommand.h b/r5dev/tier0/ConCommand.h new file mode 100644 index 00000000..05b0e2dd --- /dev/null +++ b/r5dev/tier0/ConCommand.h @@ -0,0 +1,133 @@ +#pragma once + +class CCommand +{ +private: + enum + { + COMMAND_MAX_ARGC = 64, + COMMAND_MAX_LENGTH = 512, + }; + +public: + CCommand() = delete; + + inline int MaxCommandLength() + { + return COMMAND_MAX_LENGTH - 1; + } + + inline std::int64_t ArgC() const + { + return m_nArgc; + } + + inline const char** ArgV() const + { + return m_nArgc ? (const char**)m_ppArgv : NULL; + } + + inline const char* ArgS() const + { + return m_nArgv0Size ? &m_pArgSBuffer[m_nArgv0Size] : ""; + } + + inline const char* GetCommandString() const + { + return m_nArgc ? m_pArgSBuffer : ""; + } + + inline const char* Arg(int nIndex) const + { + // FIXME: Many command handlers appear to not be particularly careful + // about checking for valid argc range. For now, we're going to + // do the extra check and return an empty string if it's out of range + if (nIndex < 0 || nIndex >= m_nArgc) + { + return ""; + } + return m_ppArgv[nIndex]; + } + + inline const char* operator[](int nIndex) const + { + return Arg(nIndex); + } + +private: + std::int64_t m_nArgc; + std::int64_t m_nArgv0Size; + char m_pArgSBuffer[COMMAND_MAX_LENGTH]; + char m_pArgvBuffer[COMMAND_MAX_LENGTH]; + const char* m_ppArgv[COMMAND_MAX_ARGC]; +}; + +class ConCommand +{ + // TODO +}; + +class ConCommandBase +{ +public: + void* m_pConCommandBaseVTable; //0x0000 + ConCommandBase* m_pNext; //0x0008 + bool m_bRegistered; //0x0010 +private: + char pad_0011[7]; //0x0011 +public: + const char* m_pszName; //0x0018 + const char* m_pszHelpString; //0x0020 +private: + char pad_0028[16]; //0x0028 +public: + int m_nFlags; //0x0038 +private: + char pad_003C[4]; //0x003C +}; //Size: 0x0038 + +namespace +{ + /* ==== CONCOMMAND ====================================================================================================================================================== */ + ADDRESS p_ConCommand_IsFlagSet = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x85\x51\x38\x0F\x95\xC0\xC3", "xxxxxxx"); + bool (*ConCommand_IsFlagSet)(ConCommandBase* cmd, int flag) = (bool (*)(ConCommandBase*, int))p_ConCommand_IsFlagSet.GetPtr(); /*85 51 38 0F 95 C0 C3*/ + + ADDRESS p_ConCommand_CMaterialSystemCmdInit = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x00\x48\x89\x74\x24\x00\x48\x89\x7C\x24\x00\x55\x41\x54\x41\x55\x41\x56\x41\x57\x48\x8B\xEC\x48\x83\xEC\x50\x48\x8B\x15\x00\x00\x00\x00", "xxxx?xxxx?xxxx?xxxxxxxxxxxxxxxxxxx????"); + ConCommand*(*ConCommand_CMaterialSystemCmdInit)() = (ConCommand* (*)())p_ConCommand_CMaterialSystemCmdInit.GetPtr(); + + ADDRESS p_ConCommand_NullSub = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\xC2\x00\x00\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\x40\x53\x48\x83\xEC\x20\x48\x8D\x05\x00\x00\x00\x00", "xxxxxxxxxxxxxxxxxxxxxxxxx????"); + void (*ConCommand_NullSub)() = (void (*)())p_ConCommand_NullSub.GetPtr(); /*C2 00 00 CC CC CC CC CC CC CC CC CC CC CC CC CC 40 53 48 83 EC 20 48 8D 05 ?? ?? ?? ??*/ + + ADDRESS p_ConCommand_CallbackCompletion = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x33\xC0\xC3\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\x80\x49\x68\x08", "xxxxxxxxxxxxxxxxxxxx"); + void* (*ConCommand_CallbackCompletion)(struct _exception* _exc) = (void* (*)(struct _exception*))p_ConCommand_CallbackCompletion.GetPtr(); /*33 C0 C3 CC CC CC CC CC CC CC CC CC CC CC CC CC 80 49 68 08*/ /*UserMathErrorFunction*/ + + ADDRESS p_ConCommand_RegisterConCommand = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x8B\xD1\x48\x8B\x0D\x00\x00\x00\x00\x48\x85\xC9\x74\x06", "xxxxxx????xxxxx"); + void* (*ConCommand_RegisterConCommand)(ConCommandBase* pCommandBase) = (void* (*)(ConCommandBase*))p_ConCommand_RegisterConCommand.GetPtr(); /*48 8B D1 48 8B 0D ?? ?? ?? ?? 48 85 C9 74 06 */ + + static ADDRESS g_pConCommandVtable = p_ConCommand_CMaterialSystemCmdInit.FindPatternSelf("4C 8D 25", ADDRESS::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x3, 0x7); +} + +/////////////////////////////////////////////////////////////////////////////// +bool HConCommand_IsFlagSet(ConCommandBase* pCommand, int nFlag); +void ConCommand_InitConCommand(); + +void ConCommand_Attach(); +void ConCommand_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +class HConCommand : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: ConCommand::IsFlagSet : 0x" << std::hex << std::uppercase << p_ConCommand_IsFlagSet.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: ConCommand::CMaterialSystemCmdInit : 0x" << std::hex << std::uppercase << p_ConCommand_CMaterialSystemCmdInit.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: ConCommand::NullSub : 0x" << std::hex << std::uppercase << p_ConCommand_NullSub.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: ConCommand::CallbackCompletion : 0x" << std::hex << std::uppercase << p_ConCommand_CallbackCompletion.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: ConCommand::RegisterConCommand : 0x" << std::hex << std::uppercase << p_ConCommand_RegisterConCommand.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: g_pConCommandVtable : 0x" << std::hex << std::uppercase << g_pConCommandVtable.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HConCommand); diff --git a/r5dev/tier0/IConVar.cpp b/r5dev/tier0/IConVar.cpp new file mode 100644 index 00000000..f2d361bf --- /dev/null +++ b/r5dev/tier0/IConVar.cpp @@ -0,0 +1,144 @@ +#include "core/stdafx.h" +#include "tier0/cvar.h" +#include "tier0/IConVar.h" +#include "engine/sys_utils.h" +#include "engine/sys_dll2.h" + +//----------------------------------------------------------------------------- +// Purpose: test each ConVar query before setting the cvar +// Input : **cvar - flag +// Output : true if change is not permitted, false if permitted +//----------------------------------------------------------------------------- +bool HIConVar_IsFlagSet(ConVar* pConVar, int nFlags) +{ + if (cm_debug_cmdquery->m_pParent->m_iValue > 0) + { + printf("--------------------------------------------------\n"); + printf(" Flaged: %08X\n", pConVar->m_ConCommandBase.m_nFlags); + } + // Mask off FCVAR_CHEATS and FCVAR_DEVELOPMENTONLY + pConVar->m_ConCommandBase.m_nFlags &= 0xFFFFBFFD; + if (cm_debug_cmdquery->m_pParent->m_iValue > 0) + { + printf(" Masked: %08X\n", pConVar->m_ConCommandBase.m_nFlags); + printf(" Verify: %08X\n", nFlags); + printf("--------------------------------------------------\n"); + } + if (nFlags & 0x80000 && cm_return_false_cmdquery_all->m_pParent->m_iValue <= 0) + { + return IConVar_IsFlagSet(pConVar, nFlags); + } + if (cm_return_false_cmdquery_all->m_pParent->m_iValue > 0) + { + // Returning false on all queries may cause problems. + return false; + } + if (cm_return_false_cmdquery_dev_cheat->m_pParent->m_iValue > 0) + { + // Return false on every FCVAR_DEVELOPMENTONLY || FCVAR_CHEAT query. + return (pConVar->m_ConCommandBase.m_nFlags & nFlags) != 0; + } + // Default behaviour. + return IConVar_IsFlagSet(pConVar, nFlags); +} + +//----------------------------------------------------------------------------- +// Purpose: register ConVar's +//----------------------------------------------------------------------------- +ConVar* IConVar_RegisterConVar(const char* szName, const char* szDefaultValue, int nFlags, const char* szHelpString, bool bMin, float fMin, bool bMax, float fMax, void* pCallback, void* unk) +{ + ConVar* allocatedConvar = reinterpret_cast(MemAlloc_Wrapper(0xA0)); // Allocate new memory with StdMemAlloc else we crash. + memset(allocatedConvar, 0, 0xA0); // Set all to null. + std::uintptr_t cvarPtr = reinterpret_cast(allocatedConvar); // To ptr. + + *(void**)(cvarPtr + 0x40) = g_pIConVarVtable.RCast(); // 0x40 to ICvar table. + *(void**)cvarPtr = g_pConVarVtable.RCast(); // 0x0 to ConVar vtable. + + p_ConVar_Register.RCast() // Call to create ConVar. + (allocatedConvar, szName, szDefaultValue, nFlags, szHelpString, bMin, fMin, bMax, fMax, pCallback, unk); + + return allocatedConvar; // Return allocated ConVar. +} + +//----------------------------------------------------------------------------- +// Purpose: convar definitions to be registered +//----------------------------------------------------------------------------- +void IConVar_InitConVar() +{ + //------------------------------------------------------------------------- + // ENGINE | + cm_debug_cmdquery = IConVar_RegisterConVar("cm_debug_cmdquery", "0", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Prints the flags of each ConVar/ConCommand query to the console ( !slower! ).", false, 0.f, false, 0.f, nullptr, nullptr); + cm_return_false_cmdquery_all = IConVar_RegisterConVar("cm_return_false_cmdquery_all", "0", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Returns false on every ConVar/ConCommand query ( !warning! ).", false, 0.f, false, 0.f, nullptr, nullptr); + cm_return_false_cmdquery_dev_cheat = IConVar_RegisterConVar("cm_return_false_cmdquery_dev_cheat", "1", FCVAR_RELEASE, "Returns false on all FCVAR_DEVELOPMENTONLY and FCVAR_CHEAT ConVar/ConCommand queries ( !warning! ).", false, 0.f, false, 0.f, nullptr, nullptr); + //------------------------------------------------------------------------- + // SERVER | + sv_showconnecting = IConVar_RegisterConVar("sv_showconnecting", "1", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Logs information about the connecting client to the console.", false, 0.f, false, 0.f, nullptr, nullptr); + //------------------------------------------------------------------------- + // CLIENT | + cl_drawconsoleoverlay = IConVar_RegisterConVar("cl_drawconsoleoverlay", "1", FCVAR_RELEASE, "Draw the console overlay at the top of the screen.", false, 0.f, false, 0.f, nullptr, nullptr); + cl_consoleoverlay_lines = IConVar_RegisterConVar("cl_consoleoverlay_lines", "3", FCVAR_RELEASE, "Number of lines of console output to draw.", false, 0.f, false, 0.f, nullptr, nullptr); + cl_consoleoverlay_offset_x = IConVar_RegisterConVar("cl_consoleoverlay_offset_x", "10", FCVAR_RELEASE, "X offset for console overlay.", false, 1.f, false, 50.f, nullptr, nullptr); + cl_consoleoverlay_offset_y = IConVar_RegisterConVar("cl_consoleoverlay_offset_y", "10", FCVAR_RELEASE, "Y offset for console overlay.", false, 1.f, false, 50.f, nullptr, nullptr); + //------------------------------------------------------------------------- + // FILESYSTEM | + fs_warning_level_native = IConVar_RegisterConVar("fs_warning_level_native", "0", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Set the filesystem warning level ( !slower! ).", false, 0.f, false, 0.f, nullptr, nullptr); + fs_packedstore_entryblock_stats = IConVar_RegisterConVar("fs_packedstore_entryblock_stats", "0", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "If set to 1, prints the stats of each file entry in the VPK during decompression ( !slower! ).", false, 0.f, false, 0.f, nullptr, nullptr); + //------------------------------------------------------------------------- + // FILESYSTEM | + mat_showdxoutput = IConVar_RegisterConVar("mat_showdxoutput", "0", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Shows debug output for the DirectX system.", false, 0.f, false, 0.f, nullptr, nullptr); + //------------------------------------------------------------------------- + // SQUIRREL | + sq_showrsonloading = IConVar_RegisterConVar("sq_showrsonloading", "1", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Logs all 'rson' files loaded by the SQVM ( !slower! ).", false, 0.f, false, 0.f, nullptr, nullptr); + sq_showscriptloading = IConVar_RegisterConVar("sq_showscriptloading", "0", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Logs all scripts loaded by the SQVM to be pre-compiled ( !slower! ).", false, 0.f, false, 0.f, nullptr, nullptr); + sq_showvmoutput = IConVar_RegisterConVar("sq_showvmoutput", "3", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Prints the VM output to the console. 1 = Log to file. 2 = 1 + log to console. 3 = 1 + 2 + log to overhead console. 4 = only log to overhead console.", false, 0.f, false, 0.f, nullptr, nullptr); + sq_showvmwarning = IConVar_RegisterConVar("sq_showvmwarning", "0", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Prints the VM warning output to the console. 1 = Log to file. 2 = 1 + log to console.", false, 0.f, false, 0.f, nullptr, nullptr); + //------------------------------------------------------------------------- + // NETCHANNEL | + net_userandomkey = IConVar_RegisterConVar("net_userandomkey", "1", FCVAR_RELEASE, "If set to 1, the netchannel generates and sets a random base64 netkey.", false, 0.f, false, 0.f, nullptr, nullptr); + r5net_matchmaking_hostname = IConVar_RegisterConVar("r5net_matchmaking_hostname", "r5a-comp-sv.herokuapp.com", FCVAR_RELEASE, "Holds the R5Net matchmaking hostname.", false, 0.f, false, 0.f, nullptr, nullptr); + r5net_show_debug = IConVar_RegisterConVar("r5net_show_debug", "1", FCVAR_RELEASE, "Shows debug output for R5Net.", false, 0.f, false, 0.f, nullptr, nullptr); +} + +//----------------------------------------------------------------------------- +// Purpose: clear all hostname ConVar's +//----------------------------------------------------------------------------- +void IConVar_ClearHostNames() +{ + const char* szHostnameArray[] = + { + "pin_telemetry_hostname", + "assetdownloads_hostname", + "users_hostname", + "persistence_hostname", + "speechtotexttoken_hostname", + "communities_hostname", + "persistenceDef_hostname", + "party_hostname", + "speechtotext_hostname", + "serverReports_hostname", + "subscription_hostname", + "steamlink_hostname", + "staticfile_hostname", + "matchmaking_hostname", + "skill_hostname", + "publication_hostname", + "stats_hostname" + }; + + for (int i = 0; i < 17; i++) + { + const char* szName = szHostnameArray[i]; + g_pCvar->FindVar(szName)->m_pzsCurrentValue = "0.0.0.0"; + } +} + +/////////////////////////////////////////////////////////////////////////////// +void IConVar_Attach() +{ + DetourAttach((LPVOID*)&IConVar_IsFlagSet, &HIConVar_IsFlagSet); +} + +void IConVar_Detach() +{ + DetourDetach((LPVOID*)&IConVar_IsFlagSet, &HIConVar_IsFlagSet); +} diff --git a/r5dev/tier0/IConVar.h b/r5dev/tier0/IConVar.h new file mode 100644 index 00000000..bc64759f --- /dev/null +++ b/r5dev/tier0/IConVar.h @@ -0,0 +1,173 @@ +#pragma once +#include "basetypes.h" +#include "ConCommand.h" + +//----------------------------------------------------------------------------- +// Command to ConVars and ConCommands +//----------------------------------------------------------------------------- +// ConVar systems +#define FCVAR_NONE 0 // The default, no flags at all +#define FCVAR_UNREGISTERED (1<<0) // If this is set, don't add to linked list, etc. +#define FCVAR_DEVELOPMENTONLY (1<<1) // Hidden in released products. Flag is removed automatically if ALLOW_DEVELOPMENT_CVARS is defined. +#define FCVAR_GAMEDLL (1<<2) // defined by the game DLL +#define FCVAR_CLIENTDLL (1<<3) // defined by the client DLL +#define FCVAR_HIDDEN (1<<4) // Hidden. Doesn't appear in find or auto complete. Like DEVELOPMENTONLY, but can't be compiled out. + +// ConVar only +#define FCVAR_PROTECTED (1<<5) // It's a server cvar, but we don't send the data since it's a password, etc. Sends 1 if it's not bland/zero, 0 otherwise as value +#define FCVAR_SPONLY (1<<6) // This cvar cannot be changed by clients connected to a multiplayer server. +#define FCVAR_ARCHIVE (1<<7) // set to cause it to be saved to vars.rc +#define FCVAR_NOTIFY (1<<8) // notifies players when changed +#define FCVAR_USERINFO (1<<9) // changes the client's info string + +#define FCVAR_PRINTABLEONLY (1<<10) // This cvar's string cannot contain unprintable characters ( e.g., used for player name etc ). + +#define FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS (1<<10) // When on concommands this allows remote clients to execute this cmd on the server. + // We are changing the default behavior of concommands to disallow execution by remote clients without + // this flag due to the number existing concommands that can lag or crash the server when clients abuse them. + +#define FCVAR_UNLOGGED (1<<11) // If this is a FCVAR_SERVER, don't log changes to the log file / console if we are creating a log +#define FCVAR_NEVER_AS_STRING (1<<12) // never try to print that cvar + +// It's a ConVar that's shared between the client and the server. +// At signon, the values of all such ConVars are sent from the server to the client (skipped for local +// client, of course ) +// If a change is requested it must come from the console (i.e., no remote client changes) +// If a value is changed while a server is active, it's replicated to all connected clients +#define FCVAR_REPLICATED (1<<13) // server setting enforced on clients, TODO rename to FCAR_SERVER at some time +#define FCVAR_CHEAT (1<<14) // Only useable in singleplayer / debug / multiplayer & sv_cheats +#define FCVAR_SS (1<<15) // causes varnameN where N == 2 through max splitscreen slots for mod to be autogenerated +#define FCVAR_DEMO (1<<16) // record this cvar when starting a demo file +#define FCVAR_DONTRECORD (1<<17) // don't record these command in demofiles +#define FCVAR_SS_ADDED (1<<18) // This is one of the "added" FCVAR_SS variables for the splitscreen players +#define FCVAR_RELEASE (1<<19) // Cvars tagged with this are the only cvars avaliable to customers +#define FCVAR_RELOAD_MATERIALS (1<<20) // If this cvar changes, it forces a material reload +#define FCVAR_RELOAD_TEXTURES (1<<21) // If this cvar changes, if forces a texture reload + +#define FCVAR_NOT_CONNECTED (1<<22) // cvar cannot be changed by a client that is connected to a server +#define FCVAR_MATERIAL_SYSTEM_THREAD (1<<23) // Indicates this cvar is read from the material system thread +#define FCVAR_ARCHIVE_GAMECONSOLE (1<<24) // cvar written to config.cfg on the Xbox + +#define FCVAR_SERVER_CAN_EXECUTE (1<<28) // the server is allowed to execute this command on clients via ClientCommand/NET_StringCmd/CBaseClientState::ProcessStringCmd. +#define FCVAR_SERVER_CANNOT_QUERY (1<<29) // If this is set, then the server is not allowed to query this cvar's value (via IServerPluginHelpers::StartQueryCvarValue). +#define FCVAR_CLIENTCMD_CAN_EXECUTE (1<<30) // IVEngineClient::ClientCmd is allowed to execute this command. + +class ConVar +{ +public: + ConCommandBase m_ConCommandBase; //0x0000 + void* m_pConVarVTable; //0x0040 + ConVar* m_pParent; //0x0048 + const char* n_pszDefaultValue; //0x0050 + const char* m_pzsCurrentValue; //0x0058 + std::int64_t m_iStringLength; //0x0060 + float m_flValue; //0x0068 + int m_iValue; //0x006C + bool m_bHasMin; //0x0070 + float m_flMinValue; //0x0074 + bool m_bHasMax; //0x0078 + float m_flMaxValue; //0x007C + char pad_0080[32]; //0x0080 +}; //Size: 0x00A0 + +class CCVarIteratorInternal // Fully reversed table, just look at the virtual function table and rename the function. +{ +public: + virtual void SetFirst(void) = 0; //0 + virtual void Next(void) = 0; //1 + virtual bool IsValid(void) = 0; //2 + virtual ConCommandBase* Get(void) = 0; //3 +}; + +class CCVar +{ +public: + ConCommandBase* FindCommandBase(const char* szCommandName) // @0x1405983A0 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = ConCommandBase * (__thiscall*)(CCVar*, const char*); + return (*reinterpret_cast(this))[14](this, szCommandName); + } + + ConVar* FindVar(const char* szVarName) // @0x1405983B0 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = ConVar * (__thiscall*)(CCVar*, const char*); + return (*reinterpret_cast(this))[16](this, szVarName); + } + + void* /*Implement ConCommand class.*/ FindCommand(const char* szCommandName) // @0x1405983F0 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = void* (__thiscall*)(CCVar*, const char*); + return (*reinterpret_cast(this))[18](this, szCommandName); + } + + CCVarIteratorInternal* FactoryInternalIterator() // @0x140597C10 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = CCVarIteratorInternal * (__thiscall*)(CCVar*); + return (*reinterpret_cast(this))[41](this); + } + + std::unordered_map DumpToMap() + { + std::stringstream ss; + CCVarIteratorInternal* itint = FactoryInternalIterator(); // Allocatd new InternalIterator. + + std::unordered_map allConVars; + + for (itint->SetFirst(); itint->IsValid(); itint->Next()) // Loop through all instances. + { + ConCommandBase* pCommand = itint->Get(); + const char* commandName = pCommand->m_pszName; + allConVars[commandName] = pCommand; + } + + return allConVars; + } +}; + +namespace +{ + /* ==== ICONVAR ========================================================================================================================================================= */ + ADDRESS p_IConVar_IsFlagSet = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x8B\x41\x48\x85\x50\x38", "xxxxxxx"); + bool (*IConVar_IsFlagSet)(ConVar* pConVar, int nFlag) = (bool (*)(ConVar*, int))p_IConVar_IsFlagSet.GetPtr(); /*48 8B 41 48 85 50 38*/ + + ADDRESS p_ConVar_SetInfo = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x53\x48\x83\xEC\x60\x48\x8B\xD9\xC6\x41\x10\x00\x33\xC9\x48\x8D\x05\x00\x00\x00\x00\x48\x89\x4C\x24\x00\x0F\x57\xC0\x48\x89\x4C\x24\x00\x48\x89\x03\x48\x8D\x05\x00\x00\x00\x00\x48\x89\x43\x40", "xxxxxxxxxxxxxxxxxx????xxxx?xxxxxxx?xxxxxx????xxxx"); + void* (*ConVar_SetInfo)(void* a1, int a2, int a3, int a4, void* a5) = (void* (*)(void* a1, int a2, int a3, int a4, void* a5))p_ConVar_SetInfo.GetPtr(); /*40 53 48 83 EC 60 48 8B D9 C6 41 10 00 33 C9 48 8D 05 ? ? ? ? 48 89 4C 24 ? 0F 57 C0 48 89 4C 24 ? 48 89 03 48 8D 05 ? ? ? ? 48 89 43 40*/ +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) + ADDRESS p_ConVar_Register = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x00\x48\x89\x6C\x24\x00\x48\x89\x74\x24\x00\x48\x89\x7C\x24\x00\x41\x56\x48\x83\xEC\x30\xF3\x0F\x10\x44\x24\x00", "xxxx?xxxx?xxxx?xxxx?xxxxxxxxxxx?"); /*48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 41 56 48 83 EC 30 F3 0F 10 44 24 ?*/ + void* (*ConVar_Register)(ConVar* allocatedConvar, const char* szName, const char* szDefaultValue, int nFlags, const char* szHelpString, bool bMin, float fMin, bool bMax, float fMax, void* pCallback, void* unk) = (void* (*)(ConVar*, const char*, const char*, int, const char*, bool, float, bool, float, void*, void*))p_ConVar_Register.GetPtr(); +#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) + ADDRESS p_ConVar_Register = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x00\x48\x89\x6C\x24\x00\x48\x89\x74\x24\x00\x57\x48\x83\xEC\x40\xF3\x0F\x10\x84\x24\x00\x00\x00\x00", "xxxx?xxxx?xxxx?xxxxxxxxxx????"); /*48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 48 83 EC 40 F3 0F 10 84 24 ? ? ? ?*/ + void* (*ConVar_Register)(ConVar* allocatedConvar, const char* szName, const char* szDefaultValue, int nFlags, const char* szHelpString, bool bMin, float fMin, bool bMax, float fMax, void* pCallback, void* unk) = (void* (*)(ConVar*, const char*, const char*, int, const char*, bool, float, bool, float, void*, void*))p_ConVar_Register.GetPtr(); +#endif +} + +namespace +{ + ADDRESS g_pConVarVtable = p_ConVar_SetInfo.Offset(0x00).FindPatternSelf("48 8D 05", ADDRESS::Direction::DOWN, 100).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr(); // Get vtable ptr for ConVar table. + ADDRESS g_pIConVarVtable = p_ConVar_SetInfo.Offset(0x16).FindPatternSelf("48 8D 05", ADDRESS::Direction::DOWN, 100).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr(); // Get vtable ptr for ICvar table. +} + +/////////////////////////////////////////////////////////////////////////////// +bool HIConVar_IsFlagSet(ConVar* pConVar, int nFlag); +void IConVar_InitConVar(); +void IConVar_ClearHostNames(); + +void IConVar_Attach(); +void IConVar_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +class HConVar : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: IConVar::IsFlagSet : 0x" << std::hex << std::uppercase << p_IConVar_IsFlagSet.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: IConVar::SetInfo : 0x" << std::hex << std::uppercase << p_ConVar_SetInfo.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: IConVar::Register : 0x" << std::hex << std::uppercase << p_ConVar_Register.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: g_pConVarVtable : 0x" << std::hex << std::uppercase << g_pConVarVtable.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: g_pIConVarVtable : 0x" << std::hex << std::uppercase << g_pIConVarVtable.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HConVar); diff --git a/r5dev/tier0/basetypes.h b/r5dev/tier0/basetypes.h new file mode 100644 index 00000000..bc9effff --- /dev/null +++ b/r5dev/tier0/basetypes.h @@ -0,0 +1,19 @@ +#pragma once + +/*----------------------------------------------------------------------------- + * _basetypes + *-----------------------------------------------------------------------------*/ + +//#define GAMEDLL_S0 /*[r]*/ +//#define GAMEDLL_S1 /*[r]*/ +//#define GAMEDLL_S2 /*[i]*/ +#define GAMEDLL_S3 /*[r]*/ +//#define GAMEDLL_S4 /*[i]*/ +//#define GAMEDLL_S7 /*[i]*/ + +#define MAX_SPLITSCREEN_CLIENT_BITS 2 // Max 2 player splitscreen in portal (don't merge this back), saves a bunch of memory [8/31/2010 tom] +#define MAX_SPLITSCREEN_CLIENTS ( 1 << MAX_SPLITSCREEN_CLIENT_BITS ) // 4 // this should == MAX_JOYSTICKS in InputEnums.h + +#define MAX_PLAYERS 128 // Max R5 players. + +#define SDK_VERSION "beta 1.6" diff --git a/r5dev/tier0/completion.cpp b/r5dev/tier0/completion.cpp new file mode 100644 index 00000000..0c47c010 --- /dev/null +++ b/r5dev/tier0/completion.cpp @@ -0,0 +1,588 @@ +//=============================================================================// +// +// Purpose: Completion functions for ConCommand callbacks +// +//=============================================================================// + +#include "core/stdafx.h" +#include "windows/id3dx.h" +#include "tier0/basetypes.h" +#include "tier0/cvar.h" +#include "tier0/IConVar.h" +#include "tier0/completion.h" +#include "engine/net_chan.h" +#include "engine/sys_utils.h" +#include "rtech/rtech.h" +#include "vpklib/packedstore.h" +#include "gameui/IConsole.h" +#include "public/include/bansystem.h" + +#ifndef DEDICATED +void _CGameConsole_f_CompletionFunc(const CCommand& cmd) +{ + g_bShowConsole = !g_bShowConsole; +} + +void _CCompanion_f_CompletionFunc(const CCommand& cmd) +{ + g_bShowBrowser = !g_bShowBrowser; +} +#endif // !DEDICATED + +void _Kick_f_CompletionFunc(CCommand* cmd) +{ + std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); + if (argSize < 2) // Do we atleast have 2 arguments? + { + return; + } + + CCommand& args = *cmd; // Get reference. + const char* firstArg = args[1]; // Get first arg. + + for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. + { + CClient* client = g_pClient->GetClientInstance(i); // Get client instance. + if (!client) + { + continue; + } + + if (!client->GetNetChan()) // Netchan valid? + { + continue; + } + + void* clientNamePtr = (void**)(((std::uintptr_t)client->GetNetChan()) + 0x1A8D); // Get client name from netchan. + std::string clientName((char*)clientNamePtr, 32); // Get full name. + + if (clientName.empty()) // Empty name? + { + continue; + } + + if (strcmp(firstArg, clientName.c_str()) != 0) // Our wanted name? + { + continue; + } + + NET_DisconnectClient(client, i, "Kicked from Server", 0, 1); // Disconnect client. + } +} + +void _KickID_f_CompletionFunc(CCommand* cmd) +{ + static auto HasOnlyDigits = [](const std::string& string) + { + for (const char& character : string) + { + if (std::isdigit(character) == 0) + { + return false; + } + } + return true; + }; + + std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); + if (argSize < 2) // Do we atleast have 2 arguments? + { + return; + } + + CCommand& args = *cmd; // Get reference. + std::string firstArg = args[1]; // Get first arg. + + try + { + bool onlyDigits = HasOnlyDigits(firstArg); // Only has digits? + for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. + { + CClient* client = g_pClient->GetClientInstance(i); // Get client instance. + if (!client) + { + continue; + } + + if (!client->GetNetChan()) // Netchan valid? + { + continue; + } + + std::string finalIpAddress = "null"; // If this stays null they modified the packet somehow. + ADDRESS ipAddressField = ADDRESS(((std::uintptr_t)client->GetNetChan()) + 0x1AC0); // Get client ip from netchan. + if (ipAddressField) + { + std::stringstream ss; + ss << std::to_string(ipAddressField.GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x1).GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x2).GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x3).GetValue()); + + finalIpAddress = ss.str(); + } + + if (onlyDigits) + { + std::int64_t ID = static_cast(std::stoll(firstArg)); + if (ID > MAX_PLAYERS) // Is it a possible originID? + { + std::int64_t originID = client->m_iOriginID; + if (originID != ID) // See if they match. + { + continue; + } + } + else // If its not try by userID. + { + std::int64_t clientID = static_cast(client->m_iUserID + 1); // Get UserID + 1. + if (clientID != ID) // See if they match. + { + continue; + } + } + + NET_DisconnectClient(client, i, "Kicked from Server", 0, 1); // Disconnect client. + } + else + { + if (firstArg.compare(finalIpAddress) != NULL) // Do the string equal? + { + continue; + } + + NET_DisconnectClient(client, i, "Kicked from Server", 0, 1); // Disconnect client. + } + } + } + catch (std::exception& e) + { + DevMsg(eDLL_T::SERVER, "sv_kickid requires a UserID or OriginID. You can get the UserID with the 'status' command. Error: %s", e.what()); + return; + } +} + +void _Ban_f_CompletionFunc(CCommand* cmd) +{ + std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); + if (argSize < 2) // Do we atleast have 2 arguments? + { + return; + } + + CCommand& args = *cmd; // Get reference. + const char* firstArg = args[1]; // Get first arg. + + for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. + { + CClient* client = g_pClient->GetClientInstance(i); // Get client instance. + if (!client) + { + continue; + } + + if (!client->GetNetChan()) // Netchan valid? + { + continue; + } + + void* clientNamePtr = (void**)(((std::uintptr_t)client->GetNetChan()) + 0x1A8D); // Get client name from netchan. + std::string clientName((char*)clientNamePtr, 32); // Get full name. + + if (clientName.empty()) // Empty name? + { + continue; + } + + if (strcmp(firstArg, clientName.c_str()) != 0) // Our wanted name? + { + continue; + } + + std::string finalIpAddress = "null"; // If this stays null they modified the packet somehow. + ADDRESS ipAddressField = ADDRESS(((std::uintptr_t)client->GetNetChan()) + 0x1AC0); // Get client ip from netchan. + if (ipAddressField && ipAddressField.GetValue() != 0x0) + { + std::stringstream ss; + ss << std::to_string(ipAddressField.GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x1).GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x2).GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x3).GetValue()); + + finalIpAddress = ss.str(); + } + + g_pBanSystem->AddEntry(finalIpAddress, client->m_iOriginID); // Add ban entry. + g_pBanSystem->Save(); // Save ban list. + NET_DisconnectClient(client, i, "Banned from Server", 0, 1); // Disconnect client. + } +} + +void _BanID_f_CompletionFunc(CCommand* cmd) +{ + static auto HasOnlyDigits = [](const std::string& string) + { + for (const char& character : string) + { + if (std::isdigit(character) == 0) + { + return false; + } + } + return true; + }; + + std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); + if (argSize < 2) // Do we atleast have 2 arguments? + { + return; + } + + CCommand& args = *cmd; // Get reference. + std::string firstArg = args[1]; + + try + { + bool onlyDigits = HasOnlyDigits(firstArg); // Only has digits? + for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. + { + CClient* client = g_pClient->GetClientInstance(i); // Get client instance. + if (!client) + { + continue; + } + + if (!client->GetNetChan()) // Netchan valid? + { + continue; + } + + std::string finalIpAddress = "null"; // If this stays null they modified the packet somehow. + ADDRESS ipAddressField = ADDRESS(((std::uintptr_t)client->GetNetChan()) + 0x1AC0); // Get client ip from netchan. + if (ipAddressField) + { + std::stringstream ss; + ss << std::to_string(ipAddressField.GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x1).GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x2).GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x3).GetValue()); + + finalIpAddress = ss.str(); + } + + if (onlyDigits) + { + std::int64_t ID = static_cast(std::stoll(firstArg)); + if (ID > MAX_PLAYERS) // Is it a possible originID? + { + std::int64_t originID = client->m_iOriginID; + if (originID != ID) // See if they match. + { + continue; + } + } + else // If its not try by userID. + { + std::int64_t clientID = static_cast(client->m_iUserID + 1); // Get UserID + 1. + if (clientID != ID) // See if they match. + { + continue; + } + } + + g_pBanSystem->AddEntry(finalIpAddress, client->m_iOriginID); // Add ban entry. + g_pBanSystem->Save(); // Save ban list. + NET_DisconnectClient(client, i, "Banned from Server", 0, 1); // Disconnect client. + } + else + { + if (firstArg.compare(finalIpAddress) != NULL) // Do the string equal? + { + continue; + } + + g_pBanSystem->AddEntry(finalIpAddress, client->m_iOriginID); // Add ban entry. + g_pBanSystem->Save(); // Save ban list. + NET_DisconnectClient(client, i, "Banned from Server", 0, 1); // Disconnect client. + } + } + } + catch (std::exception& e) + { + DevMsg(eDLL_T::SERVER, "Banid Error: %s", e.what()); + return; + } +} + +void _Unban_f_CompletionFunc(CCommand* cmd) +{ + static auto HasOnlyDigits = [](const std::string& string) + { + for (const char& character : string) + { + if (std::isdigit(character) == 0) + { + return false; + } + } + return true; + }; + + std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); + if (argSize < 2) // Do we atleast have 2 arguments? + { + return; + } + + CCommand& args = *cmd; // Get reference. + + try + { + const char* firstArg = args[1]; + if (HasOnlyDigits(firstArg)) // Check if we have an ip address or origin ID. + { + g_pBanSystem->DeleteEntry("noIP", std::stoll(firstArg)); // Delete ban entry. + g_pBanSystem->Save(); // Save modified vector to file. + } + else + { + g_pBanSystem->DeleteEntry(firstArg, 1); // Delete ban entry. + g_pBanSystem->Save(); // Save modified vector to file. + } + } + catch (std::exception& e) + { + DevMsg(eDLL_T::SERVER, "Unban Error: %s", e.what()); + return; + } +} + +void _ReloadBanList_f_CompletionFunc(CCommand* cmd) +{ + g_pBanSystem->Load(); // Reload banlist. +} + +void _RTech_GenerateGUID_f_CompletionFunc(CCommand* cmd) +{ + std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); + + if (argSize < 2) // Do we atleast have 2 arguments? + { + return; + } + + CCommand& args = *cmd; // Get reference. + const char* firstArg = args[1]; // Get first arg. + unsigned long long guid = g_pRtech->StringToGuid(firstArg); + + DevMsg(eDLL_T::RTECH, "______________________________________________________________\n"); + DevMsg(eDLL_T::RTECH, "] RTECH_HASH -------------------------------------------------\n"); + DevMsg(eDLL_T::RTECH, "] GUID: '0x%llX'\n", guid); +} + +void _RTech_Decompress_f_CompletionFunc(CCommand* cmd) +{ + std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); + + if (argSize < 2) // Do we atleast have 2 arguments? + { + return; + } + + CCommand& args = *cmd; // Get reference. + std::string firstArg = args[1]; // Get first arg. + std::string secondArg = args[2]; // Get second arg. + + const std::string mod_dir = "paks\\Win32\\"; + const std::string base_dir = "paks\\Win64\\"; + + std::string pak_name_out = mod_dir + firstArg + ".rpak"; + std::string pak_name_in = base_dir + firstArg + ".rpak"; + + DevMsg(eDLL_T::RTECH, "______________________________________________________________\n"); + DevMsg(eDLL_T::RTECH, "] RTECH_DECOMPRESS -------------------------------------------\n"); + + if (!FileExists(pak_name_in.c_str())) + { + DevMsg(eDLL_T::RTECH, "Error: pak file '%s' does not exist!\n", pak_name_in.c_str()); + return; + } + + DevMsg(eDLL_T::RTECH, "] Processing: '%s'\n", pak_name_in.c_str()); + + std::vector upak; // Compressed region. + std::ifstream ipak(pak_name_in, std::fstream::binary); + + ipak.seekg(0, std::fstream::end); + upak.resize(ipak.tellg()); + ipak.seekg(0, std::fstream::beg); + ipak.read((char*)upak.data(), upak.size()); + + rpak_h* rheader = (rpak_h*)upak.data(); + uint16_t flags = (rheader->m_nFlags[0] << 8) | rheader->m_nFlags[1]; + + DevMsg(eDLL_T::RTECH, "______________________________________________________________\n"); + DevMsg(eDLL_T::RTECH, "] HEADER_DETAILS ---------------------------------------------\n"); + DevMsg(eDLL_T::RTECH, "] Magic : '%08X'\n", rheader->m_nMagic); + DevMsg(eDLL_T::RTECH, "] Version : '%u'\n", (rheader->m_nVersion)); + DevMsg(eDLL_T::RTECH, "] Flags : '%04X'\n", (flags)); + DevMsg(eDLL_T::RTECH, "] Hash : '%llu'\n", rheader->m_nHash); + DevMsg(eDLL_T::RTECH, "] Entries : '%zu'\n", rheader->m_nAssetEntryCount); + DevMsg(eDLL_T::RTECH, "______________________________________________________________\n"); + DevMsg(eDLL_T::RTECH, "] COMPRESSION_DETAILS ----------------------------------------\n"); + DevMsg(eDLL_T::RTECH, "] Size disk: '%lld'\n", rheader->m_nSizeDisk); + DevMsg(eDLL_T::RTECH, "] Size decp: '%lld'\n", rheader->m_nSizeMemory); + DevMsg(eDLL_T::RTECH, "] Ratio : '%.02f'\n", (rheader->m_nSizeDisk * 100.f) / rheader->m_nSizeMemory); + + if (rheader->m_nMagic != 'kaPR') + { + DevMsg(eDLL_T::RTECH, "Error: pak file '%s' has invalid magic!\n", pak_name_in.c_str()); + return; + } + if ((rheader->m_nFlags[1] & 1) != 1) + { + DevMsg(eDLL_T::RTECH, "Error: pak file '%s' already decompressed!\n", pak_name_in.c_str()); + return; + } + if (rheader->m_nSizeDisk != upak.size()) + { + DevMsg(eDLL_T::RTECH, "Error: pak file '%s' decompressed size '%u' doesn't match expected value '%u'!\n", pak_name_in.c_str(), upak.size(), rheader->m_nSizeMemory); + return; + } + + std::int64_t params[18]; + std::uint32_t dsize = g_pRtech->DecompressedSize((std::int64_t)(params), upak.data(), upak.size(), 0, PAK_HEADER_SIZE); + + if (dsize == rheader->m_nSizeDisk) + { + DevMsg(eDLL_T::RTECH, "Error: calculated size: '%zu' expected: '%zu'!\n", dsize, rheader->m_nSizeMemory); + return; + } + else + { + DevMsg(eDLL_T::RTECH, "] Calculated size: '%zu'\n", dsize); + } + + std::vector pakbuf(rheader->m_nSizeMemory, 0); + + params[1] = std::int64_t(pakbuf.data()); + params[3] = -1i64; + + std::uint8_t decomp_result = g_pRtech->Decompress(params, upak.size(), pakbuf.size()); + if (decomp_result != 1) + { + DevMsg(eDLL_T::RTECH, "Error: decompression failed for '%s' return value: '%u'!\n", pak_name_in.c_str(), +decomp_result); + return; + } + + rheader->m_nFlags[1] = 0x0; // Set compressed flag to false for the decompressed pak file + rheader->m_nSizeDisk = rheader->m_nSizeMemory; // Equal compressed size with decompressed + + std::ofstream out_block(pak_name_out, std::fstream::binary); + std::ofstream out_header(pak_name_out, std::fstream::binary); + + out_block.write((char*)pakbuf.data(), params[5]); + out_header.write((char*)rheader, PAK_HEADER_SIZE); + + DevMsg(eDLL_T::RTECH, "] Decompressed rpak to: '%s'\n", pak_name_out.c_str()); + DevMsg(eDLL_T::RTECH, "--------------------------------------------------------------\n"); +} + +void _NET_TraceNetChan_f_CompletionFunc(CCommand* cmd) +{ + static bool bTraceNetChannel = false; + if (!bTraceNetChannel) + { + g_pCvar->FindVar("net_usesocketsforloopback")->m_pParent->m_iValue = 1; + DevMsg(eDLL_T::ENGINE, "\n"); + DevMsg(eDLL_T::ENGINE, "+--------------------------------------------------------+\n"); + DevMsg(eDLL_T::ENGINE, "|>>>>>>>>>>>>>| NETCHANNEL TRACE ACTIVATED |<<<<<<<<<<<<<|\n"); + DevMsg(eDLL_T::ENGINE, "+--------------------------------------------------------+\n"); + DevMsg(eDLL_T::ENGINE, "\n"); + + // Begin the detour transaction to hook the the process + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + + CNetChan_Trace_Attach(); + // Commit the transaction + if (DetourTransactionCommit() != NO_ERROR) + { + // Failed to hook into the process, terminate + TerminateProcess(GetCurrentProcess(), 0xBAD0C0DE); + } + } + else + { + DevMsg(eDLL_T::ENGINE, "\n"); + DevMsg(eDLL_T::ENGINE, "+--------------------------------------------------------+\n"); + DevMsg(eDLL_T::ENGINE, "|>>>>>>>>>>>>| NETCHANNEL TRACE DEACTIVATED |<<<<<<<<<<<<|\n"); + DevMsg(eDLL_T::ENGINE, "+--------------------------------------------------------+\n"); + DevMsg(eDLL_T::ENGINE, "\n"); + + // Begin the detour transaction to hook the the process + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + + CNetChan_Trace_Detach(); + + // Commit the transaction + DetourTransactionCommit(); + } + bTraceNetChannel = !bTraceNetChannel; +} + +void _VPK_Decompress_f_CompletionFunc(CCommand* cmd) +{ + std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); + + if (argSize < 2) // Do we atleast have 2 arguments? + { + return; + } + + CCommand& args = *cmd; // Get reference. + std::string firstArg = args[1]; // Get first arg. + std::string szPathOut = "platform\\vpk"; + + std::chrono::milliseconds msStart = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); + + DevMsg(eDLL_T::FS, "______________________________________________________________\n"); + DevMsg(eDLL_T::FS, "] FS_DECOMPRESS ----------------------------------------------\n"); + DevMsg(eDLL_T::FS, "] Processing: '%s'\n", firstArg.c_str()); + + vpk_dir_h vpk = g_pPackedStore->GetPackDirFile(firstArg); + g_pPackedStore->InitLzParams(); + + std::thread th([&] { g_pPackedStore->UnpackAll(vpk, szPathOut); }); + th.join(); + + std::chrono::milliseconds msEnd = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); + float duration = msEnd.count() - msStart.count(); + + DevMsg(eDLL_T::FS, "______________________________________________________________\n"); + DevMsg(eDLL_T::FS, "] OPERATION_DETAILS ------------------------------------------\n"); + DevMsg(eDLL_T::FS, "] Time elapsed: '%.3f' seconds\n", (duration / 1000)); + DevMsg(eDLL_T::FS, "] Decompressed vpk to: '%s'\n", szPathOut.c_str()); + DevMsg(eDLL_T::FS, "--------------------------------------------------------------\n"); +} + +void _NET_SetKey_f_CompletionFunc(CCommand* cmd) +{ + CCommand& args = *cmd; // Get reference. + std::string firstArg = args[1]; // Get first arg. + + std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); + + if (argSize < 2) // Do we atleast have 2 arguments? + { + return; + } + + HNET_SetKey(firstArg); +} + +void _NET_GenerateKey_f_CompletionFunc(CCommand* cmd) +{ + HNET_GenerateKey(); +} diff --git a/r5dev/tier0/completion.h b/r5dev/tier0/completion.h new file mode 100644 index 00000000..5a01962b --- /dev/null +++ b/r5dev/tier0/completion.h @@ -0,0 +1,49 @@ +#pragma once +#include "IConVar.h" + +namespace +{ + /* ==== CONCOMMANDCALLBACK ============================================================================================================================================== */ +#if defined (GAMEDLL_S1) + ADDRESS p_Map_Callback = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x18\x55\x41\x56\x41\x00\x00\x00\x00\x40\x02", "xxxxxxxxx????xx"); + void (*Map_Callback)(CCommand* cmd, char a2) = (void (*)(CCommand*, char))p_Map_Callback.GetPtr(); /*48 89 5C 24 18 55 41 56 41 ?? ?? ?? ?? 40 02*/ +#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) + ADDRESS p_Map_Callback = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x55\x41\x56\x41\x57\x48\x81\xEC\x00\x00\x00\x00\x83\x3D", "xxxxxxxxx????xx"); + void (*Map_Callback)(CCommand* cmd, char a2) = (void (*)(CCommand*, char))p_Map_Callback.GetPtr(); /*40 55 41 56 41 57 48 81 EC ?? ?? ?? ?? 83 3D*/ +#endif + ADDRESS p_DownloadPlaylists_Callback = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x33\xC9\xC6\x05\x00\x00\x00\x00\x00\xE9\x00\x00\x00\x00", "xxxx?????x????"); + void (*DownloadPlaylists_Callback)() = (void(*)())p_DownloadPlaylists_Callback.GetPtr(); /*33 C9 C6 05 ?? ?? ?? ?? ?? E9 ?? ?? ?? ??*/ +} + +/////////////////////////////////////////////////////////////////////////////// +#ifndef DEDICATED +void _CGameConsole_f_CompletionFunc(const CCommand& cmd); +void _CCompanion_f_CompletionFunc(const CCommand& cmd); +#endif // !DEDICATED +void _Kick_f_CompletionFunc(CCommand* cmd); +void _KickID_f_CompletionFunc(CCommand* cmd); +void _Ban_f_CompletionFunc(CCommand* cmd); +void _BanID_f_CompletionFunc(CCommand* cmd); +void _Unban_f_CompletionFunc(CCommand* cmd); +void _ReloadBanList_f_CompletionFunc(CCommand* cmd); +void _RTech_GenerateGUID_f_CompletionFunc(CCommand* cmd); +void _RTech_Decompress_f_CompletionFunc(CCommand* cmd); +void _VPK_Decompress_f_CompletionFunc(CCommand* cmd); +void _NET_TraceNetChan_f_CompletionFunc(CCommand* cmd); +void _NET_SetKey_f_CompletionFunc(CCommand* cmd); +void _NET_GenerateKey_f_CompletionFunc(CCommand* cmd); + + +/////////////////////////////////////////////////////////////////////////////// +class HCompletion : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: Map_Callback : 0x" << std::hex << std::uppercase << p_Map_Callback.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: DownloadPlaylists_Callback : 0x" << std::hex << std::uppercase << p_DownloadPlaylists_Callback.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HCompletion); diff --git a/r5dev/tier0/cvar.cpp b/r5dev/tier0/cvar.cpp new file mode 100644 index 00000000..af17f00c --- /dev/null +++ b/r5dev/tier0/cvar.cpp @@ -0,0 +1,40 @@ +#include "core/stdafx.h" +#include "tier0/cvar.h" +#include "tier0/IConVar.h" +#include "engine/sys_dll2.h" + +//------------------------------------------------------------------------- +// ENGINE | +ConVar* cm_debug_cmdquery = new ConVar(); +ConVar* cm_return_false_cmdquery_all = new ConVar(); +ConVar* cm_return_false_cmdquery_dev_cheat = new ConVar(); +//------------------------------------------------------------------------- +// SERVER | +ConVar* sv_showconnecting = new ConVar(); +//------------------------------------------------------------------------- +// CLIENT | +ConVar* cl_drawconsoleoverlay = new ConVar(); +ConVar* cl_consoleoverlay_lines = new ConVar(); +ConVar* cl_consoleoverlay_offset_x = new ConVar(); +ConVar* cl_consoleoverlay_offset_y = new ConVar(); +//------------------------------------------------------------------------- +// FILESYSTEM | +ConVar* fs_warning_level_native = new ConVar(); +ConVar* fs_packedstore_entryblock_stats = new ConVar(); +//------------------------------------------------------------------------- +// MATERIALSYSTEM | +ConVar* mat_showdxoutput = new ConVar(); +//------------------------------------------------------------------------- +// SQUIRREL | +ConVar* sq_showrsonloading = new ConVar(); +ConVar* sq_showscriptloading = new ConVar(); +ConVar* sq_showvmoutput = new ConVar(); +ConVar* sq_showvmwarning = new ConVar(); +//------------------------------------------------------------------------- +// NETCHANNEL | +ConVar* net_userandomkey = new ConVar(); +ConVar* r5net_matchmaking_hostname = new ConVar(); +ConVar* r5net_show_debug = new ConVar(); + +/////////////////////////////////////////////////////////////////////////////// +CCVar* g_pCvar = reinterpret_cast(p_CEngineAPI_Connect.FindPatternSelf("48 8D 0D", ADDRESS::Direction::DOWN, 40).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr()); diff --git a/r5dev/tier0/cvar.h b/r5dev/tier0/cvar.h new file mode 100644 index 00000000..022f1eae --- /dev/null +++ b/r5dev/tier0/cvar.h @@ -0,0 +1,66 @@ +#pragma once +#include "tier0/basetypes.h" +#include "tier0/IConVar.h" + +namespace +{ + /* ==== CCVAR =========================================================================================================================================================== */ +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) + ADDRESS p_CCvar_Disconnect = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x57\x41\x56\x48\x83\xEC\x38\x4C\x8B\x35", "xxxxxxxxxxx"); + void* (*CCvar_Disconnect)() = (void* (*)())p_CCvar_Disconnect.GetPtr(); /*40 57 41 56 48 83 EC 38 4C 8B 35 ? ? ? ?*/ +#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) + ADDRESS p_CCvar_Disconnect = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x83\xEC\x28\x48\x8B\x0D\x00\x00\x00\x00\x48\x85\xC9\x74\x26\x80\x3D\x00\x00\x00\x00\x00\x74\x1D\x48\x8B\x01\x8B\x15\x00\x00\x00\x00\xFF\x50\x58\xC7\x05\x00\x00\x00\x00\x00\x00\x00\x00\xC6\x05\x00\x00\x00\x00\x00\x48\xC7\x05\x00\x00\x00", "xxxxxxx????xxxxxxx?????xxxxxxx????xxxxx????????xx"); + void* (*CCvar_Disconnect)() = (void* (*)())p_CCvar_Disconnect.GetPtr(); /*48 83 EC 28 48 8B 0D ? ? ? ? 48 85 C9 74 26 80 3D ? ? ? ? ? 74 1D 48 8B 01 8B 15 ? ? ? ? FF 50 58 C7 05 ? ? ? ? ? ? ? ? C6 05 ? ? ? ? ? 48 C7 05 ? ? ? ? ? ? ? ?*/ +#endif +} + +//------------------------------------------------------------------------- +// ENGINE | +extern ConVar* cm_debug_cmdquery; +extern ConVar* cm_return_false_cmdquery_all; +extern ConVar* cm_return_false_cmdquery_dev_cheat; +//------------------------------------------------------------------------- +// SERVER | +extern ConVar* sv_showconnecting; +//------------------------------------------------------------------------- +// CLIENT | +extern ConVar* cl_drawconsoleoverlay; +extern ConVar* cl_consoleoverlay_lines; +extern ConVar* cl_consoleoverlay_offset_x; +extern ConVar* cl_consoleoverlay_offset_y; +//------------------------------------------------------------------------- +// FILESYSTEM | +extern ConVar* fs_warning_level_native; +extern ConVar* fs_packedstore_entryblock_stats; +//------------------------------------------------------------------------- +// FILESYSTEM | +extern ConVar* mat_showdxoutput; +//------------------------------------------------------------------------- +// SQUIRREL | +extern ConVar* sq_showrsonloading; +extern ConVar* sq_showscriptloading; +extern ConVar* sq_showvmoutput; +extern ConVar* sq_showvmwarning; +//------------------------------------------------------------------------- +// NETCHANNEL | +extern ConVar* net_userandomkey; +extern ConVar* r5net_matchmaking_hostname; +extern ConVar* r5net_show_debug; + +/////////////////////////////////////////////////////////////////////////////// +extern CCVar* g_pCvar; + + +/////////////////////////////////////////////////////////////////////////////// +class HCvar : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: CCvar::Disconnect : 0x" << std::hex << std::uppercase << p_CCvar_Disconnect.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: g_pCvar : 0x" << std::hex << std::uppercase << g_pCvar << std::setw(0) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HCvar); diff --git a/r5dev/tier0/interface.h b/r5dev/tier0/interface.h new file mode 100644 index 00000000..f41e6ef7 --- /dev/null +++ b/r5dev/tier0/interface.h @@ -0,0 +1,4 @@ +#pragma once + +typedef void* (*CreateInterfaceFn)(const char* pName, int* pReturnCode); +typedef void* (*InstantiateInterfaceFn)(); diff --git a/r5dev/vgui/CEngineVGui.cpp b/r5dev/vgui/CEngineVGui.cpp new file mode 100644 index 00000000..ed912a0e --- /dev/null +++ b/r5dev/vgui/CEngineVGui.cpp @@ -0,0 +1,118 @@ +#include "core/stdafx.h" +#include "tier0/cvar.h" +#include "vgui/CEngineVGui.h" +#include "vguimatsurface/MatSystemSurface.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int HCEngineVGui_Paint(void* thisptr, int mode) +{ + int result = CEngineVGui_Paint(thisptr, mode); + + static void* pCMatSystemSurface = ADDRESS(0x14D40B3B0).RCast(); + static auto fnRenderStart = ADDRESS(0x14053EFC0).RCast(); + static auto fnRenderEnd = ADDRESS(0x14053F1B0).RCast(); + + if (mode == 1 || mode == 2) // Render in-main menu and in-game. + { + fnRenderStart(pCMatSystemSurface); + + g_pLogSystem.Update(); + + fnRenderEnd(); + } + + return result; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogSystem::Update() +{ + if (cl_drawconsoleoverlay->m_pParent->m_iValue < 1) + { + return; + } + if (m_vLogs.empty()) + { + return; + } + if (!g_pMatSystemSurface) + { + return; + } + + static int fontHeight = 16; + + for (int i = 0; i < m_vLogs.size(); ++i) + { + if (m_vLogs[i].Ticks >= 0) + { + if (i < cl_consoleoverlay_lines->m_pParent->m_iValue) + { + float fadepct = fminf(static_cast(m_vLogs[i].Ticks) / 255.f, 4.0); // TODO [ AMOS ]: register a ConVar for this! + float ptc = static_cast(ceilf(fadepct * 100.f)); // TODO [ AMOS ]: register a ConVar for this! + int alpha = static_cast(ptc); + int y = (cl_consoleoverlay_offset_y->m_pParent->m_iValue + (fontHeight * i)); + int x = cl_consoleoverlay_offset_x->m_pParent->m_iValue; + + std::array color = GetLogColorForType(m_vLogs[i].Type); + CMatSystemSurface_DrawColoredText(g_pMatSystemSurface, 0x13, fontHeight, x, y, color[0], color[1], color[2], alpha, m_vLogs[i].Message.c_str()); + } + else + { + m_vLogs.erase(m_vLogs.begin()); + continue; + } + + m_vLogs[i].Ticks--; + } + else + { + m_vLogs.erase(m_vLogs.begin() + i); + } + } +} + +void CLogSystem::AddLog(LogType_t type, std::string message) +{ + if (message.length() > 0) + { + m_vLogs.push_back(Log{ message, 1024, type }); + } +} + +std::array CLogSystem::GetLogColorForType(LogType_t type) +{ + switch (type) + { + case LogType_t::NATIVE: + return { 255, 255, 255 }; + case LogType_t::SCRIPT_SERVER: + return { 190, 183, 240 }; + case LogType_t::SCRIPT_CLIENT: + return { 117, 116, 139 }; + case LogType_t::SCRIPT_UI: + return { 197, 160, 177 }; + default: + return { 255, 255, 255 }; + } + + return { 255, 255, 255 }; +} + +/////////////////////////////////////////////////////////////////////////////// +void CEngineVGui_Attach() +{ + //DetourAttach((LPVOID*)&CEngineVGui_Paint, &HCEngineVGui_Paint); +} + +void CEngineVGui_Detach() +{ + //DetourDetach((LPVOID*)&CEngineVGui_Paint, &HCEngineVGui_Paint); +} + +/////////////////////////////////////////////////////////////////////////////// +CLogSystem g_pLogSystem; diff --git a/r5dev/vgui/CEngineVGui.h b/r5dev/vgui/CEngineVGui.h new file mode 100644 index 00000000..fbdcd77b --- /dev/null +++ b/r5dev/vgui/CEngineVGui.h @@ -0,0 +1,76 @@ +#pragma once +#include "core/stdafx.h" +#include "tier0/basetypes.h" + +enum class LogType_t : int +{ + SCRIPT_SERVER, + SCRIPT_CLIENT, + SCRIPT_UI, + SCRIPT_WARNING, + NATIVE +}; + +struct Log +{ + Log(const std::string Message, const int Ticks, const LogType_t Type) + { + this->Message = Message; + this->Ticks = Ticks; + this->Type = Type; + } + std::string Message = ""; + int Ticks = 1024; + LogType_t Type = LogType_t::NATIVE; +}; + +class CLogSystem +{ +public: + void AddLog(LogType_t type, std::string text); + void Update(); + +private: + std::array GetLogColorForType(LogType_t type); + std::vector m_vLogs{}; +}; + +namespace +{ + /* ==== CENGINEVGUI ===================================================================================================================================================== */ +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) + ADDRESS p_CEngineVGui_Paint = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x89\x54\x24\x10\x55\x56\x41\x55\x48\x81\xEC\x00\x00\x00\x00", "xxxxxxxxxxx????"); + int (*CEngineVGui_Paint)(void* thisptr, int mode) = (int (*)(void*, int))p_CEngineVGui_Paint.GetPtr(); /*41 55 41 56 48 83 EC 78 44 8B EA*/ + + ADDRESS p_CEngineVGui_Unknown = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x4C\x8B\x81\x00\x00\x00\x00\x48\x8D\x05\x00\x00\x00\x00\x4C\x3B\xC0\x74\x1F", "xxx????xxx????xxxxx"); + void** (*CEngineVGui_Unknown)(std::int64_t a1) = (void** (*)(std::int64_t))p_CEngineVGui_Paint.GetPtr(); /*4C 8B 81 ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 4C 3B C0 74 1F*/ +#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) + ADDRESS p_CEngineVGui_Paint = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x41\x55\x41\x56\x48\x83\xEC\x78\x44\x8B\xEA", "xxxxxxxxxxx"); + int (*CEngineVGui_Paint)(void* thisptr, int mode) = (int (*)(void*, int))p_CEngineVGui_Paint.GetPtr(); /*41 55 41 56 48 83 EC 78 44 8B EA*/ + + ADDRESS p_CEngineVGui_Unknown = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x53\x48\x83\xEC\x20\x48\x8D\x05\x00\x00\x00\x00\x48\x8B\xD9\x48\x39\x81\x00\x00\x00\x00\x74\x29", "xxxxxxxxx????xxxxxx????xx"); + void** (*CEngineVGui_Unknown)(std::int64_t a1) = (void** (*)(std::int64_t))p_CEngineVGui_Paint.GetPtr(); /*40 53 48 83 EC 20 48 8D 05 ?? ?? ?? ?? 48 8B D9 48 39 81 ?? ?? ?? ?? 74 29*/ +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +int HCEngineVGui_Paint(void* thisptr, int mode); +void CEngineVGui_Attach(); +void CEngineVGui_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +extern CLogSystem g_pLogSystem; + +/////////////////////////////////////////////////////////////////////////////// +class HEngineVGui : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: CEngineVGui::Paint : 0x" << std::hex << std::uppercase << p_CEngineVGui_Paint.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: CEngineVGui::Unknown : 0x" << std::hex << std::uppercase << p_CEngineVGui_Unknown.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HEngineVGui); diff --git a/r5dev/vgui/vgui.h b/r5dev/vgui/vgui.h new file mode 100644 index 00000000..dfb22c15 --- /dev/null +++ b/r5dev/vgui/vgui.h @@ -0,0 +1,19 @@ +#pragma once + +namespace vgui +{ + // handle to an internal vgui panel + // this is the only handle to a panel that is valid across dll boundaries + typedef unsigned int VPANEL; + + // handles to vgui objects + // NULL values signify an invalid value + typedef unsigned long HScheme; + // Both -1 and 0 are used for invalid textures. Be careful. + typedef unsigned long HTexture; + typedef unsigned long HCursor; + typedef unsigned long HPanel; + const HPanel INVALID_PANEL = 0xffffffff; + typedef unsigned long HFont; + const HFont INVALID_FONT = 0; // the value of an invalid font handle +} diff --git a/r5dev/vgui/vgui_fpspanel.cpp b/r5dev/vgui/vgui_fpspanel.cpp new file mode 100644 index 00000000..26a0a8fb --- /dev/null +++ b/r5dev/vgui/vgui_fpspanel.cpp @@ -0,0 +1,20 @@ +#include "core/stdafx.h" +#include "tier0/cvar.h" +#include "vgui/vgui_fpspanel.h" +#include "vgui/CEngineVGui.h" + +ConVar* HCFPSPanel_Paint(void* thisptr) +{ + g_pLogSystem.Update(); + return CFPSPanel_Paint(thisptr); +} + +void CFPSPanel_Attach() +{ + DetourAttach((LPVOID*)&CFPSPanel_Paint, &HCFPSPanel_Paint); +} + +void CFPSPanel_Detach() +{ + DetourDetach((LPVOID*)&CFPSPanel_Paint, &HCFPSPanel_Paint); +} diff --git a/r5dev/vgui/vgui_fpspanel.h b/r5dev/vgui/vgui_fpspanel.h new file mode 100644 index 00000000..5e4693be --- /dev/null +++ b/r5dev/vgui/vgui_fpspanel.h @@ -0,0 +1,24 @@ +#pragma once +#include "tier0/IConVar.h" +namespace +{ + /* ==== CFPSPANEL ======================================================================================================================================================= */ + ADDRESS p_CFPSPanel_Paint = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x8B\xC4\x55\x56\x41\x00\x48\x8D\xA8\x00\xFD\xFF\xFF\x48\x81\xEC\x80", "xxxxxx?xxx?xxxxxxx"); + ConVar* (*CFPSPanel_Paint)(void* thisptr) = (ConVar* (*)(void*))p_CFPSPanel_Paint.GetPtr(); /*48 8B C4 55 56 41 ?? 48 8D A8 ?? FD FF FF 48 81 EC 80*/ +} + +void CFPSPanel_Attach(); +void CFPSPanel_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +class HFPSPanel : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: CFPSPanel::Paint : 0x" << std::hex << std::uppercase << p_CFPSPanel_Paint.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HFPSPanel); diff --git a/r5dev/vguimatsurface/MatSystemSurface.cpp b/r5dev/vguimatsurface/MatSystemSurface.cpp new file mode 100644 index 00000000..0090c60c --- /dev/null +++ b/r5dev/vguimatsurface/MatSystemSurface.cpp @@ -0,0 +1,3 @@ +#include "core/stdafx.h" +#include "vguimatsurface/MatSystemSurface.h" +//TODO diff --git a/r5dev/vguimatsurface/MatSystemSurface.h b/r5dev/vguimatsurface/MatSystemSurface.h new file mode 100644 index 00000000..bb2b9dfa --- /dev/null +++ b/r5dev/vguimatsurface/MatSystemSurface.h @@ -0,0 +1,31 @@ +#pragma once +#include "client/cdll_engine_int.h" +namespace +{ + /* ==== CMATSYSTEMSURFACE =============================================================================================================================================== */ + ADDRESS p_CMatSystemSurface_DrawColoredText = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x4C\x8B\xDC\x48\x83\xEC\x68\x49\x8D\x43\x58\x0F\x57\xC0", "xxxxxxxxxxxxxx"); /*4C 8B DC 48 83 EC 68 49 8D 43 58 0F 57 C0*/ + void* (*CMatSystemSurface_DrawColoredText)(void* thisptr, int font, int fontHeight, int offsetX, int offsetY, int red, int green, int blue, int alpha, const char* text, ...) = (void* (*)(void*, int, int, int, int, int, int, int, int, const char*, ...))p_CMatSystemSurface_DrawColoredText.GetPtr(); +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) + ADDRESS p_CMatSystemSurface_Unknown0 = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x8B\x0D\x00\x00\x00\x00\x48\x8B\x01\x48\xFF\xA0\x18\x01\x00\x00", "xxx????xxxxxxxxxx"); /*48 8B 0D ?? ?? ?? ?? 48 8B 01 48 FF A0 18 01 00 00*/ + std::int64_t(*CMatSystemSurface_Unknown0)() = (std::int64_t(*)())p_CMatSystemSurface_Unknown0.GetPtr(); // [ AMOS ] DELETE +#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) + ADDRESS p_CMatSystemSurface_Unknown0 = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x8B\x0D\x00\x00\x00\x00\x48\x8B\x01\x48\xFF\xA0\x20\x01\x00\x00", "xxx????xxxxxxxxxx"); /*48 8B 0D ?? ?? ?? ?? 48 8B 01 48 FF A0 20 01 00 00*/ + void*(*CMatSystemSurface_Unknown0)() = (void*(*)())p_CMatSystemSurface_Unknown0.GetPtr(); // [ AMOS ] DELETE +#endif + ADDRESS g_pMatSystemSurface = p_CHLClient_PostInit.FindPatternSelf("48 83 3D", ADDRESS::Direction::DOWN, 40).ResolveRelativeAddressSelf(0x3, 0x8).GetPtr(); +} + +/////////////////////////////////////////////////////////////////////////////// +class HMatSystemSurface : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: CMatSystemSurface::DrawColoredText : 0x" << std::hex << std::uppercase << p_CMatSystemSurface_DrawColoredText.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: CMatSystemSurface::Unknown0 : 0x" << std::hex << std::uppercase << p_CMatSystemSurface_Unknown0.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: g_pMatSystemSurface : 0x" << std::hex << std::uppercase << g_pMatSystemSurface.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HMatSystemSurface); diff --git a/r5dev/vpc/IAppSystem.cpp b/r5dev/vpc/IAppSystem.cpp new file mode 100644 index 00000000..5f5abc70 --- /dev/null +++ b/r5dev/vpc/IAppSystem.cpp @@ -0,0 +1,3 @@ +#include "core/stdafx.h" +#include "IAppSystem.h" +//TODO diff --git a/r5dev/vpc/IAppSystem.h b/r5dev/vpc/IAppSystem.h new file mode 100644 index 00000000..1a959faa --- /dev/null +++ b/r5dev/vpc/IAppSystem.h @@ -0,0 +1,22 @@ +#pragma once +#include "tier0/basetypes.h" + +namespace +{ + /* ==== IAPPSYSTEM ============================================================================================================================================== */ + ADDRESS p_IAppSystem_LoadLibrary = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x83\xEC\x28\x48\x8B\x0D\x00\x00\x00\x00\x48\x8D\x05\x00\x00\x00\x00\x48\x89\x05\x00\x00\x00\x00\x48\x85\xC9\x74\x11", "xxxxxxx????xxx????xxx????xxxxx"); + void* (*IAppSystem_LoadLibrary)() = (void * (*)())p_IAppSystem_LoadLibrary.GetPtr(); /*48 83 EC 28 48 8B 0D ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 48 89 05 ?? ?? ?? ?? 48 85 C9 74 11*/ // C initializers/terminators +} + +/////////////////////////////////////////////////////////////////////////////// +class HAppSystem : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: IAppSystem::LoadLibrary : 0x" << std::hex << std::uppercase << p_IAppSystem_LoadLibrary.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HAppSystem); diff --git a/r5dev/vpc/basefilesystem.cpp b/r5dev/vpc/basefilesystem.cpp new file mode 100644 index 00000000..fc127b30 --- /dev/null +++ b/r5dev/vpc/basefilesystem.cpp @@ -0,0 +1,63 @@ +#include "core/stdafx.h" +#include "core/logdef.h" +#include "tier0/cvar.h" +#include "vpc/basefilesystem.h" +#include "gameui/IConsole.h" + +//--------------------------------------------------------------------------------- +// Purpose: prints the output of the filesystem based on the warning level +//--------------------------------------------------------------------------------- +void HCBaseFileSystem_Warning(void* thisptr, FileWarningLevel_t level, const char* fmt, ...) +{ + if (fs_warning_level_native->m_pParent->m_iValue < (int)level) + { + return; + } + + static bool initialized = false; + static char buf[1024] = {}; + + static auto iconsole = spdlog::stdout_logger_mt("fs_warn_iconsole"); // in-game console. + static auto wconsole = spdlog::stdout_logger_mt("fs_warn_wconsole"); // windows console. + + fs_oss.str(""); + fs_oss.clear(); + + if (!initialized) + { + iconsole = std::make_shared("fs_warn_ostream", fs_ostream_sink); + iconsole->set_pattern("[%S.%e] %v"); + iconsole->set_level(spdlog::level::debug); + wconsole->set_pattern("[%S.%e] %v"); + wconsole->set_level(spdlog::level::debug); + initialized = true; + } + + va_list args{}; + va_start(args, fmt); + + vsnprintf(buf, sizeof(buf), fmt, args); + + buf[sizeof(buf) - 1] = 0; + va_end(args); + + iconsole->debug(buf); + wconsole->debug(buf); + +#ifndef DEDICATED + std::string s = fs_oss.str(); + const char* c = s.c_str(); + + Items.push_back(Strdup((const char*)c)); +#endif // !DEDICATED +} + +void CBaseFileSystem_Attach() +{ + DetourAttach((LPVOID*)&CBaseFileSystem_Warning, &HCBaseFileSystem_Warning); +} + +void CBaseFileSystem_Detach() +{ + DetourDetach((LPVOID*)&CBaseFileSystem_Warning, &HCBaseFileSystem_Warning); +} diff --git a/r5dev/vpc/basefilesystem.h b/r5dev/vpc/basefilesystem.h new file mode 100644 index 00000000..cc16fc15 --- /dev/null +++ b/r5dev/vpc/basefilesystem.h @@ -0,0 +1,37 @@ +#pragma once + +enum class FileWarningLevel_t : int +{ + FILESYSTEM_WARNING = -1, // A problem! + FILESYSTEM_WARNING_QUIET = 0, // Don't print anything + FILESYSTEM_WARNING_REPORTUNCLOSED, // On shutdown, report names of files left unclosed + FILESYSTEM_WARNING_REPORTUSAGE, // Report number of times a file was opened, closed + FILESYSTEM_WARNING_REPORTALLACCESSES, // Report all open/close events to console ( !slow! ) + FILESYSTEM_WARNING_REPORTALLACCESSES_READ, // Report all open/close/read events to the console ( !slower! ) + FILESYSTEM_WARNING_REPORTALLACCESSES_READWRITE, // Report all open/close/read/write events to the console ( !slower! ) + FILESYSTEM_WARNING_REPORTALLACCESSES_ASYNC // Report all open/close/read/write events and all async I/O file events to the console ( !slower(est)! ) +}; + +namespace +{ + /* ==== CBASEFILESYSTEM ================================================================================================================================================= */ + ADDRESS p_CBaseFileSystem_Warning = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x4C\x89\x4C\x24\x20\xC3\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\x48", "xxxxxx??????????x"); + void (*CBaseFileSystem_Warning)(void* thisptr, FileWarningLevel_t level, const char* fmt, ...) = (void (*)(void*, FileWarningLevel_t, const char*, ...))p_CBaseFileSystem_Warning.GetPtr(); /*4C 89 4C 24 20 C3 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 48*/ +} + +/////////////////////////////////////////////////////////////////////////////// +void CBaseFileSystem_Attach(); +void CBaseFileSystem_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +class HBaseFileSystem : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: CBaseFileSystem::Warning : 0x" << std::hex << std::uppercase << p_CBaseFileSystem_Warning.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HBaseFileSystem); diff --git a/r5dev/vpc/interfaces.h b/r5dev/vpc/interfaces.h new file mode 100644 index 00000000..aef3f6e3 --- /dev/null +++ b/r5dev/vpc/interfaces.h @@ -0,0 +1,15 @@ +#pragma once + +/*----------------------------------------------------------------------------- + * _interfaces.h + *-----------------------------------------------------------------------------*/ + +//----------------------------------------------------------------------------- +// Mapping of interface string to globals +//----------------------------------------------------------------------------- +struct InterfaceGlobals_t +{ + std::int64_t(*m_pInterfacePtr)(void); + const char* m_pInterfaceName; + std::int64_t* m_pNextInterfacePtr; +}; diff --git a/r5dev/vpc/keyvalues.cpp b/r5dev/vpc/keyvalues.cpp new file mode 100644 index 00000000..e9735d1c --- /dev/null +++ b/r5dev/vpc/keyvalues.cpp @@ -0,0 +1,100 @@ +#include "core/stdafx.h" +#include "vpc/keyvalues.h" +#include "rtech/stryder.h" +#include "engine/sys_dll2.h" + +/////////////////////////////////////////////////////////////////////////////// +std::vector g_szAllPlaylists = { "none" }; +CKeyValuesSystem* g_pKeyValuesSystem = reinterpret_cast(p_KeyValues_Init.FindPatternSelf("48 8D ?? ?? ?? ?? 01", ADDRESS::Direction::DOWN, 100).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr()); + +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) +KeyValues** g_pPlaylistKeyValues = reinterpret_cast(p_Stryder_StitchRequest.FindPatternSelf("48 8B 2D", ADDRESS::Direction::DOWN, 100).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr()); // Get the KeyValue for the playlist file. +#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) +KeyValues** g_pPlaylistKeyValues = reinterpret_cast(p_Stryder_StitchRequest.FindPatternSelf("48 8B 0D", ADDRESS::Direction::DOWN, 100).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr()); // Get the KeyValue for the playlist file. +#endif + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CKeyValueSystem_InitPlaylist() +{ + while (true) + { + if ((*g_pPlaylistKeyValues)) + { + KeyValues* playlists = (*g_pPlaylistKeyValues)->FindKey("Playlists", false); // Find playlists key. + if (playlists) + { + g_szAllPlaylists.clear(); + for (KeyValues* dat = playlists->m_pSub; dat != nullptr; dat = dat->m_pPeer) // Parse through all sub keys. + { + g_szAllPlaylists.push_back(dat->GetName()); // Get all playlist names. + } + + break; // Break if playlist got filled. + } + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool HKeyValues_LoadPlaylist(const char* playlist) +{ + memset(g_pMapVPKCache, 0, 0x40); // Clear VPK cache to prevent crash while loading playlist. + + CHAR sPlaylistPath[] = "\x77\x27\x35\x2b\x2c\x6c\x2b\x2c\x2b"; + PCHAR curr = sPlaylistPath; + while (*curr) + { + *curr ^= 'B'; + ++curr; + } + + if (FileExists(sPlaylistPath)) + { + std::uint8_t verifyPlaylistIntegrity[] = // Very hacky way for alternative inline assembly for x64.. + { + 0x48, 0x8B, 0x45, 0x58, // mov rcx, playlist + 0xC7, 0x00, 0x00, 0x00, 0x00, // test playlist, playlist + 0x00 + }; + void* verifyPlaylistIntegrityFn = nullptr; + VirtualAlloc(verifyPlaylistIntegrity, 10, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + memcpy(&verifyPlaylistIntegrityFn, (const void*)verifyPlaylistIntegrity, 9); + reinterpret_cast(verifyPlaylistIntegrityFn)(); + } + + return KeyValues_LoadPlaylist(playlist); // Parse playlist. +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char* KeyValues::GetName() +{ + return g_pKeyValuesSystem->GetStringForSymbol(MAKE_3_BYTES_FROM_1_AND_2(m_iKeyNameCaseSensitive, m_iKeyNameCaseSensitive2)); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CKeyValueSystem_Init() +{ + std::thread t1(CKeyValueSystem_InitPlaylist); // Start thread to grab playlists. + t1.detach(); // Detach thread from current one. +} + +/////////////////////////////////////////////////////////////////////////////// +void CKeyValueSystem_Attach() +{ + DetourAttach((LPVOID*)&KeyValues_LoadPlaylist, &HKeyValues_LoadPlaylist); +} + +void CKeyValueSystem_Detach() +{ + DetourDetach((LPVOID*)&KeyValues_LoadPlaylist, &HKeyValues_LoadPlaylist); +} diff --git a/r5dev/vpc/keyvalues.h b/r5dev/vpc/keyvalues.h new file mode 100644 index 00000000..9963479a --- /dev/null +++ b/r5dev/vpc/keyvalues.h @@ -0,0 +1,263 @@ +#pragma once +#include "tier0/basetypes.h" + +#define MAKE_3_BYTES_FROM_1_AND_2( x1, x2 ) (( (( std::uint16_t )x2) << 8 ) | (std::uint8_t)(x1)) +extern std::vector g_szAllPlaylists; +typedef int HKeySymbol; + +enum KeyValuesTypes +{ + TYPE_NONE = 0x0, + TYPE_STRING = 0x1, + TYPE_INT = 0x2, + TYPE_FLOAT = 0x3, + TYPE_PTR = 0x4, + TYPE_WSTRING = 0x5, + TYPE_COLOR = 0x6, + TYPE_UINT64 = 0x7, + TYPE_COMPILED_INT_BYTE = 0x8, + TYPE_COMPILED_INT_0 = 0x9, + TYPE_COMPILED_INT_1 = 0xA, + TYPE_NUMTYPES = 0xB, +}; + +namespace +{ + /* ==== KEYVALUES ======================================================================================================================================================= */ +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) + ADDRESS p_KeyValues_Init = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x53\x48\x83\xEC\x20\x48\x8B\xD9\xC7\x44\x24\x30\xFF\xFF\xFF", "xxxxxxxxxxxxxxxx"); /*40 53 48 83 EC 20 48 8B D9 C7 44 24 30 FF FF FF*/ + std::int64_t(*KeyValues_Init)(std::int64_t a1, std::int64_t a2, std::int64_t a3, std::int64_t a4) = (std::int64_t(*)(std::int64_t, std::int64_t, std::int64_t, std::int64_t))p_KeyValues_Init.GetPtr(); + + ADDRESS p_KeyValues_FindKey = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x10\x48\x89\x6C\x24\x18\x48\x89\x74\x24\x20\x57\x41\x54\x41\x55\x41\x56\x41\x57\x48\x81\xEC\x20\x01\x00\x00\x45", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + void* (*KeyValues_FindKey)(void* a1, const char* a2, bool a3) = (void* (*)(void*, const char*, bool))p_KeyValues_FindKey.GetPtr(); /*48 89 5C 24 10 48 89 6C 24 18 48 89 74 24 20 57 41 54 41 55 41 56 41 57 48 81 EC 20 01 00 00 45*/ +#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) + ADDRESS p_KeyValues_Init = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x53\x48\x83\xEC\x20\x48\x8B\x05\x00\x00\x00\x01\x48\x8B\xD9\x4C\x8B\xC2", "xxxxxxxxx???xxxxxxx"); /*40 53 48 83 EC 20 48 8B 05 ?? ?? ?? 01 48 8B D9 4C 8B C2*/ + void* (*KeyValues_Init)(std::int64_t a1, std::int64_t a2, std::int64_t a3, std::int64_t a4) = (void* (*)(std::int64_t, std::int64_t, std::int64_t, std::int64_t))p_KeyValues_Init.GetPtr(); + + ADDRESS p_KeyValues_FindKey = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x56\x57\x41\x57\x48\x81\xEC\x00\x00\x00\x00\x45", "xxxxxxxx????x"); + void* (*KeyValues_FindKey)(void* a1, const char* a2, bool a3) = (void* (*)(void*, const char*, bool))p_KeyValues_FindKey.GetPtr(); /*40 56 57 41 57 48 81 EC 30 01 00 00 45 0F B6 F8*/ +#endif + ADDRESS p_KeyValues_LoadPlaylist = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\xE8\x00\x00\x00\x00\x80\x3D\x00\x00\x00\x00\x00\x74\x0C", "x????xx?????xx").FollowNearCallSelf().GetPtr();; + bool (*KeyValues_LoadPlaylist)(const char* source) = (bool (*)(const char*))p_KeyValues_LoadPlaylist.GetPtr(); /*E8 ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 74 0C*/ + + ADDRESS p_KeyValues_GetMemPool = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x8B\x05\x00\x00\x00\x00\xC3\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\x48\x85\xD2", "xxx????xxxxxxxxxxxx"); + void* (*KeyValues_GetMemPool)() = (void* (*)())p_KeyValues_GetMemPool.GetPtr(); /*48 8B 05 ?? ?? ?? ?? C3 CC CC CC CC CC CC CC CC 48 85 D2*/ + + std::uintptr_t g_pKeyValuesMemPool = p_KeyValues_GetMemPool.ResolveRelativeAddressSelf(0x3, 0x7).GetPtr(); +} + +class CKeyValuesSystem // VTABLE @ 0x1413AA1E8 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM +{ +public: + + void RegisterSizeofKeyValues(std::int64_t size) //@0x1413AA1F0 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = void(__thiscall*)(CKeyValuesSystem*, std::int64_t); + (*reinterpret_cast(this))[0](this, size); + } + + void* AllocKeyValuesMemory(std::int64_t size) // @0x1413AA1F8 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = void* (__thiscall*)(CKeyValuesSystem*, std::int64_t); + return (*reinterpret_cast(this))[1](this, size); + } + + void FreeKeyValuesMemory(void* pMem) // @0x1413AA200 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = void(__thiscall*)(CKeyValuesSystem*, void*); + (*reinterpret_cast(this))[2](this, pMem); + } + + HKeySymbol GetSymbolForString(const char* name, bool bCreate) // @0x1413AA208 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = HKeySymbol(__thiscall*)(CKeyValuesSystem*, const char*, bool); + return (*reinterpret_cast(this))[3](this, name, bCreate); + } + + const char* GetStringForSymbol(HKeySymbol symbol) // @0x1413AA210 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = const char* (__thiscall*)(CKeyValuesSystem*, HKeySymbol); + return (*reinterpret_cast(this))[4](this, symbol); + } + + // void __fastcall CKeyValuesSystem::FreeKeyValuesMemory(CKeyValuesSystem* this_arg, void* ptr_mem_arg) + // { + // __int64* v2; // rax + // __int64 v4; // rax + // __int64* v5; // rax + // + // v2 = qword_14D40B538; + // if (!qword_14D40B538) + // { + // v2 = sub_140462930(); + // qword_14D40B538 = v2; + // } + // v4 = (*(*v2 + 48))(v2, ptr_mem_arg); + // if (v4 > 0) + // CKeyValuesSystem::m_pMemPool -= v4; + // v5 = qword_14D40B538; + // if (!qword_14D40B538) + // { + // v5 = sub_140462930(); + // qword_14D40B538 = v5; + // } + // (*(*v5 + 40))(v5, ptr_mem_arg); + // } + + // GetMemPool return a global variable called m_pMemPool it gets modified by AllocKeyValuesMemory and FreeKeyValuesMemory above you can see where the find it in FreeKeyValuesMemory. + void* GetMemPool() // @0x1413AA228 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + return reinterpret_cast(g_pKeyValuesMemPool); // May need to dereference this once more not sure right now. + } + + void SetKeyValuesExpressionSymbol(const char* name, bool bValue) // @0x1413AA230 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = void(__thiscall*)(CKeyValuesSystem*, const char*, bool); + (*reinterpret_cast(this))[8](this, name, bValue); + } + + bool GetKeyValuesExpressionSymbol(const char* name) // @0x1413AA238 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = bool(__thiscall*)(CKeyValuesSystem*, const char*); + return (*reinterpret_cast(this))[9](this, name); + } + + HKeySymbol GetSymbolForStringCaseSensitive(HKeySymbol& hCaseInsensitiveSymbol, const char* name, bool bCreate) // @0x1413AA240 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = HKeySymbol(__thiscall*)(CKeyValuesSystem*, HKeySymbol&, const char*, bool); + return (*reinterpret_cast(this))[10](this, hCaseInsensitiveSymbol, name, bCreate); + } + +// Datatypes aren't accurate. But full fill the actual byte distance. +public: + void* vtable; // 0x0000 + std::int64_t m_iMaxKeyValuesSize; // 0x0008 +private: + char gap10[240]; // 0x0010 +public: + int m_KvConditionalSymbolTable; // 0x0100 +private: + char gap104[4]; // 0x0104 +public: + std::int64_t field_108; // 0x0108 +private: + char gap110[32]; // 0x0110 +public: + int m_mutex; // 0x0130 +}; + +class KeyValues +{ +public: + + KeyValues* FindKey(const char* keyName, bool bCreate) + { + static auto func = reinterpret_cast(KeyValues_FindKey); + return func(this, keyName, bCreate); + } + + const char* GetName(); + + int GetInt(const char* keyName, int defaultValue) + { + KeyValues* dat = FindKey(keyName, false); + + if (!dat) + return defaultValue; + + switch (dat->m_iDataType) + { + case TYPE_STRING: + return atoi(dat->m_sValue); + case TYPE_FLOAT: + return static_cast(m_flValue()); + case TYPE_WSTRING: + return _wtoi(dat->m_wsValue); + case TYPE_UINT64: + return 0; + default: + return dat->m_iValue(); + } + + return defaultValue; + } + + void SetInt(const char* keyName, int iValue) + { + KeyValues* dat = FindKey(keyName, true); + if (dat) + { + dat->m_iValue() = iValue; + dat->m_iDataType = TYPE_INT; + } + } + + void SetFloat(const char* keyName, float flValue) + { + KeyValues* dat = FindKey(keyName, true); + if (dat) + { + dat->m_flValue() = flValue; + dat->m_iDataType = TYPE_FLOAT; + } + } + + // Compiler makes it so m_Value shares the offset spot with m_flValue that why we cast it like this. + float& m_flValue() + { + static std::int32_t offset = 0x18; + return *(float*)((std::uintptr_t)this + offset); + } + + int& m_iValue() + { + static std::int32_t offset = 0x18; + return *(int*)((std::uintptr_t)this + offset); + } + +public: + uint32_t m_iKeyName : 24; // 0x0000 + uint32_t m_iKeyNameCaseSensitive : 8; // 0x0003 + char* m_sValue; // 0x0008 + wchar_t* m_wsValue; // 0x0010 + int m_nValue; // 0x0018 +private: + char gap1C[12]; // 0x0020 +public: + char m_iDataType; // 0x0028 + uint16_t m_iKeyNameCaseSensitive2; // 0x002A + KeyValues* m_pPeer; // 0x0030 + KeyValues* m_pSub; // 0x0038 + KeyValues* m_pChain; // 0x0040 +}; + +/////////////////////////////////////////////////////////////////////////////// +void CKeyValueSystem_InitPlaylist(); +void CKeyValueSystem_Init(); +bool HKeyValues_LoadPlaylist(const char* playlist); + +void CKeyValueSystem_Attach(); +void CKeyValueSystem_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +extern CKeyValuesSystem* g_pKeyValuesSystem; +extern KeyValues** g_pPlaylistKeyValues; + +/////////////////////////////////////////////////////////////////////////////// +class HKeyValues : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: KeyValues::Init : 0x" << std::hex << std::uppercase << p_KeyValues_Init.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: KeyValues::FindKey : 0x" << std::hex << std::uppercase << p_KeyValues_FindKey.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: KeyValues::LoadPlaylist : 0x" << std::hex << std::uppercase << p_KeyValues_LoadPlaylist.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: KeyValues::GetMemPool : 0x" << std::hex << std::uppercase << p_KeyValues_GetMemPool.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: g_pKeyValuesMemPool : 0x" << std::hex << std::uppercase << g_pKeyValuesMemPool << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: g_pKeyValuesSystem : 0x" << std::hex << std::uppercase << g_pKeyValuesSystem << std::setw(0) << " |" << std::endl; + std::cout << "| VAR: g_pPlaylistKeyValues : 0x" << std::hex << std::uppercase << g_pPlaylistKeyValues << std::setw(0) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HKeyValues); diff --git a/r5dev/vphysics/QHull.cpp b/r5dev/vphysics/QHull.cpp new file mode 100644 index 00000000..b518c7ae --- /dev/null +++ b/r5dev/vphysics/QHull.cpp @@ -0,0 +1,28 @@ +#include "core/stdafx.h" +#include "vphysics/QHull.h" + +//----------------------------------------------------------------------------- +// Purpose: qhull error and debug prints +//----------------------------------------------------------------------------- +int HQHull_PrintError(char* fmt, va_list args) +{ + vprintf(fmt, args); + return QHull_PrintError(fmt, args); +} + +int HQHull_PrintDebug(char* fmt, va_list args) +{ + vprintf(fmt, args); + return QHull_PrintDebug(fmt, args); +} +void QHull_Attach() +{ + DetourAttach((LPVOID*)&QHull_PrintDebug, &HQHull_PrintDebug); + DetourAttach((LPVOID*)&QHull_PrintError, &HQHull_PrintError); +} + +void QHull_Detach() +{ + DetourDetach((LPVOID*)&QHull_PrintDebug, &HQHull_PrintDebug); + DetourDetach((LPVOID*)&QHull_PrintError, &HQHull_PrintError); +} diff --git a/r5dev/vphysics/QHull.h b/r5dev/vphysics/QHull.h new file mode 100644 index 00000000..79988035 --- /dev/null +++ b/r5dev/vphysics/QHull.h @@ -0,0 +1,31 @@ +#pragma once + +namespace +{ + ADDRESS p_QHull_PrintError = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x4C\x24\x08\x48\x89\x54\x24\x10\x4C\x89\x44\x24\x18\x4C\x89\x4C\x24\x20\x53\xB8\x40\x27\x00\x00\x00\x00\x00\x00\x00\x48", "xxxxxxxxxxxxxxxxxxxxxxxxxx????xx"); + int (*QHull_PrintError)(char* fmt, va_list args) = (int (*)(char*, va_list))p_QHull_PrintError.GetPtr(); /*48 89 4C 24 08 48 89 54 24 10 4C 89 44 24 18 4C 89 4C 24 20 53 B8 40 27 00 00 ?? ?? ?? ?? 00 48*/ + + ADDRESS p_QHull_PrintDebug = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x54\x24\x10\x4C\x89\x44\x24\x18\x4C\x89\x4C\x24\x20\x53\x56\x57\x48\x83\xEC\x30\x48\x8B\xFA\x48\x8D\x74\x24\x60\x48\x8B", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + int (*QHull_PrintDebug)(char* fmt, va_list args) = (int (*)(char*, va_list))p_QHull_PrintDebug.GetPtr(); /*48 89 54 24 10 4C 89 44 24 18 4C 89 4C 24 20 53 56 57 48 83 EC 30 48 8B FA 48 8D 74 24 60 48 8B*/ +} + +/////////////////////////////////////////////////////////////////////////////// +int HQHull_PrintError(char* fmt, va_list args); +int HQHull_PrintDebug(char* fmt, va_list args); + +void QHull_Attach(); +void QHull_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +class HQHull : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: QHull_PrintError : 0x" << std::hex << std::uppercase << p_QHull_PrintError.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: QHull_PrintDebug : 0x" << std::hex << std::uppercase << p_QHull_PrintDebug.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HQHull); diff --git a/r5dev/vpklib/packedstore.cpp b/r5dev/vpklib/packedstore.cpp new file mode 100644 index 00000000..9ec260dd --- /dev/null +++ b/r5dev/vpklib/packedstore.cpp @@ -0,0 +1,365 @@ +#include "core/stdafx.h" +#include "tier0/cvar.h" +#include "mathlib/adler32.h" +#include "mathlib/crc32.h" +#include "engine/sys_utils.h" +#include "vpklib/packedstore.h" + +/*********************************************************************** +* ██████╗ ██████╗ ██╗ ██╗██████╗ ██╗ ██╗ ██╗ ██╗██████╗ * +* ██╔══██╗╚════██╗ ██║ ██║██╔══██╗██║ ██╔╝ ██║ ██║██╔══██╗ * +* ██████╔╝ █████╔╝ ██║ ██║██████╔╝█████╔╝ ██║ ██║██████╔╝ * +* ██╔══██╗██╔═══╝ ╚██╗ ██╔╝██╔═══╝ ██╔═██╗ ██║ ██║██╔══██╗ * +* ██║ ██║███████╗ ╚████╔╝ ██║ ██║ ██╗ ███████╗██║██████╔╝ * +* ╚═╝ ╚═╝╚══════╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝╚═════╝ * +***********************************************************************/ + +//----------------------------------------------------------------------------- +// Purpose: initialize parameters for decompression algorithm +//----------------------------------------------------------------------------- +void CPackedStore::InitLzParams() +{ + /*| PARAMETERS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||*/ + m_lzDecompParams.m_dict_size_log2 = RVPK_DICT_SIZE; + m_lzDecompParams.m_decompress_flags = LZHAM_DECOMP_FLAG_OUTPUT_UNBUFFERED | LZHAM_DECOMP_FLAG_COMPUTE_CRC32; + m_lzDecompParams.m_struct_size = sizeof(lzham_decompress_params); +} + +//----------------------------------------------------------------------------- +// Purpose: obtains archive chunk path for specific file +//----------------------------------------------------------------------------- +std::string CPackedStore::GetPackChunkFile(std::string svPackDirFile, int iArchiveIndex) +{ + /*| ARCHIVES ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||*/ + std::string svPackChunkFile = StripLocalePrefix(svPackDirFile); + std::ostringstream oss; + + oss << std::setw(3) << std::setfill('0') << iArchiveIndex; + std::string svPackChunkIndex = "pak000_" + oss.str(); + + StringReplace(svPackChunkFile, "pak000_dir", svPackChunkIndex); + return svPackChunkFile; +} + +//----------------------------------------------------------------------------- +// Purpose: returns pupulated pack dir struct for specified pack dir file +//----------------------------------------------------------------------------- +vpk_dir_h CPackedStore::GetPackDirFile(std::string svPackDirFile) +{ + /*| PACKDIRFILE |||||||||||||||||||||||||||||||||||||||||||||||||||||||||*/ + std::regex rgArchiveRegex("pak000_([0-9]{3})"); + std::smatch smRegexMatches; + + std::regex_search(svPackDirFile, smRegexMatches, rgArchiveRegex); + + if (smRegexMatches.size() != 0) + { + StringReplace(svPackDirFile, smRegexMatches[0], "pak000_dir"); + + for (int i = 0; i < LANGUAGE_PACKS; i++) + { + if (strstr(svPackDirFile.c_str(), DIR_LIBRARY_PREFIX[i].c_str())) + { + for (int j = 0; j < LIBRARY_PACKS; j++) + { + if (strstr(svPackDirFile.c_str(), DIR_LIBRARY_PREFIX[j].c_str())) + { + std::string svPackDirPrefix = DIR_LOCALE_PREFIX[i] + DIR_LOCALE_PREFIX[i]; + StringReplace(svPackDirFile, DIR_LOCALE_PREFIX[i].c_str(), svPackDirPrefix.c_str()); + goto escape; + } + } + } + }escape:; + } + + vpk_dir_h vpk_dir(svPackDirFile); + return vpk_dir; +} + +//----------------------------------------------------------------------------- +// Purpose: obtains and returns the entry block to the vector +//----------------------------------------------------------------------------- +std::vector CPackedStore::GetEntryBlocks(CIOStream* reader) +{ + /*| ENTRYBLOCKS |||||||||||||||||||||||||||||||||||||||||||||||||||||||||*/ + std::string svName, svPath, svExtension; + std::vector vBlocks; + while (!(svExtension = reader->readString()).empty()) + { + while (!(svPath = reader->readString()).empty()) + { + while (!(svName = reader->readString()).empty()) + { + std::string svFilePath = FormatBlockPath(svName, svPath, svExtension); + vBlocks.push_back(vpk_entry_block(reader, svFilePath)); + } + } + } + return vBlocks; +} + +//----------------------------------------------------------------------------- +// Purpose: formats the entry block path +//----------------------------------------------------------------------------- +std::string CPackedStore::FormatBlockPath(std::string svName, std::string svPath, std::string svExtension) +{ + if (!svPath.empty()) + { + svPath += "\\"; + } + return svPath + svName + "." + svExtension; +} + +//----------------------------------------------------------------------------- +// Purpose: strips locale prefix from file path +//----------------------------------------------------------------------------- +std::string CPackedStore::StripLocalePrefix(std::string svPackDirFile) +{ + std::filesystem::path fspPackDirFile(svPackDirFile); + std::string svFileName = fspPackDirFile.filename().u8string(); + + for (int i = 0; i < LANGUAGE_PACKS; i++) + { + if (strstr(svFileName.c_str(), DIR_LOCALE_PREFIX[i].c_str())) + { + StringReplace(svFileName, DIR_LOCALE_PREFIX[i].c_str(), ""); + goto escape; + } + }escape:; + return svFileName; +} + +//----------------------------------------------------------------------------- +// Purpose: validates extraction result with precomputed ADLER32 hash +//----------------------------------------------------------------------------- +void CPackedStore::ValidateAdler32PostDecomp(std::string svAssetFile) +{ + uint32_t adler_init = {}; + std::ifstream istream(svAssetFile, std::fstream::binary); + + istream.seekg(0, std::fstream::end); + m_vHashBuffer.resize(istream.tellg()); + istream.seekg(0, std::fstream::beg); + istream.read((char*)m_vHashBuffer.data(), m_vHashBuffer.size()); + + m_nAdler32 = adler32::update(adler_init, m_vHashBuffer.data(), m_vHashBuffer.size()); + + if (m_nAdler32 != m_nAdler32_Internal) + { + DevMsg(eDLL_T::FS, "Warning: ADLER32 checksum mismatch for entry '%s' computed value '0x%lX' doesn't match expected value '0x%lX'. File may be corrupt!\n", svAssetFile.c_str(), m_nAdler32, m_nAdler32_Internal); + m_nAdler32 = 0; + m_nAdler32_Internal = 0; + } + + istream.close(); + m_vHashBuffer.clear(); +} + +//----------------------------------------------------------------------------- +// Purpose: validates extraction result with precomputed CRC32 hash +//----------------------------------------------------------------------------- +void CPackedStore::ValidateCRC32PostDecomp(std::string svDirAsset) +{ + uint32_t crc32_init = {}; + std::ifstream istream(svDirAsset, std::fstream::binary); + + istream.seekg(0, std::fstream::end); + m_vHashBuffer.resize(istream.tellg()); + istream.seekg(0, std::fstream::beg); + istream.read((char*)m_vHashBuffer.data(), m_vHashBuffer.size()); + + m_nCrc32 = crc32::update(crc32_init, m_vHashBuffer.data(), m_vHashBuffer.size()); + + if (m_nCrc32 != m_nCrc32_Internal) + { + DevMsg(eDLL_T::FS, "Warning: CRC32 checksum mismatch for entry '%s' computed value '0x%lX' doesn't match expected value '0x%lX'. File may be corrupt!\n", svDirAsset.c_str(), m_nCrc32, m_nCrc32_Internal); + m_nCrc32 = 0; + m_nCrc32_Internal = 0; + } + + istream.close(); + m_vHashBuffer.clear(); +} + +//----------------------------------------------------------------------------- +// Purpose: extracts all files from specified vpk file +//----------------------------------------------------------------------------- +void CPackedStore::UnpackAll(vpk_dir_h vpk_dir, std::string svPathOut) +{ + for (int i = 0; i < vpk_dir.m_vsvArchives.size(); i++) + { + std::filesystem::path fspVpkPath(vpk_dir.m_svDirPath); + std::string svPath = fspVpkPath.parent_path().u8string() + "\\" + vpk_dir.m_vsvArchives[i]; + std::ifstream packChunkStream(svPath, std::ios_base::binary); // Create stream to read from each archive. + + for ( vpk_entry_block block : vpk_dir.m_vvEntryBlocks) + { + if (block.m_iArchiveIndex != i) + { + // Continue if block archive index is not part of the extracting archive chunk index. + goto cont; + } + else + { + std::string svFilePath = CreateDirectories(svPathOut + "\\" + block.m_svBlockPath); + std::ofstream outFileStream(svFilePath, std::ios_base::binary | std::ios_base::out); + + if (!outFileStream.is_open()) + { + DevMsg(eDLL_T::FS, "Error: unable to access file '%s'!\n", svFilePath.c_str()); + } + outFileStream.clear(); // Make sure file is empty before writing. + for (vpk_entry_h entry : block.m_vvEntries) + { + char* pCompressedData = new char[entry.m_nCompressedSize]; + memset(pCompressedData, 0, entry.m_nCompressedSize); // Compressed region. + + packChunkStream.seekg(entry.m_nArchiveOffset); // Seek to entry offset in archive. + packChunkStream.read(pCompressedData, entry.m_nCompressedSize); // Read compressed data from archive. + + if (entry.m_bIsCompressed) + { + lzham_uint8* pLzOutputBuf = new lzham_uint8[entry.m_nUncompressedSize]; + m_lzDecompStatus = lzham_decompress_memory(&m_lzDecompParams, pLzOutputBuf, (size_t*)&entry.m_nUncompressedSize, (lzham_uint8*)pCompressedData, entry.m_nCompressedSize, &m_nAdler32_Internal, &m_nCrc32_Internal); + + if (fs_packedstore_entryblock_stats->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::FS, "--------------------------------------------------------------\n"); + DevMsg(eDLL_T::FS, "] Block path : '%s'\n", block.m_svBlockPath.c_str()); + DevMsg(eDLL_T::FS, "] Entry count : '%llu'\n", block.m_vvEntries.size()); + DevMsg(eDLL_T::FS, "] Compressed size : '%llu'\n", entry.m_nCompressedSize); + DevMsg(eDLL_T::FS, "] Uncompressed size : '%llu'\n", entry.m_nUncompressedSize); + DevMsg(eDLL_T::FS, "] Static CRC32 hash : '0x%lX'\n", block.m_nCrc32); + DevMsg(eDLL_T::FS, "] Computed CRC32 hash : '0x%lX'\n", m_nCrc32_Internal); + DevMsg(eDLL_T::FS, "] Computed ADLER32 hash : '0x%lX'\n", m_nAdler32_Internal); + DevMsg(eDLL_T::FS, "--------------------------------------------------------------\n"); + } + + if (block.m_vvEntries.size() == 1) // Internal checksum can only match block checksum if entry size is 1. + { + if (block.m_nCrc32 != m_nCrc32_Internal) + { + DevMsg(eDLL_T::FS, "Warning: CRC32 checksum mismatch for entry '%s' computed value '0x%lX' doesn't match expected value '0x%lX'. File may be corrupt!\n", block.m_svBlockPath.c_str(), m_nCrc32_Internal, block.m_nCrc32); + } + } + else { m_nEntryCount++; } + + if (m_lzDecompStatus != lzham_decompress_status_t::LZHAM_DECOMP_STATUS_SUCCESS) + { + DevMsg(eDLL_T::FS, "Error: failed decompression for an entry within block '%s' in archive '%d'!\n", block.m_svBlockPath.c_str(), i); + DevMsg(eDLL_T::FS, "'lzham_decompress_memory_func' returned with status '%d'.\n", m_lzDecompStatus); + } + else + { + // If successfully decompressed, write to file. + outFileStream.write((char*)pLzOutputBuf, entry.m_nUncompressedSize); + delete[] pLzOutputBuf, pCompressedData; + } + } + else + { + // If not compressed, write raw data into output file. + outFileStream.write(pCompressedData, entry.m_nUncompressedSize); + } + } + outFileStream.close(); + if (m_nEntryCount == block.m_vvEntries.size()) // Only validate after last entry in block had been written. + { + // Set internal hash to precomputed entry hash for post decompress validation. + m_nCrc32_Internal = block.m_nCrc32; + + ValidateCRC32PostDecomp(svFilePath); + //ValidateAdler32PostDecomp(svFilePath); + m_nEntryCount = 0; + } + }cont:; + } + packChunkStream.close(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: 'vpk_entry_block' constructor +//----------------------------------------------------------------------------- +vpk_entry_block::vpk_entry_block(CIOStream* reader, std::string svPath) +{ + std::replace(svPath.begin(), svPath.end(), '/', '\\'); // Flip forward slashes in filepath to windows-style backslash. + + this->m_svBlockPath = svPath; // Set path of block. + reader->read(this->m_nCrc32); // + reader->read(this->m_nPreloadBytes); // + reader->read(this->m_iArchiveIndex); // + + do // Loop through all entries in the block and push them to the vector. + { + vpk_entry_h entry(reader); + this->m_vvEntries.push_back(entry); + } while (reader->readR() != 65535); +} + +//----------------------------------------------------------------------------- +// Purpose: 'vpk_entry_h' constructor +//----------------------------------------------------------------------------- +vpk_entry_h::vpk_entry_h(CIOStream* reader) +{ + reader->read(this->m_nEntryFlags); // + reader->read(this->m_nTextureFlags); // + reader->read(this->m_nArchiveOffset); // + reader->read(this->m_nCompressedSize); // + reader->read(this->m_nUncompressedSize); // + this->m_bIsCompressed = (this->m_nCompressedSize != this->m_nUncompressedSize); +} + +//----------------------------------------------------------------------------- +// Purpose: 'vpk_dir_h' constructor +//----------------------------------------------------------------------------- +vpk_dir_h::vpk_dir_h(std::string svPath) +{ + CIOStream reader; + + reader.open(svPath, eStreamFileMode::READ); + reader.read(this->m_nFileMagic); + + if (this->m_nFileMagic != RVPK_DIR_MAGIC) + { + DevMsg(eDLL_T::FS, "Error: vpk_dir file '%s' has invalid magic!\n", svPath.c_str()); + return; + } + + reader.read(this->m_nMajorVersion); // + reader.read(this->m_nMinorVersion); // + reader.read(this->m_nTreeSize); // + reader.read(this->m_nFileDataSize); // + + DevMsg(eDLL_T::FS, "______________________________________________________________\n"); + DevMsg(eDLL_T::FS, "] HEADER_DETAILS ---------------------------------------------\n"); + DevMsg(eDLL_T::FS, "] File Magic : '%lu'\n", this->m_nFileMagic); + DevMsg(eDLL_T::FS, "] Major Version : '%hu'\n", (this->m_nMajorVersion)); + DevMsg(eDLL_T::FS, "] Minor Version : '%hu'\n", (this->m_nMinorVersion)); + DevMsg(eDLL_T::FS, "] Tree Size : '%lu'\n", this->m_nTreeSize); + DevMsg(eDLL_T::FS, "] File Data Size : '%lu'\n", this->m_nFileDataSize); + + this->m_vvEntryBlocks = g_pPackedStore->GetEntryBlocks(&reader); + this->m_svDirPath = svPath; // Set path to vpk_dir file. + + for (vpk_entry_block block : this->m_vvEntryBlocks) + { + if (block.m_iArchiveIndex > this->m_iArchiveCount) + { + this->m_iArchiveCount = block.m_iArchiveIndex; + } + } + + DevMsg(eDLL_T::FS, "______________________________________________________________\n"); + DevMsg(eDLL_T::FS, "] PACK_CHUNKS ------------------------------------------------\n"); + + for (int i = 0; i < this->m_iArchiveCount + 1; i++) + { + std::string svArchivePath = g_pPackedStore->GetPackChunkFile(svPath, i); + DevMsg(eDLL_T::FS, "] '%s\n", svArchivePath.c_str()); + this->m_vsvArchives.push_back(svArchivePath); + } +} +/////////////////////////////////////////////////////////////////////////////// +CPackedStore* g_pPackedStore = new CPackedStore(); diff --git a/r5dev/vpklib/packedstore.h b/r5dev/vpklib/packedstore.h new file mode 100644 index 00000000..977912bd --- /dev/null +++ b/r5dev/vpklib/packedstore.h @@ -0,0 +1,74 @@ +#pragma once +#include "public/include/binstream.h" +#include "thirdparty/lzham/include/lzham.h" + +constexpr unsigned int LIBRARY_PACKS = 2; +constexpr unsigned int LANGUAGE_PACKS = 11; +constexpr unsigned int RVPK_DICT_SIZE = 20; +constexpr unsigned int RVPK_DIR_MAGIC = 'U4'; + +const std::string DIR_LIBRARY_PREFIX[LIBRARY_PACKS] = { "server", "client" }; +const std::string DIR_LOCALE_PREFIX[LANGUAGE_PACKS] = { "english", "french", "german", "italian", "japanese", "korean", "polish", "portuguese", "russian", "spanish", "tchinese" }; + +struct vpk_entry_h +{ + uint32_t m_nEntryFlags {}; // Entry flags. + uint16_t m_nTextureFlags {}; // Texture flags (only used if the entry is a vtf). + uint64_t m_nArchiveOffset {}; // Offset in archive. + uint64_t m_nCompressedSize {}; // Compressed size of entry. + uint64_t m_nUncompressedSize{}; // Uncompressed size of entry. + bool m_bIsCompressed = false; + + vpk_entry_h(CIOStream* reader); +}; + +struct vpk_entry_block +{ + uint32_t m_nCrc32 {}; // Crc32 for the uncompressed block. + uint16_t m_nPreloadBytes{}; // Preload bytes. + uint16_t m_iArchiveIndex{}; // Index of the archive that contains this block. + std::vector m_vvEntries {}; // Vector of all the entries of a given block (entries have a size limit of 1 MiB, so anything over is split into separate entries within the same block). + std::string m_svBlockPath {}; // Path to block within vpk. + + vpk_entry_block(CIOStream* reader, std::string path); +}; + +struct vpk_dir_h +{ + uint32_t m_nFileMagic {}; // File magic. + uint16_t m_nMajorVersion{}; // Vpk major version. + uint16_t m_nMinorVersion{}; // Vpk minor version. + uint32_t m_nTreeSize {}; // Directory tree size. + uint32_t m_nFileDataSize{}; // File data section size. + std::vector m_vvEntryBlocks{}; // Vector of entry blocks. + uint16_t m_iArchiveCount{}; // Highest archive index (archive count-1). + std::vector m_vsvArchives {}; // Vector of archive file names. + std::string m_svDirPath {}; // Path to vpk_dir file. + + vpk_dir_h(std::string path); +}; + +class CPackedStore +{ + std::vector m_vHashBuffer {}; // Buffer for post decomp file validation. + std::size_t m_nEntryCount {}; // Entry per-block incrementor. + lzham_uint32 m_nAdler32_Internal{}; // Internal operation Adler32 file checksum. + lzham_uint32 m_nAdler32 {}; // Pre/post operation Adler32 file checksum. + lzham_uint32 m_nCrc32_Internal {}; // Internal operation Crc32 file checksum. + lzham_uint32 m_nCrc32 {}; // Pre/post operation Crc32 file checksum. + lzham_decompress_params m_lzDecompParams {}; // LZham decompression parameters. + lzham_decompress_status_t m_lzDecompStatus {}; // LZham decompression results. + +public: + void InitLzParams(); + vpk_dir_h GetPackDirFile(std::string svPackDirFile); + std::string GetPackChunkFile(std::string svPackDirFile, int iArchiveIndex); + std::vector GetEntryBlocks(CIOStream* reader); + std::string FormatBlockPath(std::string svName, std::string svPath, std::string svExtension); + std::string StripLocalePrefix(std::string svPackDirFile); + void UnpackAll(vpk_dir_h vpk, std::string svPathOut = ""); + void ValidateAdler32PostDecomp(std::string svDirAsset); + void ValidateCRC32PostDecomp(std::string svDirAsset); +}; +/////////////////////////////////////////////////////////////////////////////// +extern CPackedStore* g_pPackedStore; diff --git a/r5dev/src/console.cpp b/r5dev/windows/console.cpp similarity index 62% rename from r5dev/src/console.cpp rename to r5dev/windows/console.cpp index 7ee85e3a..96927d05 100644 --- a/r5dev/src/console.cpp +++ b/r5dev/windows/console.cpp @@ -1,63 +1,22 @@ -#include "pch.h" -#include "id3dx.h" -#include "hooks.h" -#include "opcptc.h" -#include "console.h" -#include "patterns.h" - -//############################################################################# -// WORKER THREAD -//############################################################################# - -DWORD __stdcall ProcessConsoleWorker(LPVOID) -{ - while (true) // Loop forever - { - std::string sCommand = std::string(); - - /////////////////////////////////////////////////////////////////////// - // Get the user input on the debug console - std::getline(std::cin, sCommand); - - /////////////////////////////////////////////////////////////////////// - // Engine toggles - if (sCommand == "toggle net") { Hooks::ToggleNetTrace(); continue; } - if (sCommand == "toggle dev") { Hooks::ToggleDevCommands(); continue; } - if (sCommand == "toggle fal") { g_bReturnAllFalse = !g_bReturnAllFalse; continue; } - - /////////////////////////////////////////////////////////////////////// - // Debug toggles - if (sCommand == "pattern test") { PrintHAddress(); PrintOAddress(); continue; } - if (sCommand == "directx test") { PrintDXAddress(); continue; } - if (sCommand == "console test") { g_bDebugConsole = !g_bDebugConsole; continue; } - - /////////////////////////////////////////////////////////////////////// - // Exec toggles - if (sCommand == "1") { Hooks::ToggleDevCommands(); addr_CommandExecute(NULL, "exec autoexec_dev"); } - if (sCommand == "2") { g_bDebugLoading = !g_bDebugLoading; continue; } - - /////////////////////////////////////////////////////////////////////// - // Execute the command in the r5 SQVM - addr_CommandExecute(NULL, sCommand.c_str()); - sCommand.clear(); - - /////////////////////////////////////////////////////////////////////// - // Sleep and loop - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - } - - return 0; -} +#include "core/stdafx.h" +#include "core/init.h" +#include "rtech/rtech.h" +#ifndef DEDICATED +#include "windows/id3dx.h" +#endif // !DEDICATED +#include "windows/console.h" +#include "client/IVEngineClient.h" +#include "common/opcodes.h" //############################################################################# // INITIALIZATION //############################################################################# -void SetupConsole() +void Console_Init() { /////////////////////////////////////////////////////////////////////////// // Create the console window - if (!AllocConsole()) + if (AllocConsole() == FALSE) { OutputDebugString("Failed to create console window!\n"); return; @@ -69,6 +28,7 @@ void SetupConsole() CHAR sBuildBuf[1024] = { 0 }; fopen_s(&sBuildTxt, "build.txt", "r"); + if (sBuildTxt) { while (fgets(sBuildBuf, sizeof(sBuildBuf), sBuildTxt) != NULL) @@ -87,24 +47,53 @@ void SetupConsole() /////////////////////////////////////////////////////////////////////////// // Create a worker thread to process console commands - DWORD threadID = NULL; - HANDLE hThread = CreateThread(NULL, 0, ProcessConsoleWorker, NULL, 0, &threadID); + DWORD threadId; + DWORD __stdcall ProcessConsoleWorker(LPVOID); + HANDLE hThread = CreateThread(NULL, 0, ProcessConsoleWorker, NULL, 0, &threadId); // Initialize global spdlog. auto console = spdlog::stdout_logger_mt("console"); - console->set_pattern("[%I:%M:%S:%e] [%L] %v"); // Set pattern. + console->set_pattern("[%S.%e] %v"); // Set pattern. + + spdlog::set_level(spdlog::level::trace); spdlog::set_default_logger(console); // Set as default. spdlog::flush_every(std::chrono::seconds(5)); // Flush buffers every 5 seconds for every logger. - -#ifdef _DEBUG - console->set_level(spdlog::level::debug); -#endif - - spdlog::debug("Console and spdlog are setup now!\n"); - + if (hThread) { - spdlog::info("THREAD ID: {}\n\n", threadID); + spdlog::debug("THREAD ID: {}\n\n", threadId); CloseHandle(hThread); } -} \ No newline at end of file +} + +//############################################################################# +// WORKER THREAD +//############################################################################# + +DWORD __stdcall ProcessConsoleWorker(LPVOID) +{ + // Loop forever + while (true) + { + static std::string sCommand = ""; + + /////////////////////////////////////////////////////////////////////// + // Get the user input on the debug console + printf(">"); + std::getline(std::cin, sCommand); + + /////////////////////////////////////////////////////////////////////// + // Debug toggles + if (sCommand == "pattern test") { PrintHAddress(); PrintOAddress(); continue; } + if (sCommand == "opcodes test") { RuntimePtc_Toggle(); continue; } + /////////////////////////////////////////////////////////////////////// + // Execute the command in the r5 SQVM + IVEngineClient_CommandExecute(NULL, sCommand.c_str()); + sCommand.clear(); + + /////////////////////////////////////////////////////////////////////// + // Sleep and loop + Sleep(50); + } + return NULL; +} diff --git a/r5dev/windows/console.h b/r5dev/windows/console.h new file mode 100644 index 00000000..d96a8035 --- /dev/null +++ b/r5dev/windows/console.h @@ -0,0 +1,3 @@ +#pragma once + +void Console_Init(); diff --git a/r5dev/windows/id3dx.cpp b/r5dev/windows/id3dx.cpp new file mode 100644 index 00000000..5d30e2c8 --- /dev/null +++ b/r5dev/windows/id3dx.cpp @@ -0,0 +1,563 @@ +#include "core/stdafx.h" +#ifndef DEDICATED // This file should not be compiled for DEDICATED! +//------------------------------ +#define STB_IMAGE_IMPLEMENTATION +#include "tier0/cvar.h" +#include "windows/id3dx.h" +#include "windows/input.h" +#include "gameui/IConsole.h" +#include "gameui/IBrowser.h" +#include "engine/sys_utils.h" +#include "inputsystem/inputsystem.h" +#include "public/include/stb_image.h" + +/********************************************************************************** +----------------------------------------------------------------------------------- +File : id3dx.cpp +Date : 15:06:2021 +Author : Kawe Mazidjatari +Purpose: Microsoft DirectX 11 'IDXGISwapChain::Present' hook implementation +----------------------------------------------------------------------------------- +History: +- 15:06:2021 | 14:56 : Created by Kawe Mazidjatari +- 17:06:2021 | 13:12 : Destroy / release objects with 'GetResizeBuffers' callback + +**********************************************************************************/ + +/////////////////////////////////////////////////////////////////////////////////// +typedef BOOL(WINAPI* IPostMessageA)(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); +typedef BOOL(WINAPI* IPostMessageW)(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); + +/////////////////////////////////////////////////////////////////////////////////// +extern BOOL g_bShowConsole = false; +extern BOOL g_bShowBrowser = false; +static BOOL g_bInitMenu = false; +static BOOL g_bInitialized = false; +static BOOL g_bPresentHooked = false; + +/////////////////////////////////////////////////////////////////////////////////// +static WNDPROC g_oWndProc = NULL; +static HWND g_hGameWindow = NULL; +extern DWORD g_dThreadId = NULL; + +/////////////////////////////////////////////////////////////////////////////////// +static IPostMessageA g_oPostMessageA = NULL; +static IPostMessageW g_oPostMessageW = NULL; + +/////////////////////////////////////////////////////////////////////////////////// +static IDXGIResizeBuffers g_oResizeBuffers = NULL; +static IDXGISwapChainPresent g_fnIDXGISwapChainPresent = NULL; +static IDXGISwapChain* g_pSwapChain = nullptr; +static ID3D11DeviceContext* g_pDeviceContext = nullptr; +static ID3D11Device* g_pDevice = nullptr; +static ID3D11RenderTargetView* g_pRenderTargetView = nullptr; +static ID3D11DepthStencilView* g_pDepthStencilView = nullptr; + +//################################################################################# +// WINDOW PROCEDURE +//################################################################################# + +LRESULT CALLBACK DXGIMsgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + +LRESULT CALLBACK HwndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN) + { + if (wParam == g_pImGuiConfig->IConsole_Config.m_nBind0 || wParam == g_pImGuiConfig->IConsole_Config.m_nBind1) + { + g_bShowConsole = !g_bShowConsole; + } + + if (wParam == g_pImGuiConfig->IBrowser_Config.m_nBind0 || wParam == g_pImGuiConfig->IBrowser_Config.m_nBind1) + { + g_bShowBrowser = !g_bShowBrowser; + } + } + + if (g_bShowConsole || g_bShowBrowser) + {////////////////////////////////////////////////////////////////////////////// + ImGui_ImplWin32_WndProcHandler(hWnd, uMsg, wParam, lParam); + g_bBlockInput = true; + + switch (uMsg) + { + case WM_LBUTTONDOWN: + return 1L; + case WM_LBUTTONUP: + return 1L; + case WM_LBUTTONDBLCLK: + return 1L; + case WM_RBUTTONDOWN: + return 1L; + case WM_RBUTTONUP: + return 1L; + case WM_RBUTTONDBLCLK: + return 1L; + case WM_MBUTTONDOWN: + return 1L; + case WM_MBUTTONUP: + return 1L; + case WM_MBUTTONDBLCLK: + return 1L; + case WM_KEYDOWN: + return 1L; + case WM_KEYUP: + return 1L; + case WM_MOUSEACTIVATE: + return 1L; + case WM_MOUSEHOVER: + return 1L; + case WM_MOUSEHWHEEL: + return 1L; + case WM_MOUSELEAVE: + return 1L; + case WM_MOUSEMOVE: + return 1L; + case WM_MOUSEWHEEL: + return 1L; + case WM_SETCURSOR: + return 1L; + default: + break; + } + }////////////////////////////////////////////////////////////////////////////// + else + { + g_bBlockInput = false; + } + + /////////////////////////////////////////////////////////////////////////////// + return CallWindowProc(g_oWndProc, hWnd, uMsg, wParam, lParam); +} + +//################################################################################# +// POST MESSAGE +//################################################################################# + +BOOL WINAPI HPostMessageA(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) +{ + if (g_bBlockInput && Msg == WM_MOUSEMOVE) + { + return TRUE; + } + + return g_oPostMessageA(hWnd, Msg, wParam, lParam); +} + +BOOL WINAPI HPostMessageW(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) +{ + if (g_bBlockInput && Msg == WM_MOUSEMOVE) + { + return TRUE; + } + + return g_oPostMessageW(hWnd, Msg, wParam, lParam); +} + +//################################################################################# +// IDXGI PRESENT +//################################################################################# + +void GetPresent() +{ + WNDCLASSEXA wc = { sizeof(WNDCLASSEX), CS_CLASSDC, DXGIMsgProc, 0L, 0L, GetModuleHandleA(NULL), NULL, NULL, NULL, NULL, "DX", NULL }; + RegisterClassExA(&wc); + + HWND hWnd = CreateWindowA("DX", NULL, WS_OVERLAPPEDWINDOW, 100, 100, 300, 300, NULL, NULL, wc.hInstance, NULL); + DXGI_SWAP_CHAIN_DESC sd = { 0 }; + D3D_FEATURE_LEVEL nFeatureLevelsSet = D3D_FEATURE_LEVEL_11_0; + D3D_FEATURE_LEVEL nFeatureLevelsSupported; + + ZeroMemory(&sd, sizeof(sd)); + + /////////////////////////////////////////////////////////////////////////////// + sd.BufferCount = 1; + sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + sd.BufferDesc.Height = 800; + sd.BufferDesc.Width = 600; + sd.BufferDesc.RefreshRate = { 60, 1 }; + sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; + sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; + sd.Windowed = TRUE; + sd.OutputWindow = hWnd; + sd.SampleDesc.Count = 1; + sd.SampleDesc.Quality = 0; + sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; + + /////////////////////////////////////////////////////////////////////////////// + g_hGameWindow = sd.OutputWindow; + UINT nFeatureLevelsRequested = 1; + HRESULT hr = 0; + IDXGISwapChain* pSwapChain = nullptr; + ID3D11Device* pDevice = nullptr; + ID3D11DeviceContext* pContext = nullptr; + + /////////////////////////////////////////////////////////////////////////////// + if (FAILED(hr = D3D11CreateDeviceAndSwapChain(NULL, + D3D_DRIVER_TYPE_HARDWARE, + NULL, + NULL, + &nFeatureLevelsSet, + nFeatureLevelsRequested, + D3D11_SDK_VERSION, + &sd, + &pSwapChain, + &pDevice, + &nFeatureLevelsSupported, + &pContext))) + { + if (mat_showdxoutput->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::MS, "+--------------------------------------------------------+\n"); + DevMsg(eDLL_T::MS, "| >>>>>>>>>| VIRTUAL METHOD TABLE HOOK FAILED |<<<<<<<<< |\n"); + DevMsg(eDLL_T::MS, "+--------------------------------------------------------+\n"); + } + DirectX_Shutdown(); + return; + } + + /////////////////////////////////////////////////////////////////////////////// + DWORD_PTR* pSwapChainVtable = nullptr; + DWORD_PTR* pContextVTable = nullptr; + DWORD_PTR* pDeviceVTable = nullptr; + + pSwapChainVtable = (DWORD_PTR*)pSwapChain; + pSwapChainVtable = (DWORD_PTR*)pSwapChainVtable[0]; + pContextVTable = (DWORD_PTR*)pContext; + pContextVTable = (DWORD_PTR*)pContextVTable[0]; + pDeviceVTable = (DWORD_PTR*)pDevice; + pDeviceVTable = (DWORD_PTR*)pDeviceVTable[0]; + + int pIDX = (int)DXGISwapChainVTbl::Present; + int rIDX = (int)DXGISwapChainVTbl::ResizeBuffers; + + g_fnIDXGISwapChainPresent = (IDXGISwapChainPresent)(DWORD_PTR)pSwapChainVtable[pIDX]; + g_oResizeBuffers = (IDXGIResizeBuffers)(DWORD_PTR)pSwapChainVtable[rIDX]; + + pSwapChain->Release(); + pContext->Release(); + pDevice->Release(); + + /////////////////////////////////////////////////////////////////////////////// + g_bPresentHooked = true; +} + +//################################################################################# +// INITIALIZATION +//################################################################################# + +void SetupImGui() +{ + /////////////////////////////////////////////////////////////////////////////// + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGui_ImplWin32_Init(g_hGameWindow); + ImGui_ImplDX11_Init(g_pDevice, g_pDeviceContext); + ImGui::GetIO().ImeWindowHandle = g_hGameWindow; + + /////////////////////////////////////////////////////////////////////////////// + ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_IsSRGB; +} + +void DrawImGui() +{ + bool bShowConsole = g_bShowConsole; + bool bShowBrowser = g_bShowBrowser; + + ImGui_ImplDX11_NewFrame(); + ImGui_ImplWin32_NewFrame(); + + ImGui::NewFrame(); + + if (g_bShowConsole) + { + g_pInputSystem->EnableInput(false); // Disable input to game when console is drawn. + DrawConsole(&bShowConsole); + } + if (g_bShowBrowser) + { + g_pInputSystem->EnableInput(false); // Disable input to game when browser is drawn. + DrawBrowser(&bShowBrowser); + } + if (!g_bShowConsole && !g_bShowBrowser) + { + g_pInputSystem->EnableInput(true); // Enable input to game when both are not drawn. + } + + ImGui::EndFrame(); + ImGui::Render(); + + g_pDeviceContext->OMSetRenderTargets(1, &g_pRenderTargetView, NULL); + ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); +} + +void CreateRenderTarget(IDXGISwapChain* pSwapChain) +{ + /////////////////////////////////////////////////////////////////////////////// + DXGI_SWAP_CHAIN_DESC sd {}; + D3D11_RENDER_TARGET_VIEW_DESC rd {}; + ID3D11Texture2D* pBackBuffer = nullptr; + + /////////////////////////////////////////////////////////////////////////////// + pSwapChain->GetDesc(&sd); + ZeroMemory(&rd, sizeof(rd)); + + g_hGameWindow = sd.OutputWindow; + rd.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + rd.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + + /////////////////////////////////////////////////////////////////////////////// + pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer); + if (pBackBuffer != NULL) { g_pDevice->CreateRenderTargetView(pBackBuffer, &rd, &g_pRenderTargetView); } + g_pDeviceContext->OMSetRenderTargets(1, &g_pRenderTargetView, NULL); + pBackBuffer->Release(); +} + +void CreateViewPort( UINT nWidth, UINT nHeight) +{ + float width = *(float*)(&nWidth); + float height = *(float*)(&nHeight); + + D3D11_VIEWPORT vp{}; + + /////////////////////////////////////////////////////////////////////////////// + vp.Width = width; + vp.Height = height; + vp.MinDepth = 0.0f; + vp.MaxDepth = 1.0f; + vp.TopLeftX = 0; + vp.TopLeftY = 0; + + /////////////////////////////////////////////////////////////////////////////// + g_pDeviceContext->RSSetViewports(1, &vp); +} + +void DestroyRenderTarget() +{ + if (g_pRenderTargetView != nullptr) + { + g_pRenderTargetView->Release(); + g_pRenderTargetView = nullptr; + g_pDeviceContext->OMSetRenderTargets(0, 0, 0); + + if (mat_showdxoutput->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::MS, "+--------------------------------------------------------+\n"); + DevMsg(eDLL_T::MS, "| >>>>>>>>>>>>>>| RENDER TARGET DESTROYED |<<<<<<<<<<<<< |\n"); + DevMsg(eDLL_T::MS, "+--------------------------------------------------------+\n"); + } + } +} + +//################################################################################# +// INTERNALS +//################################################################################# + +HRESULT GetDeviceAndCtxFromSwapchain(IDXGISwapChain* pSwapChain, ID3D11Device** ppDevice, ID3D11DeviceContext** ppContext) +{ + HRESULT ret = pSwapChain->GetDevice(__uuidof(ID3D11Device), (PVOID*)ppDevice); + if (SUCCEEDED(ret)) + { + (*ppDevice)->GetImmediateContext(ppContext); + } + return ret; +} + +HRESULT __stdcall GetResizeBuffers(IDXGISwapChain* pSwapChain, UINT nBufferCount, UINT nWidth, UINT nHeight, DXGI_FORMAT dxFormat, UINT nSwapChainFlags) +{ + g_bShowConsole = false; + g_bShowBrowser = false; + g_bInitialized = false; + g_bPresentHooked = false; + + /////////////////////////////////////////////////////////////////////////////// + DestroyRenderTarget(); + + /////////////////////////////////////////////////////////////////////////////// + return g_oResizeBuffers(pSwapChain, nBufferCount, nWidth, nHeight, dxFormat, nSwapChainFlags); +} + +HRESULT __stdcall Present(IDXGISwapChain* pSwapChain, UINT nSyncInterval, UINT nFlags) +{ + if (!g_bInitialized) + { + if (FAILED(GetDeviceAndCtxFromSwapchain(pSwapChain, &g_pDevice, &g_pDeviceContext))) + { + if (mat_showdxoutput->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::MS, "+--------------------------------------------------------+\n"); + DevMsg(eDLL_T::MS, "| >>>>>>>>>>| GET DVS AND CTX FROM SCP FAILED |<<<<<<<<< |\n"); + DevMsg(eDLL_T::MS, "+--------------------------------------------------------+\n"); + } + return g_fnIDXGISwapChainPresent(pSwapChain, nSyncInterval, nFlags); + } + + CreateRenderTarget(pSwapChain); + SetupImGui(); + + if (g_oWndProc == nullptr) + { // Only initialize HwndProc pointer once to avoid stack overflow during ResizeBuffers(..) + g_oWndProc = (WNDPROC)SetWindowLongPtr(g_hGameWindow, GWLP_WNDPROC, (LONG_PTR)HwndProc); + } + + g_bInitialized = true; + g_pSwapChain = pSwapChain; + g_pImGuiConfig->Load(); // Load ImGui configs. + } + + DrawImGui(); + g_bInitialized = true; + /////////////////////////////////////////////////////////////////////////////// + return g_fnIDXGISwapChainPresent(pSwapChain, nSyncInterval, nFlags); +} + +bool LoadTextureBuffer(unsigned char* buffer, int len, ID3D11ShaderResourceView** out_srv, int* out_width, int* out_height) +{ + // Load PNG buffer to a raw RGBA buffer + int image_width = 0; + int image_height = 0; + unsigned char* image_data = stbi_load_from_memory(buffer, len, &image_width, &image_height, NULL, 4); + + if (image_data == NULL) + { + assert(image_data == NULL); + return false; + } + + /////////////////////////////////////////////////////////////////////////////// + ID3D11Texture2D* pTexture = NULL; + D3D11_TEXTURE2D_DESC desc; + D3D11_SUBRESOURCE_DATA subResource; + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; + + /////////////////////////////////////////////////////////////////////////////// + ZeroMemory(&desc, sizeof(desc)); + desc.Width = image_width; + desc.Height = image_height; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + desc.SampleDesc.Count = 1; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = 0; + + /////////////////////////////////////////////////////////////////////////////// + subResource.pSysMem = image_data; + subResource.SysMemPitch = desc.Width * 4; + subResource.SysMemSlicePitch = 0; + g_pDevice->CreateTexture2D(&desc, &subResource, &pTexture); + + // Create texture view + ZeroMemory(&srvDesc, sizeof(srvDesc)); + srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + srvDesc.Texture2D.MipLevels = desc.MipLevels; + srvDesc.Texture2D.MostDetailedMip = 0; + + if (pTexture) + { + g_pDevice->CreateShaderResourceView(pTexture, &srvDesc, out_srv); + pTexture->Release(); + } + + *out_width = image_width; + *out_height = image_height; + stbi_image_free(image_data); + + return true; +} + +//################################################################################# +// MANAGEMENT +//################################################################################# + +void InstallDXHooks() +{ + /////////////////////////////////////////////////////////////////////////////// + g_oPostMessageA = (IPostMessageA)DetourFindFunction("user32.dll", "PostMessageA"); + g_oPostMessageW = (IPostMessageW)DetourFindFunction("user32.dll", "PostMessageW"); + + // Begin the detour transaction + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + + // Hook PostMessage + DetourAttach(&(LPVOID&)g_oPostMessageA, (PBYTE)HPostMessageA); + DetourAttach(&(LPVOID&)g_oPostMessageW, (PBYTE)HPostMessageW); + + // Hook SwapChain + DetourAttach(&(LPVOID&)g_fnIDXGISwapChainPresent, (PBYTE)Present); + DetourAttach(&(LPVOID&)g_oResizeBuffers, (PBYTE)GetResizeBuffers); + + // Commit the transaction + if (DetourTransactionCommit() != NO_ERROR) + { + // Failed to hook into the process, terminate + TerminateProcess(GetCurrentProcess(), 0xBAD0C0DE); + } +} + +void DirectX_Shutdown() +{ + // Begin the detour transaction + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + + // Unhook PostMessage + DetourDetach(&(LPVOID&)g_oPostMessageA, (PBYTE)HPostMessageA); + DetourDetach(&(LPVOID&)g_oPostMessageW, (PBYTE)HPostMessageW); + + // Unhook SwapChain + DetourDetach(&(LPVOID&)g_fnIDXGISwapChainPresent, (PBYTE)Present); + DetourDetach(&(LPVOID&)g_oResizeBuffers, (PBYTE)GetResizeBuffers); + + // Commit the transaction + DetourTransactionCommit(); + + /////////////////////////////////////////////////////////////////////////////// + // Shutdown ImGui + ImGui_ImplWin32_Shutdown(); + ImGui_ImplDX11_Shutdown(); +} + +void HIDXGI::debugp() +{ + /////////////////////////////////////////////////////////////////////////////// + std::cout << "| VAR: ID3D11DeviceContext : " << std::hex << std::uppercase << g_pDeviceContext << std::setw(4) << " |" << std::endl; + std::cout << "| VAR: ID3D11Device : " << std::hex << std::uppercase << g_pDevice << std::setw(4) << " |" << std::endl; + std::cout << "| VAR: ID3D11RenderTargetView : " << std::hex << std::uppercase << g_pRenderTargetView << std::setw(4) << " |" << std::endl; + std::cout << "| VAR: IDXGISwapChain : " << std::hex << std::uppercase << g_pSwapChain << std::setw(4) << " |" << std::endl; + std::cout << "| VAR: IDXGISwapChainPresent : " << std::hex << std::uppercase << g_fnIDXGISwapChainPresent << std::setw(4) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; +} + +//################################################################################# +// ENTRYPOINT +//################################################################################# + +DWORD __stdcall DXSwapChainWorker(LPVOID) +{ + GetPresent(); + InstallDXHooks(); + return true; +} + +void DirectX_Init() +{ + // Create a worker thread for the in-game console frontend + DWORD __stdcall DXSwapChainWorker(LPVOID); + HANDLE hThread = CreateThread(NULL, 0, DXSwapChainWorker, NULL, 0, &g_dThreadId); + + if (hThread) + { + CloseHandle(hThread); + } +} +#endif // !DEDICATED diff --git a/r5dev/windows/id3dx.h b/r5dev/windows/id3dx.h new file mode 100644 index 00000000..3a773db8 --- /dev/null +++ b/r5dev/windows/id3dx.h @@ -0,0 +1,121 @@ +#pragma once +#ifndef DEDICATED // This file should not be compiled for DEDICATED! +//------------------------------ +#include + +///////////////////////////////////////////////////////////////////////////// +// Initialization +void SetupImGui(); +void DirectX_Init(); +void DrawImGui(); +void DestroyRenderTarget(); + +///////////////////////////////////////////////////////////////////////////// +// Internals +void InstallDXHooks(); +void DirectX_Shutdown(); + +extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); +extern HRESULT __stdcall Present(IDXGISwapChain* pSwapChain, UINT nSyncInterval, UINT nFlags); +//extern bool LoadTextureBuffer(unsigned char* image_data, const int& image_width, const int& image_height, ID3D11ShaderResourceView** out_srv); +extern bool LoadTextureBuffer(unsigned char* buffer, int len, ID3D11ShaderResourceView** out_srv, int* out_width, int* out_height); + +///////////////////////////////////////////////////////////////////////////// +// Typedefs +typedef HRESULT(__stdcall* IDXGISwapChainPresent)(IDXGISwapChain* pSwapChain, UINT nSyncInterval, UINT nFlags); +typedef HRESULT(__stdcall* IDXGIResizeBuffers) (IDXGISwapChain* pSwapChain, UINT nBufferCount, UINT nWidth, UINT nHeight, DXGI_FORMAT dxFormat, UINT nSwapChainFlags); + +///////////////////////////////////////////////////////////////////////////// +// Globals +extern DWORD g_dThreadId; +extern BOOL g_bShowConsole; +extern BOOL g_bShowBrowser; + +///////////////////////////////////////////////////////////////////////////// +// Enums +enum class D3D11DeviceVTbl : short +{ + // IUnknown + QueryInterface = 0, + AddRef = 1, + Release = 2, + + // ID3D11Device + CreateBuffer = 3, + CreateTexture1D = 4, + CreateTexture2D = 5, + CreateTexture3D = 6, + CreateShaderResourceView = 7, + CreateUnorderedAccessView = 8, + CreateRenderTargetView = 9, + CreateDepthStencilView = 10, + CreateInputLayout = 11, + CreateVertexShader = 12, + CreateGeometryShader = 13, + CreateGeometryShaderWithStreamOutput = 14, + CreatePixelShader = 15, + CreateHullShader = 16, + CreateDomainShader = 17, + CreateComputeShader = 18, + CreateClassLinkage = 19, + CreateBlendState = 20, + CreateDepthStencilState = 21, + CreateRasterizerState = 22, + CreateSamplerState = 23, + CreateQuery = 24, + CreatePredicate = 25, + CreateCounter = 26, + CreateDeferredContext = 27, + OpenSharedResource = 28, + CheckFormatSupport = 29, + CheckMultisampleQualityLevels = 30, + CheckCounterInfo = 31, + CheckCounter = 32, + CheckFeatureSupport = 33, + GetPrivateData = 34, + SetPrivateData = 35, + SetPrivateDataInterface = 36, + GetFeatureLevel = 37, + GetCreationFlags = 38, + GetDeviceRemovedReason = 39, + GetImmediateContext = 40, + SetExceptionMode = 41, + GetExceptionMode = 42, +}; + +enum class DXGISwapChainVTbl : short +{ + // IUnknown + QueryInterface = 0, + AddRef = 1, + Release = 2, + + // IDXGIObject + SetPrivateData = 3, + SetPrivateDataInterface = 4, + GetPrivateData = 5, + GetParent = 6, + + // IDXGIDeviceSubObject + GetDevice = 7, + + // IDXGISwapChain + Present = 8, + GetBuffer = 9, + SetFullscreenState = 10, + GetFullscreenState = 11, + GetDesc = 12, + ResizeBuffers = 13, + ResizeTarget = 14, + GetContainingOutput = 15, + GetFrameStatistics = 16, + GetLastPresentCount = 17, +}; + +class HIDXGI : public IDetour +{ + virtual void debugp(); + /////////////////////////////////////////////////////////////////////////////// +}; +REGISTER(HIDXGI); +#endif // !DEDICATED diff --git a/r5dev/windows/input.cpp b/r5dev/windows/input.cpp new file mode 100644 index 00000000..9998e92f --- /dev/null +++ b/r5dev/windows/input.cpp @@ -0,0 +1,119 @@ +#include "core/stdafx.h" +#include "windows/input.h" + +/*----------------------------------------------------------------------------- + * _input.cpp + *-----------------------------------------------------------------------------*/ + +/////////////////////////////////////////////////////////////////////////////// +typedef BOOL(WINAPI* IGetCursorPos)(LPPOINT lpPoint); +typedef BOOL(WINAPI* ISetCursorPos)(int nX, int nY); +typedef BOOL(WINAPI* IClipCursor)(const RECT* lpRect); +typedef BOOL(WINAPI* IShowCursor)(BOOL bShow); + +/////////////////////////////////////////////////////////////////////////////// +static IGetCursorPos g_oGetCursorPos = nullptr; +static ISetCursorPos g_oSetCursorPos = nullptr; +static IClipCursor g_oClipCursor = nullptr; +static IShowCursor g_oShowCursor = nullptr; + +/////////////////////////////////////////////////////////////////////////////// +static POINT g_pLastCursorPos { 0 }; +extern BOOL g_bBlockInput = false; + +//############################################################################# +// INITIALIZATION +//############################################################################# + +void SetupIPHooks() +{ + g_oSetCursorPos = (ISetCursorPos)DetourFindFunction("user32.dll", "SetCursorPos"); + g_oClipCursor = (IClipCursor )DetourFindFunction("user32.dll", "ClipCursor" ); + g_oGetCursorPos = (IGetCursorPos)DetourFindFunction("user32.dll", "GetCursorPos"); + g_oShowCursor = (IShowCursor )DetourFindFunction("user32.dll", "ShowCursor" ); +} + +//############################################################################# +// INPUT HOOKS +//############################################################################# + +BOOL WINAPI HGetCursorPos(LPPOINT lpPoint) +{ + if (g_bBlockInput) + { + assert(lpPoint != nullptr); + *lpPoint = g_pLastCursorPos; + } + + return g_oGetCursorPos(lpPoint); +} + +BOOL WINAPI HSetCursorPos(int X, int Y) +{ + g_pLastCursorPos.x = X; + g_pLastCursorPos.y = Y; + + if (g_bBlockInput) + { + return TRUE; + } + + return g_oSetCursorPos(X, Y); +} + +BOOL WINAPI HClipCursor(const RECT* lpRect) +{ + if (g_bBlockInput) + { + lpRect = nullptr; + } + + return g_oClipCursor(lpRect); +} + +BOOL WINAPI HShowCursor(BOOL bShow) +{ + if (g_bBlockInput) + { + bShow = TRUE; + } + + return g_oShowCursor(bShow); +} + +//############################################################################# +// MANAGEMENT +//############################################################################# + +void Input_Init() +{ + SetupIPHooks(); + /////////////////////////////////////////////////////////////////////////// + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + + /////////////////////////////////////////////////////////////////////////// + DetourAttach(&(LPVOID&)g_oGetCursorPos, (PBYTE)HGetCursorPos); + DetourAttach(&(LPVOID&)g_oSetCursorPos, (PBYTE)HSetCursorPos); + DetourAttach(&(LPVOID&)g_oClipCursor, (PBYTE)HClipCursor); + DetourAttach(&(LPVOID&)g_oShowCursor, (PBYTE)HShowCursor); + + /////////////////////////////////////////////////////////////////////////// + DetourTransactionCommit(); +} + +void Input_Shutdown() +{ + /////////////////////////////////////////////////////////////////////////// + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + + /////////////////////////////////////////////////////////////////////////// + DetourDetach(&(LPVOID&)g_oGetCursorPos, (PBYTE)HGetCursorPos); + DetourDetach(&(LPVOID&)g_oSetCursorPos, (PBYTE)HSetCursorPos); + DetourDetach(&(LPVOID&)g_oClipCursor, (PBYTE)HClipCursor); + DetourDetach(&(LPVOID&)g_oShowCursor, (PBYTE)HShowCursor); + + /////////////////////////////////////////////////////////////////////////// + DetourTransactionCommit(); +} diff --git a/r5dev/windows/input.h b/r5dev/windows/input.h new file mode 100644 index 00000000..4c050f86 --- /dev/null +++ b/r5dev/windows/input.h @@ -0,0 +1,12 @@ +#pragma once + +///////////////////////////////////////////////////////////////////////////// +// Internals +void Input_Init(); +void Input_Shutdown(); + +///////////////////////////////////////////////////////////////////////////// +// Globals +extern BOOL g_bBlockInput; + +///////////////////////////////////////////////////////////////////////////// diff --git a/r5launcher/pch.cpp b/r5launcher/pch.cpp deleted file mode 100644 index 17305716..00000000 --- a/r5launcher/pch.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "pch.h" \ No newline at end of file diff --git a/r5launcher/pch.h b/r5launcher/pch.h deleted file mode 100644 index 021ffd88..00000000 --- a/r5launcher/pch.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#pragma message("[LAUNCHER] pre-compiling headers.\n") - -#include -#include -#include -#include -#include -#include -#include -#include -#include "spdlog.h" diff --git a/r5launcher/r5reloaded.ico b/r5launcher/r5reloaded.ico deleted file mode 100644 index 34370ddf87fd43a5acd47b4e51de812d0604776a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64463 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

    K_y<_Tee1`R-Kqp7!$7d$y%6XWR#*xy|fQ7{D z?%JvSd&-79(sWx)F^UXHCwd%q+7{1m``o`XMj%oUmLYR1cVy`sJ|ll|^LG2H>@Bm~ z14wQAWyCdUgNs~??_u8rGW(S?U$|ggBDXIcA>qKyZ>$`oBXfF5eJRMKE$=}JX_vBL zU`ctIxc(4*Z`1uiGQ&L%W1c}rw>_rZa8)z+RPvE+S25AxGecfOMymej*ON&eL!Z+| zH*Trx99-p$H;u=yQKc#^7N@ni%Be7GbI;O1x)aXsp>r;s%Hb6TikQ=MfkbxXUONc_n6-7B#8!iI*5h= zmt9z%MC|1c?)wkg`0bvxzW4Sa-&tAdxjLmP9-AZ8>Gy>Es3&k|nZbL!)Dga!hzpDg z$=6*|~G$+h}JD<24)WNvl&EF7_&9M0e<_IutvWTyF3l zo~$eNMfAvUSmlqbL`;R(N{r3pKc-iLEtLgi&7VK!zVKepPw=On*tN>6`%<(S`^^*7 zz-jTw+vfu%x^Ex)TY_Vt3 z)aH5D^X_Rkh`@-Yie3p-?JeOU{wz++ZICp=-aV#l+A#Li5T7>f6M?R-B zrSA^E46^kuARjnx@K}mMg*lVf_ztO`^i#L$H6aOSlG_S}bPSbi9+nwzu#i;qo;}he!$_B%V*y@|F+~l#-b^=MKqsTFy z>2j$zUoF1PAbnH(G^bA>VVYU?0$b^#+0l8?Yac476}_4-Qh}IZqez!Gr*5YCe7Z7B zp0BNGw&vY^37=XrTd5Z&BuVNB9Mg2@|Bh3PC;2iz`Yr(QAF;(4RFW@D>WiI)4=CFB##FpQ3T znH3zfyhp41R+Y2#DX)=oiNbXH94s`By%0LrakSey-eCJim#qQ#qAP(z)P8oQL(Qwk zFDyK%QK$eKPZP=%sSZz?XZp_-vRB`&snl1yq4|YAZuDt*Q)L0) zy9D7&*wT&WDm|aBB^L~@K3byGnVwb?!`C=Je9O7_m88?XeKdZu-3i<+6<5X0==-{^ zNs}wdox8X2>20iO!j1*~!!=_!9pCmDM^uugmnsGGF>l9ct!Gr|*Nq>j3fS8s_R=~# z%45)eqS5N~V}lkx8s#$GV%+ie%py~fXBq4P!m>__6`VZoWw*pF^J6>~4+mWed^RAH z=wl)+62{!X4D?o|`5s^IyqePNNqA@GJE14!HsPK&2kIQE(>GrN^<8v%-NuO*&x!73 zvfMA6)YNe<%pptjz^p@c&#D36BU4YrJH0d>lkMU)!YaSDa14aE5yHH5cDU8u*argXu#%qe`sfS#Vk38!Y7dWdgmOrsJsPLS?XOP? zJ8)r@wA6%CeEIO(k6*RxnWq%#a}|$A`}1!+b#A}6VXqKJz7u=1-sh(m?7Qk-eb(3U zNR#Ytikr1}R&PCj4n$Y&*c<1cSDaqRWsN1xFTNLOlfbc+*=yM4M)AXrJEF`aUlvP1 zWT8QP(D4I28Xr81t+S36Xf4tb?K{Umbqh|A;TE~km8g{~}%HJ=?DR2h9RjK#$_ z_!Wm*=s>2~htaoq?)OKd_2*|CC#GgoIm0`)Q9!!^lWAgMMkjA0<}bCm#5K5i)k6js zce!W!pU$?v8+)4K$cSCH>>>SXsz7X1qKVg2fcpm%tU88r+y3|ctscqWFnr~dc z&)!olJrY?F`RKT->lTIQX$1%B{BTVb&cVHr%ttUuFV>B7Qj{O^_Oh+^`lwUmHA3e3 z{xs9aPO)J*sE*Jz_6ZL4aSsAS{^(P8y7w&MD zRXgPN>MT!lTU|%Clp{4)1+)A#RUOF!&A{%r zpP>DsFm=TVZyg#KpkgAvFU*JC>&zn(HUA(ZpN!;mSKO3f4c2bDp(Y&H$8SVge#jxn z7{tvWjV0^*Z+uj}K9gafbjixzKH(|famrT)g22irdr%IX3EGDtGgGGQTri{tnTETr zFOqzyEa~J1uPz;aFmOTbLm-F7K(aN1)#*n?>ozWQcEw05I#5{NKUlp;Wyj}*RnA|` zj)=AJ&AL<h10pV&^m6S9|gN_x_#&XRrP~jXg1fPvTf+pC~J1xpRGYjn^Jlx%!&HT7$YPsy9lvedZp@v6XpCVyj;yk!NsNT3H z!a(+Ec=eSN!()`-;=%b1dv7|PqV19SK%f1EcQO0y zJlX53=lU))1P|zlOJbFu^Nhc`>)$ZUf?yS=$i=Mv`h0`6M3k9$dZ^w}@0|&l8aMOX zsC=2>1I?K#)sjT5WXdpbNK-O8#>t26tl^y;xzBF98wFowa%>jP54hA7IjYAE4<~@t^7gB0-@2BzOBu#yTDxQj*q(l-Gh0edqA@Slw zYVB%pv;+Ik_WLuPP~`=i6gbC4T>RXOxI3?USI>{W{1RIuS#F6l<%rzM>s-9*^o6yZ zdo>1I5~H5_GG2GNf+NV%YYCGoXIe3wR~Th?_A?+X9#U_LHi%*wP%=q2$ZePyR-a$k zEp+MiSzPs`R@iBwv0#lcGQZcpN9NrsoQh)zU*riTah!KH<-7@ffOURqHKRWDoH}Vs zcU=(Y*bu=#F^AC$szHn;b2YQbu|?2$xe)hJ&S1}>BpZZ ztM>}(BY0F+)v5@NVR?oZDLQD1KIRpxyK9%#2ytI&bbI@ed)*J0>9We-J2TqsMjZ#L(1FJ`{1~0A6+4ZS7VNk61B#zde~1B1dIc{5Az<_ zGqJJWYZ|9q$3XcIP#SM`e^V7QTY{#==|`5I7#Z(m?f zznMoi8M*dY&fp|ZsA?{mU#RcqCv16_4JHL%w&6TUbT^LUPoM8JP~An_H$5(3UNLn1 zj#!!URcy@DQF;$QEoG_)z)(`gv7S(Wb=@H0g7JFuIl{my27i}@#!<~=VR}i)G%E^E z{@ff~NW3~5#rAQYtizYFoJ!OWz#%#g-r>6x_zVL3fbT?5x~=!Njgm1|q1%(GjXXmn zoj(ySD1@Kj&d~ERJVQ!q4u%#>tTdmwtRXe4T?nMVGigA-jx?D3h;TVgVN|U3h&BJg zQ(klHB3W_oB;z6&=#QokbwO39Qo?Xc=n^iPNE!sWEgB3FW%6mlxoO6=7@`2E~lnY3s*K0lXB|Gn#QqPh7OBl-h6Yyyvi0{ho#{1{X3veL|lvcl29d6}k>#YKD`v$CP$DZCBFm zJNPlLEcamD*n-4o!aGt^@IZ}Tqo|gYF7Yoy;HLXu$J_~Eyz10F$ zi&tKLJRPK@#$brrFb#B4n!VWnJk9b8d!n13bU&g=ZZ^wk|ACO?x}nH`CT+_^Uj@R% z*3VYLU2&sMAQH=ie}HOno@3TFn2C|LNUtfy)?>AM#@2LP?@^<$9arX72g?hDkV|7Y9ZNWn~_eLmo_@>>KDUE#6 z-xZm}q=m6{cvZD#?AT)BOWDr5svX-&`m$s+0%7kLd&H7=eDm1T+=~HK_e|$vj}v^T z&oJs@WN%}8WEd`Glv?lioff#n&_TD@cA$j&EMG#so^t0_#*)uR>J~`GxD;#^3F28B zR$&Lt$e}80!#T%2^fG@6pFbkEHHub6Oh%(YLf%+MeEQ1NBfZsnmrtKRhUqZJ8s640 z%|4g)Bv{$8>Dgqg~Zg>dYa2ycXt^A*C``1X+@DMyID$ zs6U@&rC@S7w*FJoy`Ho|vS9drOh@{@;!FDO4onrB&8QL9cR$J+-uc& zDZGlEUjkD;;yr^4NkCk9*k-40RqegJ6>~tB{(51Wu;Q`WXMRtT3y${U92-x`y<y<7MBICAArMxAUq+hHC@{TZ4+X-Rv0XW;~e z+Z(U}lv5sRD|4dxG944vI(1K|v@ybOkSr(SNy=)&J!5RyQIEn-W9I7FysAmpkoGB< z>={$--P3%f)Ov#T62t9lewC$?v*cY9j@w))1S!s5^kEnnv-8*8Omi1B-5tK-EG_$H zvc+9G%R}>Qj%D}ke#4t2985m%q*O`uXYu69W^vF3y=MCml@hIrgtjpUJms7;EM{bva zjD~Cj{r2(^tVvGsteCzGa4E)Kme;)%@Xm&>9;>XOax7_o=EIJcm`b`h8P2Mg1n6eeqx#{+aqq{ zyCG70Z73mC=J2_>Y>%qz%yqmpmj-m{ljSa#(XFT014yHvhS42G$+Si9j#v%mDbL#rRc>yHxTBdM5kfL zcz6Hjo0pkN@Rp?vElf?j2NYTmn}zI7gZ(9>HlQ@A;7D(sNsHI*)C;(&RMi?G3du^#wUt=cg^B)wVdb;PyF|k*OtLUjm8Q>kKa)3 z(<|5~GxRCPk)a?~9d{OQp%!vlCr}mK*4W*1X;EBXt}%94S=AEL*I09P-JDFmWW`4F zllymwddm}3a7an@v14c6?JMDaiJ=Mxlo!LTm`Z&7bm=F^Ldgsdvh!l2xsxb)&S83Q z7HjQ2oj*o+HGiLc3o;mq6U%z8bhgt{5o+Z83B65?+1gt>n|XFB*sP^?EPr|A)FF_Z zxlf*upL%uPV}7uoR|;?8K^d~@={!=hiUVVNo)o&)0$Sq-aRcO9BDT+up{kJAiGrr6 zN>%)4Uq1I4vSJegtMW*n14UQND}oPS5T7JAH#4U#+rv|-&B?=P!%ZmcflM*1XQ7W0 zE161SSg$3o8^Osof7?1jk-`aO-FUygtr0qo610Ju;85PaULo>!k|Ix$PTg&)&6L}w zb2qUWVN{UhaHVlaXa9zWXJ-XUp73h=Z6~bGs_IppEToJ9l{b@&Yt=t@np4J>_6~$U zQELgX@!p_Nbce+*@jOk-Skrw*g;BoTqls8wT;w|gJ^S*hc*o$@Nz49A_73Zj*lc4S+PukkY+>Cvc$B$-K_fvTl|&+PbZHt;y<_3HPe+>0iw6_f@ey5vgwe1uYS8Yia&K^9JRxA*->;4Gjn^0L*5isa8k^@9ZJV@Z z*gQlzlt`{$ZNi>y@r70(9>s@8igNV(ck1%5GM6cVv8J>5nhGQExLyx*ASWO8P3S}aS~b3i3e=amlKm_%^zO0c`EKTI_Vr}4TjW`xf-z+&{#HWEKy~I z2DbF8-8VZtt>VsItC&sJvajO3x+Bpzxu7y?`?Q18LOM6^2Cuyw$GWFa3l}K4mkIb& z&5Z<5mn(8?chjazX%b$&APk1L(`<3m|qJ8+ZT6f?{aZD#MpRl1tv8rpC0 z;Am$6WH{)|pqrp-ylQ&)dh=%OAU5!Rs^`JMw?|YR+evrmE662l4y+FBf7VHN@WZTl zu;PFwgmNN(BxHA@k8_w8 zIp4?TtTo4&->kC;XL+>h3z^>06t{MhjC2_VePIMKyG*eeaM)UD+6Edni}F(N{3CxZB{+N^%P-pE+6GSeD0bTY>S-|&N}8l&?cwc)GW#piFl zI>;Pmopy`0c~*5et2mCpc}BKBL)V<`i{#-?PIsRkt|nhCe!?P^d&bx&$M9@~MWY=( zH~C`Yxvm#pZ*()5fp+0UNTNGE?MA6v3vSh4_AH3Lma#s!-S^>w9Rst;dNq~e%k$-j zd;^ZQGF8zH3GJI~yTD#)=6vka@eS|r>K|8uM^=IR2KVylH~Mt3jCfCzwMO@|-uzsQ z|FUgIaP1aHeTBYU!ySzz47YGT_jTz#qO`4V9hzKq%Lw$zSZ7dQ)VH$D!IRg9_Lm*$ zir0Dst1}2zxg4psb{B)BJ)uFljy=);{!zD^ISVry8LXf%S=jG@!j_NxjD{!o*1W%$ zm&}3tk65gWq0d53vfptcw$#}H-C~yzhu{?#FxUDF;40UD#W_^BX_4Z8Ey-m(_;Tl2L+cc$j;PvB?>wK&!=?R z#XK@XzWX+h&4D~4=^Zfj;M@MJKPlJZ`YL22mov40Uvh@ zxAz6AS)K9qoZQ&~Ue?MVKa@CKF}{{P&K;EL^h8q_Qz>~O-y9K_xtybRut4tomf5Wl zP8B%!{8qe6#SJO7a3|x>=b8O3mf6Sd(doQ(XS-b6=E^X!RP`+e`kw`SJ1y>S+`Z-o zMh*R(L$~-`*oX3j>kpF;d99)9%e~Us*VNI}+ECpoMydneP~qlHGhaDBGdX$t?Oo%Z z8(yKRmvpKcZ=7?tJbnNuJiX?y`T+cjgUKvo3v_e|J(@1va4M6?9ad$a>w8VzRXHrKe4yxXHJ;G)%)qt| zM~jc>j44bcj|TP}JDQcHRwo#o&^*?1H#5sWB~rH>x2UjtYkKL8?P}peW&=L+uoYQZ zI1hPA(%zYMY2e7p`ZRIh=91@^7s%1a*Iy6gA1GeBkHhbv@{2XRp?s_Qm~O%ARkD4C zrr4(n_HRaC$D}^ml=rd!yaCr~e#Pq*2Jq7%f2OnU>Lkxj_sd}9ZmWKLNWON@+>~#r zMgHEScnkPCnVH^UJY$toFYKQ*_sFYbVk@?0DCBkI?s|0Y;1!n8p=;WRNDfWdW}yJnyBtoAekr&TC#H0Qj4PX1V3F7P>rY>M z43MSEIc|V*XH23?Oh{1bSdbjj3*XQoJISM(UKr0TxM(Kl){>@&Oz#^zsq`rR%)>iP zldA-PN(zs^)^04+13!eTthqsmbr+62*mkT^u*UL4@gC7@F7lb?)gs%Nu_wdWVY`z? zox+|@O>J*g)>l{~LV0r!&nmY^XUgH{1I5!GvYgHSz9nq6S7Rog4VJeHNf^?}o`u~v z34ZlPO`+z}(%`i2^bpgcG)q<8i_88`5)>TB*6G!slqOIYFH(3OG!RmJ+dFB0<4kp? zZc%9}DbLXAbFbM$riVz1_M7Kc$2hnro7bm3jNN=pW+q4J0$mByoYnPDd!FYMxOm7Y zoNV7^<)I3zi{ufaka7{o@^5S(?^GK~{5VhYlA=}FXw9@8ex}B>X8qAi-0NSD%Ej?!ZAhfXoLarjJij%RPY0BTt`#+- zI^8z&MzqqT#m;f!RGh(f_<>!DFosgW#N04P@uIc21I@&VgUlN-A<{Y^MfKe=@v8*^ zDm9wnN2u4oY4n1h-aXibWfZU%v^)9i@l0UH$jt|m+{y8$2ew8G6m%G=(+L2k!1IkZ zsk-Hg>8dX8II{LiP+WGUcf<#4DJ}%+F78rifFJ2;kW-1My7*%EZ8z9p9cf6V+y}9q zv={8Q!>N?l-s9huoQ)9xQNg4<8+Y{z&_+mg{cvzDrcd6H5;mFv**-xy8gIyfj zo;!b2JJziQz3RqTMh#gTz0IZe6nLA5Sqj?cM!7;^y&S>&soOe44+_i9FBGi~GGHO4Ix`d0khgXB^A7=m4;lR$ zM$5eV<=&;+FiFiB5`w z6qkCDbFl4k{wM0-FvdiG4j(a{?yW5Av#d$-Hg8Wdj?C4Ovco*hgnm}>@I)>y#9l$Z zzo}b(2bD-tOy>n7Qc{j1?{CKI^4#;?eF5s{@Vm+goF|+9!P+%W)qRTtwB4nq<9;ha3QS_7!(t647Z&rGxzxYY0@SSJ6 z{)@yHwT_ZwWh@LcXQHTs__n%+?{$08X{@fYrC!W~3RqwYcUb$<5BiMvPx#~*CmY`6EDn=<#NSrsb4A~GzS;&) zsDYnIuRpQ4cJ=TEpNc0RSHX%>DLXrv&djfRkq^J6PI!{sHT>DdS8L(^&R6R=_E)Ul z?Onp2Z7BSPbkOm6l#j!^DjQwqYd$RrU)C`pbfT8_=C^NKi8OV&_freAbO#@|nfZIqw))LAdG4Hjh@*Ol_^O8}4_;5JcTE%*I4$gLr-Kqw(W^bW_8xHlINX|u5UFn=!ah$mHB_cmEq*`#1fCcZW^1o&*r39KOhup zGN*Q{qRr>NzPMzz(%{p}j|EMH@xl7uTt^=p`q{l>onG_4vM}gNw{>5&QYcLy{Ii1o z)p*Gl;|%o8CU_AC6V_zaZmzvw&V+>3#vY+{2^dbjmtITo^j%!+5S~f9_1=Hm`r&Jj z8#O8PnKI>ec;R?n#2wj~VrWnK?lsAr8vX=Tu=IN^VEbHC@qAhkr0p z*09Gl?PUMjk!K~AnzxM|PC6zTMz7TwU^;W;!raHxd3H)Nr9y45E#ng85AAo8@xdJ4 z+P1;4vh6|D`*_!l3LG5AY7Re>bu2FpXK7a1KE9u|)=>W`6qw0ImRr=uN7K0W)g5gv z_365p!%|E*HgHJf61`c!V7}-l$^p{ebu7UR%_=keX|B`DO@&kFA_Ct9Dkk^$m z*l5~BE~z@X7xLOFtvX5gU&8aB+m6WFWGj@#9+YRI4A6F8;(hpkE#H6c$HS-$(0(AJ zKDPn3`Mdug@&2`6Ot#AHm=lKmKH$H*&%-}JMieyv;kgEm1#~`uyx0DBwLSa;Yy@uf zf@I~*BNqE-{ybj)+;4wtnWZ`Lzh|D;G@eE-U&SEen@Eig+?nMZMKd?@Wz|D~mxM>37>N$(RjfxR9gK0z-&JBMj)BoxD zC=XT#XOKrl3&_mOAKUy#DWGzhn?sP{&x=Sy69Kt-XC85e<79`?G=lb>|LMH`NFUtj z3xOL$BmD!52;6M>-wr4U+_V|W0*}B=rV+TQ_5Ua2fVMe0mY-MR5xCj(ZwYWS5Cm>~ zjlhk!iDTnOS^rOpE9y)ksTuRcw)-v2uRwy1kK*zL1a1)bKe{g{>rEqY6FQ{r!(Y#L zD_HusAJMVY*-b#;#_WjvKdm$V8~PYJXBk<~Ah)yU5x5Eazs=|00l>|2kl=_p1l1@0 zJNiGWySkm3Mc}4{zg(-fLHgikg$UeC5SfSL_-6r~|NmVnp?YIT0cY=eJr zjf4j(AaFCtZ%qjg8$jS@P=DJOm+hU$#edWX;D%3#z4I(_PE&z%2fBX10~3%>!~dEb zdU^?n&5?gbe^l0;LLNR|_*46!eK8^yhir%Q|KE*AWxXk)K8UVqXun_Pe9d7zcEUG-|L}nJm5P^m1l2iK;`rUqXutOknuEIB4+MOTPQ!MD8-O9GjQ*}}6c^lN z47mx{N_c?9PxXJ4r|T*6$P}~@eihI$bTJzD%em<9X!^T+xH%f~=I!@>|5xfB{L}};WxBrhk{#_hyq>IGfz#%x?U)8*n1~-gD;2|vkC3B%Fw8>sUoBdY-9^8Ux z!+HMic=?a!qjurrl0VNqD`|oo^ddXWe?JXqpPqnzik}5^9Xbqkf&VCP|0>*GXiuU( zqn`z|f56RIvw&@w0HRVBxpw#^`$W ztGfIRX@o=NcpH+v{zrp{bb}6c3ls^0U zT=cvBsYg!CA~Ul;WuNG)LC46yD$n0hE)wcmKO2weI^+fQl;2SgiUYM}@jnqanvVMR zepg-pDjw8_Jo}S2LFE#C4foqKz6;@1kpg;(vkv3-t%1 zdI&tk_oo3}qu~L>zfJG^vHf(+rV)4$@lWeVGf>|!`d!bj{NU(XVFPu>f5%^feis1` zIsRqc@GxWq9{Bv*+kRONXdi$FZ2i&}MhQgy`3Z22`k6l-T~FV`ewq$_rSOpIe@Rb6 z-z%dw-Oqe*s9zR+|L|Y^-3U4c;epjZT?>{Q2&D)0QK0MakMC*G_ebb=9;gos9j^hQ za|nueS_s)6_^rs#V1H*iJQRgU_evW6>KUCA(RWz4p)QWf z0evrm_Mv6|mtkgS7m=rB3&d~W{_Z^@Doa$~Ua61u!S&#G>N|fYE0hO#5X>)qaLc@* zcT^7OJ5AI-j@r`bdm4B^+{z-vGN?U=`uKnKE(E0weUEqyzI*P5@BPqs>+s;a|MGtS zqfF3tLj6L&@Ksr@6J zJv#p5kA^=-$jI#iFJyfCoMrAK$LP}{b+qL_csLRJroZ|7)0@{EIC;TknML|C&%0&+ zt#N$G$Z$jahURIn@12N&x!`e**?;?(IFPxCi`WS{rs?|*#6P{^XHIV3`?sez;~?uD z<@l>24(k75{Nvi=Vu9ZiF`$?3UArc5 z@IOn(dF)*;k~W?d@2u})fM4~d&2PW-wG9rwywkCuOT~})=ZN*ycYQ8nz{LVj=0Xz> z`>#8(E-bO{C-A!HQ#0So`1j9Dkl^rQC62g!x%^+oz`;r992%{Bi#!oO7@z&|$vhDU ze<1#;luItz{I*N?uC4^Hyenum@&&BEv_zVnlUIUi_1T!qcg0-a3uaw$-Mw<+!0vr( zPhWg6cdZMnPlbGP-6fZNKJdQ2^34T%_DLUB_r{ofY?*jxZ~F0wkz7g}XsMz1?9+Hx za{rsd9&&g1YteNqXCDyrVF`1UKf3rUb~k(XF@4Uqj^ns|hWz~t?>M>n(8FgoHwxFb z`y#Ff9W7@J#-SO${dfH7sm6ZR)}M&OVsrbG@DG32E$fXQZkj!5E1t)Ju~sKr2AITl zclpwm{V^`$JlW2~$LypYY+vZE?9j}%#?pU+ zz3tZzt~Yvnv|lT1esp=Yx@Wz|mMU*RyDk33{IA6T*6I|OXH77_>D3edd=^gVmD!cs z1^wN9Z^SKi)SYzPlu5pzby98$-SXT+uAQSD-GygTdQT- ziW07&SM+_XQs1#y$H<*S>lykS-_%+xCwXKZe0AZ&Iy~mw&HjxIe;57Od+>M7cE`GQ z;cH&{F@I~Z5azGkf@8s*^)#^{m35wP-#qhIy?1knQ%MotVd1z+fh0TORt+b0dhzvuYF^NDU3a)k9+{-9cY_9^zp zwcqj{KYRLz+P;z>?ilgA_>eKE$>@CH_>9|&BusggBk#`8XQ9C^I#aipf_D6k@g_TW z?TxaHp;!4SAN%+1^T3$s7k@VU#nKm3{u+3F^w^nZKikCv?Xk#%_;Qy2j5S7F!%+C9 zJqLg93|`cy=izrHPUpSv-)MX``Bi4Imq*KT(YE)I+BOb+p85Og$ITIE)HP<)VD0+d zJ`ilvbZuygUakEcnvhTEaNxSOpHx48-A1<&{po^Zk^A;?`ap*=Z=vJ*Y~)ascLm-{ zc;I|&yqER+o_OlYSvs^|a6m876${gDcg+9c$iDRB;YUvIS6d&Z_2XpdV3z5>6@t! zVCQqP#RnzE;VdmJ#U{V_IGmfSck%T}*bj1|dD%M*7A|NxsI|a5KP{iDn}2u3-#Fjr z`Pbx1Sm_f6-URpf~#+Ih7uceJUFk zo5wh`ve;ZOmh5;RjvNnSxfF7?Ts(B$V~q2>{iJu`cR=sn*7Jb%-$HjmS0Z-&C!!Cv z{r$Xc_q_*abjC+MS&#Wj**ULtv+U|*(F<|rfiW|FrCugs>AS+Qm_E9M_WryNA47hF zGoO6cUP3$U$2B{CADs5RhhvV9wI6{mHf1?x+WmfgHC*M2JWyYdxv!A|c) zH`cB%`#;N7UNCx8`37qJ2ESYh^yupwlN=O>!j}e((%C!`xjbw3Re0q4RF2CmaCw%U z-gzEb=rhrIEno89E7zL1Z$4M0Zl4~QKS*2erB1rbt13FyA#m!~?SFJ?Tih~TUzZ)& zJ9MUaV%Y|L1^rrkPMrx}dFMyx%TdBM2^VESPkbF`$s?0+qP0J~^VBw55dF#vP*yOYezJhr;DKyuQ@)!Xf^}zP1QGtJ~TvrnxNg`_D3$ z*~rRyp)Pu%F?_4o9KQC%R=Pn)v&0u$huU>~>F7ANJ?l0*VOjM3jlqwGjxpZV&cVUA zc~P5`_VG*pTV=s}t_^>$S>S8iwjTQ2Nbm)1e`-Pa@UQmxqQ^1xGF{_q%!cC6{6KGI zvBu6)M^{^NE3lW8W!Cw@{(ASlTbTgf{5z2gqpRJa_sOemefO;VMO);RHlH%ai|F<3 zpZ?_wTgL@ol>=mG|MEHZPx|_@z#-e{JoA`M*m~aXv$2+QEZZG=d&*l?7JDh{IeHojSNR+peS4m$AHU+GcSVt#P%J{8DOU5*FmKtM`C4a@ zr$C>|HfR0L*Rf_7By*BI$xX)RmiMkVawr*<&mx%>FD~07_sB^|HYIDZt90SJe6R1j zHq=@3(|`wXKKay(O>9QW_^!{yngz#gc?TVmjnK#}GC?1VXpN6~?!j|>rd`QS_E5&Y z+z-LoyuLqOaKa-ykUe61&P;<7y^>8A@d@sNtGut@);{CL|EIR^X~T*h*<1K~FG_1A zEPda}uhuDf_T`P>FKO(P{=kTL=!5xV@6=b{GJiAf{0`82?stu+Gk)K+zndQphiNe# zR<KD!pj#;CLq0S6Reb$g=$seF|IfeuWV7DTDU4BHQ%9o@UwdD`xUhcLIFG4A zo9K5>9J*}J`mWvkjz4hG>BmE7&#o}h;jizCoEd?~i@?4RFa+)nCu|1;z?aQbMrpQk zFdm0DHl|(ic};xI94IVL;F{I&N!!R2Jhr1dwO`ER`^uX#m_4bfSr#hzq8yEg2lec*?@WW z`>7XRYW6$yE-5D^Y{o^7;Cc27&ZDIZvwx9C&^|741Rm!m*|XE1&|!f~-fVD`V?@4m zZf(9h`uNuPTN(i+!52 zRI}ak+R#XAUZYiKsb--&I2hwtzSs5LUd1`@S?qc2pJ$Pgz$C7@+=o2$xk*lju6FZ2 zT#EV1^7SX@({tcQVt=y0;gCJQGobLaEDt?AUtBGkr{BsVhI+_(qH=|nF(@C8{YIPa z5?9l%d^5AK$6g{cdmUohw-1=f=}k})2{fhyt!HM(xZ=^X?Sbuci2y@hc&)x z-753a(#MrL!)fjN{g-1Nmhl&0KnuSgaV~7LKNT_;y{EO_8REG{dKaF>!{8%ydg984 zv{^o5{dGn{nU?`Rtg+~mt%*;&@{O>O%T3JQnR68%hX*>qdDdUBn0M%?rsr%LF8+{7 z$VblANI!M3%tE(i|MNRMkfoeEzj5k3O%_d``)Z8Pot!}V{#aLQJsSpp19X6&muoz^ z@4>KzS*I73`}$xe&+)y#F?6xd2W`Q-b5%p09i-7~o;vv6uzM zNgJ{Mj<)UnTk3!d{x3bn_r^!=!qijj|E}#|T>iR^SH?IU2_4bKEdA|!Ht@b4`ngYB zzUO$?`1ZZ)*NFXmdVMSX3kQybdqe{Mo+d*h5|dy2n^c)p?MPYtP?EsktO=WlOe z4Z+XM-Ff$j*`MZI`&jS9o+M9nC4bnTIqQoacvik>?eGB~AD8ytE*ZbJ>=obl2OEQ9 zNWlWn_@?@`Bd+G<1B&L zL$g=HzgSKCXobvi-8=i&FS5pc0jGY0?GyLCu%+MVlfEE%GFp}6$+O-eU-t1$_Kl~X ziHD;+5whP9{n8uXc3y7Q2j_?+eYCGP#6BQp(|aQ(ZwNk(Px?NQfo|zJ7n3ive)_PT zx7+Xf|8UTcca*;O7Oh_sef6{dCVtSyQ2ek>DfSF~_(5}MQ~7$(xBt6vlJ|e|+8E!n z`*QN2xst!NujIy0#+-*sG$-y74SEhungdCj?xAP#cMW|8O!8p#(WiItlZ-huclafv zTe7EpuQ`_dll<50e8}{rAobxBmSaq;{EqpG}tNoC9jaR@!@+iE7J$uM_ zX$Rk-C&9at?wUb4`^P=}hYSuM+VM#khg`uzPO=w3rxnfdRKTTUFot0bij9T5(;4&BJG5FzF1L+R>=TmpAE*f#h9e)|~h<-`T2Ad3h z^iUpUQ=K6LEc2RgBrb-_t;tb z>NA$k4^#f?uLCF1r}F*sJA7q4dslyr2U<<~GLBhCTd#+n=`TGip1-SY&n11K3FBz( z6>mX?OrDzhIqOl`ZnmFS{b<(Upg(Ot74rb^H|}WKZ}5WF(fN?I*Br9uqCs_*#oVLA z;kO4rHM&dK$}4=Z8_>bYQ-l{NWDV!X)^wET-|yxzVDPTByDNvt`FbX6QS3F3ZN0aZ zPRuiBPs3Z^bAF4p>{vYi`bnMLa89cy`MtHHSwu$sW4+?muq!^fZoTGPJ`;U^?y{BDcXhpM?8%~iI$m{@b>?@zwI7PS2jst_kDqDeJTk0t&u3~^ zbg5lDIOiK$W~)0VF9x00-c>hVE>@1NVUj=nbmSX9-S8E5U2B6mQiB70HgKRMUFxExpC z)!n?`vwyrP@LGFc8)R|uPk3<6uM_;%8t}t$3778;{z9gQBe?QBpUBkR;*(DVuk~?0 zar=E^LiROA>YyjGUwSa?Wa!YCYS$Xp#lP@Cywg5LU(k*ZTDtkSA`UWWtBc1cJm7HN z0UrbF5Yd3`MvRX$u#^rmyH}A&WZ?S`! G|NURO`@$yx diff --git a/r5net/include/netpch.h b/r5net/include/netpch.h deleted file mode 100644 index 78f633cf..00000000 --- a/r5net/include/netpch.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -#pragma message("Precompiling r5net headers.\n") - -//#define DebugR5Net - -#define WIN32_LEAN_AND_MEAN -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/// - - -#include "httplib.h" -#include "json.hpp" diff --git a/r5net/include/r5/r5net.h b/r5net/include/r5/r5net.h deleted file mode 100644 index ca6b97c7..00000000 --- a/r5net/include/r5/r5net.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "serverlisting.h" - -namespace R5Net -{ - struct Config - { - std::string R_MOTD; - int R_SERVER_TTL; - int R_MIN_REQUIRED_VERSION; - bool loadBanList; - }; - - class Client - { - public: - Client(std::string serverString) : m_HttpClient(serverString.c_str()) - { - m_HttpClient.set_connection_timeout(10); - } - - std::vector GetServersList(std::string& outMessage); - bool PostServerHost(std::string& outMessage, std::string& outToken, const ServerListing& serverListing); - bool GetServerByToken(ServerListing& outServer, std::string& outMessage, const std::string token); - bool GetClientIsBanned(std::string ip, std::int64_t orid, std::string& outErrCl); - std::string GetVersionString(); - - private: - httplib::Client m_HttpClient; - Config config; - }; -} \ No newline at end of file diff --git a/r5net/include/r5/serverlisting.h b/r5net/include/r5/serverlisting.h deleted file mode 100644 index 1e895fbd..00000000 --- a/r5net/include/r5/serverlisting.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -struct ServerListing -{ - std::string name; - std::string map; - std::string ip; - std::string port; - std::string playlist; - bool hidden; - std::string remoteChecksum; - std::string version; - std::string netchanEncryptionKey; -}; - diff --git a/r5net/r5net.vcxproj b/r5net/r5net.vcxproj deleted file mode 100644 index 1ba94683..00000000 --- a/r5net/r5net.vcxproj +++ /dev/null @@ -1,178 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - - - - - - - Create - Create - - - - - - 16.0 - Win32Proj - {f04be619-0326-4ff1-b06b-fbe882e04d5e} - r5net - 10.0 - - - - StaticLibrary - true - v142 - Unicode - - - StaticLibrary - false - v142 - true - Unicode - - - StaticLibrary - true - v142 - Unicode - - - StaticLibrary - false - v142 - true - Unicode - - - - - - - - - - - - - - - - - - - - - true - - - false - - - true - $(ProjectDir)lib\$(Configuration)\ - $(SolutionDir)build\$(ProjectName)\$(Configuration)\ - $(SolutionDir)shared\include;$(ProjectDir)include;$(IncludePath) - - - false - $(ProjectDir)lib\$(Configuration)\ - $(SolutionDir)build\$(ProjectName)\$(Configuration)\ - $(SolutionDir)shared\include;$(ProjectDir)include;$(IncludePath) - - - - Level3 - true - WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) - true - Use - pch.h - - - - - true - - - - - Level3 - true - true - true - WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) - true - Use - pch.h - - - - - true - true - true - - - - - Level3 - true - _DEBUG;_LIB;%(PreprocessorDefinitions) - true - Use - netpch.h - stdcpp17 - MultiThreadedDebug - - - - - true - - - - - Level3 - true - true - true - NDEBUG;_LIB;%(PreprocessorDefinitions) - true - Use - netpch.h - stdcpp17 - - - - - true - true - true - - - - - - \ No newline at end of file diff --git a/r5net/r5net.vcxproj.filters b/r5net/r5net.vcxproj.filters deleted file mode 100644 index 91f23065..00000000 --- a/r5net/r5net.vcxproj.filters +++ /dev/null @@ -1,33 +0,0 @@ - - - - - {676c0758-86e4-4159-8aaf-17969db2d381} - - - {a3728292-4416-44be-a05c-5642a99dbbad} - - - - - include - - - include - - - include - - - - - src - - - src - - - src - - - \ No newline at end of file diff --git a/r5net/src/netpch.cpp b/r5net/src/netpch.cpp deleted file mode 100644 index 0c622c94..00000000 --- a/r5net/src/netpch.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -#include "netpch.h" \ No newline at end of file diff --git a/r5net/src/r5net.cpp b/r5net/src/r5net.cpp deleted file mode 100644 index 3c007778..00000000 --- a/r5net/src/r5net.cpp +++ /dev/null @@ -1,264 +0,0 @@ -// r5net.cpp : Defines the functions for the static library. -// - -#include "netpch.h" -#include "r5\r5net.h" - -std::string R5Net::Client::GetVersionString() -{ - return "beta 1.6"; -} - -std::vector R5Net::Client::GetServersList(std::string& outMessage) -{ - std::vector list{ }; - - nlohmann::json reqBody = nlohmann::json::object(); - reqBody["version"] = GetVersionString(); - - std::string reqBodyStr = reqBody.dump(); - -#ifdef DebugR5Net - std::cout << " [+R5Net+] Sending GetServerList post now..\n"; -#endif - - httplib::Result res = m_HttpClient.Post("/servers", reqBody.dump().c_str(), reqBody.dump().length(), "application/json"); - -#ifdef DebugR5Net - std::cout << " [+R5Net+] GetServerList replied with " << res->status << "\n"; -#endif - - if (res && res->status == 200) // STATUS_OK - { - nlohmann::json resBody = nlohmann::json::parse(res->body); - if (resBody["success"].is_boolean() && resBody["success"].get()) - { - for (auto obj : resBody["servers"]) - { - list.push_back( - ServerListing{ - obj.value("name",""), - obj.value("map", ""), - obj.value("ip", ""), - obj.value("port", ""), - obj.value("gamemode", ""), - obj.value("hidden", "false") == "true", - obj.value("remote_checksum", ""), - obj.value("version", GetVersionString()), - obj.value("encKey", "") - } - ); - } - } - else - { - if (resBody["err"].is_string()) - outMessage = resBody["err"].get(); - else - outMessage = "An unknown error occured!"; - } - } - else - { - if (res) - { - if (!res->body.empty()) - { - nlohmann::json resBody = nlohmann::json::parse(res->body); - - if (resBody["err"].is_string()) - outMessage = resBody["err"].get(); - else - outMessage = std::string("Failed to reach comp-server ") + std::to_string(res->status); - - return list; - } - - outMessage = std::string("Failed to reach comp-server ") + std::to_string(res->status); - return list; - } - - outMessage = "failed to reach comp-server unknown error code."; - return list; - } - - return list; -} - -bool R5Net::Client::PostServerHost(std::string& outMessage, std::string& outToken, const ServerListing& serverListing) -{ - nlohmann::json reqBody = nlohmann::json::object(); - reqBody["name"] = serverListing.name; - reqBody["map"] = serverListing.map; - reqBody["port"] = serverListing.port; - reqBody["remote_checksum"] = serverListing.remoteChecksum; - reqBody["version"] = GetVersionString(); - reqBody["gamemode"] = serverListing.playlist; - reqBody["encKey"] = serverListing.netchanEncryptionKey; - reqBody["hidden"] = serverListing.hidden; - - std::string reqBodyStr = reqBody.dump(); - - #ifdef DebugR5Net - std::cout << " [+R5Net+] Sending PostServerHost post now..\n" << reqBodyStr << "\n"; -#endif - - httplib::Result res = m_HttpClient.Post("/servers/add", reqBodyStr.c_str(), reqBodyStr.length(), "application/json"); - -#ifdef DebugR5Net - std::cout << " [+R5Net+] PostServerHost replied with " << res->status << "\n"; -#endif - - if (res && res->status == 200) // STATUS_OK - { - nlohmann::json resBody = nlohmann::json::parse(res->body); - if (resBody["success"].is_boolean() && resBody["success"].get()) - { - if (resBody["token"].is_string()) - outToken = resBody["token"].get(); - else - outToken = std::string(); - - return true; - } - else - { - if (resBody["err"].is_string()) - outMessage = resBody["err"].get(); - else - outMessage = "An unknown error occured!"; - - return false; - } - } - else - { - if (res) - { - if (!res->body.empty()) - { - nlohmann::json resBody = nlohmann::json::parse(res->body); - - if (resBody["err"].is_string()) - outMessage = resBody["err"].get(); - else - outMessage = std::string("Failed to reach comp-server ") + std::to_string(res->status); - - outToken = std::string(); - return false; - } - - outToken = std::string(); - outMessage = std::string("Failed to reach comp-server ") + std::to_string(res->status); - return false; - } - - outToken = std::string(); - outMessage = "failed to reach comp-server unknown error code."; - return false; - } - - return false; -} - -bool R5Net::Client::GetServerByToken(ServerListing& outServer, std::string& outMessage, const std::string token) -{ - nlohmann::json reqBody = nlohmann::json::object(); - - reqBody["token"] = token; - -#ifdef DebugR5Net - std::cout << " [+R5Net+] Sending GetServerByToken post now...\n"; -#endif - - httplib::Result res = m_HttpClient.Post("/server/byToken", reqBody.dump().c_str(), reqBody.dump().length(), "application/json"); - -#ifdef DebugR5Net - std::cout << " [+R5Net+] GetServerByToken replied with " << res->status << "\n"; -#endif - - if (res && res->status == 200) // STATUS_OK - { - if (!res->body.empty()) - { - nlohmann::json resBody = nlohmann::json::parse(res->body); - - if (res && resBody["success"].is_boolean() && resBody["success"]) - { - outServer = ServerListing{ - resBody["server"].value("name",""), - resBody["server"].value("map", ""), - resBody["server"].value("ip", ""), - resBody["server"].value("port", ""), - resBody["server"].value("gamemode", ""), - resBody["server"].value("hidden", "false") == "true", - resBody["server"].value("remote_checksum", ""), - resBody["server"].value("version", GetVersionString()), - resBody["server"].value("encKey", "") - }; - return true; - } - else - { - if (resBody["err"].is_string()) - outMessage = resBody["err"].get(); - else - outMessage = ""; - - outServer = ServerListing{}; - return false; - } - } - } - else - { - if (res) - { - if (!res->body.empty()) - { - nlohmann::json resBody = nlohmann::json::parse(res->body); - - if (resBody["err"].is_string()) - outMessage = resBody["err"].get(); - else - outMessage = std::string("Failed to reach comp-server ") + std::to_string(res->status); - - return false; - } - - outMessage = std::string("Failed to reach comp-server ") + std::to_string(res->status); - return false; - } - - outMessage = "failed to reach comp-server unknown error code."; - outServer = ServerListing{}; - return false; - } - - return false; -} - -bool R5Net::Client::GetClientIsBanned(const std::string ip, std::int64_t orid, std::string& outErrCl) -{ - nlohmann::json reqBody = nlohmann::json::object(); - reqBody["ip"] = ip; - reqBody["orid"] = orid; - - httplib::Result res = m_HttpClient.Post("/banlist/isBanned", reqBody.dump().c_str(), reqBody.dump().length(), "application/json"); - - if (res && res->status == 200) - { - nlohmann::json resBody = nlohmann::json::parse(res->body); - - if (resBody["success"].is_boolean() && resBody["success"].get()) - { - if (resBody["isBanned"].is_boolean() && resBody["isBanned"].get()) - { - outErrCl = resBody.value("errCl", "Generic error (code:gen). Contact R5Reloaded developers."); - return true; - } - } - } - return false; -} - diff --git a/r5net/src/serverlisting.cpp b/r5net/src/serverlisting.cpp deleted file mode 100644 index 1e90e1a8..00000000 --- a/r5net/src/serverlisting.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "netpch.h" -#include "r5\serverlisting.h" diff --git a/shared/include/banlist.h b/shared/include/banlist.h deleted file mode 100644 index 3ed13f42..00000000 --- a/shared/include/banlist.h +++ /dev/null @@ -1,155 +0,0 @@ -#pragma once -#include "pch.h" - -class BanList -{ -public: - BanList() - { - Load(); - } - - void operator[](std::pair pair) - { - AddEntry(pair.first, pair.second); - } - - void Load() - { - std::filesystem::path path = std::filesystem::current_path() /= "banlist.config"; // Get current path + banlist.config - - nlohmann::json in; - std::ifstream banFile(path, std::ios::in); // Parse ban list. - - int totalBans = 0; - - if (banFile.good() && banFile) // Check if it parsed. - { - banFile >> in; // into json. - banFile.close(); // Close file. - - if (!in.is_null()) // Check if json is valid - { - if (!in["totalBans"].is_null()) // Is the totalBans field populated? - { - totalBans = in["totalBans"].get(); // Get the totalBans field. - } - } - - for (int i = 0; i < totalBans; i++) // Loop through total bans. - { - nlohmann::json entry = in[std::to_string(i).c_str()]; // Get Entry for current ban. - if (entry.is_null()) // Check if entry is valid. - continue; - - std::int64_t originID = entry["originID"].get(); // Get originID field from entry. - std::string ipAddress = entry["ipAddress"].get(); // Get ipAddress field from entry. - - banList.push_back(std::make_pair(ipAddress, originID)); // Push back into vector. - } - } - } - - void Save() - { - nlohmann::json out; - - for (int i = 0; i < banList.size(); i++) - { - out["totalBans"] = banList.size(); // Populate totalBans field. - out[std::to_string(i).c_str()]["ipAddress"] = banList[i].first; // Populate ipAddress field for this entry. - out[std::to_string(i).c_str()]["originID"] = banList[i].second; // Populate originID field for this entry. - } - - std::filesystem::path path = std::filesystem::current_path() /= "banlist.config"; // Get current path + banlist.config - std::ofstream outFile(path, std::ios::out | std::ios::trunc); // Write config file.. - - outFile << out.dump(4); // Dump it into config file.. - outFile.close(); // Close the file handle. - } - - void AddEntry(std::string ipAddress, std::int64_t originID) - { - if (!ipAddress.empty() && originID > 0) // Check if args are valid. - { - banList.push_back(std::make_pair(ipAddress, originID)); // Push it back into the vector. - } - } - - void DeleteEntry(std::string ipAddress, std::int64_t originID) - { - for (int i = 0; i < banList.size(); i++) // Loop through vector. - { - if (ipAddress.compare(banList[i].first) == NULL || originID == banList[i].second) // Do any entries match our vector? - { - banList.erase(banList.begin() + i); // If so erase that vector element. - } - } - } - - void AddConnectionRefuse(std::string error, int userID) - { - if (refuseList.empty()) - { - refuseList.push_back(std::make_pair(error, userID)); - } - else - { - for (int i = 0; i < refuseList.size(); i++) // Loop through vector. - { - if (refuseList[i].second != userID) // Do any entries match our vector? - { - refuseList.push_back(std::make_pair(error, userID)); // Push it back into the vector. - } - } - } - } - - void DeleteConnectionRefuse(int userID) - { - for (int i = 0; i < refuseList.size(); i++) // Loop through vector. - { - if (refuseList[i].second == userID) // Do any entries match our vector? - { - refuseList.erase(refuseList.begin() + i); // If so erase that vector element. - } - } - } - - bool IsBanned(std::string ipAddress, std::int64_t originID) - { - for (int i = 0; i < banList.size(); i++) - { - std::string ip = banList[i].first; // Get first pair entry. - std::int64_t origin = banList[i].second; // Get second pair entry. - - if (ip.empty()) // Check if ip is empty. - continue; - - if (origin <= 0) // Is originID below 0? - continue; - - if (ip.compare(ipAddress) == NULL) // Do they match? - return true; - - if (originID == origin) // Do they match? - return true; - } - - return false; - } - - bool IsRefuseListValid() - { - return !refuseList.empty(); - } - - bool IsBanListValid() - { - return !banList.empty(); - } - - std::vector> refuseList = {};; -private: - std::vector> banList = {}; -}; diff --git a/shared/include/utility.h b/shared/include/utility.h deleted file mode 100644 index 2f4704db..00000000 --- a/shared/include/utility.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -///////////////////////////////////////////////////////////////////////////// -// Internals -BOOL FileExists(LPCTSTR szPath); -MODULEINFO GetModuleInfo(const char* szModule); - -///////////////////////////////////////////////////////////////////////////// -// Utility -void DbgPrint(LPCSTR sFormat, ...); -void HexDump(const char* szHeader, int nFunc, const void* pData, int nSize); -void PatchNetVarConVar(); - -///////////////////////////////////////////////////////////////////////////// -// Loggers -inline auto g_spddefault_logger = spdlog::basic_logger_mt("default_logger", "platform\\log\\default_r5.log"); -inline auto g_spdnetchan_logger = spdlog::basic_logger_mt("netchan_logger", "platform\\log\\netchan_r5.log"); - -///////////////////////////////////////////////////////////////////////////// - -std::string base64_encode(const std::string& in); -std::string base64_decode(const std::string& in); \ No newline at end of file diff --git a/shared/utility.cpp b/shared/utility.cpp deleted file mode 100644 index 3d6e1f30..00000000 --- a/shared/utility.cpp +++ /dev/null @@ -1,193 +0,0 @@ -#include "pch.h" -#include "utility.h" - -/*----------------------------------------------------------------------------- - * _utility.cpp - *-----------------------------------------------------------------------------*/ - - ////////////////////////////////////////////////////////////////////////////// - // -BOOL FileExists(LPCTSTR szPath) -{ - DWORD dwAttrib = GetFileAttributes(szPath); - - return (dwAttrib != INVALID_FILE_ATTRIBUTES && - !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); -} - -/////////////////////////////////////////////////////////////////////////////// -// For getting information about the specified module -MODULEINFO GetModuleInfo(const char* szModule) -{ - MODULEINFO modinfo = { 0 }; - HMODULE hModule = GetModuleHandle(szModule); - if (hModule == 0) - { - return modinfo; - } - GetModuleInformation(GetCurrentProcess(), hModule, &modinfo, sizeof(MODULEINFO)); - return modinfo; -} - -/////////////////////////////////////////////////////////////////////////////// -// -void DbgPrint(LPCSTR sFormat, ...) -{ - CHAR sBuffer[512] = { 0 }; - va_list sArgs; - - // Get the variable arg pointer - va_start(sArgs, sFormat); - - // Format print the string - int length = vsnprintf(sBuffer, sizeof(sBuffer), sFormat, sArgs); - va_end(sArgs); - - // Output the string to the debugger - OutputDebugString(sBuffer); -} - -void PatchNetVarConVar() -{ - CHAR convarPtr[] = "\x72\x3a\x73\x76\x72\x75\x73\x7a\x7a\x03\x04"; - PCHAR curr = convarPtr; - while (*curr) { - *curr ^= 'B'; - ++curr; - } - - std::int64_t cvaraddr = 0; - std::stringstream ss; - ss << std::hex << std::string(convarPtr); - ss >> cvaraddr; - void* cvarptr = reinterpret_cast(cvaraddr); - - if (*reinterpret_cast(cvarptr) == 144) - { - std::uint8_t padding[] = - { - 0x48, 0x8B, 0x45, 0x58, 0xC7, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - - void* Callback = nullptr; - VirtualAlloc(Callback, 10, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); - memcpy(Callback, (void*)padding, 9); - reinterpret_cast(Callback)(); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// For dumping data from a buffer to a file on the disk -void HexDump(const char* szHeader, int nFunc, const void* pData, int nSize) -{ - static std::atomic i, j, k = 0; - static char ascii[17] = { 0 }; - static auto logger = spdlog::get("default_logger"); - auto pattern = std::make_unique("%v", spdlog::pattern_time_type::local, std::string("")); - - // Loop until the function returned to the first caller - while (k == 1) { /*Sleep(75);*/ } - - k = 1; - ascii[16] = '\0'; - - // Add new loggers here to replace the placeholder - if (nFunc == 0) { logger = g_spdnetchan_logger; } - - // Add timestamp - logger->set_level(spdlog::level::trace); - logger->set_pattern("%v [%H:%M:%S.%f]\n"); - logger->trace("---------------------------------------------------------"); - - // Disable EOL and create block header - logger->set_formatter(std::move(pattern)); - logger->trace("{:s} ---- LEN BYTES: {}\n:\n", szHeader, nSize); - logger->trace("-------- 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF\n"); - - // Output the buffer to the file - for (i = 0; i < nSize; i++) - { - if (i % nSize == 0) { logger->trace(" 0x{:04X} ", i); } - logger->trace("{:02x} ", ((unsigned char*)pData)[i]); - - if (((unsigned char*)pData)[i] >= ' ' && ((unsigned char*)pData)[i] <= '~') { ascii[i % 16] = ((unsigned char*)pData)[i]; } - else { ascii[i % 16] = '.'; } - - if ((i + 1) % 8 == 0 || i + 1 == nSize) - { - logger->trace(" "); - - if ((i + 1) % 16 == 0) - { - if (i + 1 == nSize) - { - logger->trace("{:s}\n", ascii); - logger->trace("---------------------------------------------------------------------------\n"); - logger->trace("\n"); - } - else - { - i++; - logger->trace("{:s}\n ", ascii); - logger->trace("0x{:04X} ", i--); - } - } - else if (i + 1 == nSize) - { - ascii[(i + 1) % 16] = '\0'; - if ((i + 1) % 16 <= 8) - { - logger->trace(" "); - } - for (j = (i + 1) % 16; j < 16; j++) - { - logger->trace(" "); - } - logger->trace("{:s}\n", ascii); - logger->trace("---------------------------------------------------------------------------\n"); - logger->trace("\n"); - } - } - } - k = 0; - /////////////////////////////////////////////////////////////////////////// -} - -///// BASE 64 -std::string base64_encode(const std::string& in) { - - std::string out; - - int val = 0, valb = -6; - for (unsigned char c : in) { - val = (val << 8) + c; - valb += 8; - while (valb >= 0) { - out.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(val >> valb) & 0x3F]); - valb -= 6; - } - } - if (valb > -6) out.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[((val << 8) >> (valb + 8)) & 0x3F]); - while (out.size() % 4) out.push_back('='); - return out; -} - -std::string base64_decode(const std::string& in) { - - std::string out; - - std::vector T(256, -1); - for (int i = 0; i < 64; i++) T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i; - - int val = 0, valb = -8; - for (unsigned char c : in) { - if (T[c] == -1) break; - val = (val << 6) + T[c]; - valb += 6; - if (valb >= 0) { - out.push_back(char((val >> valb) & 0xFF)); - valb -= 8; - } - } - return out; -} \ No newline at end of file