feat(overlay): damage logger, menu, and fps unlocker
credit to [kitkat](https://github.com/kitkat-multiverse/Starlight-Patch/commit/a968b280d410cc44dcdbc92feda82174674d41b3)
This commit is contained in:
1 parent
2af3510615
commit
696e1c0b9a
10 files changed
+1521
-5
No files matched your search
@@ -1,3 +1,2 @@
|
||||
.idea
|
||||
/target
|
||||
Cargo.lock
|
||||
Generated
+756
@@ -0,0 +1,756 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3eb0f42d6c360dc3f8a821f6bf2fdea7f72bfd36b3076eb0e6d1e9e0752fff4"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600"
|
||||
|
||||
[[package]]
|
||||
name = "chlorine"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c00d31b1d19317b4777ec879192d3745bd97d05262b4b19cb1dda284b9d22f19"
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e0f1c7c3a72c66fd80abe965175f7523475c0489a87d3ff9d6e8c87d87a9d2d"
|
||||
|
||||
[[package]]
|
||||
name = "hk4e-patch"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cc",
|
||||
"hudhook",
|
||||
"ilhook",
|
||||
"lazy_static",
|
||||
"libloading",
|
||||
"patternscan",
|
||||
"serde_json",
|
||||
"winapi",
|
||||
"windows 0.54.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hudhook"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d7f3def5b8fafd0bd3acf6a63fdbe5c637963ce1d207192ef3c47bd4f28b394"
|
||||
dependencies = [
|
||||
"bitflags 2.13.2",
|
||||
"cc",
|
||||
"imgui",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"windows 0.62.2",
|
||||
"windows-numerics",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iced-x86"
|
||||
version = "1.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c447cff8c7f384a7d4f741cfcff32f75f3ad02b406432e8d6c878d56b1edf6b"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ilhook"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c40cf69be52a2e8e2a375673ecfde90b7790aa9d8aa55dd463dc7e8faacb89c9"
|
||||
dependencies = [
|
||||
"bitflags 2.13.2",
|
||||
"iced-x86",
|
||||
"libc",
|
||||
"regex",
|
||||
"thiserror",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "imgui"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8addafa5cecf0515812226e806913814e02ce38d10215778082af5174abe5669"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"cfg-if",
|
||||
"imgui-sys",
|
||||
"mint",
|
||||
"parking_lot",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "imgui-sys"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ead193f9f4b60398e8b8f4ab1483e2321640d87aeebdaa3e5f44c55633ccd804"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"chlorine",
|
||||
"mint",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.189"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
|
||||
dependencies = [
|
||||
"regex-automata",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
||||
|
||||
[[package]]
|
||||
name = "mint"
|
||||
version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "patternscan"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbf9ac94ae7c3d7f743ec57e0b6b05077631c1a90c6cea9b162a69efa6d6bbde"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.107"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||
dependencies = [
|
||||
"bitflags 2.13.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.229"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.229"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.229"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.151"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.16.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9395f0f0eee849a9b707b2f06bb92a6a422090e2123bb2ef8e87a0e61892a8e"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.119"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "3.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
|
||||
dependencies = [
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319"
|
||||
dependencies = [
|
||||
"matchers",
|
||||
"once_cell",
|
||||
"regex-automata",
|
||||
"thread_local",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.54.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49"
|
||||
dependencies = [
|
||||
"windows-core 0.54.0",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.62.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
|
||||
dependencies = [
|
||||
"windows-collections",
|
||||
"windows-core 0.62.2",
|
||||
"windows-future",
|
||||
"windows-numerics",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-collections"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610"
|
||||
dependencies = [
|
||||
"windows-core 0.62.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.54.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65"
|
||||
dependencies = [
|
||||
"windows-result 0.1.2",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.62.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-link",
|
||||
"windows-result 0.4.1",
|
||||
"windows-strings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-future"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
|
||||
dependencies = [
|
||||
"windows-core 0.62.2",
|
||||
"windows-link",
|
||||
"windows-threading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.59.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-numerics"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26"
|
||||
dependencies = [
|
||||
"windows-core 0.62.2",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets 0.53.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm 0.52.6",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows_aarch64_gnullvm 0.53.1",
|
||||
"windows_aarch64_msvc 0.53.1",
|
||||
"windows_i686_gnu 0.53.1",
|
||||
"windows_i686_gnullvm 0.53.1",
|
||||
"windows_i686_msvc 0.53.1",
|
||||
"windows_x86_64_gnu 0.53.1",
|
||||
"windows_x86_64_gnullvm 0.53.1",
|
||||
"windows_x86_64_msvc 0.53.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-threading"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
|
||||
@@ -20,8 +20,11 @@ windows = { version = "0.54.0", features = [
|
||||
"Win32_System_Console",
|
||||
"Win32_System_Threading",
|
||||
"Win32_System_Memory",
|
||||
"Win32_UI_Input_KeyboardAndMouse",
|
||||
] }
|
||||
winapi = { version = "0.3",features = ["winnt","libloaderapi","memoryapi","winuser"]}
|
||||
hudhook = { version = "0.9", default-features = false, features = ["dx11"] }
|
||||
serde_json = "1"
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0.90"
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
# hk4e-patch-universal
|
||||
|
||||
Genshin Impact encryption patch that should work on 6.5 and forward for a while
|
||||
Genshin Impact encryption patch made mainly for [LunaGC_7.1.0](https://github.com/capyb2222/LunaGC_7.1.0). The patch should also work with other Grasscutter-based servers.
|
||||
|
||||
## Features
|
||||
- Damage logger, toggled with `Ctrl+K`
|
||||
- Menu, opened with `Ctrl+L`
|
||||
- FPS unlocker (adjustable)
|
||||
|
||||
## Credits
|
||||
[xeondev](https://git.xeondev.com/reversedrooms/hk4e-patch) for the original patch and [oureveryday](https://github.com/oureveryday/) for the original hk4e-patch-universal
|
||||
[xeondev](https://git.xeondev.com/reversedrooms/hk4e-patch) for the original patch, [oureveryday](https://github.com/oureveryday/) for the original hk4e-patch-universal, and [kitkat](https://github.com/kitkat-multiverse/Starlight-Patch) for the base patch and the FPS unlocker.
|
||||
@@ -0,0 +1,50 @@
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
|
||||
|
||||
use serde_json::{json, Value};
|
||||
|
||||
const FILE_NAME: &str = "lunagc-patch.json";
|
||||
const DEFAULT_FPS: i32 = 120;
|
||||
|
||||
pub static SHOW_PANEL: AtomicBool = AtomicBool::new(true);
|
||||
pub static FPS_ENABLED: AtomicBool = AtomicBool::new(true);
|
||||
pub static FPS_TARGET: AtomicI32 = AtomicI32::new(DEFAULT_FPS);
|
||||
|
||||
fn path() -> Option<PathBuf> {
|
||||
let mut path = std::env::current_exe().ok()?;
|
||||
path.pop();
|
||||
Some(path.join(FILE_NAME))
|
||||
}
|
||||
|
||||
pub fn load() {
|
||||
let Some(text) = path().and_then(|p| std::fs::read_to_string(p).ok()) else {
|
||||
return;
|
||||
};
|
||||
let Ok(value) = serde_json::from_str::<Value>(&text) else {
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some(show) = value["showDamagePanel"].as_bool() {
|
||||
SHOW_PANEL.store(show, Ordering::Relaxed);
|
||||
}
|
||||
if let Some(enabled) = value["fpsUnlock"].as_bool() {
|
||||
FPS_ENABLED.store(enabled, Ordering::Relaxed);
|
||||
}
|
||||
if let Some(target) = value["fpsTarget"].as_i64() {
|
||||
FPS_TARGET.store(target.clamp(0, 1000) as i32, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save() {
|
||||
let value = json!({
|
||||
"showDamagePanel": SHOW_PANEL.load(Ordering::Relaxed),
|
||||
"fpsUnlock": FPS_ENABLED.load(Ordering::Relaxed),
|
||||
"fpsTarget": FPS_TARGET.load(Ordering::Relaxed),
|
||||
});
|
||||
|
||||
if let (Some(path), Ok(text)) = (path(), serde_json::to_string_pretty(&value)) {
|
||||
if let Err(e) = std::fs::write(&path, text) {
|
||||
crate::plog!("Failed to save {}: {e}", path.display());
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
-2
@@ -1,5 +1,6 @@
|
||||
#![feature(str_from_utf16_endian)]
|
||||
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::{sync::RwLock};
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
@@ -10,14 +11,16 @@ use windows::Win32::{Foundation::HINSTANCE, System::LibraryLoader::GetModuleFile
|
||||
use std::ffi::CStr;
|
||||
use std::path::Path;
|
||||
|
||||
mod config;
|
||||
mod log;
|
||||
mod proxy;
|
||||
mod interceptor;
|
||||
mod marshal;
|
||||
mod modules;
|
||||
mod overlay;
|
||||
mod util;
|
||||
|
||||
use crate::modules::{Http, MhyContext, ModuleManager, Security, WinHttp};
|
||||
use crate::modules::{Fps, Http, MhyContext, ModuleManager, Security, WinHttp};
|
||||
|
||||
unsafe fn thread_func() {
|
||||
let mut module_manager = MODULE_MANAGER.write().unwrap();
|
||||
@@ -52,17 +55,25 @@ unsafe fn thread_func() {
|
||||
// the account sdk uses winhttp, not the C# path above
|
||||
module_manager.enable(MhyContext::<WinHttp>::new(&exe_name));
|
||||
|
||||
config::load();
|
||||
module_manager.enable(MhyContext::<Fps>::new(&exe_name));
|
||||
|
||||
crate::plog!("Successfully initialized!");
|
||||
|
||||
overlay::start(MODULE.load(Ordering::Relaxed));
|
||||
}
|
||||
|
||||
static MODULE: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
lazy_static! {
|
||||
static ref MODULE_MANAGER: RwLock<ModuleManager> = RwLock::new(ModuleManager::default());
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
unsafe extern "system" fn DllMain(_: HINSTANCE, call_reason: u32, _: *mut ()) -> bool {
|
||||
unsafe extern "system" fn DllMain(module: HINSTANCE, call_reason: u32, _: *mut ()) -> bool {
|
||||
if call_reason == DLL_PROCESS_ATTACH {
|
||||
MODULE.store(module.0 as usize, Ordering::Relaxed);
|
||||
log::start_session();
|
||||
|
||||
// here, not on the thread: the game may call the exports once DllMain returns
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
|
||||
use super::{MhyContext, MhyModule, ModuleType};
|
||||
use crate::config::{FPS_ENABLED, FPS_TARGET};
|
||||
use crate::util;
|
||||
use anyhow::Result;
|
||||
use ilhook::x64::Registers;
|
||||
|
||||
const SET_TARGET_FPS: &str = "E8 ? ? ? ? E8 ? ? ? ? 83 F8 1F 0F 9C 05";
|
||||
const GAME_DEFAULT_FPS: i32 = 60;
|
||||
|
||||
static SET_TARGET_FPS_ADDR: AtomicUsize = AtomicUsize::new(0);
|
||||
static BYPASS: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
pub struct Fps;
|
||||
|
||||
impl MhyModule for MhyContext<Fps> {
|
||||
unsafe fn init(&mut self) -> Result<()> {
|
||||
let call = util::pattern_scan_il2cpp(self.assembly_name, SET_TARGET_FPS)
|
||||
.or_else(|| util::pattern_scan_code(self.assembly_name, SET_TARGET_FPS));
|
||||
|
||||
let Some(call) = call else {
|
||||
crate::plog!("Failed to find set_target_fps");
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let offset = std::ptr::read_unaligned(call.add(1) as *const i32);
|
||||
let addr = (call as isize + 5 + offset as isize) as usize;
|
||||
crate::plog!("set_target_fps: {:x}", addr);
|
||||
|
||||
self.interceptor.attach(addr, on_set_target_fps)?;
|
||||
SET_TARGET_FPS_ADDR.store(addr, Ordering::Relaxed);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn de_init(&mut self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_module_type(&self) -> super::ModuleType {
|
||||
ModuleType::Fps
|
||||
}
|
||||
}
|
||||
|
||||
pub fn available() -> bool {
|
||||
SET_TARGET_FPS_ADDR.load(Ordering::Relaxed) != 0
|
||||
}
|
||||
|
||||
fn target() -> i32 {
|
||||
match FPS_TARGET.load(Ordering::Relaxed) {
|
||||
value if value <= 0 => -1,
|
||||
value => value,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply() {
|
||||
let addr = SET_TARGET_FPS_ADDR.load(Ordering::Relaxed);
|
||||
if addr == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let set_target_fps: unsafe extern "win64" fn(i32) = unsafe { std::mem::transmute(addr) };
|
||||
|
||||
if FPS_ENABLED.load(Ordering::Relaxed) {
|
||||
unsafe { set_target_fps(target()) };
|
||||
} else {
|
||||
BYPASS.store(true, Ordering::Relaxed);
|
||||
unsafe { set_target_fps(GAME_DEFAULT_FPS) };
|
||||
BYPASS.store(false, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "win64" fn on_set_target_fps(reg: *mut Registers, _: usize) {
|
||||
if FPS_ENABLED.load(Ordering::Relaxed) && !BYPASS.load(Ordering::Relaxed) {
|
||||
(*reg).rcx = target() as u32 as u64;
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,14 @@ use anyhow::Result;
|
||||
use crate::interceptor::Interceptor;
|
||||
|
||||
mod ccp_blocker;
|
||||
pub mod fps;
|
||||
mod http;
|
||||
mod misc;
|
||||
mod security;
|
||||
mod winhttp;
|
||||
|
||||
pub use ccp_blocker::CcpBlocker;
|
||||
pub use fps::Fps;
|
||||
pub use http::Http;
|
||||
pub use misc::Misc;
|
||||
pub use security::Security;
|
||||
@@ -47,6 +49,7 @@ pub enum ModuleType {
|
||||
Misc,
|
||||
CcpBlocker,
|
||||
WinHttp,
|
||||
Fps,
|
||||
}
|
||||
|
||||
pub trait MhyModule {
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
use std::io::{Read, Write};
|
||||
use std::net::{SocketAddr, TcpStream};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Mutex;
|
||||
use std::time::Duration;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
const HOST: &str = "127.0.0.1";
|
||||
const PORT: u16 = 8088;
|
||||
const POLL: Duration = Duration::from_millis(250);
|
||||
const TIMEOUT: Duration = Duration::from_millis(800);
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Row {
|
||||
pub name: String,
|
||||
pub owner: String,
|
||||
pub element: u32,
|
||||
pub dealt: f64,
|
||||
pub taken: f64,
|
||||
pub max_hit: f64,
|
||||
pub hits: u64,
|
||||
pub dps: f64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Hit {
|
||||
pub ago: f64,
|
||||
pub name: String,
|
||||
pub target: String,
|
||||
pub damage: f64,
|
||||
pub element: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Snapshot {
|
||||
pub connected: bool,
|
||||
pub online: bool,
|
||||
pub active: bool,
|
||||
pub duration: f64,
|
||||
pub total: f64,
|
||||
pub total_taken: f64,
|
||||
pub dps: f64,
|
||||
pub rows: Vec<Row>,
|
||||
pub hits: Vec<Hit>,
|
||||
}
|
||||
|
||||
static SNAPSHOT: Mutex<Option<Snapshot>> = Mutex::new(None);
|
||||
static RESET: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
pub fn snapshot() -> Snapshot {
|
||||
SNAPSHOT
|
||||
.lock()
|
||||
.ok()
|
||||
.and_then(|s| s.clone())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn request_reset() {
|
||||
RESET.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
fn request(method: &str, path: &str) -> Option<String> {
|
||||
let addr: SocketAddr = format!("{HOST}:{PORT}").parse().ok()?;
|
||||
let mut stream = TcpStream::connect_timeout(&addr, TIMEOUT).ok()?;
|
||||
stream.set_read_timeout(Some(TIMEOUT)).ok()?;
|
||||
stream.set_write_timeout(Some(TIMEOUT)).ok()?;
|
||||
|
||||
let request = format!(
|
||||
"{method} {path} HTTP/1.1\r\nHost: {HOST}:{PORT}\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"
|
||||
);
|
||||
stream.write_all(request.as_bytes()).ok()?;
|
||||
|
||||
let mut raw = Vec::new();
|
||||
let _ = stream.read_to_end(&mut raw);
|
||||
let text = String::from_utf8_lossy(&raw);
|
||||
|
||||
let body = text.split_once("\r\n\r\n")?.1;
|
||||
let start = body.find('{')?;
|
||||
let end = body.rfind('}')?;
|
||||
(end >= start).then(|| body[start..=end].to_string())
|
||||
}
|
||||
|
||||
fn str_of(v: &Value, key: &str) -> String {
|
||||
v[key].as_str().unwrap_or_default().to_string()
|
||||
}
|
||||
|
||||
fn parse(body: &str) -> Option<Snapshot> {
|
||||
let v: Value = serde_json::from_str(body).ok()?;
|
||||
|
||||
let rows = v["rows"]
|
||||
.as_array()
|
||||
.map(|rows| {
|
||||
rows.iter()
|
||||
.map(|r| Row {
|
||||
name: str_of(r, "name"),
|
||||
owner: str_of(r, "owner"),
|
||||
element: r["element"].as_u64().unwrap_or(0) as u32,
|
||||
dealt: r["dealt"].as_f64().unwrap_or(0.0),
|
||||
taken: r["taken"].as_f64().unwrap_or(0.0),
|
||||
max_hit: r["maxHit"].as_f64().unwrap_or(0.0),
|
||||
hits: r["hits"].as_u64().unwrap_or(0),
|
||||
dps: r["dps"].as_f64().unwrap_or(0.0),
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let hits = v["hits"]
|
||||
.as_array()
|
||||
.map(|hits| {
|
||||
hits.iter()
|
||||
.map(|h| Hit {
|
||||
ago: h["ago"].as_f64().unwrap_or(0.0),
|
||||
name: str_of(h, "name"),
|
||||
target: str_of(h, "target"),
|
||||
damage: h["damage"].as_f64().unwrap_or(0.0),
|
||||
element: h["element"].as_u64().unwrap_or(0) as u32,
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
Some(Snapshot {
|
||||
connected: true,
|
||||
online: v["online"].as_bool().unwrap_or(false),
|
||||
active: v["active"].as_bool().unwrap_or(false),
|
||||
duration: v["duration"].as_f64().unwrap_or(0.0),
|
||||
total: v["total"].as_f64().unwrap_or(0.0),
|
||||
total_taken: v["totalTaken"].as_f64().unwrap_or(0.0),
|
||||
dps: v["dps"].as_f64().unwrap_or(0.0),
|
||||
rows,
|
||||
hits,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn start() {
|
||||
std::thread::spawn(|| loop {
|
||||
if RESET.swap(false, Ordering::Relaxed) {
|
||||
let _ = request("POST", "/lunagc/damagelog/reset");
|
||||
}
|
||||
|
||||
let next = request("GET", "/lunagc/damagelog")
|
||||
.and_then(|body| parse(&body))
|
||||
.unwrap_or_default();
|
||||
let connected = next.connected;
|
||||
if let Ok(mut slot) = SNAPSHOT.lock() {
|
||||
*slot = Some(next);
|
||||
}
|
||||
|
||||
std::thread::sleep(if connected { POLL } else { POLL * 8 });
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,458 @@
|
||||
mod client;
|
||||
|
||||
use hudhook::hooks::dx11::ImguiDx11Hooks;
|
||||
use hudhook::imgui::{
|
||||
Condition, Context, FontConfig, FontId, FontSource, StyleColor, StyleVar, Ui, WindowFlags,
|
||||
};
|
||||
use hudhook::windows::Win32::Foundation::HINSTANCE;
|
||||
use hudhook::{Hudhook, ImguiRenderLoop, RenderContext};
|
||||
use windows::Win32::UI::Input::KeyboardAndMouse::GetAsyncKeyState;
|
||||
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use crate::config::{self, FPS_ENABLED, FPS_TARGET, SHOW_PANEL};
|
||||
use crate::modules::fps;
|
||||
use client::{Row, Snapshot};
|
||||
|
||||
const VK_CONTROL: i32 = 0x11;
|
||||
const VK_K: i32 = 0x4B;
|
||||
const VK_L: i32 = 0x4C;
|
||||
|
||||
const FPS_PRESETS: [i32; 5] = [60, 120, 144, 165, 240];
|
||||
|
||||
const FONT_REGULAR: &str = "C:\\Windows\\Fonts\\segoeui.ttf";
|
||||
const FONT_BOLD: &str = "C:\\Windows\\Fonts\\segoeuib.ttf";
|
||||
const FONT_SIZE: f32 = 18.0;
|
||||
|
||||
const TEXT: [f32; 4] = [0.93, 0.90, 0.85, 1.0];
|
||||
const MUTED: [f32; 4] = [0.66, 0.67, 0.72, 1.0];
|
||||
const GOLD: [f32; 4] = [1.0, 0.84, 0.48, 1.0];
|
||||
const TRACK: [f32; 4] = [1.0, 1.0, 1.0, 0.08];
|
||||
const ROW_BG: [f32; 4] = [1.0, 1.0, 1.0, 0.04];
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
enum Tab {
|
||||
Dealt,
|
||||
Taken,
|
||||
Log,
|
||||
}
|
||||
|
||||
pub struct Overlay {
|
||||
menu: bool,
|
||||
menu_held: bool,
|
||||
panel_held: bool,
|
||||
fps_input: i32,
|
||||
tab: Tab,
|
||||
bold: bool,
|
||||
}
|
||||
|
||||
fn element_color(element: u32) -> [f32; 4] {
|
||||
match element {
|
||||
1 => [1.0, 0.45, 0.30, 1.0],
|
||||
2 => [0.30, 0.68, 1.0, 1.0],
|
||||
3 => [0.55, 0.85, 0.25, 1.0],
|
||||
4 => [0.76, 0.52, 1.0, 1.0],
|
||||
5 | 6 => [0.62, 0.90, 0.98, 1.0],
|
||||
7 => [0.42, 0.92, 0.78, 1.0],
|
||||
8 => [1.0, 0.80, 0.32, 1.0],
|
||||
_ => [0.85, 0.85, 0.88, 1.0],
|
||||
}
|
||||
}
|
||||
|
||||
fn group(value: f64) -> String {
|
||||
let digits = (value.max(0.0).round() as u64).to_string();
|
||||
let mut out = String::with_capacity(digits.len() + digits.len() / 3);
|
||||
for (i, c) in digits.chars().enumerate() {
|
||||
if i > 0 && (digits.len() - i) % 3 == 0 {
|
||||
out.push(',');
|
||||
}
|
||||
out.push(c);
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn clock(seconds: f64) -> String {
|
||||
let s = seconds.max(0.0) as u64;
|
||||
format!("{:02}:{:02}", s / 60, s % 60)
|
||||
}
|
||||
|
||||
fn load_font(ctx: &mut Context, path: &str) -> Option<FontId> {
|
||||
let data: &'static [u8] = Box::leak(std::fs::read(path).ok()?.into_boxed_slice());
|
||||
Some(ctx.fonts().add_font(&[FontSource::TtfData {
|
||||
data,
|
||||
size_pixels: FONT_SIZE,
|
||||
config: Some(FontConfig {
|
||||
oversample_h: 2,
|
||||
..FontConfig::default()
|
||||
}),
|
||||
}]))
|
||||
}
|
||||
|
||||
impl Overlay {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
menu: false,
|
||||
menu_held: false,
|
||||
panel_held: false,
|
||||
fps_input: FPS_TARGET.load(Ordering::Relaxed),
|
||||
tab: Tab::Dealt,
|
||||
bold: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_keys(&mut self) {
|
||||
let ctrl = unsafe { GetAsyncKeyState(VK_CONTROL) < 0 };
|
||||
let menu = ctrl && unsafe { GetAsyncKeyState(VK_L) < 0 };
|
||||
let panel = ctrl && unsafe { GetAsyncKeyState(VK_K) < 0 };
|
||||
|
||||
if menu && !self.menu_held {
|
||||
self.menu = !self.menu;
|
||||
}
|
||||
if panel && !self.panel_held {
|
||||
SHOW_PANEL.fetch_xor(true, Ordering::Relaxed);
|
||||
config::save();
|
||||
}
|
||||
|
||||
self.menu_held = menu;
|
||||
self.panel_held = panel;
|
||||
}
|
||||
|
||||
fn set_fps(&mut self, value: i32) {
|
||||
let value = value.clamp(0, 1000);
|
||||
self.fps_input = value;
|
||||
FPS_TARGET.store(value, Ordering::Relaxed);
|
||||
config::save();
|
||||
fps::apply();
|
||||
}
|
||||
|
||||
fn settings(&mut self, ui: &Ui) {
|
||||
self.bold_text(ui, GOLD, "Damage statistics");
|
||||
let mut show = SHOW_PANEL.load(Ordering::Relaxed);
|
||||
if ui.checkbox("Show damage panel", &mut show) {
|
||||
SHOW_PANEL.store(show, Ordering::Relaxed);
|
||||
config::save();
|
||||
}
|
||||
ui.text_colored(MUTED, "Ctrl+K toggles the panel anywhere.");
|
||||
|
||||
ui.spacing();
|
||||
ui.separator();
|
||||
ui.spacing();
|
||||
|
||||
self.bold_text(ui, GOLD, "FPS unlocker");
|
||||
if !fps::available() {
|
||||
ui.text_colored(MUTED, "Not available on this game version.");
|
||||
return;
|
||||
}
|
||||
|
||||
let mut enabled = FPS_ENABLED.load(Ordering::Relaxed);
|
||||
if ui.checkbox("Unlock frame rate", &mut enabled) {
|
||||
FPS_ENABLED.store(enabled, Ordering::Relaxed);
|
||||
config::save();
|
||||
fps::apply();
|
||||
}
|
||||
|
||||
ui.set_next_item_width(140.0);
|
||||
let submitted = ui
|
||||
.input_int("Target FPS", &mut self.fps_input)
|
||||
.enter_returns_true(true)
|
||||
.build();
|
||||
if submitted {
|
||||
self.set_fps(self.fps_input);
|
||||
}
|
||||
|
||||
for preset in FPS_PRESETS {
|
||||
if ui.button(preset.to_string()) {
|
||||
self.set_fps(preset);
|
||||
}
|
||||
ui.same_line();
|
||||
}
|
||||
if ui.button("Unlimited") {
|
||||
self.set_fps(0);
|
||||
}
|
||||
|
||||
let current = match FPS_TARGET.load(Ordering::Relaxed) {
|
||||
0 => "unlimited".to_string(),
|
||||
value => value.to_string(),
|
||||
};
|
||||
ui.text_colored(
|
||||
MUTED,
|
||||
format!("Current target: {current}. Press Enter to apply a typed value."),
|
||||
);
|
||||
}
|
||||
|
||||
fn panel(&mut self, ui: &Ui) {
|
||||
let snap = client::snapshot();
|
||||
let display = ui.io().display_size;
|
||||
let _min = ui.push_style_var(StyleVar::WindowMinSize([300.0, 90.0]));
|
||||
|
||||
ui.window("##lunagc-damage")
|
||||
.position([display[0] - 400.0, 150.0], Condition::FirstUseEver)
|
||||
.size([370.0, 0.0], Condition::FirstUseEver)
|
||||
.flags(
|
||||
WindowFlags::NO_TITLE_BAR
|
||||
| WindowFlags::NO_SCROLLBAR
|
||||
| WindowFlags::NO_COLLAPSE
|
||||
| WindowFlags::ALWAYS_AUTO_RESIZE
|
||||
| WindowFlags::NO_FOCUS_ON_APPEARING
|
||||
| WindowFlags::NO_NAV,
|
||||
)
|
||||
.build(|| {
|
||||
ui.dummy([342.0, 0.0]);
|
||||
self.header(ui, &snap);
|
||||
ui.separator();
|
||||
match self.tab {
|
||||
Tab::Dealt => self.rows(ui, &snap, false),
|
||||
Tab::Taken => self.rows(ui, &snap, true),
|
||||
Tab::Log => self.log(ui, &snap),
|
||||
}
|
||||
self.footer(ui, &snap);
|
||||
});
|
||||
}
|
||||
|
||||
fn bold_font(&self, ui: &Ui) -> Option<FontId> {
|
||||
self.bold.then(|| ui.fonts().fonts().get(1).copied()).flatten()
|
||||
}
|
||||
|
||||
fn bold_text(&self, ui: &Ui, color: [f32; 4], text: &str) {
|
||||
let _font = self.bold_font(ui).map(|id| ui.push_font(id));
|
||||
ui.text_colored(color, text);
|
||||
}
|
||||
|
||||
fn tab_button(&mut self, ui: &Ui, label: &str, tab: Tab) {
|
||||
let selected = self.tab == tab;
|
||||
let _text = ui.push_style_color(StyleColor::Text, if selected { GOLD } else { MUTED });
|
||||
let _bg = ui.push_style_color(
|
||||
StyleColor::Button,
|
||||
if selected { [1.0, 0.84, 0.48, 0.16] } else { [1.0, 1.0, 1.0, 0.05] },
|
||||
);
|
||||
if ui.button(label) {
|
||||
self.tab = tab;
|
||||
}
|
||||
}
|
||||
|
||||
fn header(&mut self, ui: &Ui, snap: &Snapshot) {
|
||||
self.bold_text(ui, GOLD, "Damage Statistics");
|
||||
|
||||
let status = if !snap.connected {
|
||||
"server offline"
|
||||
} else if !snap.online {
|
||||
"not in game"
|
||||
} else if snap.active {
|
||||
"in combat"
|
||||
} else {
|
||||
"idle"
|
||||
};
|
||||
let width = ui.calc_text_size(status)[0];
|
||||
ui.same_line_with_pos(ui.window_size()[0] - width - 14.0);
|
||||
ui.text_colored(if snap.active { GOLD } else { MUTED }, status);
|
||||
|
||||
self.tab_button(ui, "Dealt", Tab::Dealt);
|
||||
ui.same_line();
|
||||
self.tab_button(ui, "Taken", Tab::Taken);
|
||||
ui.same_line();
|
||||
self.tab_button(ui, "Log", Tab::Log);
|
||||
|
||||
let width = ui.calc_text_size("Reset")[0] + 20.0;
|
||||
ui.same_line_with_pos(ui.window_size()[0] - width - 14.0);
|
||||
let _text = ui.push_style_color(StyleColor::Text, MUTED);
|
||||
if ui.button_with_size("Reset", [width, 0.0]) {
|
||||
client::request_reset();
|
||||
}
|
||||
}
|
||||
|
||||
fn rows(&self, ui: &Ui, snap: &Snapshot, taken: bool) {
|
||||
let value = |row: &Row| if taken { row.taken } else { row.dealt };
|
||||
let mut rows: Vec<&Row> = snap.rows.iter().filter(|r| value(r) > 0.0).collect();
|
||||
rows.sort_by(|a, b| value(b).total_cmp(&value(a)));
|
||||
|
||||
if rows.is_empty() {
|
||||
ui.text_colored(MUTED, if taken { "No damage taken yet." } else { "No damage dealt yet." });
|
||||
return;
|
||||
}
|
||||
|
||||
let total: f64 = rows.iter().map(|r| value(r)).sum();
|
||||
let top = value(rows[0]);
|
||||
let multiplayer = rows.iter().any(|r| r.owner != rows[0].owner);
|
||||
let line = ui.text_line_height();
|
||||
let draw = ui.get_window_draw_list();
|
||||
|
||||
for (rank, row) in rows.iter().enumerate() {
|
||||
let color = element_color(row.element);
|
||||
let amount = value(row);
|
||||
let origin = ui.cursor_screen_pos();
|
||||
let width = ui.content_region_avail()[0];
|
||||
let detail = !taken;
|
||||
let height = line * if detail { 2.0 } else { 1.0 } + 16.0;
|
||||
|
||||
draw.add_rect(origin, [origin[0] + width, origin[1] + height], ROW_BG)
|
||||
.filled(true)
|
||||
.rounding(6.0)
|
||||
.build();
|
||||
draw.add_rect(origin, [origin[0] + 4.0, origin[1] + height], color)
|
||||
.filled(true)
|
||||
.rounding(2.0)
|
||||
.build();
|
||||
|
||||
let left = origin[0] + 12.0;
|
||||
let right = origin[0] + width - 8.0;
|
||||
let y = origin[1] + 4.0;
|
||||
|
||||
let name = if multiplayer {
|
||||
format!("{} {} ({})", rank + 1, row.name, row.owner)
|
||||
} else {
|
||||
format!("{} {}", rank + 1, row.name)
|
||||
};
|
||||
{
|
||||
let _font = self.bold_font(ui).map(|id| ui.push_font(id));
|
||||
draw.add_text([left, y], TEXT, &name);
|
||||
}
|
||||
|
||||
let share = if total > 0.0 { amount / total * 100.0 } else { 0.0 };
|
||||
let share_text = format!("{share:.1}%");
|
||||
let share_width = ui.calc_text_size(&share_text)[0];
|
||||
draw.add_text([right - share_width, y], MUTED, &share_text);
|
||||
|
||||
let amount_text = group(amount);
|
||||
{
|
||||
let _font = self.bold_font(ui).map(|id| ui.push_font(id));
|
||||
let amount_width = ui.calc_text_size(&amount_text)[0];
|
||||
draw.add_text([right - 58.0 - amount_width, y], color, &amount_text);
|
||||
}
|
||||
|
||||
let mut bar_y = y + line + 3.0;
|
||||
if detail {
|
||||
let stats = format!(
|
||||
"{}/s {} hits max {}",
|
||||
group(row.dps),
|
||||
row.hits,
|
||||
group(row.max_hit)
|
||||
);
|
||||
draw.add_text([left, y + line], MUTED, &stats);
|
||||
bar_y += line;
|
||||
}
|
||||
|
||||
let fill = if top > 0.0 { (amount / top) as f32 } else { 0.0 };
|
||||
draw.add_rect([left, bar_y], [right, bar_y + 4.0], TRACK)
|
||||
.filled(true)
|
||||
.rounding(2.0)
|
||||
.build();
|
||||
draw.add_rect(
|
||||
[left, bar_y],
|
||||
[left + (right - left) * fill.clamp(0.02, 1.0), bar_y + 4.0],
|
||||
color,
|
||||
)
|
||||
.filled(true)
|
||||
.rounding(2.0)
|
||||
.build();
|
||||
|
||||
ui.dummy([width, height]);
|
||||
}
|
||||
}
|
||||
|
||||
fn log(&self, ui: &Ui, snap: &Snapshot) {
|
||||
if snap.hits.is_empty() {
|
||||
ui.text_colored(MUTED, "No hits yet.");
|
||||
return;
|
||||
}
|
||||
|
||||
let right = ui.window_size()[0] - 14.0;
|
||||
for hit in snap.hits.iter().rev().take(14) {
|
||||
ui.text_colored(MUTED, format!("{:>4.0}s", hit.ago));
|
||||
ui.same_line();
|
||||
ui.text_colored(TEXT, &hit.name);
|
||||
ui.same_line();
|
||||
ui.text_colored(MUTED, format!("> {}", hit.target));
|
||||
|
||||
let amount = group(hit.damage);
|
||||
let width = ui.calc_text_size(&amount)[0];
|
||||
ui.same_line_with_pos(right - width);
|
||||
ui.text_colored(element_color(hit.element), amount);
|
||||
}
|
||||
}
|
||||
|
||||
fn footer(&self, ui: &Ui, snap: &Snapshot) {
|
||||
ui.separator();
|
||||
ui.text_colored(MUTED, "Total");
|
||||
ui.same_line();
|
||||
let total = if self.tab == Tab::Taken { snap.total_taken } else { snap.total };
|
||||
self.bold_text(ui, TEXT, &group(total));
|
||||
|
||||
let tail = format!("{}/s {}", group(snap.dps), clock(snap.duration));
|
||||
let width = ui.calc_text_size(&tail)[0];
|
||||
ui.same_line_with_pos(ui.window_size()[0] - width - 14.0);
|
||||
ui.text_colored(MUTED, tail);
|
||||
}
|
||||
}
|
||||
|
||||
impl ImguiRenderLoop for Overlay {
|
||||
fn initialize<'a>(&'a mut self, ctx: &mut Context, _: &'a mut dyn RenderContext) {
|
||||
ctx.set_ini_filename(None);
|
||||
|
||||
if load_font(ctx, FONT_REGULAR).is_some() {
|
||||
self.bold = load_font(ctx, FONT_BOLD).is_some();
|
||||
}
|
||||
|
||||
let style = ctx.style_mut();
|
||||
style.window_rounding = 10.0;
|
||||
style.window_border_size = 1.0;
|
||||
style.window_padding = [14.0, 12.0];
|
||||
style.frame_rounding = 6.0;
|
||||
style.frame_padding = [10.0, 3.0];
|
||||
style.item_spacing = [6.0, 6.0];
|
||||
style.colors[StyleColor::WindowBg as usize] = [0.07, 0.08, 0.11, 0.82];
|
||||
style.colors[StyleColor::Border as usize] = [1.0, 0.84, 0.48, 0.25];
|
||||
style.colors[StyleColor::Separator as usize] = [1.0, 1.0, 1.0, 0.10];
|
||||
style.colors[StyleColor::Text as usize] = TEXT;
|
||||
style.colors[StyleColor::Button as usize] = [1.0, 1.0, 1.0, 0.05];
|
||||
style.colors[StyleColor::ButtonHovered as usize] = [1.0, 0.84, 0.48, 0.22];
|
||||
style.colors[StyleColor::ButtonActive as usize] = [1.0, 0.84, 0.48, 0.32];
|
||||
style.colors[StyleColor::ResizeGrip as usize] = [0.0, 0.0, 0.0, 0.0];
|
||||
style.colors[StyleColor::TitleBg as usize] = [0.07, 0.08, 0.11, 0.95];
|
||||
style.colors[StyleColor::TitleBgActive as usize] = [0.12, 0.13, 0.17, 0.95];
|
||||
style.colors[StyleColor::FrameBg as usize] = [1.0, 1.0, 1.0, 0.07];
|
||||
style.colors[StyleColor::FrameBgHovered as usize] = [1.0, 0.84, 0.48, 0.18];
|
||||
style.colors[StyleColor::FrameBgActive as usize] = [1.0, 0.84, 0.48, 0.28];
|
||||
style.colors[StyleColor::CheckMark as usize] = GOLD;
|
||||
}
|
||||
|
||||
fn render(&mut self, ui: &mut Ui) {
|
||||
self.poll_keys();
|
||||
|
||||
if SHOW_PANEL.load(Ordering::Relaxed) {
|
||||
self.panel(ui);
|
||||
}
|
||||
|
||||
if self.menu {
|
||||
let mut open = true;
|
||||
ui.window("LunaGC settings")
|
||||
.position([80.0, 150.0], Condition::FirstUseEver)
|
||||
.flags(
|
||||
WindowFlags::ALWAYS_AUTO_RESIZE
|
||||
| WindowFlags::NO_COLLAPSE
|
||||
| WindowFlags::NO_SAVED_SETTINGS,
|
||||
)
|
||||
.opened(&mut open)
|
||||
.build(|| self.settings(ui));
|
||||
self.menu = open;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(module: usize) {
|
||||
client::start();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
std::thread::sleep(std::time::Duration::from_secs(5));
|
||||
|
||||
let result = Hudhook::builder()
|
||||
.with::<ImguiDx11Hooks>(Overlay::new())
|
||||
.with_hmodule(HINSTANCE(module as _))
|
||||
.build()
|
||||
.apply();
|
||||
|
||||
match result {
|
||||
Ok(()) => crate::plog!("Overlay ready: Ctrl+L opens settings, Ctrl+K toggles the damage panel"),
|
||||
Err(e) => crate::plog!("Damage overlay failed to hook: {e:?}"),
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user