From b708f63ed4d67e20d0d79d13c73443ad80334ab6 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Wed, 4 Jan 2023 16:52:57 +0100 Subject: [PATCH 01/79] change some conditional compilation to non-windows --- Cargo.lock | 121 ++++++++++++++++++++++++--------------------- Proxy/src/entry.rs | 4 +- Proxy/src/libs.rs | 4 +- 3 files changed, 69 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c872af198..df22c6fc5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,9 +49,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.66" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" +checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" [[package]] name = "atk" @@ -83,7 +83,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -204,9 +204,9 @@ checksum = "1582e1c9e755dd6ad6b224dcffb135d199399a4568d454bd89fe515ca8425695" [[package]] name = "cc" -version = "1.0.77" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" +checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" dependencies = [ "jobserver", ] @@ -423,9 +423,9 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" [[package]] name = "cxx" -version = "1.0.83" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf07d07d6531bfcdbe9b8b739b104610c6508dcc4d63b410585faf338241daf" +checksum = "5add3fc1717409d029b20c5b6903fc0c0b02fa6741d820054f4a2efa5e5816fd" dependencies = [ "cc", "cxxbridge-flags", @@ -435,9 +435,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.83" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2eb5b96ecdc99f72657332953d4d9c50135af1bac34277801cc3937906ebd39" +checksum = "b4c87959ba14bc6fbc61df77c3fcfe180fc32b93538c4f1031dd802ccb5f2ff0" dependencies = [ "cc", "codespan-reporting", @@ -450,15 +450,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.83" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac040a39517fd1674e0f32177648334b0f4074625b5588a64519804ba0553b12" +checksum = "69a3e162fde4e594ed2b07d0f83c6c67b745e7f28ce58c6df5e6b6bef99dfb59" [[package]] name = "cxxbridge-macro" -version = "1.0.83" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1362b0ddcfc4eb0a1f57b68bd77dd99f0e826958a96abd0ae9bd092e114ffed6" +checksum = "3e7e2adeb6a0d4a282e581096b06e1791532b7d576dcde5ccd9382acf55db8e6" dependencies = [ "proc-macro2", "quote", @@ -918,6 +918,15 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + [[package]] name = "hmac" version = "0.12.1" @@ -1060,9 +1069,9 @@ checksum = "11b0d96e660696543b251e58030cf9787df56da39dab19ad60eae7353040917e" [[package]] name = "itoa" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" [[package]] name = "jobserver" @@ -1090,9 +1099,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.138" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "libc-stdhandle" @@ -1106,9 +1115,9 @@ dependencies = [ [[package]] name = "link-cplusplus" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" dependencies = [ "cc", ] @@ -1216,7 +1225,7 @@ dependencies = [ "cargo-emit", "coreclr-hosting-shared", "reqwest", - "semver 1.0.14", + "semver 1.0.16", "serde", "serde_json", "zip", @@ -1243,11 +1252,11 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi", + "hermit-abi 0.2.6", "libc", ] @@ -1282,9 +1291,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" [[package]] name = "opaque-debug" @@ -1348,9 +1357,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.5.1" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc8bed3549e0f9b0a2a78bf7c0018237a2cdf085eecbbc048e52612438e4e9d0" +checksum = "0f6e86fb9e7026527a0d46bc308b841d73170ef8f443e1807f6ef88526a816d4" dependencies = [ "thiserror", "ucd-trie", @@ -1411,9 +1420,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.47" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" dependencies = [ "unicode-ident", ] @@ -1435,7 +1444,7 @@ dependencies = [ [[package]] name = "proxy-dll" version = "0.2.5" -source = "git+https://github.com/RinLovesYou/dll-proxy-rs.git#69e0ade287bb180fb7f84439daa3c18ff6bc998f" +source = "git+https://github.com/RinLovesYou/dll-proxy-rs.git#160fd47d5d5e595ff694a1d004de7d8e562aba59" dependencies = [ "ctor", "proxy-sys", @@ -1446,16 +1455,16 @@ dependencies = [ [[package]] name = "proxy-sys" version = "0.2.2" -source = "git+https://github.com/RinLovesYou/dll-proxy-rs.git#69e0ade287bb180fb7f84439daa3c18ff6bc998f" +source = "git+https://github.com/RinLovesYou/dll-proxy-rs.git#160fd47d5d5e595ff694a1d004de7d8e562aba59" dependencies = [ "syn", ] [[package]] name = "quote" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ "proc-macro2", ] @@ -1552,15 +1561,15 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" [[package]] name = "scratch" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" +checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" [[package]] name = "sct" @@ -1583,9 +1592,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" +checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" [[package]] name = "semver-parser" @@ -1598,18 +1607,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.150" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e326c9ec8042f1b5da33252c8a37e9ffbd2c9bef0155215b6e6c80c790e05f91" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.150" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a3df25b0713732468deadad63ab9da1f1fd75a48a15024b50363f128db627e" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", @@ -1618,9 +1627,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.89" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" +checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" dependencies = [ "itoa", "ryu", @@ -1700,9 +1709,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.105" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ "proc-macro2", "quote", @@ -1733,18 +1742,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", @@ -1848,9 +1857,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f" dependencies = [ "serde", ] @@ -1917,9 +1926,9 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" [[package]] name = "unicode-ident" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "unicode-normalization" @@ -1939,7 +1948,7 @@ checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "unity-rs" version = "0.1.0" -source = "git+https://github.com/RinLovesYou/unity-rs.git#8ae8053a7d66beee563ff15ca4d337a3afc8de53" +source = "git+https://github.com/RinLovesYou/unity-rs.git?rev=8ae8053#8ae8053a7d66beee563ff15ca4d337a3afc8de53" dependencies = [ "libc", "thiserror", diff --git a/Proxy/src/entry.rs b/Proxy/src/entry.rs index 785c2995a..783923e15 100644 --- a/Proxy/src/entry.rs +++ b/Proxy/src/entry.rs @@ -1,6 +1,6 @@ //! Entrypoint -#[cfg(target_os = "linux")] +#[cfg(not(target_os = "windows"))] use ctor::ctor; #[cfg(target_os = "windows")] @@ -8,7 +8,7 @@ use proxy_dll::proxy; use crate::{core, internal_failure}; -#[cfg(target_os = "linux")] +#[cfg(not(target_os = "windows"))] #[no_mangle] #[ctor] fn main(){ diff --git a/Proxy/src/libs.rs b/Proxy/src/libs.rs index 640584d50..3d1b144db 100644 --- a/Proxy/src/libs.rs +++ b/Proxy/src/libs.rs @@ -63,7 +63,7 @@ pub struct NativeLibrary { impl NativeLibrary { /// gets a function pointer - #[cfg(target_os = "linux")] + #[cfg(not(target_os = "windows"))] pub fn sym(&self, name_str: &str) -> Result, LibError> { let name = std::ffi::CString::new(name_str).map_err(|_| LibError::FailedToCreateCString)?; let ptr = unsafe { libc::dlsym(self.handle, name.as_ptr()) }; @@ -124,7 +124,7 @@ impl NativeLibrary { /// let lib = load_lib(&path); /// /// assert!(lib.is_ok()); -#[cfg(target_os = "linux")] +#[cfg(not(target_os = "windows"))] pub fn load_lib>(path: P) -> Result { use std::ffi::CString; From 6a8a6c90ec082df2e8c7224f659e2a924bd3548b Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Sat, 14 Jan 2023 03:01:26 +0000 Subject: [PATCH 02/79] Update dobby dlls to ones that support some SSE --- BaseLibs/dobby_x64.dll | Bin 52224 -> 65024 bytes BaseLibs/dobby_x86.dll | Bin 37888 -> 47616 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/BaseLibs/dobby_x64.dll b/BaseLibs/dobby_x64.dll index 69c53818180dfc0983da80021faf053c78d8f356..9d2efba07ffd210df2827291b17ed3412b6a97ec 100644 GIT binary patch literal 65024 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 z

buH$RWx50VYdaTrPe8{|!FC+&?OZWV0OHpPzm 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 aj_CKXtmt+8cKRlDw0`p0u221w3H$=U6I4e4 diff --git a/BaseLibs/dobby_x86.dll b/BaseLibs/dobby_x86.dll index 1b437d3005e712307e3d06d1e43c69563b491ea1..834eb3993d5d07dd95067711dfe2b007cf649015 100644 GIT binary patch literal 47616 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 From 2c98d66f6ae302d59f88be5140c2a9ea563a2e0b Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Fri, 20 Jan 2023 14:30:17 +0000 Subject: [PATCH 03/79] Bump tomlet and interop versions --- .../Il2CppAssemblyGenerator.csproj | 8 ++++---- Dependencies/MelonStartScreen/MelonStartScreen.csproj | 2 +- Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj | 8 ++++---- Dependencies/SupportModules/Mono/Mono.csproj | 2 +- MelonLoader/MelonLoader.csproj | 8 ++++---- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Dependencies/Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj b/Dependencies/Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj index ffdf71e0d..9f3674d40 100644 --- a/Dependencies/Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj +++ b/Dependencies/Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj @@ -14,10 +14,10 @@ - - - - + + + + diff --git a/Dependencies/MelonStartScreen/MelonStartScreen.csproj b/Dependencies/MelonStartScreen/MelonStartScreen.csproj index 559af3c2c..c3d4ad907 100644 --- a/Dependencies/MelonStartScreen/MelonStartScreen.csproj +++ b/Dependencies/MelonStartScreen/MelonStartScreen.csproj @@ -19,6 +19,6 @@ - + \ No newline at end of file diff --git a/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj b/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj index 252470915..51c7d269c 100644 --- a/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj +++ b/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj @@ -41,12 +41,12 @@ - - - + + + - + \ No newline at end of file diff --git a/Dependencies/SupportModules/Mono/Mono.csproj b/Dependencies/SupportModules/Mono/Mono.csproj index 5156b5413..38ad7eeff 100644 --- a/Dependencies/SupportModules/Mono/Mono.csproj +++ b/Dependencies/SupportModules/Mono/Mono.csproj @@ -24,6 +24,6 @@ - + \ No newline at end of file diff --git a/MelonLoader/MelonLoader.csproj b/MelonLoader/MelonLoader.csproj index 11f5384d6..d46432eed 100644 --- a/MelonLoader/MelonLoader.csproj +++ b/MelonLoader/MelonLoader.csproj @@ -35,7 +35,7 @@ - + @@ -44,9 +44,9 @@ - - - + + + From e72617774a2433168c8132b2277c6a0b5d1edde3 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Tue, 7 Mar 2023 05:35:54 +0100 Subject: [PATCH 04/79] proxy cleanup --- Cargo.lock | 124 ++++++++++++++++++++ Proxy/Cargo.toml | 2 + Proxy/src/core.rs | 69 ++++------- Proxy/src/entry.rs | 29 +++-- Proxy/src/lib.rs | 1 - Proxy/src/libs.rs | 235 -------------------------------------- Proxy/src/utils/errors.rs | 9 ++ Proxy/src/utils/files.rs | 68 ++++++----- Proxy/src/utils/mod.rs | 3 +- 9 files changed, 219 insertions(+), 321 deletions(-) delete mode 100644 Proxy/src/libs.rs create mode 100644 Proxy/src/utils/errors.rs diff --git a/Cargo.lock b/Cargo.lock index df22c6fc5..8e6f0a96a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -250,6 +250,43 @@ dependencies = [ "generic-array", ] +[[package]] +name = "clap" +version = "4.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5" +dependencies = [ + "bitflags", + "clap_derive", + "clap_lex", + "is-terminal", + "once_cell", + "strsim", + "termcolor", +] + +[[package]] +name = "clap_derive" +version = "4.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "cocoa" version = "0.24.1" @@ -553,6 +590,27 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "ffi-opaque" version = "2.0.1" @@ -1061,12 +1119,34 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "io-lifetimes" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "ipnet" version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11b0d96e660696543b251e58030cf9787df56da39dab19ad60eae7353040917e" +[[package]] +name = "is-terminal" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" +dependencies = [ + "hermit-abi 0.2.6", + "io-lifetimes", + "rustix", + "windows-sys", +] + [[package]] name = "itoa" version = "1.0.5" @@ -1113,6 +1193,16 @@ dependencies = [ "libc", ] +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + [[package]] name = "link-cplusplus" version = "1.0.8" @@ -1122,6 +1212,12 @@ dependencies = [ "cc", ] +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + [[package]] name = "log" version = "0.4.17" @@ -1301,6 +1397,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "os_str_bytes" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" + [[package]] name = "pango" version = "0.15.10" @@ -1432,9 +1534,11 @@ name = "proxy" version = "0.2.0" dependencies = [ "cc", + "clap", "ctor", "lazy_static", "libc", + "libloading", "msgbox", "proxy-dll", "thiserror", @@ -1538,6 +1642,20 @@ dependencies = [ "semver 0.11.0", ] +[[package]] +name = "rustix" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "rustls" version = "0.20.7" @@ -1701,6 +1819,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "subtle" version = "2.4.1" diff --git a/Proxy/Cargo.toml b/Proxy/Cargo.toml index 30122fec2..0093518ee 100644 --- a/Proxy/Cargo.toml +++ b/Proxy/Cargo.toml @@ -10,6 +10,8 @@ ctor = "0.1.26" msgbox = "0.7.0" lazy_static = "1.4.0" thiserror = "*" +libloading = "*" +clap = { version = "*", features = ["derive"] } [target.'cfg(windows)'.dependencies] winapi = { version = "0.3.9", features = ["winuser", "minwindef", "std", "libloaderapi"] } diff --git a/Proxy/src/core.rs b/Proxy/src/core.rs index c22948464..fbb9ea4d2 100644 --- a/Proxy/src/core.rs +++ b/Proxy/src/core.rs @@ -1,61 +1,40 @@ //! the core logic of the proxy +use crate::utils::files; +use clap::{Parser}; +use libloading::Library; use std::{error, path::PathBuf}; -use crate::{utils::files::get_bootstrap_path, libs::{NativeLibrary, load_lib}}; -static mut BOOTSTRAP: Option = None; +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +struct Cli { + #[arg(long)] + no_mods: bool, + #[arg(long="melonloader.basedir")] + base_dir: Option, +} + +static mut BOOTSTRAP: Option = None; -/// The Entrypoint -pub fn init() -> Result<(), Box> { +pub unsafe fn init() -> Result<(), Box> { + let args = Cli::parse(); let file_path = std::env::current_exe()?; - if !check_unity(&file_path)? { + //return Ok, and silently stop loading MelonLoader, if the user has specified to not load mods, + //or if the game is not a Unity game + if args.no_mods || !files::is_unity(&file_path)? { return Ok(()); } - let args: Vec = std::env::args().collect(); - for i in 0..args.len() { - if args[i] == "--melonloader.no-mods" { - return Ok(()); - } - } + let base_path = match args.base_dir { + Some(path) => PathBuf::from(path), + None => std::env::current_dir()?, + }; - let base_path = file_path.parent().ok_or_else(|| "failed to get base path")?.to_path_buf(); - let bootstrap_path = get_bootstrap_path(&base_path)?; + let bootstrap_path = files::get_bootstrap_path(&base_path)?; - unsafe { - BOOTSTRAP = Some(load_lib(bootstrap_path)?); - } + BOOTSTRAP = Some(Library::new(bootstrap_path)?); //needs to be stored, so it won't get unloaded when this function returns. Ok(()) } - -fn check_unity(file_path: &PathBuf) -> Result> { - let file_name = file_path.file_stem() - .ok_or_else(|| "failed to get file name")? - .to_str() - .ok_or_else(|| "failed to get file name")?; - - - let base_folder = file_path.parent() - .ok_or_else(|| "failed to get base folder")?; - - - let data_path = base_folder.join(format!("{}_Data", file_name)); - - - if !data_path.exists() { - return Ok(false) - } - - let global_game_managers = data_path.join("globalgamemanagers"); - let data_unity3d = data_path.join("data.unity3d"); - let main_data = data_path.join("mainData"); - - if global_game_managers.exists() || data_unity3d.exists() || main_data.exists() { - Ok(true) - } else { - Ok(false) - } -} \ No newline at end of file diff --git a/Proxy/src/entry.rs b/Proxy/src/entry.rs index 783923e15..87923142f 100644 --- a/Proxy/src/entry.rs +++ b/Proxy/src/entry.rs @@ -1,4 +1,12 @@ -//! Entrypoint +//! These are platform specific init functions. +//! On Windows, we need to "proxy" a built-in Windows library. +//! The crate 'proxy-dll' takes care of this, dynamically proxying 3 +//! different dlls, depending on the file name of the compiled binary. +//! +//! See https://github.com/RinLovesYou/dll-proxy-rs/ +//! +//! On Linux, injection is done through LD_PRELOAD, so there's no need to proxy anything. +//! there we just use `ctor`. #[cfg(not(target_os = "windows"))] use ctor::ctor; @@ -11,18 +19,21 @@ use crate::{core, internal_failure}; #[cfg(not(target_os = "windows"))] #[no_mangle] #[ctor] -fn main(){ - core::init().unwrap_or_else(|e| { - internal_failure!("Failed to initialize: {}", e); - }); +fn main() { + unsafe { + core::init().unwrap_or_else(|e| { + internal_failure!("Failed to initialize MelonLoader: {}", e); + }); + } } #[cfg(target_os = "windows")] #[no_mangle] #[proxy] fn main() { - core::init().unwrap_or_else(|e| { - internal_failure!("Failed to initialize: {}", e); - }); + unsafe { + core::init().unwrap_or_else(|e| { + internal_failure!("Failed to initialize MelonLoader: {}", e); + }); + } } - diff --git a/Proxy/src/lib.rs b/Proxy/src/lib.rs index 501e602b6..a1fcba04d 100644 --- a/Proxy/src/lib.rs +++ b/Proxy/src/lib.rs @@ -17,4 +17,3 @@ pub mod entry; pub mod core; pub mod utils; -pub mod libs; \ No newline at end of file diff --git a/Proxy/src/libs.rs b/Proxy/src/libs.rs deleted file mode 100644 index 3d1b144db..000000000 --- a/Proxy/src/libs.rs +++ /dev/null @@ -1,235 +0,0 @@ -//! cross platform utilities for permanently loading libraries -//! -//! Main use case here is just permanently having mono/dobby/etc loaded, unlike with libloading, -//! where they get unloaded when the library goes out of scope. -//! -//! # Safety -//! this is incredibly unsafe -//! -//! # Examples -//! -//! ``` -//! use std::path::PathBuf; -//! use std::mem; -//! -//! use unity_rs::libs::load_lib; -//! -//! let path = PathBuf::from("path/to/lib"); -//! let lib = load_lib(&path)?; -//! -//! let func: extern fn() = unsafe { mem::transmute(lib.get_fn_ptr("func_name")?) }; - -use std::{ - ffi::c_void, - marker::PhantomData, - ops::Deref, - path::{Path, PathBuf}, -}; -use thiserror::Error; - -/// possible library loading errors -#[derive(Debug, Error)] -pub enum LibError { - /// failed to load library - #[error("Failed to load library!")] - FailedToLoadLib, - - /// failed to get lib name - #[error("Failed to get lib name!")] - FailedToGetLibName, - - /// failed to get lib path - #[error("Failed to get lib path!")] - FailedToGetLibPath, - - /// failed to get function pointer - #[error("Failed to get function pointer!")] - FailedToGetFnPtr, - - #[error("Failed to create C-String")] - FailedToCreateCString, -} - -/// a representation of a permanently loaded library -#[derive(Debug, Clone)] -pub struct NativeLibrary { - /// the name of the lib - pub name: String, - /// the path to the lib - pub path: PathBuf, - /// the pointer to the lib - pub handle: *mut c_void, -} - -impl NativeLibrary { - /// gets a function pointer - #[cfg(not(target_os = "windows"))] - pub fn sym(&self, name_str: &str) -> Result, LibError> { - let name = std::ffi::CString::new(name_str).map_err(|_| LibError::FailedToCreateCString)?; - let ptr = unsafe { libc::dlsym(self.handle, name.as_ptr()) }; - if ptr.is_null() { - return Err(LibError::FailedToGetFnPtr); - } - - Ok(NativeMethod { - inner: ptr.cast(), - pd: PhantomData, - }) - } - - /// gets a function pointer - #[cfg(target_os = "windows")] - pub fn sym(&self, name_str: &str) -> Result, LibError> { - use std::ffi::CString; - - use winapi::um::libloaderapi::GetProcAddress; - - let name = CString::new(name_str).map_err(|_| LibError::FailedToCreateCString)?; - - let ptr = unsafe { GetProcAddress(self.handle.cast(), name.as_ptr()) }; - if ptr.is_null() { - return Err(LibError::FailedToGetFnPtr); - } - - Ok(NativeMethod { - inner: ptr.cast(), - pd: PhantomData, - }) - } -} - -/// loads a library permanently -/// -/// # Arguments -/// -/// * `path` - the path to the library -/// -/// # Errors -/// -/// * `LibError::FailedToLoadLib` - if the library failed to load -/// * `LibError::FailedToGetLibName` - if the library name failed to be retrieved -/// -/// # Safety -/// -/// this function is unsafe because it permanently loads a library -/// -/// # Examples -/// -/// ``` -/// use std::path::PathBuf; -/// -/// use unity_rs::libs::load_lib; -/// -/// let path = PathBuf::from("path/to/lib"); -/// let lib = load_lib(&path); -/// -/// assert!(lib.is_ok()); -#[cfg(not(target_os = "windows"))] -pub fn load_lib>(path: P) -> Result { - use std::ffi::CString; - - let path = path.as_ref(); - - let path_string = path.to_str().ok_or(LibError::FailedToGetLibPath)?; - - let c_path = CString::new(path_string).map_err(|_| LibError::FailedToCreateCString)?; - - let lib = unsafe { libc::dlopen(c_path.as_ptr(), libc::RTLD_NOW | libc::RTLD_GLOBAL) }; - - if lib.is_null() { - return Err(LibError::FailedToLoadLib); - } - - let lib_name = path - .file_name() - .ok_or(LibError::FailedToGetLibName)? - .to_str() - .ok_or(LibError::FailedToGetLibName)? - .to_string(); - - Ok(NativeLibrary { - name: lib_name, - path: path.to_path_buf(), - handle: lib, - }) -} - -/// loads a library permanently -/// -/// # Arguments -/// -/// * `path` - the path to the library -/// -/// # Errors -/// -/// * `LibError::FailedToLoadLib` - if the library failed to load -/// * `LibError::FailedToGetLibName` - if the library name failed to be retrieved -/// -/// # Safety -/// -/// this function is unsafe because it permanently loads a library -/// -/// # Examples -/// -/// ``` -/// use std::path::PathBuf; -/// -/// use unity_rs::libs::load_lib; -/// -/// let path = PathBuf::from("path/to/lib"); -/// let lib = load_lib(&path); -/// -/// assert!(lib.is_ok()); -#[cfg(target_os = "windows")] -pub fn load_lib>(path: P) -> Result { - use std::ffi::CString; - - let path = path.as_ref(); - - use winapi::um::libloaderapi::LoadLibraryA; - - let path_string = path.to_str().ok_or_else(|| LibError::FailedToGetLibPath)?; - let win_path = CString::new(path_string).map_err(|_| LibError::FailedToCreateCString)?; - - let lib = unsafe { LoadLibraryA(win_path.as_ptr()) }; - - if lib.is_null() { - return Err(LibError::FailedToLoadLib); - } - - let lib_name = path - .file_name() - .ok_or(LibError::FailedToGetLibName)? - .to_str() - .ok_or(LibError::FailedToGetLibName)? - .to_string(); - - Ok(NativeLibrary { - name: lib_name, - path: path.to_path_buf(), - handle: lib.cast(), - }) -} - -#[derive(Debug)] -pub struct NativeMethod { - pub inner: *mut c_void, - pd: PhantomData, -} - -unsafe impl Send for NativeMethod {} -unsafe impl Sync for NativeMethod {} - -impl Clone for NativeMethod { - fn clone(&self) -> NativeMethod { - NativeMethod { ..*self } - } -} - -impl Deref for NativeMethod { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &*(&self.inner as *const *mut _ as *const T) } - } -} diff --git a/Proxy/src/utils/errors.rs b/Proxy/src/utils/errors.rs new file mode 100644 index 000000000..3b66e0b27 --- /dev/null +++ b/Proxy/src/utils/errors.rs @@ -0,0 +1,9 @@ +use std::path::PathBuf; + +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum ProxyError { + #[error("failed to find Bootstrap at \"{0}\" please make sure you have installed MelonLoader correctly")] + BootstrapNotFound(PathBuf), +} \ No newline at end of file diff --git a/Proxy/src/utils/files.rs b/Proxy/src/utils/files.rs index 4530661f7..4a580d0b3 100644 --- a/Proxy/src/utils/files.rs +++ b/Proxy/src/utils/files.rs @@ -1,40 +1,48 @@ //! various filesystem utils -use std::path::PathBuf; - -/// get the path to bootstra -pub fn get_bootstrap_path(base_path: &PathBuf) -> Result> { - let win_default = ["MelonLoader", "Dependencies", "Bootstrap"]; - let unix_default = ["MelonLoader", "Dependencies", "libBootstrap"]; - - let mut path = base_path.clone(); - - // check for --melonloader.basepath arg - let args: Vec = std::env::args().collect(); - for i in 0..args.len() { - if args[i] == "--melonloader.basepath" { - if i + 1 >= args.len() { - return Err("No path specified for --melonloader.basepath".into()); - } - path = PathBuf::from(&args[i + 1]); - break; +use std::{env::consts::DLL_EXTENSION, error::Error, path::PathBuf}; + +use super::errors::ProxyError; + +/// search for Bootstrap in the given path +pub fn get_bootstrap_path(base_path: &PathBuf) -> Result { + let bootstrap_names = ["Bootstrap", "libBootstrap"]; //by convention, on unix, the library is prefixed with "lib" + + let path = base_path.join("MelonLoader").join("Dependencies"); + + for name in bootstrap_names.iter() { + let bootstrap_path = path.join(name).with_extension(DLL_EXTENSION); + + if bootstrap_path.exists() { + return Ok(bootstrap_path); } } - let mut windows_path = path.clone(); - let mut unix_path = path.clone(); + Err(ProxyError::BootstrapNotFound(base_path.to_owned())) +} - windows_path.extend(win_default.iter()); - unix_path.extend(unix_default.iter()); +pub fn is_unity(file_path: &PathBuf) -> Result> { + let file_name = file_path + .file_stem() + .ok_or("Failed to get file stem")? + .to_str() + .ok_or("Failed to convert file stem to str")?; + + let base_folder = file_path.parent().ok_or("Failed to get parent folder")?; + + let data_path = base_folder.join(format!("{file_name}_Data")); + + if !data_path.exists() { + return Ok(false); + } - let windows_path = windows_path.with_extension(std::env::consts::DLL_EXTENSION); - let unix_path = unix_path.with_extension(std::env::consts::DLL_EXTENSION); + let global_game_managers = data_path.join("globalgamemanagers"); + let data_unity3d = data_path.join("data.unity3d"); + let main_data = data_path.join("mainData"); - if windows_path.exists() { - Ok(windows_path) - } else if unix_path.exists() { - Ok(unix_path) + if global_game_managers.exists() || data_unity3d.exists() || main_data.exists() { + Ok(true) } else { - Err("Failed to find bootstrap".into()) + Ok(false) } -} \ No newline at end of file +} diff --git a/Proxy/src/utils/mod.rs b/Proxy/src/utils/mod.rs index abd7518a8..b5bfd8b1a 100644 --- a/Proxy/src/utils/mod.rs +++ b/Proxy/src/utils/mod.rs @@ -1,4 +1,5 @@ //! various utilites pub mod assert; -pub mod files; \ No newline at end of file +pub mod files; +pub mod errors; \ No newline at end of file From 36804174c32dda1471ac9e57cb9425d17cd4a15f Mon Sep 17 00:00:00 2001 From: Jeremy Pritts <49847914+ds5678@users.noreply.github.com> Date: Wed, 15 Mar 2023 00:04:31 -0400 Subject: [PATCH 05/79] Bump Il2CppInterop to 1.4.5 --- .../Il2CppAssemblyGenerator.csproj | 8 ++++---- Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj | 6 +++--- MelonLoader/MelonLoader.csproj | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Dependencies/Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj b/Dependencies/Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj index 9f3674d40..126911f73 100644 --- a/Dependencies/Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj +++ b/Dependencies/Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj @@ -14,10 +14,10 @@ - - - - + + + + diff --git a/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj b/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj index 51c7d269c..290a8adc2 100644 --- a/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj +++ b/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj @@ -41,9 +41,9 @@ - - - + + + diff --git a/MelonLoader/MelonLoader.csproj b/MelonLoader/MelonLoader.csproj index d46432eed..0059242f3 100644 --- a/MelonLoader/MelonLoader.csproj +++ b/MelonLoader/MelonLoader.csproj @@ -44,9 +44,9 @@ - - - + + + From 803ee1e6e0fe869e0ebd86aa96200a5b58cc48af Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Mon, 20 Mar 2023 16:47:35 +0100 Subject: [PATCH 06/79] Big Bootstrap Refactor --- Bootstrap/Cargo.toml | 26 +- Bootstrap/src/base_assembly/dotnet.rs | 112 +++++++ Bootstrap/src/base_assembly/mod.rs | 34 ++ Bootstrap/src/base_assembly/mono.rs | 95 ++++++ Bootstrap/src/console/mod.rs | 25 ++ Bootstrap/src/console/os/mod.rs | 5 + Bootstrap/src/console/os/unix/mod.rs | 18 ++ Bootstrap/src/console/os/windows/mod.rs | 146 +++++++++ Bootstrap/src/constants.rs | 41 +++ Bootstrap/src/core.rs | 25 ++ Bootstrap/src/errors/conerr.rs | 13 + Bootstrap/src/errors/hookerr.rs | 24 ++ Bootstrap/src/errors/logerr.rs | 16 + Bootstrap/src/errors/mod.rs | 5 + Bootstrap/src/hooks/functions.rs | 42 +++ Bootstrap/src/hooks/init_hook/il2cpp.rs | 35 +++ Bootstrap/src/hooks/init_hook/mod.rs | 44 +++ Bootstrap/src/hooks/init_hook/mono.rs | 70 +++++ Bootstrap/src/hooks/invoke_hook/il2cpp.rs | 47 +++ Bootstrap/src/hooks/invoke_hook/mod.rs | 44 +++ Bootstrap/src/hooks/invoke_hook/mono.rs | 62 ++++ Bootstrap/src/hooks/mod.rs | 72 +++++ Bootstrap/src/icalls/bootstrap_interop.rs | 18 ++ Bootstrap/src/icalls/melon_utils.rs | 3 + Bootstrap/src/icalls/mod.rs | 24 ++ Bootstrap/src/icalls/mono_library.rs | 34 ++ Bootstrap/src/icalls/preload.rs | 21 ++ Bootstrap/src/icalls/resolve_internals.rs | 130 ++++++++ Bootstrap/src/lib.rs | 20 +- Bootstrap/src/{utils => logging}/assert.rs | 5 +- Bootstrap/src/logging/logger.rs | 229 ++++++++++++++ Bootstrap/src/logging/mod.rs | 2 + Bootstrap/src/managers/base_asm.rs | 53 ---- Bootstrap/src/managers/core.rs | 38 --- Bootstrap/src/managers/dotnet.rs | 146 --------- Bootstrap/src/managers/hooks.rs | 247 --------------- Bootstrap/src/managers/internal_calls.rs | 207 ------------- Bootstrap/src/managers/mod.rs | 6 - Bootstrap/src/managers/mono.rs | 115 ------- Bootstrap/src/melonenv/args.rs | 22 ++ Bootstrap/src/melonenv/macros.rs | 6 + Bootstrap/src/melonenv/mod.rs | 3 + Bootstrap/src/melonenv/paths.rs | 69 +++++ Bootstrap/src/utils/console.rs | 137 --------- Bootstrap/src/utils/debug.rs | 14 - Bootstrap/src/utils/detours.rs | 52 ---- Bootstrap/src/utils/files.rs | 110 ------- Bootstrap/src/utils/log.rs | 313 ------------------- Bootstrap/src/utils/mod.rs | 8 +- Bootstrap/src/utils/runtime.rs | 23 ++ Bootstrap/src/utils/strings.rs | 36 +++ Cargo.lock | 341 ++++++++++++--------- MelonLoader/Properties/BuildInfo.cs | 2 +- Proxy/Cargo.toml | 6 +- Proxy/src/core.rs | 33 +- Proxy/src/entry.rs | 16 +- Proxy/src/lib.rs | 2 + 57 files changed, 1854 insertions(+), 1638 deletions(-) create mode 100644 Bootstrap/src/base_assembly/dotnet.rs create mode 100644 Bootstrap/src/base_assembly/mod.rs create mode 100644 Bootstrap/src/base_assembly/mono.rs create mode 100644 Bootstrap/src/console/mod.rs create mode 100644 Bootstrap/src/console/os/mod.rs create mode 100644 Bootstrap/src/console/os/unix/mod.rs create mode 100644 Bootstrap/src/console/os/windows/mod.rs create mode 100644 Bootstrap/src/constants.rs create mode 100644 Bootstrap/src/core.rs create mode 100644 Bootstrap/src/errors/conerr.rs create mode 100644 Bootstrap/src/errors/hookerr.rs create mode 100644 Bootstrap/src/errors/logerr.rs create mode 100644 Bootstrap/src/errors/mod.rs create mode 100644 Bootstrap/src/hooks/functions.rs create mode 100644 Bootstrap/src/hooks/init_hook/il2cpp.rs create mode 100644 Bootstrap/src/hooks/init_hook/mod.rs create mode 100644 Bootstrap/src/hooks/init_hook/mono.rs create mode 100644 Bootstrap/src/hooks/invoke_hook/il2cpp.rs create mode 100644 Bootstrap/src/hooks/invoke_hook/mod.rs create mode 100644 Bootstrap/src/hooks/invoke_hook/mono.rs create mode 100644 Bootstrap/src/hooks/mod.rs create mode 100644 Bootstrap/src/icalls/bootstrap_interop.rs create mode 100644 Bootstrap/src/icalls/melon_utils.rs create mode 100644 Bootstrap/src/icalls/mod.rs create mode 100644 Bootstrap/src/icalls/mono_library.rs create mode 100644 Bootstrap/src/icalls/preload.rs create mode 100644 Bootstrap/src/icalls/resolve_internals.rs rename Bootstrap/src/{utils => logging}/assert.rs (79%) mode change 100755 => 100644 create mode 100644 Bootstrap/src/logging/logger.rs create mode 100644 Bootstrap/src/logging/mod.rs delete mode 100644 Bootstrap/src/managers/base_asm.rs delete mode 100755 Bootstrap/src/managers/core.rs delete mode 100644 Bootstrap/src/managers/dotnet.rs delete mode 100755 Bootstrap/src/managers/hooks.rs delete mode 100755 Bootstrap/src/managers/internal_calls.rs delete mode 100755 Bootstrap/src/managers/mod.rs delete mode 100644 Bootstrap/src/managers/mono.rs create mode 100644 Bootstrap/src/melonenv/args.rs create mode 100644 Bootstrap/src/melonenv/macros.rs create mode 100644 Bootstrap/src/melonenv/mod.rs create mode 100644 Bootstrap/src/melonenv/paths.rs delete mode 100755 Bootstrap/src/utils/console.rs delete mode 100755 Bootstrap/src/utils/debug.rs delete mode 100644 Bootstrap/src/utils/detours.rs delete mode 100755 Bootstrap/src/utils/files.rs delete mode 100755 Bootstrap/src/utils/log.rs create mode 100644 Bootstrap/src/utils/runtime.rs create mode 100644 Bootstrap/src/utils/strings.rs diff --git a/Bootstrap/Cargo.toml b/Bootstrap/Cargo.toml index abf953a39..18e33e521 100755 --- a/Bootstrap/Cargo.toml +++ b/Bootstrap/Cargo.toml @@ -6,21 +6,25 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -msgbox = "0.7.0" -chrono = "0.4.22" -colored = "2.0.0" -thiserror = "1.0.37" +#unity-rs = { path = "C:/Users/sarah/Documents/rust/Ferrex/unity/" } +unity-rs = { git = "https://github.com/RinLovesYou/Ferrex/"} ctor = "0.1.26" +chrono = "0.4.23" +colored = "2.0.0" +thiserror = "1.0.39" lazy_static = "1.4.0" -unity-rs = { git = "https://github.com/RinLovesYou/unity-rs.git", rev = "8ae8053" } -libc = "0.2.137" -libc-stdhandle = "0.1.0" -netcorehost = "*" +msgbox = "0.7.0" +clap = { git = "https://github.com/RinLovesYou/clap", features = ["derive"] } +libc = "0.2.140" dobby-rs = { git = "https://github.com/RinLovesYou/dobby-rs" } +libc-stdhandle = "0.1.0" +netcorehost = "0.13.1" [target.'cfg(windows)'.dependencies] -winapi = { version = "0.3.9", features = ["winuser", "minwindef", "std", "libloaderapi"] } - +windows = { version = "0.46.0", features = [ + "Win32_Foundation", + "Win32_System_Console", +]} [lib] -crate-type = ["cdylib"] +crate-type = ["cdylib"] \ No newline at end of file diff --git a/Bootstrap/src/base_assembly/dotnet.rs b/Bootstrap/src/base_assembly/dotnet.rs new file mode 100644 index 000000000..b1c375e22 --- /dev/null +++ b/Bootstrap/src/base_assembly/dotnet.rs @@ -0,0 +1,112 @@ +use netcorehost::{nethost, pdcstr}; +use std::{ + ffi::c_void, + ptr::{addr_of, addr_of_mut, null_mut}, +}; + +use crate::{ + debug, + errors::DynErr, + icalls, melonenv, + utils::{self, strings::wide_str}, +}; + +#[repr(C)] +#[derive(Debug)] +pub struct HostImports { + pub load_assembly_get_ptr: fn(isize, isize, isize, *mut *mut c_void), + + pub initialize: fn(), + pub pre_start: fn(), + pub start: fn(), +} + +#[repr(C)] +#[derive(Debug)] +pub struct HostExports { + pub hook_attach: unsafe fn(*mut *mut c_void, *mut c_void), + pub hook_detach: unsafe fn(*mut *mut c_void, *mut c_void), +} + +static mut IMPORTS: Option = None; + +pub fn init() -> Result<(), DynErr> { + let runtime_dir = melonenv::paths::runtime_dir()?; + + let hostfxr = nethost::load_hostfxr()?; + let config_path = runtime_dir.join("MelonLoader.runtimeconfig.json"); + if !config_path.exists() { + Err("Failed to find the MelonLoader.runtimeconfig.json file!")? + } + + let context = hostfxr.initialize_for_runtime_config(utils::strings::pdcstr(config_path)?)?; + + let native_host_path = utils::strings::pdcstr(runtime_dir.join("MelonLoader.NativeHost.dll"))?; + + let loader = context.get_delegate_loader_for_assembly(&native_host_path)?; + let init = loader.get_function_with_unmanaged_callers_only::( + pdcstr!("MelonLoader.NativeHost.NativeEntryPoint, MelonLoader.NativeHost"), + pdcstr!("LoadStage1"), + )?; + + let mut imports = HostImports { + load_assembly_get_ptr: |_, _, _, _| {}, //hackz + initialize: || {}, + pre_start: || {}, + start: || {}, + }; + + let mut exports = HostExports { + hook_attach: icalls::bootstrap_interop::attach, + hook_detach: icalls::bootstrap_interop::detach, + }; + + debug!("[Dotnet] Invoking LoadStage1")?; + init(addr_of_mut!(imports)); + debug!( + "[Dotnet] Reloading NativeHost into correct load context and getting LoadStage2 pointer" + )?; + + let mut init_stage_two = null_mut::(); + + (imports.load_assembly_get_ptr)( + wide_str(runtime_dir.join("MelonLoader.NativeHost.dll"))?.as_ptr() as isize, + wide_str("MelonLoader.NativeHost.NativeEntryPoint, MelonLoader.NativeHost")?.as_ptr() as isize, + wide_str("LoadStage2")?.as_ptr() as isize, + addr_of_mut!(init_stage_two), + ); + + debug!("[Dotnet] Invoking LoadStage2")?; + + let init_stage_two: fn(*mut HostImports, *mut HostExports) = + unsafe { std::mem::transmute(init_stage_two) }; + init_stage_two(addr_of_mut!(imports), addr_of_mut!(exports)); + + if addr_of!(imports.initialize).is_null() { + Err("Failed to get HostImports::Initialize!")? + } + + (imports.initialize)(); + + unsafe { + IMPORTS = Some(imports); + } + + Ok(()) +} + +pub fn pre_start() -> Result<(), DynErr> { + let imports = unsafe { IMPORTS.as_ref() }.ok_or("HostImports are not initialized!")?; + + (imports.pre_start)(); + + Ok(()) +} + +pub fn start() -> Result<(), DynErr> { + let imports = unsafe { IMPORTS.as_ref() }.ok_or("HostImports are not initialized!")?; + + (imports.start)(); + + Ok(()) +} diff --git a/Bootstrap/src/base_assembly/mod.rs b/Bootstrap/src/base_assembly/mod.rs new file mode 100644 index 000000000..9e6fd74f3 --- /dev/null +++ b/Bootstrap/src/base_assembly/mod.rs @@ -0,0 +1,34 @@ +use unity_rs::runtime::{FerrexRuntime, RuntimeType}; + +use crate::{errors::DynErr, runtime}; + +pub mod dotnet; +pub mod mono; + +pub fn init(runtime: &FerrexRuntime) -> Result<(), DynErr> { + match runtime.get_type() { + RuntimeType::Mono(_) => mono::init(&runtime), + + RuntimeType::Il2Cpp(_) => dotnet::init(), + } +} + +pub fn pre_start() -> Result<(), DynErr> { + let runtime = runtime!()?; + + match runtime.get_type() { + RuntimeType::Mono(_) => mono::pre_start(), + + RuntimeType::Il2Cpp(_) => dotnet::pre_start(), + } +} + +pub fn start() -> Result<(), DynErr> { + let runtime = runtime!()?; + + match runtime.get_type() { + RuntimeType::Mono(_) => mono::start(), + + RuntimeType::Il2Cpp(_) => dotnet::start(), + } +} diff --git a/Bootstrap/src/base_assembly/mono.rs b/Bootstrap/src/base_assembly/mono.rs new file mode 100644 index 000000000..fc4a4a7eb --- /dev/null +++ b/Bootstrap/src/base_assembly/mono.rs @@ -0,0 +1,95 @@ +use std::{ptr::null_mut, sync::Mutex}; + +use lazy_static::lazy_static; +use unity_rs::{ + common::{assembly::UnityAssembly, method::UnityMethod}, + runtime::FerrexRuntime, +}; + +use crate::{debug, errors::DynErr, melonenv, runtime}; + +lazy_static! { + pub static ref MONO_PRESTART: Mutex = + Mutex::new(UnityMethod { inner: null_mut() }); + pub static ref MONO_START: Mutex = Mutex::new(UnityMethod { inner: null_mut() }); + pub static ref ASSEMBLYMANAGER_RESOLVE: Mutex = + Mutex::new(UnityMethod { inner: null_mut() }); + pub static ref ASSEMBLYMANAGER_LOADINFO: Mutex = + Mutex::new(UnityMethod { inner: null_mut() }); +} + +pub fn init(runtime: &FerrexRuntime) -> Result<(), DynErr> { + preload(runtime)?; + + debug!("Initializing BaseAssembly")?; + + //get MelonLoader.dll's path and confirm it exists + let mut melonloader_dll = melonenv::paths::MELONLOADER_FOLDER.clone(); + melonloader_dll.extend(&["net35", "MelonLoader.dll"]); + + if !melonloader_dll.exists() { + return Err("MelonLoader.dll not found".into()); + } + + //load the MelonLoader Assembly into the current domain, and grab some methods from the Core class + let melonloader_assembly = UnityAssembly::open(melonloader_dll.as_path(), runtime)?; + let core_class = melonloader_assembly.get_class("MelonLoader", "Core", runtime)?; + let initialize_method = core_class.get_method("Initialize", 0, runtime)?; + + let prestart_method = core_class.get_method("PreStart", 0, runtime)?; + let start_method = core_class.get_method("Start", 0, runtime)?; + + //get the AssemblyManager class and grab some methods from it + let assemblymanager_class = melonloader_assembly.get_class( + "MelonLoader.MonoInternals.ResolveInternals", + "AssemblyManager", + runtime, + )?; + + let resolve_method = assemblymanager_class.get_method("Resolve", 6, runtime)?; + let loadinfo_method = assemblymanager_class.get_method("LoadInfo", 1, runtime)?; + + //store the methods for later, in a thread safe global static. + *MONO_PRESTART.lock()? = prestart_method; + *MONO_START.lock()? = start_method; + *ASSEMBLYMANAGER_RESOLVE.lock()? = resolve_method; + *ASSEMBLYMANAGER_LOADINFO.lock()? = loadinfo_method; + + //invoke the MelonLoader initialize method. + let _ = initialize_method.invoke(None, None, runtime)?; + + Ok(()) +} + +pub fn pre_start() -> Result<(), DynErr> { + let prestart_method = MONO_PRESTART.lock()?; + if prestart_method.inner.is_null() { + return Err("PreStart method not found".into()); + } + + let _ = prestart_method.invoke(None, None, runtime!()?)?; + Ok(()) +} + +pub fn start() -> Result<(), DynErr> { + let start_method = MONO_START.lock()?; + if start_method.inner.is_null() { + return Err("Start method not found".into()); + } + + let _ = start_method.invoke(None, None, runtime!()?)?; + Ok(()) +} + +fn preload(runtime: &FerrexRuntime) -> Result<(), DynErr> { + if !melonenv::paths::PRELOAD_DLL.exists() { + return Err("Preload.dll not found".into()); + } + + let _ = UnityAssembly::open(melonenv::paths::PRELOAD_DLL.as_path(), runtime)? + .get_class("MelonLoader.Support", "Preload", runtime)? + .get_method("Initialize", 0, runtime)? + .invoke(None, None, runtime)?; + + Ok(()) +} diff --git a/Bootstrap/src/console/mod.rs b/Bootstrap/src/console/mod.rs new file mode 100644 index 000000000..26fa7ee00 --- /dev/null +++ b/Bootstrap/src/console/mod.rs @@ -0,0 +1,25 @@ +pub mod os; + +#[cfg(all(unix))] +use os::unix as imp; + +#[cfg(all(windows))] +use os::windows as imp; + +use crate::errors::DynErr; + +pub fn init() -> Result<(), DynErr> { + unsafe { imp::init() } +} + +pub fn null_handles() -> Result<(), DynErr> { + imp::null_handles() +} + +pub fn set_handles() -> Result<(), DynErr> { + imp::set_handles() +} + +pub fn set_title(title: &str) { + imp::set_title(title) +} diff --git a/Bootstrap/src/console/os/mod.rs b/Bootstrap/src/console/os/mod.rs new file mode 100644 index 000000000..c885d0704 --- /dev/null +++ b/Bootstrap/src/console/os/mod.rs @@ -0,0 +1,5 @@ +#[cfg(any(unix))] +pub mod unix; + +#[cfg(any(windows))] +pub mod windows; diff --git a/Bootstrap/src/console/os/unix/mod.rs b/Bootstrap/src/console/os/unix/mod.rs new file mode 100644 index 000000000..d42ab4652 --- /dev/null +++ b/Bootstrap/src/console/os/unix/mod.rs @@ -0,0 +1,18 @@ +//! Console interaction like windows is not possible on Unix systems. + +pub unsafe fn init() -> Result<(), DynErr> { + Ok(()) +} + +#[allow(dead_code)] +pub fn set_title(title: &str) { + +} + +pub fn set_handles() -> Result<(), DynErr> { + Ok(()) +} + +pub fn null_handles() -> Result<(), DynErr> { + Ok(()) +} diff --git a/Bootstrap/src/console/os/windows/mod.rs b/Bootstrap/src/console/os/windows/mod.rs new file mode 100644 index 000000000..8bd36487e --- /dev/null +++ b/Bootstrap/src/console/os/windows/mod.rs @@ -0,0 +1,146 @@ +//! Windows Console Module. + +use std::sync::Mutex; + +use lazy_static::lazy_static; +use windows::{ + core::PCSTR, + Win32::{ + Foundation::{self, HANDLE, HWND}, + System::Console::*, + }, +}; + +use crate::{ + constants::{IS_ALPHA, MELON_VERSION}, + debug_enabled, + errors::{conerr::ConsoleError, DynErr}, + win_str, +}; + +lazy_static! { + static ref WINDOW: Mutex = Mutex::new(HWND(0)); + static ref OUTPUT_HANDLE: Mutex = Mutex::new(HANDLE(0)); +} + +pub unsafe fn init() -> Result<(), DynErr> { + // creates a console window, if one already exists it'll just return true. + if !AllocConsole().as_bool() { + return Err(ConsoleError::FailedToAllocateConsole.into()); + } + + // store the console window handle + let mut window = WINDOW.lock()?; + *window = GetConsoleWindow(); + + // a null check + if window.0 == 0 { + return Err(ConsoleError::FailedToGetConsoleWindow.into()); + } + + // this lets us hook into console close events, and run some cleanup logic. + if SetConsoleCtrlHandler(Some(ctrl_handler_hook), Foundation::TRUE) == Foundation::FALSE { + return Err(ConsoleError::FailedToSetConsoleCtrlHandler.into()); + } + + set_title(&default_title()); + + let _ = libc::freopen( + win_str!(b"CONIN$\0"), + win_str!(b"r\0"), + libc_stdhandle::stdin(), + ); + let _ = libc::freopen( + win_str!(b"CONOUT$\0"), + win_str!(b"w\0"), + libc_stdhandle::stdout(), + ); + let _ = libc::freopen( + win_str!(b"CONOUT$\0"), + win_str!(b"w\0"), + libc_stdhandle::stderr(), + ); + + // needs to be in its own scope to drop the lock + { + let mut output_handle = OUTPUT_HANDLE.lock()?; + *output_handle = GetStdHandle(STD_OUTPUT_HANDLE)?; + } + + set_handles()?; + + let output_handle = OUTPUT_HANDLE.lock()?; + + let mut mode = CONSOLE_MODE(0); + let _ = GetConsoleMode(*output_handle, &mut mode); + + mode |= ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT; + + if SetConsoleMode(*output_handle, mode) != Foundation::TRUE { + mode &= !(ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT); + } else { + mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + + if SetConsoleMode(*output_handle, mode) != Foundation::TRUE { + mode &= !ENABLE_VIRTUAL_TERMINAL_PROCESSING; + } + } + + mode |= ENABLE_EXTENDED_FLAGS; + mode &= !(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT | ENABLE_INSERT_MODE); + + let _ = SetConsoleMode(*output_handle, mode); + Ok(()) +} + +fn default_title() -> String { + let mut title = match debug_enabled!() { + true => format!("[D] MelonLoader v{MELON_VERSION}"), + false => format!("MelonLoader v{MELON_VERSION}"), + }; + + match IS_ALPHA { + true => title.push_str(" Alpha-PreRelease\0"), + false => title.push_str(" Open-Beta\0"), + } + + title +} + +pub fn set_title(title: &str) { + unsafe { + let t = PCSTR(title.as_ptr()); + let _ = SetConsoleTitleA(t); + } +} + +pub fn set_handles() -> Result<(), DynErr> { + unsafe { + let handle = OUTPUT_HANDLE.lock()?; + + let _ = SetStdHandle(STD_OUTPUT_HANDLE, *handle); + let _ = SetStdHandle(STD_ERROR_HANDLE, *handle); + + Ok(()) + } +} + +pub fn null_handles() -> Result<(), DynErr> { + unsafe { + let _ = SetStdHandle(STD_OUTPUT_HANDLE, HANDLE(0)); + let _ = SetStdHandle(STD_ERROR_HANDLE, HANDLE(0)); + + Ok(()) + } +} + +unsafe extern "system" fn ctrl_handler_hook(ctrltype: u32) -> Foundation::BOOL { + match ctrltype { + CTRL_C_EVENT | CTRL_CLOSE_EVENT | CTRL_LOGOFF_EVENT | CTRL_SHUTDOWN_EVENT => { + crate::core::shutdown(); + Foundation::TRUE + } + + _ => Foundation::FALSE, + } +} diff --git a/Bootstrap/src/constants.rs b/Bootstrap/src/constants.rs new file mode 100644 index 000000000..580c3550d --- /dev/null +++ b/Bootstrap/src/constants.rs @@ -0,0 +1,41 @@ +use colored::Color; +use std::ffi::{c_char, c_void}; + +use unity_rs::{ + il2cpp::types::{Il2CppDomain, Il2CppMethod, Il2CppObject}, + mono::types::{MonoDomain, MonoMethod, MonoObject}, +}; + +pub type InvokeFnMono = + fn(*mut MonoMethod, *mut MonoObject, *mut *mut c_void, *mut *mut MonoObject) -> *mut MonoObject; +pub type InvokeFnIl2Cpp = fn( + *mut Il2CppMethod, + *mut Il2CppObject, + *mut *mut c_void, + *mut *mut Il2CppObject, +) -> *mut Il2CppObject; + +pub type InitFnMono = fn(*const c_char, *const c_char) -> *mut MonoDomain; +pub type InitFnIl2Cpp = fn(*const c_char) -> *mut Il2CppDomain; + +pub const MELON_VERSION: &str = "0.6.1"; + +pub const IS_ALPHA: bool = true; + +pub const RED: Color = Color::TrueColor { + r: (255), + g: (0), + b: (0), +}; + +pub const GREEN: Color = Color::TrueColor { + r: (0), + g: (255), + b: (0), +}; + +pub const BLUE: Color = Color::TrueColor { + r: (64), + g: (64), + b: (255), +}; diff --git a/Bootstrap/src/core.rs b/Bootstrap/src/core.rs new file mode 100644 index 000000000..3decf389f --- /dev/null +++ b/Bootstrap/src/core.rs @@ -0,0 +1,25 @@ +use ctor::ctor; + +use crate::{console, errors::DynErr, hooks, internal_failure, logging::logger}; + +#[ctor] +fn startup() { + init().unwrap_or_else(|e| { + internal_failure!("Failed to initialize MelonLoader: {}", e.to_string()); + }) +} + +fn init() -> Result<(), DynErr> { + console::init()?; + logger::init()?; + + hooks::init_hook::hook()?; + + console::null_handles()?; + + Ok(()) +} + +pub fn shutdown() { + std::process::exit(0); +} diff --git a/Bootstrap/src/errors/conerr.rs b/Bootstrap/src/errors/conerr.rs new file mode 100644 index 000000000..021c08644 --- /dev/null +++ b/Bootstrap/src/errors/conerr.rs @@ -0,0 +1,13 @@ +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum ConsoleError { + #[error("Failed to allocate console!")] + FailedToAllocateConsole, + + #[error("Failed to get console window!")] + FailedToGetConsoleWindow, + + #[error("Failed to set console control handler!")] + FailedToSetConsoleCtrlHandler, +} diff --git a/Bootstrap/src/errors/hookerr.rs b/Bootstrap/src/errors/hookerr.rs new file mode 100644 index 000000000..4326b4d65 --- /dev/null +++ b/Bootstrap/src/errors/hookerr.rs @@ -0,0 +1,24 @@ +use dobby_rs::DobbyHookError; +use thiserror::Error; +use unity_rs::runtime::RuntimeError; + +use super::logerr::LogError; + +#[derive(Debug, Error)] +pub enum HookError { + #[error(transparent)] + Dobby(#[from] DobbyHookError), + #[error(transparent)] + Runtime(#[from] RuntimeError), + #[error(transparent)] + Log(#[from] LogError), + + #[error("Hook returned a Nullpointer trampoline")] + Null, + #[error("Paramter {0} is a Nullpointer")] + Nullpointer(String), + #[error("Trampoline to {0} is none!")] + NoTrampoline(String), + #[error("Failed to hook {0}")] + Failed(String), +} diff --git a/Bootstrap/src/errors/logerr.rs b/Bootstrap/src/errors/logerr.rs new file mode 100644 index 000000000..7278d7056 --- /dev/null +++ b/Bootstrap/src/errors/logerr.rs @@ -0,0 +1,16 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum LogError { + /// the log file could not be deleted + #[error("Failed to delete the old log file. Please ensure you have permission to delete the file, and that you are not currently editing it.")] + FailedToDeleteOldLog, + + /// the log file could not be written to + #[error("Failed to write to log file. Please ensure you have permission to write to the file, and that you are not currently editing it.")] + FailedToWriteToLog, + + /// the base path could not be fetched + #[error("Failed to fetch base path")] + FailedToGetBasePath, +} diff --git a/Bootstrap/src/errors/mod.rs b/Bootstrap/src/errors/mod.rs new file mode 100644 index 000000000..e4e48057d --- /dev/null +++ b/Bootstrap/src/errors/mod.rs @@ -0,0 +1,5 @@ +pub type DynErr = Box; + +pub mod conerr; +pub mod hookerr; +pub mod logerr; diff --git a/Bootstrap/src/hooks/functions.rs b/Bootstrap/src/hooks/functions.rs new file mode 100644 index 000000000..21c3ce77b --- /dev/null +++ b/Bootstrap/src/hooks/functions.rs @@ -0,0 +1,42 @@ +use crate::errors::{hookerr::HookError, DynErr}; + +use super::HookedFunction; + +pub fn hook(target: usize, detour: usize) -> Result, HookError> { + if target == 0 { + return Err(HookError::Nullpointer("target".to_string())); + } + + if detour == 0 { + return Err(HookError::Nullpointer("detour".to_string())); + } + + unsafe { + let trampoline = dobby_rs::hook(target as dobby_rs::Address, detour as dobby_rs::Address) + .map_err(|e| HookError::Failed(e.to_string())); + + let trampoline = match trampoline { + Ok(t) => t, + Err(e) => return Err(e), + }; + + if trampoline.is_null() { + return Err(HookError::Null); + } + + //return Ok with type annotations + HookedFunction::::from(target, trampoline as usize) + } +} + +pub fn unhook(target: usize) -> Result<(), DynErr> { + if target == 0 { + return Err(HookError::Nullpointer("target".to_string()).into()); + } + + unsafe { + dobby_rs::unhook(target as dobby_rs::Address)?; + } + + Ok(()) +} diff --git a/Bootstrap/src/hooks/init_hook/il2cpp.rs b/Bootstrap/src/hooks/init_hook/il2cpp.rs new file mode 100644 index 000000000..9931119ca --- /dev/null +++ b/Bootstrap/src/hooks/init_hook/il2cpp.rs @@ -0,0 +1,35 @@ +use std::{ffi::c_char, sync::RwLock}; + +use lazy_static::lazy_static; +use unity_rs::il2cpp::types::Il2CppDomain; + +use crate::{ + base_assembly, console, constants::InitFnIl2Cpp, debug, errors::DynErr, hooks::{HookedFunction, invoke_hook}, + internal_failure, runtime, +}; + +lazy_static! { + pub static ref INIT_HOOK: RwLock> = + RwLock::new(HookedFunction::new()); +} + +pub fn detour(name: *const c_char) -> *mut Il2CppDomain { + detour_inner(name).unwrap_or_else(|e| { + internal_failure!("il2cpp_init detour failed: {e}"); + }) +} + +fn detour_inner(name: *const c_char) -> Result<*mut Il2CppDomain, DynErr> { + console::set_handles()?; + + let trampoline = INIT_HOOK.try_read()?; + let domain = trampoline(name); + + debug!("Detaching hook from il2cpp_init")?; + trampoline.unhook()?; + + base_assembly::init(runtime!()?)?; + invoke_hook::hook()?; + + Ok(domain) +} diff --git a/Bootstrap/src/hooks/init_hook/mod.rs b/Bootstrap/src/hooks/init_hook/mod.rs new file mode 100644 index 000000000..efef35ace --- /dev/null +++ b/Bootstrap/src/hooks/init_hook/mod.rs @@ -0,0 +1,44 @@ +mod il2cpp; +mod mono; + +use crate::{ + constants::{InitFnIl2Cpp, InitFnMono}, + debug, + errors::DynErr, + runtime, +}; +use unity_rs::runtime::RuntimeType; + +use super::functions; + +pub fn hook() -> Result<(), DynErr> { + let runtime = runtime!()?; + + match runtime.get_type() { + RuntimeType::Mono(_) => { + debug!("Attaching hook to mono_jit_init_version")?; + + let init_function = runtime.get_export_ptr("mono_jit_init_version")?; + let detour = mono::detour as usize; + + let hooked = functions::hook::(init_function as usize, detour)?; + + let mut init_hook = mono::INIT_HOOK.try_write()?; + *init_hook = hooked; + } + + RuntimeType::Il2Cpp(_) => { + debug!("Attaching hook to il2cpp_init")?; + + let init_function = runtime.get_export_ptr("il2cpp_init")?; + let detour = il2cpp::detour as usize; + + let hooked = functions::hook::(init_function as usize, detour)?; + + let mut init_hook = il2cpp::INIT_HOOK.try_write()?; + *init_hook = hooked; + } + }; + + Ok(()) +} diff --git a/Bootstrap/src/hooks/init_hook/mono.rs b/Bootstrap/src/hooks/init_hook/mono.rs new file mode 100644 index 000000000..3ee3de17b --- /dev/null +++ b/Bootstrap/src/hooks/init_hook/mono.rs @@ -0,0 +1,70 @@ +use std::{ffi::c_char, sync::RwLock}; + +use lazy_static::lazy_static; +use unity_rs::{common::domain::UnityDomain, mono::types::MonoDomain, runtime::RuntimeType}; + +use crate::{ + base_assembly, console, + constants::InitFnMono, + debug, debug_enabled, + errors::DynErr, + hooks::{invoke_hook, HookedFunction}, + icalls, internal_failure, melonenv, runtime, rust_str, +}; + +lazy_static! { + pub static ref INIT_HOOK: RwLock> = + RwLock::new(HookedFunction::new()); +} + +pub fn detour(name: *const c_char, version: *const c_char) -> *mut MonoDomain { + detour_inner(name, version).unwrap_or_else(|e| { + internal_failure!("mono_jit_init_version detour failed: {e}"); + }) +} + +fn detour_inner(name: *const c_char, version: *const c_char) -> Result<*mut MonoDomain, DynErr> { + console::set_handles()?; + + let rust_name = rust_str!(name)?; + let base_dir = melonenv::paths::GAME_DIR + .to_str() + .ok_or("Failed to convert base dir to string")?; + + let runtime = runtime!()?; + + let trampoline = INIT_HOOK.try_read()?; + + let domain_ptr = trampoline(name, version); + let domain = UnityDomain::new(domain_ptr.cast()); + + debug!("Detaching hook from mono_jit_init_version")?; + trampoline.unhook()?; + + if debug_enabled!() { + debug!("Creating Mono Debug Domain")?; + + //the result of this can be ignored. it is not guaranteed that this function exists + let _ = runtime.create_debug_domain(&domain); + } + + debug!("Setting Mono Main Thread")?; + let thread = runtime.get_current_thread()?; + runtime.set_main_thread(&thread)?; + + if let RuntimeType::Mono(mono) = runtime.get_type() { + if !mono.is_old { + debug!("Setting Mono Config")?; + + //the result of this can be ignored. it is not guaranteed that this function exists + let _ = runtime.set_domain_config(&domain, base_dir, rust_name); + } + } + + icalls::init(runtime)?; + base_assembly::init(runtime)?; + + invoke_hook::hook()?; + + Ok(domain.inner.cast()) +} diff --git a/Bootstrap/src/hooks/invoke_hook/il2cpp.rs b/Bootstrap/src/hooks/invoke_hook/il2cpp.rs new file mode 100644 index 000000000..3f192c49d --- /dev/null +++ b/Bootstrap/src/hooks/invoke_hook/il2cpp.rs @@ -0,0 +1,47 @@ +use std::{ffi::c_void, sync::RwLock}; + +use lazy_static::lazy_static; +use unity_rs::{il2cpp::types::{Il2CppMethod, Il2CppObject}, common::method::UnityMethod}; + +use crate::{constants::InvokeFnIl2Cpp, errors::DynErr, hooks::HookedFunction, internal_failure, runtime, debug, base_assembly}; + +lazy_static! { + pub static ref INVOKE_HOOK: RwLock> = + RwLock::new(HookedFunction::new()); +} + +pub fn detour( + method: *mut Il2CppMethod, + obj: *mut Il2CppObject, + params: *mut *mut c_void, + exc: *mut *mut Il2CppObject, +) -> *mut Il2CppObject { + detour_inner(method, obj, params, exc).unwrap_or_else(|e| { + internal_failure!("il2cpp_runtime_invoke detour failed: {e}"); + }) +} + +fn detour_inner( + method: *mut Il2CppMethod, + obj: *mut Il2CppObject, + params: *mut *mut c_void, + exc: *mut *mut Il2CppObject, +) -> Result<*mut Il2CppObject, DynErr> { + let trampoline = INVOKE_HOOK.try_read()?; + let result = trampoline(method, obj, params, exc); + + let runtime = runtime!()?; + + let safe_method = UnityMethod::new(method.cast())?; + let name = safe_method.get_name(runtime)?; + + if name.contains("Internal_ActiveSceneChanged") { + debug!("Detaching hook from il2cpp_runtime_invoke")?; + trampoline.unhook()?; + + base_assembly::pre_start()?; + base_assembly::start()?; + } + + Ok(result) +} diff --git a/Bootstrap/src/hooks/invoke_hook/mod.rs b/Bootstrap/src/hooks/invoke_hook/mod.rs new file mode 100644 index 000000000..ebd07e579 --- /dev/null +++ b/Bootstrap/src/hooks/invoke_hook/mod.rs @@ -0,0 +1,44 @@ +mod il2cpp; +mod mono; + +use crate::{ + constants::{InvokeFnIl2Cpp, InvokeFnMono}, + debug, + errors::DynErr, + runtime, +}; +use unity_rs::runtime::RuntimeType; + +use super::functions; + +pub fn hook() -> Result<(), DynErr> { + let runtime = runtime!()?; + + match runtime.get_type() { + RuntimeType::Mono(_) => { + debug!("Attaching hook to mono_runtime_invoke")?; + + let init_function = runtime.get_export_ptr("mono_runtime_invoke")?; + let detour = mono::detour as usize; + + let hooked = functions::hook::(init_function as usize, detour)?; + + let mut init_hook = mono::INVOKE_HOOK.try_write()?; + *init_hook = hooked; + } + + RuntimeType::Il2Cpp(_) => { + debug!("Attaching hook to il2cpp_runtime_invoke")?; + + let init_function = runtime.get_export_ptr("il2cpp_runtime_invoke")?; + let detour = il2cpp::detour as usize; + + let hooked = functions::hook::(init_function as usize, detour)?; + + let mut init_hook = il2cpp::INVOKE_HOOK.try_write()?; + *init_hook = hooked; + } + }; + + Ok(()) +} diff --git a/Bootstrap/src/hooks/invoke_hook/mono.rs b/Bootstrap/src/hooks/invoke_hook/mono.rs new file mode 100644 index 000000000..893306ca5 --- /dev/null +++ b/Bootstrap/src/hooks/invoke_hook/mono.rs @@ -0,0 +1,62 @@ +use std::{ffi::c_void, sync::RwLock}; + +use lazy_static::lazy_static; +use unity_rs::{ + common::method::UnityMethod, + mono::types::{MonoMethod, MonoObject}, + runtime::RuntimeType, +}; + +use crate::{ + base_assembly, constants::InvokeFnMono, debug, errors::DynErr, hooks::HookedFunction, + internal_failure, runtime, +}; + +lazy_static! { + pub static ref INVOKE_HOOK: RwLock> = + RwLock::new(HookedFunction::new()); +} + +pub fn detour( + method: *mut MonoMethod, + obj: *mut MonoObject, + params: *mut *mut c_void, + exc: *mut *mut MonoObject, +) -> *mut MonoObject { + detour_inner(method, obj, params, exc).unwrap_or_else(|e| { + internal_failure!("mono_runtime_invoke detour failed: {e}"); + }) +} + +fn detour_inner( + method: *mut MonoMethod, + obj: *mut MonoObject, + params: *mut *mut c_void, + exc: *mut *mut MonoObject, +) -> Result<*mut MonoObject, DynErr> { + let trampoline = INVOKE_HOOK.try_read()?; + let result = trampoline(method, obj, params, exc); + + let runtime = runtime!()?; + + let safe_method = UnityMethod::new(method.cast())?; + let name = safe_method.get_name(runtime)?; + + let mono = match runtime.get_type() { + RuntimeType::Mono(mono) => mono, + _ => return Ok(result), + }; + + if (name.contains("Internal_ActiveSceneChanged") + || name.contains("UnityEngine.ISerializationCallbackReceiver.OnAfterSerialize")) + || (mono.is_old && (name.contains("Awake") || name.contains("DoSendMouseEvents"))) + { + debug!("Detaching hook from mono_runtime_invoke")?; + trampoline.unhook()?; + + base_assembly::pre_start()?; + base_assembly::start()?; + } + + Ok(result) +} diff --git a/Bootstrap/src/hooks/mod.rs b/Bootstrap/src/hooks/mod.rs new file mode 100644 index 000000000..8ef123ce2 --- /dev/null +++ b/Bootstrap/src/hooks/mod.rs @@ -0,0 +1,72 @@ +use crate::errors::{hookerr::HookError, DynErr}; +use std::marker::PhantomData; +use std::ops::Deref; + +pub mod functions; +pub mod init_hook; +pub mod invoke_hook; + +#[derive(Debug)] +pub struct HookedFunction { + pub target: usize, + pub trampoline: usize, + pd: PhantomData, +} + +impl HookedFunction { + pub fn new() -> Self { + Self { + target: 0, + trampoline: 0, + pd: PhantomData, + } + } + + pub fn from(target: usize, trampoline: usize) -> Result { + if target == 0 { + return Err(HookError::Nullpointer("target".to_string())); + } + + if trampoline == 0 { + return Err(HookError::Nullpointer("trampoline".to_string())); + } + + Ok(Self { + target, + trampoline, + pd: PhantomData, + }) + } + + pub fn is_hooked(&self) -> bool { + self.target != 0 && self.trampoline != 0 + } + + pub fn unhook(&self) -> Result<(), DynErr> { + if !self.is_hooked() { + return Ok(()); + } + + functions::unhook(self.target)?; + + Ok(()) + } +} + +unsafe impl Send for HookedFunction {} +unsafe impl Sync for HookedFunction {} + +impl Clone for HookedFunction { + fn clone(&self) -> Self { + HookedFunction { ..*self } + } +} + +impl Deref for HookedFunction { + type Target = T; + + fn deref(&self) -> &T { + let t = self.trampoline as *mut std::ffi::c_void; + unsafe { &*(&t as *const *mut _ as *const T) } + } +} diff --git a/Bootstrap/src/icalls/bootstrap_interop.rs b/Bootstrap/src/icalls/bootstrap_interop.rs new file mode 100644 index 000000000..865040ba0 --- /dev/null +++ b/Bootstrap/src/icalls/bootstrap_interop.rs @@ -0,0 +1,18 @@ +use std::ffi::c_void; + +use crate::{error, hooks}; + +pub unsafe fn attach(target: *mut *mut c_void, detour: *mut c_void) { + match hooks::functions::hook::(*target as usize, detour as usize) { + Ok(res) => *target = res.trampoline as *mut c_void, + Err(e) => { + let _ = error!("Failed to hook function: {}", e.to_string()); + } + }; +} + +pub unsafe fn detach(target: *mut *mut c_void, _detour: *mut c_void) { + hooks::functions::unhook(*target as usize).unwrap_or_else(|e| { + let _ = error!("Failed to unhook function: {}", e.to_string()); + }); +} diff --git a/Bootstrap/src/icalls/melon_utils.rs b/Bootstrap/src/icalls/melon_utils.rs new file mode 100644 index 000000000..f691ba790 --- /dev/null +++ b/Bootstrap/src/icalls/melon_utils.rs @@ -0,0 +1,3 @@ +pub fn is_32_bit() -> bool { + cfg!(target_pointer_width = "32") +} diff --git a/Bootstrap/src/icalls/mod.rs b/Bootstrap/src/icalls/mod.rs new file mode 100644 index 000000000..0729ebc1b --- /dev/null +++ b/Bootstrap/src/icalls/mod.rs @@ -0,0 +1,24 @@ +use unity_rs::{common::method::MethodPointer, runtime::FerrexRuntime}; + +use crate::{errors::DynErr, debug}; + +mod melon_utils; +pub mod bootstrap_interop; +mod mono_library; +mod resolve_internals; +mod preload; + +pub fn init(runtime: &FerrexRuntime) -> Result<(), DynErr> { + debug!("Initializing internal calls")?; + + runtime.add_internal_call("MelonLoader.MelonUtils::IsGame32Bit", melon_utils::is_32_bit as MethodPointer)?; + runtime.add_internal_call("MelonLoader.BootstrapInterop::NativeHookAttach", bootstrap_interop::attach as MethodPointer)?; + runtime.add_internal_call("MelonLoader.BootstrapInterop::NativeHookDetach", bootstrap_interop::detach as MethodPointer)?; + runtime.add_internal_call("MelonLoader.MonoInternals.MonoLibrary::GetLibPtr", mono_library::get_lib_ptr as MethodPointer)?; + runtime.add_internal_call("MelonLoader.MonoInternals.MonoLibrary::CastManagedAssemblyPtr", mono_library::cast_assembly_ptr as MethodPointer)?; + runtime.add_internal_call("MelonLoader.MonoInternals.MonoLibrary::GetRootDomainPtr", mono_library::get_domain_ptr as MethodPointer)?; + runtime.add_internal_call("MelonLoader.MonoInternals.ResolveInternals.AssemblyManager::InstallHooks", resolve_internals::install_hooks as MethodPointer)?; + runtime.add_internal_call("MelonLoader.Support.Preload::GetManagedDirectory", preload::get_managed_dir as MethodPointer)?; + + Ok(()) +} \ No newline at end of file diff --git a/Bootstrap/src/icalls/mono_library.rs b/Bootstrap/src/icalls/mono_library.rs new file mode 100644 index 000000000..3f1ecbf4d --- /dev/null +++ b/Bootstrap/src/icalls/mono_library.rs @@ -0,0 +1,34 @@ +use libc::c_void; +use unity_rs::{mono::{types::MonoReflectionAssembly}, runtime::{RuntimeType}}; + +use crate::{runtime, internal_failure}; + +pub fn get_lib_ptr() -> *mut c_void { + let runtime = runtime!().unwrap_or_else(|e| { + internal_failure!("Failed to get runtime: {e}"); + }); + + if let RuntimeType::Mono(mono) = runtime.get_type() { + mono.mono_lib.handle + } else { + internal_failure!("Game is not mono"); + } +} + +pub fn cast_assembly_ptr(assembly: *mut c_void) -> *mut MonoReflectionAssembly { + if assembly.is_null() { + internal_failure!("Failed to cast assembly ptr: Assembly is null"); + } + + assembly.cast() +} + +pub fn get_domain_ptr() -> *mut c_void { + let runtime = runtime!().unwrap_or_else(|e| { + internal_failure!("Failed to get runtime: {e}"); + }); + + runtime.get_domain().unwrap_or_else(|e| { + internal_failure!("Failed to get domain: {e}"); + }).inner +} \ No newline at end of file diff --git a/Bootstrap/src/icalls/preload.rs b/Bootstrap/src/icalls/preload.rs new file mode 100644 index 000000000..fc9ff571b --- /dev/null +++ b/Bootstrap/src/icalls/preload.rs @@ -0,0 +1,21 @@ +use unity_rs::{mono::types::MonoString, common::string::UnityString}; + +use crate::{melonenv, internal_failure, runtime}; + +pub fn get_managed_dir() -> *mut MonoString { + let path = melonenv::paths::get_managed_dir().unwrap_or_else(|e| { + internal_failure!("Failed to get managed dir: {e}"); + }); + + let path = path.to_str().unwrap_or_else(|| { + internal_failure!("Failed to convert managed dir to str"); + }); + + let runtime = runtime!().unwrap_or_else(|e| { + internal_failure!("Failed to get runtime: {e}"); + }); + + UnityString::from_string(path, runtime).unwrap_or_else(|e| { + internal_failure!("Failed to create UnityString: {e}"); + }).inner.cast() +} \ No newline at end of file diff --git a/Bootstrap/src/icalls/resolve_internals.rs b/Bootstrap/src/icalls/resolve_internals.rs new file mode 100644 index 000000000..80a4a5eb8 --- /dev/null +++ b/Bootstrap/src/icalls/resolve_internals.rs @@ -0,0 +1,130 @@ +use std::ffi::{c_char, c_void}; + +use unity_rs::{ + common::{assembly::UnityAssembly, method::MethodPointer, string::UnityString}, + mono::{ + types::{AssemblyName, MonoAssembly, MonoReflectionAssembly}, + AssemblyHookType, + }, +}; + +use crate::{base_assembly, errors::DynErr, internal_failure, runtime}; + +pub fn install_hooks() { + install_hooks_inner().unwrap_or_else(|e| { + internal_failure!("Failed to install assembly hooks: {}", e.to_string()); + }) +} + +fn install_hooks_inner() -> Result<(), DynErr> { + let runtime = runtime!()?; + + runtime.install_assembly_hook(AssemblyHookType::Preload, preload_hook as MethodPointer)?; + runtime.install_assembly_hook(AssemblyHookType::Search, search_hook as MethodPointer)?; + runtime.install_assembly_hook(AssemblyHookType::Load, load_hook as MethodPointer)?; + + Ok(()) +} + +fn assembly_resolve( + aname: *mut AssemblyName, + _user_data: *mut c_void, + mut is_preload: bool, +) -> Result<*mut MonoAssembly, DynErr> { + let runtime = runtime!()?; + + let resolve_method = base_assembly::mono::ASSEMBLYMANAGER_RESOLVE.lock()?; + + if resolve_method.inner.is_null() { + return Err("AssemblyManager.Resolve is null".into()); + } + + let safe_aname = unsafe { aname.as_ref().ok_or("AssemblyName is null")? }; + + let (mut major, mut minor, mut build, mut revision) = ( + safe_aname.major, + safe_aname.minor, + safe_aname.build, + safe_aname.revision, + ); + + let name = UnityString::from_raw(safe_aname.name.cast(), runtime)?; + + let mut args = vec![ + name.inner.cast::(), + std::ptr::addr_of_mut!(major).cast::(), + std::ptr::addr_of_mut!(minor).cast::(), + std::ptr::addr_of_mut!(build).cast::(), + std::ptr::addr_of_mut!(revision).cast::(), + std::ptr::addr_of_mut!(is_preload).cast::(), + ]; + + let res = resolve_method.invoke(None, Some(&mut args), runtime); + + if res.is_err() { + return Ok(std::ptr::null_mut()); + } + + let res = res?; + + match res { + Some(res) => { + if res.inner.is_null() { + return Ok(std::ptr::null_mut()); + } + + let res = res.inner.cast::(); + let res = unsafe { + res.as_ref() + .ok_or("AssemblyManager.Resolve returned null")? + }; + + Ok(res.assembly) + } + None => Ok(std::ptr::null_mut()), + } +} + +fn load_hook_inner(assembly: *mut MonoAssembly) -> Result<(), DynErr> { + if assembly.is_null() { + return Ok(()); + } + + let load_method = base_assembly::mono::ASSEMBLYMANAGER_LOADINFO.lock()?; + if load_method.inner.is_null() { + return Ok(()); + } + + let runtime = runtime!()?; + let safe_assembly = UnityAssembly::new(assembly.cast())?; + + let assembly_object = runtime.get_assembly_object(&safe_assembly)?; + + let mut args = vec![assembly_object.inner]; + + let _ = load_method.invoke(None, Some(&mut args), runtime!()?)?; + + Ok(()) +} + +fn preload_hook( + aname: *mut AssemblyName, + _assemblies_path: *mut *mut c_char, + user_data: *mut c_void, +) -> *mut MonoAssembly { + assembly_resolve(aname, user_data, true).unwrap_or_else(|e| { + internal_failure!("Failed to preload assembly: {e}"); + }) +} + +fn search_hook(aname: *mut AssemblyName, user_data: *mut c_void) -> *mut MonoAssembly { + assembly_resolve(aname, user_data, false).unwrap_or_else(|e| { + internal_failure!("Failed to search assembly: {e}"); + }) +} + +fn load_hook(_assembly: *mut MonoAssembly, _user_data: *mut c_void) { + load_hook_inner(_assembly).unwrap_or_else(|e| { + internal_failure!("Failed to load assembly: {e}"); + }) +} diff --git a/Bootstrap/src/lib.rs b/Bootstrap/src/lib.rs index aa9132617..8c2d908cf 100755 --- a/Bootstrap/src/lib.rs +++ b/Bootstrap/src/lib.rs @@ -1,3 +1,4 @@ +#![feature(is_some_and)] #![allow(non_snake_case)] #![deny( missing_debug_implementations, @@ -13,15 +14,14 @@ #![allow(clippy::inherent_to_string, clippy::type_complexity, improper_ctypes)] #![cfg_attr(docsrs, feature(doc_cfg))] -use ctor::ctor; -use managers::core; - +pub mod base_assembly; +pub mod console; +pub mod constants; +pub mod errors; +pub mod hooks; +pub mod icalls; +pub mod logging; +pub mod melonenv; pub mod utils; -pub mod managers; -#[ctor] -fn init() { - core::init().unwrap_or_else(|e| { - internal_failure!("Failed to initialize Bootstrap: {}", e); - }) -} +pub mod core; diff --git a/Bootstrap/src/utils/assert.rs b/Bootstrap/src/logging/assert.rs old mode 100755 new mode 100644 similarity index 79% rename from Bootstrap/src/utils/assert.rs rename to Bootstrap/src/logging/assert.rs index ddf5b1b73..660d6236b --- a/Bootstrap/src/utils/assert.rs +++ b/Bootstrap/src/logging/assert.rs @@ -14,8 +14,9 @@ macro_rules! internal_failure { ($($arg:tt)*) => {{ let msg = &format_args!($($arg)*).to_string(); - let _ = $crate::err!("Internal Failure: {}", msg); + std::println!("{}", msg); + let _ = $crate::logging::logger::log_console_file($crate::logging::logger::LogLevel::Error, msg); let _ = msgbox::create("Internal Failure", msg, msgbox::IconType::Error); std::process::exit(-1); }}; -} \ No newline at end of file +} diff --git a/Bootstrap/src/logging/logger.rs b/Bootstrap/src/logging/logger.rs new file mode 100644 index 000000000..b21410121 --- /dev/null +++ b/Bootstrap/src/logging/logger.rs @@ -0,0 +1,229 @@ +use crate::{ + constants, debug_enabled, + errors::{logerr::LogError, DynErr}, +}; +use colored::Colorize; +use std::io::Write; + +#[derive(Debug)] +#[repr(u8)] +pub enum LogLevel { + Info, + Warning, + Error, + Debug, +} + +impl std::convert::TryFrom for LogLevel { + type Error = DynErr; + + fn try_from(value: u8) -> Result>::Error> { + match value { + 0 => Ok(LogLevel::Info), + 1 => Ok(LogLevel::Warning), + 2 => Ok(LogLevel::Error), + 3 => Ok(LogLevel::Debug), + _ => Err("Invalid value for enum `LogLevel` possible: [1, 2, 3]".into()), + } + } +} + +macro_rules! log_path { + () => { + std::env::current_dir()?.join("MelonLoader").join("Latest-Bootstrap.log") + }; +} + +pub fn init() -> Result<(), DynErr> { + let log_file = log_path!(); + + if log_file.exists() { + std::fs::remove_file(log_file).map_err(|_| LogError::FailedToDeleteOldLog)?; + } + + Ok(()) +} + +fn write(msg: &str) -> Result<(), DynErr> { + let log_file = log_path!(); + + let mut file = std::fs::OpenOptions::new() + .write(true) + .append(true) + .create(true) + .open(&log_file) + .map_err(|_| LogError::FailedToWriteToLog)?; + + let message = format!("{}\r\n", msg); + + file.write_all(message.as_bytes()) + .map_err(|_| LogError::FailedToWriteToLog)?; + + Ok(()) +} + +/// logs to console and file, should not be used, use the log! macro instead +pub fn log_console_file(level: LogLevel, message: &str) -> Result<(), LogError> { + match level { + LogLevel::Info => { + // [19:11:50.321] message + let console_string = format!( + "{}{}{} {}", + "[".bright_black(), + timestamp().color(constants::GREEN), + "]".bright_black(), + message + ); + + let file_string = format!("[{}] {}", timestamp(), message); + + println!("{}", console_string); + write(&file_string).map_err(|_| LogError::FailedToWriteToLog)?; + } + LogLevel::Warning => { + //[19:11:50.321] [WARNING] message + let console_string = format!("[{}] [WARNING] {}", timestamp(), message); + + let file_string = format!("[{}] [WARNING] {}", timestamp(), message); + + println!("{}", console_string.bright_yellow()); + + write(&file_string).map_err(|_| LogError::FailedToWriteToLog)?; + } + LogLevel::Error => { + //[19:11:50.321] [ERROR] message + let log_string = format!("[{}] [ERROR] {}", timestamp(), message); + + println!("{}", log_string.color(constants::RED)); + write(&log_string).map_err(|_| LogError::FailedToWriteToLog)?; + } + LogLevel::Debug => { + if !debug_enabled!() { + return Ok(()); + } + //[19:11:50.321] [DEBUG] message + let console_string = format!( + "{}{}{} {}{}{} {}", + "[".bright_black(), + timestamp().color(constants::GREEN), + "]".bright_black(), + "[".bright_black(), + "DEBUG".color(constants::BLUE), + "]".bright_black(), + message + ); + + let file_string = format!("[{}] [DEBUG] {}", timestamp(), message); + + println!("{}", console_string); + write(&file_string).map_err(|_| LogError::FailedToWriteToLog)?; + } + } + + Ok(()) +} + +/// Fetches the current time, and formats it. +/// +/// returns a String, formatted as 15:23:24:123 +fn timestamp() -> String { + let now = chrono::Local::now(); + let time = now.time(); + + time.format("%H:%M:%S.%3f").to_string() +} + +/// Logs a message to the console and log file +/// +/// # Example +/// ``` +/// log!("Hello World!")?; +/// ``` +/// log! returns a Result<(), Box>, so please handle this. +#[macro_export] +macro_rules! log { + //case 1: empty + () => { + $crate::logging::logger::log_console_file($crate::logging::logger::LogLevel::Info, "") + }; + + //case 3: multiple arguments + ($($arg:tt)*) => {{ + $crate::logging::logger::log_console_file($crate::logging::logger::LogLevel::Info, &format_args!($($arg)*).to_string()) + }}; +} + +/// Logs a message to the console and log file, with a warning prefix +/// +/// # Example +/// ``` +/// warn!("Hello World!")?; +/// ``` +/// +/// warn! returns a Result<(), Box>, so please handle this. +#[macro_export] +macro_rules! warn { + //case 1: empty + () => { + $crate::logging::logger::log_console_file($crate::logging::logger::LogLevel::Warning, "") + }; + + //case 3: multiple arguments + ($($arg:tt)*) => {{ + $crate::logging::logger::log_console_file($crate::logging::logger::LogLevel::Warning, &format_args!($($arg)*).to_string()) + }}; +} + +/// Logs a message to the console and log file, with an error prefix +/// +/// # Example +/// ``` +/// error!("Hello World!")?; +/// ``` +/// +/// error! returns a Result<(), Box>, so please handle this. +#[macro_export] +macro_rules! error { + //case 1: empty + () => { + $crate::logging::logger::log_console_file($crate::logging::logger::LogLevel::Error, "") + }; + + //case 3: multiple arguments + ($($arg:tt)*) => {{ + $crate::logging::logger::log_console_file($crate::logging::logger::LogLevel::Error, &format_args!($($arg)*).to_string()) + }}; +} + +/// Logs a message to the console and log file, with a debug prefix +/// +/// # Example +/// ``` +/// debug!("Hello World!")?; +/// ``` +/// +/// debug! returns a Result<(), Box>, so please handle this. +#[macro_export] +macro_rules! debug { + //case 1: empty + () => { + $crate::logging::logger::log_console_file($crate::logging::logger::LogLevel::Debug, "") + }; + + //case 3: multiple arguments + ($($arg:tt)*) => {{ + $crate::logging::logger::log_console_file($crate::logging::logger::LogLevel::Debug, &format_args!($($arg)*).to_string()) + }}; +} + +#[macro_export] +macro_rules! cstr { + ($s:expr) => { + std::ffi::CString::new($s)?.as_ptr() + }; + + //format str + ($($arg:tt)*) => { + std::ffi::CString::new(format!($($arg)*))?.as_ptr() + }; +} diff --git a/Bootstrap/src/logging/mod.rs b/Bootstrap/src/logging/mod.rs new file mode 100644 index 000000000..5f640a9fc --- /dev/null +++ b/Bootstrap/src/logging/mod.rs @@ -0,0 +1,2 @@ +pub mod assert; +pub mod logger; \ No newline at end of file diff --git a/Bootstrap/src/managers/base_asm.rs b/Bootstrap/src/managers/base_asm.rs deleted file mode 100644 index 309131693..000000000 --- a/Bootstrap/src/managers/base_asm.rs +++ /dev/null @@ -1,53 +0,0 @@ -use std::error::Error; - -use unity_rs::runtime::{Runtime, UnityRuntime}; - -use super::dotnet; - -pub fn init() -> Result<(), Box> { - let runtime = Runtime::new()?; - let runtime = runtime.runtime; - - match runtime { - UnityRuntime::MonoRuntime(mono) => { - super::mono::init(&mono)?; - }, - - UnityRuntime::Il2Cpp(il2cpp) => { - dotnet::init(&il2cpp)?; - } - } - Ok(()) -} - -pub fn pre_start() -> Result<(), Box> { - let runtime = Runtime::new()?; - let runtime = runtime.runtime; - - match runtime { - UnityRuntime::MonoRuntime(_) => { - super::mono::pre_start()?; - }, - - UnityRuntime::Il2Cpp(_) => { - dotnet::pre_start()?; - } - } - Ok(()) -} - -pub fn start() -> Result<(), Box> { - let runtime = Runtime::new()?; - let runtime = runtime.runtime; - - match runtime { - UnityRuntime::MonoRuntime(_) => { - super::mono::start()?; - }, - - UnityRuntime::Il2Cpp(_) => { - dotnet::start()?; - } - } - Ok(()) -} \ No newline at end of file diff --git a/Bootstrap/src/managers/core.rs b/Bootstrap/src/managers/core.rs deleted file mode 100755 index d323adacf..000000000 --- a/Bootstrap/src/managers/core.rs +++ /dev/null @@ -1,38 +0,0 @@ -use std::{error::Error}; - -use unity_rs::runtime::Runtime; - -use crate::{utils::log, debug}; - -use super::hooks; - -pub fn version() -> &'static str { - "0.6.0" -} - -pub fn is_alpha() -> bool { - false -} - -pub fn init() -> Result<(), Box> { - log::init()?; - - debug!("Logging initialized")?; - - #[cfg(windows)] - { - use crate::utils::console; - console::init()?; - } - - hooks::init( - Runtime::new()?.runtime - )?; - - #[cfg(windows)] - { - use crate::utils::console; - console::null_handles(); - } - Ok(()) -} \ No newline at end of file diff --git a/Bootstrap/src/managers/dotnet.rs b/Bootstrap/src/managers/dotnet.rs deleted file mode 100644 index 6e890a501..000000000 --- a/Bootstrap/src/managers/dotnet.rs +++ /dev/null @@ -1,146 +0,0 @@ -use std::{error, str::FromStr, ptr::addr_of_mut, mem::transmute}; - -use libc::c_void; -use netcorehost::{nethost, pdcstr, pdcstring::PdCString}; -use unity_rs::il2cpp::Il2Cpp; - -use crate::{utils::files, debug}; - -use super::internal_calls; - -#[repr(C)] -#[derive(Debug)] -pub struct HostImports { - //public delegate* unmanaged[Stdcall] LoadAssemblyAndGetPtr; - pub load_assembly_get_ptr: *mut c_void, - - pub initialize: *mut c_void, - pub pre_start: *mut c_void, - pub start: *mut c_void, -} - -#[repr(C)] -#[derive(Debug)] -pub struct HostExports { - pub hook_attach: fn(*mut *mut c_void, *mut c_void), - pub hook_detach: fn(*mut *mut c_void, *mut c_void), -} - -static mut IMPORTS: Option = None; - -pub fn init(il2cpp: &Il2Cpp) -> Result<(), Box> { - let domain = il2cpp.domain_current()?; - let _ = il2cpp.thread_attach(domain)?; - - let hostfxr = nethost::load_hostfxr()?; - let config_path = files::runtime_dir()?.join("MelonLoader.runtimeconfig.json"); - - if !config_path.exists() { - Err("Failed to find the MelonLoader.runtimeconfig.json file!")? - } - - let config_path = config_path.to_str().ok_or("Failed to convert the path to a string!")?; - - let context = hostfxr.initialize_for_runtime_config(PdCString::from_str(config_path)?)?; - - let runtime_dir = files::runtime_dir()?; - - let ml_managed_path = runtime_dir.join("MelonLoader.NativeHost.dll"); - let wide_ml_managed_path = wide_str(ml_managed_path.to_str().ok_or("Failed to convert the path to a string!")?); - let ml_managed_path = PdCString::from_os_str(ml_managed_path.as_os_str())?; - - let dotnet_type = pdcstr!("MelonLoader.NativeHost.NativeEntryPoint, MelonLoader.NativeHost"); - let wide_dotnet_type = wide_str("MelonLoader.NativeHost.NativeEntryPoint, MelonLoader.NativeHost"); - let dotnet_method = pdcstr!("LoadStage1"); - let dotnet_method_stage_2 = wide_str("LoadStage2"); - - let loader = context.get_delegate_loader_for_assembly(ml_managed_path.clone())?; - let init = loader.get_function_with_unmanaged_callers_only::(dotnet_type, dotnet_method)?; - - let mut imports = HostImports { - load_assembly_get_ptr: std::ptr::null_mut(), - initialize: std::ptr::null_mut(), - pre_start: std::ptr::null_mut(), - start: std::ptr::null_mut(), - }; - - let mut exports = HostExports { - hook_attach: internal_calls::NativeHookAttach, - hook_detach: internal_calls::NativeHookDetach, - }; - - debug!("[Dotnet] Invoking LoadStage1...")?; - - init(addr_of_mut!(imports)); - - debug!("[Dotnet] Reloading NativeHost into correct load context and getting LoadStage2 pointer...")?; - - let mut init2 = std::ptr::null_mut(); - - unsafe { - - let func: unsafe extern "C" fn(isize, isize, isize, *mut *mut c_void) = transmute(imports.load_assembly_get_ptr); - - func( - wide_ml_managed_path.as_ptr() as isize, - wide_dotnet_type.as_ptr() as isize, - dotnet_method_stage_2.as_ptr() as isize, - addr_of_mut!(init2), - ); - } - - debug!("[Dotnet] Invoking LoadStage2...")?; - unsafe { - let func = transmute::<*mut c_void, fn(*mut HostImports, *mut HostExports)>(init2); - func(addr_of_mut!(imports), addr_of_mut!(exports)); - - if imports.initialize.is_null() { - Err("Failed to find HostImports::Initialize!")? - } - - let func: unsafe extern "system" fn() = transmute(imports.initialize); - - func(); - - IMPORTS = Some(imports); - } - - Ok(()) -} - -pub fn pre_start() -> Result<(), Box> { - unsafe { - if let Some(imports) = &IMPORTS { - if imports.pre_start.is_null() { - Err("Failed to find HostImports::PreStart!")? - } - - let func: unsafe extern "system" fn() = transmute(imports.pre_start); - func(); - } else { - Err("Failed to find HostImports!")? - } - } - - Ok(()) -} - -pub fn start() -> Result<(), Box> { - unsafe { - if let Some(imports) = &IMPORTS { - if imports.start.is_null() { - Err("Failed to find HostImports::Start!")? - } - let func: unsafe extern "system" fn() = transmute(imports.start); - func(); - } else { - Err("Failed to find HostImports")? - } - } - - Ok(()) -} - -pub fn wide_str(s: &str) -> Vec { - s.encode_utf16().chain(Some(0)).collect() -} \ No newline at end of file diff --git a/Bootstrap/src/managers/hooks.rs b/Bootstrap/src/managers/hooks.rs deleted file mode 100755 index 01c2638c9..000000000 --- a/Bootstrap/src/managers/hooks.rs +++ /dev/null @@ -1,247 +0,0 @@ -use std::{ffi::{c_char, c_void, CStr}, mem::transmute, error}; -use thiserror::Error; -use unity_rs::{mono::{types::{MonoDomain, MonoMethod, MonoObject}}, il2cpp::{types::{Il2CppDomain, Il2CppObject, Il2CppMethod}}, runtime::{UnityRuntime, Runtime}}; - -use crate::{internal_failure, debug, utils::{debug, detours::{self}}, managers::{internal_calls, base_asm}}; - -#[derive(Debug, Error)] -pub enum HookError { - #[error("Failed to get mono_jit_init_version")] - MonoJitInitVersion, - #[error("Failed to get mono_runtime_invoke")] - MonoRuntimeInvoke, - #[error("Failed to get il2cpp_method_get_name")] - Il2CppMethodGetName, - #[error("Failed to get il2cpp_runtime_invoke")] - Il2CppRuntimeInvoke, - #[error("Failed to hook function")] - HookFunction, -} - -type InvokeFnMono = fn(*mut MonoMethod, *mut MonoObject, *mut *mut c_void, *mut *mut MonoObject) -> *mut MonoObject; -type InvokeFnIl2Cpp = fn(*mut Il2CppMethod, *mut Il2CppObject, *mut *mut c_void, *mut *mut Il2CppObject) -> *mut Il2CppObject; - -type InitFnMono = fn(*const c_char, *const c_char) -> *mut MonoDomain; -type InitFnIl2Cpp = fn(*const c_char) -> *mut Il2CppDomain; - -static mut MONO_JIT_INIT_ORIGINAL: Option = None; -static mut MONO_RUNTIME_INVOKE_ORIGINAL: Option = None; - -static mut IL2CPP_INIT_ORIGINAL: Option = None; -static mut IL2CPP_RUNTIME_INVOKE_ORIGINAL: Option = None; - - -pub fn init(runtime: UnityRuntime) -> Result<(), Box> { - match runtime { - UnityRuntime::MonoRuntime(mono) => unsafe { - debug!("Attaching hook to mono_jit_init_version")?; - let func = mono.exports.mono_jit_init_version.ok_or(HookError::MonoJitInitVersion)?; - - let trampoline = detours::hook(*func as usize, mono_jit_init_version_detour as usize).map_err(|_| HookError::HookFunction)?; - - MONO_JIT_INIT_ORIGINAL = Some(transmute(trampoline)); - - Ok(()) - }, - UnityRuntime::Il2Cpp(il2cpp) => unsafe { - debug!("Attaching hook to il2cpp_init")?; - let func = il2cpp.exports.il2cpp_init.ok_or(HookError::MonoJitInitVersion)?; - - let trampoline = detours::hook(*func as usize, il2cpp_init_detour as usize).map_err(|_| HookError::HookFunction)?; - - IL2CPP_INIT_ORIGINAL = Some(transmute(trampoline)); - - Ok(()) - }, - } -} - -fn invoke(runtime: UnityRuntime) -> Result<(), HookError> { - match runtime { - UnityRuntime::MonoRuntime(mono) => unsafe { - let func = mono.exports.mono_runtime_invoke.ok_or(HookError::MonoRuntimeInvoke)?; - - let trampoline = detours::hook(*func as usize, mono_runtime_invoke_detour as usize).map_err(|_| HookError::HookFunction)?; - - MONO_RUNTIME_INVOKE_ORIGINAL = Some(transmute(trampoline)); - - Ok(()) - }, - UnityRuntime::Il2Cpp(il2cpp) => unsafe { - let func = il2cpp.exports.il2cpp_runtime_invoke.ok_or(HookError::MonoRuntimeInvoke)?; - - let trampoline = detours::hook(*func as usize, il2cpp_runtime_invoke_detour as usize).map_err(|_| HookError::HookFunction)?; - - IL2CPP_RUNTIME_INVOKE_ORIGINAL = Some(transmute(trampoline)); - Ok(()) - }, - } -} - -fn mono_jit_init_version_detour(name: *const c_char, version: *const c_char) -> *mut MonoDomain { - mono_jit_init_version_detour_inner(name, version).unwrap_or_else(|e| { - internal_failure!("mono_jit_init_version detour failed: {e}"); - }) -} - -fn mono_runtime_invoke_detour(method: *mut MonoMethod, obj: *mut MonoObject, params: *mut *mut c_void, exc: *mut *mut MonoObject) -> *mut MonoObject { - mono_runtime_invoke_detour_inner(method, obj, params, exc).unwrap_or_else(|e| { - internal_failure!("mono_runtime_invoke detour failed: {e}"); - }) -} - - -fn il2cpp_init_detour(name: *const c_char) -> *mut Il2CppDomain { - il2cpp_init_detour_inner(name).unwrap_or_else(|e| { - internal_failure!("il2cpp_init detour failed: {e}"); - }) -} - -fn il2cpp_runtime_invoke_detour(method: *mut Il2CppMethod, obj: *mut Il2CppObject, params: *mut *mut c_void, exc: *mut *mut Il2CppObject) -> *mut Il2CppObject { - il2cpp_runtime_invoke_detour_inner(method, obj, params, exc).unwrap_or_else(|e| { - internal_failure!("il2cpp_runtime_invoke detour failed: {e}"); - }) -} - - -fn mono_jit_init_version_detour_inner(name: *const c_char, version: *const c_char) -> Result<*mut MonoDomain, Box> { - #[cfg(windows)] - { - use crate::utils::console; - console::set_handles(); - } - - let trampoline = unsafe { - MONO_JIT_INIT_ORIGINAL.ok_or_else(|| "mono_jit_init_version trampoline not found")? - }; - - let domain = trampoline(name, version); - - let runtime = Runtime::new()?; - - if let UnityRuntime::MonoRuntime(mono) = &runtime.runtime{ - debug!("Detaching hook from mono_jit_init_version")?; - let func = mono.exports.clone(); - let func = func.mono_jit_init_version.ok_or(HookError::MonoJitInitVersion)?; - detours::unhook(*func as usize)?; - - if debug::enabled() { - debug!("Creating Mono Debug Domain")?; - if let Some(mono_debug_domain_create) = &mono.exports.mono_debug_domain_create { - mono_debug_domain_create(domain); - } - } - - debug!("Setting Mono Main Thread")?; - let thread = runtime.get_current_thread()?; - mono.thread_set_main(thread)?; - - if !mono.is_old { - if let Some(mono_domain_set_config) = &mono.exports.mono_domain_set_config { - let base_dir = std::env::current_dir()?; - let base_dir = base_dir.to_str().ok_or("Failed to convert base dir to string")?; - let base_dir = std::ffi::CString::new(base_dir)?; - - debug!("Setting Mono Config")?; - mono_domain_set_config(domain, base_dir.as_ptr(), name); - } - } - - internal_calls::init(mono.to_owned())?; - base_asm::init()?; - - debug!("attaching hook to mono_runtime_invoke")?; - invoke(runtime.runtime)?; - } - - - - Ok(domain) -} - -fn mono_runtime_invoke_detour_inner(method: *mut MonoMethod, obj: *mut MonoObject, params: *mut *mut c_void, exc: *mut *mut MonoObject) -> Result<*mut MonoObject, Box> { - let trampoline = unsafe { - MONO_RUNTIME_INVOKE_ORIGINAL.ok_or("mono_runtime_invoke trampoline not found")? - }; - - let result = trampoline(method, obj, params, exc); - - let name = MonoMethod::get_name(method)?; - - let runtime = Runtime::new()?; - let mono = match &runtime.runtime { - UnityRuntime::MonoRuntime(mono) => mono, - _ => return Ok(result), - }; - - if (name.contains("Internal_ActiveSceneChanged") || name.contains("UnityEngine.ISerializationCallbackReceiver.OnAfterSerialize")) || - (mono.is_old && (name.contains("Awake") || name.contains("DoSendMouseEvents"))) { - debug!("Detaching hook from mono_runtime_invoke")?; - - let func = mono.exports.clone().mono_runtime_invoke.ok_or(HookError::MonoRuntimeInvoke)?; - detours::unhook(*func as usize)?; - - base_asm::pre_start()?; - base_asm::start()?; - } - - Ok(result) -} - -fn il2cpp_init_detour_inner(name: *const c_char) -> Result<*mut Il2CppDomain, Box> { - #[cfg(windows)] - { - use crate::utils::console; - console::set_handles(); - } - - let trampoline = unsafe { - IL2CPP_INIT_ORIGINAL.ok_or("il2cpp_init trampoline not found")? - }; - - let domain = trampoline(name); - - let runtime = Runtime::new()?; - - if let UnityRuntime::Il2Cpp(il2cpp) = &runtime.runtime { - debug!("Detaching hook from il2cpp_init")?; - let func = il2cpp.exports.clone().il2cpp_init.ok_or(HookError::MonoJitInitVersion)?; - detours::unhook(*func as usize)?; - - base_asm::init()?; - invoke(runtime.runtime)?; - } - - Ok(domain) -} - -fn il2cpp_runtime_invoke_detour_inner(method: *mut Il2CppMethod, obj: *mut Il2CppObject, params: *mut *mut c_void, exc: *mut *mut Il2CppObject) -> Result<*mut Il2CppObject, Box> { - let trampoline = unsafe { - IL2CPP_RUNTIME_INVOKE_ORIGINAL.ok_or("il2cpp_runtime_invoke trampoline not found")? - }; - let result = trampoline(method, obj, params, exc); - - let runtime = Runtime::new()?; - - if let UnityRuntime::Il2Cpp(il2cpp) = &runtime.runtime { - let exports = &il2cpp.exports; - - let il2cpp_method_get_name = exports.clone().il2cpp_method_get_name.ok_or(HookError::Il2CppMethodGetName)?; - let il2cpp_runtime_invoke = exports.clone().il2cpp_runtime_invoke.ok_or(HookError::Il2CppRuntimeInvoke)?; - - let name = unsafe { - CStr::from_ptr(il2cpp_method_get_name(method)).to_str()? - }; - - if name.contains("Internal_ActiveSceneChanged") { - debug!("Detaching hook from il2cpp_runtime_invoke")?; - - detours::unhook(*il2cpp_runtime_invoke as usize)?; - - base_asm::pre_start()?; - base_asm::start()?; - } - } - - Ok(result) -} \ No newline at end of file diff --git a/Bootstrap/src/managers/internal_calls.rs b/Bootstrap/src/managers/internal_calls.rs deleted file mode 100755 index da49876b0..000000000 --- a/Bootstrap/src/managers/internal_calls.rs +++ /dev/null @@ -1,207 +0,0 @@ -use std::{ - error::{Error, self}, - ffi::{c_void, c_char}, mem::transmute -}; -use unity_rs::{mono::{Mono, types::{MonoReflectionAssembly, AssemblyName, MonoAssembly, MonoString, MonoMethod}, AssemblyHookType}, runtime::{Runtime, UnityRuntime}}; - -use crate::{debug, internal_failure, err, utils::{files, detours}}; - -static mut MONO: Option = None; - -pub fn init(mono: Mono) -> Result<(), Box> { - debug!("Initializing internal calls")?; - - mono.add_internal_call("MelonLoader.MelonUtils::IsGame32Bit", IsGame32Bit as usize)?; - mono.add_internal_call("MelonLoader.BootstrapInterop::NativeHookAttach", NativeHookAttach as usize)?; - mono.add_internal_call("MelonLoader.BootstrapInterop::NativeHookDetach", NativeHookDetach as usize)?; - mono.add_internal_call("MelonLoader.MonoInternals.MonoLibrary::GetLibPtr", GetLibPtr as usize)?; - mono.add_internal_call("MelonLoader.MonoInternals.MonoLibrary::CastManagedAssemblyPtr", CastManagedAssemblyPtr as usize)?; - mono.add_internal_call("MelonLoader.MonoInternals.ResolveInternals.AssemblyManager::InstallHooks", InstallHooks as usize)?; - mono.add_internal_call("MelonLoader.MonoInternals.MonoLibrary::GetRootDomainPtr", GetRootDomainPtr as usize)?; - mono.add_internal_call("MelonLoader.Support.Preload::GetManagedDirectory", GetManagedDirectory as usize)?; - - unsafe { - MONO = Some(mono); - } - Ok(()) -} - -fn IsGame32Bit() -> bool { - cfg!(target_pointer_width = "32") -} - -pub fn NativeHookAttach(target: *mut *mut c_void, detour: *mut c_void) { - unsafe { - match detours::hook(*target as usize, detour as usize) { - Ok(res) => *target = transmute(res), - Err(e) => { - let _ = err!("Failed to hook function: {}", e.to_string()); - } - }; - } -} - -pub fn NativeHookDetach(target: *mut *mut c_void, _detour: *mut c_void) { - unsafe { - detours::unhook(*target as usize).unwrap_or_else(|e| { - let _ = err!("Failed to unhook function: {}", e.to_string()); - }); - } -} - -fn GetLibPtr() -> *mut c_void { - get_lib_ptr().unwrap_or_else(|e| { - internal_failure!("Failed to get lib ptr: {e}"); - }) -} - -fn get_lib_ptr() -> Result<*mut c_void, Box> { - let runtime = Runtime::new()?; - - if let UnityRuntime::MonoRuntime(mono) = runtime.runtime { - Ok(mono.mono_lib.handle) - } else { - Err("Game is not mono".into()) - } -} - -fn CastManagedAssemblyPtr(assembly: *mut c_void) -> *mut MonoReflectionAssembly { - cast_assembly_ptr(assembly).unwrap_or_else(|e| { - internal_failure!("Failed to cast assembly ptr: {e}"); - }) -} - -fn cast_assembly_ptr(assembly: *mut c_void) -> Result<*mut MonoReflectionAssembly, Box> { - Ok(assembly.cast()) -} - -fn InstallHooks() { - install_hooks().unwrap_or_else(|e| { - internal_failure!("Failed to install hooks: {e}"); - }) -} - -fn install_hooks() -> Result<(), Box> { - let runtime = Runtime::new()?; - - if let UnityRuntime::MonoRuntime(mono) = runtime.runtime { - mono.install_assembly_hook(AssemblyHookType::Preload, preload_hook as usize)?; - mono.install_assembly_hook(AssemblyHookType::Search, search_hook as usize)?; - mono.install_assembly_hook(AssemblyHookType::Load, load_hook as usize)?; - } else { - return Err("Game is not mono".into()); - } - - Ok(()) -} - -fn preload_hook(aname: *mut AssemblyName, _assemblies_path: *mut *mut c_char, user_data: *mut c_void) -> *mut MonoAssembly { - assembly_resolve(aname, user_data, true).unwrap_or_else(|e| { - internal_failure!("Failed to preload assembly: {e}"); - }) -} - -fn search_hook(aname: *mut AssemblyName, user_data: *mut c_void) -> *mut MonoAssembly { - assembly_resolve(aname, user_data, false).unwrap_or_else(|e| { - internal_failure!("Failed to search assembly: {e}"); - }) -} - -fn assembly_resolve(aname: *mut AssemblyName, _user_data: *mut c_void, mut is_preload: bool) -> Result<*mut MonoAssembly, Box> { - let resolve = unsafe { - match super::mono::ASSEMBLYMANAGER_RESOLVE { - Some(resolve) => resolve, - None => return Err("AssemblyManager.Resolve is null".into()) - } - }; - - let safe_aname = unsafe { - aname.as_ref().ok_or("AssemblyName is null")? - }; - - let name = MonoString::from_raw(safe_aname.name)?; - - let mut major = safe_aname.major; - let mut minor = safe_aname.minor; - let mut build = safe_aname.build; - let mut revision = safe_aname.revision; - - let mut args = vec![ - name.cast::(), - std::ptr::addr_of_mut!(major).cast::(), - std::ptr::addr_of_mut!(minor).cast::(), - std::ptr::addr_of_mut!(build).cast::(), - std::ptr::addr_of_mut!(revision).cast::(), - std::ptr::addr_of_mut!(is_preload).cast::() - ]; - - let res = MonoMethod::invoke(resolve, None, Some(&mut args)); - - if res.is_err() { - return Ok(std::ptr::null_mut()); - } - - let res = res?; - - match res { - Some(res) => { - if res.is_null() { - return Ok(std::ptr::null_mut()); - } - - let res = res.cast::(); - let res = unsafe { - res.as_ref().ok_or("AssemblyManager.Resolve returned null")? - }; - Ok(res.assembly) - }, - None => Ok(std::ptr::null_mut()) - } -} - -fn load_hook(_assembly: *mut MonoAssembly, _user_data: *mut c_void) { - load_hook_inner(_assembly).unwrap_or_else(|e| { - internal_failure!("Failed to load assembly: {e}"); - }) -} - -fn load_hook_inner(_assembly: *mut MonoAssembly) -> Result<(), Box> { - if _assembly.is_null() { - return Ok(()); - } - let load_info = unsafe { - match super::mono::ASSEMBLYMANAGER_LOADINFO { - Some(load_info) => load_info, - None => return Err("AssemblyManager.LoadInfo is null".into()) - } - }; - - let reference = MonoAssembly::as_object(_assembly)?; - - let mut args = vec![reference as *mut c_void]; - - let _ = MonoMethod::invoke(load_info, None, Some(&mut args))?; - - Ok(()) -} - -fn GetRootDomainPtr() -> *mut c_void { - unsafe { - match MONO { - Some(ref mono) => mono.get_domain().unwrap_or(std::ptr::null_mut()).cast(), - None => std::ptr::null_mut() - } - } -} - -fn GetManagedDirectory() -> *mut MonoString { - get_managed_directory().unwrap_or_else(|e| { - internal_failure!("Failed to get managed directory: {}", e.to_string()); - }) -} - -fn get_managed_directory() -> Result<*mut MonoString, Box> { - let path = files::managed_dir()?; - let path = path.to_str().ok_or_else(|| "Failed to convert path to string")?; - Ok(MonoString::new(path)?) -} \ No newline at end of file diff --git a/Bootstrap/src/managers/mod.rs b/Bootstrap/src/managers/mod.rs deleted file mode 100755 index ee16fc910..000000000 --- a/Bootstrap/src/managers/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod core; -pub mod hooks; -pub mod internal_calls; -pub mod base_asm; -pub mod mono; -pub mod dotnet; \ No newline at end of file diff --git a/Bootstrap/src/managers/mono.rs b/Bootstrap/src/managers/mono.rs deleted file mode 100644 index 8d1fc6282..000000000 --- a/Bootstrap/src/managers/mono.rs +++ /dev/null @@ -1,115 +0,0 @@ -use std::error; - -use unity_rs::mono::{Mono, types::{MonoImage, MonoClass, MonoMethod}}; - -use crate::{utils::files, debug}; - -/// mono prestart method -pub static mut MONO_PRESTART: Option<*mut MonoMethod> = None; -/// mono start method -pub static mut MONO_START: Option<*mut MonoMethod> = None; - -/// assembly manager resolve -pub static mut ASSEMBLYMANAGER_RESOLVE: Option<*mut MonoMethod> = None; -/// assembly manager load -pub static mut ASSEMBLYMANAGER_LOADINFO: Option<*mut MonoMethod> = None; - -pub fn init(mono: &Mono) -> Result<(), Box> { - preload(mono)?; - - debug!("Initializing base assembly")?; - - let melonloader_dll = files::melonloader_dir()? - .join("net35") - .join("MelonLoader.dll"); - - if !melonloader_dll.exists() { - return Err("MelonLoader.dll not found".into()); - } - - let melonloader_dll = melonloader_dll.to_str().ok_or_else(|| "MelonLoader.dll path is not valid UTF-8")?; - - let image = MonoImage::open(melonloader_dll)?; - let class = MonoImage::get_class(image, "MelonLoader", "Core")?; - let method = MonoClass::get_method(class, "Initialize", 0)?; - - unsafe { - MONO_PRESTART = Some( - MonoClass::get_method(class, "PreStart", 0)? - ); - - MONO_START = Some( - MonoClass::get_method(class, "Start", 0)? - ); - } - - let assemblymanager_class = MonoImage::get_class(image, "MelonLoader.MonoInternals.ResolveInternals", "AssemblyManager")?; - - unsafe { - ASSEMBLYMANAGER_RESOLVE = Some( - MonoClass::get_method(assemblymanager_class, "Resolve", 6)? - ); - - ASSEMBLYMANAGER_LOADINFO = Some( - MonoClass::get_method(assemblymanager_class, "LoadInfo", 1)? - ); - } - - let _ = MonoMethod::invoke(method, None, None)?; - - Ok(()) -} - -pub fn pre_start() -> Result<(), Box> { - match unsafe { MONO_PRESTART } { - Some(method) => { - let res = MonoMethod::invoke(method, None, None); - match res { - Ok(_) => Ok(()), - Err(e) => Err(e) - } - }, - None => { - Err("Mono prestart method not found".into()) - } - } -} - -pub fn start() -> Result<(), Box> { - match unsafe { MONO_START } { - Some(method) => { - let res = MonoMethod::invoke(method, None, None); - match res { - Ok(_) => Ok(()), - Err(e) => Err(e) - } - }, - None => { - Err("Mono start method not found".into()) - } - } -} - -fn preload(mono: &Mono) -> Result<(), Box> { - if !mono.is_old { - return Ok(()); - } - - let mut preload_path = files::melonloader_dir()?; - preload_path.extend(["Dependencies", "SupportModules", "Preload.dll"].iter()); - - if !preload_path.exists() { - return Err("Preload.dll not found".into()); - } - - let preload_path = preload_path.to_str().ok_or_else(|| "Preload.dll path is not valid UTF-8")?; - - let image = MonoImage::open(preload_path)?; - let class = MonoImage::get_class(image, "MelonLoader.Support", "Preload")?; - let method = MonoClass::get_method(class, "Initialize", 0)?; - - let _ = MonoMethod::invoke(method, None, None)?; - - - Ok(()) -} \ No newline at end of file diff --git a/Bootstrap/src/melonenv/args.rs b/Bootstrap/src/melonenv/args.rs new file mode 100644 index 000000000..d55d11846 --- /dev/null +++ b/Bootstrap/src/melonenv/args.rs @@ -0,0 +1,22 @@ +use clap::Parser; +use lazy_static::lazy_static; + +use crate::{internal_failure}; + +#[derive(Debug, Parser)] +#[command(author, version, about, long_about = None)] +pub struct Cli { + #[arg(short, long = "melonloader.debug", default_value = "false")] + pub debug: bool, + + #[arg(short, long = "melonloader.basedir")] + pub base_dir: Option, +} + +lazy_static! { + pub static ref ARGS: Cli = { + Cli::parse_optimistic().unwrap_or_else(|e| { + internal_failure!("Failed to parse command line arguments: {}", e.to_string()); + }) + }; +} diff --git a/Bootstrap/src/melonenv/macros.rs b/Bootstrap/src/melonenv/macros.rs new file mode 100644 index 000000000..d8c87ab07 --- /dev/null +++ b/Bootstrap/src/melonenv/macros.rs @@ -0,0 +1,6 @@ +#[macro_export] +macro_rules! debug_enabled { + () => { + $crate::melonenv::args::ARGS.debug + }; +} diff --git a/Bootstrap/src/melonenv/mod.rs b/Bootstrap/src/melonenv/mod.rs new file mode 100644 index 000000000..94ffc337b --- /dev/null +++ b/Bootstrap/src/melonenv/mod.rs @@ -0,0 +1,3 @@ +pub mod args; +pub mod macros; +pub mod paths; diff --git a/Bootstrap/src/melonenv/paths.rs b/Bootstrap/src/melonenv/paths.rs new file mode 100644 index 000000000..107f288d5 --- /dev/null +++ b/Bootstrap/src/melonenv/paths.rs @@ -0,0 +1,69 @@ +use std::path::PathBuf; + +use lazy_static::lazy_static; +use unity_rs::runtime::RuntimeType; + +use crate::{errors::DynErr, internal_failure, runtime}; + +use super::args::ARGS; + +lazy_static! { + pub static ref BASE_DIR: PathBuf = { + match ARGS.base_dir { + Some(ref path) => PathBuf::from(path.clone()), + None => std::env::current_dir().unwrap_or_else(|e| { + internal_failure!("Failed to get base directory: {}", e.to_string()); + }), + } + }; + pub static ref GAME_DIR: PathBuf = { + std::env::current_dir().unwrap_or_else(|e| { + internal_failure!("Failed to get game directory: {}", e.to_string()); + }) + }; + pub static ref MELONLOADER_FOLDER: PathBuf = BASE_DIR.join("MelonLoader"); + pub static ref DEPENDENCIES_FOLDER: PathBuf = MELONLOADER_FOLDER.join("Dependencies"); + pub static ref SUPPORT_MODULES_FOLDER: PathBuf = DEPENDENCIES_FOLDER.join("SupportModules"); + pub static ref PRELOAD_DLL: PathBuf = SUPPORT_MODULES_FOLDER.join("Preload.dll"); +} + +pub fn runtime_dir() -> Result { + let runtime = runtime!()?; + + let mut path = MELONLOADER_FOLDER.clone(); + + match runtime.get_type() { + RuntimeType::Mono(_) => path.push("net35"), + RuntimeType::Il2Cpp(_) => path.push("net6"), + } + + Ok(path) +} + +pub fn get_managed_dir() -> Result { + let file_path = std::env::current_exe()?; + + let file_name = file_path + .file_stem() + .ok_or_else(|| "Failed to get File Stem!")? + .to_str() + .ok_or_else(|| "Failed to get File Stem!")?; + + let base_folder = file_path.parent().ok_or_else(|| "Data Path not found!")?; + + let managed_path = base_folder + .join(format!("{}_Data", file_name)) + .join("Managed"); + + match managed_path.exists() { + true => Ok(managed_path), + false => { + let managed_path = base_folder.join("MelonLoader").join("Managed"); + + match managed_path.exists() { + true => Ok(managed_path), + false => Err("Failed to find the managed directory!")?, + } + } + } +} diff --git a/Bootstrap/src/utils/console.rs b/Bootstrap/src/utils/console.rs deleted file mode 100755 index a102596d0..000000000 --- a/Bootstrap/src/utils/console.rs +++ /dev/null @@ -1,137 +0,0 @@ -//! Windows console stuff - -#![cfg(windows)] - -use std::{error, ffi::{c_char, CStr}}; - -use libc::{freopen, c_void}; -use thiserror::Error; -use winapi::{um::{consoleapi::{AllocConsole, SetConsoleCtrlHandler, GetConsoleMode, SetConsoleMode}, wincon::{GetConsoleWindow, CTRL_C_EVENT, CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, CTRL_SHUTDOWN_EVENT, SetConsoleTitleA, ENABLE_LINE_INPUT, ENABLE_PROCESSED_INPUT, ENABLE_VIRTUAL_TERMINAL_PROCESSING, ENABLE_EXTENDED_FLAGS, ENABLE_MOUSE_INPUT, ENABLE_WINDOW_INPUT, ENABLE_INSERT_MODE}, winuser::{SetForegroundWindow, ShowWindow}, processenv::{GetStdHandle, SetStdHandle}, winbase::{STD_OUTPUT_HANDLE, STD_ERROR_HANDLE}}, shared::{minwindef::{self, DWORD}, windef::HWND}}; - -use crate::{managers::core, debug}; - -use super::{debug}; - -/// console errors -#[derive(Debug, Error)] -pub enum ConsoleError { - /// failed to allocate console - #[error("Failed to allocate console!")] - FailedToAllocateConsole, - - /// failed to get console window - #[error("Failed to get console window!")] - FailedToGetConsoleWindow, -} - -static mut OUTPUT_HANDLE: *mut c_void = std::ptr::null_mut(); -static mut WINDOW: HWND = std::ptr::null_mut(); - -/// Initializes the console -pub fn init() -> Result<(), Box> { - unsafe { - if AllocConsole() != 1 { - return Err(Box::new(ConsoleError::FailedToAllocateConsole)); - } - - WINDOW = GetConsoleWindow(); - if WINDOW.is_null() { - return Err(Box::new(ConsoleError::FailedToGetConsoleWindow)); - } - - let _ = SetConsoleCtrlHandler(Some(event_handler), minwindef::TRUE); - - set_default_title(); - - let _ = SetForegroundWindow(WINDOW); - - let win_stdin = win_str(b"CONIN$\0"); - let win_stdout = win_str(b"CONOUT$\0"); - let read_mode = win_str(b"r\0"); - let write_mode = win_str(b"w\0"); - - let _ = freopen(win_stdin, read_mode, libc_stdhandle::stdin()); - let _ = freopen(win_stdout, write_mode, libc_stdhandle::stdout()); - let _ = freopen(win_stdout, write_mode, libc_stdhandle::stderr()); - - //let input_handle = GetStdHandle(STD_INPUT_HANDLE); - OUTPUT_HANDLE = GetStdHandle(STD_OUTPUT_HANDLE); - set_handles(); - - let mut mode: DWORD = 0; - let _ = GetConsoleMode(OUTPUT_HANDLE, &mut mode); - - mode |= ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT; - - if SetConsoleMode(OUTPUT_HANDLE, mode) != 1 { - mode &= !(ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT); - } else { - mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; - - if SetConsoleMode(OUTPUT_HANDLE, mode) != 1 { - mode &= !ENABLE_VIRTUAL_TERMINAL_PROCESSING; - } - } - - mode |= ENABLE_EXTENDED_FLAGS; - mode &= !(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT | ENABLE_INSERT_MODE); - - let _ = SetConsoleMode(OUTPUT_HANDLE, mode); - } - - Ok(()) -} - -/// Sets the console handles -pub fn set_handles() { - unsafe { - let _ = SetStdHandle(STD_OUTPUT_HANDLE, OUTPUT_HANDLE); - let _ = SetStdHandle(STD_ERROR_HANDLE, OUTPUT_HANDLE); - } -} - -/// nulls handles -pub fn null_handles() { - unsafe { - let _ = SetStdHandle(STD_OUTPUT_HANDLE, std::ptr::null_mut()); - let _ = SetStdHandle(STD_ERROR_HANDLE, std::ptr::null_mut()); - } -} - -extern "system" fn event_handler(evt: DWORD) -> minwindef::BOOL { - match evt { - CTRL_C_EVENT | CTRL_CLOSE_EVENT | CTRL_LOGOFF_EVENT | CTRL_SHUTDOWN_EVENT => { - close(); - std::process::exit(0); - } - _ => minwindef::FALSE - } -} - -fn close() { - unsafe { - let _ = ShowWindow(WINDOW, 0); - let _ = debug!("goodbye!"); - } -} - -fn set_default_title() { - let mut version_str = core::version().to_string(); - if debug::enabled() { - version_str = format!("[D] {}", version_str); - } - - set_title(&version_str); -} - -fn set_title(title: &str) { - let c_title = std::ffi::CString::new(title).unwrap(); - - unsafe { - let _ = SetConsoleTitleA(c_title.as_ptr()); - } -} - -pub fn win_str(bytes: &[u8]) -> *const c_char { - unsafe { CStr::from_bytes_with_nul_unchecked(bytes).as_ptr() } -} \ No newline at end of file diff --git a/Bootstrap/src/utils/debug.rs b/Bootstrap/src/utils/debug.rs deleted file mode 100755 index 9f7a2bc00..000000000 --- a/Bootstrap/src/utils/debug.rs +++ /dev/null @@ -1,14 +0,0 @@ -//! module for debugging - -use std::env; - -/// returns a bool, indicating whether or not we're running in debug mode -pub fn enabled() -> bool { - for arg in env::args() { - if arg == "--melonloader.debug" { - return true; - } - } - - false -} \ No newline at end of file diff --git a/Bootstrap/src/utils/detours.rs b/Bootstrap/src/utils/detours.rs deleted file mode 100644 index 47c69c105..000000000 --- a/Bootstrap/src/utils/detours.rs +++ /dev/null @@ -1,52 +0,0 @@ -//! dynamic dobby wrapper - -use std::{ - error, mem::transmute -}; - -use thiserror::Error; - -/// dobby errors -#[derive(Debug, Error)] -pub enum DobbyError { - /// failed to load dobby - #[error("Failed to load Dobby!")] - FailedToLoadDobby, - - /// failed to get dobby path - #[error("Failed to get Dobby path!")] - FailedToGetDobbyPath, - - /// failed to get dobby name - #[error("Failed to get Function!")] - FailedToGetFunction, - - /// failed to hook function - #[error("Failed to hook Function!")] - FailedToHookFunction, - - /// failed to unhook function - #[error("Failed to unhook Function!")] - FailedToUnhookFunction, -} - -/// hook a function pointer -pub fn hook(target: usize, replacement: usize) -> Result<&'static (), Box> { - use dobby_rs::{Address}; - - unsafe { - let res = dobby_rs::hook(target as Address, replacement as Address)?; - Ok(transmute(res)) - } -} - -/// hook a function pointer -pub fn unhook(target: usize) -> Result<(), Box> { - use dobby_rs::Address; - - unsafe { - dobby_rs::unhook(target as Address)?; - } - - Ok(()) -} diff --git a/Bootstrap/src/utils/files.rs b/Bootstrap/src/utils/files.rs deleted file mode 100755 index 98ef19382..000000000 --- a/Bootstrap/src/utils/files.rs +++ /dev/null @@ -1,110 +0,0 @@ -//! Contains various utilities for files - -use std::{error::Error, path::PathBuf, env}; - -use unity_rs::runtime::{Runtime, UnityRuntime}; - -/// evaluates wether or not a path contains non-ASCII characters. -pub fn check_ascii(path: &PathBuf) -> Result<(), Box> { - let base_string = path.to_str().ok_or("Failed to turn PathBuf into a string!")?; - - if base_string.is_ascii() { - Ok(()) - } else { - Err("The base directory path contains non-ASCII characters!")? - } -} - -/// joins a path with a file name, and appends the platform specific extension. -/// -/// # Arguments -/// -/// * `path` - The path to join with the file name. -/// * `file_name` - The file name to join with the path. -/// -/// # Example -/// -/// ``` -/// use std::path::PathBuf; -/// -/// use crate::utils::files::join_path; -/// -/// let path = PathBuf::from("C:\\Users\\User\\Desktop"); -/// let file_name = "test"; -/// -/// let joined_path = join_path(&path, file_name); -/// -/// #[cfg(target_os = "windows")] -/// assert_eq!(joined_path, PathBuf::from("C:\\Users\\User\\Desktop\\test.dll")); -/// #[cfg(target_os = "linux")] -/// assert_eq!(joined_path, PathBuf::from("C:\\Users\\User\\Desktop\\test.so")); -/// #[cfg(target_os = "macos")] -/// assert_eq!(joined_path, PathBuf::from("C:\\Users\\User\\Desktop\\test.dylib")); -#[macro_export] -macro_rules! join_dll_path { - //take two arguments, a PathBuf and a file name - ($path:expr, $file:expr) => { - //join the path and the file name - $path.join($file) - //add the correct extension - .with_extension(std::env::consts::DLL_EXTENSION) - }; -} - -/// gets the game's base directory. -pub fn base_dir() -> Result> { - let app_path = env::current_exe()?; - - let base_path = app_path.parent().ok_or("Failed to get the base path!")?; - - match base_path.exists() { - true => Ok(base_path.to_path_buf()), - false => Err("The base path does not exist!")?, - } -} - -pub fn managed_dir() -> Result> { - let file_path = std::env::current_exe()?; - - let file_name = file_path.file_stem() - .ok_or_else(|| "Data Path not found!")? - .to_str() - .ok_or_else(|| "Data Path not found!")?; - - let base_folder = file_path.parent() - .ok_or_else(|| "Data Path not found!")?; - - let managed_path = base_folder.join(format!("{}_Data", file_name)).join("Managed"); - - match managed_path.exists() { - true => Ok(managed_path), - false => { - let managed_path = base_folder.join("MelonLoader").join("Managed"); - - match managed_path.exists() { - true => Ok(managed_path), - false => Err("Failed to find the managed directory!")?, - } - } - } -} - -pub fn melonloader_dir() -> Result> { - let melonloader_path = base_dir()?.join("MelonLoader"); - - match melonloader_path.exists() { - true => Ok(melonloader_path), - false => Err("MelonLoader Folder does not exist!")?, - } -} - -pub fn runtime_dir() -> Result> { - let runtime_dir = melonloader_dir()?; - - let runtime = Runtime::new()?; - - match runtime.runtime { - UnityRuntime::MonoRuntime(_) => Ok(runtime_dir.join("net35")), - UnityRuntime::Il2Cpp(_) => Ok(runtime_dir.join("net6")), - } -} \ No newline at end of file diff --git a/Bootstrap/src/utils/log.rs b/Bootstrap/src/utils/log.rs deleted file mode 100755 index be950c60f..000000000 --- a/Bootstrap/src/utils/log.rs +++ /dev/null @@ -1,313 +0,0 @@ -//! Handles logging across the entire package - -use std::{ - io::Write, - path::PathBuf, error, -}; - -use colored::{Color, Colorize}; -use thiserror::Error; - -use super::{debug, files}; - -/// all possible errors that can occur when working with the logger -/// -/// # Errors -/// ``` -/// LogError::FailedToDeleteOldLog // Failed to delete the old log file -/// LogError::FailedToWriteToLog //Failed to write to the log file -/// LogError::FailedToGetBasePath //Failed to get the base path -/// ``` -#[derive(Error, Debug)] -pub enum LogError { - /// the log file could not be deleted - #[error("Failed to delete the old log file")] - FailedToDeleteOldLog, - - /// the log file could not be written to - #[error("Failed to write to log file")] - FailedToWriteToLog, - - /// the base path could not be fetched - #[error("Failed to fetch base path")] - FailedToGetBasePath, -} - -/// gets the path to the log file -fn log_path() -> Result> { - let base_path = files::base_dir()?; - let log_path = base_path.join("MelonLoader/Latest-Bootstrap.log"); - - Ok(log_path) -} - -/// Initializes MelonLogger, which takes care of both logging to console & file -pub fn init() -> Result<(), Box> { - let log_path = log_path().map_err(|_| LogError::FailedToGetBasePath)?; - - if log_path.exists() { - std::fs::remove_file(&log_path).map_err(|_| LogError::FailedToDeleteOldLog)?; - } - - Ok(()) -} - -/// the different log levels -/// -/// # Levels -/// ``` -/// LogLevel::Info -/// LogLevel::Warning -/// LogLevel::Error -/// LogLevel::Debug -#[derive(Debug)] -pub enum LogLevel { - /// Informational, always printed to console - Info, - /// Warning, always printed to console - Warning, - /// Error, always printed to console - Error, - /// Debug, only printed to console if --melonloader.debug is passed as a launch option - Debug, -} - -static RED: Color = Color::TrueColor { - r: (255), - g: (0), - b: (0), -}; -static GREEN: Color = Color::TrueColor { - r: (0), - g: (255), - b: (0), -}; -static BLUE: Color = Color::TrueColor { - r: (64), - g: (64), - b: (255), -}; - -fn write(msg: &str) -> Result<(), Box> { - let log_path = log_path().map_err(|_| LogError::FailedToGetBasePath)?; - let mut file = std::fs::OpenOptions::new() - .write(true) - .append(true) - .create(true) - .open(&log_path) - .map_err(|_| LogError::FailedToWriteToLog)?; - - let message = format!("{}\r\n", msg); - - file.write_all(message.as_bytes()) - .map_err(|_| LogError::FailedToWriteToLog)?; - - Ok(()) -} - -/// logs to console and file, should not be used, use the log! macro instead -pub fn log_console_file(level: LogLevel, message: &str) -> Result<(), Box> { - match level { - LogLevel::Info => { - let console_string = format!( - "{}{}{} {}", - "[".bright_black(), - timestamp().color(GREEN), - "]".bright_black(), - message - ); - - let file_string = format!( - "[{}] {}", - timestamp(), - message - ); - - println!("{}", console_string); - write(&file_string).map_err(|_| LogError::FailedToWriteToLog)?; - } - LogLevel::Warning => { - //same as log, but all colors are yellow - let console_string = format!( - "[{}] [WARNING] {}", - timestamp(), - message - ); - - let file_string = format!( - "[{}] [WARNING] {}", - timestamp(), - message - ); - - println!("{}", console_string.yellow()); - - write(&file_string).map_err(|_| LogError::FailedToWriteToLog)?; - } - LogLevel::Error => { - //same as log, but all colors are red - - let log_string = format!( - "[{}] [ERROR] {}", - timestamp(), - message - ); - - println!("{}", log_string.color(RED)); - write(&log_string).map_err(|_| LogError::FailedToWriteToLog)?; - } - LogLevel::Debug => { - if !debug::enabled() { - return Ok(()); - } - - let console_string = format!( - "{}{}{} {}{}{} {}", - "[".bright_black(), - timestamp().color(GREEN), - "]".bright_black(), - "[".bright_black(), - "DEBUG".color(BLUE), - "]".bright_black(), - message - ); - - let file_string = format!( - "[{}] [DEBUG] {}", - timestamp(), - message - ); - - println!("{}", console_string); - write(&file_string).map_err(|_| LogError::FailedToWriteToLog)?; - } - } - - Ok(()) -} - -/// Fetches the current time, and formats it. -/// -/// returns a String, formatted as 15:23:24:123 -fn timestamp() -> String { - let now = chrono::Local::now(); - let time = now.time(); - - time.format("%H:%M:%S.%3f").to_string() -} - -/// Logs a message to the console and log file -/// -/// # Example -/// ``` -/// log!("Hello World!")?; -/// ``` -/// log! returns a Result<(), Box>, so please handle this. -#[macro_export] -macro_rules! log { - //case 1: empty - () => { - $crate::utils::log::log_console_file($crate::utils::log::LogLevel::Info, "") - }; - - //case 2: single argument - ($msg:expr) => { - $crate::utils::log::log_console_file($crate::utils::log::LogLevel::Info, $msg) - }; - - //case 3: multiple arguments - ($($arg:tt)*) => {{ - let msg = &format_args!($($arg)*).to_string(); - $crate::utils::log::log_console_file($crate::utils::log::LogLevel::Info, msg) - }}; -} - -/// Logs a warning to the console and log file -/// -/// # Example -/// ``` -/// warn!("Hello World!")?; -/// ``` -/// warn! returns a Result<(), Box>, so please handle this. -#[macro_export] -macro_rules! warn { - //case 1: empty - () => { - $crate::utils::log::log_console_file($crate::utils::log::LogLevel::Warning, "") - }; - - //case 2: single argument - ($msg:expr) => { - $crate::utils::log::log_console_file($crate::utils::log::LogLevel::Warning, $msg) - }; - - //case 3: multiple arguments - ($($arg:tt)*) => {{ - let msg = &format_args!($($arg)*).to_string(); - $crate::utils::log::log_console_file($crate::utils::log::LogLevel::Warning, msg) - }}; -} - -/// Logs an error to the console and log file -/// -/// # Example -/// ``` -/// error!("Hello World!")?; -/// ``` -/// error! returns a Result<(), Box>, so please handle this. -#[macro_export] -macro_rules! err { - //case 1: empty - () => { - $crate::utils::log::log_console_file($crate::utils::log::LogLevel::Error, "") - }; - - //case 2: single argument - ($msg:expr) => { - $crate::utils::log::log_console_file($crate::utils::log::LogLevel::Error, $msg) - }; - - //case 3: multiple arguments - ($($arg:tt)*) => {{ - let msg = &format_args!($($arg)*).to_string(); - $crate::utils::log::log_console_file($crate::utils::log::LogLevel::Error, msg) - }}; -} - -/// Logs a debug message to the console and log file -/// -/// # Example -/// ``` -/// debug!("Hello World!")?; -/// ``` -/// debug! returns a Result<(), Box>, so please handle this. -#[macro_export] -macro_rules! debug { - //case 1: empty - () => { - $crate::utils::log::log_console_file($crate::utils::log::LogLevel::Debug, "") - }; - - //case 2: single argument - ($msg:expr) => { - $crate::utils::log::log_console_file($crate::utils::log::LogLevel::Debug, $msg) - }; - - //case 3: multiple arguments - ($($arg:tt)*) => {{ - let msg = &format_args!($($arg)*).to_string(); - $crate::utils::log::log_console_file($crate::utils::log::LogLevel::Debug, msg) - }}; -} - -#[macro_export] -macro_rules! cstr { - ($s:expr) => { - std::ffi::CString::new($s)?.as_ptr() - }; - - //format str - ($($arg:tt)*) => { - std::ffi::CString::new(format!($($arg)*))?.as_ptr() - }; -} diff --git a/Bootstrap/src/utils/mod.rs b/Bootstrap/src/utils/mod.rs index 55197c1a9..7bb78192a 100755 --- a/Bootstrap/src/utils/mod.rs +++ b/Bootstrap/src/utils/mod.rs @@ -1,6 +1,2 @@ -pub mod assert; -pub mod log; -pub mod debug; -pub mod files; -pub mod console; -pub mod detours; \ No newline at end of file +pub mod runtime; +pub mod strings; diff --git a/Bootstrap/src/utils/runtime.rs b/Bootstrap/src/utils/runtime.rs new file mode 100644 index 000000000..dea4933a2 --- /dev/null +++ b/Bootstrap/src/utils/runtime.rs @@ -0,0 +1,23 @@ +use unity_rs::runtime::FerrexRuntime; + +use crate::errors::DynErr; + +#[allow(dead_code)] +pub static mut RUNTIME: Option = None; + +#[macro_export] +macro_rules! runtime { + () => { + $crate::utils::runtime::get_runtime() + }; +} + +pub fn get_runtime() -> Result<&'static FerrexRuntime, DynErr> { + unsafe { + if RUNTIME.is_none() { + RUNTIME = Some(unity_rs::runtime::get_runtime()?) + } + + Ok(RUNTIME.as_ref().ok_or("Failed to get runtime")?) + } +} diff --git a/Bootstrap/src/utils/strings.rs b/Bootstrap/src/utils/strings.rs new file mode 100644 index 000000000..c199c35ec --- /dev/null +++ b/Bootstrap/src/utils/strings.rs @@ -0,0 +1,36 @@ +use std::{path::Path, str::FromStr}; + +use netcorehost::pdcstring::PdCString; + +use crate::errors::DynErr; + +#[macro_export] +macro_rules! win_str { + ($s:expr) => { + std::ffi::CStr::from_bytes_with_nul_unchecked($s).as_ptr() + }; +} + +#[macro_export] +macro_rules! rust_str { + ($s:expr) => { + unsafe { std::ffi::CStr::from_ptr($s) }.to_str() + }; +} + +pub fn pdcstr>(path: P) -> Result { + Ok(PdCString::from_str( + path.as_ref() + .to_str() + .ok_or("Failed to convert path to string!")?, + )?) +} + +pub fn wide_str>(path: P) -> Result, DynErr> { + let s = path + .as_ref() + .to_str() + .ok_or("Failed to convert path to string!")?; + + Ok(s.encode_utf16().chain(Some(0)).collect()) +} diff --git a/Cargo.lock b/Cargo.lock index 8e6f0a96a..f37f212af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,6 +7,7 @@ name = "Bootstrap" version = "0.1.0" dependencies = [ "chrono", + "clap 4.1.8 (git+https://github.com/RinLovesYou/clap)", "colored", "ctor", "dobby-rs", @@ -17,7 +18,7 @@ dependencies = [ "netcorehost", "thiserror", "unity-rs", - "winapi", + "windows", ] [[package]] @@ -96,15 +97,15 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "base64" -version = "0.13.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" [[package]] name = "base64ct" -version = "1.5.3" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bitflags" @@ -120,9 +121,9 @@ checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" [[package]] name = "block-buffer" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] @@ -135,9 +136,9 @@ checksum = "832133bbabbbaa9fbdba793456a2827627a7d2b8fb96032fa1e7666d7895832b" [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "byteorder" @@ -147,15 +148,15 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "bzip2" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6afcd980b5f3a45017c57e57a2fcccbb351cc43a356ce117ef760ef8052b89b0" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" dependencies = [ "bzip2-sys", "libc", @@ -253,12 +254,24 @@ dependencies = [ [[package]] name = "clap" version = "4.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5" dependencies = [ "bitflags", - "clap_derive", - "clap_lex", + "clap_derive 4.1.8", + "clap_lex 0.3.2", + "is-terminal", + "once_cell", + "strsim", + "termcolor", +] + +[[package]] +name = "clap" +version = "4.1.8" +source = "git+https://github.com/RinLovesYou/clap#f959816de9a07eaf9867f0419a1484353e0629f6" +dependencies = [ + "bitflags", + "clap_derive 4.1.8 (git+https://github.com/RinLovesYou/clap)", + "clap_lex 0.3.2 (git+https://github.com/RinLovesYou/clap)", "is-terminal", "once_cell", "strsim", @@ -268,8 +281,18 @@ dependencies = [ [[package]] name = "clap_derive" version = "4.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_derive" +version = "4.1.8" +source = "git+https://github.com/RinLovesYou/clap#f959816de9a07eaf9867f0419a1484353e0629f6" dependencies = [ "heck", "proc-macro-error", @@ -281,8 +304,14 @@ dependencies = [ [[package]] name = "clap_lex" version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "clap_lex" +version = "0.3.2" +source = "git+https://github.com/RinLovesYou/clap#f959816de9a07eaf9867f0419a1484353e0629f6" dependencies = [ "os_str_bytes", ] @@ -415,9 +444,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.14" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" dependencies = [ "cfg-if", ] @@ -460,9 +489,9 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" [[package]] name = "cxx" -version = "1.0.85" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5add3fc1717409d029b20c5b6903fc0c0b02fa6741d820054f4a2efa5e5816fd" +checksum = "9a140f260e6f3f79013b8bfc65e7ce630c9ab4388c6a89c71e07226f49487b72" dependencies = [ "cc", "cxxbridge-flags", @@ -472,9 +501,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.85" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c87959ba14bc6fbc61df77c3fcfe180fc32b93538c4f1031dd802ccb5f2ff0" +checksum = "da6383f459341ea689374bf0a42979739dc421874f112ff26f829b8040b8e613" dependencies = [ "cc", "codespan-reporting", @@ -487,15 +516,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.85" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69a3e162fde4e594ed2b07d0f83c6c67b745e7f28ce58c6df5e6b6bef99dfb59" +checksum = "90201c1a650e95ccff1c8c0bb5a343213bdd317c6e600a93075bca2eff54ec97" [[package]] name = "cxxbridge-macro" -version = "1.0.85" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e7e2adeb6a0d4a282e581096b06e1791532b7d576dcde5ccd9382acf55db8e6" +checksum = "0b75aed41bb2e6367cae39e6326ef817a851db13c13e4f3263714ca3cfb8de56" dependencies = [ "proc-macro2", "quote", @@ -583,9 +612,9 @@ source = "git+https://github.com/RinLovesYou/dobby-sys.git#8ea9f0800de90b0707356 [[package]] name = "encoding_rs" -version = "0.8.31" +version = "0.8.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" dependencies = [ "cfg-if", ] @@ -701,9 +730,9 @@ checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" [[package]] name = "futures-sink" -version = "0.3.25" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" +checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" [[package]] name = "futures-task" @@ -938,9 +967,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" dependencies = [ "bytes", "fnv", @@ -1006,9 +1035,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -1040,9 +1069,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.23" +version = "0.14.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" dependencies = [ "bytes", "futures-channel", @@ -1126,14 +1155,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] name = "ipnet" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11b0d96e660696543b251e58030cf9787df56da39dab19ad60eae7353040917e" +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" [[package]] name = "is-terminal" @@ -1144,29 +1173,29 @@ dependencies = [ "hermit-abi 0.2.6", "io-lifetimes", "rustix", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] name = "itoa" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "jobserver" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" dependencies = [ "wasm-bindgen", ] @@ -1179,9 +1208,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.139" +version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" [[package]] name = "libc-stdhandle" @@ -1268,14 +1297,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -1313,15 +1342,15 @@ dependencies = [ [[package]] name = "nethost-sys" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fafb3d1394dda30656b09f0c212f572f7a97a7674b6b6673426d0a86b76c51e" +checksum = "45882ffd272817567a1743af759c2ab840e0dde3f89ef30c4e0d17268838fe50" dependencies = [ "build-target", "cargo-emit", "coreclr-hosting-shared", "reqwest", - "semver 1.0.16", + "semver 1.0.17", "serde", "serde_json", "zip", @@ -1358,18 +1387,18 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.5.7" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.7" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" dependencies = [ "proc-macro2", "quote", @@ -1534,7 +1563,7 @@ name = "proxy" version = "0.2.0" dependencies = [ "cc", - "clap", + "clap 4.1.8", "ctor", "lazy_static", "libc", @@ -1542,13 +1571,11 @@ dependencies = [ "msgbox", "proxy-dll", "thiserror", - "winapi", ] [[package]] name = "proxy-dll" version = "0.2.5" -source = "git+https://github.com/RinLovesYou/dll-proxy-rs.git#160fd47d5d5e595ff694a1d004de7d8e562aba59" dependencies = [ "ctor", "proxy-sys", @@ -1581,9 +1608,9 @@ checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" [[package]] name = "reqwest" -version = "0.11.13" +version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c" +checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" dependencies = [ "base64", "bytes", @@ -1653,14 +1680,14 @@ dependencies = [ "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] name = "rustls" -version = "0.20.7" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" dependencies = [ "log", "ring", @@ -1670,24 +1697,24 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" dependencies = [ "base64", ] [[package]] name = "ryu" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "scratch" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" +checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" [[package]] name = "sct" @@ -1710,9 +1737,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "semver-parser" @@ -1745,9 +1772,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.91" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" dependencies = [ "itoa", "ryu", @@ -1805,9 +1832,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -1866,18 +1893,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" dependencies = [ "proc-macro2", "quote", @@ -1897,14 +1924,12 @@ dependencies = [ [[package]] name = "time" -version = "0.3.17" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" +checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" dependencies = [ - "itoa", "serde", "time-core", - "time-macros", ] [[package]] @@ -1913,15 +1938,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" -[[package]] -name = "time-macros" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" -dependencies = [ - "time-core", -] - [[package]] name = "tinyvec" version = "1.6.0" @@ -1933,15 +1949,15 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.23.0" +version = "1.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46" +checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" dependencies = [ "autocfg", "bytes", @@ -1951,7 +1967,7 @@ dependencies = [ "num_cpus", "pin-project-lite", "socket2", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -1967,9 +1983,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" dependencies = [ "bytes", "futures-core", @@ -2016,9 +2032,9 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "typenum" @@ -2044,9 +2060,9 @@ checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "7d502c968c6a838ead8e69b2ee18ec708802f99db92a0d156705ec9ef801993b" [[package]] name = "unicode-ident" @@ -2072,9 +2088,10 @@ checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "unity-rs" version = "0.1.0" -source = "git+https://github.com/RinLovesYou/unity-rs.git?rev=8ae8053#8ae8053a7d66beee563ff15ca4d337a3afc8de53" +source = "git+https://github.com/RinLovesYou/Ferrex/#6af81ebb5d2354941a2f01485631591ed5ebedba" dependencies = [ "libc", + "libloading", "thiserror", "winapi", ] @@ -2132,9 +2149,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2142,9 +2159,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ "bumpalo", "log", @@ -2157,9 +2174,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" dependencies = [ "cfg-if", "js-sys", @@ -2169,9 +2186,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2179,9 +2196,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", @@ -2192,9 +2209,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "wchar" @@ -2218,9 +2235,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" dependencies = [ "js-sys", "wasm-bindgen", @@ -2282,6 +2299,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.42.0" @@ -2297,47 +2323,71 @@ dependencies = [ "windows_x86_64_msvc", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_i686_gnu" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "winreg" @@ -2350,9 +2400,9 @@ dependencies = [ [[package]] name = "zip" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "537ce7411d25e54e8ae21a7ce0b15840e7bfcff15b51d697ec3266cc76bdf080" +checksum = "0445d0fbc924bb93539b4316c11afb121ea39296f99a3c4c9edad09e3658cdef" dependencies = [ "aes", "byteorder", @@ -2364,7 +2414,7 @@ dependencies = [ "hmac", "pbkdf2", "sha1", - "time 0.3.17", + "time 0.3.20", "zstd", ] @@ -2389,10 +2439,11 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.4+zstd.1.5.2" +version = "2.0.7+zstd.1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fa202f2ef00074143e219d15b62ffc317d17cc33909feac471c044087cad7b0" +checksum = "94509c3ba2fe55294d752b79842c530ccfab760192521df74a081a78d2b3c7f5" dependencies = [ "cc", "libc", + "pkg-config", ] diff --git a/MelonLoader/Properties/BuildInfo.cs b/MelonLoader/Properties/BuildInfo.cs index 89e646205..34e726168 100644 --- a/MelonLoader/Properties/BuildInfo.cs +++ b/MelonLoader/Properties/BuildInfo.cs @@ -6,6 +6,6 @@ public static class BuildInfo public const string Description = "MelonLoader"; public const string Author = "Lava Gang"; public const string Company = "discord.gg/2Wn3N2P"; - public const string Version = "0.6.0"; + public const string Version = "0.6.1"; } } \ No newline at end of file diff --git a/Proxy/Cargo.toml b/Proxy/Cargo.toml index 0093518ee..1cf045518 100644 --- a/Proxy/Cargo.toml +++ b/Proxy/Cargo.toml @@ -11,11 +11,11 @@ msgbox = "0.7.0" lazy_static = "1.4.0" thiserror = "*" libloading = "*" -clap = { version = "*", features = ["derive"] } +clap = { path = "C:\\Users\\sarah\\Documents\\rust\\clap", features = ["derive"] } [target.'cfg(windows)'.dependencies] -winapi = { version = "0.3.9", features = ["winuser", "minwindef", "std", "libloaderapi"] } -proxy-dll = { git = "https://github.com/RinLovesYou/dll-proxy-rs.git" } +#proxy-dll = { git = "https://github.com/RinLovesYou/dll-proxy-rs.git" } +proxy-dll = { path = "C:\\Users\\sarah\\Documents\\rust\\dll-proxy-rs\\proxy" } [target.'cfg(unix)'.dependencies] libc = "0.2.137" diff --git a/Proxy/src/core.rs b/Proxy/src/core.rs index fbb9ea4d2..121e2c4c7 100644 --- a/Proxy/src/core.rs +++ b/Proxy/src/core.rs @@ -1,29 +1,36 @@ //! the core logic of the proxy use crate::utils::files; -use clap::{Parser}; +use clap::Parser; +use lazy_static::lazy_static; use libloading::Library; -use std::{error, path::PathBuf}; - +use std::{error, path::PathBuf, sync::Mutex}; #[derive(Parser)] -#[command(author, version, about, long_about = None)] -struct Cli { - #[arg(long)] +struct Arguments { + #[arg(long, short, default_value = "false")] no_mods: bool, - #[arg(long="melonloader.basedir")] + + #[arg(long = "melonloader.basedir")] base_dir: Option, } -static mut BOOTSTRAP: Option = None; +lazy_static!( + static ref BOOTSTRAP: Mutex> = Mutex::new(None); +); -pub unsafe fn init() -> Result<(), Box> { - let args = Cli::parse(); +pub fn init() -> Result<(), Box> { let file_path = std::env::current_exe()?; + if !files::is_unity(&file_path)? { + return Ok(()); + } + + let args = Arguments::parse_optimistic()?; + //return Ok, and silently stop loading MelonLoader, if the user has specified to not load mods, //or if the game is not a Unity game - if args.no_mods || !files::is_unity(&file_path)? { + if args.no_mods { return Ok(()); } @@ -34,7 +41,9 @@ pub unsafe fn init() -> Result<(), Box> { let bootstrap_path = files::get_bootstrap_path(&base_path)?; - BOOTSTRAP = Some(Library::new(bootstrap_path)?); //needs to be stored, so it won't get unloaded when this function returns. + unsafe { + *BOOTSTRAP.lock()? = Some(Library::new(&bootstrap_path)?); + } Ok(()) } diff --git a/Proxy/src/entry.rs b/Proxy/src/entry.rs index 87923142f..8b6dbf4df 100644 --- a/Proxy/src/entry.rs +++ b/Proxy/src/entry.rs @@ -20,20 +20,16 @@ use crate::{core, internal_failure}; #[no_mangle] #[ctor] fn main() { - unsafe { - core::init().unwrap_or_else(|e| { - internal_failure!("Failed to initialize MelonLoader: {}", e); - }); - } + core::init().unwrap_or_else(|e| { + internal_failure!("Failed to initialize MelonLoader: {}", e); + }); } #[cfg(target_os = "windows")] #[no_mangle] #[proxy] fn main() { - unsafe { - core::init().unwrap_or_else(|e| { - internal_failure!("Failed to initialize MelonLoader: {}", e); - }); - } + core::init().unwrap_or_else(|e| { + internal_failure!("Failed to initialize MelonLoader: {}", e); + }); } diff --git a/Proxy/src/lib.rs b/Proxy/src/lib.rs index a1fcba04d..c373203ec 100644 --- a/Proxy/src/lib.rs +++ b/Proxy/src/lib.rs @@ -1,5 +1,7 @@ //! Cross platform reimplementation of MelonLoader's Proxy in rust +#![feature(is_some_and)] + #![deny( missing_debug_implementations, unused_results, From a29ae6225c940b62c3fb7e4f82566b36f0a4f81b Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Mon, 20 Mar 2023 16:52:10 +0100 Subject: [PATCH 07/79] fix local dependency paths being left in --- Cargo.lock | 40 +++++----------------------------------- Proxy/Cargo.toml | 6 +++--- 2 files changed, 8 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f37f212af..e67969caa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,7 +7,7 @@ name = "Bootstrap" version = "0.1.0" dependencies = [ "chrono", - "clap 4.1.8 (git+https://github.com/RinLovesYou/clap)", + "clap", "colored", "ctor", "dobby-rs", @@ -251,44 +251,20 @@ dependencies = [ "generic-array", ] -[[package]] -name = "clap" -version = "4.1.8" -dependencies = [ - "bitflags", - "clap_derive 4.1.8", - "clap_lex 0.3.2", - "is-terminal", - "once_cell", - "strsim", - "termcolor", -] - [[package]] name = "clap" version = "4.1.8" source = "git+https://github.com/RinLovesYou/clap#f959816de9a07eaf9867f0419a1484353e0629f6" dependencies = [ "bitflags", - "clap_derive 4.1.8 (git+https://github.com/RinLovesYou/clap)", - "clap_lex 0.3.2 (git+https://github.com/RinLovesYou/clap)", + "clap_derive", + "clap_lex", "is-terminal", "once_cell", "strsim", "termcolor", ] -[[package]] -name = "clap_derive" -version = "4.1.8" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "clap_derive" version = "4.1.8" @@ -301,13 +277,6 @@ dependencies = [ "syn", ] -[[package]] -name = "clap_lex" -version = "0.3.2" -dependencies = [ - "os_str_bytes", -] - [[package]] name = "clap_lex" version = "0.3.2" @@ -1563,7 +1532,7 @@ name = "proxy" version = "0.2.0" dependencies = [ "cc", - "clap 4.1.8", + "clap", "ctor", "lazy_static", "libc", @@ -1576,6 +1545,7 @@ dependencies = [ [[package]] name = "proxy-dll" version = "0.2.5" +source = "git+https://github.com/RinLovesYou/dll-proxy-rs.git#160fd47d5d5e595ff694a1d004de7d8e562aba59" dependencies = [ "ctor", "proxy-sys", diff --git a/Proxy/Cargo.toml b/Proxy/Cargo.toml index 1cf045518..0a937f22a 100644 --- a/Proxy/Cargo.toml +++ b/Proxy/Cargo.toml @@ -11,11 +11,11 @@ msgbox = "0.7.0" lazy_static = "1.4.0" thiserror = "*" libloading = "*" -clap = { path = "C:\\Users\\sarah\\Documents\\rust\\clap", features = ["derive"] } +clap = { git = "https://github.com/RinLovesYou/clap", features = ["derive"] } [target.'cfg(windows)'.dependencies] -#proxy-dll = { git = "https://github.com/RinLovesYou/dll-proxy-rs.git" } -proxy-dll = { path = "C:\\Users\\sarah\\Documents\\rust\\dll-proxy-rs\\proxy" } +proxy-dll = { git = "https://github.com/RinLovesYou/dll-proxy-rs.git" } +#proxy-dll = { path = "C:\\Users\\sarah\\Documents\\rust\\dll-proxy-rs\\proxy" } [target.'cfg(unix)'.dependencies] libc = "0.2.137" From 2a19c32fcb4b10ccd558b123559780d9027c4613 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Mon, 20 Mar 2023 17:14:35 +0100 Subject: [PATCH 08/79] fix missing reference in unix os stub for console --- Bootstrap/src/console/os/unix/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Bootstrap/src/console/os/unix/mod.rs b/Bootstrap/src/console/os/unix/mod.rs index d42ab4652..966c8ccb0 100644 --- a/Bootstrap/src/console/os/unix/mod.rs +++ b/Bootstrap/src/console/os/unix/mod.rs @@ -1,5 +1,7 @@ //! Console interaction like windows is not possible on Unix systems. +use crate::error::DynErr; + pub unsafe fn init() -> Result<(), DynErr> { Ok(()) } From a6d87398d3688dedd3f2283446157b49cd30cc32 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Mon, 20 Mar 2023 17:20:43 +0100 Subject: [PATCH 09/79] fix typo --- Bootstrap/src/console/os/unix/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Bootstrap/src/console/os/unix/mod.rs b/Bootstrap/src/console/os/unix/mod.rs index 966c8ccb0..0e6d9e5b9 100644 --- a/Bootstrap/src/console/os/unix/mod.rs +++ b/Bootstrap/src/console/os/unix/mod.rs @@ -1,6 +1,6 @@ //! Console interaction like windows is not possible on Unix systems. -use crate::error::DynErr; +use crate::errors::DynErr; pub unsafe fn init() -> Result<(), DynErr> { Ok(()) From 59d7d0e1254c3a5e8a52ed92679e1ceae3566817 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Tue, 21 Mar 2023 11:37:46 +0100 Subject: [PATCH 10/79] Fix Stack UB in release mode, Update build.yml for Release/Debug --- .github/workflows/build.yml | 121 ++++++++++++++++------ Bootstrap/src/base_assembly/dotnet.rs | 51 ++++++--- Bootstrap/src/console/os/unix/mod.rs | 2 +- Bootstrap/src/constants.rs | 8 +- Bootstrap/src/errors/dotneterr.rs | 10 ++ Bootstrap/src/errors/mod.rs | 1 + Bootstrap/src/hooks/functions.rs | 6 +- Bootstrap/src/hooks/init_hook/il2cpp.rs | 8 +- Bootstrap/src/hooks/init_hook/mod.rs | 18 ++-- Bootstrap/src/hooks/init_hook/mono.rs | 8 +- Bootstrap/src/hooks/invoke_hook/il2cpp.rs | 8 +- Bootstrap/src/hooks/invoke_hook/mod.rs | 16 ++- Bootstrap/src/hooks/invoke_hook/mono.rs | 8 +- Bootstrap/src/hooks/mod.rs | 58 +++++------ Bootstrap/src/icalls/bootstrap_interop.rs | 14 ++- Bootstrap/src/lib.rs | 2 + Bootstrap/src/melonenv/macros.rs | 6 +- Cargo.toml | 7 +- Proxy/Cargo.toml | 5 - 19 files changed, 225 insertions(+), 132 deletions(-) create mode 100644 Bootstrap/src/errors/dotneterr.rs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ec609948e..8260cc191 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -46,32 +46,68 @@ jobs: toolchain: nightly # Target triple to install for this toolchain targets: i686-pc-windows-msvc, x86_64-pc-windows-msvc - - name: Build Rust | Windows - x86 + # Build Rust Release + - name: Build Rust Release | Windows - x86 shell: cmd run: cargo +nightly build --target i686-pc-windows-msvc --release - - name: Build Rust | Windows - x64 + - name: Build Rust Release | Windows - x64 shell: cmd run: cargo +nightly build --target x86_64-pc-windows-msvc --release - - name: Upload Proxy | Windows x86 + # Build Rust Debug + - name: Build Rust Debug | Windows - x86 + shell: cmd + run: cargo +nightly build --target i686-pc-windows-msvc + - name: Build Rust Debug | Windows - x64 + shell: cmd + run: cargo +nightly build --target x86_64-pc-windows-msvc + # Upload Proxy Release - x86 + - name: Upload Proxy Release | Windows x86 uses: actions/upload-artifact@v3 with: - name: MLProxyX86-Windows + name: MLProxyX86-Windows-Release path: target/i686-pc-windows-msvc/release/version.dll - - name: Upload Bootstrap | Windows x86 + # Upload Bootstrap Release - x86 + - name: Upload Bootstrap Release | Windows x86 uses: actions/upload-artifact@v3 with: - name: MLBootstrapX86-Windows + name: MLBootstrapX86-Windows-Release path: target/i686-pc-windows-msvc/release/Bootstrap.dll - - name: Upload Proxy | Windows x64 + # Upload Proxy Release - x64 + - name: Upload Proxy Release | Windows x64 uses: actions/upload-artifact@v3 with: - name: MLProxyX64-Windows + name: MLProxyX64-Windows-Release path: target/x86_64-pc-windows-msvc/release/version.dll - - name: Upload Bootstrap | Windows x64 + # Upload Bootstrap Release - x64 + - name: Upload Bootstrap Release | Windows x64 uses: actions/upload-artifact@v3 with: - name: MLBootstrapX64-Windows + name: MLBootstrapX64-Windows-Release path: target/x86_64-pc-windows-msvc/release/Bootstrap.dll + # Upload Proxy Debug - x86 + - name: Upload Proxy Debug | Windows x86 + uses: actions/upload-artifact@v3 + with: + name: MLProxyX86-Windows-Debug + path: target/i686-pc-windows-msvc/debug/version.dll + # Upload Bootstrap Debug - x86 + - name: Upload Bootstrap Debug | Windows x86 + uses: actions/upload-artifact@v3 + with: + name: MLBootstrapX86-Windows-Debug + path: target/i686-pc-windows-msvc/debug/Bootstrap.dll + # Upload Proxy Debug - x64 + - name: Upload Proxy Debug | Windows x64 + uses: actions/upload-artifact@v3 + with: + name: MLProxyX64-Windows-Debug + path: target/x86_64-pc-windows-msvc/debug/version.dll + # Upload Bootstrap Debug - x64 + - name: Upload Bootstrap Debug | Windows x64 + uses: actions/upload-artifact@v3 + with: + name: MLBootstrapX64-Windows-Debug + path: target/x86_64-pc-windows-msvc/debug/Bootstrap.dll build_rust_linux: runs-on: ubuntu-latest steps: @@ -85,19 +121,32 @@ jobs: - name: install dev dependencies shell: bash run: sudo apt-get install libgtk-3-dev - - name: Build Rust | Linux - x64 + - name: Build Rust Release | Linux - x64 shell: bash run: cargo +nightly build --target x86_64-unknown-linux-gnu --release - - name: Upload Proxy | Linux x64 + - name: Build Rust Debug | Linux - x64 + shell: bash + run: cargo +nightly build --target x86_64-unknown-linux-gnu + - name: Upload Proxy Release | Linux x64 uses: actions/upload-artifact@v3 with: - name: MLProxyX64-Linux + name: MLProxyX64-Linux-Release path: target/x86_64-unknown-linux-gnu/release/libversion.so - - name: Upload Bootstrap | Linux x64 + - name: Upload Bootstrap Release | Linux x64 uses: actions/upload-artifact@v3 with: - name: MLBootstrapX64-Linux + name: MLBootstrapX64-Linux-Release path: target/x86_64-unknown-linux-gnu/release/libBootstrap.so + - name: Upload Proxy Debug | Linux x64 + uses: actions/upload-artifact@v3 + with: + name: MLProxyX64-Linux-Debug + path: target/x86_64-unknown-linux-gnu/debug/libversion.so + - name: Upload Bootstrap Debug | Linux x64 + uses: actions/upload-artifact@v3 + with: + name: MLBootstrapX64-Linux-Debug + path: target/x86_64-unknown-linux-gnu/debug/libBootstrap.so finalize_x64_debug_zip_windows: runs-on: windows-latest needs: [build_core_debug, build_rust_windows] @@ -111,12 +160,12 @@ jobs: - name: Download proxy x64 uses: actions/download-artifact@v3 with: - name: MLProxyX64-Windows + name: MLProxyX64-Windows-Debug path: Output/Debug/x64/ - name: Download bootstrap x64 uses: actions/download-artifact@v3 with: - name: MLBootstrapX64-Windows + name: MLBootstrapX64-Windows-Debug path: Output/Debug/x64/MelonLoader/Dependencies/ - name: Package x64 zip shell: cmd @@ -153,12 +202,12 @@ jobs: - name: Download proxy x86 uses: actions/download-artifact@v3 with: - name: MLProxyX86-Windows + name: MLProxyX86-Windows-Debug path: Output/Debug/x86/ - name: Download bootstrap x86 uses: actions/download-artifact@v3 with: - name: MLBootstrapX86-Windows + name: MLBootstrapX86-Windows-Debug path: Output/Debug/x86/MelonLoader/Dependencies/ - name: Package x86 zip shell: cmd @@ -195,12 +244,12 @@ jobs: - name: Download proxy x64 uses: actions/download-artifact@v3 with: - name: MLProxyX64-Windows + name: MLProxyX64-Windows-Release path: Output/Release/x64/ - name: Download bootstrap x64 uses: actions/download-artifact@v3 with: - name: MLBootstrapX64-Windows + name: MLBootstrapX64-Windows-Release path: Output/Release/x64/MelonLoader/Dependencies/ - name: Package x64 zip shell: cmd @@ -237,12 +286,12 @@ jobs: - name: Download proxy x86 uses: actions/download-artifact@v3 with: - name: MLProxyX86-Windows + name: MLProxyX86-Windows-Release path: Output/Release/x86/ - name: Download bootstrap x86 uses: actions/download-artifact@v3 with: - name: MLBootstrapX86-Windows + name: MLBootstrapX86-Windows-Release path: Output/Release/x86/MelonLoader/Dependencies/ - name: Package x86 zip shell: cmd @@ -279,12 +328,12 @@ jobs: - name: Download proxy x64 uses: actions/download-artifact@v3 with: - name: MLProxyX64-Linux + name: MLProxyX64-Linux-Debug path: Output/Debug/x64/ - name: Download bootstrap x64 uses: actions/download-artifact@v3 with: - name: MLBootstrapX64-Linux + name: MLBootstrapX64-Linux-Debug path: Output/Debug/x64/MelonLoader/Dependencies/ - name: Package x64 zip shell: cmd @@ -318,12 +367,12 @@ jobs: - name: Download proxy x64 uses: actions/download-artifact@v3 with: - name: MLProxyX64-Linux + name: MLProxyX64-Linux-Release path: Output/Release/x64/ - name: Download bootstrap x64 uses: actions/download-artifact@v3 with: - name: MLBootstrapX64-Linux + name: MLBootstrapX64-Linux-Release path: Output/Release/x64/MelonLoader/Dependencies/ - name: Package x64 zip shell: cmd @@ -353,9 +402,15 @@ jobs: name: | MLCoreDebug MLCoreRelease - MLProxyX86-Windows - MLBootstrapX86-Windows - MLProxyX64-Windows - MLBootstrapX64-Windows - MLProxyX64-Linux - MLBootstrapX64-Linux + MLProxyX86-Windows-Debug + MLBootstrapX86-Windows-Debug + MLProxyX64-Windows-Debug + MLBootstrapX64-Windows-Debug + MLProxyX64-Linux-Debug + MLBootstrapX64-Linux-Debug + MLProxyX86-Windows-Release + MLBootstrapX86-Windows-Release + MLProxyX64-Windows-Release + MLBootstrapX64-Windows-Release + MLProxyX64-Linux-Release + MLBootstrapX64-Linux-Release diff --git a/Bootstrap/src/base_assembly/dotnet.rs b/Bootstrap/src/base_assembly/dotnet.rs index b1c375e22..19e7ecc40 100644 --- a/Bootstrap/src/base_assembly/dotnet.rs +++ b/Bootstrap/src/base_assembly/dotnet.rs @@ -1,16 +1,20 @@ +use lazy_static::lazy_static; use netcorehost::{nethost, pdcstr}; use std::{ ffi::c_void, ptr::{addr_of, addr_of_mut, null_mut}, + sync::RwLock, }; use crate::{ debug, - errors::DynErr, + errors::{dotneterr::DotnetErr, DynErr}, icalls, melonenv, utils::{self, strings::wide_str}, }; +/// These are functions that MelonLoader.BootstrapInterop.dll will fill in, once we call LoadStage1. +/// Interacting with the .net runtime is a pain, so it's a lot easier to just have it give us pointers like this directly. #[repr(C)] #[derive(Debug)] pub struct HostImports { @@ -21,6 +25,9 @@ pub struct HostImports { pub start: fn(), } +/// These are functions that we will pass to MelonLoader.BootstrapInterop.dll. +/// CoreCLR does not have internal calls like mono does, so we have to pass these ourselves. +/// They are stored in Managed, and are accessed by MelonLoader for hooking. #[repr(C)] #[derive(Debug)] pub struct HostExports { @@ -28,29 +35,39 @@ pub struct HostExports { pub hook_detach: unsafe fn(*mut *mut c_void, *mut c_void), } -static mut IMPORTS: Option = None; +// Initializing the host imports as a static variable. Later on this is replaced with a filled in version of the struct. +lazy_static! { + pub static ref IMPORTS: RwLock = RwLock::new(HostImports { + load_assembly_get_ptr: |_, _, _, _| {}, + initialize: || {}, + pre_start: || {}, + start: || {}, + }); +} pub fn init() -> Result<(), DynErr> { let runtime_dir = melonenv::paths::runtime_dir()?; - let hostfxr = nethost::load_hostfxr()?; + let hostfxr = nethost::load_hostfxr().map_err(|_| DotnetErr::FailedHostFXRLoad)?; + let config_path = runtime_dir.join("MelonLoader.runtimeconfig.json"); if !config_path.exists() { - Err("Failed to find the MelonLoader.runtimeconfig.json file!")? + return Err(DotnetErr::RuntimeConfig.into()); } let context = hostfxr.initialize_for_runtime_config(utils::strings::pdcstr(config_path)?)?; - let native_host_path = utils::strings::pdcstr(runtime_dir.join("MelonLoader.NativeHost.dll"))?; + let loader = context.get_delegate_loader_for_assembly(utils::strings::pdcstr( + runtime_dir.join("MelonLoader.NativeHost.dll"), + )?)?; - let loader = context.get_delegate_loader_for_assembly(&native_host_path)?; let init = loader.get_function_with_unmanaged_callers_only::( pdcstr!("MelonLoader.NativeHost.NativeEntryPoint, MelonLoader.NativeHost"), pdcstr!("LoadStage1"), )?; let mut imports = HostImports { - load_assembly_get_ptr: |_, _, _, _| {}, //hackz + load_assembly_get_ptr: |_, _, _, _| {}, initialize: || {}, pre_start: || {}, start: || {}, @@ -62,22 +79,26 @@ pub fn init() -> Result<(), DynErr> { }; debug!("[Dotnet] Invoking LoadStage1")?; + //MelonLoader.BootstrapInterop will fill in the HostImports struct with pointers to functions init(addr_of_mut!(imports)); - debug!( - "[Dotnet] Reloading NativeHost into correct load context and getting LoadStage2 pointer" - )?; + debug!("[Dotnet] Reloading NativeHost into correct load context and getting LoadStage2 pointer")?; + + //a function pointer to be filled let mut init_stage_two = null_mut::(); + //have to make all strings utf16 for C# to understand, of course they can only be passed as IntPtrs (imports.load_assembly_get_ptr)( wide_str(runtime_dir.join("MelonLoader.NativeHost.dll"))?.as_ptr() as isize, - wide_str("MelonLoader.NativeHost.NativeEntryPoint, MelonLoader.NativeHost")?.as_ptr() as isize, + wide_str("MelonLoader.NativeHost.NativeEntryPoint, MelonLoader.NativeHost")?.as_ptr() + as isize, wide_str("LoadStage2")?.as_ptr() as isize, addr_of_mut!(init_stage_two), ); debug!("[Dotnet] Invoking LoadStage2")?; + //turn the function pointer into a function we can invoke let init_stage_two: fn(*mut HostImports, *mut HostExports) = unsafe { std::mem::transmute(init_stage_two) }; init_stage_two(addr_of_mut!(imports), addr_of_mut!(exports)); @@ -88,15 +109,13 @@ pub fn init() -> Result<(), DynErr> { (imports.initialize)(); - unsafe { - IMPORTS = Some(imports); - } + *IMPORTS.try_write()? = imports; Ok(()) } pub fn pre_start() -> Result<(), DynErr> { - let imports = unsafe { IMPORTS.as_ref() }.ok_or("HostImports are not initialized!")?; + let imports = IMPORTS.try_read()?; (imports.pre_start)(); @@ -104,7 +123,7 @@ pub fn pre_start() -> Result<(), DynErr> { } pub fn start() -> Result<(), DynErr> { - let imports = unsafe { IMPORTS.as_ref() }.ok_or("HostImports are not initialized!")?; + let imports = IMPORTS.try_read()?; (imports.start)(); diff --git a/Bootstrap/src/console/os/unix/mod.rs b/Bootstrap/src/console/os/unix/mod.rs index 0e6d9e5b9..a0438b301 100644 --- a/Bootstrap/src/console/os/unix/mod.rs +++ b/Bootstrap/src/console/os/unix/mod.rs @@ -7,7 +7,7 @@ pub unsafe fn init() -> Result<(), DynErr> { } #[allow(dead_code)] -pub fn set_title(title: &str) { +pub fn set_title(_title: &str) { } diff --git a/Bootstrap/src/constants.rs b/Bootstrap/src/constants.rs index 580c3550d..36e354eb7 100644 --- a/Bootstrap/src/constants.rs +++ b/Bootstrap/src/constants.rs @@ -7,16 +7,16 @@ use unity_rs::{ }; pub type InvokeFnMono = - fn(*mut MonoMethod, *mut MonoObject, *mut *mut c_void, *mut *mut MonoObject) -> *mut MonoObject; -pub type InvokeFnIl2Cpp = fn( + extern "C" fn(*mut MonoMethod, *mut MonoObject, *mut *mut c_void, *mut *mut MonoObject) -> *mut MonoObject; +pub type InvokeFnIl2Cpp = extern "C" fn( *mut Il2CppMethod, *mut Il2CppObject, *mut *mut c_void, *mut *mut Il2CppObject, ) -> *mut Il2CppObject; -pub type InitFnMono = fn(*const c_char, *const c_char) -> *mut MonoDomain; -pub type InitFnIl2Cpp = fn(*const c_char) -> *mut Il2CppDomain; +pub type InitFnMono = extern "C" fn(*const c_char, *const c_char) -> *mut MonoDomain; +pub type InitFnIl2Cpp = extern "C" fn(*const c_char) -> *mut Il2CppDomain; pub const MELON_VERSION: &str = "0.6.1"; diff --git a/Bootstrap/src/errors/dotneterr.rs b/Bootstrap/src/errors/dotneterr.rs new file mode 100644 index 000000000..b1ea94a2e --- /dev/null +++ b/Bootstrap/src/errors/dotneterr.rs @@ -0,0 +1,10 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum DotnetErr { + #[error("Failed to load hostfxr. Please make sure you have installed the .NET 6.0 runtime.")] + FailedHostFXRLoad, + + #[error("Failed to find MelonLoader.runtimeconfig.json. Please reinstall MelonLoader.")] + RuntimeConfig +} \ No newline at end of file diff --git a/Bootstrap/src/errors/mod.rs b/Bootstrap/src/errors/mod.rs index e4e48057d..0f68b267c 100644 --- a/Bootstrap/src/errors/mod.rs +++ b/Bootstrap/src/errors/mod.rs @@ -3,3 +3,4 @@ pub type DynErr = Box; pub mod conerr; pub mod hookerr; pub mod logerr; +pub mod dotneterr; \ No newline at end of file diff --git a/Bootstrap/src/hooks/functions.rs b/Bootstrap/src/hooks/functions.rs index 21c3ce77b..a007f946d 100644 --- a/Bootstrap/src/hooks/functions.rs +++ b/Bootstrap/src/hooks/functions.rs @@ -1,8 +1,6 @@ use crate::errors::{hookerr::HookError, DynErr}; -use super::HookedFunction; - -pub fn hook(target: usize, detour: usize) -> Result, HookError> { +pub fn hook(target: usize, detour: usize) -> Result { if target == 0 { return Err(HookError::Nullpointer("target".to_string())); } @@ -25,7 +23,7 @@ pub fn hook(target: usize, detour: usize) -> Result, HookEr } //return Ok with type annotations - HookedFunction::::from(target, trampoline as usize) + Ok(trampoline as usize) } } diff --git a/Bootstrap/src/hooks/init_hook/il2cpp.rs b/Bootstrap/src/hooks/init_hook/il2cpp.rs index 9931119ca..1bee7d3cf 100644 --- a/Bootstrap/src/hooks/init_hook/il2cpp.rs +++ b/Bootstrap/src/hooks/init_hook/il2cpp.rs @@ -1,16 +1,16 @@ -use std::{ffi::c_char, sync::RwLock}; +use std::{ffi::c_char, sync::RwLock, ptr::null_mut}; use lazy_static::lazy_static; use unity_rs::il2cpp::types::Il2CppDomain; use crate::{ - base_assembly, console, constants::InitFnIl2Cpp, debug, errors::DynErr, hooks::{HookedFunction, invoke_hook}, + base_assembly, console, constants::InitFnIl2Cpp, debug, errors::DynErr, hooks::{NativeHook, invoke_hook}, internal_failure, runtime, }; lazy_static! { - pub static ref INIT_HOOK: RwLock> = - RwLock::new(HookedFunction::new()); + pub static ref INIT_HOOK: RwLock> = + RwLock::new(NativeHook::new(null_mut(), null_mut())); } pub fn detour(name: *const c_char) -> *mut Il2CppDomain { diff --git a/Bootstrap/src/hooks/init_hook/mod.rs b/Bootstrap/src/hooks/init_hook/mod.rs index efef35ace..caecaa8ab 100644 --- a/Bootstrap/src/hooks/init_hook/mod.rs +++ b/Bootstrap/src/hooks/init_hook/mod.rs @@ -2,14 +2,14 @@ mod il2cpp; mod mono; use crate::{ - constants::{InitFnIl2Cpp, InitFnMono}, debug, errors::DynErr, runtime, }; +use std::ffi::c_void; use unity_rs::runtime::RuntimeType; -use super::functions; +use super::{NativeHook}; pub fn hook() -> Result<(), DynErr> { let runtime = runtime!()?; @@ -19,12 +19,12 @@ pub fn hook() -> Result<(), DynErr> { debug!("Attaching hook to mono_jit_init_version")?; let init_function = runtime.get_export_ptr("mono_jit_init_version")?; - let detour = mono::detour as usize; - - let hooked = functions::hook::(init_function as usize, detour)?; + let detour = mono::detour as *mut c_void; let mut init_hook = mono::INIT_HOOK.try_write()?; - *init_hook = hooked; + *init_hook = NativeHook::new(init_function, detour); + + init_hook.hook()?; } RuntimeType::Il2Cpp(_) => { @@ -33,10 +33,10 @@ pub fn hook() -> Result<(), DynErr> { let init_function = runtime.get_export_ptr("il2cpp_init")?; let detour = il2cpp::detour as usize; - let hooked = functions::hook::(init_function as usize, detour)?; - let mut init_hook = il2cpp::INIT_HOOK.try_write()?; - *init_hook = hooked; + *init_hook = NativeHook::new(init_function, detour as *mut c_void); + + init_hook.hook()?; } }; diff --git a/Bootstrap/src/hooks/init_hook/mono.rs b/Bootstrap/src/hooks/init_hook/mono.rs index 3ee3de17b..9071970da 100644 --- a/Bootstrap/src/hooks/init_hook/mono.rs +++ b/Bootstrap/src/hooks/init_hook/mono.rs @@ -1,4 +1,4 @@ -use std::{ffi::c_char, sync::RwLock}; +use std::{ffi::c_char, sync::RwLock, ptr::null_mut}; use lazy_static::lazy_static; use unity_rs::{common::domain::UnityDomain, mono::types::MonoDomain, runtime::RuntimeType}; @@ -8,13 +8,13 @@ use crate::{ constants::InitFnMono, debug, debug_enabled, errors::DynErr, - hooks::{invoke_hook, HookedFunction}, + hooks::{invoke_hook, NativeHook}, icalls, internal_failure, melonenv, runtime, rust_str, }; lazy_static! { - pub static ref INIT_HOOK: RwLock> = - RwLock::new(HookedFunction::new()); + pub static ref INIT_HOOK: RwLock> = + RwLock::new(NativeHook::new(null_mut(), null_mut())); } pub fn detour(name: *const c_char, version: *const c_char) -> *mut MonoDomain { diff --git a/Bootstrap/src/hooks/invoke_hook/il2cpp.rs b/Bootstrap/src/hooks/invoke_hook/il2cpp.rs index 3f192c49d..d0b48e0d6 100644 --- a/Bootstrap/src/hooks/invoke_hook/il2cpp.rs +++ b/Bootstrap/src/hooks/invoke_hook/il2cpp.rs @@ -1,13 +1,13 @@ -use std::{ffi::c_void, sync::RwLock}; +use std::{ffi::c_void, sync::RwLock, ptr::null_mut}; use lazy_static::lazy_static; use unity_rs::{il2cpp::types::{Il2CppMethod, Il2CppObject}, common::method::UnityMethod}; -use crate::{constants::InvokeFnIl2Cpp, errors::DynErr, hooks::HookedFunction, internal_failure, runtime, debug, base_assembly}; +use crate::{constants::InvokeFnIl2Cpp, errors::DynErr, hooks::NativeHook, internal_failure, runtime, debug, base_assembly}; lazy_static! { - pub static ref INVOKE_HOOK: RwLock> = - RwLock::new(HookedFunction::new()); + pub static ref INVOKE_HOOK: RwLock> = + RwLock::new(NativeHook::new(null_mut(), null_mut())); } pub fn detour( diff --git a/Bootstrap/src/hooks/invoke_hook/mod.rs b/Bootstrap/src/hooks/invoke_hook/mod.rs index ebd07e579..f53e4c5e8 100644 --- a/Bootstrap/src/hooks/invoke_hook/mod.rs +++ b/Bootstrap/src/hooks/invoke_hook/mod.rs @@ -2,14 +2,14 @@ mod il2cpp; mod mono; use crate::{ - constants::{InvokeFnIl2Cpp, InvokeFnMono}, debug, errors::DynErr, runtime, }; +use std::ffi::c_void; use unity_rs::runtime::RuntimeType; -use super::functions; +use super::{NativeHook}; pub fn hook() -> Result<(), DynErr> { let runtime = runtime!()?; @@ -19,12 +19,10 @@ pub fn hook() -> Result<(), DynErr> { debug!("Attaching hook to mono_runtime_invoke")?; let init_function = runtime.get_export_ptr("mono_runtime_invoke")?; - let detour = mono::detour as usize; - - let hooked = functions::hook::(init_function as usize, detour)?; + let detour = mono::detour as *mut c_void; let mut init_hook = mono::INVOKE_HOOK.try_write()?; - *init_hook = hooked; + *init_hook = NativeHook::new(init_function, detour); } RuntimeType::Il2Cpp(_) => { @@ -33,10 +31,10 @@ pub fn hook() -> Result<(), DynErr> { let init_function = runtime.get_export_ptr("il2cpp_runtime_invoke")?; let detour = il2cpp::detour as usize; - let hooked = functions::hook::(init_function as usize, detour)?; - let mut init_hook = il2cpp::INVOKE_HOOK.try_write()?; - *init_hook = hooked; + *init_hook = NativeHook::new(init_function, detour as *mut c_void); + + init_hook.hook()?; } }; diff --git a/Bootstrap/src/hooks/invoke_hook/mono.rs b/Bootstrap/src/hooks/invoke_hook/mono.rs index 893306ca5..15b515ae0 100644 --- a/Bootstrap/src/hooks/invoke_hook/mono.rs +++ b/Bootstrap/src/hooks/invoke_hook/mono.rs @@ -1,4 +1,4 @@ -use std::{ffi::c_void, sync::RwLock}; +use std::{ffi::c_void, sync::RwLock, ptr::null_mut}; use lazy_static::lazy_static; use unity_rs::{ @@ -8,13 +8,13 @@ use unity_rs::{ }; use crate::{ - base_assembly, constants::InvokeFnMono, debug, errors::DynErr, hooks::HookedFunction, + base_assembly, constants::InvokeFnMono, debug, errors::DynErr, hooks::NativeHook, internal_failure, runtime, }; lazy_static! { - pub static ref INVOKE_HOOK: RwLock> = - RwLock::new(HookedFunction::new()); + pub static ref INVOKE_HOOK: RwLock> = + RwLock::new(NativeHook::new(null_mut(), null_mut())); } pub fn detour( diff --git a/Bootstrap/src/hooks/mod.rs b/Bootstrap/src/hooks/mod.rs index 8ef123ce2..070958276 100644 --- a/Bootstrap/src/hooks/mod.rs +++ b/Bootstrap/src/hooks/mod.rs @@ -1,45 +1,44 @@ -use crate::errors::{hookerr::HookError, DynErr}; +use crate::errors::{DynErr}; use std::marker::PhantomData; use std::ops::Deref; +use std::ffi::c_void; +use std::ptr::null_mut; pub mod functions; pub mod init_hook; pub mod invoke_hook; #[derive(Debug)] -pub struct HookedFunction { - pub target: usize, - pub trampoline: usize, +pub struct NativeHook { + pub target: *mut c_void, + pub trampoline: *mut c_void, + pub detour: *mut c_void, pd: PhantomData, } -impl HookedFunction { - pub fn new() -> Self { +impl NativeHook { + pub fn new(target: *mut c_void, detour: *mut c_void) -> Self { Self { - target: 0, - trampoline: 0, + target, + trampoline: null_mut(), + detour, pd: PhantomData, } } - pub fn from(target: usize, trampoline: usize) -> Result { - if target == 0 { - return Err(HookError::Nullpointer("target".to_string())); - } + pub fn is_hooked(&self) -> bool { + !self.target.is_null() && !self.trampoline.is_null() + } - if trampoline == 0 { - return Err(HookError::Nullpointer("trampoline".to_string())); + pub fn hook(&mut self) -> Result<(), DynErr> { + if self.is_hooked() { + return Ok(()); } - Ok(Self { - target, - trampoline, - pd: PhantomData, - }) - } + let trampoline = functions::hook(self.target as usize, self.detour as usize)?; - pub fn is_hooked(&self) -> bool { - self.target != 0 && self.trampoline != 0 + self.trampoline = trampoline as *mut c_void; + Ok(()) } pub fn unhook(&self) -> Result<(), DynErr> { @@ -47,26 +46,25 @@ impl HookedFunction { return Ok(()); } - functions::unhook(self.target)?; + functions::unhook(self.target as usize)?; Ok(()) } } -unsafe impl Send for HookedFunction {} -unsafe impl Sync for HookedFunction {} +unsafe impl Send for NativeHook {} +unsafe impl Sync for NativeHook {} -impl Clone for HookedFunction { +impl Clone for NativeHook { fn clone(&self) -> Self { - HookedFunction { ..*self } + NativeHook { ..*self } } } -impl Deref for HookedFunction { +impl Deref for NativeHook { type Target = T; fn deref(&self) -> &T { - let t = self.trampoline as *mut std::ffi::c_void; - unsafe { &*(&t as *const *mut _ as *const T) } + unsafe { &*(&self.trampoline as *const *mut _ as *const T) } } } diff --git a/Bootstrap/src/icalls/bootstrap_interop.rs b/Bootstrap/src/icalls/bootstrap_interop.rs index 865040ba0..536739cb4 100644 --- a/Bootstrap/src/icalls/bootstrap_interop.rs +++ b/Bootstrap/src/icalls/bootstrap_interop.rs @@ -1,10 +1,18 @@ use std::ffi::c_void; -use crate::{error, hooks}; +use crate::{error, hooks::{self, NativeHook}}; pub unsafe fn attach(target: *mut *mut c_void, detour: *mut c_void) { - match hooks::functions::hook::(*target as usize, detour as usize) { - Ok(res) => *target = res.trampoline as *mut c_void, + // match NativeHook::::new(*target as usize, detour as usize).hook() { + // Ok(res) => *target = res.trampoline as *mut c_void, + // Err(e) => { + // let _ = error!("Failed to hook function: {}", e.to_string()); + // } + // }; + + let mut hook = NativeHook::::new(*target, detour); + match hook.hook() { + Ok(_) => *target = hook.trampoline as *mut c_void, Err(e) => { let _ = error!("Failed to hook function: {}", e.to_string()); } diff --git a/Bootstrap/src/lib.rs b/Bootstrap/src/lib.rs index 8c2d908cf..7a8866248 100755 --- a/Bootstrap/src/lib.rs +++ b/Bootstrap/src/lib.rs @@ -1,4 +1,6 @@ #![feature(is_some_and)] +#![feature(once_cell)] + #![allow(non_snake_case)] #![deny( missing_debug_implementations, diff --git a/Bootstrap/src/melonenv/macros.rs b/Bootstrap/src/melonenv/macros.rs index d8c87ab07..d85b56238 100644 --- a/Bootstrap/src/melonenv/macros.rs +++ b/Bootstrap/src/melonenv/macros.rs @@ -1,6 +1,10 @@ #[macro_export] macro_rules! debug_enabled { () => { - $crate::melonenv::args::ARGS.debug + if cfg!(debug_assertions) { + true + } else { + $crate::melonenv::args::ARGS.debug + } }; } diff --git a/Cargo.toml b/Cargo.toml index 2e401c3c7..0001ba43a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,7 @@ [workspace] -members = [ "Bootstrap", "Proxy" ] \ No newline at end of file +members = [ "Bootstrap", "Proxy" ] + +[profile.release] +lto = true +codegen-units = 1 +opt-level = "z" \ No newline at end of file diff --git a/Proxy/Cargo.toml b/Proxy/Cargo.toml index 0a937f22a..579871f29 100644 --- a/Proxy/Cargo.toml +++ b/Proxy/Cargo.toml @@ -24,10 +24,5 @@ libc = "0.2.137" name = "version" crate-type = ["cdylib"] -[profile.release] -lto = true -codegen-units = 1 -opt-level = "z" - [build-dependencies] cc = "1.0.76" From e9e2f3dc3c7485aea4a29d279063f07158614971 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Tue, 21 Mar 2023 12:11:42 +0100 Subject: [PATCH 11/79] Fix references to NativeHost in documentation, actually hook mono_runtime_invoke --- Bootstrap/src/base_assembly/dotnet.rs | 6 +++--- Bootstrap/src/hooks/invoke_hook/mod.rs | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Bootstrap/src/base_assembly/dotnet.rs b/Bootstrap/src/base_assembly/dotnet.rs index 19e7ecc40..3a5d8f490 100644 --- a/Bootstrap/src/base_assembly/dotnet.rs +++ b/Bootstrap/src/base_assembly/dotnet.rs @@ -13,7 +13,7 @@ use crate::{ utils::{self, strings::wide_str}, }; -/// These are functions that MelonLoader.BootstrapInterop.dll will fill in, once we call LoadStage1. +/// These are functions that MelonLoader.NativeHost.dll will fill in, once we call LoadStage1. /// Interacting with the .net runtime is a pain, so it's a lot easier to just have it give us pointers like this directly. #[repr(C)] #[derive(Debug)] @@ -25,7 +25,7 @@ pub struct HostImports { pub start: fn(), } -/// These are functions that we will pass to MelonLoader.BootstrapInterop.dll. +/// These are functions that we will pass to MelonLoader.NativeHost.dll. /// CoreCLR does not have internal calls like mono does, so we have to pass these ourselves. /// They are stored in Managed, and are accessed by MelonLoader for hooking. #[repr(C)] @@ -79,7 +79,7 @@ pub fn init() -> Result<(), DynErr> { }; debug!("[Dotnet] Invoking LoadStage1")?; - //MelonLoader.BootstrapInterop will fill in the HostImports struct with pointers to functions + //MelonLoader.NativeHost will fill in the HostImports struct with pointers to functions init(addr_of_mut!(imports)); debug!("[Dotnet] Reloading NativeHost into correct load context and getting LoadStage2 pointer")?; diff --git a/Bootstrap/src/hooks/invoke_hook/mod.rs b/Bootstrap/src/hooks/invoke_hook/mod.rs index f53e4c5e8..b417099bf 100644 --- a/Bootstrap/src/hooks/invoke_hook/mod.rs +++ b/Bootstrap/src/hooks/invoke_hook/mod.rs @@ -23,6 +23,8 @@ pub fn hook() -> Result<(), DynErr> { let mut init_hook = mono::INVOKE_HOOK.try_write()?; *init_hook = NativeHook::new(init_function, detour); + + init_hook.hook()?; } RuntimeType::Il2Cpp(_) => { From ad45a8aeb4eec074368a1f97563db4eb12f8d036 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Tue, 21 Mar 2023 18:15:48 +0100 Subject: [PATCH 12/79] update ReadMe, implement consoleontop & consoledst --- Bootstrap/Cargo.toml | 1 + Bootstrap/src/console/os/windows/mod.rs | 14 +++++++++++--- Bootstrap/src/melonenv/args.rs | 6 ++++++ Bootstrap/src/melonenv/macros.rs | 14 ++++++++++++++ MelonLoader/BootstrapInterop.cs | 3 +++ MelonLoader/MelonUtils.cs | 3 +++ README.md | 7 ++++--- 7 files changed, 42 insertions(+), 6 deletions(-) diff --git a/Bootstrap/Cargo.toml b/Bootstrap/Cargo.toml index 18e33e521..79707783d 100755 --- a/Bootstrap/Cargo.toml +++ b/Bootstrap/Cargo.toml @@ -24,6 +24,7 @@ netcorehost = "0.13.1" windows = { version = "0.46.0", features = [ "Win32_Foundation", "Win32_System_Console", + "Win32_UI_WindowsAndMessaging" ]} [lib] diff --git a/Bootstrap/src/console/os/windows/mod.rs b/Bootstrap/src/console/os/windows/mod.rs index 8bd36487e..1ab659f1b 100644 --- a/Bootstrap/src/console/os/windows/mod.rs +++ b/Bootstrap/src/console/os/windows/mod.rs @@ -7,7 +7,7 @@ use windows::{ core::PCSTR, Win32::{ Foundation::{self, HANDLE, HWND}, - System::Console::*, + System::Console::*, UI::WindowsAndMessaging::{SetWindowPos, HWND_TOPMOST, SWP_NOMOVE, SWP_NOSIZE}, }, }; @@ -15,7 +15,7 @@ use crate::{ constants::{IS_ALPHA, MELON_VERSION}, debug_enabled, errors::{conerr::ConsoleError, DynErr}, - win_str, + win_str, should_set_title, console_on_top, }; lazy_static! { @@ -30,7 +30,7 @@ pub unsafe fn init() -> Result<(), DynErr> { } // store the console window handle - let mut window = WINDOW.lock()?; + let mut window = WINDOW.try_lock()?; *window = GetConsoleWindow(); // a null check @@ -45,6 +45,10 @@ pub unsafe fn init() -> Result<(), DynErr> { set_title(&default_title()); + if console_on_top!() { + let _ = SetWindowPos(*window, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + } + let _ = libc::freopen( win_str!(b"CONIN$\0"), win_str!(b"r\0"), @@ -108,6 +112,10 @@ fn default_title() -> String { } pub fn set_title(title: &str) { + if !should_set_title!() { + return; + } + unsafe { let t = PCSTR(title.as_ptr()); let _ = SetConsoleTitleA(t); diff --git a/Bootstrap/src/melonenv/args.rs b/Bootstrap/src/melonenv/args.rs index d55d11846..b848153f0 100644 --- a/Bootstrap/src/melonenv/args.rs +++ b/Bootstrap/src/melonenv/args.rs @@ -9,6 +9,12 @@ pub struct Cli { #[arg(short, long = "melonloader.debug", default_value = "false")] pub debug: bool, + #[arg(short, long = "melonloader.consoledst", default_value = "false")] + pub console_dst: bool, + + #[arg(short, long = "melonloader.consoleontop", default_value = "false")] + pub console_on_top: bool, + #[arg(short, long = "melonloader.basedir")] pub base_dir: Option, } diff --git a/Bootstrap/src/melonenv/macros.rs b/Bootstrap/src/melonenv/macros.rs index d85b56238..2be3a36fd 100644 --- a/Bootstrap/src/melonenv/macros.rs +++ b/Bootstrap/src/melonenv/macros.rs @@ -8,3 +8,17 @@ macro_rules! debug_enabled { } }; } + +#[macro_export] +macro_rules! should_set_title { + () => { + !$crate::melonenv::args::ARGS.console_dst + }; +} + +#[macro_export] +macro_rules! console_on_top { + () => { + $crate::melonenv::args::ARGS.console_on_top + }; +} diff --git a/MelonLoader/BootstrapInterop.cs b/MelonLoader/BootstrapInterop.cs index 741a8035c..2b6c828bc 100644 --- a/MelonLoader/BootstrapInterop.cs +++ b/MelonLoader/BootstrapInterop.cs @@ -16,6 +16,9 @@ internal static unsafe class BootstrapInterop internal static void SetDefaultConsoleTitleWithGameName([MarshalAs(UnmanagedType.LPStr)] string GameName, [MarshalAs(UnmanagedType.LPStr)] string GameVersion = null) { + if (!MelonLaunchOptions.Console.ShouldSetTitle) + return; + string versionStr = Core.GetVersionString() + $" - {GameName} {GameVersion ?? ""}"; diff --git a/MelonLoader/MelonUtils.cs b/MelonLoader/MelonUtils.cs index 98a6df2d0..a9a04cdb6 100644 --- a/MelonLoader/MelonUtils.cs +++ b/MelonLoader/MelonUtils.cs @@ -409,6 +409,9 @@ public static bool IsOldMono() => File.Exists(MelonEnvironment.UnityGameDataDire public static void SetConsoleTitle(string title) { + if (!MelonLaunchOptions.Console.ShouldSetTitle) + return; + Console.Title = title; } diff --git a/README.md b/README.md index 96567a553..572405652 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Use it to help Develop and Debug MelonLoader, Plugins, and Mods. - [Launch Options](#launch-options) - [Debugging](https://melonwiki.xyz/#/modders/debugging)

-- [Linux Support (__WINE / STEAM PROTON__)](https://melonwiki.xyz/#/README?id=linux-instructions) +- [Linux Support (__WINE / STEAM PROTON / NATIVE__)](https://melonwiki.xyz/#/README?id=linux-instructions) - [Android & Oculus Quest Support (__WIP__)](https://melonwiki.xyz/#/android/general)

@@ -67,6 +67,7 @@ Use it to help Develop and Debug MelonLoader, Plugins, and Mods. - [.NET Framework 3.5 Runtime](https://www.microsoft.com/en-us/download/details.aspx?id=21) - [.NET Framework 4.7.2 Runtime](https://dotnet.microsoft.com/download/dotnet-framework/net472) - [.NET Framework 4.8 Runtime](https://dotnet.microsoft.com/download/dotnet-framework/net48) +- [.NET 6.0 Desktop Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/6.0#runtime-6.0.15) - Microsoft Visual C++ 2015-2019 Re-distributable [[x86](https://aka.ms/vs/16/release/vc_redist.x86.exe)] [[x64](https://aka.ms/vs/16/release/vc_redist.x64.exe)] --- @@ -98,13 +99,13 @@ Use it to help Develop and Debug MelonLoader, Plugins, and Mods. 2. Make sure you have all the [Requirements](#requirements) Installed before attempting to Install. 3. Download MelonLoader [[x86](https://github.com/LavaGang/MelonLoader/releases/latest/download/MelonLoader.x86.zip)] [[x64](https://github.com/LavaGang/MelonLoader/releases/latest/download/MelonLoader.x64.zip)] 4. Extract the MelonLoader folder from the MelonLoader Zip Archive to the Game's Installation Folder. -5. Extract version.dll from the MelonLoader Zip Archive to the Game's Installation Folder. +5. Extract version.dll & dobby.dll from the MelonLoader Zip Archive to the Game's Installation Folder. ### UN-INSTALL: 1. Make sure the Game is Closed and Not Running before attempting to UN-INSTALL. -2. Remove the version.dll file from the Game's Installation Folder. +2. Remove the version.dll & dobby.dll files from the Game's Installation Folder. 3. Remove the MelonLoader folder from the Game's Installation Folder. These additional steps below are OPTIONAL if you want to do a FULL UN-INSTALL. From db9b885abf8f9bbf5c3e9a3379b14285fd7fcc55 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Tue, 21 Mar 2023 18:27:36 +0100 Subject: [PATCH 13/79] implement hideconsole --- Bootstrap/src/console/mod.rs | 18 +++++++++++++++++- Bootstrap/src/console/os/windows/mod.rs | 4 ++-- Bootstrap/src/melonenv/args.rs | 3 +++ Bootstrap/src/melonenv/macros.rs | 7 +++++++ MelonLoader/BootstrapInterop.cs | 2 +- MelonLoader/MelonUtils.cs | 2 +- MelonLoader/Utils/MelonConsole.cs | 8 ++++---- 7 files changed, 35 insertions(+), 9 deletions(-) diff --git a/Bootstrap/src/console/mod.rs b/Bootstrap/src/console/mod.rs index 26fa7ee00..efa9020d9 100644 --- a/Bootstrap/src/console/mod.rs +++ b/Bootstrap/src/console/mod.rs @@ -6,20 +6,36 @@ use os::unix as imp; #[cfg(all(windows))] use os::windows as imp; -use crate::errors::DynErr; +use crate::{errors::DynErr, hide_console}; pub fn init() -> Result<(), DynErr> { + if hide_console!() { + return Ok(()); + } + unsafe { imp::init() } } pub fn null_handles() -> Result<(), DynErr> { + if hide_console!() { + return Ok(()); + } + imp::null_handles() } pub fn set_handles() -> Result<(), DynErr> { + if hide_console!() { + return Ok(()); + } + imp::set_handles() } pub fn set_title(title: &str) { + if hide_console!() { + return; + } + imp::set_title(title) } diff --git a/Bootstrap/src/console/os/windows/mod.rs b/Bootstrap/src/console/os/windows/mod.rs index 1ab659f1b..d961fe862 100644 --- a/Bootstrap/src/console/os/windows/mod.rs +++ b/Bootstrap/src/console/os/windows/mod.rs @@ -73,7 +73,7 @@ pub unsafe fn init() -> Result<(), DynErr> { set_handles()?; - let output_handle = OUTPUT_HANDLE.lock()?; + let output_handle = OUTPUT_HANDLE.try_lock()?; let mut mode = CONSOLE_MODE(0); let _ = GetConsoleMode(*output_handle, &mut mode); @@ -124,7 +124,7 @@ pub fn set_title(title: &str) { pub fn set_handles() -> Result<(), DynErr> { unsafe { - let handle = OUTPUT_HANDLE.lock()?; + let handle = OUTPUT_HANDLE.try_lock()?; let _ = SetStdHandle(STD_OUTPUT_HANDLE, *handle); let _ = SetStdHandle(STD_ERROR_HANDLE, *handle); diff --git a/Bootstrap/src/melonenv/args.rs b/Bootstrap/src/melonenv/args.rs index b848153f0..2af2a0252 100644 --- a/Bootstrap/src/melonenv/args.rs +++ b/Bootstrap/src/melonenv/args.rs @@ -15,6 +15,9 @@ pub struct Cli { #[arg(short, long = "melonloader.consoleontop", default_value = "false")] pub console_on_top: bool, + #[arg(short, long = "melonloader.hideconsole", default_value = "false")] + pub hide_console: bool, + #[arg(short, long = "melonloader.basedir")] pub base_dir: Option, } diff --git a/Bootstrap/src/melonenv/macros.rs b/Bootstrap/src/melonenv/macros.rs index 2be3a36fd..20e8e3e45 100644 --- a/Bootstrap/src/melonenv/macros.rs +++ b/Bootstrap/src/melonenv/macros.rs @@ -22,3 +22,10 @@ macro_rules! console_on_top { $crate::melonenv::args::ARGS.console_on_top }; } + +#[macro_export] +macro_rules! hide_console { + () => { + $crate::melonenv::args::ARGS.hide_console + }; +} \ No newline at end of file diff --git a/MelonLoader/BootstrapInterop.cs b/MelonLoader/BootstrapInterop.cs index 2b6c828bc..7f9acab19 100644 --- a/MelonLoader/BootstrapInterop.cs +++ b/MelonLoader/BootstrapInterop.cs @@ -16,7 +16,7 @@ internal static unsafe class BootstrapInterop internal static void SetDefaultConsoleTitleWithGameName([MarshalAs(UnmanagedType.LPStr)] string GameName, [MarshalAs(UnmanagedType.LPStr)] string GameVersion = null) { - if (!MelonLaunchOptions.Console.ShouldSetTitle) + if (!MelonLaunchOptions.Console.ShouldSetTitle || MelonLaunchOptions.Console.ShouldHide) return; string versionStr = Core.GetVersionString() + diff --git a/MelonLoader/MelonUtils.cs b/MelonLoader/MelonUtils.cs index a9a04cdb6..9a7542f13 100644 --- a/MelonLoader/MelonUtils.cs +++ b/MelonLoader/MelonUtils.cs @@ -409,7 +409,7 @@ public static bool IsOldMono() => File.Exists(MelonEnvironment.UnityGameDataDire public static void SetConsoleTitle(string title) { - if (!MelonLaunchOptions.Console.ShouldSetTitle) + if (!MelonLaunchOptions.Console.ShouldSetTitle || MelonLaunchOptions.Console.ShouldHide) return; Console.Title = title; diff --git a/MelonLoader/Utils/MelonConsole.cs b/MelonLoader/Utils/MelonConsole.cs index e1fbd31f9..c9ff95a5d 100644 --- a/MelonLoader/Utils/MelonConsole.cs +++ b/MelonLoader/Utils/MelonConsole.cs @@ -16,7 +16,7 @@ internal static class MelonConsole internal static void Init() { - if (MelonUtils.IsUnderWineOrSteamProton() || !MelonUtils.IsWindows) + if (MelonUtils.IsUnderWineOrSteamProton() || !MelonUtils.IsWindows || MelonLaunchOptions.Console.ShouldHide) return; ConsoleOutHandle = GetStdHandle(STD_OUTPUT_HANDLE); @@ -27,7 +27,7 @@ internal static void Init() internal static void WriteLine(string txt) { - if (MelonUtils.IsUnderWineOrSteamProton() || !MelonUtils.IsWindows) + if (MelonUtils.IsUnderWineOrSteamProton() || !MelonUtils.IsWindows || MelonLaunchOptions.Console.ShouldHide) { Console.WriteLine(txt); return; @@ -37,7 +37,7 @@ internal static void WriteLine(string txt) internal static void WriteLine(object txt) { - if (MelonUtils.IsUnderWineOrSteamProton() || !MelonUtils.IsWindows) + if (MelonUtils.IsUnderWineOrSteamProton() || !MelonUtils.IsWindows || MelonLaunchOptions.Console.ShouldHide) { Console.WriteLine(txt.ToString()); return; @@ -47,7 +47,7 @@ internal static void WriteLine(object txt) internal static void WriteLine() { - if (MelonUtils.IsUnderWineOrSteamProton() || !MelonUtils.IsWindows) + if (MelonUtils.IsUnderWineOrSteamProton() || !MelonUtils.IsWindows || MelonLaunchOptions.Console.ShouldHide) { Console.WriteLine(); return; From c80726c0e0d14bf513330fc8570384851aaf5b44 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Tue, 21 Mar 2023 18:30:32 +0100 Subject: [PATCH 14/79] impliment hidewarnings --- MelonLoader/Utils/MelonLogger.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/MelonLoader/Utils/MelonLogger.cs b/MelonLoader/Utils/MelonLogger.cs index b2cddf736..fba93cc83 100644 --- a/MelonLoader/Utils/MelonLogger.cs +++ b/MelonLoader/Utils/MelonLogger.cs @@ -196,7 +196,13 @@ internal static string GetTimestamp(bool error) return builder.ToString(); } - internal static void Internal_Warning(string namesection, string txt) => Internal_Msg(Color.Yellow, Color.Yellow, namesection, txt); + internal static void Internal_Warning(string namesection, string txt) + { + if (MelonLaunchOptions.Console.HideWarnings) + return; + + Internal_Msg(Color.Yellow, Color.Yellow, namesection, txt); + } internal static void Internal_Error(string namesection, string txt) => Internal_Msg(Color.IndianRed, Color.IndianRed, namesection, txt); From 9c3b87c1626ab1080a2e1942d17f75d54b519707 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Tue, 21 Mar 2023 18:32:25 +0100 Subject: [PATCH 15/79] Update Readme, implement Dabbing --- MelonLoader/Utils/ManagedAnalyticsBlocker.cs | 2 +- README.md | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/MelonLoader/Utils/ManagedAnalyticsBlocker.cs b/MelonLoader/Utils/ManagedAnalyticsBlocker.cs index 7430f78c2..bcdbe4e58 100644 --- a/MelonLoader/Utils/ManagedAnalyticsBlocker.cs +++ b/MelonLoader/Utils/ManagedAnalyticsBlocker.cs @@ -109,7 +109,7 @@ public static void Install() //And, if on x64, ws2_32 getaddrinfo //TODO: is this doable on Unix? - if (MelonUtils.IsMac || MelonUtils.IsUnix || MelonUtils.IsUnderWineOrSteamProton()) + if (MelonUtils.IsMac || MelonUtils.IsUnix || MelonUtils.IsUnderWineOrSteamProton() || MelonLaunchOptions.AnalyticsBlocker.ShouldDAB) return; MelonDebug.Msg("Initializing Analytics Blocker..."); diff --git a/README.md b/README.md index 572405652..b350debde 100644 --- a/README.md +++ b/README.md @@ -217,7 +217,6 @@ These additional steps below are OPTIONAL if you want to do a FULL UN-INSTALL. | File Names: | | - | -| psapi.dll | | version.dll | | winhttp.dll | | winmm.dll | From 3cc313e65cedbcc7383abc42cf2b576f7fcc5025 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Tue, 21 Mar 2023 18:35:08 +0100 Subject: [PATCH 16/79] nevermind, dabbing is cringe --- MelonLoader/Utils/ManagedAnalyticsBlocker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MelonLoader/Utils/ManagedAnalyticsBlocker.cs b/MelonLoader/Utils/ManagedAnalyticsBlocker.cs index bcdbe4e58..7430f78c2 100644 --- a/MelonLoader/Utils/ManagedAnalyticsBlocker.cs +++ b/MelonLoader/Utils/ManagedAnalyticsBlocker.cs @@ -109,7 +109,7 @@ public static void Install() //And, if on x64, ws2_32 getaddrinfo //TODO: is this doable on Unix? - if (MelonUtils.IsMac || MelonUtils.IsUnix || MelonUtils.IsUnderWineOrSteamProton() || MelonLaunchOptions.AnalyticsBlocker.ShouldDAB) + if (MelonUtils.IsMac || MelonUtils.IsUnix || MelonUtils.IsUnderWineOrSteamProton()) return; MelonDebug.Msg("Initializing Analytics Blocker..."); From 4ba4f472308daf8a5e90ad5645779833aa4ccab0 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Thu, 23 Mar 2023 18:59:44 +0100 Subject: [PATCH 17/79] remove short args for linux --- Bootstrap/Cargo.toml | 2 +- Bootstrap/src/melonenv/args.rs | 10 +++++----- Proxy/src/core.rs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Bootstrap/Cargo.toml b/Bootstrap/Cargo.toml index 79707783d..0e51c1f8d 100755 --- a/Bootstrap/Cargo.toml +++ b/Bootstrap/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] #unity-rs = { path = "C:/Users/sarah/Documents/rust/Ferrex/unity/" } -unity-rs = { git = "https://github.com/RinLovesYou/Ferrex/"} +unity-rs = { git = "https://github.com/RinLovesYou/Ferrex/", commit = "8989aae" } ctor = "0.1.26" chrono = "0.4.23" colored = "2.0.0" diff --git a/Bootstrap/src/melonenv/args.rs b/Bootstrap/src/melonenv/args.rs index 2af2a0252..a78d772c0 100644 --- a/Bootstrap/src/melonenv/args.rs +++ b/Bootstrap/src/melonenv/args.rs @@ -6,19 +6,19 @@ use crate::{internal_failure}; #[derive(Debug, Parser)] #[command(author, version, about, long_about = None)] pub struct Cli { - #[arg(short, long = "melonloader.debug", default_value = "false")] + #[arg(long = "melonloader.debug", default_value = "false")] pub debug: bool, - #[arg(short, long = "melonloader.consoledst", default_value = "false")] + #[arg(long = "melonloader.consoledst", default_value = "false")] pub console_dst: bool, - #[arg(short, long = "melonloader.consoleontop", default_value = "false")] + #[arg(long = "melonloader.consoleontop", default_value = "false")] pub console_on_top: bool, - #[arg(short, long = "melonloader.hideconsole", default_value = "false")] + #[arg(long = "melonloader.hideconsole", default_value = "false")] pub hide_console: bool, - #[arg(short, long = "melonloader.basedir")] + #[arg(long = "melonloader.basedir")] pub base_dir: Option, } diff --git a/Proxy/src/core.rs b/Proxy/src/core.rs index 121e2c4c7..a0e2c53fc 100644 --- a/Proxy/src/core.rs +++ b/Proxy/src/core.rs @@ -8,7 +8,7 @@ use std::{error, path::PathBuf, sync::Mutex}; #[derive(Parser)] struct Arguments { - #[arg(long, short, default_value = "false")] + #[arg(long = "no-mods", default_value = "false")] no_mods: bool, #[arg(long = "melonloader.basedir")] From 3fb1e556a787bd3929c6bf09f0fe4cf0ce9398b6 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Thu, 23 Mar 2023 19:54:29 +0100 Subject: [PATCH 18/79] specify ferrex revision --- Bootstrap/Cargo.toml | 2 +- Cargo.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Bootstrap/Cargo.toml b/Bootstrap/Cargo.toml index 0e51c1f8d..e587fb1f6 100755 --- a/Bootstrap/Cargo.toml +++ b/Bootstrap/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] #unity-rs = { path = "C:/Users/sarah/Documents/rust/Ferrex/unity/" } -unity-rs = { git = "https://github.com/RinLovesYou/Ferrex/", commit = "8989aae" } +unity-rs = { git = "https://github.com/RinLovesYou/Ferrex/", rev = "8989aae" } ctor = "0.1.26" chrono = "0.4.23" colored = "2.0.0" diff --git a/Cargo.lock b/Cargo.lock index e67969caa..99c9b7802 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2058,7 +2058,7 @@ checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "unity-rs" version = "0.1.0" -source = "git+https://github.com/RinLovesYou/Ferrex/#6af81ebb5d2354941a2f01485631591ed5ebedba" +source = "git+https://github.com/RinLovesYou/Ferrex/?rev=8989aae#8989aae9f778e58f0c8231114a4bf70de137bd08" dependencies = [ "libc", "libloading", From 73e01f597976a8b2a2a669eb44597ce4bb2b5403 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Thu, 23 Mar 2023 21:11:58 +0100 Subject: [PATCH 19/79] bump unity-rs --- Bootstrap/Cargo.toml | 2 +- Cargo.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Bootstrap/Cargo.toml b/Bootstrap/Cargo.toml index e587fb1f6..c31c08fa8 100755 --- a/Bootstrap/Cargo.toml +++ b/Bootstrap/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] #unity-rs = { path = "C:/Users/sarah/Documents/rust/Ferrex/unity/" } -unity-rs = { git = "https://github.com/RinLovesYou/Ferrex/", rev = "8989aae" } +unity-rs = { git = "https://github.com/RinLovesYou/Ferrex/", rev = "77d114c" } ctor = "0.1.26" chrono = "0.4.23" colored = "2.0.0" diff --git a/Cargo.lock b/Cargo.lock index 99c9b7802..a571eb0f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2058,7 +2058,7 @@ checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "unity-rs" version = "0.1.0" -source = "git+https://github.com/RinLovesYou/Ferrex/?rev=8989aae#8989aae9f778e58f0c8231114a4bf70de137bd08" +source = "git+https://github.com/RinLovesYou/Ferrex/?rev=77d114c#77d114c08fa57609b2cfcb1b9cb4572f53c60259" dependencies = [ "libc", "libloading", From dcef63893a3ffed8bcd00560a87bed4cb80e41bf Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Thu, 23 Mar 2023 21:16:39 +0100 Subject: [PATCH 20/79] replace lock() with try_lock() everywhere to prevent panics --- Bootstrap/src/base_assembly/mono.rs | 12 ++++++------ Bootstrap/src/console/os/windows/mod.rs | 2 +- Bootstrap/src/icalls/resolve_internals.rs | 4 ++-- Proxy/src/core.rs | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Bootstrap/src/base_assembly/mono.rs b/Bootstrap/src/base_assembly/mono.rs index fc4a4a7eb..a3a732724 100644 --- a/Bootstrap/src/base_assembly/mono.rs +++ b/Bootstrap/src/base_assembly/mono.rs @@ -50,10 +50,10 @@ pub fn init(runtime: &FerrexRuntime) -> Result<(), DynErr> { let loadinfo_method = assemblymanager_class.get_method("LoadInfo", 1, runtime)?; //store the methods for later, in a thread safe global static. - *MONO_PRESTART.lock()? = prestart_method; - *MONO_START.lock()? = start_method; - *ASSEMBLYMANAGER_RESOLVE.lock()? = resolve_method; - *ASSEMBLYMANAGER_LOADINFO.lock()? = loadinfo_method; + *MONO_PRESTART.try_lock()? = prestart_method; + *MONO_START.try_lock()? = start_method; + *ASSEMBLYMANAGER_RESOLVE.try_lock()? = resolve_method; + *ASSEMBLYMANAGER_LOADINFO.try_lock()? = loadinfo_method; //invoke the MelonLoader initialize method. let _ = initialize_method.invoke(None, None, runtime)?; @@ -62,7 +62,7 @@ pub fn init(runtime: &FerrexRuntime) -> Result<(), DynErr> { } pub fn pre_start() -> Result<(), DynErr> { - let prestart_method = MONO_PRESTART.lock()?; + let prestart_method = MONO_PRESTART.try_lock()?; if prestart_method.inner.is_null() { return Err("PreStart method not found".into()); } @@ -72,7 +72,7 @@ pub fn pre_start() -> Result<(), DynErr> { } pub fn start() -> Result<(), DynErr> { - let start_method = MONO_START.lock()?; + let start_method = MONO_START.try_lock()?; if start_method.inner.is_null() { return Err("Start method not found".into()); } diff --git a/Bootstrap/src/console/os/windows/mod.rs b/Bootstrap/src/console/os/windows/mod.rs index d961fe862..1ab049e52 100644 --- a/Bootstrap/src/console/os/windows/mod.rs +++ b/Bootstrap/src/console/os/windows/mod.rs @@ -67,7 +67,7 @@ pub unsafe fn init() -> Result<(), DynErr> { // needs to be in its own scope to drop the lock { - let mut output_handle = OUTPUT_HANDLE.lock()?; + let mut output_handle = OUTPUT_HANDLE.try_lock()?; *output_handle = GetStdHandle(STD_OUTPUT_HANDLE)?; } diff --git a/Bootstrap/src/icalls/resolve_internals.rs b/Bootstrap/src/icalls/resolve_internals.rs index 80a4a5eb8..fc6a4046c 100644 --- a/Bootstrap/src/icalls/resolve_internals.rs +++ b/Bootstrap/src/icalls/resolve_internals.rs @@ -33,7 +33,7 @@ fn assembly_resolve( ) -> Result<*mut MonoAssembly, DynErr> { let runtime = runtime!()?; - let resolve_method = base_assembly::mono::ASSEMBLYMANAGER_RESOLVE.lock()?; + let resolve_method = base_assembly::mono::ASSEMBLYMANAGER_RESOLVE.try_lock()?; if resolve_method.inner.is_null() { return Err("AssemblyManager.Resolve is null".into()); @@ -90,7 +90,7 @@ fn load_hook_inner(assembly: *mut MonoAssembly) -> Result<(), DynErr> { return Ok(()); } - let load_method = base_assembly::mono::ASSEMBLYMANAGER_LOADINFO.lock()?; + let load_method = base_assembly::mono::ASSEMBLYMANAGER_LOADINFO.try_lock()?; if load_method.inner.is_null() { return Ok(()); } diff --git a/Proxy/src/core.rs b/Proxy/src/core.rs index a0e2c53fc..dab8040b6 100644 --- a/Proxy/src/core.rs +++ b/Proxy/src/core.rs @@ -42,7 +42,7 @@ pub fn init() -> Result<(), Box> { let bootstrap_path = files::get_bootstrap_path(&base_path)?; unsafe { - *BOOTSTRAP.lock()? = Some(Library::new(&bootstrap_path)?); + *BOOTSTRAP.try_lock()? = Some(Library::new(&bootstrap_path)?); } Ok(()) From 0c6317087691e979174f9b5b32f5102f4c4b3b45 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Thu, 23 Mar 2023 21:44:26 +0100 Subject: [PATCH 21/79] implement try_into String for PathBuf --- Bootstrap/src/constants.rs | 3 +++ Bootstrap/src/errors/melonerr.rs | 7 +++++++ Bootstrap/src/errors/mod.rs | 3 ++- Bootstrap/src/hooks/init_hook/mono.rs | 8 +++----- Bootstrap/src/melonenv/paths.rs | 26 +++++++++++++------------- Bootstrap/src/utils/mod.rs | 1 + Bootstrap/src/utils/pathbuf_impls.rs | 25 +++++++++++++++++++++++++ 7 files changed, 54 insertions(+), 19 deletions(-) create mode 100644 Bootstrap/src/errors/melonerr.rs create mode 100644 Bootstrap/src/utils/pathbuf_impls.rs diff --git a/Bootstrap/src/constants.rs b/Bootstrap/src/constants.rs index 36e354eb7..152fd6063 100644 --- a/Bootstrap/src/constants.rs +++ b/Bootstrap/src/constants.rs @@ -39,3 +39,6 @@ pub const BLUE: Color = Color::TrueColor { g: (64), b: (255), }; + +#[derive(Debug, Clone, Copy)] +pub struct W(pub T); \ No newline at end of file diff --git a/Bootstrap/src/errors/melonerr.rs b/Bootstrap/src/errors/melonerr.rs new file mode 100644 index 000000000..71e532093 --- /dev/null +++ b/Bootstrap/src/errors/melonerr.rs @@ -0,0 +1,7 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum MelonErr { + #[error("{0}")] + Generic(String), +} diff --git a/Bootstrap/src/errors/mod.rs b/Bootstrap/src/errors/mod.rs index 0f68b267c..877f13e87 100644 --- a/Bootstrap/src/errors/mod.rs +++ b/Bootstrap/src/errors/mod.rs @@ -3,4 +3,5 @@ pub type DynErr = Box; pub mod conerr; pub mod hookerr; pub mod logerr; -pub mod dotneterr; \ No newline at end of file +pub mod dotneterr; +pub mod melonerr; \ No newline at end of file diff --git a/Bootstrap/src/hooks/init_hook/mono.rs b/Bootstrap/src/hooks/init_hook/mono.rs index 9071970da..ce701d27e 100644 --- a/Bootstrap/src/hooks/init_hook/mono.rs +++ b/Bootstrap/src/hooks/init_hook/mono.rs @@ -1,6 +1,6 @@ use std::{ffi::c_char, sync::RwLock, ptr::null_mut}; -use lazy_static::lazy_static; +use lazy_static::{lazy_static}; use unity_rs::{common::domain::UnityDomain, mono::types::MonoDomain, runtime::RuntimeType}; use crate::{ @@ -27,9 +27,7 @@ fn detour_inner(name: *const c_char, version: *const c_char) -> Result<*mut Mono console::set_handles()?; let rust_name = rust_str!(name)?; - let base_dir = melonenv::paths::GAME_DIR - .to_str() - .ok_or("Failed to convert base dir to string")?; + let base_dir: String = melonenv::paths::GAME_DIR.clone().try_into()?; let runtime = runtime!()?; @@ -57,7 +55,7 @@ fn detour_inner(name: *const c_char, version: *const c_char) -> Result<*mut Mono debug!("Setting Mono Config")?; //the result of this can be ignored. it is not guaranteed that this function exists - let _ = runtime.set_domain_config(&domain, base_dir, rust_name); + let _ = runtime.set_domain_config(&domain, base_dir.as_str(), rust_name); } } diff --git a/Bootstrap/src/melonenv/paths.rs b/Bootstrap/src/melonenv/paths.rs index 107f288d5..b34e1a2e6 100644 --- a/Bootstrap/src/melonenv/paths.rs +++ b/Bootstrap/src/melonenv/paths.rs @@ -3,28 +3,28 @@ use std::path::PathBuf; use lazy_static::lazy_static; use unity_rs::runtime::RuntimeType; -use crate::{errors::DynErr, internal_failure, runtime}; +use crate::{errors::DynErr, internal_failure, runtime, constants::W}; use super::args::ARGS; lazy_static! { - pub static ref BASE_DIR: PathBuf = { + pub static ref BASE_DIR: W = { match ARGS.base_dir { - Some(ref path) => PathBuf::from(path.clone()), - None => std::env::current_dir().unwrap_or_else(|e| { + Some(ref path) => W(PathBuf::from(path.clone())), + None => W(std::env::current_dir().unwrap_or_else(|e| { internal_failure!("Failed to get base directory: {}", e.to_string()); - }), + })), } }; - pub static ref GAME_DIR: PathBuf = { - std::env::current_dir().unwrap_or_else(|e| { + pub static ref GAME_DIR: W = { + W(std::env::current_dir().unwrap_or_else(|e| { internal_failure!("Failed to get game directory: {}", e.to_string()); - }) + })) }; - pub static ref MELONLOADER_FOLDER: PathBuf = BASE_DIR.join("MelonLoader"); - pub static ref DEPENDENCIES_FOLDER: PathBuf = MELONLOADER_FOLDER.join("Dependencies"); - pub static ref SUPPORT_MODULES_FOLDER: PathBuf = DEPENDENCIES_FOLDER.join("SupportModules"); - pub static ref PRELOAD_DLL: PathBuf = SUPPORT_MODULES_FOLDER.join("Preload.dll"); + pub static ref MELONLOADER_FOLDER: W = W(BASE_DIR.join("MelonLoader")); + pub static ref DEPENDENCIES_FOLDER: W = W(MELONLOADER_FOLDER.join("Dependencies")); + pub static ref SUPPORT_MODULES_FOLDER: W = W(DEPENDENCIES_FOLDER.join("SupportModules")); + pub static ref PRELOAD_DLL: W = W(SUPPORT_MODULES_FOLDER.join("Preload.dll")); } pub fn runtime_dir() -> Result { @@ -37,7 +37,7 @@ pub fn runtime_dir() -> Result { RuntimeType::Il2Cpp(_) => path.push("net6"), } - Ok(path) + Ok(path.to_path_buf()) } pub fn get_managed_dir() -> Result { diff --git a/Bootstrap/src/utils/mod.rs b/Bootstrap/src/utils/mod.rs index 7bb78192a..18a86e077 100755 --- a/Bootstrap/src/utils/mod.rs +++ b/Bootstrap/src/utils/mod.rs @@ -1,2 +1,3 @@ pub mod runtime; pub mod strings; +pub mod pathbuf_impls; \ No newline at end of file diff --git a/Bootstrap/src/utils/pathbuf_impls.rs b/Bootstrap/src/utils/pathbuf_impls.rs new file mode 100644 index 000000000..fde42a866 --- /dev/null +++ b/Bootstrap/src/utils/pathbuf_impls.rs @@ -0,0 +1,25 @@ +use std::{path::{PathBuf}, ops::{DerefMut, Deref}}; + +use crate::{constants::W, errors::{melonerr::MelonErr}}; + +impl TryFrom> for String { + type Error = MelonErr; + + fn try_from(value: W) -> Result { + value.0.to_str().map(String::from).ok_or_else(|| MelonErr::Generic("Failed to convert PathBuf to &str".to_string())) + } +} + +impl Deref for W { + type Target = PathBuf; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for W { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} \ No newline at end of file From 5df43783942144aca313a401daa3068c23fee934 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Wed, 29 Mar 2023 20:29:31 +0200 Subject: [PATCH 22/79] Bump MonoMod.RuntimeDetour to latest --- MelonLoader/MelonLoader.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/MelonLoader/MelonLoader.csproj b/MelonLoader/MelonLoader.csproj index 0059242f3..7d871b751 100644 --- a/MelonLoader/MelonLoader.csproj +++ b/MelonLoader/MelonLoader.csproj @@ -35,6 +35,7 @@ +
From 3c37c0b3e7d2ab969bf34d5d899adb7a94ce76c1 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Wed, 29 Mar 2023 21:50:43 +0200 Subject: [PATCH 23/79] update classdata.tpk --- MelonLoader/Resources/classdata.tpk | Bin 170602 -> 167971 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/MelonLoader/Resources/classdata.tpk b/MelonLoader/Resources/classdata.tpk index 6c99808447fb2faa4cd6c768eaf31b225b981318..1c763ebbf5582c40fa2a05d559fe34380d4df540 100644 GIT binary patch literal 167971 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
{BG#$OKe69RbEExj@J8ZR^S9#UEL?-Y=p!8$F#P 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

    UDW2~*`%Uh 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 )J9NM0eW zq&nX_ZYiY|$6xhB&&02d!fB@$@yWsy6sg*2noJL#y8enLf@(6BAH2yx#*Qxu{+?9{ zvNuyC+VY+89)rN;d$A}DQHdu8aO}x-Z*OER1$^_D-YWv7ZG*(8maix2kT5kd7TsK_ zSh@)Wv13-0{z&BE9or81Bt>fh3Nk&M>Kdx60{902&sgvy^pl3Q1~f_DPCj_J%_Z?? z(ASvkAHq}N7-N2lW|Y#@%r!lqAE{jg?`UmUf6%98DJCwhm|baUZU>uR4B_w);zr)9 zt?(Ot0Qvn!;^LzbYhiYA{m^x0`A1Wf4cqc*Aj_Z8R$O+Ut#BHx&k2_eD=s-ut$N>Q zS_q%6caM+GUz|*eaz68%-qoRW)Q++xS-6#@Zgcuzh*Awn*dL3aj&OOz+#-AY$@%(3 z;ecL)Bk8c;BbC=Wc1V_T(`fF)rmTG0eTe}fU!&<$0Hb_d)mW=?OUx^6q*UFM?+YlS z6yZN*i(JcCe6eOxQ(aL|A+#J#haF#m@iIy#_4qa_Ep$tA^UUG~zRp4F1YhNv?mz

    + /// Any additional credits that the mod author might want to include + /// + public string Credits { get; internal set; } + + /// + /// AdditionalCredits constructor + /// + /// The additional credits of the mod + public MelonAdditionalCreditsAttribute(string credits) { + Credits = credits; + } + } +} diff --git a/MelonLoader/Melons/MelonAssembly.cs b/MelonLoader/Melons/MelonAssembly.cs index 906e181cd..853d61c25 100644 --- a/MelonLoader/Melons/MelonAssembly.cs +++ b/MelonLoader/Melons/MelonAssembly.cs @@ -254,6 +254,7 @@ public void LoadMelons() var priorityAttr = MelonUtils.PullAttributeFromAssembly(Assembly); var colorAttr = MelonUtils.PullAttributeFromAssembly(Assembly); var authorColorAttr = MelonUtils.PullAttributeFromAssembly(Assembly); + var additionalCreditsAttr = MelonUtils.PullAttributeFromAssembly(Assembly); var procAttrs = MelonUtils.PullAttributesFromAssembly(Assembly); var gameAttrs = MelonUtils.PullAttributesFromAssembly(Assembly); var optionalDependenciesAttr = MelonUtils.PullAttributeFromAssembly(Assembly); @@ -266,6 +267,7 @@ public void LoadMelons() var harmonyDPAAttr = MelonUtils.PullAttributeFromAssembly(Assembly); melon.Info = info; + melon.AdditionalCredits = additionalCreditsAttr; melon.MelonAssembly = this; melon.Priority = priorityAttr?.Priority ?? 0; melon.ConsoleColor = colorAttr?.DrawingColor ?? MelonLogger.DefaultMelonColor; diff --git a/MelonLoader/Melons/MelonBase.cs b/MelonLoader/Melons/MelonBase.cs index 047950ce7..611921486 100644 --- a/MelonLoader/Melons/MelonBase.cs +++ b/MelonLoader/Melons/MelonBase.cs @@ -111,6 +111,11 @@ private static void SortMelons(ref List melons) where T : MelonBase /// public MelonInfoAttribute Info { get; internal set; } + /// + /// AdditionalCredits Attribute of the Melon + /// + public MelonAdditionalCreditsAttribute AdditionalCredits { get; internal set; } + /// /// Process Attributes of the Melon. /// @@ -525,7 +530,7 @@ private void PrintLoadInfo() { MelonLogger.WriteLine(Color.DarkGreen); - MelonLogger.Internal_PrintModName(ConsoleColor, AuthorConsoleColor, Info.Name, Info.Author, Info.Version, ID); + MelonLogger.Internal_PrintModName(ConsoleColor, AuthorConsoleColor, Info.Name, Info.Author, AdditionalCredits?.Credits, Info.Version, ID); MelonLogger.MsgDirect(Color.DarkGray, $"Assembly: {Path.GetFileName(MelonAssembly.Location)}"); MelonLogger.WriteLine(Color.DarkGreen); @@ -536,7 +541,7 @@ private void PrintUnloadInfo(string reason) MelonLogger.WriteLine(Color.DarkRed); MelonLogger.MsgDirect(Color.DarkGray, MelonTypeName + " deinitialized:"); - MelonLogger.Internal_PrintModName(ConsoleColor, AuthorConsoleColor, Info.Name, Info.Author, Info.Version, ID); + MelonLogger.Internal_PrintModName(ConsoleColor, AuthorConsoleColor, Info.Name, Info.Author, AdditionalCredits?.Credits, Info.Version, ID); if (!string.IsNullOrEmpty(reason)) { diff --git a/MelonLoader/Utils/MelonLogger.cs b/MelonLoader/Utils/MelonLogger.cs index fba93cc83..162988147 100644 --- a/MelonLoader/Utils/MelonLogger.cs +++ b/MelonLoader/Utils/MelonLogger.cs @@ -217,7 +217,7 @@ internal static void WriteSpacer() Utils.MelonConsole.WriteLine(); } - internal static void Internal_PrintModName(Color meloncolor, Color authorcolor, string name, string author, string version, string id) + internal static void Internal_PrintModName(Color meloncolor, Color authorcolor, string name, string author, string additionalCredits, string version, string id) { LogWriter.WriteLine($"[{GetTimeStamp()}] {name} v{version}{(id == null ? "" : $" ({id})")}"); LogWriter.WriteLine($"[{GetTimeStamp()}] by {author}"); @@ -230,6 +230,12 @@ internal static void Internal_PrintModName(Color meloncolor, Color authorcolor, builder.Append(GetTimestamp(false)); builder.Append($"by {author}".Pastel(authorcolor)); + if (additionalCredits is not null) { + builder.AppendLine(); + builder.Append(GetTimestamp(false)); + builder.Append($"Additional credits: {additionalCredits}"); + } + Utils.MelonConsole.WriteLine(builder.ToString()); } From ecb7cc90bb6478b20c0e5a981448d13f10a24204 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Mar 2023 18:30:13 +0000 Subject: [PATCH 31/79] Bump AssetsTools.NET from 3.0.0-preview1 to 3.0.0-preview3 Bumps [AssetsTools.NET](https://github.com/nesrak1/AssetsTools.NET) from 3.0.0-preview1 to 3.0.0-preview3. - [Release notes](https://github.com/nesrak1/AssetsTools.NET/releases) - [Commits](https://github.com/nesrak1/AssetsTools.NET/commits) --- updated-dependencies: - dependency-name: AssetsTools.NET dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- MelonLoader/MelonLoader.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MelonLoader/MelonLoader.csproj b/MelonLoader/MelonLoader.csproj index 7d871b751..1cf112fbe 100644 --- a/MelonLoader/MelonLoader.csproj +++ b/MelonLoader/MelonLoader.csproj @@ -33,7 +33,7 @@ - + From e3f72b89629927e483f02bbbdfc99f3c27d2dca9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Mar 2023 08:00:40 +0000 Subject: [PATCH 32/79] Bump AsmResolver.DotNet from 5.0.0 to 5.2.0 Bumps [AsmResolver.DotNet](https://github.com/Washi1337/AsmResolver) from 5.0.0 to 5.2.0. - [Release notes](https://github.com/Washi1337/AsmResolver/releases) - [Commits](https://github.com/Washi1337/AsmResolver/compare/v5.0.0...v5.2.0) --- updated-dependencies: - dependency-name: AsmResolver.DotNet dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- MelonLoader/MelonLoader.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MelonLoader/MelonLoader.csproj b/MelonLoader/MelonLoader.csproj index 1cf112fbe..cd7a94263 100644 --- a/MelonLoader/MelonLoader.csproj +++ b/MelonLoader/MelonLoader.csproj @@ -40,7 +40,7 @@ - + From c85060fa3dbcf2b199b30c6828095078177a1d6a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Apr 2023 22:49:35 +0000 Subject: [PATCH 33/79] Bump Microsoft.Diagnostics.Runtime from 2.2.343001 to 2.4.416101 Bumps [Microsoft.Diagnostics.Runtime](https://github.com/Microsoft/clrmd) from 2.2.343001 to 2.4.416101. - [Release notes](https://github.com/Microsoft/clrmd/releases) - [Changelog](https://github.com/microsoft/clrmd/blob/main/doc/ReleaseNotes2.0.md) - [Commits](https://github.com/Microsoft/clrmd/commits) --- updated-dependencies: - dependency-name: Microsoft.Diagnostics.Runtime dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- MelonLoader/MelonLoader.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MelonLoader/MelonLoader.csproj b/MelonLoader/MelonLoader.csproj index cd7a94263..a8f4531ff 100644 --- a/MelonLoader/MelonLoader.csproj +++ b/MelonLoader/MelonLoader.csproj @@ -1,4 +1,4 @@ - + net35;net6 Latest @@ -41,7 +41,7 @@ - + From 2b2a7babc63fd8667b78ae4a6ea298a9015feccf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Apr 2023 22:49:36 +0000 Subject: [PATCH 34/79] Bump AssetRipper.VersionUtilities from 1.2.1 to 1.4.0 Bumps [AssetRipper.VersionUtilities](https://github.com/AssetRipper/VersionUtilities) from 1.2.1 to 1.4.0. - [Release notes](https://github.com/AssetRipper/VersionUtilities/releases) - [Commits](https://github.com/AssetRipper/VersionUtilities/compare/1.2.1.0...1.4.0.0) --- updated-dependencies: - dependency-name: AssetRipper.VersionUtilities dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .../Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj | 4 ++-- Dependencies/MelonStartScreen/MelonStartScreen.csproj | 4 ++-- Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj | 4 ++-- MelonLoader/MelonLoader.csproj | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Dependencies/Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj b/Dependencies/Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj index 126911f73..f0c2630f2 100644 --- a/Dependencies/Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj +++ b/Dependencies/Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj @@ -1,4 +1,4 @@ - + MelonLoader.Il2CppAssemblyGenerator net6 @@ -20,6 +20,6 @@ - + \ No newline at end of file diff --git a/Dependencies/MelonStartScreen/MelonStartScreen.csproj b/Dependencies/MelonStartScreen/MelonStartScreen.csproj index c3d4ad907..8e363e185 100644 --- a/Dependencies/MelonStartScreen/MelonStartScreen.csproj +++ b/Dependencies/MelonStartScreen/MelonStartScreen.csproj @@ -1,4 +1,4 @@ - + MelonLoader.MelonStartScreen net35;net6 @@ -18,7 +18,7 @@ - + \ No newline at end of file diff --git a/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj b/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj index 290a8adc2..2d305c18d 100644 --- a/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj +++ b/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj @@ -1,4 +1,4 @@ - + MelonLoader.Support net6 @@ -39,7 +39,7 @@ - + diff --git a/MelonLoader/MelonLoader.csproj b/MelonLoader/MelonLoader.csproj index a8f4531ff..6d90b8a6f 100644 --- a/MelonLoader/MelonLoader.csproj +++ b/MelonLoader/MelonLoader.csproj @@ -32,7 +32,7 @@ - + From 965f07745d4cef6241e018fbc3794912dfe6448c Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Tue, 4 Apr 2023 23:56:03 +0100 Subject: [PATCH 35/79] Bump interop --- .../Il2CppAssemblyGenerator.csproj | 10 +++++----- Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Dependencies/Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj b/Dependencies/Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj index f0c2630f2..541cd1ff6 100644 --- a/Dependencies/Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj +++ b/Dependencies/Il2CppAssemblyGenerator/Il2CppAssemblyGenerator.csproj @@ -1,4 +1,4 @@ - + MelonLoader.Il2CppAssemblyGenerator net6 @@ -14,10 +14,10 @@ - - - - + + + + diff --git a/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj b/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj index 2d305c18d..e6d53a997 100644 --- a/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj +++ b/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj @@ -1,4 +1,4 @@ - + MelonLoader.Support net6 @@ -41,9 +41,9 @@ - - - + + + From 1684616919944e0c0ea512b14b58afdbc85e7781 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Wed, 5 Apr 2023 02:29:39 +0200 Subject: [PATCH 36/79] fix lock funnies --- Bootstrap/src/base_assembly/mono.rs | 14 +++++++------- Bootstrap/src/icalls/resolve_internals.rs | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Bootstrap/src/base_assembly/mono.rs b/Bootstrap/src/base_assembly/mono.rs index a3a732724..1a01b0ea7 100644 --- a/Bootstrap/src/base_assembly/mono.rs +++ b/Bootstrap/src/base_assembly/mono.rs @@ -1,4 +1,4 @@ -use std::{ptr::null_mut, sync::Mutex}; +use std::{ptr::null_mut, sync::{RwLock, Mutex}}; use lazy_static::lazy_static; use unity_rs::{ @@ -12,10 +12,10 @@ lazy_static! { pub static ref MONO_PRESTART: Mutex = Mutex::new(UnityMethod { inner: null_mut() }); pub static ref MONO_START: Mutex = Mutex::new(UnityMethod { inner: null_mut() }); - pub static ref ASSEMBLYMANAGER_RESOLVE: Mutex = - Mutex::new(UnityMethod { inner: null_mut() }); - pub static ref ASSEMBLYMANAGER_LOADINFO: Mutex = - Mutex::new(UnityMethod { inner: null_mut() }); + pub static ref ASSEMBLYMANAGER_RESOLVE: RwLock = + RwLock::new(UnityMethod { inner: null_mut() }); + pub static ref ASSEMBLYMANAGER_LOADINFO: RwLock = + RwLock::new(UnityMethod { inner: null_mut() }); } pub fn init(runtime: &FerrexRuntime) -> Result<(), DynErr> { @@ -52,8 +52,8 @@ pub fn init(runtime: &FerrexRuntime) -> Result<(), DynErr> { //store the methods for later, in a thread safe global static. *MONO_PRESTART.try_lock()? = prestart_method; *MONO_START.try_lock()? = start_method; - *ASSEMBLYMANAGER_RESOLVE.try_lock()? = resolve_method; - *ASSEMBLYMANAGER_LOADINFO.try_lock()? = loadinfo_method; + *ASSEMBLYMANAGER_RESOLVE.try_write()? = resolve_method; + *ASSEMBLYMANAGER_LOADINFO.try_write()? = loadinfo_method; //invoke the MelonLoader initialize method. let _ = initialize_method.invoke(None, None, runtime)?; diff --git a/Bootstrap/src/icalls/resolve_internals.rs b/Bootstrap/src/icalls/resolve_internals.rs index fc6a4046c..386fbd832 100644 --- a/Bootstrap/src/icalls/resolve_internals.rs +++ b/Bootstrap/src/icalls/resolve_internals.rs @@ -33,7 +33,7 @@ fn assembly_resolve( ) -> Result<*mut MonoAssembly, DynErr> { let runtime = runtime!()?; - let resolve_method = base_assembly::mono::ASSEMBLYMANAGER_RESOLVE.try_lock()?; + let resolve_method = base_assembly::mono::ASSEMBLYMANAGER_RESOLVE.try_read()?; if resolve_method.inner.is_null() { return Err("AssemblyManager.Resolve is null".into()); @@ -90,7 +90,7 @@ fn load_hook_inner(assembly: *mut MonoAssembly) -> Result<(), DynErr> { return Ok(()); } - let load_method = base_assembly::mono::ASSEMBLYMANAGER_LOADINFO.try_lock()?; + let load_method = base_assembly::mono::ASSEMBLYMANAGER_LOADINFO.try_read()?; if load_method.inner.is_null() { return Ok(()); } From 0606eb91f01f1a85c37733ade58d3fea66ef7e43 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Wed, 5 Apr 2023 14:22:40 +0200 Subject: [PATCH 37/79] make args optional --- Bootstrap/src/melonenv/args.rs | 16 ++++++++-------- Bootstrap/src/melonenv/macros.rs | 8 ++++---- Proxy/src/core.rs | 6 +++--- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Bootstrap/src/melonenv/args.rs b/Bootstrap/src/melonenv/args.rs index a78d772c0..a045e01ef 100644 --- a/Bootstrap/src/melonenv/args.rs +++ b/Bootstrap/src/melonenv/args.rs @@ -6,17 +6,17 @@ use crate::{internal_failure}; #[derive(Debug, Parser)] #[command(author, version, about, long_about = None)] pub struct Cli { - #[arg(long = "melonloader.debug", default_value = "false")] - pub debug: bool, + #[arg(long = "melonloader.debug")] + pub debug: Option, - #[arg(long = "melonloader.consoledst", default_value = "false")] - pub console_dst: bool, + #[arg(long = "melonloader.consoledst")] + pub console_dst: Option, - #[arg(long = "melonloader.consoleontop", default_value = "false")] - pub console_on_top: bool, + #[arg(long = "melonloader.consoleontop")] + pub console_on_top: Option, - #[arg(long = "melonloader.hideconsole", default_value = "false")] - pub hide_console: bool, + #[arg(long = "melonloader.hideconsole")] + pub hide_console: Option, #[arg(long = "melonloader.basedir")] pub base_dir: Option, diff --git a/Bootstrap/src/melonenv/macros.rs b/Bootstrap/src/melonenv/macros.rs index 20e8e3e45..1975a0292 100644 --- a/Bootstrap/src/melonenv/macros.rs +++ b/Bootstrap/src/melonenv/macros.rs @@ -4,7 +4,7 @@ macro_rules! debug_enabled { if cfg!(debug_assertions) { true } else { - $crate::melonenv::args::ARGS.debug + $crate::melonenv::args::ARGS.debug.is_some_and(|b| b) } }; } @@ -12,20 +12,20 @@ macro_rules! debug_enabled { #[macro_export] macro_rules! should_set_title { () => { - !$crate::melonenv::args::ARGS.console_dst + !$crate::melonenv::args::ARGS.console_dst.is_some_and(|b| b) }; } #[macro_export] macro_rules! console_on_top { () => { - $crate::melonenv::args::ARGS.console_on_top + $crate::melonenv::args::ARGS.console_on_top.is_some_and(|b| b) }; } #[macro_export] macro_rules! hide_console { () => { - $crate::melonenv::args::ARGS.hide_console + $crate::melonenv::args::ARGS.hide_console.is_some_and(|b| b) }; } \ No newline at end of file diff --git a/Proxy/src/core.rs b/Proxy/src/core.rs index dab8040b6..eb755fee1 100644 --- a/Proxy/src/core.rs +++ b/Proxy/src/core.rs @@ -8,8 +8,8 @@ use std::{error, path::PathBuf, sync::Mutex}; #[derive(Parser)] struct Arguments { - #[arg(long = "no-mods", default_value = "false")] - no_mods: bool, + #[arg(long = "no-mods")] + no_mods: Option, #[arg(long = "melonloader.basedir")] base_dir: Option, @@ -30,7 +30,7 @@ pub fn init() -> Result<(), Box> { //return Ok, and silently stop loading MelonLoader, if the user has specified to not load mods, //or if the game is not a Unity game - if args.no_mods { + if args.no_mods.is_some_and(|b| b) { return Ok(()); } From 12b9e5801aad5021b04b50f7e84beca32c061c65 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Wed, 5 Apr 2023 02:29:39 +0200 Subject: [PATCH 38/79] fix lock funnies --- Bootstrap/src/base_assembly/mono.rs | 14 +++++++------- Bootstrap/src/icalls/resolve_internals.rs | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Bootstrap/src/base_assembly/mono.rs b/Bootstrap/src/base_assembly/mono.rs index a3a732724..1a01b0ea7 100644 --- a/Bootstrap/src/base_assembly/mono.rs +++ b/Bootstrap/src/base_assembly/mono.rs @@ -1,4 +1,4 @@ -use std::{ptr::null_mut, sync::Mutex}; +use std::{ptr::null_mut, sync::{RwLock, Mutex}}; use lazy_static::lazy_static; use unity_rs::{ @@ -12,10 +12,10 @@ lazy_static! { pub static ref MONO_PRESTART: Mutex = Mutex::new(UnityMethod { inner: null_mut() }); pub static ref MONO_START: Mutex = Mutex::new(UnityMethod { inner: null_mut() }); - pub static ref ASSEMBLYMANAGER_RESOLVE: Mutex = - Mutex::new(UnityMethod { inner: null_mut() }); - pub static ref ASSEMBLYMANAGER_LOADINFO: Mutex = - Mutex::new(UnityMethod { inner: null_mut() }); + pub static ref ASSEMBLYMANAGER_RESOLVE: RwLock = + RwLock::new(UnityMethod { inner: null_mut() }); + pub static ref ASSEMBLYMANAGER_LOADINFO: RwLock = + RwLock::new(UnityMethod { inner: null_mut() }); } pub fn init(runtime: &FerrexRuntime) -> Result<(), DynErr> { @@ -52,8 +52,8 @@ pub fn init(runtime: &FerrexRuntime) -> Result<(), DynErr> { //store the methods for later, in a thread safe global static. *MONO_PRESTART.try_lock()? = prestart_method; *MONO_START.try_lock()? = start_method; - *ASSEMBLYMANAGER_RESOLVE.try_lock()? = resolve_method; - *ASSEMBLYMANAGER_LOADINFO.try_lock()? = loadinfo_method; + *ASSEMBLYMANAGER_RESOLVE.try_write()? = resolve_method; + *ASSEMBLYMANAGER_LOADINFO.try_write()? = loadinfo_method; //invoke the MelonLoader initialize method. let _ = initialize_method.invoke(None, None, runtime)?; diff --git a/Bootstrap/src/icalls/resolve_internals.rs b/Bootstrap/src/icalls/resolve_internals.rs index fc6a4046c..386fbd832 100644 --- a/Bootstrap/src/icalls/resolve_internals.rs +++ b/Bootstrap/src/icalls/resolve_internals.rs @@ -33,7 +33,7 @@ fn assembly_resolve( ) -> Result<*mut MonoAssembly, DynErr> { let runtime = runtime!()?; - let resolve_method = base_assembly::mono::ASSEMBLYMANAGER_RESOLVE.try_lock()?; + let resolve_method = base_assembly::mono::ASSEMBLYMANAGER_RESOLVE.try_read()?; if resolve_method.inner.is_null() { return Err("AssemblyManager.Resolve is null".into()); @@ -90,7 +90,7 @@ fn load_hook_inner(assembly: *mut MonoAssembly) -> Result<(), DynErr> { return Ok(()); } - let load_method = base_assembly::mono::ASSEMBLYMANAGER_LOADINFO.try_lock()?; + let load_method = base_assembly::mono::ASSEMBLYMANAGER_LOADINFO.try_read()?; if load_method.inner.is_null() { return Ok(()); } From 2f2627ee2034cb6b5ecfddb13c7fd031a3d744ad Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Wed, 5 Apr 2023 14:22:40 +0200 Subject: [PATCH 39/79] make args optional --- Bootstrap/src/melonenv/args.rs | 16 ++++++++-------- Bootstrap/src/melonenv/macros.rs | 8 ++++---- Proxy/src/core.rs | 6 +++--- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Bootstrap/src/melonenv/args.rs b/Bootstrap/src/melonenv/args.rs index a78d772c0..a045e01ef 100644 --- a/Bootstrap/src/melonenv/args.rs +++ b/Bootstrap/src/melonenv/args.rs @@ -6,17 +6,17 @@ use crate::{internal_failure}; #[derive(Debug, Parser)] #[command(author, version, about, long_about = None)] pub struct Cli { - #[arg(long = "melonloader.debug", default_value = "false")] - pub debug: bool, + #[arg(long = "melonloader.debug")] + pub debug: Option, - #[arg(long = "melonloader.consoledst", default_value = "false")] - pub console_dst: bool, + #[arg(long = "melonloader.consoledst")] + pub console_dst: Option, - #[arg(long = "melonloader.consoleontop", default_value = "false")] - pub console_on_top: bool, + #[arg(long = "melonloader.consoleontop")] + pub console_on_top: Option, - #[arg(long = "melonloader.hideconsole", default_value = "false")] - pub hide_console: bool, + #[arg(long = "melonloader.hideconsole")] + pub hide_console: Option, #[arg(long = "melonloader.basedir")] pub base_dir: Option, diff --git a/Bootstrap/src/melonenv/macros.rs b/Bootstrap/src/melonenv/macros.rs index 20e8e3e45..1975a0292 100644 --- a/Bootstrap/src/melonenv/macros.rs +++ b/Bootstrap/src/melonenv/macros.rs @@ -4,7 +4,7 @@ macro_rules! debug_enabled { if cfg!(debug_assertions) { true } else { - $crate::melonenv::args::ARGS.debug + $crate::melonenv::args::ARGS.debug.is_some_and(|b| b) } }; } @@ -12,20 +12,20 @@ macro_rules! debug_enabled { #[macro_export] macro_rules! should_set_title { () => { - !$crate::melonenv::args::ARGS.console_dst + !$crate::melonenv::args::ARGS.console_dst.is_some_and(|b| b) }; } #[macro_export] macro_rules! console_on_top { () => { - $crate::melonenv::args::ARGS.console_on_top + $crate::melonenv::args::ARGS.console_on_top.is_some_and(|b| b) }; } #[macro_export] macro_rules! hide_console { () => { - $crate::melonenv::args::ARGS.hide_console + $crate::melonenv::args::ARGS.hide_console.is_some_and(|b| b) }; } \ No newline at end of file diff --git a/Proxy/src/core.rs b/Proxy/src/core.rs index dab8040b6..eb755fee1 100644 --- a/Proxy/src/core.rs +++ b/Proxy/src/core.rs @@ -8,8 +8,8 @@ use std::{error, path::PathBuf, sync::Mutex}; #[derive(Parser)] struct Arguments { - #[arg(long = "no-mods", default_value = "false")] - no_mods: bool, + #[arg(long = "no-mods")] + no_mods: Option, #[arg(long = "melonloader.basedir")] base_dir: Option, @@ -30,7 +30,7 @@ pub fn init() -> Result<(), Box> { //return Ok, and silently stop loading MelonLoader, if the user has specified to not load mods, //or if the game is not a Unity game - if args.no_mods { + if args.no_mods.is_some_and(|b| b) { return Ok(()); } From b336d289605bb30621a7f5f8a8da9f12d2bc0985 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Tue, 11 Apr 2023 03:42:39 +0200 Subject: [PATCH 40/79] remove is_some_and feature flag --- Bootstrap/src/lib.rs | 2 -- Proxy/src/lib.rs | 2 -- 2 files changed, 4 deletions(-) diff --git a/Bootstrap/src/lib.rs b/Bootstrap/src/lib.rs index b7f81a541..3a8e8a57f 100755 --- a/Bootstrap/src/lib.rs +++ b/Bootstrap/src/lib.rs @@ -1,5 +1,3 @@ -#![feature(is_some_and)] - #![allow(non_snake_case)] #![deny( missing_debug_implementations, diff --git a/Proxy/src/lib.rs b/Proxy/src/lib.rs index c373203ec..a1fcba04d 100644 --- a/Proxy/src/lib.rs +++ b/Proxy/src/lib.rs @@ -1,7 +1,5 @@ //! Cross platform reimplementation of MelonLoader's Proxy in rust -#![feature(is_some_and)] - #![deny( missing_debug_implementations, unused_results, From 1a4ef2304fa76c5d1c8ed4a52559dd12d2c9b7c3 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Wed, 12 Apr 2023 01:03:01 +0200 Subject: [PATCH 41/79] EOS Compatibility Layer --- .../CompatibilityLayers/EOS/EOS.csproj | 16 +++++++ .../CompatibilityLayers/EOS/Module.cs | 48 +++++++++++++++++++ MelonLoader.sln | 15 ++++++ .../MelonCompatibilityLayer.cs | 2 + 4 files changed, 81 insertions(+) create mode 100644 Dependencies/CompatibilityLayers/EOS/EOS.csproj create mode 100644 Dependencies/CompatibilityLayers/EOS/Module.cs diff --git a/Dependencies/CompatibilityLayers/EOS/EOS.csproj b/Dependencies/CompatibilityLayers/EOS/EOS.csproj new file mode 100644 index 000000000..cc4d68bae --- /dev/null +++ b/Dependencies/CompatibilityLayers/EOS/EOS.csproj @@ -0,0 +1,16 @@ + + + MelonLoader.CompatibilityLayers + net35 + Latest + true + false + false + $(SolutionDir)Output\$(Configuration)\MelonLoader\Dependencies\CompatibilityLayers\ + true + embedded + + + + + \ No newline at end of file diff --git a/Dependencies/CompatibilityLayers/EOS/Module.cs b/Dependencies/CompatibilityLayers/EOS/Module.cs new file mode 100644 index 000000000..280b94730 --- /dev/null +++ b/Dependencies/CompatibilityLayers/EOS/Module.cs @@ -0,0 +1,48 @@ +using MelonLoader.Modules; +using MelonLoader.NativeUtils; +using System; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle(MelonLoader.BuildInfo.Description)] +[assembly: AssemblyDescription(MelonLoader.BuildInfo.Description)] +[assembly: AssemblyCompany(MelonLoader.BuildInfo.Company)] +[assembly: AssemblyProduct(MelonLoader.BuildInfo.Name)] +[assembly: AssemblyCopyright("Created by " + MelonLoader.BuildInfo.Author)] +[assembly: AssemblyTrademark(MelonLoader.BuildInfo.Company)] +[assembly: Guid("5100810A-9842-4073-9658-E5841FDF9D73")] +[assembly: AssemblyVersion(MelonLoader.BuildInfo.Version)] +[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)] +[assembly: MelonLoader.PatchShield] + +namespace MelonLoader.CompatibilityLayers +{ + internal class EOS_Module : MelonModule + { + [DllImport("kernel32", CharSet = CharSet.Unicode)] + static extern IntPtr LoadLibrary(string lpFileName); + + [UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Unicode)] + private delegate IntPtr LoadLibraryWDetour(string path); + private NativeHook _hook = new NativeHook(); + public override void OnInitialize() + { + var lib = new NativeLibrary(LoadLibrary("kernel32.dll")); + var func = lib.GetExport("LoadLibraryW"); + + var detour = Marshal.GetFunctionPointerForDelegate((LoadLibraryWDetour)Detour); + + _hook = new NativeHook(func, detour); + _hook.Attach(); + } + + private IntPtr Detour(string path) + { + if (path.EndsWith("EOSOVH-Win64-Shipping.dll") || path.EndsWith("EOSOVH-Win32-Shipping.dll")) + return IntPtr.Zero; + + return _hook.Trampoline(path); + } + } +} diff --git a/MelonLoader.sln b/MelonLoader.sln index a432ca006..7b720c2fd 100644 --- a/MelonLoader.sln +++ b/MelonLoader.sln @@ -59,6 +59,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MelonLoader.NativeHost", "M EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Preload", "Dependencies\SupportModules\Preload\Preload.csproj", "{81172B93-0ADE-47A5-9CC4-9833676C2D38}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EOS", "Dependencies\CompatibilityLayers\EOS\EOS.csproj", "{DB9D4AFC-E266-42C8-909A-21C14EEB8A5B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -201,6 +203,18 @@ Global {81172B93-0ADE-47A5-9CC4-9833676C2D38}.Release|Windows - x64.Build.0 = Release|Any CPU {81172B93-0ADE-47A5-9CC4-9833676C2D38}.Release|Windows - x86.ActiveCfg = Release|Any CPU {81172B93-0ADE-47A5-9CC4-9833676C2D38}.Release|Windows - x86.Build.0 = Release|Any CPU + {DB9D4AFC-E266-42C8-909A-21C14EEB8A5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DB9D4AFC-E266-42C8-909A-21C14EEB8A5B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DB9D4AFC-E266-42C8-909A-21C14EEB8A5B}.Debug|Windows - x64.ActiveCfg = Debug|Any CPU + {DB9D4AFC-E266-42C8-909A-21C14EEB8A5B}.Debug|Windows - x64.Build.0 = Debug|Any CPU + {DB9D4AFC-E266-42C8-909A-21C14EEB8A5B}.Debug|Windows - x86.ActiveCfg = Debug|Any CPU + {DB9D4AFC-E266-42C8-909A-21C14EEB8A5B}.Debug|Windows - x86.Build.0 = Debug|Any CPU + {DB9D4AFC-E266-42C8-909A-21C14EEB8A5B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DB9D4AFC-E266-42C8-909A-21C14EEB8A5B}.Release|Any CPU.Build.0 = Release|Any CPU + {DB9D4AFC-E266-42C8-909A-21C14EEB8A5B}.Release|Windows - x64.ActiveCfg = Release|Any CPU + {DB9D4AFC-E266-42C8-909A-21C14EEB8A5B}.Release|Windows - x64.Build.0 = Release|Any CPU + {DB9D4AFC-E266-42C8-909A-21C14EEB8A5B}.Release|Windows - x86.ActiveCfg = Release|Any CPU + {DB9D4AFC-E266-42C8-909A-21C14EEB8A5B}.Release|Windows - x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -217,6 +231,7 @@ Global {3AF58371-2E0A-4256-B3A3-C100DB07E599} = {AB4D471C-9AB2-4A5F-93E1-8929E436C121} {1DB3679C-DCCA-492D-A725-75604A379C7A} = {AB4D471C-9AB2-4A5F-93E1-8929E436C121} {81172B93-0ADE-47A5-9CC4-9833676C2D38} = {8D8A18CB-7319-4220-BED8-6B3E23E6C19F} + {DB9D4AFC-E266-42C8-909A-21C14EEB8A5B} = {AB4D471C-9AB2-4A5F-93E1-8929E436C121} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {4AB93B1D-1C52-4A80-809D-C28770140E0A} diff --git a/MelonLoader/CompatibilityLayers/MelonCompatibilityLayer.cs b/MelonLoader/CompatibilityLayers/MelonCompatibilityLayer.cs index 00c1941bc..72c8b81b6 100644 --- a/MelonLoader/CompatibilityLayers/MelonCompatibilityLayer.cs +++ b/MelonLoader/CompatibilityLayers/MelonCompatibilityLayer.cs @@ -1,6 +1,7 @@ using MelonLoader.Modules; using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; namespace MelonLoader @@ -16,6 +17,7 @@ public static class MelonCompatibilityLayer // Illusion Plugin Architecture new MelonModule.Info(Path.Combine(baseDirectory, "IPA.dll"), MelonUtils.IsGameIl2Cpp), + new MelonModule.Info(Path.Combine(baseDirectory, "EOS.dll"), () => !MelonUtils.IsWindows) }; private static void CheckGameLayerWithPlatform(string name, Func shouldBeIgnored) From 66e7dd54b1d12e7c57eb18df7b9cd8de7d8672ae Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Wed, 12 Apr 2023 01:04:23 +0200 Subject: [PATCH 42/79] Detach after preventing EOS Overlay from loading --- Dependencies/CompatibilityLayers/EOS/Module.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Dependencies/CompatibilityLayers/EOS/Module.cs b/Dependencies/CompatibilityLayers/EOS/Module.cs index 280b94730..9b50efb5f 100644 --- a/Dependencies/CompatibilityLayers/EOS/Module.cs +++ b/Dependencies/CompatibilityLayers/EOS/Module.cs @@ -40,7 +40,10 @@ public override void OnInitialize() private IntPtr Detour(string path) { if (path.EndsWith("EOSOVH-Win64-Shipping.dll") || path.EndsWith("EOSOVH-Win32-Shipping.dll")) + { + _hook.Detach(); return IntPtr.Zero; + } return _hook.Trampoline(path); } From ed984c39bea2a389040ec699cea5a464c9d048cf Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Wed, 12 Apr 2023 01:06:43 +0200 Subject: [PATCH 43/79] Set trampoline handle to null after unhooking --- MelonLoader/NativeUtils/NativeHooks.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/MelonLoader/NativeUtils/NativeHooks.cs b/MelonLoader/NativeUtils/NativeHooks.cs index 4c72e6cad..4b4a4cd81 100644 --- a/MelonLoader/NativeUtils/NativeHooks.cs +++ b/MelonLoader/NativeUtils/NativeHooks.cs @@ -107,6 +107,7 @@ public unsafe void Detach() BootstrapInterop.NativeHookDetach((IntPtr)(&original), _detourHandle); IsHooked= false; + _trampoline = null; _trampolineHandle = IntPtr.Zero; } } From 34797d1fcc02fed8a366073d6b2ab3769b9c5f87 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Wed, 12 Apr 2023 02:15:14 +0200 Subject: [PATCH 44/79] unhook on module destruction --- Dependencies/CompatibilityLayers/EOS/Module.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Dependencies/CompatibilityLayers/EOS/Module.cs b/Dependencies/CompatibilityLayers/EOS/Module.cs index 9b50efb5f..fe51e5e4b 100644 --- a/Dependencies/CompatibilityLayers/EOS/Module.cs +++ b/Dependencies/CompatibilityLayers/EOS/Module.cs @@ -23,9 +23,8 @@ internal class EOS_Module : MelonModule [DllImport("kernel32", CharSet = CharSet.Unicode)] static extern IntPtr LoadLibrary(string lpFileName); - [UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Unicode)] - private delegate IntPtr LoadLibraryWDetour(string path); - private NativeHook _hook = new NativeHook(); + internal delegate IntPtr LoadLibraryWDetour(IntPtr path); + internal static NativeHook _hook = new NativeHook(); public override void OnInitialize() { var lib = new NativeLibrary(LoadLibrary("kernel32.dll")); @@ -37,9 +36,10 @@ public override void OnInitialize() _hook.Attach(); } - private IntPtr Detour(string path) + private IntPtr Detour(IntPtr path) { - if (path.EndsWith("EOSOVH-Win64-Shipping.dll") || path.EndsWith("EOSOVH-Win32-Shipping.dll")) + var pathString = Marshal.PtrToStringUni(path); + if (pathString.EndsWith("EOSOVH-Win64-Shipping.dll") || pathString.EndsWith("EOSOVH-Win32-Shipping.dll")) { _hook.Detach(); return IntPtr.Zero; @@ -47,5 +47,10 @@ private IntPtr Detour(string path) return _hook.Trampoline(path); } + + ~EOS_Module() + { + _hook.Detach(); + } } } From a353065d735a2f551a2c0930977ac54539391c9d Mon Sep 17 00:00:00 2001 From: Sarah Date: Wed, 17 May 2023 07:15:16 +0200 Subject: [PATCH 45/79] Override Interop Databases location for XREF --- MelonLoader/Core.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/MelonLoader/Core.cs b/MelonLoader/Core.cs index 8b2a4d1d3..67f5d6073 100644 --- a/MelonLoader/Core.cs +++ b/MelonLoader/Core.cs @@ -42,6 +42,10 @@ internal static int Initialize() MelonEnvironment.MelonLoaderDirectory = runtimeDirInfo.Parent!.FullName; MelonEnvironment.GameRootDirectory = Path.GetDirectoryName(MelonEnvironment.GameExecutablePath); +#if NET6_0 + Environment.SetEnvironmentVariable("IL2CPP_INTEROP_DATABASES_LOCATION", MelonEnvironment.Il2CppAssembliesDirectory); +#endif + SetupWineCheck(); Utils.MelonConsole.Init(); From 145b2efce33bee82d08b1a3bb71931a46300b053 Mon Sep 17 00:00:00 2001 From: Kyler Li <46853114+loukylor@users.noreply.github.com> Date: Sun, 25 Jun 2023 17:48:36 -0700 Subject: [PATCH 46/79] Fix melon load order --- MelonLoader/Melons/MelonBase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MelonLoader/Melons/MelonBase.cs b/MelonLoader/Melons/MelonBase.cs index 611921486..94d77a073 100644 --- a/MelonLoader/Melons/MelonBase.cs +++ b/MelonLoader/Melons/MelonBase.cs @@ -65,7 +65,7 @@ public static void RegisterSorted(IEnumerable melons) where T : MelonBase var collection = melons.ToList(); SortMelons(ref collection); - foreach (var m in melons) + foreach (var m in collection) m.Register(); } @@ -656,4 +656,4 @@ public enum Incompatibility Platform } } -} \ No newline at end of file +} From ee7578d21d4e72ee4c93847db551db73ddf4561f Mon Sep 17 00:00:00 2001 From: Sarah Date: Mon, 14 Aug 2023 23:40:07 +0200 Subject: [PATCH 47/79] update crates --- Cargo.lock | 933 +++++++++++++++++++++++++++++------------------------ 1 file changed, 507 insertions(+), 426 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a571eb0f1..3e25b485b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,7 +18,16 @@ dependencies = [ "netcorehost", "thiserror", "unity-rs", - "windows", + "windows 0.46.0", +] + +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", ] [[package]] @@ -29,16 +38,21 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aes" -version = "0.7.5" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" dependencies = [ "cfg-if", "cipher", "cpufeatures", - "opaque-debug", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -50,9 +64,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.68" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" [[package]] name = "atk" @@ -61,7 +75,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c3d816ce6f0e2909a96830d6911c2aff044370b1ef92d7f267b43bae5addedd" dependencies = [ "atk-sys", - "bitflags", + "bitflags 1.3.2", "glib", "libc", ] @@ -78,28 +92,32 @@ dependencies = [ "system-deps", ] -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" -version = "0.21.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" [[package]] name = "base64ct" @@ -113,6 +131,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + [[package]] name = "block" version = "0.1.6" @@ -136,9 +160,9 @@ checksum = "832133bbabbbaa9fbdba793456a2827627a7d2b8fb96032fa1e7666d7895832b" [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "byteorder" @@ -179,7 +203,7 @@ version = "0.15.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c76ee391b03d35510d9fa917357c7f1855bd9a6659c95a1b392e33f49b3369bc" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cairo-sys-rs", "glib", "libc", @@ -205,20 +229,22 @@ checksum = "1582e1c9e755dd6ad6b224dcffb135d199399a4568d454bd89fe515ca8425695" [[package]] name = "cc" -version = "1.0.78" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" +checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01" dependencies = [ "jobserver", + "libc", ] [[package]] name = "cfg-expr" -version = "0.11.0" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0357a6402b295ca3a86bc148e84df46c02e41f41fef186bda662557ef6328aa" +checksum = "b40ccee03b5175c18cde8f37e7d2a33bcef6f8ec8f7cc0d81090d1bb380949c9" dependencies = [ "smallvec", + "target-lexicon", ] [[package]] @@ -229,13 +255,13 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" dependencies = [ + "android-tzdata", "iana-time-zone", "js-sys", - "num-integer", "num-traits", "time 0.1.45", "wasm-bindgen", @@ -244,11 +270,12 @@ dependencies = [ [[package]] name = "cipher" -version = "0.3.0" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "generic-array", + "crypto-common", + "inout", ] [[package]] @@ -256,7 +283,7 @@ name = "clap" version = "4.1.8" source = "git+https://github.com/RinLovesYou/clap#f959816de9a07eaf9867f0419a1484353e0629f6" dependencies = [ - "bitflags", + "bitflags 1.3.2", "clap_derive", "clap_lex", "is-terminal", @@ -274,7 +301,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -291,7 +318,7 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" dependencies = [ - "bitflags", + "bitflags 1.3.2", "block", "cocoa-foundation", "core-foundation", @@ -303,11 +330,11 @@ dependencies = [ [[package]] name = "cocoa-foundation" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" +checksum = "931d3837c286f56e3c58423ce4eba12d08db2374461a785c86f672b08b5650d6" dependencies = [ - "bitflags", + "bitflags 1.3.2", "block", "core-foundation", "core-graphics-types", @@ -316,25 +343,15 @@ dependencies = [ "objc", ] -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - [[package]] name = "colored" -version = "2.0.0" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" +checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" dependencies = [ - "atty", + "is-terminal", "lazy_static", - "winapi", + "windows-sys", ] [[package]] @@ -355,9 +372,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "core-graphics" @@ -365,7 +382,7 @@ version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-graphics-types", "foreign-types", @@ -374,13 +391,12 @@ dependencies = [ [[package]] name = "core-graphics-types" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" +checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", - "foreign-types", "libc", ] @@ -395,9 +411,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] @@ -413,9 +429,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.15" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] @@ -447,7 +463,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -457,48 +473,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" [[package]] -name = "cxx" -version = "1.0.92" +name = "deranged" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a140f260e6f3f79013b8bfc65e7ce630c9ab4388c6a89c71e07226f49487b72" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da6383f459341ea689374bf0a42979739dc421874f112ff26f829b8040b8e613" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90201c1a650e95ccff1c8c0bb5a343213bdd317c6e600a93075bca2eff54ec97" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b75aed41bb2e6367cae39e6326ef817a851db13c13e4f3263714ca3cfb8de56" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "7684a49fb1af197853ef7b2ee694bc1f5b4179556f1e5710e1760c5db6f5e929" [[package]] name = "derive_more" @@ -508,7 +486,7 @@ checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -528,14 +506,14 @@ checksum = "802d3d884d31343822c33802e75133a845322231ab334ffada47857376f7f9a2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", @@ -562,7 +540,7 @@ checksum = "3a09ac8bb8c16a282264c379dffba707b9c998afc7506009137f3c6136888078" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -588,15 +566,21 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" -version = "0.2.8" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" dependencies = [ "errno-dragonfly", "libc", - "winapi", + "windows-sys", ] [[package]] @@ -617,9 +601,9 @@ checksum = "ec54ac60a7f2ee9a97cad9946f9bf629a3bc6a7ae59e68983dc9318f5a54b81a" [[package]] name = "field-offset" -version = "0.3.4" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e1c54951450cbd39f3dbcf1005ac413b49487dabf18a720ad2383eccfeffb92" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" dependencies = [ "memoffset", "rustc_version", @@ -627,9 +611,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" dependencies = [ "crc32fast", "miniz_oxide", @@ -658,33 +642,33 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] [[package]] name = "futures-channel" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", @@ -693,30 +677,42 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.28", +] [[package]] name = "futures-sink" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-core", "futures-io", + "futures-macro", "futures-task", "memchr", "pin-project-lite", @@ -730,7 +726,7 @@ version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6e05c1f572ab0e1f15be94217f0dc29088c248b14f792a5ff0af0d84bcda9e8" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cairo-rs", "gdk-pixbuf", "gdk-sys", @@ -746,7 +742,7 @@ version = "0.15.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad38dd9cc8b099cceecdf41375bb6d481b1b5a7cd5cd603e10a69a9383f8619a" dependencies = [ - "bitflags", + "bitflags 1.3.2", "gdk-pixbuf-sys", "gio", "glib", @@ -785,21 +781,27 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", ] +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + [[package]] name = "gio" version = "0.15.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68fdbc90312d462781a395f7a16d96a2b379bb6ef8cd6310a2df272771c4283b" dependencies = [ - "bitflags", + "bitflags 1.3.2", "futures-channel", "futures-core", "futures-io", @@ -829,7 +831,7 @@ version = "0.15.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edb0306fbad0ab5428b0ca674a23893db909a98582969c9b537be4ced78c505d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "futures-channel", "futures-core", "futures-executor", @@ -845,9 +847,9 @@ dependencies = [ [[package]] name = "glib-macros" -version = "0.15.11" +version = "0.15.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25a68131a662b04931e71891fb14aaf65ee4b44d08e8abc10f49e77418c86c64" +checksum = "10c6ae9f6fa26f4fb2ac16b528d138d971ead56141de489f8111e259b9df3c4a" dependencies = [ "anyhow", "heck", @@ -855,7 +857,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -886,7 +888,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92e3004a2d5d6d8b5057d2b57b3712c9529b62e82c77f25c1fecde1fd5c23bd0" dependencies = [ "atk", - "bitflags", + "bitflags 1.3.2", "cairo-rs", "field-offset", "futures-channel", @@ -922,23 +924,23 @@ dependencies = [ [[package]] name = "gtk3-macros" -version = "0.15.4" +version = "0.15.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24f518afe90c23fba585b2d7697856f9e6a7bbc62f65588035e66f6afb01a2e9" +checksum = "684c0456c086e8e7e9af73ec5b84e35938df394712054550e81558d21c44ab0d" dependencies = [ "anyhow", "proc-macro-crate", "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "h2" -version = "0.3.16" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" dependencies = [ "bytes", "fnv", @@ -946,7 +948,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -960,28 +962,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] -name = "heck" -version = "0.4.0" +name = "hashbrown" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" [[package]] -name = "hermit-abi" -version = "0.1.19" +name = "heck" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.2.6" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "hmac" @@ -1032,15 +1028,15 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.25" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -1053,7 +1049,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -1062,10 +1058,11 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.23.2" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" dependencies = [ + "futures-util", "http", "hyper", "rustls", @@ -1075,33 +1072,32 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.53" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "winapi", + "windows 0.48.0", ] [[package]] name = "iana-time-zone-haiku" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "cxx", - "cxx-build", + "cc", ] [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -1109,47 +1105,55 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", ] [[package]] -name = "io-lifetimes" -version = "1.0.4" +name = "indexmap" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" dependencies = [ - "libc", - "windows-sys 0.42.0", + "equivalent", + "hashbrown 0.14.0", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", ] [[package]] name = "ipnet" -version = "2.7.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "is-terminal" -version = "0.4.2" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ - "hermit-abi 0.2.6", - "io-lifetimes", + "hermit-abi", "rustix", - "windows-sys 0.42.0", + "windows-sys", ] [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "jobserver" @@ -1162,9 +1166,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -1177,9 +1181,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.140" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "libc-stdhandle" @@ -1202,28 +1206,26 @@ dependencies = [ ] [[package]] -name = "link-cplusplus" -version = "1.0.8" +name = "libloading" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb" dependencies = [ - "cc", + "cfg-if", + "windows-sys", ] [[package]] name = "linux-raw-sys" -version = "0.1.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" [[package]] name = "log" -version = "0.4.17" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "malloc_buf" @@ -1242,38 +1244,37 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memoffset" -version = "0.6.5" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ "autocfg", ] [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -1319,38 +1320,28 @@ dependencies = [ "cargo-emit", "coreclr-hosting-shared", "reqwest", - "semver 1.0.17", + "semver", "serde", "serde_json", "zip", ] -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi", "libc", ] @@ -1371,7 +1362,7 @@ checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1384,22 +1375,25 @@ dependencies = [ ] [[package]] -name = "once_cell" -version = "1.17.0" +name = "object" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] [[package]] -name = "opaque-debug" -version = "0.3.0" +name = "once_cell" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "os_str_bytes" -version = "6.4.1" +version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" +checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" [[package]] name = "pango" @@ -1407,7 +1401,7 @@ version = "0.15.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22e4045548659aee5313bde6c582b0d83a627b7904dd20dc2d9ef0895d414e4f" dependencies = [ - "bitflags", + "bitflags 1.3.2", "glib", "libc", "once_cell", @@ -1451,25 +1445,15 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" - -[[package]] -name = "pest" -version = "2.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f6e86fb9e7026527a0d46bc308b841d73170ef8f443e1807f6ef88526a816d4" -dependencies = [ - "thiserror", - "ucd-trie", -] +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" [[package]] name = "pin-utils" @@ -1479,19 +1463,18 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "proc-macro-crate" -version = "1.2.1" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "thiserror", - "toml", + "toml_edit", ] [[package]] @@ -1503,7 +1486,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -1520,9 +1503,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.49" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] @@ -1536,7 +1519,7 @@ dependencies = [ "ctor", "lazy_static", "libc", - "libloading", + "libloading 0.8.0", "msgbox", "proxy-dll", "thiserror", @@ -1558,14 +1541,14 @@ name = "proxy-sys" version = "0.2.2" source = "git+https://github.com/RinLovesYou/dll-proxy-rs.git#160fd47d5d5e595ff694a1d004de7d8e562aba59" dependencies = [ - "syn", + "syn 1.0.109", ] [[package]] name = "quote" -version = "1.0.23" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] @@ -1578,9 +1561,9 @@ checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" [[package]] name = "reqwest" -version = "0.11.14" +version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ "base64", "bytes", @@ -1630,61 +1613,70 @@ dependencies = [ "winapi", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustc_version" -version = "0.3.3" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 0.11.0", + "semver", ] [[package]] name = "rustix" -version = "0.36.7" +version = "0.38.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03" +checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" dependencies = [ - "bitflags", + "bitflags 2.4.0", "errno", - "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.42.0", + "windows-sys", ] [[package]] name = "rustls" -version = "0.20.8" +version = "0.21.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +checksum = "1d1feddffcfcc0b33f5c6ce9a29e341e4cd59c3f78e7ee45f4a40c038b1d6cbb" dependencies = [ "log", "ring", + "rustls-webpki", "sct", - "webpki", ] [[package]] name = "rustls-pemfile" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ "base64", ] [[package]] -name = "ryu" -version = "1.0.13" +name = "rustls-webpki" +version = "0.101.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "261e9e0888cba427c3316e6322805653c9425240b6fd96cee7cb671ab70ab8d0" +dependencies = [ + "ring", + "untrusted", +] [[package]] -name = "scratch" -version = "1.0.5" +name = "ryu" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "sct" @@ -1698,59 +1690,50 @@ dependencies = [ [[package]] name = "semver" -version = "0.11.0" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" - -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.152" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.28", ] [[package]] name = "serde_json" -version = "1.0.94" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -1776,9 +1759,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", @@ -1787,18 +1770,18 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] name = "socket2" @@ -1810,6 +1793,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "spin" version = "0.5.2" @@ -1824,15 +1817,26 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "subtle" -version = "2.4.1" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] [[package]] name = "syn" -version = "1.0.107" +version = "2.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" dependencies = [ "proc-macro2", "quote", @@ -1841,9 +1845,9 @@ dependencies = [ [[package]] name = "system-deps" -version = "6.0.3" +version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2955b1fe31e1fa2fbd1976b71cc69a606d7d4da16f6de3333d0c92d51419aeff" +checksum = "30c2de8a4d8f4b823d634affc9cd2a74ec98c53a756f317e529a48046cbf71f3" dependencies = [ "cfg-expr", "heck", @@ -1852,33 +1856,39 @@ dependencies = [ "version-compare", ] +[[package]] +name = "target-lexicon" +version = "0.12.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" + [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" -version = "1.0.39" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.39" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.28", ] [[package]] @@ -1894,19 +1904,20 @@ dependencies = [ [[package]] name = "time" -version = "0.3.20" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" +checksum = "b0fdd63d58b18d663fbdf70e049f00a22c8e42be082203be7f26589213cd75ea" dependencies = [ + "deranged", "serde", "time-core", ] [[package]] name = "time-core" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "tinyvec" @@ -1925,37 +1936,35 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.26.0" +version = "1.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" +checksum = "40de3a2ba249dcb097e01be5e67a5ff53cf250397715a071a81543e8a832a920" dependencies = [ - "autocfg", + "backtrace", "bytes", "libc", - "memchr", "mio", "num_cpus", "pin-project-lite", - "socket2", - "windows-sys 0.45.0", + "socket2 0.5.3", + "windows-sys", ] [[package]] name = "tokio-rustls" -version = "0.23.4" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ "rustls", "tokio", - "webpki", ] [[package]] name = "tokio-util" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -1967,11 +1976,36 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.10" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" dependencies = [ "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" +dependencies = [ + "indexmap 2.0.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", ] [[package]] @@ -1993,9 +2027,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", ] @@ -2022,23 +2056,17 @@ dependencies = [ "widestring", ] -[[package]] -name = "ucd-trie" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" - [[package]] name = "unicode-bidi" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d502c968c6a838ead8e69b2ee18ec708802f99db92a0d156705ec9ef801993b" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-normalization" @@ -2049,19 +2077,13 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - [[package]] name = "unity-rs" version = "0.1.0" source = "git+https://github.com/RinLovesYou/Ferrex/?rev=77d114c#77d114c08fa57609b2cfcb1b9cb4572f53c60259" dependencies = [ "libc", - "libloading", + "libloading 0.7.4", "thiserror", "winapi", ] @@ -2074,9 +2096,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", "idna", @@ -2097,11 +2119,10 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -2119,9 +2140,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2129,24 +2150,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.28", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.34" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if", "js-sys", @@ -2156,9 +2177,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2166,22 +2187,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.28", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "wchar" @@ -2200,14 +2221,14 @@ checksum = "075c93156fed21f9dab57af5e81604d0fdb67432c919a8c1f78bb979f06a3d25" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "web-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -2275,31 +2296,25 @@ version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", ] [[package]] -name = "windows-sys" -version = "0.42.0" +name = "windows" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows-targets 0.48.2", ] [[package]] name = "windows-sys" -version = "0.45.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.2", ] [[package]] @@ -2308,13 +2323,28 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1eeca1c172a285ee6c2c84c341ccea837e7c01b12fbb2d0fe3c9e550ce49ec8" +dependencies = [ + "windows_aarch64_gnullvm 0.48.2", + "windows_aarch64_msvc 0.48.2", + "windows_i686_gnu 0.48.2", + "windows_i686_msvc 0.48.2", + "windows_x86_64_gnu 0.48.2", + "windows_x86_64_gnullvm 0.48.2", + "windows_x86_64_msvc 0.48.2", ] [[package]] @@ -2323,42 +2353,93 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b10d0c968ba7f6166195e13d593af609ec2e3d24f916f081690695cf5eaffb2f" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "571d8d4e62f26d4932099a9efe89660e8bd5087775a2ab5cdd8b747b811f1058" + [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2229ad223e178db5fbbc8bd8d3835e51e566b8474bfca58d2e6150c48bb723cd" + [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "600956e2d840c194eedfc5d18f8242bc2e17c7775b6684488af3a9fff6fe3287" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea99ff3f8b49fb7a8e0d305e5aec485bd068c2ba691b6e277d29eaeac945868a" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1a05a1ece9a7a0d5a7ccf30ba2c33e3a61a30e042ffd247567d1de1d94120d" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d419259aba16b663966e29e6d7c6ecfa0bb8425818bb96f6f1f3c3eb71a6e7b9" + +[[package]] +name = "winnow" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5504cc7644f4b593cbc05c4a55bf9bd4e94b867c3c0bd440934174d50482427d" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.10.1" @@ -2370,9 +2451,9 @@ dependencies = [ [[package]] name = "zip" -version = "0.6.4" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0445d0fbc924bb93539b4316c11afb121ea39296f99a3c4c9edad09e3658cdef" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" dependencies = [ "aes", "byteorder", @@ -2384,7 +2465,7 @@ dependencies = [ "hmac", "pbkdf2", "sha1", - "time 0.3.20", + "time 0.3.25", "zstd", ] @@ -2409,9 +2490,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.7+zstd.1.5.4" +version = "2.0.8+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94509c3ba2fe55294d752b79842c530ccfab760192521df74a081a78d2b3c7f5" +checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" dependencies = [ "cc", "libc", From 42f943deebcdd08ffc9fcc55d927799744765678 Mon Sep 17 00:00:00 2001 From: Sarah Date: Mon, 14 Aug 2023 23:51:14 +0200 Subject: [PATCH 48/79] Make Preload cross-platform --- .../SupportModules/Preload/Preload.cs | 32 +++-- .../SupportModules/Preload/Preload.csproj | 11 +- .../Preload/Properties/Resources.Designer.cs | 92 ------------- .../Preload/Properties/Resources.resx | 130 ------------------ 4 files changed, 24 insertions(+), 241 deletions(-) delete mode 100644 Dependencies/SupportModules/Preload/Properties/Resources.Designer.cs delete mode 100644 Dependencies/SupportModules/Preload/Properties/Resources.resx diff --git a/Dependencies/SupportModules/Preload/Preload.cs b/Dependencies/SupportModules/Preload/Preload.cs index c75f027be..a40fe72aa 100644 --- a/Dependencies/SupportModules/Preload/Preload.cs +++ b/Dependencies/SupportModules/Preload/Preload.cs @@ -1,4 +1,5 @@ using System.IO; +using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; @@ -8,23 +9,32 @@ internal static class Preload { private static void Initialize() { - string ManagedFolder = string.Copy(GetManagedDirectory()); + string managedFolder = string.Copy(GetManagedDirectory()); - string SystemPath = Path.Combine(ManagedFolder, "System.dll"); - if (!File.Exists(SystemPath)) - File.WriteAllBytes(SystemPath, Properties.Resources.System); + string systemPath = Path.Combine(managedFolder, "System.dll"); + if (!File.Exists(systemPath)) + WriteResource("System.dll", systemPath); - string SystemCorePath = Path.Combine(ManagedFolder, "System.Core.dll"); - if (!File.Exists(SystemCorePath)) - File.WriteAllBytes(SystemCorePath, Properties.Resources.System_Core); + string systemCorePath = Path.Combine(managedFolder, "System.Core.dll"); + if (!File.Exists(systemCorePath)) + WriteResource("System.Core.dll", systemCorePath); - string SystemDrawingPath = Path.Combine(ManagedFolder, "System.Drawing.dll"); - if (!File.Exists(SystemDrawingPath)) - File.WriteAllBytes(SystemDrawingPath, Properties.Resources.System_Drawing); + string systemDrawingPath = Path.Combine(managedFolder, "System.Drawing.dll"); + if (!File.Exists(systemDrawingPath)) + WriteResource("System.Drawing.dll", systemCorePath); } [MethodImpl(MethodImplOptions.InternalCall)] [return: MarshalAs(UnmanagedType.LPStr)] - private extern static string GetManagedDirectory(); + private static extern string GetManagedDirectory(); + + private static void WriteResource(string name, string destination) + { + var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name); + var fileStream = new FileStream(destination, FileMode.CreateNew); + for (int i = 0; i < stream!.Length; i++) + fileStream.WriteByte((byte)stream.ReadByte()); + fileStream.Close(); + } } } \ No newline at end of file diff --git a/Dependencies/SupportModules/Preload/Preload.csproj b/Dependencies/SupportModules/Preload/Preload.csproj index 05f254dfe..10d6b8531 100644 --- a/Dependencies/SupportModules/Preload/Preload.csproj +++ b/Dependencies/SupportModules/Preload/Preload.csproj @@ -21,15 +21,10 @@ - - True - True - Resources.resx - - - - + + + \ No newline at end of file diff --git a/Dependencies/SupportModules/Preload/Properties/Resources.Designer.cs b/Dependencies/SupportModules/Preload/Properties/Resources.Designer.cs deleted file mode 100644 index 598a47a04..000000000 --- a/Dependencies/SupportModules/Preload/Properties/Resources.Designer.cs +++ /dev/null @@ -1,92 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace MelonLoader.Support.Properties { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MelonLoader.Support.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized resource of type System.Byte[]. - /// - internal static byte[] System { - get { - object obj = ResourceManager.GetObject("System", resourceCulture); - return ((byte[])(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Byte[]. - /// - internal static byte[] System_Core { - get { - object obj = ResourceManager.GetObject("System_Core", resourceCulture); - return ((byte[])(obj)); - } - } - - internal static byte[] System_Drawing - { - get - { - object obj = ResourceManager.GetObject("System_Drawing", resourceCulture); - return ((byte[])(obj)); - } - } - } -} diff --git a/Dependencies/SupportModules/Preload/Properties/Resources.resx b/Dependencies/SupportModules/Preload/Properties/Resources.resx deleted file mode 100644 index d74d9783a..000000000 --- a/Dependencies/SupportModules/Preload/Properties/Resources.resx +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - ..\Resources\System.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - ..\Resources\System.Core.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - ..\Resources\System.Drawing.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file From ded39c8470287ca7fefda3a115af3d0e2d217a6e Mon Sep 17 00:00:00 2001 From: Sarah Date: Tue, 15 Aug 2023 00:01:10 +0200 Subject: [PATCH 49/79] Bump Version --- MelonLoader/Properties/BuildInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MelonLoader/Properties/BuildInfo.cs b/MelonLoader/Properties/BuildInfo.cs index 34e726168..6d2613f13 100644 --- a/MelonLoader/Properties/BuildInfo.cs +++ b/MelonLoader/Properties/BuildInfo.cs @@ -6,6 +6,6 @@ public static class BuildInfo public const string Description = "MelonLoader"; public const string Author = "Lava Gang"; public const string Company = "discord.gg/2Wn3N2P"; - public const string Version = "0.6.1"; + public const string Version = "0.6.2"; } } \ No newline at end of file From 07e059622a44d52029bfe5bf4545cb576a22c3e0 Mon Sep 17 00:00:00 2001 From: Sarah Date: Wed, 16 Aug 2023 02:15:42 +0200 Subject: [PATCH 50/79] temporary arg parsing, update windows & netcorehost --- Bootstrap/Cargo.toml | 4 +- Bootstrap/src/console/os/windows/mod.rs | 10 +- Bootstrap/src/melonenv/macros.rs | 28 ++-- Cargo.lock | 185 +++++++++--------------- 4 files changed, 87 insertions(+), 140 deletions(-) diff --git a/Bootstrap/Cargo.toml b/Bootstrap/Cargo.toml index c31c08fa8..f443a254c 100755 --- a/Bootstrap/Cargo.toml +++ b/Bootstrap/Cargo.toml @@ -18,10 +18,10 @@ clap = { git = "https://github.com/RinLovesYou/clap", features = ["derive"] } libc = "0.2.140" dobby-rs = { git = "https://github.com/RinLovesYou/dobby-rs" } libc-stdhandle = "0.1.0" -netcorehost = "0.13.1" +netcorehost = "0.15.1" [target.'cfg(windows)'.dependencies] -windows = { version = "0.46.0", features = [ +windows = { version = "0.51.0", features = [ "Win32_Foundation", "Win32_System_Console", "Win32_UI_WindowsAndMessaging" diff --git a/Bootstrap/src/console/os/windows/mod.rs b/Bootstrap/src/console/os/windows/mod.rs index 1ab049e52..ce1a4a6c9 100644 --- a/Bootstrap/src/console/os/windows/mod.rs +++ b/Bootstrap/src/console/os/windows/mod.rs @@ -25,9 +25,7 @@ lazy_static! { pub unsafe fn init() -> Result<(), DynErr> { // creates a console window, if one already exists it'll just return true. - if !AllocConsole().as_bool() { - return Err(ConsoleError::FailedToAllocateConsole.into()); - } + AllocConsole()?; // store the console window handle let mut window = WINDOW.try_lock()?; @@ -39,7 +37,7 @@ pub unsafe fn init() -> Result<(), DynErr> { } // this lets us hook into console close events, and run some cleanup logic. - if SetConsoleCtrlHandler(Some(ctrl_handler_hook), Foundation::TRUE) == Foundation::FALSE { + if SetConsoleCtrlHandler(Some(ctrl_handler_hook), Foundation::TRUE).is_err() { return Err(ConsoleError::FailedToSetConsoleCtrlHandler.into()); } @@ -80,12 +78,12 @@ pub unsafe fn init() -> Result<(), DynErr> { mode |= ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT; - if SetConsoleMode(*output_handle, mode) != Foundation::TRUE { + if SetConsoleMode(*output_handle, mode).is_err() { mode &= !(ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT); } else { mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; - if SetConsoleMode(*output_handle, mode) != Foundation::TRUE { + if SetConsoleMode(*output_handle, mode).is_err() { mode &= !ENABLE_VIRTUAL_TERMINAL_PROCESSING; } } diff --git a/Bootstrap/src/melonenv/macros.rs b/Bootstrap/src/melonenv/macros.rs index 1975a0292..6758a8405 100644 --- a/Bootstrap/src/melonenv/macros.rs +++ b/Bootstrap/src/melonenv/macros.rs @@ -1,31 +1,35 @@ #[macro_export] macro_rules! debug_enabled { - () => { + () => {{ if cfg!(debug_assertions) { true } else { - $crate::melonenv::args::ARGS.debug.is_some_and(|b| b) + let args: Vec = std::env::args().collect(); + args.contains(&"--melonloader.debug".to_string()) } - }; + }}; } #[macro_export] macro_rules! should_set_title { - () => { - !$crate::melonenv::args::ARGS.console_dst.is_some_and(|b| b) - }; + () => {{ + let args: Vec = std::env::args().collect(); + !args.contains(&"--melonloader.consoledst".to_string()) + }}; } #[macro_export] macro_rules! console_on_top { - () => { - $crate::melonenv::args::ARGS.console_on_top.is_some_and(|b| b) - }; + () => {{ + let args: Vec = std::env::args().collect(); + args.contains(&"--melonloader.consoleontop".to_string()) + }}; } #[macro_export] macro_rules! hide_console { - () => { - $crate::melonenv::args::ARGS.hide_console.is_some_and(|b| b) - }; + () => {{ + let args: Vec = std::env::args().collect(); + args.contains(&"--melonloader.hideconsole".to_string()) + }}; } \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 3e25b485b..99b554b6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,7 +18,7 @@ dependencies = [ "netcorehost", "thiserror", "unity-rs", - "windows 0.46.0", + "windows 0.51.0", ] [[package]] @@ -491,22 +491,22 @@ dependencies = [ [[package]] name = "destruct-drop" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be82dda1a4115f575d2a3c1a4712fc8b3473e1a983b5656841754c7d705cd2c9" +checksum = "0eef803a96c15c37e6c7dba7636950982e024fbbe00d1b07cfe60e01ab01e0e0" dependencies = [ "destruct-drop-derive", ] [[package]] name = "destruct-drop-derive" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "802d3d884d31343822c33802e75133a845322231ab334ffada47857376f7f9a2" +checksum = "133a7fa5cffeec6867fb2847335ec2d688f5bbee6318889d2a137ce1d226b180" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.28", ] [[package]] @@ -522,9 +522,9 @@ dependencies = [ [[package]] name = "dlopen2" -version = "0.4.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b121caccfc363e4d9a4589528f3bef7c71b83c6ed01c8dc68cbeeb7fd29ec698" +checksum = "6bc2c7ed06fd72a8513ded8d0d2f6fd2655a85d6885c48cae8625d80faf28c03" dependencies = [ "dlopen2_derive", "libc", @@ -534,13 +534,13 @@ dependencies = [ [[package]] name = "dlopen2_derive" -version = "0.2.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a09ac8bb8c16a282264c379dffba707b9c998afc7506009137f3c6136888078" +checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.28", ] [[package]] @@ -566,6 +566,26 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "enum-map" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9705d8de4776df900a4a0b2384f8b0ab42f775e93b083b42f8ce71bdc32a47e3" +dependencies = [ + "enum-map-derive", +] + +[[package]] +name = "enum-map-derive" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccb14d927583dd5c2eac0f2cf264fc4762aefe1ae14c47a8a20fc1939d3a5fc0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.28", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -990,12 +1010,13 @@ dependencies = [ [[package]] name = "hostfxr-sys" -version = "0.5.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df740eff9acae39f7013829bd17cf4c65731f122b7efbfab9e6426fc70ebac0c" +checksum = "99ee6bbb19e9e466a49bc72baa370eac6959375ed1824e2574f30df6a3f10f98" dependencies = [ "coreclr-hosting-shared", "dlopen2", + "enum-map", ] [[package]] @@ -1293,20 +1314,21 @@ dependencies = [ [[package]] name = "netcorehost" -version = "0.13.1" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdcbf040ee492f59dd7b57247ae6b6491295d541702739eba3ae7cf4d22c2282" +checksum = "8016e6dd942c7495f6c2bc58dbef5da4a3096c0c424efbb210e8fec151afb5b3" dependencies = [ "coreclr-hosting-shared", "cstr", "derive_more", "destruct-drop", + "enum-map", "ffi-opaque", "hostfxr-sys", "nethost-sys", "num_enum", + "once_cell", "thiserror", - "u16cstr", "widestring", ] @@ -1347,22 +1369,22 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.5.11" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.11" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.28", ] [[package]] @@ -2046,16 +2068,6 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" -[[package]] -name = "u16cstr" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f9527d7b5b2bf503a8eafe1e4c16944c95e8455ce4b5be9e78be7f290ad581" -dependencies = [ - "wchar", - "widestring", -] - [[package]] name = "unicode-bidi" version = "0.3.13" @@ -2204,26 +2216,6 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" -[[package]] -name = "wchar" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1ca6ea80317e76471c3aa6d47efb151ef04538960ab810845a1c854f5cd7d8c" -dependencies = [ - "wchar-impl", -] - -[[package]] -name = "wchar-impl" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "075c93156fed21f9dab57af5e81604d0fdb67432c919a8c1f78bb979f06a3d25" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "web-sys" version = "0.3.64" @@ -2292,44 +2284,39 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.46.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets 0.42.2", + "windows-targets", ] [[package]] name = "windows" -version = "0.48.0" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "a9763fb813068e9f4ab70a92a0c6ad61ff6b342f693b1ed0e5387c854386e670" dependencies = [ - "windows-targets 0.48.2", + "windows-core", + "windows-targets", ] [[package]] -name = "windows-sys" -version = "0.48.0" +name = "windows-core" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "b81650771e76355778637954dc9d7eb8d991cd89ad64ba26f21eeb3c22d8d836" dependencies = [ - "windows-targets 0.48.2", + "windows-targets", ] [[package]] -name = "windows-targets" -version = "0.42.2" +name = "windows-sys" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets", ] [[package]] @@ -2338,93 +2325,51 @@ version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1eeca1c172a285ee6c2c84c341ccea837e7c01b12fbb2d0fe3c9e550ce49ec8" dependencies = [ - "windows_aarch64_gnullvm 0.48.2", - "windows_aarch64_msvc 0.48.2", - "windows_i686_gnu 0.48.2", - "windows_i686_msvc 0.48.2", - "windows_x86_64_gnu 0.48.2", - "windows_x86_64_gnullvm 0.48.2", - "windows_x86_64_msvc 0.48.2", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b10d0c968ba7f6166195e13d593af609ec2e3d24f916f081690695cf5eaffb2f" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "571d8d4e62f26d4932099a9efe89660e8bd5087775a2ab5cdd8b747b811f1058" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2229ad223e178db5fbbc8bd8d3835e51e566b8474bfca58d2e6150c48bb723cd" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "600956e2d840c194eedfc5d18f8242bc2e17c7775b6684488af3a9fff6fe3287" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea99ff3f8b49fb7a8e0d305e5aec485bd068c2ba691b6e277d29eaeac945868a" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f1a05a1ece9a7a0d5a7ccf30ba2c33e3a61a30e042ffd247567d1de1d94120d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.2" From d1417d0127579a6d37208c1ea39cba22ea3fcc0b Mon Sep 17 00:00:00 2001 From: Sarah Date: Wed, 30 Aug 2023 22:04:43 +0200 Subject: [PATCH 51/79] fix base dir & launch args in general --- Bootstrap/Cargo.toml | 1 + Bootstrap/src/base_assembly/mono.rs | 4 +- Bootstrap/src/lib.rs | 28 ++--- Bootstrap/src/logging/logger.rs | 2 +- Bootstrap/src/melonenv/paths.rs | 33 +++-- Bootstrap/src/utils/runtime.rs | 82 +++++++++++- Cargo.lock | 119 +++++++++++++++++- .../MelonCompatibilityLayer.cs | 3 +- MelonLoader/Core.cs | 3 +- .../InternalUtils/Il2CppAssemblyGenerator.cs | 2 +- MelonLoader/Melons/MelonHandler.cs | 28 +++++ Proxy/src/core.rs | 39 +++--- Proxy/src/lib.rs | 26 ++-- 13 files changed, 301 insertions(+), 69 deletions(-) diff --git a/Bootstrap/Cargo.toml b/Bootstrap/Cargo.toml index f443a254c..9bf9f33d4 100755 --- a/Bootstrap/Cargo.toml +++ b/Bootstrap/Cargo.toml @@ -19,6 +19,7 @@ libc = "0.2.140" dobby-rs = { git = "https://github.com/RinLovesYou/dobby-rs" } libc-stdhandle = "0.1.0" netcorehost = "0.15.1" +exe = "0.5.6" [target.'cfg(windows)'.dependencies] windows = { version = "0.51.0", features = [ diff --git a/Bootstrap/src/base_assembly/mono.rs b/Bootstrap/src/base_assembly/mono.rs index 1a01b0ea7..bda843860 100644 --- a/Bootstrap/src/base_assembly/mono.rs +++ b/Bootstrap/src/base_assembly/mono.rs @@ -6,7 +6,7 @@ use unity_rs::{ runtime::FerrexRuntime, }; -use crate::{debug, errors::DynErr, melonenv, runtime}; +use crate::{debug, errors::DynErr, melonenv::{self, paths}, runtime}; lazy_static! { pub static ref MONO_PRESTART: Mutex = @@ -23,6 +23,8 @@ pub fn init(runtime: &FerrexRuntime) -> Result<(), DynErr> { debug!("Initializing BaseAssembly")?; + let _runtime_dir = paths::runtime_dir()?; + //get MelonLoader.dll's path and confirm it exists let mut melonloader_dll = melonenv::paths::MELONLOADER_FOLDER.clone(); melonloader_dll.extend(&["net35", "MelonLoader.dll"]); diff --git a/Bootstrap/src/lib.rs b/Bootstrap/src/lib.rs index 3a8e8a57f..8b04c1f38 100755 --- a/Bootstrap/src/lib.rs +++ b/Bootstrap/src/lib.rs @@ -1,17 +1,17 @@ -#![allow(non_snake_case)] -#![deny( - missing_debug_implementations, - unused_results, - warnings, - clippy::extra_unused_lifetimes, - clippy::from_over_into, - clippy::needless_borrow, - clippy::new_without_default, - clippy::useless_conversion -)] -#![forbid(rust_2018_idioms)] -#![allow(clippy::inherent_to_string, clippy::type_complexity, improper_ctypes)] -#![cfg_attr(docsrs, feature(doc_cfg))] +// #![allow(non_snake_case)] +// #![deny( +// missing_debug_implementations, +// unused_results, +// warnings, +// clippy::extra_unused_lifetimes, +// clippy::from_over_into, +// clippy::needless_borrow, +// clippy::new_without_default, +// clippy::useless_conversion +// )] +// #![forbid(rust_2018_idioms)] +// #![allow(clippy::inherent_to_string, clippy::type_complexity, improper_ctypes)] +// #![cfg_attr(docsrs, feature(doc_cfg))] pub mod base_assembly; pub mod console; diff --git a/Bootstrap/src/logging/logger.rs b/Bootstrap/src/logging/logger.rs index b21410121..88aba07fe 100644 --- a/Bootstrap/src/logging/logger.rs +++ b/Bootstrap/src/logging/logger.rs @@ -30,7 +30,7 @@ impl std::convert::TryFrom for LogLevel { macro_rules! log_path { () => { - std::env::current_dir()?.join("MelonLoader").join("Latest-Bootstrap.log") + $crate::melonenv::paths::BASE_DIR.clone().join("MelonLoader").join("Latest-Bootstrap.log") }; } diff --git a/Bootstrap/src/melonenv/paths.rs b/Bootstrap/src/melonenv/paths.rs index b34e1a2e6..040f0df66 100644 --- a/Bootstrap/src/melonenv/paths.rs +++ b/Bootstrap/src/melonenv/paths.rs @@ -1,25 +1,36 @@ -use std::path::PathBuf; +use std::path::{PathBuf, Path}; use lazy_static::lazy_static; use unity_rs::runtime::RuntimeType; -use crate::{errors::DynErr, internal_failure, runtime, constants::W}; +use crate::{errors::DynErr, internal_failure, runtime, constants::W, utils::runtime::{self, NetstandardVersion}}; use super::args::ARGS; lazy_static! { pub static ref BASE_DIR: W = { - match ARGS.base_dir { - Some(ref path) => W(PathBuf::from(path.clone())), - None => W(std::env::current_dir().unwrap_or_else(|e| { - internal_failure!("Failed to get base directory: {}", e.to_string()); - })), + let args: Vec = std::env::args().collect(); + let mut base_dir = std::env::current_dir().unwrap_or_else(|e|internal_failure!("Failed to get base dir: {e}")); + for arg in args.iter() { + if arg.starts_with("--melonloader.basedir") { + let a: Vec<&str> = arg.split("=").collect(); + base_dir = PathBuf::from(a[1]); + } } + + W(base_dir) }; pub static ref GAME_DIR: W = { - W(std::env::current_dir().unwrap_or_else(|e| { - internal_failure!("Failed to get game directory: {}", e.to_string()); - })) + let args: Vec = std::env::args().collect(); + let mut base_dir = std::env::current_dir().unwrap_or_else(|e|internal_failure!("Failed to get game dir: {e}")); + for arg in args.iter() { + if arg.starts_with("--melonloader.basedir") { + let a: Vec<&str> = arg.split("=").collect(); + base_dir = PathBuf::from(a[1]); + } + } + + W(base_dir) }; pub static ref MELONLOADER_FOLDER: W = W(BASE_DIR.join("MelonLoader")); pub static ref DEPENDENCIES_FOLDER: W = W(MELONLOADER_FOLDER.join("Dependencies")); @@ -32,6 +43,8 @@ pub fn runtime_dir() -> Result { let mut path = MELONLOADER_FOLDER.clone(); + //let version = runtime::get_netstandard_version()?; + match runtime.get_type() { RuntimeType::Mono(_) => path.push("net35"), RuntimeType::Il2Cpp(_) => path.push("net6"), diff --git a/Bootstrap/src/utils/runtime.rs b/Bootstrap/src/utils/runtime.rs index dea4933a2..d657995c5 100644 --- a/Bootstrap/src/utils/runtime.rs +++ b/Bootstrap/src/utils/runtime.rs @@ -1,6 +1,9 @@ +use std::{error::Error, collections::HashMap, io, path::Path}; + +use exe::{ImageDirectoryEntry, ResourceDirectory, VecPE, PE, ImportDirectory, ImportData, CCharString, ImageDataDirectory, ImageResourceDirectory}; use unity_rs::runtime::FerrexRuntime; -use crate::errors::DynErr; +use crate::{errors::DynErr, log, melonenv::paths}; #[allow(dead_code)] pub static mut RUNTIME: Option = None; @@ -21,3 +24,80 @@ pub fn get_runtime() -> Result<&'static FerrexRuntime, DynErr> { Ok(RUNTIME.as_ref().ok_or("Failed to get runtime")?) } } + +#[derive(Debug)] +pub enum NetstandardVersion { + Old, + New, +} + +impl Default for PeFileInfo { + fn default () -> PeFileInfo { + PeFileInfo { + product_version: String::new(), + original_filename: String::new(), + file_description: String::new(), + file_version: String::new(), + product_name: String::new(), + company_name: String::new(), + internal_name: String::new(), + legal_copyright: String::new() + } + } +} +#[derive(Clone)] +pub struct PeFileInfo { + pub product_version: String, + pub original_filename: String, + pub file_description: String, + pub file_version: String, + pub product_name: String, + pub company_name: String, + pub internal_name: String, + pub legal_copyright: String +} + +pub fn get_netstandard_version() -> Result> { + let netstandard = paths::get_managed_dir()?.join("netstandard.dll"); + if !netstandard.exists() { + return Ok(NetstandardVersion::Old); + } + + let file_info = get_pe_file_info(&netstandard)?; + + match file_info.file_version.as_str() { + "2.1.0.0" => Ok(NetstandardVersion::New), + _ => Ok(NetstandardVersion::Old) + } +} + +fn get_pe_file_info(path: &Path) -> io::Result { + let mut file_info = PeFileInfo::default(); + let Ok(pefile) = exe::VecPE::from_disk_file(path) else { return Ok(file_info) }; + let Ok(vs_version_check) = exe::VSVersionInfo::parse(&pefile) else { return Ok(file_info) }; + let vs_version = vs_version_check; + if let Some(string_file_info) = vs_version.string_file_info { + let Ok(string_map) = string_file_info.children[0].string_map() else { return Ok(file_info) }; + file_info.product_version = get_hashmap_value(&string_map, "ProductVersion")?; + file_info.original_filename = get_hashmap_value(&string_map, "OriginalFilename")?; + file_info.file_description = get_hashmap_value(&string_map, "FileDescription")?; + file_info.file_version = get_hashmap_value(&string_map, "FileVersion")?; + file_info.product_name = get_hashmap_value(&string_map, "ProductName")?; + file_info.company_name = get_hashmap_value(&string_map, "CompanyName")?; + file_info.internal_name = get_hashmap_value(&string_map, "InternalName")?; + file_info.legal_copyright = get_hashmap_value(&string_map, "LegalCopyright")?; + } + Ok(file_info) +} + + +fn get_hashmap_value( + string_map: &HashMap, + value_name: &str + ) -> io::Result +{ +let _v = match string_map.get(value_name) { +Some(v) => return Ok(v.to_string()), +None => return Ok("".to_string()) +}; +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 99b554b6c..04818e267 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,7 @@ dependencies = [ "colored", "ctor", "dobby-rs", + "exe", "lazy_static", "libc", "libc-stdhandle", @@ -143,6 +144,15 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -509,13 +519,22 @@ dependencies = [ "syn 2.0.28", ] +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", + "block-buffer 0.10.4", "crypto-common", "subtle", ] @@ -613,6 +632,24 @@ dependencies = [ "libc", ] +[[package]] +name = "exe" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37f6e4213f515539932c53cd36a87ad8296bb8b2bea7f71b27438b811ad64392" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "chrono", + "hex", + "md-5", + "num-traits", + "pkbuffer", + "sha-1", + "sha2 0.9.9", + "widestring", +] + [[package]] name = "ffi-opaque" version = "2.0.1" @@ -999,13 +1036,19 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hmac" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest", + "digest 0.10.7", ] [[package]] @@ -1257,6 +1300,17 @@ dependencies = [ "libc", ] +[[package]] +name = "md-5" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "opaque-debug", +] + [[package]] name = "memchr" version = "2.5.0" @@ -1411,6 +1465,12 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + [[package]] name = "os_str_bytes" version = "6.5.1" @@ -1459,10 +1519,10 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "digest", + "digest 0.10.7", "hmac", "password-hash", - "sha2", + "sha2 0.10.7", ] [[package]] @@ -1483,6 +1543,27 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkbuffer" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfc2d171e6bab0498ea5f696960ea3647194a0549b12af99070f1e4cf6d62d94" +dependencies = [ + "memchr", + "pkbuffer_derive", +] + +[[package]] +name = "pkbuffer_derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c86ad26b9715c9c1664b79f6e5c44baf38fb87a5133bdd7ec90baff7b71d155" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "pkg-config" version = "0.3.27" @@ -1768,6 +1849,19 @@ dependencies = [ "serde", ] +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + [[package]] name = "sha1" version = "0.10.5" @@ -1776,7 +1870,20 @@ checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", ] [[package]] @@ -1787,7 +1894,7 @@ checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.7", ] [[package]] diff --git a/MelonLoader/CompatibilityLayers/MelonCompatibilityLayer.cs b/MelonLoader/CompatibilityLayers/MelonCompatibilityLayer.cs index 72c8b81b6..df567faf2 100644 --- a/MelonLoader/CompatibilityLayers/MelonCompatibilityLayer.cs +++ b/MelonLoader/CompatibilityLayers/MelonCompatibilityLayer.cs @@ -3,12 +3,13 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using MelonLoader.Utils; namespace MelonLoader { public static class MelonCompatibilityLayer { - public static string baseDirectory = $"MelonLoader{Path.DirectorySeparatorChar}Dependencies{Path.DirectorySeparatorChar}CompatibilityLayers"; + public static string baseDirectory = $"{MelonEnvironment.GameRootDirectory}{Path.DirectorySeparatorChar}MelonLoader{Path.DirectorySeparatorChar}Dependencies{Path.DirectorySeparatorChar}CompatibilityLayers"; private static List layers = new List() { diff --git a/MelonLoader/Core.cs b/MelonLoader/Core.cs index 67f5d6073..e570fac17 100644 --- a/MelonLoader/Core.cs +++ b/MelonLoader/Core.cs @@ -93,7 +93,8 @@ internal static int Initialize() MelonCompatibilityLayer.LoadModules(); bHapticsManager.Connect(BuildInfo.Name, UnityInformationHandler.GameName); - + + MelonHandler.LoadUserlibs(MelonEnvironment.UserLibsDirectory); MelonHandler.LoadMelonsFromDirectory(MelonEnvironment.PluginsDirectory); MelonEvents.MelonHarmonyEarlyInit.Invoke(); MelonEvents.OnPreInitialization.Invoke(); diff --git a/MelonLoader/InternalUtils/Il2CppAssemblyGenerator.cs b/MelonLoader/InternalUtils/Il2CppAssemblyGenerator.cs index 55df8d3ac..572660602 100644 --- a/MelonLoader/InternalUtils/Il2CppAssemblyGenerator.cs +++ b/MelonLoader/InternalUtils/Il2CppAssemblyGenerator.cs @@ -10,7 +10,7 @@ namespace MelonLoader.InternalUtils internal static class Il2CppAssemblyGenerator { public static readonly MelonModule.Info moduleInfo = new MelonModule.Info( - $"MelonLoader{Path.DirectorySeparatorChar}Dependencies{Path.DirectorySeparatorChar}Il2CppAssemblyGenerator{Path.DirectorySeparatorChar}Il2CppAssemblyGenerator.dll" + $"{MelonEnvironment.GameRootDirectory}{Path.DirectorySeparatorChar}MelonLoader{Path.DirectorySeparatorChar}Dependencies{Path.DirectorySeparatorChar}Il2CppAssemblyGenerator{Path.DirectorySeparatorChar}Il2CppAssemblyGenerator.dll" , () => !MelonUtils.IsGameIl2Cpp()); internal static bool Run() diff --git a/MelonLoader/Melons/MelonHandler.cs b/MelonLoader/Melons/MelonHandler.cs index a63c033c7..e8ec51ca9 100644 --- a/MelonLoader/Melons/MelonHandler.cs +++ b/MelonLoader/Melons/MelonHandler.cs @@ -91,6 +91,34 @@ public static void LoadMelonsFromDirectory(string path) where T : MelonTypeBa MelonLogger.WriteSpacer(); firstSpacer = true; } + + public static void LoadUserlibs(string path) + { + path = Path.GetFullPath(path); + + var loadingMsg = $"Loading UserLibss from '{path}'..."; + MelonLogger.WriteSpacer(); + MelonLogger.Msg(loadingMsg); + + bool hasWroteLine = false; + + var files = Directory.GetFiles(path, "*.dll"); + var melonAssemblies = new List(); + foreach (var f in files) + { + if (!hasWroteLine) + { + hasWroteLine = true; + MelonLogger.WriteLine(Color.Magenta); + } + + var asm = MelonAssembly.LoadMelonAssembly(f, false); + if (asm == null) + continue; + + melonAssemblies.Add(asm); + } + } #region Obsolete Members /// diff --git a/Proxy/src/core.rs b/Proxy/src/core.rs index eb755fee1..466eb921c 100644 --- a/Proxy/src/core.rs +++ b/Proxy/src/core.rs @@ -1,23 +1,13 @@ //! the core logic of the proxy use crate::utils::files; -use clap::Parser; use lazy_static::lazy_static; use libloading::Library; use std::{error, path::PathBuf, sync::Mutex}; -#[derive(Parser)] -struct Arguments { - #[arg(long = "no-mods")] - no_mods: Option, - - #[arg(long = "melonloader.basedir")] - base_dir: Option, -} - -lazy_static!( +lazy_static! { static ref BOOTSTRAP: Mutex> = Mutex::new(None); -); +} pub fn init() -> Result<(), Box> { let file_path = std::env::current_exe()?; @@ -26,20 +16,29 @@ pub fn init() -> Result<(), Box> { return Ok(()); } - let args = Arguments::parse_optimistic()?; + let args: Vec = std::env::args().collect(); + let mut base_dir = std::env::current_dir()?; + let mut no_mods = false; + + for arg in args.iter() { + if arg.starts_with("--melonloader.basedir") { + let a: Vec<&str> = arg.split("=").collect(); + base_dir = PathBuf::from(a[1]); + } + + if arg.contains("--no-mods") { + no_mods = true; + } + } + //return Ok, and silently stop loading MelonLoader, if the user has specified to not load mods, //or if the game is not a Unity game - if args.no_mods.is_some_and(|b| b) { + if no_mods { return Ok(()); } - let base_path = match args.base_dir { - Some(path) => PathBuf::from(path), - None => std::env::current_dir()?, - }; - - let bootstrap_path = files::get_bootstrap_path(&base_path)?; + let bootstrap_path = files::get_bootstrap_path(&base_dir)?; unsafe { *BOOTSTRAP.try_lock()? = Some(Library::new(&bootstrap_path)?); diff --git a/Proxy/src/lib.rs b/Proxy/src/lib.rs index a1fcba04d..897c0219a 100644 --- a/Proxy/src/lib.rs +++ b/Proxy/src/lib.rs @@ -1,18 +1,18 @@ //! Cross platform reimplementation of MelonLoader's Proxy in rust -#![deny( - missing_debug_implementations, - unused_results, - warnings, - clippy::extra_unused_lifetimes, - clippy::from_over_into, - clippy::needless_borrow, - clippy::new_without_default, - clippy::useless_conversion -)] -#![forbid(rust_2018_idioms)] -#![allow(clippy::inherent_to_string, clippy::type_complexity, improper_ctypes)] -#![cfg_attr(docsrs, feature(doc_cfg))] +// #![deny( +// missing_debug_implementations, +// unused_results, +// warnings, +// clippy::extra_unused_lifetimes, +// clippy::from_over_into, +// clippy::needless_borrow, +// clippy::new_without_default, +// clippy::useless_conversion +// )] +// #![forbid(rust_2018_idioms)] +// #![allow(clippy::inherent_to_string, clippy::type_complexity, improper_ctypes)] +// #![cfg_attr(docsrs, feature(doc_cfg))] pub mod entry; pub mod core; From 47339f072deeb3f2966b8e748e5c2b5ec568f95a Mon Sep 17 00:00:00 2001 From: Akiva Silver Date: Wed, 13 Sep 2023 05:16:55 -0400 Subject: [PATCH 52/79] fix oculus profile pictures --- MelonLoader/Utils/ManagedAnalyticsBlocker.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/MelonLoader/Utils/ManagedAnalyticsBlocker.cs b/MelonLoader/Utils/ManagedAnalyticsBlocker.cs index 7430f78c2..9f49b4433 100644 --- a/MelonLoader/Utils/ManagedAnalyticsBlocker.cs +++ b/MelonLoader/Utils/ManagedAnalyticsBlocker.cs @@ -67,6 +67,14 @@ internal static unsafe class ManagedAnalyticsBlocker "\u0066\u0061\u0063\u0065\u0062\u006f\u006f\u006b\u002e\u0063\u006f\u006d", }; + /// + /// Explicitly allow domains that may be a subdomain of a blocked domain. + /// + private static HashSet _explicitAllowList = new() + { + "\u0073\u0063\u006f\u006e\u0074\u0065\u006e\u0074\u002e\u006f\u0063\u0075\u006c\u0075\u0073\u0063\u0064\u006e\u002e\u0063\u006f\u006d" + }; + private static List _observedHostnames = new() { //Default ignored (as in, not logged) hostnames. I'm leaving these in cleartext cause it's easier. @@ -87,7 +95,8 @@ private static bool CheckShouldBlock(string hostname) hostname = hostname.Trim().ToLowerInvariant(); - var shouldBlock = _blockList.Any(b => hostname.Contains(b)); + var shouldBlock = !_explicitAllowList.Contains(hostname) && + _blockList.Any(b => hostname.Contains(b)); if (MelonDebug.IsEnabled() || MelonLaunchOptions.Core.ShouldDisplayAnalyticsBlocker) { From d02bb5c67432e49d8ff87ed405254d9762767b9d Mon Sep 17 00:00:00 2001 From: Akiva Silver Date: Wed, 13 Sep 2023 05:33:08 -0400 Subject: [PATCH 53/79] fixed DAB thread safety --- MelonLoader/Utils/ManagedAnalyticsBlocker.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/MelonLoader/Utils/ManagedAnalyticsBlocker.cs b/MelonLoader/Utils/ManagedAnalyticsBlocker.cs index 9f49b4433..59b197f20 100644 --- a/MelonLoader/Utils/ManagedAnalyticsBlocker.cs +++ b/MelonLoader/Utils/ManagedAnalyticsBlocker.cs @@ -25,6 +25,8 @@ internal static unsafe class ManagedAnalyticsBlocker private static gethostbyname_delegate original_gethostbyname; private static getaddrinfo_delegate original_getaddrinfo; + + private static object _observedDomainsLock = new object(); private static List _blockList = new() { @@ -102,10 +104,16 @@ private static bool CheckShouldBlock(string hostname) { if (shouldBlock) MelonDebug.Msg($"Host Name or IP blocked: {hostname}"); - else if (!_observedHostnames.Any(h => hostname.Contains(h))) + else { - MelonDebug.Msg($"Unique Host Name or IP Found: {hostname}"); - _observedHostnames.Add(hostname); + lock (_observedDomainsLock) + { + if (!_observedHostnames.Any(h => hostname.Contains(h))) + { + MelonDebug.Msg($"Unique Host Name or IP Found: {hostname}"); + _observedHostnames.Add(hostname); + } + } } } From 9866aa81f821f8f71cbe35395e60626213d65653 Mon Sep 17 00:00:00 2001 From: DubyaDude Date: Tue, 19 Sep 2023 14:43:24 -0400 Subject: [PATCH 54/79] Depricate ruby-core for dubyadu.de --- .../Il2CppAssemblyGenerator/RemoteAPI.cs | 31 ++----------------- MelonLoader/Utils/ManagedAnalyticsBlocker.cs | 2 +- 2 files changed, 3 insertions(+), 30 deletions(-) diff --git a/Dependencies/Il2CppAssemblyGenerator/RemoteAPI.cs b/Dependencies/Il2CppAssemblyGenerator/RemoteAPI.cs index 5ca438600..2f64d3a25 100644 --- a/Dependencies/Il2CppAssemblyGenerator/RemoteAPI.cs +++ b/Dependencies/Il2CppAssemblyGenerator/RemoteAPI.cs @@ -40,7 +40,7 @@ static RemoteAPI() new HostInfo($"{DefaultHostInfo.Melon.API_URL_1}{gamename}", DefaultHostInfo.Melon.Contact), new HostInfo($"{DefaultHostInfo.Melon.API_URL_2}{gamename}", DefaultHostInfo.Melon.Contact), new HostInfo($"{DefaultHostInfo.Melon.API_URL_SAMBOY}{gamename}", DefaultHostInfo.Melon.Contact), - new HostInfo($"{DefaultHostInfo.Ruby.API_URL}{gamename}.json", DefaultHostInfo.Ruby.Contact), + new HostInfo($"{DefaultHostInfo.Melon.API_URL_DUBYADUDE}{gamename}", DefaultHostInfo.Melon.Contact), }; } @@ -118,6 +118,7 @@ internal static class Melon internal static string API_URL_1 = $"https://api-1.melonloader.com/api/{API_VERSION}/game/"; internal static string API_URL_2 = $"https://api-2.melonloader.com/api/{API_VERSION}/game/"; internal static string API_URL_SAMBOY = $"https://melon.samboy.dev/api/{API_VERSION}/game/"; + internal static string API_URL_DUBYADUDE = $"https://melon.dubyadu.de/api/{API_VERSION}/game/"; internal static InfoStruct Contact(string response_str) { @@ -144,34 +145,6 @@ internal class ResponseStruct public string obfuscationRegex = null; } } - - internal static class Ruby - { - internal static string API_URL = "https://ruby-core.com/api/ml/"; - - internal static InfoStruct Contact(string response_str) - { - ResponseStruct responseobj = MelonUtils.ParseJSONStringtoStruct(response_str); - if (responseobj == null) - return null; - - InfoStruct returninfo = new InfoStruct(); - //returninfo.ForceDumperVersion = responseobj.forceDumperVersion; - returninfo.ObfuscationRegex = responseobj.obfuscationRegex; - returninfo.MappingURL = responseobj.mappingURL; - returninfo.MappingFileSHA512 = responseobj.mappingFileSHA512; - return returninfo; - } - - private class ResponseStruct - { - public string forceDumperVersion = null; - public string forceUnhollowerVersion = null; //TODO: Remove this from the API - public string obfuscationRegex = null; - public string mappingURL = null; - public string mappingFileSHA512 = null; - } - } } } } diff --git a/MelonLoader/Utils/ManagedAnalyticsBlocker.cs b/MelonLoader/Utils/ManagedAnalyticsBlocker.cs index 7430f78c2..f102e7d1d 100644 --- a/MelonLoader/Utils/ManagedAnalyticsBlocker.cs +++ b/MelonLoader/Utils/ManagedAnalyticsBlocker.cs @@ -74,7 +74,7 @@ internal static unsafe class ManagedAnalyticsBlocker "bonetome.com", "samboy.dev", "github.com", - "ruby-core.com", + "dubyadu.de", "melonloader.com", "githubusercontent.com", "thetrueyoshifan.com" From 0d28856317106f628e0985de18de17fcc43b0f55 Mon Sep 17 00:00:00 2001 From: DubyaDude Date: Tue, 19 Sep 2023 14:44:20 -0400 Subject: [PATCH 55/79] Remove bonetome.com (Hijacked Domain) --- MelonLoader/Utils/ManagedAnalyticsBlocker.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/MelonLoader/Utils/ManagedAnalyticsBlocker.cs b/MelonLoader/Utils/ManagedAnalyticsBlocker.cs index f102e7d1d..316dd8ead 100644 --- a/MelonLoader/Utils/ManagedAnalyticsBlocker.cs +++ b/MelonLoader/Utils/ManagedAnalyticsBlocker.cs @@ -71,7 +71,6 @@ internal static unsafe class ManagedAnalyticsBlocker { //Default ignored (as in, not logged) hostnames. I'm leaving these in cleartext cause it's easier. "ntp.org", - "bonetome.com", "samboy.dev", "github.com", "dubyadu.de", From a8a615818140fe5a8c6a2493f98f07d753ac21c9 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Mon, 16 Oct 2023 22:55:29 -0500 Subject: [PATCH 56/79] Fix logger sha256 hash --- MelonLoader/MelonUtils.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MelonLoader/MelonUtils.cs b/MelonLoader/MelonUtils.cs index 4be065b5a..ec830a898 100644 --- a/MelonLoader/MelonUtils.cs +++ b/MelonLoader/MelonUtils.cs @@ -30,7 +30,7 @@ public static class MelonUtils internal static void Setup(AppDomain domain) { using (var sha = SHA256.Create()) - HashCode = string.Join("", sha.ComputeHash(File.ReadAllBytes(Assembly.GetExecutingAssembly().Location)).Select(b => b.ToString("X")).ToArray()); + HashCode = string.Concat(sha.ComputeHash(File.ReadAllBytes(Assembly.GetExecutingAssembly().Location)).Select(b => b.ToString("X2")).ToArray()); Core.WelcomeMessage(); @@ -447,4 +447,4 @@ public static string GetFileProductName(string filepath) //[return: MarshalAs(UnmanagedType.LPStr)] //private extern static string Internal_GetGameDirectory(); } -} \ No newline at end of file +} From b6460412f33b5459d7a6fbbd077eb98f4c40857e Mon Sep 17 00:00:00 2001 From: Sarah Date: Fri, 29 Dec 2023 21:49:01 +0100 Subject: [PATCH 57/79] Fix missing Close Button icalls (This never needed to be an icall anyways) --- Cargo.lock | 16 +++++++++++++--- MelonLoader/BootstrapInterop.cs | 21 +++++++-------------- Proxy/Cargo.toml | 2 +- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 04818e267..2e643590f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,7 +9,7 @@ dependencies = [ "chrono", "clap", "colored", - "ctor", + "ctor 0.1.26", "dobby-rs", "exe", "lazy_static", @@ -476,6 +476,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ctor" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d2b3721e861707777e3195b0158f950ae6dc4a27e4d02ff9f67e3eb3de199e" +dependencies = [ + "quote", + "syn 2.0.28", +] + [[package]] name = "cty" version = "0.2.2" @@ -1619,7 +1629,7 @@ version = "0.2.0" dependencies = [ "cc", "clap", - "ctor", + "ctor 0.2.6", "lazy_static", "libc", "libloading 0.8.0", @@ -1633,7 +1643,7 @@ name = "proxy-dll" version = "0.2.5" source = "git+https://github.com/RinLovesYou/dll-proxy-rs.git#160fd47d5d5e595ff694a1d004de7d8e562aba59" dependencies = [ - "ctor", + "ctor 0.1.26", "proxy-sys", "thiserror", "winapi", diff --git a/MelonLoader/BootstrapInterop.cs b/MelonLoader/BootstrapInterop.cs index 7f9acab19..a4e5a28c2 100644 --- a/MelonLoader/BootstrapInterop.cs +++ b/MelonLoader/BootstrapInterop.cs @@ -24,19 +24,7 @@ internal static void SetDefaultConsoleTitleWithGameName([MarshalAs(UnmanagedType Console.Title = versionStr; } - -#if !NET6_0 - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void EnableCloseButton(IntPtr mainWindow); - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void DisableCloseButton(IntPtr mainWindow); - - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern void NativeHookAttach(IntPtr target, IntPtr detour); - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern void NativeHookDetach(IntPtr target, IntPtr detour); -#else - + private const int MF_BYCOMMAND = 0x00000000; private const int MF_ENABLED = 0x00000000; @@ -63,7 +51,12 @@ public static void DisableCloseButton(IntPtr mainWindow) EnableMenuItem(GetSystemMenu(mainWindow, 0), SC_CLOSE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); } - +#if !NET6_0 + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern void NativeHookAttach(IntPtr target, IntPtr detour); + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern void NativeHookDetach(IntPtr target, IntPtr detour); +#else public static void NativeHookAttach(IntPtr target, IntPtr detour) { //SanityCheckDetour is able to wrap and fix the bad method in a delegate where possible, so we pass the detour by ref. diff --git a/Proxy/Cargo.toml b/Proxy/Cargo.toml index 579871f29..573cdb319 100644 --- a/Proxy/Cargo.toml +++ b/Proxy/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ctor = "0.1.26" +ctor = "0.2.6" msgbox = "0.7.0" lazy_static = "1.4.0" thiserror = "*" From dcc06560f56ffd53d04c1be88be02a93261983c3 Mon Sep 17 00:00:00 2001 From: Sarah Date: Fri, 29 Dec 2023 22:34:50 +0100 Subject: [PATCH 58/79] Backport universality Proxy --- Cargo.lock | 180 ++++-- Cargo.toml | 8 +- MelonProxy-sys/Cargo.toml | 20 + MelonProxy-sys/src/lib.rs | 99 +++ MelonProxy/Cargo.toml | 25 + MelonProxy/build.rs | 27 + MelonProxy/deps/Exports.def | 266 ++++++++ MelonProxy/deps/version.x64.S | 52 ++ MelonProxy/deps/version.x86.S | 52 ++ MelonProxy/deps/winhttp.x64.S | 196 ++++++ MelonProxy/deps/winhttp.x86.S | 260 ++++++++ MelonProxy/deps/winmm.x64.S | 724 ++++++++++++++++++++++ MelonProxy/deps/winmm.x86.S | 724 ++++++++++++++++++++++ {Proxy => MelonProxy}/src/core.rs | 36 +- MelonProxy/src/lib.rs | 42 ++ MelonProxy/src/proxy/exports.rs | 316 ++++++++++ MelonProxy/src/proxy/hinstance_ext.rs | 62 ++ MelonProxy/src/proxy/mod.rs | 3 + MelonProxy/src/proxy/windows_ext.rs | 23 + {Proxy => MelonProxy}/src/utils/assert.rs | 2 +- MelonProxy/src/utils/files.rs | 25 + MelonProxy/src/utils/mod.rs | 2 + Proxy/Cargo.toml | 28 - Proxy/src/entry.rs | 35 -- Proxy/src/lib.rs | 19 - Proxy/src/utils/errors.rs | 9 - Proxy/src/utils/files.rs | 48 -- Proxy/src/utils/mod.rs | 5 - 28 files changed, 3063 insertions(+), 225 deletions(-) create mode 100644 MelonProxy-sys/Cargo.toml create mode 100644 MelonProxy-sys/src/lib.rs create mode 100644 MelonProxy/Cargo.toml create mode 100644 MelonProxy/build.rs create mode 100644 MelonProxy/deps/Exports.def create mode 100644 MelonProxy/deps/version.x64.S create mode 100644 MelonProxy/deps/version.x86.S create mode 100644 MelonProxy/deps/winhttp.x64.S create mode 100644 MelonProxy/deps/winhttp.x86.S create mode 100644 MelonProxy/deps/winmm.x64.S create mode 100644 MelonProxy/deps/winmm.x86.S rename {Proxy => MelonProxy}/src/core.rs (59%) create mode 100644 MelonProxy/src/lib.rs create mode 100644 MelonProxy/src/proxy/exports.rs create mode 100644 MelonProxy/src/proxy/hinstance_ext.rs create mode 100644 MelonProxy/src/proxy/mod.rs create mode 100644 MelonProxy/src/proxy/windows_ext.rs rename {Proxy => MelonProxy}/src/utils/assert.rs (87%) create mode 100644 MelonProxy/src/utils/files.rs create mode 100644 MelonProxy/src/utils/mod.rs delete mode 100644 Proxy/Cargo.toml delete mode 100644 Proxy/src/entry.rs delete mode 100644 Proxy/src/lib.rs delete mode 100644 Proxy/src/utils/errors.rs delete mode 100644 Proxy/src/utils/files.rs delete mode 100644 Proxy/src/utils/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 2e643590f..74267c499 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -483,7 +483,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30d2b3721e861707777e3195b0158f950ae6dc4a27e4d02ff9f67e3eb3de199e" dependencies = [ "quote", - "syn 2.0.28", + "syn 2.0.43", ] [[package]] @@ -526,7 +526,7 @@ checksum = "133a7fa5cffeec6867fb2847335ec2d688f5bbee6318889d2a137ce1d226b180" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.43", ] [[package]] @@ -569,7 +569,7 @@ checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.43", ] [[package]] @@ -612,7 +612,7 @@ checksum = "ccb14d927583dd5c2eac0f2cf264fc4762aefe1ae14c47a8a20fc1939d3a5fc0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.43", ] [[package]] @@ -756,7 +756,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.43", ] [[package]] @@ -1321,6 +1321,24 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "melon_proxy" +version = "0.2.5" +dependencies = [ + "ctor 0.2.6", + "libloading 0.8.0", + "melon_proxy-sys", + "msgbox", + "windows 0.52.0", +] + +[[package]] +name = "melon_proxy-sys" +version = "0.2.2" +dependencies = [ + "syn 2.0.43", +] + [[package]] name = "memchr" version = "2.5.0" @@ -1448,7 +1466,7 @@ checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.43", ] [[package]] @@ -1616,47 +1634,13 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" dependencies = [ "unicode-ident", ] -[[package]] -name = "proxy" -version = "0.2.0" -dependencies = [ - "cc", - "clap", - "ctor 0.2.6", - "lazy_static", - "libc", - "libloading 0.8.0", - "msgbox", - "proxy-dll", - "thiserror", -] - -[[package]] -name = "proxy-dll" -version = "0.2.5" -source = "git+https://github.com/RinLovesYou/dll-proxy-rs.git#160fd47d5d5e595ff694a1d004de7d8e562aba59" -dependencies = [ - "ctor 0.1.26", - "proxy-sys", - "thiserror", - "winapi", -] - -[[package]] -name = "proxy-sys" -version = "0.2.2" -source = "git+https://github.com/RinLovesYou/dll-proxy-rs.git#160fd47d5d5e595ff694a1d004de7d8e562aba59" -dependencies = [ - "syn 1.0.109", -] - [[package]] name = "quote" version = "1.0.32" @@ -1824,7 +1808,7 @@ checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.43", ] [[package]] @@ -1973,9 +1957,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.28" +version = "2.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" +checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" dependencies = [ "proc-macro2", "quote", @@ -2027,7 +2011,7 @@ checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.43", ] [[package]] @@ -2288,7 +2272,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.43", "wasm-bindgen-shared", ] @@ -2322,7 +2306,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.43", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2405,7 +2389,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets", + "windows-targets 0.48.2", ] [[package]] @@ -2414,8 +2398,18 @@ version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9763fb813068e9f4ab70a92a0c6ad61ff6b342f693b1ed0e5387c854386e670" dependencies = [ - "windows-core", - "windows-targets", + "windows-core 0.51.0", + "windows-targets 0.48.2", +] + +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core 0.52.0", + "windows-targets 0.52.0", ] [[package]] @@ -2424,7 +2418,16 @@ version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b81650771e76355778637954dc9d7eb8d991cd89ad64ba26f21eeb3c22d8d836" dependencies = [ - "windows-targets", + "windows-targets 0.48.2", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", ] [[package]] @@ -2433,7 +2436,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.2", ] [[package]] @@ -2442,13 +2445,28 @@ version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1eeca1c172a285ee6c2c84c341ccea837e7c01b12fbb2d0fe3c9e550ce49ec8" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.2", + "windows_aarch64_msvc 0.48.2", + "windows_i686_gnu 0.48.2", + "windows_i686_msvc 0.48.2", + "windows_x86_64_gnu 0.48.2", + "windows_x86_64_gnullvm 0.48.2", + "windows_x86_64_msvc 0.48.2", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] @@ -2457,42 +2475,84 @@ version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b10d0c968ba7f6166195e13d593af609ec2e3d24f916f081690695cf5eaffb2f" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "571d8d4e62f26d4932099a9efe89660e8bd5087775a2ab5cdd8b747b811f1058" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2229ad223e178db5fbbc8bd8d3835e51e566b8474bfca58d2e6150c48bb723cd" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "600956e2d840c194eedfc5d18f8242bc2e17c7775b6684488af3a9fff6fe3287" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea99ff3f8b49fb7a8e0d305e5aec485bd068c2ba691b6e277d29eaeac945868a" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f1a05a1ece9a7a0d5a7ccf30ba2c33e3a61a30e042ffd247567d1de1d94120d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d419259aba16b663966e29e6d7c6ecfa0bb8425818bb96f6f1f3c3eb71a6e7b9" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + [[package]] name = "winnow" version = "0.5.10" diff --git a/Cargo.toml b/Cargo.toml index 0001ba43a..cfc57d747 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,3 @@ [workspace] -members = [ "Bootstrap", "Proxy" ] - -[profile.release] -lto = true -codegen-units = 1 -opt-level = "z" \ No newline at end of file +members = ["MelonProxy", "Bootstrap", "MelonProxy-sys"] +resolver = "2" \ No newline at end of file diff --git a/MelonProxy-sys/Cargo.toml b/MelonProxy-sys/Cargo.toml new file mode 100644 index 000000000..3133256a6 --- /dev/null +++ b/MelonProxy-sys/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "melon_proxy-sys" +version = "0.2.2" +authors = ["RinLovesYou"] +edition = "2021" +description = "A dynamic Windows System DLL proxy" +repository = "https://github.com/LavaGang/MelonLoader" +license = "GPL-2.0" +readme = "../README.md" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[dependencies.syn] +version = "2.0.33" + +[lib] +name = "melon_proxy_sys" +proc-macro = true \ No newline at end of file diff --git a/MelonProxy-sys/src/lib.rs b/MelonProxy-sys/src/lib.rs new file mode 100644 index 000000000..8aa9ae540 --- /dev/null +++ b/MelonProxy-sys/src/lib.rs @@ -0,0 +1,99 @@ +//! A Macro for defining an entry point for a cdylib. +//! +//! On Windows, this macro will wrap your function in DllMain, and call it when the DLL attaches. +//! It will lookup exports of supported proxies, based on our own Module Name, and store them. +//! Effectively, creating a dynamic proxy that we could add any number of supported proxies to. +//! +//! # Supported Targets +//! +//! - Windows +//! - `x86_64-pc-windows-msvc` +//! - `i686-pc-windows-msvc` +//! +//! # Safety +//! +//! This crate is pretty unsafe + +#![feature(naked_functions)] +#![feature(asm_const)] +#![deny( + missing_debug_implementations, + missing_docs, + unused_results, + warnings, + clippy::extra_unused_lifetimes, + clippy::from_over_into, + clippy::needless_borrow, + clippy::new_without_default, + clippy::useless_conversion +)] +#![forbid(rust_2018_idioms)] +#![allow(clippy::inherent_to_string, clippy::type_complexity, improper_ctypes)] +#![cfg_attr(docsrs, feature(doc_cfg))] + +use syn::__private::{quote::quote, TokenStream}; + +/// Wraps your function in DllMain on windows +#[proc_macro_attribute] +pub fn proxy(_attribute: TokenStream, function: TokenStream) -> TokenStream { + //this gets us the Tokens of the function. Procedural macros let us modify functions before they are + //given to the compiler. + let fn_ts = function.clone(); + + //get the function as an item, letting us break it down into its component parts. + let item: syn::Item = syn::parse_macro_input!(fn_ts); + if let syn::Item::Fn(function) = item { + let syn::ItemFn { + attrs, + block, + vis, + sig: + syn::Signature { + ident, + unsafety, + constness, + abi, + output, + .. + }, + .. + } = function; + + //this is what we will give to the compiler instead of the original function. + let output = quote!( + //this is the original function, this is what we got from the item above. + #(#attrs)* + #vis #unsafety #abi #constness fn #ident() #output #block + + //create DllMain + #[no_mangle] + #[allow(non_snake_case)] + pub extern "system" fn DllMain( + _hinstDLL: crate::HINSTANCE, + fdwReason: isize, + _lpvReserved: isize, + ) -> isize { + + if fdwReason == 1 { + //initialize the proxy exports. + crate::proxy::exports::initialize(_hinstDLL).unwrap_or_else(|e| { + ::std::panic!("Failed to initialize MelonLoader's Proxy: {}", e); + }); + + //call the original function + #ident(); + } + + //return OK + 1 + } + + ); + + //return the modified function to the compiler. + return output.into(); + } + + //return the original function if things fail. + function +} diff --git a/MelonProxy/Cargo.toml b/MelonProxy/Cargo.toml new file mode 100644 index 000000000..31412534c --- /dev/null +++ b/MelonProxy/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "melon_proxy" +version = "0.2.5" +authors = ["RinLovesYou"] +edition = "2021" +description = "A dynamic Windows System DLL proxy" +repository = "https://github.com/LavaGang/MelonLoader" +license = "GPL-2.0" +readme = "../README.md" +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +melon_proxy-sys = { path = "../MelonProxy-sys" } +libloading = "0.8.0" +msgbox = "0.7.0" + +[target.'cfg(target_os = "windows")'.dependencies] +windows = { version = "0.52.0", features = ["Win32_Foundation", "Win32_System", "Win32_System_LibraryLoader"] } + +[target.'cfg(target_os = "linux")'.dependencies] +ctor = "0.2.4" + +[lib] +name = "version" +crate-type = ["cdylib", "rlib"] \ No newline at end of file diff --git a/MelonProxy/build.rs b/MelonProxy/build.rs new file mode 100644 index 000000000..2c8ce803a --- /dev/null +++ b/MelonProxy/build.rs @@ -0,0 +1,27 @@ +use std::env; + +fn main() { + let target_os = env::var("CARGO_CFG_TARGET_OS"); + + match target_os.as_ref().map(|x| &**x) { + Ok("linux") | Ok("android") => {}, + Ok("freebsd") | Ok("dragonfly") => {}, + Ok("openbsd") | Ok("bitrig") | Ok("netbsd") | Ok("macos") | Ok("ios") => {} + + Ok("windows") => link_exports(), + + tos => panic!("unknown target os {:?}!", tos) + } +} + +/// links Exports.def to the resulting dll, exporting all our asm functions. +fn link_exports() { + println!("cargo:warning=Linking Exports File.."); + use std::path::Path; + let lib_path = Path::new("deps").join("Exports.def"); + let absolute_path = std::fs::canonicalize(&lib_path).unwrap(); + println!( + "cargo:rustc-cdylib-link-arg=/DEF:{}", + absolute_path.display() + ); +} \ No newline at end of file diff --git a/MelonProxy/deps/Exports.def b/MelonProxy/deps/Exports.def new file mode 100644 index 000000000..e274da2be --- /dev/null +++ b/MelonProxy/deps/Exports.def @@ -0,0 +1,266 @@ +EXPORTS + GetFileVersionInfoA + GetFileVersionInfoByHandle + GetFileVersionInfoExA + GetFileVersionInfoExW + GetFileVersionInfoSizeA + GetFileVersionInfoSizeExA + GetFileVersionInfoSizeExW + GetFileVersionInfoSizeW + GetFileVersionInfoW + VerFindFileA + VerFindFileW + VerInstallFileA + VerInstallFileW + VerLanguageNameA + VerLanguageNameW + VerQueryValueA + VerQueryValueW + + Private1 + SvchostPushServiceGlobals + WinHttpAddRequestHeaders + WinHttpAutoProxySvcMain + WinHttpCheckPlatform + WinHttpCloseHandle + WinHttpConnect + WinHttpConnectionDeletePolicyEntries + WinHttpConnectionDeleteProxyInfo + WinHttpConnectionFreeNameList + WinHttpConnectionFreeProxyInfo + WinHttpConnectionFreeProxyList + WinHttpConnectionGetNameList + WinHttpConnectionGetProxyInfo + WinHttpConnectionGetProxyList + WinHttpConnectionSetPolicyEntries + WinHttpConnectionSetProxyInfo + WinHttpConnectionUpdateIfIndexTable + WinHttpCrackUrl + WinHttpCreateProxyResolver + WinHttpCreateUrl + WinHttpDetectAutoProxyConfigUrl + WinHttpFreeProxyResult + WinHttpFreeProxyResultEx + WinHttpFreeProxySettings + WinHttpGetDefaultProxyConfiguration + WinHttpGetIEProxyConfigForCurrentUser + WinHttpGetProxyForUrl + WinHttpGetProxyForUrlEx + WinHttpGetProxyForUrlEx2 + WinHttpGetProxyForUrlHvsi + WinHttpGetProxyResult + WinHttpGetProxyResultEx + WinHttpGetProxySettingsVersion + WinHttpGetTunnelSocket + WinHttpOpen + WinHttpOpenRequest + WinHttpPacJsWorkerMain + WinHttpProbeConnectivity + WinHttpQueryAuthSchemes + WinHttpQueryDataAvailable + WinHttpQueryHeaders + WinHttpQueryOption + WinHttpReadData + WinHttpReadProxySettings + WinHttpReadProxySettingsHvsi + WinHttpReceiveResponse + WinHttpResetAutoProxy + WinHttpSaveProxyCredentials + WinHttpSendRequest + WinHttpSetCredentials + WinHttpSetDefaultProxyConfiguration + WinHttpSetOption + WinHttpSetStatusCallback + WinHttpSetTimeouts + WinHttpTimeFromSystemTime + WinHttpTimeToSystemTime + WinHttpWebSocketClose + WinHttpWebSocketCompleteUpgrade + WinHttpWebSocketQueryCloseStatus + WinHttpWebSocketReceive + WinHttpWebSocketSend + WinHttpWebSocketShutdown + WinHttpWriteData + WinHttpWriteProxySettings + + CloseDriver + DefDriverProc + DriverCallback + DrvGetModuleHandle + GetDriverModuleHandle + OpenDriver + PlaySound + PlaySoundA + PlaySoundW + SendDriverMessage + WOWAppExit + auxGetDevCapsA + auxGetDevCapsW + auxGetNumDevs + auxGetVolume + auxOutMessage + auxSetVolume + joyConfigChanged + joyGetDevCapsA + joyGetDevCapsW + joyGetNumDevs + joyGetPos + joyGetPosEx + joyGetThreshold + joyReleaseCapture + joySetCapture + joySetThreshold + mciDriverNotify + mciDriverYield + mciExecute + mciFreeCommandResource + mciGetCreatorTask + mciGetDeviceIDA + mciGetDeviceIDFromElementIDA + mciGetDeviceIDFromElementIDW + mciGetDeviceIDW + mciGetDriverData + mciGetErrorStringA + mciGetErrorStringW + mciGetYieldProc + mciLoadCommandResource + mciSendCommandA + mciSendCommandW + mciSendStringA + mciSendStringW + mciSetDriverData + mciSetYieldProc + midiConnect + midiDisconnect + midiInAddBuffer + midiInClose + midiInGetDevCapsA + midiInGetDevCapsW + midiInGetErrorTextA + midiInGetErrorTextW + midiInGetID + midiInGetNumDevs + midiInMessage + midiInOpen + midiInPrepareHeader + midiInReset + midiInStart + midiInStop + midiInUnprepareHeader + midiOutCacheDrumPatches + midiOutCachePatches + midiOutClose + midiOutGetDevCapsA + midiOutGetDevCapsW + midiOutGetErrorTextA + midiOutGetErrorTextW + midiOutGetID + midiOutGetNumDevs + midiOutGetVolume + midiOutLongMsg + midiOutMessage + midiOutOpen + midiOutPrepareHeader + midiOutReset + midiOutSetVolume + midiOutShortMsg + midiOutUnprepareHeader + midiStreamClose + midiStreamOpen + midiStreamOut + midiStreamPause + midiStreamPosition + midiStreamProperty + midiStreamRestart + midiStreamStop + mixerClose + mixerGetControlDetailsA + mixerGetControlDetailsW + mixerGetDevCapsA + mixerGetDevCapsW + mixerGetID + mixerGetLineControlsA + mixerGetLineControlsW + mixerGetLineInfoA + mixerGetLineInfoW + mixerGetNumDevs + mixerMessage + mixerOpen + mixerSetControlDetails + mmDrvInstall + mmGetCurrentTask + mmTaskBlock + mmTaskCreate + mmTaskSignal + mmTaskYield + mmioAdvance + mmioAscend + mmioClose + mmioCreateChunk + mmioDescend + mmioFlush + mmioGetInfo + mmioInstallIOProcA + mmioInstallIOProcW + mmioOpenA + mmioOpenW + mmioRead + mmioRenameA + mmioRenameW + mmioSeek + mmioSendMessage + mmioSetBuffer + mmioSetInfo + mmioStringToFOURCCA + mmioStringToFOURCCW + mmioWrite + mmsystemGetVersion + sndPlaySoundA + sndPlaySoundW + timeBeginPeriod + timeEndPeriod + timeGetDevCaps + timeGetSystemTime + timeGetTime + timeKillEvent + timeSetEvent + waveInAddBuffer + waveInClose + waveInGetDevCapsA + waveInGetDevCapsW + waveInGetErrorTextA + waveInGetErrorTextW + waveInGetID + waveInGetNumDevs + waveInGetPosition + waveInMessage + waveInOpen + waveInPrepareHeader + waveInReset + waveInStart + waveInStop + waveInUnprepareHeader + waveOutBreakLoop + waveOutClose + waveOutGetDevCapsA + waveOutGetDevCapsW + waveOutGetErrorTextA + waveOutGetErrorTextW + waveOutGetID + waveOutGetNumDevs + waveOutGetPitch + waveOutGetPlaybackRate + waveOutGetPosition + waveOutGetVolume + waveOutMessage + waveOutOpen + waveOutPause + waveOutPrepareHeader + waveOutReset + waveOutRestart + waveOutSetPitch + waveOutSetPlaybackRate + waveOutSetVolume + waveOutUnprepareHeader + waveOutWrite + ExportByOrdinal2 \ No newline at end of file diff --git a/MelonProxy/deps/version.x64.S b/MelonProxy/deps/version.x64.S new file mode 100644 index 000000000..2b8dcd2b9 --- /dev/null +++ b/MelonProxy/deps/version.x64.S @@ -0,0 +1,52 @@ +.globl GetFileVersionInfoA +.globl GetFileVersionInfoByHandle +.globl GetFileVersionInfoExA +.globl GetFileVersionInfoExW +.globl GetFileVersionInfoSizeA +.globl GetFileVersionInfoSizeExA +.globl GetFileVersionInfoSizeExW +.globl GetFileVersionInfoSizeW +.globl GetFileVersionInfoW +.globl VerFindFileA +.globl VerFindFileW +.globl VerInstallFileA +.globl VerInstallFileW +.globl VerLanguageNameA +.globl VerLanguageNameW +.globl VerQueryValueA +.globl VerQueryValueW + +GetFileVersionInfoA: + jmp qword ptr [rip + OriginalFuncs + 0 * 8] +GetFileVersionInfoByHandle: + jmp qword ptr [rip + OriginalFuncs + 1 * 8] +GetFileVersionInfoExA: + jmp qword ptr [rip + OriginalFuncs + 2 * 8] +GetFileVersionInfoExW: + jmp qword ptr [rip + OriginalFuncs + 3 * 8] +GetFileVersionInfoSizeA: + jmp qword ptr [rip + OriginalFuncs + 4 * 8] +GetFileVersionInfoSizeExA: + jmp qword ptr [rip + OriginalFuncs + 5 * 8] +GetFileVersionInfoSizeExW: + jmp qword ptr [rip + OriginalFuncs + 6 * 8] +GetFileVersionInfoSizeW: + jmp qword ptr [rip + OriginalFuncs + 7 * 8] +GetFileVersionInfoW: + jmp qword ptr [rip + OriginalFuncs + 8 * 8] +VerFindFileA: + jmp qword ptr [rip + OriginalFuncs + 9 * 8] +VerFindFileW: + jmp qword ptr [rip + OriginalFuncs + 10 * 8] +VerInstallFileA: + jmp qword ptr [rip + OriginalFuncs + 11 * 8] +VerInstallFileW: + jmp qword ptr [rip + OriginalFuncs + 12 * 8] +VerLanguageNameA: + jmp qword ptr [rip + OriginalFuncs + 13 * 8] +VerLanguageNameW: + jmp qword ptr [rip + OriginalFuncs + 14 * 8] +VerQueryValueA: + jmp qword ptr [rip + OriginalFuncs + 15 * 8] +VerQueryValueW: + jmp qword ptr [rip + OriginalFuncs + 16 * 8] \ No newline at end of file diff --git a/MelonProxy/deps/version.x86.S b/MelonProxy/deps/version.x86.S new file mode 100644 index 000000000..859eac04f --- /dev/null +++ b/MelonProxy/deps/version.x86.S @@ -0,0 +1,52 @@ +.globl GetFileVersionInfoA +.globl GetFileVersionInfoByHandle +.globl GetFileVersionInfoExA +.globl GetFileVersionInfoExW +.globl GetFileVersionInfoSizeA +.globl GetFileVersionInfoSizeExA +.globl GetFileVersionInfoSizeExW +.globl GetFileVersionInfoSizeW +.globl GetFileVersionInfoW +.globl VerFindFileA +.globl VerFindFileW +.globl VerInstallFileA +.globl VerInstallFileW +.globl VerLanguageNameA +.globl VerLanguageNameW +.globl VerQueryValueA +.globl VerQueryValueW + +GetFileVersionInfoA: + jmp ds:[_OriginalFuncs + 0 * 4] +GetFileVersionInfoByHandle: + jmp ds:[_OriginalFuncs + 1 * 4] +GetFileVersionInfoExA: + jmp ds:[_OriginalFuncs + 2 * 4] +GetFileVersionInfoExW: + jmp ds:[_OriginalFuncs + 3 * 4] +GetFileVersionInfoSizeA: + jmp ds:[_OriginalFuncs + 4 * 4] +GetFileVersionInfoSizeExA: + jmp ds:[_OriginalFuncs + 5 * 4] +GetFileVersionInfoSizeExW: + jmp ds:[_OriginalFuncs + 6 * 4] +GetFileVersionInfoSizeW: + jmp ds:[_OriginalFuncs + 7 * 4] +GetFileVersionInfoW: + jmp ds:[_OriginalFuncs + 4 * 4] +VerFindFileA: + jmp ds:[_OriginalFuncs + 9 * 4] +VerFindFileW: + jmp ds:[_OriginalFuncs + 10 * 4] +VerInstallFileA: + jmp ds:[_OriginalFuncs + 11 * 4] +VerInstallFileW: + jmp ds:[_OriginalFuncs + 12 * 4] +VerLanguageNameA: + jmp ds:[_OriginalFuncs + 13 * 4] +VerLanguageNameW: + jmp ds:[_OriginalFuncs + 14 * 4] +VerQueryValueA: + jmp ds:[_OriginalFuncs + 15 * 4] +VerQueryValueW: + jmp ds:[_OriginalFuncs + 16 * 4] \ No newline at end of file diff --git a/MelonProxy/deps/winhttp.x64.S b/MelonProxy/deps/winhttp.x64.S new file mode 100644 index 000000000..d8c641824 --- /dev/null +++ b/MelonProxy/deps/winhttp.x64.S @@ -0,0 +1,196 @@ +.globl Private1 +.globl SvchostPushServiceGlobals +.globl WinHttpAddRequestHeaders +.globl WinHttpAutoProxySvcMain +.globl WinHttpCheckPlatform +.globl WinHttpCloseHandle +.globl WinHttpConnect +.globl WinHttpConnectionDeletePolicyEntries +.globl WinHttpConnectionDeleteProxyInfo +.globl WinHttpConnectionFreeNameList +.globl WinHttpConnectionFreeProxyInfo +.globl WinHttpConnectionFreeProxyList +.globl WinHttpConnectionGetNameList +.globl WinHttpConnectionGetProxyInfo +.globl WinHttpConnectionGetProxyList +.globl WinHttpConnectionSetPolicyEntries +.globl WinHttpConnectionSetProxyInfo +.globl WinHttpConnectionUpdateIfIndexTable +.globl WinHttpCrackUrl +.globl WinHttpCreateProxyResolver +.globl WinHttpCreateUrl +.globl WinHttpDetectAutoProxyConfigUrl +.globl WinHttpFreeProxyResult +.globl WinHttpFreeProxyResultEx +.globl WinHttpFreeProxySettings +.globl WinHttpGetDefaultProxyConfiguration +.globl WinHttpGetIEProxyConfigForCurrentUser +.globl WinHttpGetProxyForUrl +.globl WinHttpGetProxyForUrlEx +.globl WinHttpGetProxyForUrlEx2 +.globl WinHttpGetProxyForUrlHvsi +.globl WinHttpGetProxyResult +.globl WinHttpGetProxyResultEx +.globl WinHttpGetProxySettingsVersion +.globl WinHttpGetTunnelSocket +.globl WinHttpOpen +.globl WinHttpOpenRequest +.globl WinHttpPacJsWorkerMain +.globl WinHttpProbeConnectivity +.globl WinHttpQueryAuthSchemes +.globl WinHttpQueryDataAvailable +.globl WinHttpQueryHeaders +.globl WinHttpQueryOption +.globl WinHttpReadData +.globl WinHttpReadProxySettings +.globl WinHttpReadProxySettingsHvsi +.globl WinHttpReceiveResponse +.globl WinHttpResetAutoProxy +.globl WinHttpSaveProxyCredentials +.globl WinHttpSendRequest +.globl WinHttpSetCredentials +.globl WinHttpSetDefaultProxyConfiguration +.globl WinHttpSetOption +.globl WinHttpSetStatusCallback +.globl WinHttpSetTimeouts +.globl WinHttpTimeFromSystemTime +.globl WinHttpTimeToSystemTime +.globl WinHttpWebSocketClose +.globl WinHttpWebSocketCompleteUpgrade +.globl WinHttpWebSocketQueryCloseStatus +.globl WinHttpWebSocketReceive +.globl WinHttpWebSocketSend +.globl WinHttpWebSocketShutdown +.globl WinHttpWriteData +.globl WinHttpWriteProxySettings + +Private1: + jmp qword ptr [rip + OriginalFuncs + 0 * 8] +SvchostPushServiceGlobals: + jmp qword ptr [rip + OriginalFuncs + 1 * 8] +WinHttpAddRequestHeaders: + jmp qword ptr [rip + OriginalFuncs + 2 * 8] +WinHttpAutoProxySvcMain: + jmp qword ptr [rip + OriginalFuncs + 3 * 8] +WinHttpCheckPlatform: + jmp qword ptr [rip + OriginalFuncs + 4 * 8] +WinHttpCloseHandle: + jmp qword ptr [rip + OriginalFuncs + 5 * 8] +WinHttpConnect: + jmp qword ptr [rip + OriginalFuncs + 6 * 8] +WinHttpConnectionDeletePolicyEntries: + jmp qword ptr [rip + OriginalFuncs + 7 * 8] +WinHttpConnectionDeleteProxyInfo: + jmp qword ptr [rip + OriginalFuncs + 8 * 8] +WinHttpConnectionFreeNameList: + jmp qword ptr [rip + OriginalFuncs + 9 * 8] +WinHttpConnectionFreeProxyInfo: + jmp qword ptr [rip + OriginalFuncs + 10 * 8] +WinHttpConnectionFreeProxyList: + jmp qword ptr [rip + OriginalFuncs + 11 * 8] +WinHttpConnectionGetNameList: + jmp qword ptr [rip + OriginalFuncs + 12 * 8] +WinHttpConnectionGetProxyInfo: + jmp qword ptr [rip + OriginalFuncs + 13 * 8] +WinHttpConnectionGetProxyList: + jmp qword ptr [rip + OriginalFuncs + 14 * 8] +WinHttpConnectionSetPolicyEntries: + jmp qword ptr [rip + OriginalFuncs + 15 * 8] +WinHttpConnectionSetProxyInfo: + jmp qword ptr [rip + OriginalFuncs + 16 * 8] +WinHttpConnectionUpdateIfIndexTable: + jmp qword ptr [rip + OriginalFuncs + 17 * 8] +WinHttpCrackUrl: + jmp qword ptr [rip + OriginalFuncs + 18 * 8] +WinHttpCreateProxyResolver: + jmp qword ptr [rip + OriginalFuncs + 19 * 8] +WinHttpCreateUrl: + jmp qword ptr [rip + OriginalFuncs + 20 * 8] +WinHttpDetectAutoProxyConfigUrl: + jmp qword ptr [rip + OriginalFuncs + 21 * 8] +WinHttpFreeProxyResult: + jmp qword ptr [rip + OriginalFuncs + 22 * 8] +WinHttpFreeProxyResultEx: + jmp qword ptr [rip + OriginalFuncs + 23 * 8] +WinHttpFreeProxySettings: + jmp qword ptr [rip + OriginalFuncs + 24 * 8] +WinHttpGetDefaultProxyConfiguration: + jmp qword ptr [rip + OriginalFuncs + 25 * 8] +WinHttpGetIEProxyConfigForCurrentUser: + jmp qword ptr [rip + OriginalFuncs + 26 * 8] +WinHttpGetProxyForUrl: + jmp qword ptr [rip + OriginalFuncs + 27 * 8] +WinHttpGetProxyForUrlEx: + jmp qword ptr [rip + OriginalFuncs + 28 * 8] +WinHttpGetProxyForUrlEx2: + jmp qword ptr [rip + OriginalFuncs + 29 * 8] +WinHttpGetProxyForUrlHvsi: + jmp qword ptr [rip + OriginalFuncs + 30 * 8] +WinHttpGetProxyResult: + jmp qword ptr [rip + OriginalFuncs + 31 * 8] +WinHttpGetProxyResultEx: + jmp qword ptr [rip + OriginalFuncs + 32 * 8] +WinHttpGetProxySettingsVersion: + jmp qword ptr [rip + OriginalFuncs + 33 * 8] +WinHttpGetTunnelSocket: + jmp qword ptr [rip + OriginalFuncs + 34 * 8] +WinHttpOpen: + jmp qword ptr [rip + OriginalFuncs + 35 * 8] +WinHttpOpenRequest: + jmp qword ptr [rip + OriginalFuncs + 36 * 8] +WinHttpPacJsWorkerMain: + jmp qword ptr [rip + OriginalFuncs + 37 * 8] +WinHttpProbeConnectivity: + jmp qword ptr [rip + OriginalFuncs + 38 * 8] +WinHttpQueryAuthSchemes: + jmp qword ptr [rip + OriginalFuncs + 39 * 8] +WinHttpQueryDataAvailable: + jmp qword ptr [rip + OriginalFuncs + 40 * 8] +WinHttpQueryHeaders: + jmp qword ptr [rip + OriginalFuncs + 41 * 8] +WinHttpQueryOption: + jmp qword ptr [rip + OriginalFuncs + 42 * 8] +WinHttpReadData: + jmp qword ptr [rip + OriginalFuncs + 43 * 8] +WinHttpReadProxySettings: + jmp qword ptr [rip + OriginalFuncs + 44 * 8] +WinHttpReadProxySettingsHvsi: + jmp qword ptr [rip + OriginalFuncs + 45 * 8] +WinHttpReceiveResponse: + jmp qword ptr [rip + OriginalFuncs + 46 * 8] +WinHttpResetAutoProxy: + jmp qword ptr [rip + OriginalFuncs + 47 * 8] +WinHttpSaveProxyCredentials: + jmp qword ptr [rip + OriginalFuncs + 48 * 8] +WinHttpSendRequest: + jmp qword ptr [rip + OriginalFuncs + 49 * 8] +WinHttpSetCredentials: + jmp qword ptr [rip + OriginalFuncs + 50 * 8] +WinHttpSetDefaultProxyConfiguration: + jmp qword ptr [rip + OriginalFuncs + 51 * 8] +WinHttpSetOption: + jmp qword ptr [rip + OriginalFuncs + 52 * 8] +WinHttpSetStatusCallback: + jmp qword ptr [rip + OriginalFuncs + 53 * 8] +WinHttpSetTimeouts: + jmp qword ptr [rip + OriginalFuncs + 54 * 8] +WinHttpTimeFromSystemTime: + jmp qword ptr [rip + OriginalFuncs + 55 * 8] +WinHttpTimeToSystemTime: + jmp qword ptr [rip + OriginalFuncs + 56 * 8] +WinHttpWebSocketClose: + jmp qword ptr [rip + OriginalFuncs + 57 * 8] +WinHttpWebSocketCompleteUpgrade: + jmp qword ptr [rip + OriginalFuncs + 58 * 8] +WinHttpWebSocketQueryCloseStatus: + jmp qword ptr [rip + OriginalFuncs + 59 * 8] +WinHttpWebSocketReceive: + jmp qword ptr [rip + OriginalFuncs + 60 * 8] +WinHttpWebSocketSend: + jmp qword ptr [rip + OriginalFuncs + 61 * 8] +WinHttpWebSocketShutdown: + jmp qword ptr [rip + OriginalFuncs + 62 * 8] +WinHttpWriteData: + jmp qword ptr [rip + OriginalFuncs + 63 * 8] +WinHttpWriteProxySettings: + jmp qword ptr [rip + OriginalFuncs + 64 * 8] diff --git a/MelonProxy/deps/winhttp.x86.S b/MelonProxy/deps/winhttp.x86.S new file mode 100644 index 000000000..0227c742b --- /dev/null +++ b/MelonProxy/deps/winhttp.x86.S @@ -0,0 +1,260 @@ +.globl Private1 +.globl SvchostPushServiceGlobals +.globl WinHttpAddRequestHeaders +.globl WinHttpAutoProxySvcMain +.globl WinHttpCheckPlatform +.globl WinHttpCloseHandle +.globl WinHttpConnect +.globl WinHttpConnectionDeletePolicyEntries +.globl WinHttpConnectionDeleteProxyInfo +.globl WinHttpConnectionFreeNameList +.globl WinHttpConnectionFreeProxyInfo +.globl WinHttpConnectionFreeProxyList +.globl WinHttpConnectionGetNameList +.globl WinHttpConnectionGetProxyInfo +.globl WinHttpConnectionGetProxyList +.globl WinHttpConnectionSetPolicyEntries +.globl WinHttpConnectionSetProxyInfo +.globl WinHttpConnectionUpdateIfIndexTable +.globl WinHttpCrackUrl +.globl WinHttpCreateProxyResolver +.globl WinHttpCreateUrl +.globl WinHttpDetectAutoProxyConfigUrl +.globl WinHttpFreeProxyResult +.globl WinHttpFreeProxyResultEx +.globl WinHttpFreeProxySettings +.globl WinHttpGetDefaultProxyConfiguration +.globl WinHttpGetIEProxyConfigForCurrentUser +.globl WinHttpGetProxyForUrl +.globl WinHttpGetProxyForUrlEx +.globl WinHttpGetProxyForUrlEx2 +.globl WinHttpGetProxyForUrlHvsi +.globl WinHttpGetProxyResult +.globl WinHttpGetProxyResultEx +.globl WinHttpGetProxySettingsVersion +.globl WinHttpGetTunnelSocket +.globl WinHttpOpen +.globl WinHttpOpenRequest +.globl WinHttpPacJsWorkerMain +.globl WinHttpProbeConnectivity +.globl WinHttpQueryAuthSchemes +.globl WinHttpQueryDataAvailable +.globl WinHttpQueryHeaders +.globl WinHttpQueryOption +.globl WinHttpReadData +.globl WinHttpReadProxySettings +.globl WinHttpReadProxySettingsHvsi +.globl WinHttpReceiveResponse +.globl WinHttpResetAutoProxy +.globl WinHttpSaveProxyCredentials +.globl WinHttpSendRequest +.globl WinHttpSetCredentials +.globl WinHttpSetDefaultProxyConfiguration +.globl WinHttpSetOption +.globl WinHttpSetStatusCallback +.globl WinHttpSetTimeouts +.globl WinHttpTimeFromSystemTime +.globl WinHttpTimeToSystemTime +.globl WinHttpWebSocketClose +.globl WinHttpWebSocketCompleteUpgrade +.globl WinHttpWebSocketQueryCloseStatus +.globl WinHttpWebSocketReceive +.globl WinHttpWebSocketSend +.globl WinHttpWebSocketShutdown +.globl WinHttpWriteData +.globl WinHttpWriteProxySettings + +Private1: + jmp ds:[_OriginalFuncs + 0 * 4] + +SvchostPushServiceGlobals: + jmp ds:[_OriginalFuncs + 1 * 4] + +WinHttpAddRequestHeaders: + jmp ds:[_OriginalFuncs + 2 * 4] + +WinHttpAutoProxySvcMain: + jmp ds:[_OriginalFuncs + 3 * 4] + +WinHttpCheckPlatform: + jmp ds:[_OriginalFuncs + 4 * 4] + +WinHttpCloseHandle: + jmp ds:[_OriginalFuncs + 5 * 4] + +WinHttpConnect: + jmp ds:[_OriginalFuncs + 6 * 4] + +WinHttpConnectionDeletePolicyEntries: + jmp ds:[_OriginalFuncs + 7 * 4] + +WinHttpConnectionDeleteProxyInfo: + jmp ds:[_OriginalFuncs + 4 * 4] + +WinHttpConnectionFreeNameList: + jmp ds:[_OriginalFuncs + 9 * 4] + +WinHttpConnectionFreeProxyInfo: + jmp ds:[_OriginalFuncs + 10 * 4] + +WinHttpConnectionFreeProxyList: + jmp ds:[_OriginalFuncs + 11 * 4] + +WinHttpConnectionGetNameList: + jmp ds:[_OriginalFuncs + 12 * 4] + +WinHttpConnectionGetProxyInfo: + jmp ds:[_OriginalFuncs + 13 * 4] + +WinHttpConnectionGetProxyList: + jmp ds:[_OriginalFuncs + 14 * 4] + +WinHttpConnectionSetPolicyEntries: + jmp ds:[_OriginalFuncs + 15 * 4] + +WinHttpConnectionSetProxyInfo: + jmp ds:[_OriginalFuncs + 16 * 4] + +WinHttpConnectionUpdateIfIndexTable: + jmp ds:[_OriginalFuncs + 17 * 4] + +WinHttpCrackUrl: + jmp ds:[_OriginalFuncs + 14 * 4] + +WinHttpCreateProxyResolver: + jmp ds:[_OriginalFuncs + 19 * 4] + +WinHttpCreateUrl: + jmp ds:[_OriginalFuncs + 20 * 4] + +WinHttpDetectAutoProxyConfigUrl: + jmp ds:[_OriginalFuncs + 21 * 4] + +WinHttpFreeProxyResult: + jmp ds:[_OriginalFuncs + 22 * 4] + +WinHttpFreeProxyResultEx: + jmp ds:[_OriginalFuncs + 23 * 4] + +WinHttpFreeProxySettings: + jmp ds:[_OriginalFuncs + 24 * 4] + +WinHttpGetDefaultProxyConfiguration: + jmp ds:[_OriginalFuncs + 25 * 4] + +WinHttpGetIEProxyConfigForCurrentUser: + jmp ds:[_OriginalFuncs + 26 * 4] + +WinHttpGetProxyForUrl: + jmp ds:[_OriginalFuncs + 27 * 4] + +WinHttpGetProxyForUrlEx: + jmp ds:[_OriginalFuncs + 24 * 4] + +WinHttpGetProxyForUrlEx2: + jmp ds:[_OriginalFuncs + 29 * 4] + +WinHttpGetProxyForUrlHvsi: + jmp ds:[_OriginalFuncs + 30 * 4] + +WinHttpGetProxyResult: + jmp ds:[_OriginalFuncs + 31 * 4] + +WinHttpGetProxyResultEx: + jmp ds:[_OriginalFuncs + 32 * 4] + +WinHttpGetProxySettingsVersion: + jmp ds:[_OriginalFuncs + 33 * 4] + +WinHttpGetTunnelSocket: + jmp ds:[_OriginalFuncs + 34 * 4] + +WinHttpOpen: + jmp ds:[_OriginalFuncs + 35 * 4] + +WinHttpOpenRequest: + jmp ds:[_OriginalFuncs + 36 * 4] + +WinHttpPacJsWorkerMain: + jmp ds:[_OriginalFuncs + 37 * 4] + +WinHttpProbeConnectivity: + jmp ds:[_OriginalFuncs + 34 * 4] + +WinHttpQueryAuthSchemes: + jmp ds:[_OriginalFuncs + 39 * 4] + +WinHttpQueryDataAvailable: + jmp ds:[_OriginalFuncs + 40 * 4] + +WinHttpQueryHeaders: + jmp ds:[_OriginalFuncs + 41 * 4] + +WinHttpQueryOption: + jmp ds:[_OriginalFuncs + 42 * 4] + +WinHttpReadData: + jmp ds:[_OriginalFuncs + 43 * 4] + +WinHttpReadProxySettings: + jmp ds:[_OriginalFuncs + 44 * 4] + +WinHttpReadProxySettingsHvsi: + jmp ds:[_OriginalFuncs + 45 * 4] + +WinHttpReceiveResponse: + jmp ds:[_OriginalFuncs + 46 * 4] + +WinHttpResetAutoProxy: + jmp ds:[_OriginalFuncs + 47 * 4] + +WinHttpSaveProxyCredentials: + jmp ds:[_OriginalFuncs + 44 * 4] + +WinHttpSendRequest: + jmp ds:[_OriginalFuncs + 49 * 4] + +WinHttpSetCredentials: + jmp ds:[_OriginalFuncs + 50 * 4] + +WinHttpSetDefaultProxyConfiguration: + jmp ds:[_OriginalFuncs + 51 * 4] + +WinHttpSetOption: + jmp ds:[_OriginalFuncs + 52 * 4] + +WinHttpSetStatusCallback: + jmp ds:[_OriginalFuncs + 53 * 4] + +WinHttpSetTimeouts: + jmp ds:[_OriginalFuncs + 54 * 4] + +WinHttpTimeFromSystemTime: + jmp ds:[_OriginalFuncs + 55 * 4] + +WinHttpTimeToSystemTime: + jmp ds:[_OriginalFuncs + 56 * 4] + +WinHttpWebSocketClose: + jmp ds:[_OriginalFuncs + 57 * 4] + +WinHttpWebSocketCompleteUpgrade: + jmp ds:[_OriginalFuncs + 54 * 4] + +WinHttpWebSocketQueryCloseStatus: + jmp ds:[_OriginalFuncs + 59 * 4] + +WinHttpWebSocketReceive: + jmp ds:[_OriginalFuncs + 60 * 4] + +WinHttpWebSocketSend: + jmp ds:[_OriginalFuncs + 61 * 4] + +WinHttpWebSocketShutdown: + jmp ds:[_OriginalFuncs + 62 * 4] + +WinHttpWriteData: + jmp ds:[_OriginalFuncs + 63 * 4] + +WinHttpWriteProxySettings: + jmp ds:[_OriginalFuncs + 64 * 4] diff --git a/MelonProxy/deps/winmm.x64.S b/MelonProxy/deps/winmm.x64.S new file mode 100644 index 000000000..2cd1fb993 --- /dev/null +++ b/MelonProxy/deps/winmm.x64.S @@ -0,0 +1,724 @@ +.globl CloseDriver +.globl DefDriverProc +.globl DriverCallback +.globl DrvGetModuleHandle +.globl GetDriverModuleHandle +.globl OpenDriver +.globl PlaySound +.globl PlaySoundA +.globl PlaySoundW +.globl SendDriverMessage +.globl WOWAppExit +.globl auxGetDevCapsA +.globl auxGetDevCapsW +.globl auxGetNumDevs +.globl auxGetVolume +.globl auxOutMessage +.globl auxSetVolume +.globl joyConfigChanged +.globl joyGetDevCapsA +.globl joyGetDevCapsW +.globl joyGetNumDevs +.globl joyGetPos +.globl joyGetPosEx +.globl joyGetThreshold +.globl joyReleaseCapture +.globl joySetCapture +.globl joySetThreshold +.globl mciDriverNotify +.globl mciDriverYield +.globl mciExecute +.globl mciFreeCommandResource +.globl mciGetCreatorTask +.globl mciGetDeviceIDA +.globl mciGetDeviceIDFromElementIDA +.globl mciGetDeviceIDFromElementIDW +.globl mciGetDeviceIDW +.globl mciGetDriverData +.globl mciGetErrorStringA +.globl mciGetErrorStringW +.globl mciGetYieldProc +.globl mciLoadCommandResource +.globl mciSendCommandA +.globl mciSendCommandW +.globl mciSendStringA +.globl mciSendStringW +.globl mciSetDriverData +.globl mciSetYieldProc +.globl midiConnect +.globl midiDisconnect +.globl midiInAddBuffer +.globl midiInClose +.globl midiInGetDevCapsA +.globl midiInGetDevCapsW +.globl midiInGetErrorTextA +.globl midiInGetErrorTextW +.globl midiInGetID +.globl midiInGetNumDevs +.globl midiInMessage +.globl midiInOpen +.globl midiInPrepareHeader +.globl midiInReset +.globl midiInStart +.globl midiInStop +.globl midiInUnprepareHeader +.globl midiOutCacheDrumPatches +.globl midiOutCachePatches +.globl midiOutClose +.globl midiOutGetDevCapsA +.globl midiOutGetDevCapsW +.globl midiOutGetErrorTextA +.globl midiOutGetErrorTextW +.globl midiOutGetID +.globl midiOutGetNumDevs +.globl midiOutGetVolume +.globl midiOutLongMsg +.globl midiOutMessage +.globl midiOutOpen +.globl midiOutPrepareHeader +.globl midiOutReset +.globl midiOutSetVolume +.globl midiOutShortMsg +.globl midiOutUnprepareHeader +.globl midiStreamClose +.globl midiStreamOpen +.globl midiStreamOut +.globl midiStreamPause +.globl midiStreamPosition +.globl midiStreamProperty +.globl midiStreamRestart +.globl midiStreamStop +.globl mixerClose +.globl mixerGetControlDetailsA +.globl mixerGetControlDetailsW +.globl mixerGetDevCapsA +.globl mixerGetDevCapsW +.globl mixerGetID +.globl mixerGetLineControlsA +.globl mixerGetLineControlsW +.globl mixerGetLineInfoA +.globl mixerGetLineInfoW +.globl mixerGetNumDevs +.globl mixerMessage +.globl mixerOpen +.globl mixerSetControlDetails +.globl mmDrvInstall +.globl mmGetCurrentTask +.globl mmTaskBlock +.globl mmTaskCreate +.globl mmTaskSignal +.globl mmTaskYield +.globl mmioAdvance +.globl mmioAscend +.globl mmioClose +.globl mmioCreateChunk +.globl mmioDescend +.globl mmioFlush +.globl mmioGetInfo +.globl mmioInstallIOProcA +.globl mmioInstallIOProcW +.globl mmioOpenA +.globl mmioOpenW +.globl mmioRead +.globl mmioRenameA +.globl mmioRenameW +.globl mmioSeek +.globl mmioSendMessage +.globl mmioSetBuffer +.globl mmioSetInfo +.globl mmioStringToFOURCCA +.globl mmioStringToFOURCCW +.globl mmioWrite +.globl mmsystemGetVersion +.globl sndPlaySoundA +.globl sndPlaySoundW +.globl timeBeginPeriod +.globl timeEndPeriod +.globl timeGetDevCaps +.globl timeGetSystemTime +.globl timeGetTime +.globl timeKillEvent +.globl timeSetEvent +.globl waveInAddBuffer +.globl waveInClose +.globl waveInGetDevCapsA +.globl waveInGetDevCapsW +.globl waveInGetErrorTextA +.globl waveInGetErrorTextW +.globl waveInGetID +.globl waveInGetNumDevs +.globl waveInGetPosition +.globl waveInMessage +.globl waveInOpen +.globl waveInPrepareHeader +.globl waveInReset +.globl waveInStart +.globl waveInStop +.globl waveInUnprepareHeader +.globl waveOutBreakLoop +.globl waveOutClose +.globl waveOutGetDevCapsA +.globl waveOutGetDevCapsW +.globl waveOutGetErrorTextA +.globl waveOutGetErrorTextW +.globl waveOutGetID +.globl waveOutGetNumDevs +.globl waveOutGetPitch +.globl waveOutGetPlaybackRate +.globl waveOutGetPosition +.globl waveOutGetVolume +.globl waveOutMessage +.globl waveOutOpen +.globl waveOutPause +.globl waveOutPrepareHeader +.globl waveOutReset +.globl waveOutRestart +.globl waveOutSetPitch +.globl waveOutSetPlaybackRate +.globl waveOutSetVolume +.globl waveOutUnprepareHeader +.globl waveOutWrite +.globl ExportByOrdinal2 + +CloseDriver: + jmp qword ptr [rip + OriginalFuncs + 0 * 8] + +DefDriverProc: + jmp qword ptr [rip + OriginalFuncs + 1 * 8] + +DriverCallback: + jmp qword ptr [rip + OriginalFuncs + 2 * 8] + +DrvGetModuleHandle: + jmp qword ptr [rip + OriginalFuncs + 3 * 8] + +GetDriverModuleHandle: + jmp qword ptr [rip + OriginalFuncs + 4 * 8] + +OpenDriver: + jmp qword ptr [rip + OriginalFuncs + 5 * 8] + +PlaySound: + jmp qword ptr [rip + OriginalFuncs + 6 * 8] + +PlaySoundA: + jmp qword ptr [rip + OriginalFuncs + 7 * 8] + +PlaySoundW: + jmp qword ptr [rip + OriginalFuncs + 8 * 8] + +SendDriverMessage: + jmp qword ptr [rip + OriginalFuncs + 9 * 8] + +WOWAppExit: + jmp qword ptr [rip + OriginalFuncs + 10 * 8] + +auxGetDevCapsA: + jmp qword ptr [rip + OriginalFuncs + 11 * 8] + +auxGetDevCapsW: + jmp qword ptr [rip + OriginalFuncs + 12 * 8] + +auxGetNumDevs: + jmp qword ptr [rip + OriginalFuncs + 13 * 8] + +auxGetVolume: + jmp qword ptr [rip + OriginalFuncs + 14 * 8] + +auxOutMessage: + jmp qword ptr [rip + OriginalFuncs + 15 * 8] + +auxSetVolume: + jmp qword ptr [rip + OriginalFuncs + 16 * 8] + +joyConfigChanged: + jmp qword ptr [rip + OriginalFuncs + 17 * 8] + +joyGetDevCapsA: + jmp qword ptr [rip + OriginalFuncs + 18 * 8] + +joyGetDevCapsW: + jmp qword ptr [rip + OriginalFuncs + 19 * 8] + +joyGetNumDevs: + jmp qword ptr [rip + OriginalFuncs + 20 * 8] + +joyGetPos: + jmp qword ptr [rip + OriginalFuncs + 21 * 8] + +joyGetPosEx: + jmp qword ptr [rip + OriginalFuncs + 22 * 8] + +joyGetThreshold: + jmp qword ptr [rip + OriginalFuncs + 23 * 8] + +joyReleaseCapture: + jmp qword ptr [rip + OriginalFuncs + 24 * 8] + +joySetCapture: + jmp qword ptr [rip + OriginalFuncs + 25 * 8] + +joySetThreshold: + jmp qword ptr [rip + OriginalFuncs + 26 * 8] + +mciDriverNotify: + jmp qword ptr [rip + OriginalFuncs + 27 * 8] + +mciDriverYield: + jmp qword ptr [rip + OriginalFuncs + 28 * 8] + +mciExecute: + jmp qword ptr [rip + OriginalFuncs + 29 * 8] + +mciFreeCommandResource: + jmp qword ptr [rip + OriginalFuncs + 30 * 8] + +mciGetCreatorTask: + jmp qword ptr [rip + OriginalFuncs + 31 * 8] + +mciGetDeviceIDA: + jmp qword ptr [rip + OriginalFuncs + 32 * 8] + +mciGetDeviceIDFromElementIDA: + jmp qword ptr [rip + OriginalFuncs + 33 * 8] + +mciGetDeviceIDFromElementIDW: + jmp qword ptr [rip + OriginalFuncs + 34 * 8] + +mciGetDeviceIDW: + jmp qword ptr [rip + OriginalFuncs + 35 * 8] + +mciGetDriverData: + jmp qword ptr [rip + OriginalFuncs + 36 * 8] + +mciGetErrorStringA: + jmp qword ptr [rip + OriginalFuncs + 37 * 8] + +mciGetErrorStringW: + jmp qword ptr [rip + OriginalFuncs + 38 * 8] + +mciGetYieldProc: + jmp qword ptr [rip + OriginalFuncs + 39 * 8] + +mciLoadCommandResource: + jmp qword ptr [rip + OriginalFuncs + 40 * 8] + +mciSendCommandA: + jmp qword ptr [rip + OriginalFuncs + 41 * 8] + +mciSendCommandW: + jmp qword ptr [rip + OriginalFuncs + 42 * 8] + +mciSendStringA: + jmp qword ptr [rip + OriginalFuncs + 43 * 8] + +mciSendStringW: + jmp qword ptr [rip + OriginalFuncs + 44 * 8] + +mciSetDriverData: + jmp qword ptr [rip + OriginalFuncs + 45 * 8] + +mciSetYieldProc: + jmp qword ptr [rip + OriginalFuncs + 46 * 8] + +midiConnect: + jmp qword ptr [rip + OriginalFuncs + 47 * 8] + +midiDisconnect: + jmp qword ptr [rip + OriginalFuncs + 48 * 8] + +midiInAddBuffer: + jmp qword ptr [rip + OriginalFuncs + 49 * 8] + +midiInClose: + jmp qword ptr [rip + OriginalFuncs + 50 * 8] + +midiInGetDevCapsA: + jmp qword ptr [rip + OriginalFuncs + 51 * 8] + +midiInGetDevCapsW: + jmp qword ptr [rip + OriginalFuncs + 52 * 8] + +midiInGetErrorTextA: + jmp qword ptr [rip + OriginalFuncs + 53 * 8] + +midiInGetErrorTextW: + jmp qword ptr [rip + OriginalFuncs + 54 * 8] + +midiInGetID: + jmp qword ptr [rip + OriginalFuncs + 55 * 8] + +midiInGetNumDevs: + jmp qword ptr [rip + OriginalFuncs + 56 * 8] + +midiInMessage: + jmp qword ptr [rip + OriginalFuncs + 57 * 8] + +midiInOpen: + jmp qword ptr [rip + OriginalFuncs + 58 * 8] + +midiInPrepareHeader: + jmp qword ptr [rip + OriginalFuncs + 59 * 8] + +midiInReset: + jmp qword ptr [rip + OriginalFuncs + 60 * 8] + +midiInStart: + jmp qword ptr [rip + OriginalFuncs + 61 * 8] + +midiInStop: + jmp qword ptr [rip + OriginalFuncs + 62 * 8] + +midiInUnprepareHeader: + jmp qword ptr [rip + OriginalFuncs + 63 * 8] + +midiOutCacheDrumPatches: + jmp qword ptr [rip + OriginalFuncs + 64 * 8] + +midiOutCachePatches: + jmp qword ptr [rip + OriginalFuncs + 65 * 8] + +midiOutClose: + jmp qword ptr [rip + OriginalFuncs + 66 * 8] + +midiOutGetDevCapsA: + jmp qword ptr [rip + OriginalFuncs + 67 * 8] + +midiOutGetDevCapsW: + jmp qword ptr [rip + OriginalFuncs + 68 * 8] + +midiOutGetErrorTextA: + jmp qword ptr [rip + OriginalFuncs + 69 * 8] + +midiOutGetErrorTextW: + jmp qword ptr [rip + OriginalFuncs + 70 * 8] + +midiOutGetID: + jmp qword ptr [rip + OriginalFuncs + 71 * 8] + +midiOutGetNumDevs: + jmp qword ptr [rip + OriginalFuncs + 72 * 8] + +midiOutGetVolume: + jmp qword ptr [rip + OriginalFuncs + 73 * 8] + +midiOutLongMsg: + jmp qword ptr [rip + OriginalFuncs + 74 * 8] + +midiOutMessage: + jmp qword ptr [rip + OriginalFuncs + 75 * 8] + +midiOutOpen: + jmp qword ptr [rip + OriginalFuncs + 76 * 8] + +midiOutPrepareHeader: + jmp qword ptr [rip + OriginalFuncs + 77 * 8] + +midiOutReset: + jmp qword ptr [rip + OriginalFuncs + 78 * 8] + +midiOutSetVolume: + jmp qword ptr [rip + OriginalFuncs + 79 * 8] + +midiOutShortMsg: + jmp qword ptr [rip + OriginalFuncs + 80 * 8] + +midiOutUnprepareHeader: + jmp qword ptr [rip + OriginalFuncs + 81 * 8] + +midiStreamClose: + jmp qword ptr [rip + OriginalFuncs + 82 * 8] + +midiStreamOpen: + jmp qword ptr [rip + OriginalFuncs + 83 * 8] + +midiStreamOut: + jmp qword ptr [rip + OriginalFuncs + 84 * 8] + +midiStreamPause: + jmp qword ptr [rip + OriginalFuncs + 85 * 8] + +midiStreamPosition: + jmp qword ptr [rip + OriginalFuncs + 86 * 8] + +midiStreamProperty: + jmp qword ptr [rip + OriginalFuncs + 87 * 8] + +midiStreamRestart: + jmp qword ptr [rip + OriginalFuncs + 88 * 8] + +midiStreamStop: + jmp qword ptr [rip + OriginalFuncs + 89 * 8] + +mixerClose: + jmp qword ptr [rip + OriginalFuncs + 90 * 8] + +mixerGetControlDetailsA: + jmp qword ptr [rip + OriginalFuncs + 91 * 8] + +mixerGetControlDetailsW: + jmp qword ptr [rip + OriginalFuncs + 92 * 8] + +mixerGetDevCapsA: + jmp qword ptr [rip + OriginalFuncs + 93 * 8] + +mixerGetDevCapsW: + jmp qword ptr [rip + OriginalFuncs + 94 * 8] + +mixerGetID: + jmp qword ptr [rip + OriginalFuncs + 95 * 8] + +mixerGetLineControlsA: + jmp qword ptr [rip + OriginalFuncs + 96 * 8] + +mixerGetLineControlsW: + jmp qword ptr [rip + OriginalFuncs + 97 * 8] + +mixerGetLineInfoA: + jmp qword ptr [rip + OriginalFuncs + 98 * 8] + +mixerGetLineInfoW: + jmp qword ptr [rip + OriginalFuncs + 99 * 8] + +mixerGetNumDevs: + jmp qword ptr [rip + OriginalFuncs + 100 * 8] + +mixerMessage: + jmp qword ptr [rip + OriginalFuncs + 101 * 8] + +mixerOpen: + jmp qword ptr [rip + OriginalFuncs + 102 * 8] + +mixerSetControlDetails: + jmp qword ptr [rip + OriginalFuncs + 103 * 8] + +mmDrvInstall: + jmp qword ptr [rip + OriginalFuncs + 104 * 8] + +mmGetCurrentTask: + jmp qword ptr [rip + OriginalFuncs + 105 * 8] + +mmTaskBlock: + jmp qword ptr [rip + OriginalFuncs + 106 * 8] + +mmTaskCreate: + jmp qword ptr [rip + OriginalFuncs + 107 * 8] + +mmTaskSignal: + jmp qword ptr [rip + OriginalFuncs + 108 * 8] + +mmTaskYield: + jmp qword ptr [rip + OriginalFuncs + 109 * 8] + +mmioAdvance: + jmp qword ptr [rip + OriginalFuncs + 110 * 8] + +mmioAscend: + jmp qword ptr [rip + OriginalFuncs + 111 * 8] + +mmioClose: + jmp qword ptr [rip + OriginalFuncs + 112 * 8] + +mmioCreateChunk: + jmp qword ptr [rip + OriginalFuncs + 113 * 8] + +mmioDescend: + jmp qword ptr [rip + OriginalFuncs + 114 * 8] + +mmioFlush: + jmp qword ptr [rip + OriginalFuncs + 115 * 8] + +mmioGetInfo: + jmp qword ptr [rip + OriginalFuncs + 116 * 8] + +mmioInstallIOProcA: + jmp qword ptr [rip + OriginalFuncs + 117 * 8] + +mmioInstallIOProcW: + jmp qword ptr [rip + OriginalFuncs + 118 * 8] + +mmioOpenA: + jmp qword ptr [rip + OriginalFuncs + 119 * 8] + +mmioOpenW: + jmp qword ptr [rip + OriginalFuncs + 120 * 8] + +mmioRead: + jmp qword ptr [rip + OriginalFuncs + 121 * 8] + +mmioRenameA: + jmp qword ptr [rip + OriginalFuncs + 122 * 8] + +mmioRenameW: + jmp qword ptr [rip + OriginalFuncs + 123 * 8] + +mmioSeek: + jmp qword ptr [rip + OriginalFuncs + 124 * 8] + +mmioSendMessage: + jmp qword ptr [rip + OriginalFuncs + 125 * 8] + +mmioSetBuffer: + jmp qword ptr [rip + OriginalFuncs + 126 * 8] + +mmioSetInfo: + jmp qword ptr [rip + OriginalFuncs + 127 * 8] + +mmioStringToFOURCCA: + jmp qword ptr [rip + OriginalFuncs + 128 * 8] + +mmioStringToFOURCCW: + jmp qword ptr [rip + OriginalFuncs + 129 * 8] + +mmioWrite: + jmp qword ptr [rip + OriginalFuncs + 130 * 8] + +mmsystemGetVersion: + jmp qword ptr [rip + OriginalFuncs + 131 * 8] + +sndPlaySoundA: + jmp qword ptr [rip + OriginalFuncs + 132 * 8] + +sndPlaySoundW: + jmp qword ptr [rip + OriginalFuncs + 133 * 8] + +timeBeginPeriod: + jmp qword ptr [rip + OriginalFuncs + 134 * 8] + +timeEndPeriod: + jmp qword ptr [rip + OriginalFuncs + 135 * 8] + +timeGetDevCaps: + jmp qword ptr [rip + OriginalFuncs + 136 * 8] + +timeGetSystemTime: + jmp qword ptr [rip + OriginalFuncs + 137 * 8] + +timeGetTime: + jmp qword ptr [rip + OriginalFuncs + 138 * 8] + +timeKillEvent: + jmp qword ptr [rip + OriginalFuncs + 139 * 8] + +timeSetEvent: + jmp qword ptr [rip + OriginalFuncs + 140 * 8] + +waveInAddBuffer: + jmp qword ptr [rip + OriginalFuncs + 141 * 8] + +waveInClose: + jmp qword ptr [rip + OriginalFuncs + 142 * 8] + +waveInGetDevCapsA: + jmp qword ptr [rip + OriginalFuncs + 143 * 8] + +waveInGetDevCapsW: + jmp qword ptr [rip + OriginalFuncs + 144 * 8] + +waveInGetErrorTextA: + jmp qword ptr [rip + OriginalFuncs + 145 * 8] + +waveInGetErrorTextW: + jmp qword ptr [rip + OriginalFuncs + 146 * 8] + +waveInGetID: + jmp qword ptr [rip + OriginalFuncs + 147 * 8] + +waveInGetNumDevs: + jmp qword ptr [rip + OriginalFuncs + 148 * 8] + +waveInGetPosition: + jmp qword ptr [rip + OriginalFuncs + 149 * 8] + +waveInMessage: + jmp qword ptr [rip + OriginalFuncs + 150 * 8] + +waveInOpen: + jmp qword ptr [rip + OriginalFuncs + 151 * 8] + +waveInPrepareHeader: + jmp qword ptr [rip + OriginalFuncs + 152 * 8] + +waveInReset: + jmp qword ptr [rip + OriginalFuncs + 153 * 8] + +waveInStart: + jmp qword ptr [rip + OriginalFuncs + 154 * 8] + +waveInStop: + jmp qword ptr [rip + OriginalFuncs + 155 * 8] + +waveInUnprepareHeader: + jmp qword ptr [rip + OriginalFuncs + 156 * 8] + +waveOutBreakLoop: + jmp qword ptr [rip + OriginalFuncs + 157 * 8] + +waveOutClose: + jmp qword ptr [rip + OriginalFuncs + 158 * 8] + +waveOutGetDevCapsA: + jmp qword ptr [rip + OriginalFuncs + 159 * 8] + +waveOutGetDevCapsW: + jmp qword ptr [rip + OriginalFuncs + 160 * 8] + +waveOutGetErrorTextA: + jmp qword ptr [rip + OriginalFuncs + 161 * 8] + +waveOutGetErrorTextW: + jmp qword ptr [rip + OriginalFuncs + 162 * 8] + +waveOutGetID: + jmp qword ptr [rip + OriginalFuncs + 163 * 8] + +waveOutGetNumDevs: + jmp qword ptr [rip + OriginalFuncs + 164 * 8] + +waveOutGetPitch: + jmp qword ptr [rip + OriginalFuncs + 165 * 8] + +waveOutGetPlaybackRate: + jmp qword ptr [rip + OriginalFuncs + 166 * 8] + +waveOutGetPosition: + jmp qword ptr [rip + OriginalFuncs + 167 * 8] + +waveOutGetVolume: + jmp qword ptr [rip + OriginalFuncs + 168 * 8] + +waveOutMessage: + jmp qword ptr [rip + OriginalFuncs + 169 * 8] + +waveOutOpen: + jmp qword ptr [rip + OriginalFuncs + 170 * 8] + +waveOutPause: + jmp qword ptr [rip + OriginalFuncs + 171 * 8] + +waveOutPrepareHeader: + jmp qword ptr [rip + OriginalFuncs + 172 * 8] + +waveOutReset: + jmp qword ptr [rip + OriginalFuncs + 173 * 8] + +waveOutRestart: + jmp qword ptr [rip + OriginalFuncs + 174 * 8] + +waveOutSetPitch: + jmp qword ptr [rip + OriginalFuncs + 175 * 8] + +waveOutSetPlaybackRate: + jmp qword ptr [rip + OriginalFuncs + 176 * 8] + +waveOutSetVolume: + jmp qword ptr [rip + OriginalFuncs + 177 * 8] + +waveOutUnprepareHeader: + jmp qword ptr [rip + OriginalFuncs + 178 * 8] + +waveOutWrite: + jmp qword ptr [rip + OriginalFuncs + 179 * 8] + +ExportByOrdinal2: + jmp qword ptr [rip + OriginalFuncs + 180 * 8] \ No newline at end of file diff --git a/MelonProxy/deps/winmm.x86.S b/MelonProxy/deps/winmm.x86.S new file mode 100644 index 000000000..3eb2bbfb0 --- /dev/null +++ b/MelonProxy/deps/winmm.x86.S @@ -0,0 +1,724 @@ +.globl CloseDriver +.globl DefDriverProc +.globl DriverCallback +.globl DrvGetModuleHandle +.globl GetDriverModuleHandle +.globl OpenDriver +.globl PlaySound +.globl PlaySoundA +.globl PlaySoundW +.globl SendDriverMessage +.globl WOWAppExit +.globl auxGetDevCapsA +.globl auxGetDevCapsW +.globl auxGetNumDevs +.globl auxGetVolume +.globl auxOutMessage +.globl auxSetVolume +.globl joyConfigChanged +.globl joyGetDevCapsA +.globl joyGetDevCapsW +.globl joyGetNumDevs +.globl joyGetPos +.globl joyGetPosEx +.globl joyGetThreshold +.globl joyReleaseCapture +.globl joySetCapture +.globl joySetThreshold +.globl mciDriverNotify +.globl mciDriverYield +.globl mciExecute +.globl mciFreeCommandResource +.globl mciGetCreatorTask +.globl mciGetDeviceIDA +.globl mciGetDeviceIDFromElementIDA +.globl mciGetDeviceIDFromElementIDW +.globl mciGetDeviceIDW +.globl mciGetDriverData +.globl mciGetErrorStringA +.globl mciGetErrorStringW +.globl mciGetYieldProc +.globl mciLoadCommandResource +.globl mciSendCommandA +.globl mciSendCommandW +.globl mciSendStringA +.globl mciSendStringW +.globl mciSetDriverData +.globl mciSetYieldProc +.globl midiConnect +.globl midiDisconnect +.globl midiInAddBuffer +.globl midiInClose +.globl midiInGetDevCapsA +.globl midiInGetDevCapsW +.globl midiInGetErrorTextA +.globl midiInGetErrorTextW +.globl midiInGetID +.globl midiInGetNumDevs +.globl midiInMessage +.globl midiInOpen +.globl midiInPrepareHeader +.globl midiInReset +.globl midiInStart +.globl midiInStop +.globl midiInUnprepareHeader +.globl midiOutCacheDrumPatches +.globl midiOutCachePatches +.globl midiOutClose +.globl midiOutGetDevCapsA +.globl midiOutGetDevCapsW +.globl midiOutGetErrorTextA +.globl midiOutGetErrorTextW +.globl midiOutGetID +.globl midiOutGetNumDevs +.globl midiOutGetVolume +.globl midiOutLongMsg +.globl midiOutMessage +.globl midiOutOpen +.globl midiOutPrepareHeader +.globl midiOutReset +.globl midiOutSetVolume +.globl midiOutShortMsg +.globl midiOutUnprepareHeader +.globl midiStreamClose +.globl midiStreamOpen +.globl midiStreamOut +.globl midiStreamPause +.globl midiStreamPosition +.globl midiStreamProperty +.globl midiStreamRestart +.globl midiStreamStop +.globl mixerClose +.globl mixerGetControlDetailsA +.globl mixerGetControlDetailsW +.globl mixerGetDevCapsA +.globl mixerGetDevCapsW +.globl mixerGetID +.globl mixerGetLineControlsA +.globl mixerGetLineControlsW +.globl mixerGetLineInfoA +.globl mixerGetLineInfoW +.globl mixerGetNumDevs +.globl mixerMessage +.globl mixerOpen +.globl mixerSetControlDetails +.globl mmDrvInstall +.globl mmGetCurrentTask +.globl mmTaskBlock +.globl mmTaskCreate +.globl mmTaskSignal +.globl mmTaskYield +.globl mmioAdvance +.globl mmioAscend +.globl mmioClose +.globl mmioCreateChunk +.globl mmioDescend +.globl mmioFlush +.globl mmioGetInfo +.globl mmioInstallIOProcA +.globl mmioInstallIOProcW +.globl mmioOpenA +.globl mmioOpenW +.globl mmioRead +.globl mmioRenameA +.globl mmioRenameW +.globl mmioSeek +.globl mmioSendMessage +.globl mmioSetBuffer +.globl mmioSetInfo +.globl mmioStringToFOURCCA +.globl mmioStringToFOURCCW +.globl mmioWrite +.globl mmsystemGetVersion +.globl sndPlaySoundA +.globl sndPlaySoundW +.globl timeBeginPeriod +.globl timeEndPeriod +.globl timeGetDevCaps +.globl timeGetSystemTime +.globl timeGetTime +.globl timeKillEvent +.globl timeSetEvent +.globl waveInAddBuffer +.globl waveInClose +.globl waveInGetDevCapsA +.globl waveInGetDevCapsW +.globl waveInGetErrorTextA +.globl waveInGetErrorTextW +.globl waveInGetID +.globl waveInGetNumDevs +.globl waveInGetPosition +.globl waveInMessage +.globl waveInOpen +.globl waveInPrepareHeader +.globl waveInReset +.globl waveInStart +.globl waveInStop +.globl waveInUnprepareHeader +.globl waveOutBreakLoop +.globl waveOutClose +.globl waveOutGetDevCapsA +.globl waveOutGetDevCapsW +.globl waveOutGetErrorTextA +.globl waveOutGetErrorTextW +.globl waveOutGetID +.globl waveOutGetNumDevs +.globl waveOutGetPitch +.globl waveOutGetPlaybackRate +.globl waveOutGetPosition +.globl waveOutGetVolume +.globl waveOutMessage +.globl waveOutOpen +.globl waveOutPause +.globl waveOutPrepareHeader +.globl waveOutReset +.globl waveOutRestart +.globl waveOutSetPitch +.globl waveOutSetPlaybackRate +.globl waveOutSetVolume +.globl waveOutUnprepareHeader +.globl waveOutWrite +.globl ExportByOrdinal2 + +CloseDriver: + jmp ds:[_OriginalFuncs + 0 * 4] + +DefDriverProc: + jmp ds:[_OriginalFuncs + 1 * 4] + +DriverCallback: + jmp ds:[_OriginalFuncs + 2 * 4] + +DrvGetModuleHandle: + jmp ds:[_OriginalFuncs + 3 * 4] + +GetDriverModuleHandle: + jmp ds:[_OriginalFuncs + 4 * 4] + +OpenDriver: + jmp ds:[_OriginalFuncs + 5 * 4] + +PlaySound: + jmp ds:[_OriginalFuncs + 6 * 4] + +PlaySoundA: + jmp ds:[_OriginalFuncs + 7 * 4] + +PlaySoundW: + jmp ds:[_OriginalFuncs + 4 * 4] + +SendDriverMessage: + jmp ds:[_OriginalFuncs + 9 * 4] + +WOWAppExit: + jmp ds:[_OriginalFuncs + 10 * 4] + +auxGetDevCapsA: + jmp ds:[_OriginalFuncs + 11 * 4] + +auxGetDevCapsW: + jmp ds:[_OriginalFuncs + 12 * 4] + +auxGetNumDevs: + jmp ds:[_OriginalFuncs + 13 * 4] + +auxGetVolume: + jmp ds:[_OriginalFuncs + 14 * 4] + +auxOutMessage: + jmp ds:[_OriginalFuncs + 15 * 4] + +auxSetVolume: + jmp ds:[_OriginalFuncs + 16 * 4] + +joyConfigChanged: + jmp ds:[_OriginalFuncs + 17 * 4] + +joyGetDevCapsA: + jmp ds:[_OriginalFuncs + 14 * 4] + +joyGetDevCapsW: + jmp ds:[_OriginalFuncs + 19 * 4] + +joyGetNumDevs: + jmp ds:[_OriginalFuncs + 20 * 4] + +joyGetPos: + jmp ds:[_OriginalFuncs + 21 * 4] + +joyGetPosEx: + jmp ds:[_OriginalFuncs + 22 * 4] + +joyGetThreshold: + jmp ds:[_OriginalFuncs + 23 * 4] + +joyReleaseCapture: + jmp ds:[_OriginalFuncs + 24 * 4] + +joySetCapture: + jmp ds:[_OriginalFuncs + 25 * 4] + +joySetThreshold: + jmp ds:[_OriginalFuncs + 26 * 4] + +mciDriverNotify: + jmp ds:[_OriginalFuncs + 27 * 4] + +mciDriverYield: + jmp ds:[_OriginalFuncs + 24 * 4] + +mciExecute: + jmp ds:[_OriginalFuncs + 29 * 4] + +mciFreeCommandResource: + jmp ds:[_OriginalFuncs + 30 * 4] + +mciGetCreatorTask: + jmp ds:[_OriginalFuncs + 31 * 4] + +mciGetDeviceIDA: + jmp ds:[_OriginalFuncs + 32 * 4] + +mciGetDeviceIDFromElementIDA: + jmp ds:[_OriginalFuncs + 33 * 4] + +mciGetDeviceIDFromElementIDW: + jmp ds:[_OriginalFuncs + 34 * 4] + +mciGetDeviceIDW: + jmp ds:[_OriginalFuncs + 35 * 4] + +mciGetDriverData: + jmp ds:[_OriginalFuncs + 36 * 4] + +mciGetErrorStringA: + jmp ds:[_OriginalFuncs + 37 * 4] + +mciGetErrorStringW: + jmp ds:[_OriginalFuncs + 34 * 4] + +mciGetYieldProc: + jmp ds:[_OriginalFuncs + 39 * 4] + +mciLoadCommandResource: + jmp ds:[_OriginalFuncs + 40 * 4] + +mciSendCommandA: + jmp ds:[_OriginalFuncs + 41 * 4] + +mciSendCommandW: + jmp ds:[_OriginalFuncs + 42 * 4] + +mciSendStringA: + jmp ds:[_OriginalFuncs + 43 * 4] + +mciSendStringW: + jmp ds:[_OriginalFuncs + 44 * 4] + +mciSetDriverData: + jmp ds:[_OriginalFuncs + 45 * 4] + +mciSetYieldProc: + jmp ds:[_OriginalFuncs + 46 * 4] + +midiConnect: + jmp ds:[_OriginalFuncs + 47 * 4] + +midiDisconnect: + jmp ds:[_OriginalFuncs + 44 * 4] + +midiInAddBuffer: + jmp ds:[_OriginalFuncs + 49 * 4] + +midiInClose: + jmp ds:[_OriginalFuncs + 50 * 4] + +midiInGetDevCapsA: + jmp ds:[_OriginalFuncs + 51 * 4] + +midiInGetDevCapsW: + jmp ds:[_OriginalFuncs + 52 * 4] + +midiInGetErrorTextA: + jmp ds:[_OriginalFuncs + 53 * 4] + +midiInGetErrorTextW: + jmp ds:[_OriginalFuncs + 54 * 4] + +midiInGetID: + jmp ds:[_OriginalFuncs + 55 * 4] + +midiInGetNumDevs: + jmp ds:[_OriginalFuncs + 56 * 4] + +midiInMessage: + jmp ds:[_OriginalFuncs + 57 * 4] + +midiInOpen: + jmp ds:[_OriginalFuncs + 54 * 4] + +midiInPrepareHeader: + jmp ds:[_OriginalFuncs + 59 * 4] + +midiInReset: + jmp ds:[_OriginalFuncs + 60 * 4] + +midiInStart: + jmp ds:[_OriginalFuncs + 61 * 4] + +midiInStop: + jmp ds:[_OriginalFuncs + 62 * 4] + +midiInUnprepareHeader: + jmp ds:[_OriginalFuncs + 63 * 4] + +midiOutCacheDrumPatches: + jmp ds:[_OriginalFuncs + 64 * 4] + +midiOutCachePatches: + jmp ds:[_OriginalFuncs + 65 * 4] + +midiOutClose: + jmp ds:[_OriginalFuncs + 66 * 4] + +midiOutGetDevCapsA: + jmp ds:[_OriginalFuncs + 67 * 4] + +midiOutGetDevCapsW: + jmp ds:[_OriginalFuncs + 64 * 4] + +midiOutGetErrorTextA: + jmp ds:[_OriginalFuncs + 69 * 4] + +midiOutGetErrorTextW: + jmp ds:[_OriginalFuncs + 70 * 4] + +midiOutGetID: + jmp ds:[_OriginalFuncs + 71 * 4] + +midiOutGetNumDevs: + jmp ds:[_OriginalFuncs + 72 * 4] + +midiOutGetVolume: + jmp ds:[_OriginalFuncs + 73 * 4] + +midiOutLongMsg: + jmp ds:[_OriginalFuncs + 74 * 4] + +midiOutMessage: + jmp ds:[_OriginalFuncs + 75 * 4] + +midiOutOpen: + jmp ds:[_OriginalFuncs + 76 * 4] + +midiOutPrepareHeader: + jmp ds:[_OriginalFuncs + 77 * 4] + +midiOutReset: + jmp ds:[_OriginalFuncs + 74 * 4] + +midiOutSetVolume: + jmp ds:[_OriginalFuncs + 79 * 4] + +midiOutShortMsg: + jmp ds:[_OriginalFuncs + 40 * 4] + +midiOutUnprepareHeader: + jmp ds:[_OriginalFuncs + 41 * 4] + +midiStreamClose: + jmp ds:[_OriginalFuncs + 42 * 4] + +midiStreamOpen: + jmp ds:[_OriginalFuncs + 43 * 4] + +midiStreamOut: + jmp ds:[_OriginalFuncs + 44 * 4] + +midiStreamPause: + jmp ds:[_OriginalFuncs + 45 * 4] + +midiStreamPosition: + jmp ds:[_OriginalFuncs + 46 * 4] + +midiStreamProperty: + jmp ds:[_OriginalFuncs + 47 * 4] + +midiStreamRestart: + jmp ds:[_OriginalFuncs + 44 * 4] + +midiStreamStop: + jmp ds:[_OriginalFuncs + 49 * 4] + +mixerClose: + jmp ds:[_OriginalFuncs + 90 * 4] + +mixerGetControlDetailsA: + jmp ds:[_OriginalFuncs + 91 * 4] + +mixerGetControlDetailsW: + jmp ds:[_OriginalFuncs + 92 * 4] + +mixerGetDevCapsA: + jmp ds:[_OriginalFuncs + 93 * 4] + +mixerGetDevCapsW: + jmp ds:[_OriginalFuncs + 94 * 4] + +mixerGetID: + jmp ds:[_OriginalFuncs + 95 * 4] + +mixerGetLineControlsA: + jmp ds:[_OriginalFuncs + 96 * 4] + +mixerGetLineControlsW: + jmp ds:[_OriginalFuncs + 97 * 4] + +mixerGetLineInfoA: + jmp ds:[_OriginalFuncs + 94 * 4] + +mixerGetLineInfoW: + jmp ds:[_OriginalFuncs + 99 * 4] + +mixerGetNumDevs: + jmp ds:[_OriginalFuncs + 100 * 4] + +mixerMessage: + jmp ds:[_OriginalFuncs + 101 * 4] + +mixerOpen: + jmp ds:[_OriginalFuncs + 102 * 4] + +mixerSetControlDetails: + jmp ds:[_OriginalFuncs + 103 * 4] + +mmDrvInstall: + jmp ds:[_OriginalFuncs + 104 * 4] + +mmGetCurrentTask: + jmp ds:[_OriginalFuncs + 105 * 4] + +mmTaskBlock: + jmp ds:[_OriginalFuncs + 106 * 4] + +mmTaskCreate: + jmp ds:[_OriginalFuncs + 107 * 4] + +mmTaskSignal: + jmp ds:[_OriginalFuncs + 104 * 4] + +mmTaskYield: + jmp ds:[_OriginalFuncs + 109 * 4] + +mmioAdvance: + jmp ds:[_OriginalFuncs + 110 * 4] + +mmioAscend: + jmp ds:[_OriginalFuncs + 111 * 4] + +mmioClose: + jmp ds:[_OriginalFuncs + 112 * 4] + +mmioCreateChunk: + jmp ds:[_OriginalFuncs + 113 * 4] + +mmioDescend: + jmp ds:[_OriginalFuncs + 114 * 4] + +mmioFlush: + jmp ds:[_OriginalFuncs + 115 * 4] + +mmioGetInfo: + jmp ds:[_OriginalFuncs + 116 * 4] + +mmioInstallIOProcA: + jmp ds:[_OriginalFuncs + 117 * 4] + +mmioInstallIOProcW: + jmp ds:[_OriginalFuncs + 114 * 4] + +mmioOpenA: + jmp ds:[_OriginalFuncs + 119 * 4] + +mmioOpenW: + jmp ds:[_OriginalFuncs + 120 * 4] + +mmioRead: + jmp ds:[_OriginalFuncs + 121 * 4] + +mmioRenameA: + jmp ds:[_OriginalFuncs + 122 * 4] + +mmioRenameW: + jmp ds:[_OriginalFuncs + 123 * 4] + +mmioSeek: + jmp ds:[_OriginalFuncs + 124 * 4] + +mmioSendMessage: + jmp ds:[_OriginalFuncs + 125 * 4] + +mmioSetBuffer: + jmp ds:[_OriginalFuncs + 126 * 4] + +mmioSetInfo: + jmp ds:[_OriginalFuncs + 127 * 4] + +mmioStringToFOURCCA: + jmp ds:[_OriginalFuncs + 124 * 4] + +mmioStringToFOURCCW: + jmp ds:[_OriginalFuncs + 129 * 4] + +mmioWrite: + jmp ds:[_OriginalFuncs + 130 * 4] + +mmsystemGetVersion: + jmp ds:[_OriginalFuncs + 131 * 4] + +sndPlaySoundA: + jmp ds:[_OriginalFuncs + 132 * 4] + +sndPlaySoundW: + jmp ds:[_OriginalFuncs + 133 * 4] + +timeBeginPeriod: + jmp ds:[_OriginalFuncs + 134 * 4] + +timeEndPeriod: + jmp ds:[_OriginalFuncs + 135 * 4] + +timeGetDevCaps: + jmp ds:[_OriginalFuncs + 136 * 4] + +timeGetSystemTime: + jmp ds:[_OriginalFuncs + 137 * 4] + +timeGetTime: + jmp ds:[_OriginalFuncs + 134 * 4] + +timeKillEvent: + jmp ds:[_OriginalFuncs + 139 * 4] + +timeSetEvent: + jmp ds:[_OriginalFuncs + 140 * 4] + +waveInAddBuffer: + jmp ds:[_OriginalFuncs + 141 * 4] + +waveInClose: + jmp ds:[_OriginalFuncs + 142 * 4] + +waveInGetDevCapsA: + jmp ds:[_OriginalFuncs + 143 * 4] + +waveInGetDevCapsW: + jmp ds:[_OriginalFuncs + 144 * 4] + +waveInGetErrorTextA: + jmp ds:[_OriginalFuncs + 145 * 4] + +waveInGetErrorTextW: + jmp ds:[_OriginalFuncs + 146 * 4] + +waveInGetID: + jmp ds:[_OriginalFuncs + 147 * 4] + +waveInGetNumDevs: + jmp ds:[_OriginalFuncs + 144 * 4] + +waveInGetPosition: + jmp ds:[_OriginalFuncs + 149 * 4] + +waveInMessage: + jmp ds:[_OriginalFuncs + 150 * 4] + +waveInOpen: + jmp ds:[_OriginalFuncs + 151 * 4] + +waveInPrepareHeader: + jmp ds:[_OriginalFuncs + 152 * 4] + +waveInReset: + jmp ds:[_OriginalFuncs + 153 * 4] + +waveInStart: + jmp ds:[_OriginalFuncs + 154 * 4] + +waveInStop: + jmp ds:[_OriginalFuncs + 155 * 4] + +waveInUnprepareHeader: + jmp ds:[_OriginalFuncs + 156 * 4] + +waveOutBreakLoop: + jmp ds:[_OriginalFuncs + 157 * 4] + +waveOutClose: + jmp ds:[_OriginalFuncs + 154 * 4] + +waveOutGetDevCapsA: + jmp ds:[_OriginalFuncs + 159 * 4] + +waveOutGetDevCapsW: + jmp ds:[_OriginalFuncs + 160 * 4] + +waveOutGetErrorTextA: + jmp ds:[_OriginalFuncs + 161 * 4] + +waveOutGetErrorTextW: + jmp ds:[_OriginalFuncs + 162 * 4] + +waveOutGetID: + jmp ds:[_OriginalFuncs + 163 * 4] + +waveOutGetNumDevs: + jmp ds:[_OriginalFuncs + 164 * 4] + +waveOutGetPitch: + jmp ds:[_OriginalFuncs + 165 * 4] + +waveOutGetPlaybackRate: + jmp ds:[_OriginalFuncs + 166 * 4] + +waveOutGetPosition: + jmp ds:[_OriginalFuncs + 167 * 4] + +waveOutGetVolume: + jmp ds:[_OriginalFuncs + 164 * 4] + +waveOutMessage: + jmp ds:[_OriginalFuncs + 169 * 4] + +waveOutOpen: + jmp ds:[_OriginalFuncs + 170 * 4] + +waveOutPause: + jmp ds:[_OriginalFuncs + 171 * 4] + +waveOutPrepareHeader: + jmp ds:[_OriginalFuncs + 172 * 4] + +waveOutReset: + jmp ds:[_OriginalFuncs + 173 * 4] + +waveOutRestart: + jmp ds:[_OriginalFuncs + 174 * 4] + +waveOutSetPitch: + jmp ds:[_OriginalFuncs + 175 * 4] + +waveOutSetPlaybackRate: + jmp ds:[_OriginalFuncs + 176 * 4] + +waveOutSetVolume: + jmp ds:[_OriginalFuncs + 177 * 4] + +waveOutUnprepareHeader: + jmp ds:[_OriginalFuncs + 174 * 4] + +waveOutWrite: + jmp ds:[_OriginalFuncs + 179 * 4] + +ExportByOrdinal2: + jmp ds:[_OriginalFuncs + 140 * 4] \ No newline at end of file diff --git a/Proxy/src/core.rs b/MelonProxy/src/core.rs similarity index 59% rename from Proxy/src/core.rs rename to MelonProxy/src/core.rs index 466eb921c..20fa394eb 100644 --- a/Proxy/src/core.rs +++ b/MelonProxy/src/core.rs @@ -1,25 +1,34 @@ -//! the core logic of the proxy +use std::{ + error::Error, + path::PathBuf, + sync::{LazyLock, Mutex}, +}; -use crate::utils::files; -use lazy_static::lazy_static; use libloading::Library; -use std::{error, path::PathBuf, sync::Mutex}; - -lazy_static! { - static ref BOOTSTRAP: Mutex> = Mutex::new(None); -} -pub fn init() -> Result<(), Box> { - let file_path = std::env::current_exe()?; +use crate::utils::files; - if !files::is_unity(&file_path)? { - return Ok(()); - } +pub static BOOTSTRAP: LazyLock>> = LazyLock::new(|| Mutex::new(None)); +pub fn init() -> Result<(), Box> { let args: Vec = std::env::args().collect(); + + //TODO: Support UTF-16 (it will suck) let mut base_dir = std::env::current_dir()?; let mut no_mods = false; + let current_exe = std::env::current_exe()?; + let game_name = current_exe + .file_name() + .ok_or("Failed to get game name")? + .to_str() + .ok_or("Failed to get game name")?; + + if game_name.starts_with("UnityCrashHandler64") || game_name.starts_with("UnityCrashHandler32") + { + return Ok(()); + } + for arg in args.iter() { if arg.starts_with("--melonloader.basedir") { let a: Vec<&str> = arg.split("=").collect(); @@ -30,7 +39,6 @@ pub fn init() -> Result<(), Box> { no_mods = true; } } - //return Ok, and silently stop loading MelonLoader, if the user has specified to not load mods, //or if the game is not a Unity game diff --git a/MelonProxy/src/lib.rs b/MelonProxy/src/lib.rs new file mode 100644 index 000000000..8b7e796b7 --- /dev/null +++ b/MelonProxy/src/lib.rs @@ -0,0 +1,42 @@ +#![feature(fn_ptr_trait)] +#![feature(lazy_cell)] + +#![allow(non_snake_case)] +#![deny( + missing_debug_implementations, + unused_results, + warnings, + clippy::extra_unused_lifetimes, + clippy::from_over_into, + clippy::needless_borrow, + clippy::new_without_default, + clippy::useless_conversion +)] +#![forbid(rust_2018_idioms)] +#![allow(clippy::inherent_to_string, clippy::type_complexity, improper_ctypes)] +#![cfg_attr(docsrs, feature(doc_cfg))] + +#[cfg(target_os = "windows")] +pub use windows::Win32::Foundation::HINSTANCE; + +#[cfg(target_os = "windows")] +pub mod proxy; + +pub mod utils; +pub mod core; + +/// this function will get called by our proxy macro. See MelonProxy-sys +#[cfg_attr( + not(target_os = "windows"), + ctor::ctor +)] +#[cfg_attr( + target_os = "windows", + melon_proxy_sys::proxy +)] +#[allow(dead_code)] +fn main() { + core::init().unwrap_or_else(|e| { + internal_failure!("Failed to initialize MelonLoader: {}", e); + }); +} \ No newline at end of file diff --git a/MelonProxy/src/proxy/exports.rs b/MelonProxy/src/proxy/exports.rs new file mode 100644 index 000000000..800650230 --- /dev/null +++ b/MelonProxy/src/proxy/exports.rs @@ -0,0 +1,316 @@ +//! all the logic for exporting the functions. + +use std::{ + arch::global_asm, + marker::FnPtr, + ptr::null_mut, + sync::{LazyLock, Mutex}, io, +}; + +use super::hinstance_ext::ProxyDll; +use libloading::Library; +use windows::Win32::Foundation::HINSTANCE; + +// These arrays are accessed by assembly code to jump to the given function. +// TODO: once dynamically sized arrays are implemented, consider starting this off at 17 and dynamically resizing as needed +#[no_mangle] +static mut OriginalFuncs: [*const (); 181] = [null_mut(); 181]; + +// These assembly files define the Windows DLL functions that we're proxying, they are exported through a linked .def file. +#[cfg(target_arch = "x86_64")] +global_asm!(include_str!("../../deps/version.x64.S")); +#[cfg(target_arch = "x86")] +global_asm!(include_str!("../../deps/version.x86.S")); + +#[cfg(target_arch = "x86_64")] +global_asm!(include_str!("../../deps/winhttp.x64.S")); +#[cfg(target_arch = "x86")] +global_asm!(include_str!("../../deps/winhttp.x86.S")); + +#[cfg(target_arch = "x86_64")] +global_asm!(include_str!("../../deps/winmm.x64.S")); +#[cfg(target_arch = "x86")] +global_asm!(include_str!("../../deps/winmm.x86.S")); + +const EXPORTS_VERSION: [&[u8]; 17] = [ + b"GetFileVersionInfoA", + b"GetFileVersionInfoByHandle", + b"GetFileVersionInfoExA", + b"GetFileVersionInfoExW", + b"GetFileVersionInfoSizeA", + b"GetFileVersionInfoSizeExA", + b"GetFileVersionInfoSizeExW", + b"GetFileVersionInfoSizeW", + b"GetFileVersionInfoW", + b"VerFindFileA", + b"VerFindFileW", + b"VerInstallFileA", + b"VerInstallFileW", + b"VerLanguageNameA", + b"VerLanguageNameW", + b"VerQueryValueA", + b"VerQueryValueW", +]; + +const EXPORTS_WINHTTP: [&[u8]; 27] = [ + b"EmptyWorkingSet", + b"EnumDeviceDrivers", + b"EnumPageFilesA", + b"EnumPageFilesW", + b"EnumProcessModules", + b"EnumProcessModulesEx", + b"EnumProcesses", + b"GetDeviceDriverBaseNameA", + b"GetDeviceDriverBaseNameW", + b"GetDeviceDriverFileNameA", + b"GetDeviceDriverFileNameW", + b"GetMappedFileNameA", + b"GetMappedFileNameW", + b"GetModuleBaseNameA", + b"GetModuleBaseNameW", + b"GetModuleFileNameExA", + b"GetModuleFileNameExW", + b"GetModuleInformation", + b"GetPerformanceInfo", + b"GetProcessImageFileNameA", + b"GetProcessImageFileNameW", + b"GetProcessMemoryInfo", + b"GetWsChanges", + b"GetWsChangesEx", + b"InitializeProcessForWsWatch", + b"QueryWorkingSet", + b"QueryWorkingSetEx", +]; + +const EXPORTS_WINMM: [&[u8]; 181] = [ + b"CloseDriver", + b"DefDriverProc", + b"DriverCallback", + b"DrvGetModuleHandle", + b"GetDriverModuleHandle", + b"OpenDriver", + b"PlaySound", + b"PlaySoundA", + b"PlaySoundW", + b"SendDriverMessage", + b"WOWAppExit", + b"auxGetDevCapsA", + b"auxGetDevCapsW", + b"auxGetNumDevs", + b"auxGetVolume", + b"auxOutMessage", + b"auxSetVolume", + b"joyConfigChanged", + b"joyGetDevCapsA", + b"joyGetDevCapsW", + b"joyGetNumDevs", + b"joyGetPos", + b"joyGetPosEx", + b"joyGetThreshold", + b"joyReleaseCapture", + b"joySetCapture", + b"joySetThreshold", + b"mciDriverNotify", + b"mciDriverYield", + b"mciExecute", + b"mciFreeCommandResource", + b"mciGetCreatorTask", + b"mciGetDeviceIDA", + b"mciGetDeviceIDFromElementIDA", + b"mciGetDeviceIDFromElementIDW", + b"mciGetDeviceIDW", + b"mciGetDriverData", + b"mciGetErrorStringA", + b"mciGetErrorStringW", + b"mciGetYieldProc", + b"mciLoadCommandResource", + b"mciSendCommandA", + b"mciSendCommandW", + b"mciSendStringA", + b"mciSendStringW", + b"mciSetDriverData", + b"mciSetYieldProc", + b"midiConnect", + b"midiDisconnect", + b"midiInAddBuffer", + b"midiInClose", + b"midiInGetDevCapsA", + b"midiInGetDevCapsW", + b"midiInGetErrorTextA", + b"midiInGetErrorTextW", + b"midiInGetID", + b"midiInGetNumDevs", + b"midiInMessage", + b"midiInOpen", + b"midiInPrepareHeader", + b"midiInReset", + b"midiInStart", + b"midiInStop", + b"midiInUnprepareHeader", + b"midiOutCacheDrumPatches", + b"midiOutCachePatches", + b"midiOutClose", + b"midiOutGetDevCapsA", + b"midiOutGetDevCapsW", + b"midiOutGetErrorTextA", + b"midiOutGetErrorTextW", + b"midiOutGetID", + b"midiOutGetNumDevs", + b"midiOutGetVolume", + b"midiOutLongMsg", + b"midiOutMessage", + b"midiOutOpen", + b"midiOutPrepareHeader", + b"midiOutReset", + b"midiOutSetVolume", + b"midiOutShortMsg", + b"midiOutUnprepareHeader", + b"midiStreamClose", + b"midiStreamOpen", + b"midiStreamOut", + b"midiStreamPause", + b"midiStreamPosition", + b"midiStreamProperty", + b"midiStreamRestart", + b"midiStreamStop", + b"mixerClose", + b"mixerGetControlDetailsA", + b"mixerGetControlDetailsW", + b"mixerGetDevCapsA", + b"mixerGetDevCapsW", + b"mixerGetID", + b"mixerGetLineControlsA", + b"mixerGetLineControlsW", + b"mixerGetLineInfoA", + b"mixerGetLineInfoW", + b"mixerGetNumDevs", + b"mixerMessage", + b"mixerOpen", + b"mixerSetControlDetails", + b"mmDrvInstall", + b"mmGetCurrentTask", + b"mmTaskBlock", + b"mmTaskCreate", + b"mmTaskSignal", + b"mmTaskYield", + b"mmioAdvance", + b"mmioAscend", + b"mmioClose", + b"mmioCreateChunk", + b"mmioDescend", + b"mmioFlush", + b"mmioGetInfo", + b"mmioInstallIOProcA", + b"mmioInstallIOProcW", + b"mmioOpenA", + b"mmioOpenW", + b"mmioRead", + b"mmioRenameA", + b"mmioRenameW", + b"mmioSeek", + b"mmioSendMessage", + b"mmioSetBuffer", + b"mmioSetInfo", + b"mmioStringToFOURCCA", + b"mmioStringToFOURCCW", + b"mmioWrite", + b"mmsystemGetVersion", + b"sndPlaySoundA", + b"sndPlaySoundW", + b"timeBeginPeriod", + b"timeEndPeriod", + b"timeGetDevCaps", + b"timeGetSystemTime", + b"timeGetTime", + b"timeKillEvent", + b"timeSetEvent", + b"waveInAddBuffer", + b"waveInClose", + b"waveInGetDevCapsA", + b"waveInGetDevCapsW", + b"waveInGetErrorTextA", + b"waveInGetErrorTextW", + b"waveInGetID", + b"waveInGetNumDevs", + b"waveInGetPosition", + b"waveInMessage", + b"waveInOpen", + b"waveInPrepareHeader", + b"waveInReset", + b"waveInStart", + b"waveInStop", + b"waveInUnprepareHeader", + b"waveOutBreakLoop", + b"waveOutClose", + b"waveOutGetDevCapsA", + b"waveOutGetDevCapsW", + b"waveOutGetErrorTextA", + b"waveOutGetErrorTextW", + b"waveOutGetID", + b"waveOutGetNumDevs", + b"waveOutGetPitch", + b"waveOutGetPlaybackRate", + b"waveOutGetPosition", + b"waveOutGetVolume", + b"waveOutMessage", + b"waveOutOpen", + b"waveOutPause", + b"waveOutPrepareHeader", + b"waveOutReset", + b"waveOutRestart", + b"waveOutSetPitch", + b"waveOutSetPlaybackRate", + b"waveOutSetVolume", + b"waveOutUnprepareHeader", + b"waveOutWrite", + b"ExportByOrdinal2", +]; + +// we have to statically store the original library here, because if it gets dropped, the function pointers we get out of it become invalid. +pub static ORIGINAL: LazyLock>> = LazyLock::new(|| Mutex::new(None)); + +/// this function gets called by the #[proxy] macro in our entrypoint. +pub fn initialize(module: HINSTANCE) -> Result<(), io::Error> { + let INVALID_HANDLE: io::Error = io::Error::new(io::ErrorKind::InvalidInput, "Invalid module handle"); + let INVALID_FILE_NAME: io::Error = io::Error::new(io::ErrorKind::InvalidInput, "Invalid file name"); + let POISONED_LOCK: io::Error = io::Error::new(io::ErrorKind::Other, "Poisoned lock"); + + if module.is_invalid() { + return Err(INVALID_HANDLE); + } + + let name = module.get_file_name()?; + let original = module.load_original()?; + + let exports = match name.as_str() { + "version.dll" => EXPORTS_VERSION.to_vec(), + "winhttp.dll" => EXPORTS_WINHTTP.to_vec(), + "winmm.dll" => EXPORTS_WINMM.to_vec(), + _ => return Err(INVALID_FILE_NAME), + }; + + for (i, export) in exports.iter().enumerate() { + unsafe { + OriginalFuncs[i] = get_maybe(&original, export); + } + } + + //store the library so it doesn't get unloaded + let orig = ORIGINAL.try_lock(); + if orig.is_err() { + return Err(POISONED_LOCK); + } + + *orig.unwrap() = Some(original); + + Ok(()) +} + +unsafe fn get_maybe(lib: &Library, name: &[u8]) -> *const () { + let func = lib.get::(name); + + match func.is_err() { + false => func.unwrap().addr(), + true => null_mut(), + } +} diff --git a/MelonProxy/src/proxy/hinstance_ext.rs b/MelonProxy/src/proxy/hinstance_ext.rs new file mode 100644 index 000000000..d7348d0d1 --- /dev/null +++ b/MelonProxy/src/proxy/hinstance_ext.rs @@ -0,0 +1,62 @@ +use std::{io, path::Path}; + +use super::windows_ext::get_module_path; +use libloading::Library; +use windows::Win32::Foundation::HINSTANCE; + +pub trait ProxyDll { + fn get_file_name(&self) -> Result; + fn is_compatible(&self) -> Result; + fn load_original(&self) -> Result; +} + +impl ProxyDll for HINSTANCE { + fn get_file_name(&self) -> Result { + let path = get_module_path(*self)?; + + let file_name = path + .file_name() + .ok_or(io::Error::new( + io::ErrorKind::NotFound, + "Failed to get File Name", + ))? + .to_str() + .ok_or(io::Error::new( + io::ErrorKind::NotFound, + "Failed to get File Name", + ))?; + + Ok(file_name.to_lowercase().to_string()) + } + + fn is_compatible(&self) -> Result { + let file_name = self.get_file_name()?; + + Ok(file_name.eq("version.dll") || file_name.eq("winhttp.dll") || file_name.eq("winmm.dll")) + } + + fn load_original(&self) -> Result { + + let INVALID_FILE_NAME: io::Error = + io::Error::new(io::ErrorKind::NotFound, "Invalid File Name"); + let LIBRARY_NOT_FOUND: io::Error = + io::Error::new(io::ErrorKind::NotFound, "Failed to load original Proxy DLL"); + if !self.is_compatible()? { + return Err(INVALID_FILE_NAME); + } + + let name = self.get_file_name()?; + let path = Path::new("C:\\Windows\\System32").join(name); + + if !path.exists() { + return Err(LIBRARY_NOT_FOUND); + } + + let lib = unsafe { Library::new(path) }; + + match lib { + Ok(lib) => Ok(lib), + Err(_) => Err(LIBRARY_NOT_FOUND), + } + } +} diff --git a/MelonProxy/src/proxy/mod.rs b/MelonProxy/src/proxy/mod.rs new file mode 100644 index 000000000..ca08762df --- /dev/null +++ b/MelonProxy/src/proxy/mod.rs @@ -0,0 +1,3 @@ +pub mod exports; +pub mod hinstance_ext; +pub mod windows_ext; \ No newline at end of file diff --git a/MelonProxy/src/proxy/windows_ext.rs b/MelonProxy/src/proxy/windows_ext.rs new file mode 100644 index 000000000..c9ccb6e05 --- /dev/null +++ b/MelonProxy/src/proxy/windows_ext.rs @@ -0,0 +1,23 @@ +use std::{io, path::PathBuf}; + +use windows::Win32::{ + Foundation::{HINSTANCE, MAX_PATH}, + System::LibraryLoader::GetModuleFileNameW, +}; + +pub fn get_module_path(module: HINSTANCE) -> Result { + let mut path = [0u16; MAX_PATH as usize]; + + let len = unsafe { GetModuleFileNameW(module, &mut path) }; + + if len <= 0 { + return Err(io::Error::new( + io::ErrorKind::NotFound, + "Failed to get File Name", + )); + } + + let str = String::from_utf16_lossy(&path[..len as usize]); + + Ok(PathBuf::from(str)) +} diff --git a/Proxy/src/utils/assert.rs b/MelonProxy/src/utils/assert.rs similarity index 87% rename from Proxy/src/utils/assert.rs rename to MelonProxy/src/utils/assert.rs index 2cd9ec6e7..c28382f0f 100644 --- a/Proxy/src/utils/assert.rs +++ b/MelonProxy/src/utils/assert.rs @@ -2,7 +2,7 @@ /// Throws an internal failure with the given message /// -/// This logs your message to file, creates a message box, and then panics. +/// This creates a message box, and then panics. /// It uses the same syntax as _format!_ /// /// # Examples diff --git a/MelonProxy/src/utils/files.rs b/MelonProxy/src/utils/files.rs new file mode 100644 index 000000000..78858b3f4 --- /dev/null +++ b/MelonProxy/src/utils/files.rs @@ -0,0 +1,25 @@ +use std::{ + env::consts::DLL_EXTENSION, + io::{self, Error, ErrorKind}, + path::PathBuf, +}; + +/// search for Bootstrap in the given path +pub fn get_bootstrap_path(base_path: &PathBuf) -> Result { + let bootstrap_names = ["Bootstrap", "libBootstrap"]; //by convention, on unix, the library is prefixed with "lib" + + let path = base_path.join("MelonLoader").join("Dependencies"); + + for name in bootstrap_names.iter() { + let bootstrap_path = path.join(name).with_extension(DLL_EXTENSION); + + if bootstrap_path.exists() { + return Ok(bootstrap_path); + } + } + + Err(Error::new( + ErrorKind::NotFound, + "Failed to find MelonLoader Bootstrap", + )) +} diff --git a/MelonProxy/src/utils/mod.rs b/MelonProxy/src/utils/mod.rs new file mode 100644 index 000000000..7c6fb7640 --- /dev/null +++ b/MelonProxy/src/utils/mod.rs @@ -0,0 +1,2 @@ +pub mod assert; +pub mod files; \ No newline at end of file diff --git a/Proxy/Cargo.toml b/Proxy/Cargo.toml deleted file mode 100644 index 573cdb319..000000000 --- a/Proxy/Cargo.toml +++ /dev/null @@ -1,28 +0,0 @@ -[package] -name = "proxy" -version = "0.2.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -ctor = "0.2.6" -msgbox = "0.7.0" -lazy_static = "1.4.0" -thiserror = "*" -libloading = "*" -clap = { git = "https://github.com/RinLovesYou/clap", features = ["derive"] } - -[target.'cfg(windows)'.dependencies] -proxy-dll = { git = "https://github.com/RinLovesYou/dll-proxy-rs.git" } -#proxy-dll = { path = "C:\\Users\\sarah\\Documents\\rust\\dll-proxy-rs\\proxy" } - -[target.'cfg(unix)'.dependencies] -libc = "0.2.137" - -[lib] -name = "version" -crate-type = ["cdylib"] - -[build-dependencies] -cc = "1.0.76" diff --git a/Proxy/src/entry.rs b/Proxy/src/entry.rs deleted file mode 100644 index 8b6dbf4df..000000000 --- a/Proxy/src/entry.rs +++ /dev/null @@ -1,35 +0,0 @@ -//! These are platform specific init functions. -//! On Windows, we need to "proxy" a built-in Windows library. -//! The crate 'proxy-dll' takes care of this, dynamically proxying 3 -//! different dlls, depending on the file name of the compiled binary. -//! -//! See https://github.com/RinLovesYou/dll-proxy-rs/ -//! -//! On Linux, injection is done through LD_PRELOAD, so there's no need to proxy anything. -//! there we just use `ctor`. - -#[cfg(not(target_os = "windows"))] -use ctor::ctor; - -#[cfg(target_os = "windows")] -use proxy_dll::proxy; - -use crate::{core, internal_failure}; - -#[cfg(not(target_os = "windows"))] -#[no_mangle] -#[ctor] -fn main() { - core::init().unwrap_or_else(|e| { - internal_failure!("Failed to initialize MelonLoader: {}", e); - }); -} - -#[cfg(target_os = "windows")] -#[no_mangle] -#[proxy] -fn main() { - core::init().unwrap_or_else(|e| { - internal_failure!("Failed to initialize MelonLoader: {}", e); - }); -} diff --git a/Proxy/src/lib.rs b/Proxy/src/lib.rs deleted file mode 100644 index 897c0219a..000000000 --- a/Proxy/src/lib.rs +++ /dev/null @@ -1,19 +0,0 @@ -//! Cross platform reimplementation of MelonLoader's Proxy in rust - -// #![deny( -// missing_debug_implementations, -// unused_results, -// warnings, -// clippy::extra_unused_lifetimes, -// clippy::from_over_into, -// clippy::needless_borrow, -// clippy::new_without_default, -// clippy::useless_conversion -// )] -// #![forbid(rust_2018_idioms)] -// #![allow(clippy::inherent_to_string, clippy::type_complexity, improper_ctypes)] -// #![cfg_attr(docsrs, feature(doc_cfg))] - -pub mod entry; -pub mod core; -pub mod utils; diff --git a/Proxy/src/utils/errors.rs b/Proxy/src/utils/errors.rs deleted file mode 100644 index 3b66e0b27..000000000 --- a/Proxy/src/utils/errors.rs +++ /dev/null @@ -1,9 +0,0 @@ -use std::path::PathBuf; - -use thiserror::Error; - -#[derive(Debug, Error)] -pub enum ProxyError { - #[error("failed to find Bootstrap at \"{0}\" please make sure you have installed MelonLoader correctly")] - BootstrapNotFound(PathBuf), -} \ No newline at end of file diff --git a/Proxy/src/utils/files.rs b/Proxy/src/utils/files.rs deleted file mode 100644 index 4a580d0b3..000000000 --- a/Proxy/src/utils/files.rs +++ /dev/null @@ -1,48 +0,0 @@ -//! various filesystem utils - -use std::{env::consts::DLL_EXTENSION, error::Error, path::PathBuf}; - -use super::errors::ProxyError; - -/// search for Bootstrap in the given path -pub fn get_bootstrap_path(base_path: &PathBuf) -> Result { - let bootstrap_names = ["Bootstrap", "libBootstrap"]; //by convention, on unix, the library is prefixed with "lib" - - let path = base_path.join("MelonLoader").join("Dependencies"); - - for name in bootstrap_names.iter() { - let bootstrap_path = path.join(name).with_extension(DLL_EXTENSION); - - if bootstrap_path.exists() { - return Ok(bootstrap_path); - } - } - - Err(ProxyError::BootstrapNotFound(base_path.to_owned())) -} - -pub fn is_unity(file_path: &PathBuf) -> Result> { - let file_name = file_path - .file_stem() - .ok_or("Failed to get file stem")? - .to_str() - .ok_or("Failed to convert file stem to str")?; - - let base_folder = file_path.parent().ok_or("Failed to get parent folder")?; - - let data_path = base_folder.join(format!("{file_name}_Data")); - - if !data_path.exists() { - return Ok(false); - } - - let global_game_managers = data_path.join("globalgamemanagers"); - let data_unity3d = data_path.join("data.unity3d"); - let main_data = data_path.join("mainData"); - - if global_game_managers.exists() || data_unity3d.exists() || main_data.exists() { - Ok(true) - } else { - Ok(false) - } -} diff --git a/Proxy/src/utils/mod.rs b/Proxy/src/utils/mod.rs deleted file mode 100644 index b5bfd8b1a..000000000 --- a/Proxy/src/utils/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -//! various utilites - -pub mod assert; -pub mod files; -pub mod errors; \ No newline at end of file From d85cf67158e321abee05857ce657d7f7e01126c1 Mon Sep 17 00:00:00 2001 From: Sarah Date: Sat, 30 Dec 2023 19:14:49 +0100 Subject: [PATCH 59/79] Update build.rs to print the absolute path of the Exports.def file --- MelonProxy/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MelonProxy/build.rs b/MelonProxy/build.rs index 2c8ce803a..3674d6830 100644 --- a/MelonProxy/build.rs +++ b/MelonProxy/build.rs @@ -16,10 +16,10 @@ fn main() { /// links Exports.def to the resulting dll, exporting all our asm functions. fn link_exports() { - println!("cargo:warning=Linking Exports File.."); use std::path::Path; let lib_path = Path::new("deps").join("Exports.def"); let absolute_path = std::fs::canonicalize(&lib_path).unwrap(); + println!("cargo:warning=Linking Exports File: {}", absolute_path.display()); println!( "cargo:rustc-cdylib-link-arg=/DEF:{}", absolute_path.display() From 9181d4c510982977d9ba90957da6157cf3d5b3cf Mon Sep 17 00:00:00 2001 From: Sarah Date: Sat, 30 Dec 2023 19:22:41 +0100 Subject: [PATCH 60/79] Fix path issue in MelonProxy build.rs --- MelonProxy/build.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/MelonProxy/build.rs b/MelonProxy/build.rs index 3674d6830..cfabcbd6f 100644 --- a/MelonProxy/build.rs +++ b/MelonProxy/build.rs @@ -18,10 +18,18 @@ fn main() { fn link_exports() { use std::path::Path; let lib_path = Path::new("deps").join("Exports.def"); - let absolute_path = std::fs::canonicalize(&lib_path).unwrap(); - println!("cargo:warning=Linking Exports File: {}", absolute_path.display()); + let mut absolute_path = std::fs::canonicalize(&lib_path).unwrap().to_str().unwrap().to_string(); + if absolute_path.contains("\\\\?\\") { + absolute_path = absolute_path.replace("\\\\?\\", ""); + } + + if !lib_path.exists() { + panic!("Exports.def not found at {}", lib_path.display()); + } + + println!("cargo:warning=Linking Exports File: {}", absolute_path); println!( "cargo:rustc-cdylib-link-arg=/DEF:{}", - absolute_path.display() + absolute_path ); } \ No newline at end of file From 9a60d635bb70f3010c340d171a7a358aa72cb6aa Mon Sep 17 00:00:00 2001 From: Sarah Date: Sat, 30 Dec 2023 19:34:26 +0100 Subject: [PATCH 61/79] I hate github CI --- MelonProxy/build.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/MelonProxy/build.rs b/MelonProxy/build.rs index cfabcbd6f..3f057becf 100644 --- a/MelonProxy/build.rs +++ b/MelonProxy/build.rs @@ -18,12 +18,9 @@ fn main() { fn link_exports() { use std::path::Path; let lib_path = Path::new("deps").join("Exports.def"); - let mut absolute_path = std::fs::canonicalize(&lib_path).unwrap().to_str().unwrap().to_string(); - if absolute_path.contains("\\\\?\\") { - absolute_path = absolute_path.replace("\\\\?\\", ""); - } + let absolute_path = std::fs::canonicalize(&lib_path).unwrap().to_str().unwrap().to_string().replace("\\\\?\\", ""); - if !lib_path.exists() { + if !Path::new(&absolute_path).exists() { panic!("Exports.def not found at {}", lib_path.display()); } From 61a6da5e3651eefda8ef6e5224d4cb6dc9336211 Mon Sep 17 00:00:00 2001 From: Sarah Date: Sat, 30 Dec 2023 20:00:08 +0100 Subject: [PATCH 62/79] Try fixing build script again --- MelonProxy/build.rs | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/MelonProxy/build.rs b/MelonProxy/build.rs index 3f057becf..5383971ce 100644 --- a/MelonProxy/build.rs +++ b/MelonProxy/build.rs @@ -4,29 +4,25 @@ fn main() { let target_os = env::var("CARGO_CFG_TARGET_OS"); match target_os.as_ref().map(|x| &**x) { - Ok("linux") | Ok("android") => {}, - Ok("freebsd") | Ok("dragonfly") => {}, + Ok("linux") | Ok("android") => {} + Ok("freebsd") | Ok("dragonfly") => {} Ok("openbsd") | Ok("bitrig") | Ok("netbsd") | Ok("macos") | Ok("ios") => {} Ok("windows") => link_exports(), - tos => panic!("unknown target os {:?}!", tos) + tos => panic!("unknown target os {:?}!", tos), } } /// links Exports.def to the resulting dll, exporting all our asm functions. fn link_exports() { - use std::path::Path; - let lib_path = Path::new("deps").join("Exports.def"); - let absolute_path = std::fs::canonicalize(&lib_path).unwrap().to_str().unwrap().to_string().replace("\\\\?\\", ""); + let lib_path = env::current_dir().unwrap().join("deps").join("Exports.def"); - if !Path::new(&absolute_path).exists() { - panic!("Exports.def not found at {}", lib_path.display()); + + if !lib_path.exists() { + println!("cargo:error=Exports.def not found at {}", lib_path.display()); } - println!("cargo:warning=Linking Exports File: {}", absolute_path); - println!( - "cargo:rustc-cdylib-link-arg=/DEF:{}", - absolute_path - ); -} \ No newline at end of file + println!("cargo:warning=Linking Exports File: {}", lib_path.display()); + println!("cargo:rustc-cdylib-link-arg=/DEF:{}", lib_path.display()); +} From a71a25fc4fa9112a905c89e225afa1fe9a53a9ec Mon Sep 17 00:00:00 2001 From: Sarah Date: Sat, 30 Dec 2023 21:22:59 +0100 Subject: [PATCH 63/79] Try another way of exporting functions --- MelonProxy/build.rs | 12 +- MelonProxy/deps/Exports.def | 528 ++++++++++++++++++------------------ 2 files changed, 272 insertions(+), 268 deletions(-) diff --git a/MelonProxy/build.rs b/MelonProxy/build.rs index 5383971ce..83702c986 100644 --- a/MelonProxy/build.rs +++ b/MelonProxy/build.rs @@ -10,7 +10,7 @@ fn main() { Ok("windows") => link_exports(), - tos => panic!("unknown target os {:?}!", tos), + tos => println!("cargo:error=unknown target os {:?}!", tos), } } @@ -23,6 +23,12 @@ fn link_exports() { println!("cargo:error=Exports.def not found at {}", lib_path.display()); } - println!("cargo:warning=Linking Exports File: {}", lib_path.display()); - println!("cargo:rustc-cdylib-link-arg=/DEF:{}", lib_path.display()); + let lines = std::fs::read_to_string(lib_path).unwrap(); + let lines = lines.split("\n"); + let lines = lines.filter(|x| !x.is_empty()); + + //export each function + for line in lines { + println!("cargo:rustc-cdylib-link-arg=/EXPORT:{}", line); + } } diff --git a/MelonProxy/deps/Exports.def b/MelonProxy/deps/Exports.def index e274da2be..9e4416cc5 100644 --- a/MelonProxy/deps/Exports.def +++ b/MelonProxy/deps/Exports.def @@ -1,266 +1,264 @@ -EXPORTS - GetFileVersionInfoA - GetFileVersionInfoByHandle - GetFileVersionInfoExA - GetFileVersionInfoExW - GetFileVersionInfoSizeA - GetFileVersionInfoSizeExA - GetFileVersionInfoSizeExW - GetFileVersionInfoSizeW - GetFileVersionInfoW - VerFindFileA - VerFindFileW - VerInstallFileA - VerInstallFileW - VerLanguageNameA - VerLanguageNameW - VerQueryValueA - VerQueryValueW - - Private1 - SvchostPushServiceGlobals - WinHttpAddRequestHeaders - WinHttpAutoProxySvcMain - WinHttpCheckPlatform - WinHttpCloseHandle - WinHttpConnect - WinHttpConnectionDeletePolicyEntries - WinHttpConnectionDeleteProxyInfo - WinHttpConnectionFreeNameList - WinHttpConnectionFreeProxyInfo - WinHttpConnectionFreeProxyList - WinHttpConnectionGetNameList - WinHttpConnectionGetProxyInfo - WinHttpConnectionGetProxyList - WinHttpConnectionSetPolicyEntries - WinHttpConnectionSetProxyInfo - WinHttpConnectionUpdateIfIndexTable - WinHttpCrackUrl - WinHttpCreateProxyResolver - WinHttpCreateUrl - WinHttpDetectAutoProxyConfigUrl - WinHttpFreeProxyResult - WinHttpFreeProxyResultEx - WinHttpFreeProxySettings - WinHttpGetDefaultProxyConfiguration - WinHttpGetIEProxyConfigForCurrentUser - WinHttpGetProxyForUrl - WinHttpGetProxyForUrlEx - WinHttpGetProxyForUrlEx2 - WinHttpGetProxyForUrlHvsi - WinHttpGetProxyResult - WinHttpGetProxyResultEx - WinHttpGetProxySettingsVersion - WinHttpGetTunnelSocket - WinHttpOpen - WinHttpOpenRequest - WinHttpPacJsWorkerMain - WinHttpProbeConnectivity - WinHttpQueryAuthSchemes - WinHttpQueryDataAvailable - WinHttpQueryHeaders - WinHttpQueryOption - WinHttpReadData - WinHttpReadProxySettings - WinHttpReadProxySettingsHvsi - WinHttpReceiveResponse - WinHttpResetAutoProxy - WinHttpSaveProxyCredentials - WinHttpSendRequest - WinHttpSetCredentials - WinHttpSetDefaultProxyConfiguration - WinHttpSetOption - WinHttpSetStatusCallback - WinHttpSetTimeouts - WinHttpTimeFromSystemTime - WinHttpTimeToSystemTime - WinHttpWebSocketClose - WinHttpWebSocketCompleteUpgrade - WinHttpWebSocketQueryCloseStatus - WinHttpWebSocketReceive - WinHttpWebSocketSend - WinHttpWebSocketShutdown - WinHttpWriteData - WinHttpWriteProxySettings +GetFileVersionInfoA +GetFileVersionInfoByHandle +GetFileVersionInfoExA +GetFileVersionInfoExW +GetFileVersionInfoSizeA +GetFileVersionInfoSizeExA +GetFileVersionInfoSizeExW +GetFileVersionInfoSizeW +GetFileVersionInfoW +VerFindFileA +VerFindFileW +VerInstallFileA +VerInstallFileW +VerLanguageNameA +VerLanguageNameW +VerQueryValueA +VerQueryValueW - CloseDriver - DefDriverProc - DriverCallback - DrvGetModuleHandle - GetDriverModuleHandle - OpenDriver - PlaySound - PlaySoundA - PlaySoundW - SendDriverMessage - WOWAppExit - auxGetDevCapsA - auxGetDevCapsW - auxGetNumDevs - auxGetVolume - auxOutMessage - auxSetVolume - joyConfigChanged - joyGetDevCapsA - joyGetDevCapsW - joyGetNumDevs - joyGetPos - joyGetPosEx - joyGetThreshold - joyReleaseCapture - joySetCapture - joySetThreshold - mciDriverNotify - mciDriverYield - mciExecute - mciFreeCommandResource - mciGetCreatorTask - mciGetDeviceIDA - mciGetDeviceIDFromElementIDA - mciGetDeviceIDFromElementIDW - mciGetDeviceIDW - mciGetDriverData - mciGetErrorStringA - mciGetErrorStringW - mciGetYieldProc - mciLoadCommandResource - mciSendCommandA - mciSendCommandW - mciSendStringA - mciSendStringW - mciSetDriverData - mciSetYieldProc - midiConnect - midiDisconnect - midiInAddBuffer - midiInClose - midiInGetDevCapsA - midiInGetDevCapsW - midiInGetErrorTextA - midiInGetErrorTextW - midiInGetID - midiInGetNumDevs - midiInMessage - midiInOpen - midiInPrepareHeader - midiInReset - midiInStart - midiInStop - midiInUnprepareHeader - midiOutCacheDrumPatches - midiOutCachePatches - midiOutClose - midiOutGetDevCapsA - midiOutGetDevCapsW - midiOutGetErrorTextA - midiOutGetErrorTextW - midiOutGetID - midiOutGetNumDevs - midiOutGetVolume - midiOutLongMsg - midiOutMessage - midiOutOpen - midiOutPrepareHeader - midiOutReset - midiOutSetVolume - midiOutShortMsg - midiOutUnprepareHeader - midiStreamClose - midiStreamOpen - midiStreamOut - midiStreamPause - midiStreamPosition - midiStreamProperty - midiStreamRestart - midiStreamStop - mixerClose - mixerGetControlDetailsA - mixerGetControlDetailsW - mixerGetDevCapsA - mixerGetDevCapsW - mixerGetID - mixerGetLineControlsA - mixerGetLineControlsW - mixerGetLineInfoA - mixerGetLineInfoW - mixerGetNumDevs - mixerMessage - mixerOpen - mixerSetControlDetails - mmDrvInstall - mmGetCurrentTask - mmTaskBlock - mmTaskCreate - mmTaskSignal - mmTaskYield - mmioAdvance - mmioAscend - mmioClose - mmioCreateChunk - mmioDescend - mmioFlush - mmioGetInfo - mmioInstallIOProcA - mmioInstallIOProcW - mmioOpenA - mmioOpenW - mmioRead - mmioRenameA - mmioRenameW - mmioSeek - mmioSendMessage - mmioSetBuffer - mmioSetInfo - mmioStringToFOURCCA - mmioStringToFOURCCW - mmioWrite - mmsystemGetVersion - sndPlaySoundA - sndPlaySoundW - timeBeginPeriod - timeEndPeriod - timeGetDevCaps - timeGetSystemTime - timeGetTime - timeKillEvent - timeSetEvent - waveInAddBuffer - waveInClose - waveInGetDevCapsA - waveInGetDevCapsW - waveInGetErrorTextA - waveInGetErrorTextW - waveInGetID - waveInGetNumDevs - waveInGetPosition - waveInMessage - waveInOpen - waveInPrepareHeader - waveInReset - waveInStart - waveInStop - waveInUnprepareHeader - waveOutBreakLoop - waveOutClose - waveOutGetDevCapsA - waveOutGetDevCapsW - waveOutGetErrorTextA - waveOutGetErrorTextW - waveOutGetID - waveOutGetNumDevs - waveOutGetPitch - waveOutGetPlaybackRate - waveOutGetPosition - waveOutGetVolume - waveOutMessage - waveOutOpen - waveOutPause - waveOutPrepareHeader - waveOutReset - waveOutRestart - waveOutSetPitch - waveOutSetPlaybackRate - waveOutSetVolume - waveOutUnprepareHeader - waveOutWrite - ExportByOrdinal2 \ No newline at end of file +Private1 +SvchostPushServiceGlobals +WinHttpAddRequestHeaders +WinHttpAutoProxySvcMain +WinHttpCheckPlatform +WinHttpCloseHandle +WinHttpConnect +WinHttpConnectionDeletePolicyEntries +WinHttpConnectionDeleteProxyInfo +WinHttpConnectionFreeNameList +WinHttpConnectionFreeProxyInfo +WinHttpConnectionFreeProxyList +WinHttpConnectionGetNameList +WinHttpConnectionGetProxyInfo +WinHttpConnectionGetProxyList +WinHttpConnectionSetPolicyEntries +WinHttpConnectionSetProxyInfo +WinHttpConnectionUpdateIfIndexTable +WinHttpCrackUrl +WinHttpCreateProxyResolver +WinHttpCreateUrl +WinHttpDetectAutoProxyConfigUrl +WinHttpFreeProxyResult +WinHttpFreeProxyResultEx +WinHttpFreeProxySettings +WinHttpGetDefaultProxyConfiguration +WinHttpGetIEProxyConfigForCurrentUser +WinHttpGetProxyForUrl +WinHttpGetProxyForUrlEx +WinHttpGetProxyForUrlEx2 +WinHttpGetProxyForUrlHvsi +WinHttpGetProxyResult +WinHttpGetProxyResultEx +WinHttpGetProxySettingsVersion +WinHttpGetTunnelSocket +WinHttpOpen +WinHttpOpenRequest +WinHttpPacJsWorkerMain +WinHttpProbeConnectivity +WinHttpQueryAuthSchemes +WinHttpQueryDataAvailable +WinHttpQueryHeaders +WinHttpQueryOption +WinHttpReadData +WinHttpReadProxySettings +WinHttpReadProxySettingsHvsi +WinHttpReceiveResponse +WinHttpResetAutoProxy +WinHttpSaveProxyCredentials +WinHttpSendRequest +WinHttpSetCredentials +WinHttpSetDefaultProxyConfiguration +WinHttpSetOption +WinHttpSetStatusCallback +WinHttpSetTimeouts +WinHttpTimeFromSystemTime +WinHttpTimeToSystemTime +WinHttpWebSocketClose +WinHttpWebSocketCompleteUpgrade +WinHttpWebSocketQueryCloseStatus +WinHttpWebSocketReceive +WinHttpWebSocketSend +WinHttpWebSocketShutdown +WinHttpWriteData +WinHttpWriteProxySettings +CloseDriver +DefDriverProc +DriverCallback +DrvGetModuleHandle +GetDriverModuleHandle +OpenDriver +PlaySound +PlaySoundA +PlaySoundW +SendDriverMessage +WOWAppExit +auxGetDevCapsA +auxGetDevCapsW +auxGetNumDevs +auxGetVolume +auxOutMessage +auxSetVolume +joyConfigChanged +joyGetDevCapsA +joyGetDevCapsW +joyGetNumDevs +joyGetPos +joyGetPosEx +joyGetThreshold +joyReleaseCapture +joySetCapture +joySetThreshold +mciDriverNotify +mciDriverYield +mciExecute +mciFreeCommandResource +mciGetCreatorTask +mciGetDeviceIDA +mciGetDeviceIDFromElementIDA +mciGetDeviceIDFromElementIDW +mciGetDeviceIDW +mciGetDriverData +mciGetErrorStringA +mciGetErrorStringW +mciGetYieldProc +mciLoadCommandResource +mciSendCommandA +mciSendCommandW +mciSendStringA +mciSendStringW +mciSetDriverData +mciSetYieldProc +midiConnect +midiDisconnect +midiInAddBuffer +midiInClose +midiInGetDevCapsA +midiInGetDevCapsW +midiInGetErrorTextA +midiInGetErrorTextW +midiInGetID +midiInGetNumDevs +midiInMessage +midiInOpen +midiInPrepareHeader +midiInReset +midiInStart +midiInStop +midiInUnprepareHeader +midiOutCacheDrumPatches +midiOutCachePatches +midiOutClose +midiOutGetDevCapsA +midiOutGetDevCapsW +midiOutGetErrorTextA +midiOutGetErrorTextW +midiOutGetID +midiOutGetNumDevs +midiOutGetVolume +midiOutLongMsg +midiOutMessage +midiOutOpen +midiOutPrepareHeader +midiOutReset +midiOutSetVolume +midiOutShortMsg +midiOutUnprepareHeader +midiStreamClose +midiStreamOpen +midiStreamOut +midiStreamPause +midiStreamPosition +midiStreamProperty +midiStreamRestart +midiStreamStop +mixerClose +mixerGetControlDetailsA +mixerGetControlDetailsW +mixerGetDevCapsA +mixerGetDevCapsW +mixerGetID +mixerGetLineControlsA +mixerGetLineControlsW +mixerGetLineInfoA +mixerGetLineInfoW +mixerGetNumDevs +mixerMessage +mixerOpen +mixerSetControlDetails +mmDrvInstall +mmGetCurrentTask +mmTaskBlock +mmTaskCreate +mmTaskSignal +mmTaskYield +mmioAdvance +mmioAscend +mmioClose +mmioCreateChunk +mmioDescend +mmioFlush +mmioGetInfo +mmioInstallIOProcA +mmioInstallIOProcW +mmioOpenA +mmioOpenW +mmioRead +mmioRenameA +mmioRenameW +mmioSeek +mmioSendMessage +mmioSetBuffer +mmioSetInfo +mmioStringToFOURCCA +mmioStringToFOURCCW +mmioWrite +mmsystemGetVersion +sndPlaySoundA +sndPlaySoundW +timeBeginPeriod +timeEndPeriod +timeGetDevCaps +timeGetSystemTime +timeGetTime +timeKillEvent +timeSetEvent +waveInAddBuffer +waveInClose +waveInGetDevCapsA +waveInGetDevCapsW +waveInGetErrorTextA +waveInGetErrorTextW +waveInGetID +waveInGetNumDevs +waveInGetPosition +waveInMessage +waveInOpen +waveInPrepareHeader +waveInReset +waveInStart +waveInStop +waveInUnprepareHeader +waveOutBreakLoop +waveOutClose +waveOutGetDevCapsA +waveOutGetDevCapsW +waveOutGetErrorTextA +waveOutGetErrorTextW +waveOutGetID +waveOutGetNumDevs +waveOutGetPitch +waveOutGetPlaybackRate +waveOutGetPosition +waveOutGetVolume +waveOutMessage +waveOutOpen +waveOutPause +waveOutPrepareHeader +waveOutReset +waveOutRestart +waveOutSetPitch +waveOutSetPlaybackRate +waveOutSetVolume +waveOutUnprepareHeader +waveOutWrite +ExportByOrdinal2 \ No newline at end of file From d4d446909687515cc47fc6de976f733a0e607ab7 Mon Sep 17 00:00:00 2001 From: Sarah Date: Sat, 30 Dec 2023 21:42:09 +0100 Subject: [PATCH 64/79] Update build.yml --- .github/workflows/build.yml | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8260cc191..9d7e13a61 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,14 +38,19 @@ jobs: path: Output/Release/MelonLoader/ build_rust_windows: runs-on: windows-latest + strategy: + matrix: + target: + - i686-pc-windows-msvc + - x86_64-pc-windows-msvc steps: - uses: actions/checkout@v3 - name: rust-toolchain - uses: dtolnay/rust-toolchain@stable + uses: actions-rs/toolchain@v1 with: toolchain: nightly # Target triple to install for this toolchain - targets: i686-pc-windows-msvc, x86_64-pc-windows-msvc + target: ${{ matrix.target }} # Build Rust Release - name: Build Rust Release | Windows - x86 shell: cmd @@ -110,14 +115,18 @@ jobs: path: target/x86_64-pc-windows-msvc/debug/Bootstrap.dll build_rust_linux: runs-on: ubuntu-latest + strategy: + matrix: + target: + - x86_64-unknown-linux-gnu steps: - uses: actions/checkout@v3 - name: rust-toolchain - uses: dtolnay/rust-toolchain@stable + uses: actions-rs/toolchain@v1 with: toolchain: nightly # Target triple to install for this toolchain - targets: x86_64-unknown-linux-gnu + target: ${{ matrix.target }} - name: install dev dependencies shell: bash run: sudo apt-get install libgtk-3-dev From 55843f5c784855b086cac96c40fccc5d20a2706f Mon Sep 17 00:00:00 2001 From: Sarah Date: Sat, 30 Dec 2023 21:48:53 +0100 Subject: [PATCH 65/79] Update build.yml --- .github/workflows/build.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9d7e13a61..2b8b1d372 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,19 +38,21 @@ jobs: path: Output/Release/MelonLoader/ build_rust_windows: runs-on: windows-latest - strategy: - matrix: - target: - - i686-pc-windows-msvc - - x86_64-pc-windows-msvc steps: - uses: actions/checkout@v3 - - name: rust-toolchain + - name: rust-toolchain x64 uses: actions-rs/toolchain@v1 with: toolchain: nightly # Target triple to install for this toolchain - target: ${{ matrix.target }} + target: x86_64-pc-windows-msvc + - name: rust-toolchain x86 + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + # Target triple to install for this toolchain + target: i686-pc-windows-msvc + override: true # Build Rust Release - name: Build Rust Release | Windows - x86 shell: cmd From b60661908e37794ecaf15787d740e800f30cfe03 Mon Sep 17 00:00:00 2001 From: Sarah Date: Sat, 30 Dec 2023 23:04:45 +0100 Subject: [PATCH 66/79] Update build.yml --- .github/workflows/build.yml | 41 ++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2b8b1d372..11e92b3a8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -40,19 +40,15 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@v3 - - name: rust-toolchain x64 - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - # Target triple to install for this toolchain - target: x86_64-pc-windows-msvc - - name: rust-toolchain x86 - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - # Target triple to install for this toolchain - target: i686-pc-windows-msvc - override: true + - name: Install Rust (nightly) + run: + curl https://sh.rustup.rs -sSf | sh -s -- -y + - name: Make Rust Nightly + shell: cmd + run: rustup default nightly + - name: Install target x86 + shell: cmd + run: rustup target add i686-pc-windows-msvc # Build Rust Release - name: Build Rust Release | Windows - x86 shell: cmd @@ -117,18 +113,17 @@ jobs: path: target/x86_64-pc-windows-msvc/debug/Bootstrap.dll build_rust_linux: runs-on: ubuntu-latest - strategy: - matrix: - target: - - x86_64-unknown-linux-gnu steps: - uses: actions/checkout@v3 - - name: rust-toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - # Target triple to install for this toolchain - target: ${{ matrix.target }} + - name: Install Rust (nightly) + run: + curl https://sh.rustup.rs -sSf | sh -s -- -y + - name: Make Rust Nightly + shell: bash + run: rustup default nightly + - name: Install target x64 + shell: bash + run: rustup target add x86_64-unknown-linux-gnu - name: install dev dependencies shell: bash run: sudo apt-get install libgtk-3-dev From b20b4107485233ec9e65b7bcd4289abd81065b13 Mon Sep 17 00:00:00 2001 From: Sarah Date: Thu, 4 Jan 2024 07:11:45 +0100 Subject: [PATCH 67/79] new proxy (again) and streamlined build.yml --- .github/workflows/build.yml | 161 ++-- Bootstrap/Cargo.toml | 4 +- Bootstrap/src/melonenv/paths.rs | 6 +- Bootstrap/src/utils/runtime.rs | 3 +- Cargo.lock | 199 ++--- Cargo.toml | 2 +- MelonProxy-sys/Cargo.toml | 20 - MelonProxy-sys/src/lib.rs | 99 --- MelonProxy/Cargo.toml | 33 +- MelonProxy/build.rs | 34 - MelonProxy/deps/Exports.def | 264 ------- MelonProxy/deps/version.x64.S | 52 -- MelonProxy/deps/version.x86.S | 52 -- MelonProxy/deps/winhttp.x64.S | 196 ----- MelonProxy/deps/winhttp.x86.S | 260 ------- MelonProxy/deps/winmm.x64.S | 724 ------------------ MelonProxy/deps/winmm.x86.S | 724 ------------------ MelonProxy/src/export_indices.rs | 256 +++++++ MelonProxy/src/intercepted_exports.rs | 44 ++ MelonProxy/src/lib.rs | 214 +++++- MelonProxy/src/orig_exports.rs | 276 +++++++ MelonProxy/src/proxied_exports.rs | 1007 +++++++++++++++++++++++++ MelonProxy/src/proxy/exports.rs | 316 -------- MelonProxy/src/proxy/hinstance_ext.rs | 62 -- MelonProxy/src/proxy/mod.rs | 3 - MelonProxy/src/proxy/windows_ext.rs | 23 - 26 files changed, 1966 insertions(+), 3068 deletions(-) delete mode 100644 MelonProxy-sys/Cargo.toml delete mode 100644 MelonProxy-sys/src/lib.rs delete mode 100644 MelonProxy/build.rs delete mode 100644 MelonProxy/deps/Exports.def delete mode 100644 MelonProxy/deps/version.x64.S delete mode 100644 MelonProxy/deps/version.x86.S delete mode 100644 MelonProxy/deps/winhttp.x64.S delete mode 100644 MelonProxy/deps/winhttp.x86.S delete mode 100644 MelonProxy/deps/winmm.x64.S delete mode 100644 MelonProxy/deps/winmm.x86.S create mode 100644 MelonProxy/src/export_indices.rs create mode 100644 MelonProxy/src/intercepted_exports.rs create mode 100644 MelonProxy/src/orig_exports.rs create mode 100644 MelonProxy/src/proxied_exports.rs delete mode 100644 MelonProxy/src/proxy/exports.rs delete mode 100644 MelonProxy/src/proxy/hinstance_ext.rs delete mode 100644 MelonProxy/src/proxy/mod.rs delete mode 100644 MelonProxy/src/proxy/windows_ext.rs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 11e92b3a8..777ccf7cf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,62 +8,28 @@ on: workflow_dispatch: jobs: - build_core_debug: + build_melonloader: runs-on: windows-latest steps: - uses: actions/checkout@v3 - - name: setup-msbuild + - name: Setup MsBuild uses: microsoft/setup-msbuild@v1 - - name: Build Melonloader Core - shell: cmd - run: msbuild /restore /p:Platform="Windows - x64" # Platform is actually irrelevant for core, it's compiled as AnyCPU either way - - name: Upload core artifact - uses: actions/upload-artifact@v3 - with: - name: MLCoreDebug - path: Output/Debug/MelonLoader/ - build_core_release: - runs-on: windows-latest - steps: - - uses: actions/checkout@v3 - - name: setup-msbuild - uses: microsoft/setup-msbuild@v1 - - name: Build Melonloader Core + - name: Build Melonloader (Release) shell: cmd run: msbuild /restore /p:Configuration=Release /p:Platform="Windows - x64" - - name: Upload core artifact + - name: Build MelonLoader (Debug) + shell: cmd + run: msbuild /restore /p:Platform="Windows - x64" + - name: Upload Release Artifact uses: actions/upload-artifact@v3 with: - name: MLCoreRelease + name: MLRelease path: Output/Release/MelonLoader/ - build_rust_windows: - runs-on: windows-latest - steps: - - uses: actions/checkout@v3 - - name: Install Rust (nightly) - run: - curl https://sh.rustup.rs -sSf | sh -s -- -y - - name: Make Rust Nightly - shell: cmd - run: rustup default nightly - - name: Install target x86 - shell: cmd - run: rustup target add i686-pc-windows-msvc - # Build Rust Release - - name: Build Rust Release | Windows - x86 - shell: cmd - run: cargo +nightly build --target i686-pc-windows-msvc --release - - name: Build Rust Release | Windows - x64 - shell: cmd - run: cargo +nightly build --target x86_64-pc-windows-msvc --release - # Build Rust Debug - - name: Build Rust Debug | Windows - x86 - shell: cmd - run: cargo +nightly build --target i686-pc-windows-msvc - - name: Build Rust Debug | Windows - x64 - shell: cmd - run: cargo +nightly build --target x86_64-pc-windows-msvc - # Upload Proxy Release - x86 + - name: Upload Debug Artifact + uses: actions/upload-artifact@v3 + with: + name: MLDebug + path: Output/Debug/MelonLoader/ - name: Upload Proxy Release | Windows x86 uses: actions/upload-artifact@v3 with: @@ -111,7 +77,7 @@ jobs: with: name: MLBootstrapX64-Windows-Debug path: target/x86_64-pc-windows-msvc/debug/Bootstrap.dll - build_rust_linux: + build_rust: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -121,18 +87,57 @@ jobs: - name: Make Rust Nightly shell: bash run: rustup default nightly - - name: Install target x64 + - name: Install Linux target x64 shell: bash run: rustup target add x86_64-unknown-linux-gnu + - name: Install Windows target x64 + shell: bash + run: rustup target add x86_64-pc-windows-gnu + - name: Install Windows target x86 + shell: bash + run: rustup target add i686-pc-windows-gnu + - name: Install Windows target x64 (MSVC) + shell: bash + run: rustup target add x86_64-pc-windows-msvc + - name: Install Windows target x86 (MSVC) + shell: bash + run: rustup target add i686-pc-windows-msvc + - name: Install x-win + shell: bash + run: cargo install cargo-xwin - name: install dev dependencies shell: bash - run: sudo apt-get install libgtk-3-dev + run: sudo apt-get install libgtk-3-dev mingw-w64 binutils-mingw-w64 wine - name: Build Rust Release | Linux - x64 shell: bash - run: cargo +nightly build --target x86_64-unknown-linux-gnu --release + run: cargo build --target x86_64-unknown-linux-gnu --release - name: Build Rust Debug | Linux - x64 shell: bash - run: cargo +nightly build --target x86_64-unknown-linux-gnu + run: cargo build --target x86_64-unknown-linux-gnu + - name: Build Bootstrap Release | Windows - x64 + shell: bash + run: cargo xwin build --package Bootstrap --target x86_64-pc-windows-msvc --release + - name: Build Bootstrap Debug | Windows - x64 + shell: bash + run: cargo xwin build --package Bootstrap --target x86_64-pc-windows-msvc + - name: Build Bootstrap Release | Windows - x86 + shell: bash + run: cargo xwin build --package Bootstrap --target i686-pc-windows-msvc --release + - name: Build Bootstrap Debug | Windows - x86 + shell: bash + run: cargo xwin build --package Bootstrap --target i686-pc-windows-msvc + - name: Build Proxy Release | Windows - x64 + shell: bash + run: cargo build --package MelonProxy --target x86_64-pc-windows-gnu --release + - name: Build Proxy Debug | Windows - x64 + shell: bash + run: cargo build --package MelonProxy --target x86_64-pc-windows-gnu + - name: Build Proxy Release | Windows - x86 + shell: bash + run: cargo build --package MelonProxy --target i686-pc-windows-gnu --release + - name: Build Proxy Debug | Windows - x86 + shell: bash + run: cargo build --package MelonProxy --target i686-pc-windows-gnu - name: Upload Proxy Release | Linux x64 uses: actions/upload-artifact@v3 with: @@ -153,9 +158,49 @@ jobs: with: name: MLBootstrapX64-Linux-Debug path: target/x86_64-unknown-linux-gnu/debug/libBootstrap.so + - name: Upload Proxy Release | Windows x86 + uses: actions/upload-artifact@v3 + with: + name: MLProxyX86-Windows-Release + path: target/i686-pc-windows-gnu/release/version.dll + - name: Upload Bootstrap Release | Windows x86 + uses: actions/upload-artifact@v3 + with: + name: MLBootstrapX86-Windows-Release + path: target/i686-pc-windows-msvc/release/Bootstrap.dll + - name: Upload Proxy Release | Windows x64 + uses: actions/upload-artifact@v3 + with: + name: MLProxyX64-Windows-Release + path: target/x86_64-pc-windows-gnu/release/version.dll + - name: Upload Bootstrap Release | Windows x64 + uses: actions/upload-artifact@v3 + with: + name: MLBootstrapX64-Windows-Release + path: target/x86_64-pc-windows-msvc/release/Bootstrap.dll + - name: Upload Proxy Debug | Windows x86 + uses: actions/upload-artifact@v3 + with: + name: MLProxyX86-Windows-Debug + path: target/i686-pc-windows-gnu/debug/version.dll + - name: Upload Bootstrap Debug | Windows x86 + uses: actions/upload-artifact@v3 + with: + name: MLBootstrapX86-Windows-Debug + path: target/i686-pc-windows-msvc/debug/Bootstrap.dll + - name: Upload Proxy Debug | Windows x64 + uses: actions/upload-artifact@v3 + with: + name: MLProxyX64-Windows-Debug + path: target/x86_64-pc-windows-gnu/debug/version.dll + - name: Upload Bootstrap Debug | Windows x64 + uses: actions/upload-artifact@v3 + with: + name: MLBootstrapX64-Windows-Debug + path: target/x86_64-pc-windows-msvc/debug/Bootstrap.dll finalize_x64_debug_zip_windows: runs-on: windows-latest - needs: [build_core_debug, build_rust_windows] + needs: [build_rust, build_melonloader] steps: - uses: actions/checkout@v3 - name: Download core artifact @@ -197,7 +242,7 @@ jobs: path: ./Output/Debug/x64/* finalize_x86_debug_zip_windows: runs-on: windows-latest - needs: [build_core_debug, build_rust_windows] + needs: [build_rust, build_melonloader] steps: - uses: actions/checkout@v3 - name: Download core artifact @@ -239,7 +284,7 @@ jobs: path: ./Output/Debug/x86/* finalize_x64_release_zip_windows: runs-on: windows-latest - needs: [build_core_release, build_rust_windows] + needs: [build_rust, build_melonloader] steps: - uses: actions/checkout@v3 - name: Download core artifact @@ -281,7 +326,7 @@ jobs: path: ./Output/Release/x64/* finalize_x86_release_zip_windows: runs-on: windows-latest - needs: [build_core_release, build_rust_windows] + needs: [build_rust, build_melonloader] steps: - uses: actions/checkout@v3 - name: Download core artifact @@ -323,7 +368,7 @@ jobs: path: ./Output/Release/x86/* finalize_x64_debug_zip_linux: runs-on: windows-latest - needs: [build_core_debug, build_rust_linux] + needs: [build_rust, build_melonloader] steps: - uses: actions/checkout@v3 - name: Download core artifact @@ -362,7 +407,7 @@ jobs: path: ./Output/Debug/x64/* finalize_x64_release_zip_linux: runs-on: windows-latest - needs: [build_core_release, build_rust_linux] + needs: [build_rust, build_melonloader] steps: - uses: actions/checkout@v3 - name: Download core artifact diff --git a/Bootstrap/Cargo.toml b/Bootstrap/Cargo.toml index 9bf9f33d4..30e1f18fb 100755 --- a/Bootstrap/Cargo.toml +++ b/Bootstrap/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" [dependencies] #unity-rs = { path = "C:/Users/sarah/Documents/rust/Ferrex/unity/" } unity-rs = { git = "https://github.com/RinLovesYou/Ferrex/", rev = "77d114c" } -ctor = "0.1.26" +ctor = "0.2.6" chrono = "0.4.23" colored = "2.0.0" thiserror = "1.0.39" @@ -22,7 +22,7 @@ netcorehost = "0.15.1" exe = "0.5.6" [target.'cfg(windows)'.dependencies] -windows = { version = "0.51.0", features = [ +windows = { version = "0.52.0", features = [ "Win32_Foundation", "Win32_System_Console", "Win32_UI_WindowsAndMessaging" diff --git a/Bootstrap/src/melonenv/paths.rs b/Bootstrap/src/melonenv/paths.rs index 040f0df66..db8b11d7c 100644 --- a/Bootstrap/src/melonenv/paths.rs +++ b/Bootstrap/src/melonenv/paths.rs @@ -1,11 +1,11 @@ -use std::path::{PathBuf, Path}; +use std::path::{PathBuf}; use lazy_static::lazy_static; use unity_rs::runtime::RuntimeType; -use crate::{errors::DynErr, internal_failure, runtime, constants::W, utils::runtime::{self, NetstandardVersion}}; +use crate::{errors::DynErr, internal_failure, runtime, constants::W}; + -use super::args::ARGS; lazy_static! { pub static ref BASE_DIR: W = { diff --git a/Bootstrap/src/utils/runtime.rs b/Bootstrap/src/utils/runtime.rs index d657995c5..84fbf0c54 100644 --- a/Bootstrap/src/utils/runtime.rs +++ b/Bootstrap/src/utils/runtime.rs @@ -1,9 +1,8 @@ use std::{error::Error, collections::HashMap, io, path::Path}; -use exe::{ImageDirectoryEntry, ResourceDirectory, VecPE, PE, ImportDirectory, ImportData, CCharString, ImageDataDirectory, ImageResourceDirectory}; use unity_rs::runtime::FerrexRuntime; -use crate::{errors::DynErr, log, melonenv::paths}; +use crate::{errors::DynErr, melonenv::paths}; #[allow(dead_code)] pub static mut RUNTIME: Option = None; diff --git a/Cargo.lock b/Cargo.lock index 74267c499..fc296188d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,7 +9,7 @@ dependencies = [ "chrono", "clap", "colored", - "ctor 0.1.26", + "ctor", "dobby-rs", "exe", "lazy_static", @@ -19,7 +19,18 @@ dependencies = [ "netcorehost", "thiserror", "unity-rs", - "windows 0.51.0", + "windows 0.52.0", +] + +[[package]] +name = "MelonProxy" +version = "0.1.0" +dependencies = [ + "ctor", + "libloading 0.8.1", + "msgbox", + "proxygen-macros", + "winapi", ] [[package]] @@ -134,9 +145,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "block" @@ -265,17 +276,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.26" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", - "time 0.1.45", "wasm-bindgen", - "winapi", + "windows-targets 0.48.5", ] [[package]] @@ -466,16 +476,6 @@ dependencies = [ "quote", ] -[[package]] -name = "ctor" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" -dependencies = [ - "quote", - "syn 1.0.109", -] - [[package]] name = "ctor" version = "0.2.6" @@ -483,7 +483,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30d2b3721e861707777e3195b0158f950ae6dc4a27e4d02ff9f67e3eb3de199e" dependencies = [ "quote", - "syn 2.0.43", + "syn 2.0.44", ] [[package]] @@ -526,7 +526,7 @@ checksum = "133a7fa5cffeec6867fb2847335ec2d688f5bbee6318889d2a137ce1d226b180" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.44", ] [[package]] @@ -569,7 +569,7 @@ checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.44", ] [[package]] @@ -584,7 +584,7 @@ dependencies = [ [[package]] name = "dobby-sys" version = "0.1.0" -source = "git+https://github.com/RinLovesYou/dobby-sys.git#8ea9f0800de90b07073561306fe258bef973930f" +source = "git+https://github.com/RinLovesYou/dobby-sys.git#3001d5debc9a8b53fdbd7bf7224b16c584c26fbf" [[package]] name = "encoding_rs" @@ -612,7 +612,7 @@ checksum = "ccb14d927583dd5c2eac0f2cf264fc4762aefe1ae14c47a8a20fc1939d3a5fc0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.44", ] [[package]] @@ -756,7 +756,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.44", ] [[package]] @@ -1281,9 +1281,9 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" dependencies = [ "cfg-if", "windows-sys", @@ -1321,24 +1321,6 @@ dependencies = [ "opaque-debug", ] -[[package]] -name = "melon_proxy" -version = "0.2.5" -dependencies = [ - "ctor 0.2.6", - "libloading 0.8.0", - "melon_proxy-sys", - "msgbox", - "windows 0.52.0", -] - -[[package]] -name = "melon_proxy-sys" -version = "0.2.2" -dependencies = [ - "syn 2.0.43", -] - [[package]] name = "memchr" version = "2.5.0" @@ -1376,7 +1358,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys", ] @@ -1432,9 +1414,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] @@ -1466,7 +1448,7 @@ checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.44", ] [[package]] @@ -1634,18 +1616,27 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.71" +version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" +checksum = "2dd5e8a1f1029c43224ad5898e50140c2aebb1705f19e67c918ebf5b9e797fe1" dependencies = [ "unicode-ident", ] +[[package]] +name = "proxygen-macros" +version = "0.5.0" +source = "git+https://github.com/RinLovesYou/proxygen#c549e4fad7cd50e1d3db252c97f2e1e94713d869" +dependencies = [ + "quote", + "syn 2.0.44", +] + [[package]] name = "quote" -version = "1.0.32" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +checksum = "22a37c9326af5ed140c86a46655b5278de879853be5573c01df185b6f49a580a" dependencies = [ "proc-macro2", ] @@ -1731,7 +1722,7 @@ version = "0.38.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", @@ -1808,7 +1799,7 @@ checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.44", ] [[package]] @@ -1957,9 +1948,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.43" +version = "2.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" +checksum = "92d27c2c202598d05175a6dd3af46824b7f747f8d8e9b14c623f19fa5069735d" dependencies = [ "proc-macro2", "quote", @@ -2011,18 +2002,7 @@ checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", -] - -[[package]] -name = "time" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", + "syn 2.0.44", ] [[package]] @@ -2239,12 +2219,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2272,7 +2246,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.44", "wasm-bindgen-shared", ] @@ -2306,7 +2280,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.44", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2389,17 +2363,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets 0.48.2", -] - -[[package]] -name = "windows" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9763fb813068e9f4ab70a92a0c6ad61ff6b342f693b1ed0e5387c854386e670" -dependencies = [ - "windows-core 0.51.0", - "windows-targets 0.48.2", + "windows-targets 0.48.5", ] [[package]] @@ -2408,19 +2372,10 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ - "windows-core 0.52.0", + "windows-core", "windows-targets 0.52.0", ] -[[package]] -name = "windows-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b81650771e76355778637954dc9d7eb8d991cd89ad64ba26f21eeb3c22d8d836" -dependencies = [ - "windows-targets 0.48.2", -] - [[package]] name = "windows-core" version = "0.52.0" @@ -2436,22 +2391,22 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.2", + "windows-targets 0.48.5", ] [[package]] name = "windows-targets" -version = "0.48.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1eeca1c172a285ee6c2c84c341ccea837e7c01b12fbb2d0fe3c9e550ce49ec8" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm 0.48.2", - "windows_aarch64_msvc 0.48.2", - "windows_i686_gnu 0.48.2", - "windows_i686_msvc 0.48.2", - "windows_x86_64_gnu 0.48.2", - "windows_x86_64_gnullvm 0.48.2", - "windows_x86_64_msvc 0.48.2", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -2471,9 +2426,9 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b10d0c968ba7f6166195e13d593af609ec2e3d24f916f081690695cf5eaffb2f" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" @@ -2483,9 +2438,9 @@ checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" [[package]] name = "windows_aarch64_msvc" -version = "0.48.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "571d8d4e62f26d4932099a9efe89660e8bd5087775a2ab5cdd8b747b811f1058" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" @@ -2495,9 +2450,9 @@ checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" [[package]] name = "windows_i686_gnu" -version = "0.48.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2229ad223e178db5fbbc8bd8d3835e51e566b8474bfca58d2e6150c48bb723cd" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" @@ -2507,9 +2462,9 @@ checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" [[package]] name = "windows_i686_msvc" -version = "0.48.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "600956e2d840c194eedfc5d18f8242bc2e17c7775b6684488af3a9fff6fe3287" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" @@ -2519,9 +2474,9 @@ checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" [[package]] name = "windows_x86_64_gnu" -version = "0.48.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea99ff3f8b49fb7a8e0d305e5aec485bd068c2ba691b6e277d29eaeac945868a" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" @@ -2531,9 +2486,9 @@ checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1a05a1ece9a7a0d5a7ccf30ba2c33e3a61a30e042ffd247567d1de1d94120d" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" @@ -2543,9 +2498,9 @@ checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" [[package]] name = "windows_x86_64_msvc" -version = "0.48.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d419259aba16b663966e29e6d7c6ecfa0bb8425818bb96f6f1f3c3eb71a6e7b9" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" @@ -2587,7 +2542,7 @@ dependencies = [ "hmac", "pbkdf2", "sha1", - "time 0.3.25", + "time", "zstd", ] diff --git a/Cargo.toml b/Cargo.toml index cfc57d747..6b580a745 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] -members = ["MelonProxy", "Bootstrap", "MelonProxy-sys"] +members = ["MelonProxy", "Bootstrap"] resolver = "2" \ No newline at end of file diff --git a/MelonProxy-sys/Cargo.toml b/MelonProxy-sys/Cargo.toml deleted file mode 100644 index 3133256a6..000000000 --- a/MelonProxy-sys/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "melon_proxy-sys" -version = "0.2.2" -authors = ["RinLovesYou"] -edition = "2021" -description = "A dynamic Windows System DLL proxy" -repository = "https://github.com/LavaGang/MelonLoader" -license = "GPL-2.0" -readme = "../README.md" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] - -[dependencies.syn] -version = "2.0.33" - -[lib] -name = "melon_proxy_sys" -proc-macro = true \ No newline at end of file diff --git a/MelonProxy-sys/src/lib.rs b/MelonProxy-sys/src/lib.rs deleted file mode 100644 index 8aa9ae540..000000000 --- a/MelonProxy-sys/src/lib.rs +++ /dev/null @@ -1,99 +0,0 @@ -//! A Macro for defining an entry point for a cdylib. -//! -//! On Windows, this macro will wrap your function in DllMain, and call it when the DLL attaches. -//! It will lookup exports of supported proxies, based on our own Module Name, and store them. -//! Effectively, creating a dynamic proxy that we could add any number of supported proxies to. -//! -//! # Supported Targets -//! -//! - Windows -//! - `x86_64-pc-windows-msvc` -//! - `i686-pc-windows-msvc` -//! -//! # Safety -//! -//! This crate is pretty unsafe - -#![feature(naked_functions)] -#![feature(asm_const)] -#![deny( - missing_debug_implementations, - missing_docs, - unused_results, - warnings, - clippy::extra_unused_lifetimes, - clippy::from_over_into, - clippy::needless_borrow, - clippy::new_without_default, - clippy::useless_conversion -)] -#![forbid(rust_2018_idioms)] -#![allow(clippy::inherent_to_string, clippy::type_complexity, improper_ctypes)] -#![cfg_attr(docsrs, feature(doc_cfg))] - -use syn::__private::{quote::quote, TokenStream}; - -/// Wraps your function in DllMain on windows -#[proc_macro_attribute] -pub fn proxy(_attribute: TokenStream, function: TokenStream) -> TokenStream { - //this gets us the Tokens of the function. Procedural macros let us modify functions before they are - //given to the compiler. - let fn_ts = function.clone(); - - //get the function as an item, letting us break it down into its component parts. - let item: syn::Item = syn::parse_macro_input!(fn_ts); - if let syn::Item::Fn(function) = item { - let syn::ItemFn { - attrs, - block, - vis, - sig: - syn::Signature { - ident, - unsafety, - constness, - abi, - output, - .. - }, - .. - } = function; - - //this is what we will give to the compiler instead of the original function. - let output = quote!( - //this is the original function, this is what we got from the item above. - #(#attrs)* - #vis #unsafety #abi #constness fn #ident() #output #block - - //create DllMain - #[no_mangle] - #[allow(non_snake_case)] - pub extern "system" fn DllMain( - _hinstDLL: crate::HINSTANCE, - fdwReason: isize, - _lpvReserved: isize, - ) -> isize { - - if fdwReason == 1 { - //initialize the proxy exports. - crate::proxy::exports::initialize(_hinstDLL).unwrap_or_else(|e| { - ::std::panic!("Failed to initialize MelonLoader's Proxy: {}", e); - }); - - //call the original function - #ident(); - } - - //return OK - 1 - } - - ); - - //return the modified function to the compiler. - return output.into(); - } - - //return the original function if things fail. - function -} diff --git a/MelonProxy/Cargo.toml b/MelonProxy/Cargo.toml index 31412534c..cfb72059e 100644 --- a/MelonProxy/Cargo.toml +++ b/MelonProxy/Cargo.toml @@ -1,25 +1,32 @@ [package] -name = "melon_proxy" -version = "0.2.5" -authors = ["RinLovesYou"] +name = "MelonProxy" +version = "0.1.0" edition = "2021" -description = "A dynamic Windows System DLL proxy" -repository = "https://github.com/LavaGang/MelonLoader" -license = "GPL-2.0" -readme = "../README.md" + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -melon_proxy-sys = { path = "../MelonProxy-sys" } -libloading = "0.8.0" msgbox = "0.7.0" +libloading = "0.8.1" [target.'cfg(target_os = "windows")'.dependencies] -windows = { version = "0.52.0", features = ["Win32_Foundation", "Win32_System", "Win32_System_LibraryLoader"] } +#proxygen-macros = "0.5.0" +proxygen-macros = { git = "https://github.com/RinLovesYou/proxygen" } # temporary until PR is accepted +winapi = { version = "0.3.9", features = [ + "minwindef", + "libloaderapi", + "processthreadsapi", + "consoleapi", + "processenv", + "winbase", + "winuser", + "errhandlingapi", +] } + +[target.'cfg(not(target_os = "windows"))'.dependencies] +ctor = "0.2.6" -[target.'cfg(target_os = "linux")'.dependencies] -ctor = "0.2.4" [lib] name = "version" -crate-type = ["cdylib", "rlib"] \ No newline at end of file +crate-type = ["cdylib"] diff --git a/MelonProxy/build.rs b/MelonProxy/build.rs deleted file mode 100644 index 83702c986..000000000 --- a/MelonProxy/build.rs +++ /dev/null @@ -1,34 +0,0 @@ -use std::env; - -fn main() { - let target_os = env::var("CARGO_CFG_TARGET_OS"); - - match target_os.as_ref().map(|x| &**x) { - Ok("linux") | Ok("android") => {} - Ok("freebsd") | Ok("dragonfly") => {} - Ok("openbsd") | Ok("bitrig") | Ok("netbsd") | Ok("macos") | Ok("ios") => {} - - Ok("windows") => link_exports(), - - tos => println!("cargo:error=unknown target os {:?}!", tos), - } -} - -/// links Exports.def to the resulting dll, exporting all our asm functions. -fn link_exports() { - let lib_path = env::current_dir().unwrap().join("deps").join("Exports.def"); - - - if !lib_path.exists() { - println!("cargo:error=Exports.def not found at {}", lib_path.display()); - } - - let lines = std::fs::read_to_string(lib_path).unwrap(); - let lines = lines.split("\n"); - let lines = lines.filter(|x| !x.is_empty()); - - //export each function - for line in lines { - println!("cargo:rustc-cdylib-link-arg=/EXPORT:{}", line); - } -} diff --git a/MelonProxy/deps/Exports.def b/MelonProxy/deps/Exports.def deleted file mode 100644 index 9e4416cc5..000000000 --- a/MelonProxy/deps/Exports.def +++ /dev/null @@ -1,264 +0,0 @@ -GetFileVersionInfoA -GetFileVersionInfoByHandle -GetFileVersionInfoExA -GetFileVersionInfoExW -GetFileVersionInfoSizeA -GetFileVersionInfoSizeExA -GetFileVersionInfoSizeExW -GetFileVersionInfoSizeW -GetFileVersionInfoW -VerFindFileA -VerFindFileW -VerInstallFileA -VerInstallFileW -VerLanguageNameA -VerLanguageNameW -VerQueryValueA -VerQueryValueW - -Private1 -SvchostPushServiceGlobals -WinHttpAddRequestHeaders -WinHttpAutoProxySvcMain -WinHttpCheckPlatform -WinHttpCloseHandle -WinHttpConnect -WinHttpConnectionDeletePolicyEntries -WinHttpConnectionDeleteProxyInfo -WinHttpConnectionFreeNameList -WinHttpConnectionFreeProxyInfo -WinHttpConnectionFreeProxyList -WinHttpConnectionGetNameList -WinHttpConnectionGetProxyInfo -WinHttpConnectionGetProxyList -WinHttpConnectionSetPolicyEntries -WinHttpConnectionSetProxyInfo -WinHttpConnectionUpdateIfIndexTable -WinHttpCrackUrl -WinHttpCreateProxyResolver -WinHttpCreateUrl -WinHttpDetectAutoProxyConfigUrl -WinHttpFreeProxyResult -WinHttpFreeProxyResultEx -WinHttpFreeProxySettings -WinHttpGetDefaultProxyConfiguration -WinHttpGetIEProxyConfigForCurrentUser -WinHttpGetProxyForUrl -WinHttpGetProxyForUrlEx -WinHttpGetProxyForUrlEx2 -WinHttpGetProxyForUrlHvsi -WinHttpGetProxyResult -WinHttpGetProxyResultEx -WinHttpGetProxySettingsVersion -WinHttpGetTunnelSocket -WinHttpOpen -WinHttpOpenRequest -WinHttpPacJsWorkerMain -WinHttpProbeConnectivity -WinHttpQueryAuthSchemes -WinHttpQueryDataAvailable -WinHttpQueryHeaders -WinHttpQueryOption -WinHttpReadData -WinHttpReadProxySettings -WinHttpReadProxySettingsHvsi -WinHttpReceiveResponse -WinHttpResetAutoProxy -WinHttpSaveProxyCredentials -WinHttpSendRequest -WinHttpSetCredentials -WinHttpSetDefaultProxyConfiguration -WinHttpSetOption -WinHttpSetStatusCallback -WinHttpSetTimeouts -WinHttpTimeFromSystemTime -WinHttpTimeToSystemTime -WinHttpWebSocketClose -WinHttpWebSocketCompleteUpgrade -WinHttpWebSocketQueryCloseStatus -WinHttpWebSocketReceive -WinHttpWebSocketSend -WinHttpWebSocketShutdown -WinHttpWriteData -WinHttpWriteProxySettings -CloseDriver -DefDriverProc -DriverCallback -DrvGetModuleHandle -GetDriverModuleHandle -OpenDriver -PlaySound -PlaySoundA -PlaySoundW -SendDriverMessage -WOWAppExit -auxGetDevCapsA -auxGetDevCapsW -auxGetNumDevs -auxGetVolume -auxOutMessage -auxSetVolume -joyConfigChanged -joyGetDevCapsA -joyGetDevCapsW -joyGetNumDevs -joyGetPos -joyGetPosEx -joyGetThreshold -joyReleaseCapture -joySetCapture -joySetThreshold -mciDriverNotify -mciDriverYield -mciExecute -mciFreeCommandResource -mciGetCreatorTask -mciGetDeviceIDA -mciGetDeviceIDFromElementIDA -mciGetDeviceIDFromElementIDW -mciGetDeviceIDW -mciGetDriverData -mciGetErrorStringA -mciGetErrorStringW -mciGetYieldProc -mciLoadCommandResource -mciSendCommandA -mciSendCommandW -mciSendStringA -mciSendStringW -mciSetDriverData -mciSetYieldProc -midiConnect -midiDisconnect -midiInAddBuffer -midiInClose -midiInGetDevCapsA -midiInGetDevCapsW -midiInGetErrorTextA -midiInGetErrorTextW -midiInGetID -midiInGetNumDevs -midiInMessage -midiInOpen -midiInPrepareHeader -midiInReset -midiInStart -midiInStop -midiInUnprepareHeader -midiOutCacheDrumPatches -midiOutCachePatches -midiOutClose -midiOutGetDevCapsA -midiOutGetDevCapsW -midiOutGetErrorTextA -midiOutGetErrorTextW -midiOutGetID -midiOutGetNumDevs -midiOutGetVolume -midiOutLongMsg -midiOutMessage -midiOutOpen -midiOutPrepareHeader -midiOutReset -midiOutSetVolume -midiOutShortMsg -midiOutUnprepareHeader -midiStreamClose -midiStreamOpen -midiStreamOut -midiStreamPause -midiStreamPosition -midiStreamProperty -midiStreamRestart -midiStreamStop -mixerClose -mixerGetControlDetailsA -mixerGetControlDetailsW -mixerGetDevCapsA -mixerGetDevCapsW -mixerGetID -mixerGetLineControlsA -mixerGetLineControlsW -mixerGetLineInfoA -mixerGetLineInfoW -mixerGetNumDevs -mixerMessage -mixerOpen -mixerSetControlDetails -mmDrvInstall -mmGetCurrentTask -mmTaskBlock -mmTaskCreate -mmTaskSignal -mmTaskYield -mmioAdvance -mmioAscend -mmioClose -mmioCreateChunk -mmioDescend -mmioFlush -mmioGetInfo -mmioInstallIOProcA -mmioInstallIOProcW -mmioOpenA -mmioOpenW -mmioRead -mmioRenameA -mmioRenameW -mmioSeek -mmioSendMessage -mmioSetBuffer -mmioSetInfo -mmioStringToFOURCCA -mmioStringToFOURCCW -mmioWrite -mmsystemGetVersion -sndPlaySoundA -sndPlaySoundW -timeBeginPeriod -timeEndPeriod -timeGetDevCaps -timeGetSystemTime -timeGetTime -timeKillEvent -timeSetEvent -waveInAddBuffer -waveInClose -waveInGetDevCapsA -waveInGetDevCapsW -waveInGetErrorTextA -waveInGetErrorTextW -waveInGetID -waveInGetNumDevs -waveInGetPosition -waveInMessage -waveInOpen -waveInPrepareHeader -waveInReset -waveInStart -waveInStop -waveInUnprepareHeader -waveOutBreakLoop -waveOutClose -waveOutGetDevCapsA -waveOutGetDevCapsW -waveOutGetErrorTextA -waveOutGetErrorTextW -waveOutGetID -waveOutGetNumDevs -waveOutGetPitch -waveOutGetPlaybackRate -waveOutGetPosition -waveOutGetVolume -waveOutMessage -waveOutOpen -waveOutPause -waveOutPrepareHeader -waveOutReset -waveOutRestart -waveOutSetPitch -waveOutSetPlaybackRate -waveOutSetVolume -waveOutUnprepareHeader -waveOutWrite -ExportByOrdinal2 \ No newline at end of file diff --git a/MelonProxy/deps/version.x64.S b/MelonProxy/deps/version.x64.S deleted file mode 100644 index 2b8dcd2b9..000000000 --- a/MelonProxy/deps/version.x64.S +++ /dev/null @@ -1,52 +0,0 @@ -.globl GetFileVersionInfoA -.globl GetFileVersionInfoByHandle -.globl GetFileVersionInfoExA -.globl GetFileVersionInfoExW -.globl GetFileVersionInfoSizeA -.globl GetFileVersionInfoSizeExA -.globl GetFileVersionInfoSizeExW -.globl GetFileVersionInfoSizeW -.globl GetFileVersionInfoW -.globl VerFindFileA -.globl VerFindFileW -.globl VerInstallFileA -.globl VerInstallFileW -.globl VerLanguageNameA -.globl VerLanguageNameW -.globl VerQueryValueA -.globl VerQueryValueW - -GetFileVersionInfoA: - jmp qword ptr [rip + OriginalFuncs + 0 * 8] -GetFileVersionInfoByHandle: - jmp qword ptr [rip + OriginalFuncs + 1 * 8] -GetFileVersionInfoExA: - jmp qword ptr [rip + OriginalFuncs + 2 * 8] -GetFileVersionInfoExW: - jmp qword ptr [rip + OriginalFuncs + 3 * 8] -GetFileVersionInfoSizeA: - jmp qword ptr [rip + OriginalFuncs + 4 * 8] -GetFileVersionInfoSizeExA: - jmp qword ptr [rip + OriginalFuncs + 5 * 8] -GetFileVersionInfoSizeExW: - jmp qword ptr [rip + OriginalFuncs + 6 * 8] -GetFileVersionInfoSizeW: - jmp qword ptr [rip + OriginalFuncs + 7 * 8] -GetFileVersionInfoW: - jmp qword ptr [rip + OriginalFuncs + 8 * 8] -VerFindFileA: - jmp qword ptr [rip + OriginalFuncs + 9 * 8] -VerFindFileW: - jmp qword ptr [rip + OriginalFuncs + 10 * 8] -VerInstallFileA: - jmp qword ptr [rip + OriginalFuncs + 11 * 8] -VerInstallFileW: - jmp qword ptr [rip + OriginalFuncs + 12 * 8] -VerLanguageNameA: - jmp qword ptr [rip + OriginalFuncs + 13 * 8] -VerLanguageNameW: - jmp qword ptr [rip + OriginalFuncs + 14 * 8] -VerQueryValueA: - jmp qword ptr [rip + OriginalFuncs + 15 * 8] -VerQueryValueW: - jmp qword ptr [rip + OriginalFuncs + 16 * 8] \ No newline at end of file diff --git a/MelonProxy/deps/version.x86.S b/MelonProxy/deps/version.x86.S deleted file mode 100644 index 859eac04f..000000000 --- a/MelonProxy/deps/version.x86.S +++ /dev/null @@ -1,52 +0,0 @@ -.globl GetFileVersionInfoA -.globl GetFileVersionInfoByHandle -.globl GetFileVersionInfoExA -.globl GetFileVersionInfoExW -.globl GetFileVersionInfoSizeA -.globl GetFileVersionInfoSizeExA -.globl GetFileVersionInfoSizeExW -.globl GetFileVersionInfoSizeW -.globl GetFileVersionInfoW -.globl VerFindFileA -.globl VerFindFileW -.globl VerInstallFileA -.globl VerInstallFileW -.globl VerLanguageNameA -.globl VerLanguageNameW -.globl VerQueryValueA -.globl VerQueryValueW - -GetFileVersionInfoA: - jmp ds:[_OriginalFuncs + 0 * 4] -GetFileVersionInfoByHandle: - jmp ds:[_OriginalFuncs + 1 * 4] -GetFileVersionInfoExA: - jmp ds:[_OriginalFuncs + 2 * 4] -GetFileVersionInfoExW: - jmp ds:[_OriginalFuncs + 3 * 4] -GetFileVersionInfoSizeA: - jmp ds:[_OriginalFuncs + 4 * 4] -GetFileVersionInfoSizeExA: - jmp ds:[_OriginalFuncs + 5 * 4] -GetFileVersionInfoSizeExW: - jmp ds:[_OriginalFuncs + 6 * 4] -GetFileVersionInfoSizeW: - jmp ds:[_OriginalFuncs + 7 * 4] -GetFileVersionInfoW: - jmp ds:[_OriginalFuncs + 4 * 4] -VerFindFileA: - jmp ds:[_OriginalFuncs + 9 * 4] -VerFindFileW: - jmp ds:[_OriginalFuncs + 10 * 4] -VerInstallFileA: - jmp ds:[_OriginalFuncs + 11 * 4] -VerInstallFileW: - jmp ds:[_OriginalFuncs + 12 * 4] -VerLanguageNameA: - jmp ds:[_OriginalFuncs + 13 * 4] -VerLanguageNameW: - jmp ds:[_OriginalFuncs + 14 * 4] -VerQueryValueA: - jmp ds:[_OriginalFuncs + 15 * 4] -VerQueryValueW: - jmp ds:[_OriginalFuncs + 16 * 4] \ No newline at end of file diff --git a/MelonProxy/deps/winhttp.x64.S b/MelonProxy/deps/winhttp.x64.S deleted file mode 100644 index d8c641824..000000000 --- a/MelonProxy/deps/winhttp.x64.S +++ /dev/null @@ -1,196 +0,0 @@ -.globl Private1 -.globl SvchostPushServiceGlobals -.globl WinHttpAddRequestHeaders -.globl WinHttpAutoProxySvcMain -.globl WinHttpCheckPlatform -.globl WinHttpCloseHandle -.globl WinHttpConnect -.globl WinHttpConnectionDeletePolicyEntries -.globl WinHttpConnectionDeleteProxyInfo -.globl WinHttpConnectionFreeNameList -.globl WinHttpConnectionFreeProxyInfo -.globl WinHttpConnectionFreeProxyList -.globl WinHttpConnectionGetNameList -.globl WinHttpConnectionGetProxyInfo -.globl WinHttpConnectionGetProxyList -.globl WinHttpConnectionSetPolicyEntries -.globl WinHttpConnectionSetProxyInfo -.globl WinHttpConnectionUpdateIfIndexTable -.globl WinHttpCrackUrl -.globl WinHttpCreateProxyResolver -.globl WinHttpCreateUrl -.globl WinHttpDetectAutoProxyConfigUrl -.globl WinHttpFreeProxyResult -.globl WinHttpFreeProxyResultEx -.globl WinHttpFreeProxySettings -.globl WinHttpGetDefaultProxyConfiguration -.globl WinHttpGetIEProxyConfigForCurrentUser -.globl WinHttpGetProxyForUrl -.globl WinHttpGetProxyForUrlEx -.globl WinHttpGetProxyForUrlEx2 -.globl WinHttpGetProxyForUrlHvsi -.globl WinHttpGetProxyResult -.globl WinHttpGetProxyResultEx -.globl WinHttpGetProxySettingsVersion -.globl WinHttpGetTunnelSocket -.globl WinHttpOpen -.globl WinHttpOpenRequest -.globl WinHttpPacJsWorkerMain -.globl WinHttpProbeConnectivity -.globl WinHttpQueryAuthSchemes -.globl WinHttpQueryDataAvailable -.globl WinHttpQueryHeaders -.globl WinHttpQueryOption -.globl WinHttpReadData -.globl WinHttpReadProxySettings -.globl WinHttpReadProxySettingsHvsi -.globl WinHttpReceiveResponse -.globl WinHttpResetAutoProxy -.globl WinHttpSaveProxyCredentials -.globl WinHttpSendRequest -.globl WinHttpSetCredentials -.globl WinHttpSetDefaultProxyConfiguration -.globl WinHttpSetOption -.globl WinHttpSetStatusCallback -.globl WinHttpSetTimeouts -.globl WinHttpTimeFromSystemTime -.globl WinHttpTimeToSystemTime -.globl WinHttpWebSocketClose -.globl WinHttpWebSocketCompleteUpgrade -.globl WinHttpWebSocketQueryCloseStatus -.globl WinHttpWebSocketReceive -.globl WinHttpWebSocketSend -.globl WinHttpWebSocketShutdown -.globl WinHttpWriteData -.globl WinHttpWriteProxySettings - -Private1: - jmp qword ptr [rip + OriginalFuncs + 0 * 8] -SvchostPushServiceGlobals: - jmp qword ptr [rip + OriginalFuncs + 1 * 8] -WinHttpAddRequestHeaders: - jmp qword ptr [rip + OriginalFuncs + 2 * 8] -WinHttpAutoProxySvcMain: - jmp qword ptr [rip + OriginalFuncs + 3 * 8] -WinHttpCheckPlatform: - jmp qword ptr [rip + OriginalFuncs + 4 * 8] -WinHttpCloseHandle: - jmp qword ptr [rip + OriginalFuncs + 5 * 8] -WinHttpConnect: - jmp qword ptr [rip + OriginalFuncs + 6 * 8] -WinHttpConnectionDeletePolicyEntries: - jmp qword ptr [rip + OriginalFuncs + 7 * 8] -WinHttpConnectionDeleteProxyInfo: - jmp qword ptr [rip + OriginalFuncs + 8 * 8] -WinHttpConnectionFreeNameList: - jmp qword ptr [rip + OriginalFuncs + 9 * 8] -WinHttpConnectionFreeProxyInfo: - jmp qword ptr [rip + OriginalFuncs + 10 * 8] -WinHttpConnectionFreeProxyList: - jmp qword ptr [rip + OriginalFuncs + 11 * 8] -WinHttpConnectionGetNameList: - jmp qword ptr [rip + OriginalFuncs + 12 * 8] -WinHttpConnectionGetProxyInfo: - jmp qword ptr [rip + OriginalFuncs + 13 * 8] -WinHttpConnectionGetProxyList: - jmp qword ptr [rip + OriginalFuncs + 14 * 8] -WinHttpConnectionSetPolicyEntries: - jmp qword ptr [rip + OriginalFuncs + 15 * 8] -WinHttpConnectionSetProxyInfo: - jmp qword ptr [rip + OriginalFuncs + 16 * 8] -WinHttpConnectionUpdateIfIndexTable: - jmp qword ptr [rip + OriginalFuncs + 17 * 8] -WinHttpCrackUrl: - jmp qword ptr [rip + OriginalFuncs + 18 * 8] -WinHttpCreateProxyResolver: - jmp qword ptr [rip + OriginalFuncs + 19 * 8] -WinHttpCreateUrl: - jmp qword ptr [rip + OriginalFuncs + 20 * 8] -WinHttpDetectAutoProxyConfigUrl: - jmp qword ptr [rip + OriginalFuncs + 21 * 8] -WinHttpFreeProxyResult: - jmp qword ptr [rip + OriginalFuncs + 22 * 8] -WinHttpFreeProxyResultEx: - jmp qword ptr [rip + OriginalFuncs + 23 * 8] -WinHttpFreeProxySettings: - jmp qword ptr [rip + OriginalFuncs + 24 * 8] -WinHttpGetDefaultProxyConfiguration: - jmp qword ptr [rip + OriginalFuncs + 25 * 8] -WinHttpGetIEProxyConfigForCurrentUser: - jmp qword ptr [rip + OriginalFuncs + 26 * 8] -WinHttpGetProxyForUrl: - jmp qword ptr [rip + OriginalFuncs + 27 * 8] -WinHttpGetProxyForUrlEx: - jmp qword ptr [rip + OriginalFuncs + 28 * 8] -WinHttpGetProxyForUrlEx2: - jmp qword ptr [rip + OriginalFuncs + 29 * 8] -WinHttpGetProxyForUrlHvsi: - jmp qword ptr [rip + OriginalFuncs + 30 * 8] -WinHttpGetProxyResult: - jmp qword ptr [rip + OriginalFuncs + 31 * 8] -WinHttpGetProxyResultEx: - jmp qword ptr [rip + OriginalFuncs + 32 * 8] -WinHttpGetProxySettingsVersion: - jmp qword ptr [rip + OriginalFuncs + 33 * 8] -WinHttpGetTunnelSocket: - jmp qword ptr [rip + OriginalFuncs + 34 * 8] -WinHttpOpen: - jmp qword ptr [rip + OriginalFuncs + 35 * 8] -WinHttpOpenRequest: - jmp qword ptr [rip + OriginalFuncs + 36 * 8] -WinHttpPacJsWorkerMain: - jmp qword ptr [rip + OriginalFuncs + 37 * 8] -WinHttpProbeConnectivity: - jmp qword ptr [rip + OriginalFuncs + 38 * 8] -WinHttpQueryAuthSchemes: - jmp qword ptr [rip + OriginalFuncs + 39 * 8] -WinHttpQueryDataAvailable: - jmp qword ptr [rip + OriginalFuncs + 40 * 8] -WinHttpQueryHeaders: - jmp qword ptr [rip + OriginalFuncs + 41 * 8] -WinHttpQueryOption: - jmp qword ptr [rip + OriginalFuncs + 42 * 8] -WinHttpReadData: - jmp qword ptr [rip + OriginalFuncs + 43 * 8] -WinHttpReadProxySettings: - jmp qword ptr [rip + OriginalFuncs + 44 * 8] -WinHttpReadProxySettingsHvsi: - jmp qword ptr [rip + OriginalFuncs + 45 * 8] -WinHttpReceiveResponse: - jmp qword ptr [rip + OriginalFuncs + 46 * 8] -WinHttpResetAutoProxy: - jmp qword ptr [rip + OriginalFuncs + 47 * 8] -WinHttpSaveProxyCredentials: - jmp qword ptr [rip + OriginalFuncs + 48 * 8] -WinHttpSendRequest: - jmp qword ptr [rip + OriginalFuncs + 49 * 8] -WinHttpSetCredentials: - jmp qword ptr [rip + OriginalFuncs + 50 * 8] -WinHttpSetDefaultProxyConfiguration: - jmp qword ptr [rip + OriginalFuncs + 51 * 8] -WinHttpSetOption: - jmp qword ptr [rip + OriginalFuncs + 52 * 8] -WinHttpSetStatusCallback: - jmp qword ptr [rip + OriginalFuncs + 53 * 8] -WinHttpSetTimeouts: - jmp qword ptr [rip + OriginalFuncs + 54 * 8] -WinHttpTimeFromSystemTime: - jmp qword ptr [rip + OriginalFuncs + 55 * 8] -WinHttpTimeToSystemTime: - jmp qword ptr [rip + OriginalFuncs + 56 * 8] -WinHttpWebSocketClose: - jmp qword ptr [rip + OriginalFuncs + 57 * 8] -WinHttpWebSocketCompleteUpgrade: - jmp qword ptr [rip + OriginalFuncs + 58 * 8] -WinHttpWebSocketQueryCloseStatus: - jmp qword ptr [rip + OriginalFuncs + 59 * 8] -WinHttpWebSocketReceive: - jmp qword ptr [rip + OriginalFuncs + 60 * 8] -WinHttpWebSocketSend: - jmp qword ptr [rip + OriginalFuncs + 61 * 8] -WinHttpWebSocketShutdown: - jmp qword ptr [rip + OriginalFuncs + 62 * 8] -WinHttpWriteData: - jmp qword ptr [rip + OriginalFuncs + 63 * 8] -WinHttpWriteProxySettings: - jmp qword ptr [rip + OriginalFuncs + 64 * 8] diff --git a/MelonProxy/deps/winhttp.x86.S b/MelonProxy/deps/winhttp.x86.S deleted file mode 100644 index 0227c742b..000000000 --- a/MelonProxy/deps/winhttp.x86.S +++ /dev/null @@ -1,260 +0,0 @@ -.globl Private1 -.globl SvchostPushServiceGlobals -.globl WinHttpAddRequestHeaders -.globl WinHttpAutoProxySvcMain -.globl WinHttpCheckPlatform -.globl WinHttpCloseHandle -.globl WinHttpConnect -.globl WinHttpConnectionDeletePolicyEntries -.globl WinHttpConnectionDeleteProxyInfo -.globl WinHttpConnectionFreeNameList -.globl WinHttpConnectionFreeProxyInfo -.globl WinHttpConnectionFreeProxyList -.globl WinHttpConnectionGetNameList -.globl WinHttpConnectionGetProxyInfo -.globl WinHttpConnectionGetProxyList -.globl WinHttpConnectionSetPolicyEntries -.globl WinHttpConnectionSetProxyInfo -.globl WinHttpConnectionUpdateIfIndexTable -.globl WinHttpCrackUrl -.globl WinHttpCreateProxyResolver -.globl WinHttpCreateUrl -.globl WinHttpDetectAutoProxyConfigUrl -.globl WinHttpFreeProxyResult -.globl WinHttpFreeProxyResultEx -.globl WinHttpFreeProxySettings -.globl WinHttpGetDefaultProxyConfiguration -.globl WinHttpGetIEProxyConfigForCurrentUser -.globl WinHttpGetProxyForUrl -.globl WinHttpGetProxyForUrlEx -.globl WinHttpGetProxyForUrlEx2 -.globl WinHttpGetProxyForUrlHvsi -.globl WinHttpGetProxyResult -.globl WinHttpGetProxyResultEx -.globl WinHttpGetProxySettingsVersion -.globl WinHttpGetTunnelSocket -.globl WinHttpOpen -.globl WinHttpOpenRequest -.globl WinHttpPacJsWorkerMain -.globl WinHttpProbeConnectivity -.globl WinHttpQueryAuthSchemes -.globl WinHttpQueryDataAvailable -.globl WinHttpQueryHeaders -.globl WinHttpQueryOption -.globl WinHttpReadData -.globl WinHttpReadProxySettings -.globl WinHttpReadProxySettingsHvsi -.globl WinHttpReceiveResponse -.globl WinHttpResetAutoProxy -.globl WinHttpSaveProxyCredentials -.globl WinHttpSendRequest -.globl WinHttpSetCredentials -.globl WinHttpSetDefaultProxyConfiguration -.globl WinHttpSetOption -.globl WinHttpSetStatusCallback -.globl WinHttpSetTimeouts -.globl WinHttpTimeFromSystemTime -.globl WinHttpTimeToSystemTime -.globl WinHttpWebSocketClose -.globl WinHttpWebSocketCompleteUpgrade -.globl WinHttpWebSocketQueryCloseStatus -.globl WinHttpWebSocketReceive -.globl WinHttpWebSocketSend -.globl WinHttpWebSocketShutdown -.globl WinHttpWriteData -.globl WinHttpWriteProxySettings - -Private1: - jmp ds:[_OriginalFuncs + 0 * 4] - -SvchostPushServiceGlobals: - jmp ds:[_OriginalFuncs + 1 * 4] - -WinHttpAddRequestHeaders: - jmp ds:[_OriginalFuncs + 2 * 4] - -WinHttpAutoProxySvcMain: - jmp ds:[_OriginalFuncs + 3 * 4] - -WinHttpCheckPlatform: - jmp ds:[_OriginalFuncs + 4 * 4] - -WinHttpCloseHandle: - jmp ds:[_OriginalFuncs + 5 * 4] - -WinHttpConnect: - jmp ds:[_OriginalFuncs + 6 * 4] - -WinHttpConnectionDeletePolicyEntries: - jmp ds:[_OriginalFuncs + 7 * 4] - -WinHttpConnectionDeleteProxyInfo: - jmp ds:[_OriginalFuncs + 4 * 4] - -WinHttpConnectionFreeNameList: - jmp ds:[_OriginalFuncs + 9 * 4] - -WinHttpConnectionFreeProxyInfo: - jmp ds:[_OriginalFuncs + 10 * 4] - -WinHttpConnectionFreeProxyList: - jmp ds:[_OriginalFuncs + 11 * 4] - -WinHttpConnectionGetNameList: - jmp ds:[_OriginalFuncs + 12 * 4] - -WinHttpConnectionGetProxyInfo: - jmp ds:[_OriginalFuncs + 13 * 4] - -WinHttpConnectionGetProxyList: - jmp ds:[_OriginalFuncs + 14 * 4] - -WinHttpConnectionSetPolicyEntries: - jmp ds:[_OriginalFuncs + 15 * 4] - -WinHttpConnectionSetProxyInfo: - jmp ds:[_OriginalFuncs + 16 * 4] - -WinHttpConnectionUpdateIfIndexTable: - jmp ds:[_OriginalFuncs + 17 * 4] - -WinHttpCrackUrl: - jmp ds:[_OriginalFuncs + 14 * 4] - -WinHttpCreateProxyResolver: - jmp ds:[_OriginalFuncs + 19 * 4] - -WinHttpCreateUrl: - jmp ds:[_OriginalFuncs + 20 * 4] - -WinHttpDetectAutoProxyConfigUrl: - jmp ds:[_OriginalFuncs + 21 * 4] - -WinHttpFreeProxyResult: - jmp ds:[_OriginalFuncs + 22 * 4] - -WinHttpFreeProxyResultEx: - jmp ds:[_OriginalFuncs + 23 * 4] - -WinHttpFreeProxySettings: - jmp ds:[_OriginalFuncs + 24 * 4] - -WinHttpGetDefaultProxyConfiguration: - jmp ds:[_OriginalFuncs + 25 * 4] - -WinHttpGetIEProxyConfigForCurrentUser: - jmp ds:[_OriginalFuncs + 26 * 4] - -WinHttpGetProxyForUrl: - jmp ds:[_OriginalFuncs + 27 * 4] - -WinHttpGetProxyForUrlEx: - jmp ds:[_OriginalFuncs + 24 * 4] - -WinHttpGetProxyForUrlEx2: - jmp ds:[_OriginalFuncs + 29 * 4] - -WinHttpGetProxyForUrlHvsi: - jmp ds:[_OriginalFuncs + 30 * 4] - -WinHttpGetProxyResult: - jmp ds:[_OriginalFuncs + 31 * 4] - -WinHttpGetProxyResultEx: - jmp ds:[_OriginalFuncs + 32 * 4] - -WinHttpGetProxySettingsVersion: - jmp ds:[_OriginalFuncs + 33 * 4] - -WinHttpGetTunnelSocket: - jmp ds:[_OriginalFuncs + 34 * 4] - -WinHttpOpen: - jmp ds:[_OriginalFuncs + 35 * 4] - -WinHttpOpenRequest: - jmp ds:[_OriginalFuncs + 36 * 4] - -WinHttpPacJsWorkerMain: - jmp ds:[_OriginalFuncs + 37 * 4] - -WinHttpProbeConnectivity: - jmp ds:[_OriginalFuncs + 34 * 4] - -WinHttpQueryAuthSchemes: - jmp ds:[_OriginalFuncs + 39 * 4] - -WinHttpQueryDataAvailable: - jmp ds:[_OriginalFuncs + 40 * 4] - -WinHttpQueryHeaders: - jmp ds:[_OriginalFuncs + 41 * 4] - -WinHttpQueryOption: - jmp ds:[_OriginalFuncs + 42 * 4] - -WinHttpReadData: - jmp ds:[_OriginalFuncs + 43 * 4] - -WinHttpReadProxySettings: - jmp ds:[_OriginalFuncs + 44 * 4] - -WinHttpReadProxySettingsHvsi: - jmp ds:[_OriginalFuncs + 45 * 4] - -WinHttpReceiveResponse: - jmp ds:[_OriginalFuncs + 46 * 4] - -WinHttpResetAutoProxy: - jmp ds:[_OriginalFuncs + 47 * 4] - -WinHttpSaveProxyCredentials: - jmp ds:[_OriginalFuncs + 44 * 4] - -WinHttpSendRequest: - jmp ds:[_OriginalFuncs + 49 * 4] - -WinHttpSetCredentials: - jmp ds:[_OriginalFuncs + 50 * 4] - -WinHttpSetDefaultProxyConfiguration: - jmp ds:[_OriginalFuncs + 51 * 4] - -WinHttpSetOption: - jmp ds:[_OriginalFuncs + 52 * 4] - -WinHttpSetStatusCallback: - jmp ds:[_OriginalFuncs + 53 * 4] - -WinHttpSetTimeouts: - jmp ds:[_OriginalFuncs + 54 * 4] - -WinHttpTimeFromSystemTime: - jmp ds:[_OriginalFuncs + 55 * 4] - -WinHttpTimeToSystemTime: - jmp ds:[_OriginalFuncs + 56 * 4] - -WinHttpWebSocketClose: - jmp ds:[_OriginalFuncs + 57 * 4] - -WinHttpWebSocketCompleteUpgrade: - jmp ds:[_OriginalFuncs + 54 * 4] - -WinHttpWebSocketQueryCloseStatus: - jmp ds:[_OriginalFuncs + 59 * 4] - -WinHttpWebSocketReceive: - jmp ds:[_OriginalFuncs + 60 * 4] - -WinHttpWebSocketSend: - jmp ds:[_OriginalFuncs + 61 * 4] - -WinHttpWebSocketShutdown: - jmp ds:[_OriginalFuncs + 62 * 4] - -WinHttpWriteData: - jmp ds:[_OriginalFuncs + 63 * 4] - -WinHttpWriteProxySettings: - jmp ds:[_OriginalFuncs + 64 * 4] diff --git a/MelonProxy/deps/winmm.x64.S b/MelonProxy/deps/winmm.x64.S deleted file mode 100644 index 2cd1fb993..000000000 --- a/MelonProxy/deps/winmm.x64.S +++ /dev/null @@ -1,724 +0,0 @@ -.globl CloseDriver -.globl DefDriverProc -.globl DriverCallback -.globl DrvGetModuleHandle -.globl GetDriverModuleHandle -.globl OpenDriver -.globl PlaySound -.globl PlaySoundA -.globl PlaySoundW -.globl SendDriverMessage -.globl WOWAppExit -.globl auxGetDevCapsA -.globl auxGetDevCapsW -.globl auxGetNumDevs -.globl auxGetVolume -.globl auxOutMessage -.globl auxSetVolume -.globl joyConfigChanged -.globl joyGetDevCapsA -.globl joyGetDevCapsW -.globl joyGetNumDevs -.globl joyGetPos -.globl joyGetPosEx -.globl joyGetThreshold -.globl joyReleaseCapture -.globl joySetCapture -.globl joySetThreshold -.globl mciDriverNotify -.globl mciDriverYield -.globl mciExecute -.globl mciFreeCommandResource -.globl mciGetCreatorTask -.globl mciGetDeviceIDA -.globl mciGetDeviceIDFromElementIDA -.globl mciGetDeviceIDFromElementIDW -.globl mciGetDeviceIDW -.globl mciGetDriverData -.globl mciGetErrorStringA -.globl mciGetErrorStringW -.globl mciGetYieldProc -.globl mciLoadCommandResource -.globl mciSendCommandA -.globl mciSendCommandW -.globl mciSendStringA -.globl mciSendStringW -.globl mciSetDriverData -.globl mciSetYieldProc -.globl midiConnect -.globl midiDisconnect -.globl midiInAddBuffer -.globl midiInClose -.globl midiInGetDevCapsA -.globl midiInGetDevCapsW -.globl midiInGetErrorTextA -.globl midiInGetErrorTextW -.globl midiInGetID -.globl midiInGetNumDevs -.globl midiInMessage -.globl midiInOpen -.globl midiInPrepareHeader -.globl midiInReset -.globl midiInStart -.globl midiInStop -.globl midiInUnprepareHeader -.globl midiOutCacheDrumPatches -.globl midiOutCachePatches -.globl midiOutClose -.globl midiOutGetDevCapsA -.globl midiOutGetDevCapsW -.globl midiOutGetErrorTextA -.globl midiOutGetErrorTextW -.globl midiOutGetID -.globl midiOutGetNumDevs -.globl midiOutGetVolume -.globl midiOutLongMsg -.globl midiOutMessage -.globl midiOutOpen -.globl midiOutPrepareHeader -.globl midiOutReset -.globl midiOutSetVolume -.globl midiOutShortMsg -.globl midiOutUnprepareHeader -.globl midiStreamClose -.globl midiStreamOpen -.globl midiStreamOut -.globl midiStreamPause -.globl midiStreamPosition -.globl midiStreamProperty -.globl midiStreamRestart -.globl midiStreamStop -.globl mixerClose -.globl mixerGetControlDetailsA -.globl mixerGetControlDetailsW -.globl mixerGetDevCapsA -.globl mixerGetDevCapsW -.globl mixerGetID -.globl mixerGetLineControlsA -.globl mixerGetLineControlsW -.globl mixerGetLineInfoA -.globl mixerGetLineInfoW -.globl mixerGetNumDevs -.globl mixerMessage -.globl mixerOpen -.globl mixerSetControlDetails -.globl mmDrvInstall -.globl mmGetCurrentTask -.globl mmTaskBlock -.globl mmTaskCreate -.globl mmTaskSignal -.globl mmTaskYield -.globl mmioAdvance -.globl mmioAscend -.globl mmioClose -.globl mmioCreateChunk -.globl mmioDescend -.globl mmioFlush -.globl mmioGetInfo -.globl mmioInstallIOProcA -.globl mmioInstallIOProcW -.globl mmioOpenA -.globl mmioOpenW -.globl mmioRead -.globl mmioRenameA -.globl mmioRenameW -.globl mmioSeek -.globl mmioSendMessage -.globl mmioSetBuffer -.globl mmioSetInfo -.globl mmioStringToFOURCCA -.globl mmioStringToFOURCCW -.globl mmioWrite -.globl mmsystemGetVersion -.globl sndPlaySoundA -.globl sndPlaySoundW -.globl timeBeginPeriod -.globl timeEndPeriod -.globl timeGetDevCaps -.globl timeGetSystemTime -.globl timeGetTime -.globl timeKillEvent -.globl timeSetEvent -.globl waveInAddBuffer -.globl waveInClose -.globl waveInGetDevCapsA -.globl waveInGetDevCapsW -.globl waveInGetErrorTextA -.globl waveInGetErrorTextW -.globl waveInGetID -.globl waveInGetNumDevs -.globl waveInGetPosition -.globl waveInMessage -.globl waveInOpen -.globl waveInPrepareHeader -.globl waveInReset -.globl waveInStart -.globl waveInStop -.globl waveInUnprepareHeader -.globl waveOutBreakLoop -.globl waveOutClose -.globl waveOutGetDevCapsA -.globl waveOutGetDevCapsW -.globl waveOutGetErrorTextA -.globl waveOutGetErrorTextW -.globl waveOutGetID -.globl waveOutGetNumDevs -.globl waveOutGetPitch -.globl waveOutGetPlaybackRate -.globl waveOutGetPosition -.globl waveOutGetVolume -.globl waveOutMessage -.globl waveOutOpen -.globl waveOutPause -.globl waveOutPrepareHeader -.globl waveOutReset -.globl waveOutRestart -.globl waveOutSetPitch -.globl waveOutSetPlaybackRate -.globl waveOutSetVolume -.globl waveOutUnprepareHeader -.globl waveOutWrite -.globl ExportByOrdinal2 - -CloseDriver: - jmp qword ptr [rip + OriginalFuncs + 0 * 8] - -DefDriverProc: - jmp qword ptr [rip + OriginalFuncs + 1 * 8] - -DriverCallback: - jmp qword ptr [rip + OriginalFuncs + 2 * 8] - -DrvGetModuleHandle: - jmp qword ptr [rip + OriginalFuncs + 3 * 8] - -GetDriverModuleHandle: - jmp qword ptr [rip + OriginalFuncs + 4 * 8] - -OpenDriver: - jmp qword ptr [rip + OriginalFuncs + 5 * 8] - -PlaySound: - jmp qword ptr [rip + OriginalFuncs + 6 * 8] - -PlaySoundA: - jmp qword ptr [rip + OriginalFuncs + 7 * 8] - -PlaySoundW: - jmp qword ptr [rip + OriginalFuncs + 8 * 8] - -SendDriverMessage: - jmp qword ptr [rip + OriginalFuncs + 9 * 8] - -WOWAppExit: - jmp qword ptr [rip + OriginalFuncs + 10 * 8] - -auxGetDevCapsA: - jmp qword ptr [rip + OriginalFuncs + 11 * 8] - -auxGetDevCapsW: - jmp qword ptr [rip + OriginalFuncs + 12 * 8] - -auxGetNumDevs: - jmp qword ptr [rip + OriginalFuncs + 13 * 8] - -auxGetVolume: - jmp qword ptr [rip + OriginalFuncs + 14 * 8] - -auxOutMessage: - jmp qword ptr [rip + OriginalFuncs + 15 * 8] - -auxSetVolume: - jmp qword ptr [rip + OriginalFuncs + 16 * 8] - -joyConfigChanged: - jmp qword ptr [rip + OriginalFuncs + 17 * 8] - -joyGetDevCapsA: - jmp qword ptr [rip + OriginalFuncs + 18 * 8] - -joyGetDevCapsW: - jmp qword ptr [rip + OriginalFuncs + 19 * 8] - -joyGetNumDevs: - jmp qword ptr [rip + OriginalFuncs + 20 * 8] - -joyGetPos: - jmp qword ptr [rip + OriginalFuncs + 21 * 8] - -joyGetPosEx: - jmp qword ptr [rip + OriginalFuncs + 22 * 8] - -joyGetThreshold: - jmp qword ptr [rip + OriginalFuncs + 23 * 8] - -joyReleaseCapture: - jmp qword ptr [rip + OriginalFuncs + 24 * 8] - -joySetCapture: - jmp qword ptr [rip + OriginalFuncs + 25 * 8] - -joySetThreshold: - jmp qword ptr [rip + OriginalFuncs + 26 * 8] - -mciDriverNotify: - jmp qword ptr [rip + OriginalFuncs + 27 * 8] - -mciDriverYield: - jmp qword ptr [rip + OriginalFuncs + 28 * 8] - -mciExecute: - jmp qword ptr [rip + OriginalFuncs + 29 * 8] - -mciFreeCommandResource: - jmp qword ptr [rip + OriginalFuncs + 30 * 8] - -mciGetCreatorTask: - jmp qword ptr [rip + OriginalFuncs + 31 * 8] - -mciGetDeviceIDA: - jmp qword ptr [rip + OriginalFuncs + 32 * 8] - -mciGetDeviceIDFromElementIDA: - jmp qword ptr [rip + OriginalFuncs + 33 * 8] - -mciGetDeviceIDFromElementIDW: - jmp qword ptr [rip + OriginalFuncs + 34 * 8] - -mciGetDeviceIDW: - jmp qword ptr [rip + OriginalFuncs + 35 * 8] - -mciGetDriverData: - jmp qword ptr [rip + OriginalFuncs + 36 * 8] - -mciGetErrorStringA: - jmp qword ptr [rip + OriginalFuncs + 37 * 8] - -mciGetErrorStringW: - jmp qword ptr [rip + OriginalFuncs + 38 * 8] - -mciGetYieldProc: - jmp qword ptr [rip + OriginalFuncs + 39 * 8] - -mciLoadCommandResource: - jmp qword ptr [rip + OriginalFuncs + 40 * 8] - -mciSendCommandA: - jmp qword ptr [rip + OriginalFuncs + 41 * 8] - -mciSendCommandW: - jmp qword ptr [rip + OriginalFuncs + 42 * 8] - -mciSendStringA: - jmp qword ptr [rip + OriginalFuncs + 43 * 8] - -mciSendStringW: - jmp qword ptr [rip + OriginalFuncs + 44 * 8] - -mciSetDriverData: - jmp qword ptr [rip + OriginalFuncs + 45 * 8] - -mciSetYieldProc: - jmp qword ptr [rip + OriginalFuncs + 46 * 8] - -midiConnect: - jmp qword ptr [rip + OriginalFuncs + 47 * 8] - -midiDisconnect: - jmp qword ptr [rip + OriginalFuncs + 48 * 8] - -midiInAddBuffer: - jmp qword ptr [rip + OriginalFuncs + 49 * 8] - -midiInClose: - jmp qword ptr [rip + OriginalFuncs + 50 * 8] - -midiInGetDevCapsA: - jmp qword ptr [rip + OriginalFuncs + 51 * 8] - -midiInGetDevCapsW: - jmp qword ptr [rip + OriginalFuncs + 52 * 8] - -midiInGetErrorTextA: - jmp qword ptr [rip + OriginalFuncs + 53 * 8] - -midiInGetErrorTextW: - jmp qword ptr [rip + OriginalFuncs + 54 * 8] - -midiInGetID: - jmp qword ptr [rip + OriginalFuncs + 55 * 8] - -midiInGetNumDevs: - jmp qword ptr [rip + OriginalFuncs + 56 * 8] - -midiInMessage: - jmp qword ptr [rip + OriginalFuncs + 57 * 8] - -midiInOpen: - jmp qword ptr [rip + OriginalFuncs + 58 * 8] - -midiInPrepareHeader: - jmp qword ptr [rip + OriginalFuncs + 59 * 8] - -midiInReset: - jmp qword ptr [rip + OriginalFuncs + 60 * 8] - -midiInStart: - jmp qword ptr [rip + OriginalFuncs + 61 * 8] - -midiInStop: - jmp qword ptr [rip + OriginalFuncs + 62 * 8] - -midiInUnprepareHeader: - jmp qword ptr [rip + OriginalFuncs + 63 * 8] - -midiOutCacheDrumPatches: - jmp qword ptr [rip + OriginalFuncs + 64 * 8] - -midiOutCachePatches: - jmp qword ptr [rip + OriginalFuncs + 65 * 8] - -midiOutClose: - jmp qword ptr [rip + OriginalFuncs + 66 * 8] - -midiOutGetDevCapsA: - jmp qword ptr [rip + OriginalFuncs + 67 * 8] - -midiOutGetDevCapsW: - jmp qword ptr [rip + OriginalFuncs + 68 * 8] - -midiOutGetErrorTextA: - jmp qword ptr [rip + OriginalFuncs + 69 * 8] - -midiOutGetErrorTextW: - jmp qword ptr [rip + OriginalFuncs + 70 * 8] - -midiOutGetID: - jmp qword ptr [rip + OriginalFuncs + 71 * 8] - -midiOutGetNumDevs: - jmp qword ptr [rip + OriginalFuncs + 72 * 8] - -midiOutGetVolume: - jmp qword ptr [rip + OriginalFuncs + 73 * 8] - -midiOutLongMsg: - jmp qword ptr [rip + OriginalFuncs + 74 * 8] - -midiOutMessage: - jmp qword ptr [rip + OriginalFuncs + 75 * 8] - -midiOutOpen: - jmp qword ptr [rip + OriginalFuncs + 76 * 8] - -midiOutPrepareHeader: - jmp qword ptr [rip + OriginalFuncs + 77 * 8] - -midiOutReset: - jmp qword ptr [rip + OriginalFuncs + 78 * 8] - -midiOutSetVolume: - jmp qword ptr [rip + OriginalFuncs + 79 * 8] - -midiOutShortMsg: - jmp qword ptr [rip + OriginalFuncs + 80 * 8] - -midiOutUnprepareHeader: - jmp qword ptr [rip + OriginalFuncs + 81 * 8] - -midiStreamClose: - jmp qword ptr [rip + OriginalFuncs + 82 * 8] - -midiStreamOpen: - jmp qword ptr [rip + OriginalFuncs + 83 * 8] - -midiStreamOut: - jmp qword ptr [rip + OriginalFuncs + 84 * 8] - -midiStreamPause: - jmp qword ptr [rip + OriginalFuncs + 85 * 8] - -midiStreamPosition: - jmp qword ptr [rip + OriginalFuncs + 86 * 8] - -midiStreamProperty: - jmp qword ptr [rip + OriginalFuncs + 87 * 8] - -midiStreamRestart: - jmp qword ptr [rip + OriginalFuncs + 88 * 8] - -midiStreamStop: - jmp qword ptr [rip + OriginalFuncs + 89 * 8] - -mixerClose: - jmp qword ptr [rip + OriginalFuncs + 90 * 8] - -mixerGetControlDetailsA: - jmp qword ptr [rip + OriginalFuncs + 91 * 8] - -mixerGetControlDetailsW: - jmp qword ptr [rip + OriginalFuncs + 92 * 8] - -mixerGetDevCapsA: - jmp qword ptr [rip + OriginalFuncs + 93 * 8] - -mixerGetDevCapsW: - jmp qword ptr [rip + OriginalFuncs + 94 * 8] - -mixerGetID: - jmp qword ptr [rip + OriginalFuncs + 95 * 8] - -mixerGetLineControlsA: - jmp qword ptr [rip + OriginalFuncs + 96 * 8] - -mixerGetLineControlsW: - jmp qword ptr [rip + OriginalFuncs + 97 * 8] - -mixerGetLineInfoA: - jmp qword ptr [rip + OriginalFuncs + 98 * 8] - -mixerGetLineInfoW: - jmp qword ptr [rip + OriginalFuncs + 99 * 8] - -mixerGetNumDevs: - jmp qword ptr [rip + OriginalFuncs + 100 * 8] - -mixerMessage: - jmp qword ptr [rip + OriginalFuncs + 101 * 8] - -mixerOpen: - jmp qword ptr [rip + OriginalFuncs + 102 * 8] - -mixerSetControlDetails: - jmp qword ptr [rip + OriginalFuncs + 103 * 8] - -mmDrvInstall: - jmp qword ptr [rip + OriginalFuncs + 104 * 8] - -mmGetCurrentTask: - jmp qword ptr [rip + OriginalFuncs + 105 * 8] - -mmTaskBlock: - jmp qword ptr [rip + OriginalFuncs + 106 * 8] - -mmTaskCreate: - jmp qword ptr [rip + OriginalFuncs + 107 * 8] - -mmTaskSignal: - jmp qword ptr [rip + OriginalFuncs + 108 * 8] - -mmTaskYield: - jmp qword ptr [rip + OriginalFuncs + 109 * 8] - -mmioAdvance: - jmp qword ptr [rip + OriginalFuncs + 110 * 8] - -mmioAscend: - jmp qword ptr [rip + OriginalFuncs + 111 * 8] - -mmioClose: - jmp qword ptr [rip + OriginalFuncs + 112 * 8] - -mmioCreateChunk: - jmp qword ptr [rip + OriginalFuncs + 113 * 8] - -mmioDescend: - jmp qword ptr [rip + OriginalFuncs + 114 * 8] - -mmioFlush: - jmp qword ptr [rip + OriginalFuncs + 115 * 8] - -mmioGetInfo: - jmp qword ptr [rip + OriginalFuncs + 116 * 8] - -mmioInstallIOProcA: - jmp qword ptr [rip + OriginalFuncs + 117 * 8] - -mmioInstallIOProcW: - jmp qword ptr [rip + OriginalFuncs + 118 * 8] - -mmioOpenA: - jmp qword ptr [rip + OriginalFuncs + 119 * 8] - -mmioOpenW: - jmp qword ptr [rip + OriginalFuncs + 120 * 8] - -mmioRead: - jmp qword ptr [rip + OriginalFuncs + 121 * 8] - -mmioRenameA: - jmp qword ptr [rip + OriginalFuncs + 122 * 8] - -mmioRenameW: - jmp qword ptr [rip + OriginalFuncs + 123 * 8] - -mmioSeek: - jmp qword ptr [rip + OriginalFuncs + 124 * 8] - -mmioSendMessage: - jmp qword ptr [rip + OriginalFuncs + 125 * 8] - -mmioSetBuffer: - jmp qword ptr [rip + OriginalFuncs + 126 * 8] - -mmioSetInfo: - jmp qword ptr [rip + OriginalFuncs + 127 * 8] - -mmioStringToFOURCCA: - jmp qword ptr [rip + OriginalFuncs + 128 * 8] - -mmioStringToFOURCCW: - jmp qword ptr [rip + OriginalFuncs + 129 * 8] - -mmioWrite: - jmp qword ptr [rip + OriginalFuncs + 130 * 8] - -mmsystemGetVersion: - jmp qword ptr [rip + OriginalFuncs + 131 * 8] - -sndPlaySoundA: - jmp qword ptr [rip + OriginalFuncs + 132 * 8] - -sndPlaySoundW: - jmp qword ptr [rip + OriginalFuncs + 133 * 8] - -timeBeginPeriod: - jmp qword ptr [rip + OriginalFuncs + 134 * 8] - -timeEndPeriod: - jmp qword ptr [rip + OriginalFuncs + 135 * 8] - -timeGetDevCaps: - jmp qword ptr [rip + OriginalFuncs + 136 * 8] - -timeGetSystemTime: - jmp qword ptr [rip + OriginalFuncs + 137 * 8] - -timeGetTime: - jmp qword ptr [rip + OriginalFuncs + 138 * 8] - -timeKillEvent: - jmp qword ptr [rip + OriginalFuncs + 139 * 8] - -timeSetEvent: - jmp qword ptr [rip + OriginalFuncs + 140 * 8] - -waveInAddBuffer: - jmp qword ptr [rip + OriginalFuncs + 141 * 8] - -waveInClose: - jmp qword ptr [rip + OriginalFuncs + 142 * 8] - -waveInGetDevCapsA: - jmp qword ptr [rip + OriginalFuncs + 143 * 8] - -waveInGetDevCapsW: - jmp qword ptr [rip + OriginalFuncs + 144 * 8] - -waveInGetErrorTextA: - jmp qword ptr [rip + OriginalFuncs + 145 * 8] - -waveInGetErrorTextW: - jmp qword ptr [rip + OriginalFuncs + 146 * 8] - -waveInGetID: - jmp qword ptr [rip + OriginalFuncs + 147 * 8] - -waveInGetNumDevs: - jmp qword ptr [rip + OriginalFuncs + 148 * 8] - -waveInGetPosition: - jmp qword ptr [rip + OriginalFuncs + 149 * 8] - -waveInMessage: - jmp qword ptr [rip + OriginalFuncs + 150 * 8] - -waveInOpen: - jmp qword ptr [rip + OriginalFuncs + 151 * 8] - -waveInPrepareHeader: - jmp qword ptr [rip + OriginalFuncs + 152 * 8] - -waveInReset: - jmp qword ptr [rip + OriginalFuncs + 153 * 8] - -waveInStart: - jmp qword ptr [rip + OriginalFuncs + 154 * 8] - -waveInStop: - jmp qword ptr [rip + OriginalFuncs + 155 * 8] - -waveInUnprepareHeader: - jmp qword ptr [rip + OriginalFuncs + 156 * 8] - -waveOutBreakLoop: - jmp qword ptr [rip + OriginalFuncs + 157 * 8] - -waveOutClose: - jmp qword ptr [rip + OriginalFuncs + 158 * 8] - -waveOutGetDevCapsA: - jmp qword ptr [rip + OriginalFuncs + 159 * 8] - -waveOutGetDevCapsW: - jmp qword ptr [rip + OriginalFuncs + 160 * 8] - -waveOutGetErrorTextA: - jmp qword ptr [rip + OriginalFuncs + 161 * 8] - -waveOutGetErrorTextW: - jmp qword ptr [rip + OriginalFuncs + 162 * 8] - -waveOutGetID: - jmp qword ptr [rip + OriginalFuncs + 163 * 8] - -waveOutGetNumDevs: - jmp qword ptr [rip + OriginalFuncs + 164 * 8] - -waveOutGetPitch: - jmp qword ptr [rip + OriginalFuncs + 165 * 8] - -waveOutGetPlaybackRate: - jmp qword ptr [rip + OriginalFuncs + 166 * 8] - -waveOutGetPosition: - jmp qword ptr [rip + OriginalFuncs + 167 * 8] - -waveOutGetVolume: - jmp qword ptr [rip + OriginalFuncs + 168 * 8] - -waveOutMessage: - jmp qword ptr [rip + OriginalFuncs + 169 * 8] - -waveOutOpen: - jmp qword ptr [rip + OriginalFuncs + 170 * 8] - -waveOutPause: - jmp qword ptr [rip + OriginalFuncs + 171 * 8] - -waveOutPrepareHeader: - jmp qword ptr [rip + OriginalFuncs + 172 * 8] - -waveOutReset: - jmp qword ptr [rip + OriginalFuncs + 173 * 8] - -waveOutRestart: - jmp qword ptr [rip + OriginalFuncs + 174 * 8] - -waveOutSetPitch: - jmp qword ptr [rip + OriginalFuncs + 175 * 8] - -waveOutSetPlaybackRate: - jmp qword ptr [rip + OriginalFuncs + 176 * 8] - -waveOutSetVolume: - jmp qword ptr [rip + OriginalFuncs + 177 * 8] - -waveOutUnprepareHeader: - jmp qword ptr [rip + OriginalFuncs + 178 * 8] - -waveOutWrite: - jmp qword ptr [rip + OriginalFuncs + 179 * 8] - -ExportByOrdinal2: - jmp qword ptr [rip + OriginalFuncs + 180 * 8] \ No newline at end of file diff --git a/MelonProxy/deps/winmm.x86.S b/MelonProxy/deps/winmm.x86.S deleted file mode 100644 index 3eb2bbfb0..000000000 --- a/MelonProxy/deps/winmm.x86.S +++ /dev/null @@ -1,724 +0,0 @@ -.globl CloseDriver -.globl DefDriverProc -.globl DriverCallback -.globl DrvGetModuleHandle -.globl GetDriverModuleHandle -.globl OpenDriver -.globl PlaySound -.globl PlaySoundA -.globl PlaySoundW -.globl SendDriverMessage -.globl WOWAppExit -.globl auxGetDevCapsA -.globl auxGetDevCapsW -.globl auxGetNumDevs -.globl auxGetVolume -.globl auxOutMessage -.globl auxSetVolume -.globl joyConfigChanged -.globl joyGetDevCapsA -.globl joyGetDevCapsW -.globl joyGetNumDevs -.globl joyGetPos -.globl joyGetPosEx -.globl joyGetThreshold -.globl joyReleaseCapture -.globl joySetCapture -.globl joySetThreshold -.globl mciDriverNotify -.globl mciDriverYield -.globl mciExecute -.globl mciFreeCommandResource -.globl mciGetCreatorTask -.globl mciGetDeviceIDA -.globl mciGetDeviceIDFromElementIDA -.globl mciGetDeviceIDFromElementIDW -.globl mciGetDeviceIDW -.globl mciGetDriverData -.globl mciGetErrorStringA -.globl mciGetErrorStringW -.globl mciGetYieldProc -.globl mciLoadCommandResource -.globl mciSendCommandA -.globl mciSendCommandW -.globl mciSendStringA -.globl mciSendStringW -.globl mciSetDriverData -.globl mciSetYieldProc -.globl midiConnect -.globl midiDisconnect -.globl midiInAddBuffer -.globl midiInClose -.globl midiInGetDevCapsA -.globl midiInGetDevCapsW -.globl midiInGetErrorTextA -.globl midiInGetErrorTextW -.globl midiInGetID -.globl midiInGetNumDevs -.globl midiInMessage -.globl midiInOpen -.globl midiInPrepareHeader -.globl midiInReset -.globl midiInStart -.globl midiInStop -.globl midiInUnprepareHeader -.globl midiOutCacheDrumPatches -.globl midiOutCachePatches -.globl midiOutClose -.globl midiOutGetDevCapsA -.globl midiOutGetDevCapsW -.globl midiOutGetErrorTextA -.globl midiOutGetErrorTextW -.globl midiOutGetID -.globl midiOutGetNumDevs -.globl midiOutGetVolume -.globl midiOutLongMsg -.globl midiOutMessage -.globl midiOutOpen -.globl midiOutPrepareHeader -.globl midiOutReset -.globl midiOutSetVolume -.globl midiOutShortMsg -.globl midiOutUnprepareHeader -.globl midiStreamClose -.globl midiStreamOpen -.globl midiStreamOut -.globl midiStreamPause -.globl midiStreamPosition -.globl midiStreamProperty -.globl midiStreamRestart -.globl midiStreamStop -.globl mixerClose -.globl mixerGetControlDetailsA -.globl mixerGetControlDetailsW -.globl mixerGetDevCapsA -.globl mixerGetDevCapsW -.globl mixerGetID -.globl mixerGetLineControlsA -.globl mixerGetLineControlsW -.globl mixerGetLineInfoA -.globl mixerGetLineInfoW -.globl mixerGetNumDevs -.globl mixerMessage -.globl mixerOpen -.globl mixerSetControlDetails -.globl mmDrvInstall -.globl mmGetCurrentTask -.globl mmTaskBlock -.globl mmTaskCreate -.globl mmTaskSignal -.globl mmTaskYield -.globl mmioAdvance -.globl mmioAscend -.globl mmioClose -.globl mmioCreateChunk -.globl mmioDescend -.globl mmioFlush -.globl mmioGetInfo -.globl mmioInstallIOProcA -.globl mmioInstallIOProcW -.globl mmioOpenA -.globl mmioOpenW -.globl mmioRead -.globl mmioRenameA -.globl mmioRenameW -.globl mmioSeek -.globl mmioSendMessage -.globl mmioSetBuffer -.globl mmioSetInfo -.globl mmioStringToFOURCCA -.globl mmioStringToFOURCCW -.globl mmioWrite -.globl mmsystemGetVersion -.globl sndPlaySoundA -.globl sndPlaySoundW -.globl timeBeginPeriod -.globl timeEndPeriod -.globl timeGetDevCaps -.globl timeGetSystemTime -.globl timeGetTime -.globl timeKillEvent -.globl timeSetEvent -.globl waveInAddBuffer -.globl waveInClose -.globl waveInGetDevCapsA -.globl waveInGetDevCapsW -.globl waveInGetErrorTextA -.globl waveInGetErrorTextW -.globl waveInGetID -.globl waveInGetNumDevs -.globl waveInGetPosition -.globl waveInMessage -.globl waveInOpen -.globl waveInPrepareHeader -.globl waveInReset -.globl waveInStart -.globl waveInStop -.globl waveInUnprepareHeader -.globl waveOutBreakLoop -.globl waveOutClose -.globl waveOutGetDevCapsA -.globl waveOutGetDevCapsW -.globl waveOutGetErrorTextA -.globl waveOutGetErrorTextW -.globl waveOutGetID -.globl waveOutGetNumDevs -.globl waveOutGetPitch -.globl waveOutGetPlaybackRate -.globl waveOutGetPosition -.globl waveOutGetVolume -.globl waveOutMessage -.globl waveOutOpen -.globl waveOutPause -.globl waveOutPrepareHeader -.globl waveOutReset -.globl waveOutRestart -.globl waveOutSetPitch -.globl waveOutSetPlaybackRate -.globl waveOutSetVolume -.globl waveOutUnprepareHeader -.globl waveOutWrite -.globl ExportByOrdinal2 - -CloseDriver: - jmp ds:[_OriginalFuncs + 0 * 4] - -DefDriverProc: - jmp ds:[_OriginalFuncs + 1 * 4] - -DriverCallback: - jmp ds:[_OriginalFuncs + 2 * 4] - -DrvGetModuleHandle: - jmp ds:[_OriginalFuncs + 3 * 4] - -GetDriverModuleHandle: - jmp ds:[_OriginalFuncs + 4 * 4] - -OpenDriver: - jmp ds:[_OriginalFuncs + 5 * 4] - -PlaySound: - jmp ds:[_OriginalFuncs + 6 * 4] - -PlaySoundA: - jmp ds:[_OriginalFuncs + 7 * 4] - -PlaySoundW: - jmp ds:[_OriginalFuncs + 4 * 4] - -SendDriverMessage: - jmp ds:[_OriginalFuncs + 9 * 4] - -WOWAppExit: - jmp ds:[_OriginalFuncs + 10 * 4] - -auxGetDevCapsA: - jmp ds:[_OriginalFuncs + 11 * 4] - -auxGetDevCapsW: - jmp ds:[_OriginalFuncs + 12 * 4] - -auxGetNumDevs: - jmp ds:[_OriginalFuncs + 13 * 4] - -auxGetVolume: - jmp ds:[_OriginalFuncs + 14 * 4] - -auxOutMessage: - jmp ds:[_OriginalFuncs + 15 * 4] - -auxSetVolume: - jmp ds:[_OriginalFuncs + 16 * 4] - -joyConfigChanged: - jmp ds:[_OriginalFuncs + 17 * 4] - -joyGetDevCapsA: - jmp ds:[_OriginalFuncs + 14 * 4] - -joyGetDevCapsW: - jmp ds:[_OriginalFuncs + 19 * 4] - -joyGetNumDevs: - jmp ds:[_OriginalFuncs + 20 * 4] - -joyGetPos: - jmp ds:[_OriginalFuncs + 21 * 4] - -joyGetPosEx: - jmp ds:[_OriginalFuncs + 22 * 4] - -joyGetThreshold: - jmp ds:[_OriginalFuncs + 23 * 4] - -joyReleaseCapture: - jmp ds:[_OriginalFuncs + 24 * 4] - -joySetCapture: - jmp ds:[_OriginalFuncs + 25 * 4] - -joySetThreshold: - jmp ds:[_OriginalFuncs + 26 * 4] - -mciDriverNotify: - jmp ds:[_OriginalFuncs + 27 * 4] - -mciDriverYield: - jmp ds:[_OriginalFuncs + 24 * 4] - -mciExecute: - jmp ds:[_OriginalFuncs + 29 * 4] - -mciFreeCommandResource: - jmp ds:[_OriginalFuncs + 30 * 4] - -mciGetCreatorTask: - jmp ds:[_OriginalFuncs + 31 * 4] - -mciGetDeviceIDA: - jmp ds:[_OriginalFuncs + 32 * 4] - -mciGetDeviceIDFromElementIDA: - jmp ds:[_OriginalFuncs + 33 * 4] - -mciGetDeviceIDFromElementIDW: - jmp ds:[_OriginalFuncs + 34 * 4] - -mciGetDeviceIDW: - jmp ds:[_OriginalFuncs + 35 * 4] - -mciGetDriverData: - jmp ds:[_OriginalFuncs + 36 * 4] - -mciGetErrorStringA: - jmp ds:[_OriginalFuncs + 37 * 4] - -mciGetErrorStringW: - jmp ds:[_OriginalFuncs + 34 * 4] - -mciGetYieldProc: - jmp ds:[_OriginalFuncs + 39 * 4] - -mciLoadCommandResource: - jmp ds:[_OriginalFuncs + 40 * 4] - -mciSendCommandA: - jmp ds:[_OriginalFuncs + 41 * 4] - -mciSendCommandW: - jmp ds:[_OriginalFuncs + 42 * 4] - -mciSendStringA: - jmp ds:[_OriginalFuncs + 43 * 4] - -mciSendStringW: - jmp ds:[_OriginalFuncs + 44 * 4] - -mciSetDriverData: - jmp ds:[_OriginalFuncs + 45 * 4] - -mciSetYieldProc: - jmp ds:[_OriginalFuncs + 46 * 4] - -midiConnect: - jmp ds:[_OriginalFuncs + 47 * 4] - -midiDisconnect: - jmp ds:[_OriginalFuncs + 44 * 4] - -midiInAddBuffer: - jmp ds:[_OriginalFuncs + 49 * 4] - -midiInClose: - jmp ds:[_OriginalFuncs + 50 * 4] - -midiInGetDevCapsA: - jmp ds:[_OriginalFuncs + 51 * 4] - -midiInGetDevCapsW: - jmp ds:[_OriginalFuncs + 52 * 4] - -midiInGetErrorTextA: - jmp ds:[_OriginalFuncs + 53 * 4] - -midiInGetErrorTextW: - jmp ds:[_OriginalFuncs + 54 * 4] - -midiInGetID: - jmp ds:[_OriginalFuncs + 55 * 4] - -midiInGetNumDevs: - jmp ds:[_OriginalFuncs + 56 * 4] - -midiInMessage: - jmp ds:[_OriginalFuncs + 57 * 4] - -midiInOpen: - jmp ds:[_OriginalFuncs + 54 * 4] - -midiInPrepareHeader: - jmp ds:[_OriginalFuncs + 59 * 4] - -midiInReset: - jmp ds:[_OriginalFuncs + 60 * 4] - -midiInStart: - jmp ds:[_OriginalFuncs + 61 * 4] - -midiInStop: - jmp ds:[_OriginalFuncs + 62 * 4] - -midiInUnprepareHeader: - jmp ds:[_OriginalFuncs + 63 * 4] - -midiOutCacheDrumPatches: - jmp ds:[_OriginalFuncs + 64 * 4] - -midiOutCachePatches: - jmp ds:[_OriginalFuncs + 65 * 4] - -midiOutClose: - jmp ds:[_OriginalFuncs + 66 * 4] - -midiOutGetDevCapsA: - jmp ds:[_OriginalFuncs + 67 * 4] - -midiOutGetDevCapsW: - jmp ds:[_OriginalFuncs + 64 * 4] - -midiOutGetErrorTextA: - jmp ds:[_OriginalFuncs + 69 * 4] - -midiOutGetErrorTextW: - jmp ds:[_OriginalFuncs + 70 * 4] - -midiOutGetID: - jmp ds:[_OriginalFuncs + 71 * 4] - -midiOutGetNumDevs: - jmp ds:[_OriginalFuncs + 72 * 4] - -midiOutGetVolume: - jmp ds:[_OriginalFuncs + 73 * 4] - -midiOutLongMsg: - jmp ds:[_OriginalFuncs + 74 * 4] - -midiOutMessage: - jmp ds:[_OriginalFuncs + 75 * 4] - -midiOutOpen: - jmp ds:[_OriginalFuncs + 76 * 4] - -midiOutPrepareHeader: - jmp ds:[_OriginalFuncs + 77 * 4] - -midiOutReset: - jmp ds:[_OriginalFuncs + 74 * 4] - -midiOutSetVolume: - jmp ds:[_OriginalFuncs + 79 * 4] - -midiOutShortMsg: - jmp ds:[_OriginalFuncs + 40 * 4] - -midiOutUnprepareHeader: - jmp ds:[_OriginalFuncs + 41 * 4] - -midiStreamClose: - jmp ds:[_OriginalFuncs + 42 * 4] - -midiStreamOpen: - jmp ds:[_OriginalFuncs + 43 * 4] - -midiStreamOut: - jmp ds:[_OriginalFuncs + 44 * 4] - -midiStreamPause: - jmp ds:[_OriginalFuncs + 45 * 4] - -midiStreamPosition: - jmp ds:[_OriginalFuncs + 46 * 4] - -midiStreamProperty: - jmp ds:[_OriginalFuncs + 47 * 4] - -midiStreamRestart: - jmp ds:[_OriginalFuncs + 44 * 4] - -midiStreamStop: - jmp ds:[_OriginalFuncs + 49 * 4] - -mixerClose: - jmp ds:[_OriginalFuncs + 90 * 4] - -mixerGetControlDetailsA: - jmp ds:[_OriginalFuncs + 91 * 4] - -mixerGetControlDetailsW: - jmp ds:[_OriginalFuncs + 92 * 4] - -mixerGetDevCapsA: - jmp ds:[_OriginalFuncs + 93 * 4] - -mixerGetDevCapsW: - jmp ds:[_OriginalFuncs + 94 * 4] - -mixerGetID: - jmp ds:[_OriginalFuncs + 95 * 4] - -mixerGetLineControlsA: - jmp ds:[_OriginalFuncs + 96 * 4] - -mixerGetLineControlsW: - jmp ds:[_OriginalFuncs + 97 * 4] - -mixerGetLineInfoA: - jmp ds:[_OriginalFuncs + 94 * 4] - -mixerGetLineInfoW: - jmp ds:[_OriginalFuncs + 99 * 4] - -mixerGetNumDevs: - jmp ds:[_OriginalFuncs + 100 * 4] - -mixerMessage: - jmp ds:[_OriginalFuncs + 101 * 4] - -mixerOpen: - jmp ds:[_OriginalFuncs + 102 * 4] - -mixerSetControlDetails: - jmp ds:[_OriginalFuncs + 103 * 4] - -mmDrvInstall: - jmp ds:[_OriginalFuncs + 104 * 4] - -mmGetCurrentTask: - jmp ds:[_OriginalFuncs + 105 * 4] - -mmTaskBlock: - jmp ds:[_OriginalFuncs + 106 * 4] - -mmTaskCreate: - jmp ds:[_OriginalFuncs + 107 * 4] - -mmTaskSignal: - jmp ds:[_OriginalFuncs + 104 * 4] - -mmTaskYield: - jmp ds:[_OriginalFuncs + 109 * 4] - -mmioAdvance: - jmp ds:[_OriginalFuncs + 110 * 4] - -mmioAscend: - jmp ds:[_OriginalFuncs + 111 * 4] - -mmioClose: - jmp ds:[_OriginalFuncs + 112 * 4] - -mmioCreateChunk: - jmp ds:[_OriginalFuncs + 113 * 4] - -mmioDescend: - jmp ds:[_OriginalFuncs + 114 * 4] - -mmioFlush: - jmp ds:[_OriginalFuncs + 115 * 4] - -mmioGetInfo: - jmp ds:[_OriginalFuncs + 116 * 4] - -mmioInstallIOProcA: - jmp ds:[_OriginalFuncs + 117 * 4] - -mmioInstallIOProcW: - jmp ds:[_OriginalFuncs + 114 * 4] - -mmioOpenA: - jmp ds:[_OriginalFuncs + 119 * 4] - -mmioOpenW: - jmp ds:[_OriginalFuncs + 120 * 4] - -mmioRead: - jmp ds:[_OriginalFuncs + 121 * 4] - -mmioRenameA: - jmp ds:[_OriginalFuncs + 122 * 4] - -mmioRenameW: - jmp ds:[_OriginalFuncs + 123 * 4] - -mmioSeek: - jmp ds:[_OriginalFuncs + 124 * 4] - -mmioSendMessage: - jmp ds:[_OriginalFuncs + 125 * 4] - -mmioSetBuffer: - jmp ds:[_OriginalFuncs + 126 * 4] - -mmioSetInfo: - jmp ds:[_OriginalFuncs + 127 * 4] - -mmioStringToFOURCCA: - jmp ds:[_OriginalFuncs + 124 * 4] - -mmioStringToFOURCCW: - jmp ds:[_OriginalFuncs + 129 * 4] - -mmioWrite: - jmp ds:[_OriginalFuncs + 130 * 4] - -mmsystemGetVersion: - jmp ds:[_OriginalFuncs + 131 * 4] - -sndPlaySoundA: - jmp ds:[_OriginalFuncs + 132 * 4] - -sndPlaySoundW: - jmp ds:[_OriginalFuncs + 133 * 4] - -timeBeginPeriod: - jmp ds:[_OriginalFuncs + 134 * 4] - -timeEndPeriod: - jmp ds:[_OriginalFuncs + 135 * 4] - -timeGetDevCaps: - jmp ds:[_OriginalFuncs + 136 * 4] - -timeGetSystemTime: - jmp ds:[_OriginalFuncs + 137 * 4] - -timeGetTime: - jmp ds:[_OriginalFuncs + 134 * 4] - -timeKillEvent: - jmp ds:[_OriginalFuncs + 139 * 4] - -timeSetEvent: - jmp ds:[_OriginalFuncs + 140 * 4] - -waveInAddBuffer: - jmp ds:[_OriginalFuncs + 141 * 4] - -waveInClose: - jmp ds:[_OriginalFuncs + 142 * 4] - -waveInGetDevCapsA: - jmp ds:[_OriginalFuncs + 143 * 4] - -waveInGetDevCapsW: - jmp ds:[_OriginalFuncs + 144 * 4] - -waveInGetErrorTextA: - jmp ds:[_OriginalFuncs + 145 * 4] - -waveInGetErrorTextW: - jmp ds:[_OriginalFuncs + 146 * 4] - -waveInGetID: - jmp ds:[_OriginalFuncs + 147 * 4] - -waveInGetNumDevs: - jmp ds:[_OriginalFuncs + 144 * 4] - -waveInGetPosition: - jmp ds:[_OriginalFuncs + 149 * 4] - -waveInMessage: - jmp ds:[_OriginalFuncs + 150 * 4] - -waveInOpen: - jmp ds:[_OriginalFuncs + 151 * 4] - -waveInPrepareHeader: - jmp ds:[_OriginalFuncs + 152 * 4] - -waveInReset: - jmp ds:[_OriginalFuncs + 153 * 4] - -waveInStart: - jmp ds:[_OriginalFuncs + 154 * 4] - -waveInStop: - jmp ds:[_OriginalFuncs + 155 * 4] - -waveInUnprepareHeader: - jmp ds:[_OriginalFuncs + 156 * 4] - -waveOutBreakLoop: - jmp ds:[_OriginalFuncs + 157 * 4] - -waveOutClose: - jmp ds:[_OriginalFuncs + 154 * 4] - -waveOutGetDevCapsA: - jmp ds:[_OriginalFuncs + 159 * 4] - -waveOutGetDevCapsW: - jmp ds:[_OriginalFuncs + 160 * 4] - -waveOutGetErrorTextA: - jmp ds:[_OriginalFuncs + 161 * 4] - -waveOutGetErrorTextW: - jmp ds:[_OriginalFuncs + 162 * 4] - -waveOutGetID: - jmp ds:[_OriginalFuncs + 163 * 4] - -waveOutGetNumDevs: - jmp ds:[_OriginalFuncs + 164 * 4] - -waveOutGetPitch: - jmp ds:[_OriginalFuncs + 165 * 4] - -waveOutGetPlaybackRate: - jmp ds:[_OriginalFuncs + 166 * 4] - -waveOutGetPosition: - jmp ds:[_OriginalFuncs + 167 * 4] - -waveOutGetVolume: - jmp ds:[_OriginalFuncs + 164 * 4] - -waveOutMessage: - jmp ds:[_OriginalFuncs + 169 * 4] - -waveOutOpen: - jmp ds:[_OriginalFuncs + 170 * 4] - -waveOutPause: - jmp ds:[_OriginalFuncs + 171 * 4] - -waveOutPrepareHeader: - jmp ds:[_OriginalFuncs + 172 * 4] - -waveOutReset: - jmp ds:[_OriginalFuncs + 173 * 4] - -waveOutRestart: - jmp ds:[_OriginalFuncs + 174 * 4] - -waveOutSetPitch: - jmp ds:[_OriginalFuncs + 175 * 4] - -waveOutSetPlaybackRate: - jmp ds:[_OriginalFuncs + 176 * 4] - -waveOutSetVolume: - jmp ds:[_OriginalFuncs + 177 * 4] - -waveOutUnprepareHeader: - jmp ds:[_OriginalFuncs + 174 * 4] - -waveOutWrite: - jmp ds:[_OriginalFuncs + 179 * 4] - -ExportByOrdinal2: - jmp ds:[_OriginalFuncs + 140 * 4] \ No newline at end of file diff --git a/MelonProxy/src/export_indices.rs b/MelonProxy/src/export_indices.rs new file mode 100644 index 000000000..13e524567 --- /dev/null +++ b/MelonProxy/src/export_indices.rs @@ -0,0 +1,256 @@ +// After making changes in this file, you should run `proxygen update .`` in the root of this project to update exports + +#![allow(non_upper_case_globals)] + +pub const TOTAL_EXPORTS: usize = 251; +pub const Index_CloseDriver: usize = 0; +pub const Index_DefDriverProc: usize = 1; +pub const Index_DllCanUnloadNow: usize = 2; +pub const Index_DllGetClassObject: usize = 3; +pub const Index_DllRegisterServer: usize = 4; +pub const Index_DllUnregisterServer: usize = 5; +pub const Index_DriverCallback: usize = 6; +pub const Index_DrvClose: usize = 7; +pub const Index_DrvDefDriverProc: usize = 8; +pub const Index_DrvGetModuleHandle: usize = 9; +pub const Index_DrvOpen: usize = 10; +pub const Index_DrvOpenA: usize = 11; +pub const Index_DrvSendMessage: usize = 12; +pub const Index_GetDriverFlags: usize = 13; +pub const Index_GetDriverModuleHandle: usize = 14; +pub const Index_GetFileVersionInfoA: usize = 15; +pub const Index_GetFileVersionInfoExA: usize = 16; +pub const Index_GetFileVersionInfoExW: usize = 17; +pub const Index_GetFileVersionInfoSizeA: usize = 18; +pub const Index_GetFileVersionInfoSizeExA: usize = 19; +pub const Index_GetFileVersionInfoSizeExW: usize = 20; +pub const Index_GetFileVersionInfoSizeW: usize = 21; +pub const Index_GetFileVersionInfoW: usize = 22; +pub const Index_OpenDriver: usize = 23; +pub const Index_OpenDriverA: usize = 24; +pub const Index_PlaySound: usize = 25; +pub const Index_PlaySoundA: usize = 26; +pub const Index_PlaySoundW: usize = 27; +pub const Index_SendDriverMessage: usize = 28; +pub const Index_VerFindFileA: usize = 29; +pub const Index_VerFindFileW: usize = 30; +pub const Index_VerInstallFileA: usize = 31; +pub const Index_VerInstallFileW: usize = 32; +pub const Index_VerLanguageNameA: usize = 33; +pub const Index_VerLanguageNameW: usize = 34; +pub const Index_VerQueryValueA: usize = 35; +pub const Index_VerQueryValueW: usize = 36; +pub const Index_WinHttpAddRequestHeaders: usize = 37; +pub const Index_WinHttpCheckPlatform: usize = 38; +pub const Index_WinHttpCloseHandle: usize = 39; +pub const Index_WinHttpConnect: usize = 40; +pub const Index_WinHttpCrackUrl: usize = 41; +pub const Index_WinHttpCreateProxyResolver: usize = 42; +pub const Index_WinHttpCreateUrl: usize = 43; +pub const Index_WinHttpDetectAutoProxyConfigUrl: usize = 44; +pub const Index_WinHttpFreeProxyResult: usize = 45; +pub const Index_WinHttpFreeProxyResultEx: usize = 46; +pub const Index_WinHttpFreeProxySettings: usize = 47; +pub const Index_WinHttpGetDefaultProxyConfiguration: usize = 48; +pub const Index_WinHttpGetIEProxyConfigForCurrentUser: usize = 49; +pub const Index_WinHttpGetProxyForUrl: usize = 50; +pub const Index_WinHttpGetProxyForUrlEx: usize = 51; +pub const Index_WinHttpGetProxyForUrlEx2: usize = 52; +pub const Index_WinHttpGetProxyResult: usize = 53; +pub const Index_WinHttpGetProxyResultEx: usize = 54; +pub const Index_WinHttpGetProxySettingsVersion: usize = 55; +pub const Index_WinHttpOpen: usize = 56; +pub const Index_WinHttpOpenRequest: usize = 57; +pub const Index_WinHttpQueryAuthSchemes: usize = 58; +pub const Index_WinHttpQueryDataAvailable: usize = 59; +pub const Index_WinHttpQueryHeaders: usize = 60; +pub const Index_WinHttpQueryOption: usize = 61; +pub const Index_WinHttpReadData: usize = 62; +pub const Index_WinHttpReadProxySettings: usize = 63; +pub const Index_WinHttpReceiveResponse: usize = 64; +pub const Index_WinHttpResetAutoProxy: usize = 65; +pub const Index_WinHttpSendRequest: usize = 66; +pub const Index_WinHttpSetCredentials: usize = 67; +pub const Index_WinHttpSetDefaultProxyConfiguration: usize = 68; +pub const Index_WinHttpSetOption: usize = 69; +pub const Index_WinHttpSetStatusCallback: usize = 70; +pub const Index_WinHttpSetTimeouts: usize = 71; +pub const Index_WinHttpTimeFromSystemTime: usize = 72; +pub const Index_WinHttpTimeToSystemTime: usize = 73; +pub const Index_WinHttpWebSocketClose: usize = 74; +pub const Index_WinHttpWebSocketCompleteUpgrade: usize = 75; +pub const Index_WinHttpWebSocketQueryCloseStatus: usize = 76; +pub const Index_WinHttpWebSocketReceive: usize = 77; +pub const Index_WinHttpWebSocketSend: usize = 78; +pub const Index_WinHttpWebSocketShutdown: usize = 79; +pub const Index_WinHttpWriteData: usize = 80; +pub const Index_WinHttpWriteProxySettings: usize = 81; +pub const Index_auxGetDevCapsA: usize = 82; +pub const Index_auxGetDevCapsW: usize = 83; +pub const Index_auxGetNumDevs: usize = 84; +pub const Index_auxGetVolume: usize = 85; +pub const Index_auxOutMessage: usize = 86; +pub const Index_auxSetVolume: usize = 87; +pub const Index_joyConfigChanged: usize = 88; +pub const Index_joyGetDevCapsA: usize = 89; +pub const Index_joyGetDevCapsW: usize = 90; +pub const Index_joyGetNumDevs: usize = 91; +pub const Index_joyGetPos: usize = 92; +pub const Index_joyGetPosEx: usize = 93; +pub const Index_joyGetThreshold: usize = 94; +pub const Index_joyReleaseCapture: usize = 95; +pub const Index_joySetCapture: usize = 96; +pub const Index_joySetThreshold: usize = 97; +pub const Index_mciDriverNotify: usize = 98; +pub const Index_mciDriverYield: usize = 99; +pub const Index_mciExecute: usize = 100; +pub const Index_mciFreeCommandResource: usize = 101; +pub const Index_mciGetCreatorTask: usize = 102; +pub const Index_mciGetDeviceIDA: usize = 103; +pub const Index_mciGetDeviceIDFromElementIDA: usize = 104; +pub const Index_mciGetDeviceIDFromElementIDW: usize = 105; +pub const Index_mciGetDeviceIDW: usize = 106; +pub const Index_mciGetDriverData: usize = 107; +pub const Index_mciGetErrorStringA: usize = 108; +pub const Index_mciGetErrorStringW: usize = 109; +pub const Index_mciGetYieldProc: usize = 110; +pub const Index_mciLoadCommandResource: usize = 111; +pub const Index_mciSendCommandA: usize = 112; +pub const Index_mciSendCommandW: usize = 113; +pub const Index_mciSendStringA: usize = 114; +pub const Index_mciSendStringW: usize = 115; +pub const Index_mciSetDriverData: usize = 116; +pub const Index_mciSetYieldProc: usize = 117; +pub const Index_midiConnect: usize = 118; +pub const Index_midiDisconnect: usize = 119; +pub const Index_midiInAddBuffer: usize = 120; +pub const Index_midiInClose: usize = 121; +pub const Index_midiInGetDevCapsA: usize = 122; +pub const Index_midiInGetDevCapsW: usize = 123; +pub const Index_midiInGetErrorTextA: usize = 124; +pub const Index_midiInGetErrorTextW: usize = 125; +pub const Index_midiInGetID: usize = 126; +pub const Index_midiInGetNumDevs: usize = 127; +pub const Index_midiInMessage: usize = 128; +pub const Index_midiInOpen: usize = 129; +pub const Index_midiInPrepareHeader: usize = 130; +pub const Index_midiInReset: usize = 131; +pub const Index_midiInStart: usize = 132; +pub const Index_midiInStop: usize = 133; +pub const Index_midiInUnprepareHeader: usize = 134; +pub const Index_midiOutCacheDrumPatches: usize = 135; +pub const Index_midiOutCachePatches: usize = 136; +pub const Index_midiOutClose: usize = 137; +pub const Index_midiOutGetDevCapsA: usize = 138; +pub const Index_midiOutGetDevCapsW: usize = 139; +pub const Index_midiOutGetErrorTextA: usize = 140; +pub const Index_midiOutGetErrorTextW: usize = 141; +pub const Index_midiOutGetID: usize = 142; +pub const Index_midiOutGetNumDevs: usize = 143; +pub const Index_midiOutGetVolume: usize = 144; +pub const Index_midiOutLongMsg: usize = 145; +pub const Index_midiOutMessage: usize = 146; +pub const Index_midiOutOpen: usize = 147; +pub const Index_midiOutPrepareHeader: usize = 148; +pub const Index_midiOutReset: usize = 149; +pub const Index_midiOutSetVolume: usize = 150; +pub const Index_midiOutShortMsg: usize = 151; +pub const Index_midiOutUnprepareHeader: usize = 152; +pub const Index_midiStreamClose: usize = 153; +pub const Index_midiStreamOpen: usize = 154; +pub const Index_midiStreamOut: usize = 155; +pub const Index_midiStreamPause: usize = 156; +pub const Index_midiStreamPosition: usize = 157; +pub const Index_midiStreamProperty: usize = 158; +pub const Index_midiStreamRestart: usize = 159; +pub const Index_midiStreamStop: usize = 160; +pub const Index_mixerClose: usize = 161; +pub const Index_mixerGetControlDetailsA: usize = 162; +pub const Index_mixerGetControlDetailsW: usize = 163; +pub const Index_mixerGetDevCapsA: usize = 164; +pub const Index_mixerGetDevCapsW: usize = 165; +pub const Index_mixerGetID: usize = 166; +pub const Index_mixerGetLineControlsA: usize = 167; +pub const Index_mixerGetLineControlsW: usize = 168; +pub const Index_mixerGetLineInfoA: usize = 169; +pub const Index_mixerGetLineInfoW: usize = 170; +pub const Index_mixerGetNumDevs: usize = 171; +pub const Index_mixerMessage: usize = 172; +pub const Index_mixerOpen: usize = 173; +pub const Index_mixerSetControlDetails: usize = 174; +pub const Index_mmGetCurrentTask: usize = 175; +pub const Index_mmTaskBlock: usize = 176; +pub const Index_mmTaskCreate: usize = 177; +pub const Index_mmTaskSignal: usize = 178; +pub const Index_mmTaskYield: usize = 179; +pub const Index_mmioAdvance: usize = 180; +pub const Index_mmioAscend: usize = 181; +pub const Index_mmioClose: usize = 182; +pub const Index_mmioCreateChunk: usize = 183; +pub const Index_mmioDescend: usize = 184; +pub const Index_mmioFlush: usize = 185; +pub const Index_mmioGetInfo: usize = 186; +pub const Index_mmioInstallIOProc16: usize = 187; +pub const Index_mmioInstallIOProcA: usize = 188; +pub const Index_mmioInstallIOProcW: usize = 189; +pub const Index_mmioOpenA: usize = 190; +pub const Index_mmioOpenW: usize = 191; +pub const Index_mmioRead: usize = 192; +pub const Index_mmioRenameA: usize = 193; +pub const Index_mmioRenameW: usize = 194; +pub const Index_mmioSeek: usize = 195; +pub const Index_mmioSendMessage: usize = 196; +pub const Index_mmioSetBuffer: usize = 197; +pub const Index_mmioSetInfo: usize = 198; +pub const Index_mmioStringToFOURCCA: usize = 199; +pub const Index_mmioStringToFOURCCW: usize = 200; +pub const Index_mmioWrite: usize = 201; +pub const Index_mmsystemGetVersion: usize = 202; +pub const Index_sndPlaySoundA: usize = 203; +pub const Index_sndPlaySoundW: usize = 204; +pub const Index_timeBeginPeriod: usize = 205; +pub const Index_timeEndPeriod: usize = 206; +pub const Index_timeGetDevCaps: usize = 207; +pub const Index_timeGetSystemTime: usize = 208; +pub const Index_timeGetTime: usize = 209; +pub const Index_timeKillEvent: usize = 210; +pub const Index_timeSetEvent: usize = 211; +pub const Index_waveInAddBuffer: usize = 212; +pub const Index_waveInClose: usize = 213; +pub const Index_waveInGetDevCapsA: usize = 214; +pub const Index_waveInGetDevCapsW: usize = 215; +pub const Index_waveInGetErrorTextA: usize = 216; +pub const Index_waveInGetErrorTextW: usize = 217; +pub const Index_waveInGetID: usize = 218; +pub const Index_waveInGetNumDevs: usize = 219; +pub const Index_waveInGetPosition: usize = 220; +pub const Index_waveInMessage: usize = 221; +pub const Index_waveInOpen: usize = 222; +pub const Index_waveInPrepareHeader: usize = 223; +pub const Index_waveInReset: usize = 224; +pub const Index_waveInStart: usize = 225; +pub const Index_waveInStop: usize = 226; +pub const Index_waveInUnprepareHeader: usize = 227; +pub const Index_waveOutBreakLoop: usize = 228; +pub const Index_waveOutClose: usize = 229; +pub const Index_waveOutGetDevCapsA: usize = 230; +pub const Index_waveOutGetDevCapsW: usize = 231; +pub const Index_waveOutGetErrorTextA: usize = 232; +pub const Index_waveOutGetErrorTextW: usize = 233; +pub const Index_waveOutGetID: usize = 234; +pub const Index_waveOutGetNumDevs: usize = 235; +pub const Index_waveOutGetPitch: usize = 236; +pub const Index_waveOutGetPlaybackRate: usize = 237; +pub const Index_waveOutGetPosition: usize = 238; +pub const Index_waveOutGetVolume: usize = 239; +pub const Index_waveOutMessage: usize = 240; +pub const Index_waveOutOpen: usize = 241; +pub const Index_waveOutPause: usize = 242; +pub const Index_waveOutPrepareHeader: usize = 243; +pub const Index_waveOutReset: usize = 244; +pub const Index_waveOutRestart: usize = 245; +pub const Index_waveOutSetPitch: usize = 246; +pub const Index_waveOutSetPlaybackRate: usize = 247; +pub const Index_waveOutSetVolume: usize = 248; +pub const Index_waveOutUnprepareHeader: usize = 249; +pub const Index_waveOutWrite: usize = 250; diff --git a/MelonProxy/src/intercepted_exports.rs b/MelonProxy/src/intercepted_exports.rs new file mode 100644 index 000000000..b4bffa80b --- /dev/null +++ b/MelonProxy/src/intercepted_exports.rs @@ -0,0 +1,44 @@ +// Intercepted/replaced functions go here +// Remember to run `proxygen update .` in the root of this project every time you add or remove an export here +// +// Example function proxies/hooks. +// Note, if using any of the `proxy`, `pre_hook` or `post_hook` macros, you will have access to the original function +// You can easily call `orig_func` with the same args as your interceptor function +// NOTE: Use the correct arg types and return type for any functions you proxy, or else you will probably mess up the stack +// and you will probably crash whatever program the DLL is loaded into +// +// #[pre_hook(sig="known")] +// #[export_name="SomeFunction"] +// pub extern "C" fn SomeFunction(some_arg_1: usize, some_arg_2: u32) -> bool { +// println!("Pre-hooked SomeFunction. Args: {}, {}", some_arg_1, some_arg_2); +// // After all our code in this pre-hook runs, if we don't return, the original function will be called +// // and its result will be returned +// } +// +// #[proxy(sig="known")] +// #[export_name="SomeFunction"] +// pub extern "C" fn SomeFunction(some_arg_1: usize, some_arg_2: u32) -> bool { +// let orig_result = orig_func(some_arg_1, some_arg_2); +// println!("Manually proxied SomeFunction. Args: {}, {}. Result: {}", some_arg_1, some_arg_2, orig_result); +// // This is just a normal/manual proxy. It is up to us to return a value. +// // Also note that the original function `orig_func` will not be run in this case unless we explicitly call it +// true +// } +// +// #[post_hook(sig="known")] +// #[export_name="SomeFunction"] +// pub extern "C" fn SomeFunction(some_arg_1: usize, some_arg_2: u32) -> bool { +// // `orig_func` got run just before our code. Its result is stored in `orig_result` +// println!("In post-hook for SomeFunction. Args: {}, {}. Result: {}", some_arg_1, some_arg_2, orig_result); +// // We could manually return something here if we didn't want `orig_result` returned +// } +// +// #[pre_hook(sig="unknown")] +// #[export_name="SomeFunction"] +// pub extern "C" fn SomeFunction() { +// println!("In pre-hook for SomeFunction. (signature unknown)") +// } + +#![allow(unused_imports)] +use proxygen_macros::{post_hook, pre_hook, proxy, forward}; + diff --git a/MelonProxy/src/lib.rs b/MelonProxy/src/lib.rs index 8b7e796b7..62e61bc72 100644 --- a/MelonProxy/src/lib.rs +++ b/MelonProxy/src/lib.rs @@ -1,42 +1,180 @@ -#![feature(fn_ptr_trait)] -#![feature(lazy_cell)] +//! Most of this Project was generated using [ProxyGen](https://github.com/WarrenHood/proxygen) +//! +//! Altered to work with MelonLoader, and be cross platform. +#![feature(naked_functions)] +#![allow(named_asm_labels)] #![allow(non_snake_case)] -#![deny( - missing_debug_implementations, - unused_results, - warnings, - clippy::extra_unused_lifetimes, - clippy::from_over_into, - clippy::needless_borrow, - clippy::new_without_default, - clippy::useless_conversion -)] -#![forbid(rust_2018_idioms)] -#![allow(clippy::inherent_to_string, clippy::type_complexity, improper_ctypes)] -#![cfg_attr(docsrs, feature(doc_cfg))] - -#[cfg(target_os = "windows")] -pub use windows::Win32::Foundation::HINSTANCE; - -#[cfg(target_os = "windows")] -pub mod proxy; - -pub mod utils; -pub mod core; - -/// this function will get called by our proxy macro. See MelonProxy-sys -#[cfg_attr( - not(target_os = "windows"), - ctor::ctor -)] -#[cfg_attr( - target_os = "windows", - melon_proxy_sys::proxy -)] -#[allow(dead_code)] -fn main() { +#![feature(asm_const)] +#![feature(lazy_cell)] + +#[cfg(target_os = "windows")] +mod export_indices; +#[cfg(target_os = "windows")] +mod intercepted_exports; +#[cfg(target_os = "windows")] +mod orig_exports; +#[cfg(target_os = "windows")] +mod proxied_exports; + +mod utils; +mod core; + + +#[allow(unused_imports)] +#[cfg(target_os = "windows")] +pub use intercepted_exports::*; +#[cfg(target_os = "windows")] +pub use proxied_exports::*; + +#[cfg(target_os = "windows")] +use export_indices::TOTAL_EXPORTS; +#[cfg(target_os = "windows")] +use orig_exports::load_dll_funcs; +#[cfg(target_arch="x86_64")] +#[cfg(target_os = "windows")] +use std::arch::x86_64::_mm_pause; +#[cfg(target_arch="x86")] +#[cfg(target_os = "windows")] +use std::arch::x86::_mm_pause; +#[cfg(target_os = "windows")] +use std::ffi::OsString; +#[cfg(target_os = "windows")] +use std::os::windows::prelude::{AsRawHandle, OsStringExt}; + +#[cfg(target_os = "windows")] +use winapi::{ + ctypes::c_void, + shared::{ + minwindef::{FARPROC, HMODULE}, + ntdef::LPCSTR, + }, + um::{ + consoleapi::AllocConsole, + errhandlingapi::GetLastError, + libloaderapi::{DisableThreadLibraryCalls, FreeLibrary, GetModuleFileNameW, LoadLibraryA}, + processenv::SetStdHandle, + processthreadsapi::{CreateThread, GetCurrentProcess, TerminateProcess}, + winbase::{STD_ERROR_HANDLE, STD_OUTPUT_HANDLE}, + winnt::{DLL_PROCESS_ATTACH, DLL_PROCESS_DETACH}, + winuser::{MessageBoxA, MB_OK}, + }, +}; + + +// Static handles +#[cfg(target_os = "windows")] +static mut THIS_HANDLE: Option = None; +#[cfg(target_os = "windows")] +static mut ORIG_DLL_HANDLE: Option = None; + +// Original funcs +#[cfg(target_os = "windows")] +#[no_mangle] +static mut ORIGINAL_FUNCS: [FARPROC; TOTAL_EXPORTS] = [std::ptr::null_mut(); TOTAL_EXPORTS]; +#[cfg(target_os = "windows")] +#[no_mangle] +static mut ORIG_FUNCS_PTR: *const FARPROC = std::ptr::null_mut(); + +/// Indicates once we are ready to accept incoming calls to proxied functions + +static mut PROXYGEN_READY: bool = false; + +#[cfg(target_os = "windows")] +#[no_mangle] +pub unsafe extern "stdcall" fn DllMain(module: HMODULE, reason: isize, _res: *const c_void) -> i32 { + THIS_HANDLE = Some(module); + + if reason == 1 { + init(std::ptr::null_mut()); + + core::init().unwrap_or_else(|e| { + internal_failure!("Failed to initialize MelonLoader: {}", e) + }); + } + + 1 +} + +#[cfg(not(target_os = "windows"))] +#[ctor::ctor] +fn init() { core::init().unwrap_or_else(|e| { - internal_failure!("Failed to initialize MelonLoader: {}", e); + internal_failure!("Failed to initialize MelonLoader: {}", e) }); -} \ No newline at end of file +} + + +/// Get the current DLLs path +#[cfg(target_os = "windows")] +unsafe fn get_dll_path() -> Option { + let mut buffer: Vec = vec![0; 260]; + if THIS_HANDLE.is_none() { + return None; + } + let size = GetModuleFileNameW( + THIS_HANDLE.unwrap(), + buffer.as_mut_ptr(), + buffer.len() as u32, + ); + + if size == 0 { + return None; + } + + buffer.truncate(size as usize); + let os_string = OsString::from_wide(&buffer); + Some(os_string.to_string_lossy().into_owned()) +} + +/// Called when the thread is spawned +#[cfg(target_os = "windows")] +unsafe extern "system" fn init(_: *mut c_void) -> u32 { + use std::path::PathBuf; + + ORIG_FUNCS_PTR = ORIGINAL_FUNCS.as_ptr(); + + if let Some(dll_path) = get_dll_path() { + println!("This DLL path: {}", &dll_path); + let path = PathBuf::from(dll_path); + let orig_dll_name = path.file_name().unwrap_or_else(|| { + internal_failure!("Failed to get DLL name"); + }); + + let path = PathBuf::from("C:\\Windows\\System32").join(orig_dll_name); + + if !path.exists() { + internal_failure!("Original DLL does not exist"); + } + + let path = path.to_str().unwrap_or_else(|| { + internal_failure!("Failed to convert path to string"); + }); + + ORIG_DLL_HANDLE = Some(LoadLibraryA(path.as_ptr() as *const i8)); + } else { + internal_failure!("Failed to get DLL path"); + return 1; + } + if let Some(orig_dll_handle) = ORIG_DLL_HANDLE { + if orig_dll_handle.is_null() { + internal_failure!("Failed to load original DLL"); + } + println!("Original DLL handle: {:?}", orig_dll_handle); + } else { + let err = GetLastError(); + internal_failure!("Failed to load original DLL: {}", err); + } + load_dll_funcs(); + PROXYGEN_READY = true; + 0 +} + +/// Call this before attempting to call a function in the proxied DLL +/// +/// This will wait for proxygen to fully load up all the proxied function addresses before returning +#[cfg(target_os = "windows")] +#[no_mangle] +pub extern "C" fn wait_dll_proxy_init() { + //leftover from proxygen +} diff --git a/MelonProxy/src/orig_exports.rs b/MelonProxy/src/orig_exports.rs new file mode 100644 index 000000000..74eb55408 --- /dev/null +++ b/MelonProxy/src/orig_exports.rs @@ -0,0 +1,276 @@ +use crate::export_indices::*; +use crate::{ORIGINAL_FUNCS, ORIG_DLL_HANDLE}; +use std::ffi::CString; +use winapi::{ + shared::minwindef::{FARPROC, HMODULE}, + um::libloaderapi::GetProcAddress, +}; + +/// Loads up the address of the original function in the given module +unsafe fn load_dll_func(index: usize, h_module: HMODULE, func: &str) { + let func_c_string = CString::new(func).unwrap(); + let proc_address: FARPROC = GetProcAddress(h_module, func_c_string.as_ptr()); + ORIGINAL_FUNCS[index] = proc_address; + println!("[0x{:016x}] Loaded {}", proc_address as u64, func); +} + +/// Loads the original DLL functions for later use +pub unsafe fn load_dll_funcs() { + println!("Loading original DLL functions"); + if ORIG_DLL_HANDLE.is_none() { + eprintln!("Original DLL handle is none. Cannot load original DLL funcs"); + return; + } + let dll_handle = ORIG_DLL_HANDLE.unwrap(); + load_dll_func(Index_CloseDriver, dll_handle, "CloseDriver"); + load_dll_func(Index_DefDriverProc, dll_handle, "DefDriverProc"); + load_dll_func(Index_DllCanUnloadNow, dll_handle, "DllCanUnloadNow"); + load_dll_func(Index_DllGetClassObject, dll_handle, "DllGetClassObject"); + load_dll_func(Index_DllRegisterServer, dll_handle, "DllRegisterServer"); + load_dll_func(Index_DllUnregisterServer, dll_handle, "DllUnregisterServer"); + load_dll_func(Index_DriverCallback, dll_handle, "DriverCallback"); + load_dll_func(Index_DrvClose, dll_handle, "DrvClose"); + load_dll_func(Index_DrvDefDriverProc, dll_handle, "DrvDefDriverProc"); + load_dll_func(Index_DrvGetModuleHandle, dll_handle, "DrvGetModuleHandle"); + load_dll_func(Index_DrvOpen, dll_handle, "DrvOpen"); + load_dll_func(Index_DrvOpenA, dll_handle, "DrvOpenA"); + load_dll_func(Index_DrvSendMessage, dll_handle, "DrvSendMessage"); + load_dll_func(Index_GetDriverFlags, dll_handle, "GetDriverFlags"); + load_dll_func(Index_GetDriverModuleHandle, dll_handle, "GetDriverModuleHandle"); + load_dll_func(Index_GetFileVersionInfoA, dll_handle, "GetFileVersionInfoA"); + load_dll_func(Index_GetFileVersionInfoExA, dll_handle, "GetFileVersionInfoExA"); + load_dll_func(Index_GetFileVersionInfoExW, dll_handle, "GetFileVersionInfoExW"); + load_dll_func(Index_GetFileVersionInfoSizeA, dll_handle, "GetFileVersionInfoSizeA"); + load_dll_func(Index_GetFileVersionInfoSizeExA, dll_handle, "GetFileVersionInfoSizeExA"); + load_dll_func(Index_GetFileVersionInfoSizeExW, dll_handle, "GetFileVersionInfoSizeExW"); + load_dll_func(Index_GetFileVersionInfoSizeW, dll_handle, "GetFileVersionInfoSizeW"); + load_dll_func(Index_GetFileVersionInfoW, dll_handle, "GetFileVersionInfoW"); + load_dll_func(Index_OpenDriver, dll_handle, "OpenDriver"); + load_dll_func(Index_OpenDriverA, dll_handle, "OpenDriverA"); + load_dll_func(Index_PlaySound, dll_handle, "PlaySound"); + load_dll_func(Index_PlaySoundA, dll_handle, "PlaySoundA"); + load_dll_func(Index_PlaySoundW, dll_handle, "PlaySoundW"); + load_dll_func(Index_SendDriverMessage, dll_handle, "SendDriverMessage"); + load_dll_func(Index_VerFindFileA, dll_handle, "VerFindFileA"); + load_dll_func(Index_VerFindFileW, dll_handle, "VerFindFileW"); + load_dll_func(Index_VerInstallFileA, dll_handle, "VerInstallFileA"); + load_dll_func(Index_VerInstallFileW, dll_handle, "VerInstallFileW"); + load_dll_func(Index_VerLanguageNameA, dll_handle, "VerLanguageNameA"); + load_dll_func(Index_VerLanguageNameW, dll_handle, "VerLanguageNameW"); + load_dll_func(Index_VerQueryValueA, dll_handle, "VerQueryValueA"); + load_dll_func(Index_VerQueryValueW, dll_handle, "VerQueryValueW"); + load_dll_func(Index_WinHttpAddRequestHeaders, dll_handle, "WinHttpAddRequestHeaders"); + load_dll_func(Index_WinHttpCheckPlatform, dll_handle, "WinHttpCheckPlatform"); + load_dll_func(Index_WinHttpCloseHandle, dll_handle, "WinHttpCloseHandle"); + load_dll_func(Index_WinHttpConnect, dll_handle, "WinHttpConnect"); + load_dll_func(Index_WinHttpCrackUrl, dll_handle, "WinHttpCrackUrl"); + load_dll_func(Index_WinHttpCreateProxyResolver, dll_handle, "WinHttpCreateProxyResolver"); + load_dll_func(Index_WinHttpCreateUrl, dll_handle, "WinHttpCreateUrl"); + load_dll_func(Index_WinHttpDetectAutoProxyConfigUrl, dll_handle, "WinHttpDetectAutoProxyConfigUrl"); + load_dll_func(Index_WinHttpFreeProxyResult, dll_handle, "WinHttpFreeProxyResult"); + load_dll_func(Index_WinHttpFreeProxyResultEx, dll_handle, "WinHttpFreeProxyResultEx"); + load_dll_func(Index_WinHttpFreeProxySettings, dll_handle, "WinHttpFreeProxySettings"); + load_dll_func(Index_WinHttpGetDefaultProxyConfiguration, dll_handle, "WinHttpGetDefaultProxyConfiguration"); + load_dll_func(Index_WinHttpGetIEProxyConfigForCurrentUser, dll_handle, "WinHttpGetIEProxyConfigForCurrentUser"); + load_dll_func(Index_WinHttpGetProxyForUrl, dll_handle, "WinHttpGetProxyForUrl"); + load_dll_func(Index_WinHttpGetProxyForUrlEx, dll_handle, "WinHttpGetProxyForUrlEx"); + load_dll_func(Index_WinHttpGetProxyForUrlEx2, dll_handle, "WinHttpGetProxyForUrlEx2"); + load_dll_func(Index_WinHttpGetProxyResult, dll_handle, "WinHttpGetProxyResult"); + load_dll_func(Index_WinHttpGetProxyResultEx, dll_handle, "WinHttpGetProxyResultEx"); + load_dll_func(Index_WinHttpGetProxySettingsVersion, dll_handle, "WinHttpGetProxySettingsVersion"); + load_dll_func(Index_WinHttpOpen, dll_handle, "WinHttpOpen"); + load_dll_func(Index_WinHttpOpenRequest, dll_handle, "WinHttpOpenRequest"); + load_dll_func(Index_WinHttpQueryAuthSchemes, dll_handle, "WinHttpQueryAuthSchemes"); + load_dll_func(Index_WinHttpQueryDataAvailable, dll_handle, "WinHttpQueryDataAvailable"); + load_dll_func(Index_WinHttpQueryHeaders, dll_handle, "WinHttpQueryHeaders"); + load_dll_func(Index_WinHttpQueryOption, dll_handle, "WinHttpQueryOption"); + load_dll_func(Index_WinHttpReadData, dll_handle, "WinHttpReadData"); + load_dll_func(Index_WinHttpReadProxySettings, dll_handle, "WinHttpReadProxySettings"); + load_dll_func(Index_WinHttpReceiveResponse, dll_handle, "WinHttpReceiveResponse"); + load_dll_func(Index_WinHttpResetAutoProxy, dll_handle, "WinHttpResetAutoProxy"); + load_dll_func(Index_WinHttpSendRequest, dll_handle, "WinHttpSendRequest"); + load_dll_func(Index_WinHttpSetCredentials, dll_handle, "WinHttpSetCredentials"); + load_dll_func(Index_WinHttpSetDefaultProxyConfiguration, dll_handle, "WinHttpSetDefaultProxyConfiguration"); + load_dll_func(Index_WinHttpSetOption, dll_handle, "WinHttpSetOption"); + load_dll_func(Index_WinHttpSetStatusCallback, dll_handle, "WinHttpSetStatusCallback"); + load_dll_func(Index_WinHttpSetTimeouts, dll_handle, "WinHttpSetTimeouts"); + load_dll_func(Index_WinHttpTimeFromSystemTime, dll_handle, "WinHttpTimeFromSystemTime"); + load_dll_func(Index_WinHttpTimeToSystemTime, dll_handle, "WinHttpTimeToSystemTime"); + load_dll_func(Index_WinHttpWebSocketClose, dll_handle, "WinHttpWebSocketClose"); + load_dll_func(Index_WinHttpWebSocketCompleteUpgrade, dll_handle, "WinHttpWebSocketCompleteUpgrade"); + load_dll_func(Index_WinHttpWebSocketQueryCloseStatus, dll_handle, "WinHttpWebSocketQueryCloseStatus"); + load_dll_func(Index_WinHttpWebSocketReceive, dll_handle, "WinHttpWebSocketReceive"); + load_dll_func(Index_WinHttpWebSocketSend, dll_handle, "WinHttpWebSocketSend"); + load_dll_func(Index_WinHttpWebSocketShutdown, dll_handle, "WinHttpWebSocketShutdown"); + load_dll_func(Index_WinHttpWriteData, dll_handle, "WinHttpWriteData"); + load_dll_func(Index_WinHttpWriteProxySettings, dll_handle, "WinHttpWriteProxySettings"); + load_dll_func(Index_auxGetDevCapsA, dll_handle, "auxGetDevCapsA"); + load_dll_func(Index_auxGetDevCapsW, dll_handle, "auxGetDevCapsW"); + load_dll_func(Index_auxGetNumDevs, dll_handle, "auxGetNumDevs"); + load_dll_func(Index_auxGetVolume, dll_handle, "auxGetVolume"); + load_dll_func(Index_auxOutMessage, dll_handle, "auxOutMessage"); + load_dll_func(Index_auxSetVolume, dll_handle, "auxSetVolume"); + load_dll_func(Index_joyConfigChanged, dll_handle, "joyConfigChanged"); + load_dll_func(Index_joyGetDevCapsA, dll_handle, "joyGetDevCapsA"); + load_dll_func(Index_joyGetDevCapsW, dll_handle, "joyGetDevCapsW"); + load_dll_func(Index_joyGetNumDevs, dll_handle, "joyGetNumDevs"); + load_dll_func(Index_joyGetPos, dll_handle, "joyGetPos"); + load_dll_func(Index_joyGetPosEx, dll_handle, "joyGetPosEx"); + load_dll_func(Index_joyGetThreshold, dll_handle, "joyGetThreshold"); + load_dll_func(Index_joyReleaseCapture, dll_handle, "joyReleaseCapture"); + load_dll_func(Index_joySetCapture, dll_handle, "joySetCapture"); + load_dll_func(Index_joySetThreshold, dll_handle, "joySetThreshold"); + load_dll_func(Index_mciDriverNotify, dll_handle, "mciDriverNotify"); + load_dll_func(Index_mciDriverYield, dll_handle, "mciDriverYield"); + load_dll_func(Index_mciExecute, dll_handle, "mciExecute"); + load_dll_func(Index_mciFreeCommandResource, dll_handle, "mciFreeCommandResource"); + load_dll_func(Index_mciGetCreatorTask, dll_handle, "mciGetCreatorTask"); + load_dll_func(Index_mciGetDeviceIDA, dll_handle, "mciGetDeviceIDA"); + load_dll_func(Index_mciGetDeviceIDFromElementIDA, dll_handle, "mciGetDeviceIDFromElementIDA"); + load_dll_func(Index_mciGetDeviceIDFromElementIDW, dll_handle, "mciGetDeviceIDFromElementIDW"); + load_dll_func(Index_mciGetDeviceIDW, dll_handle, "mciGetDeviceIDW"); + load_dll_func(Index_mciGetDriverData, dll_handle, "mciGetDriverData"); + load_dll_func(Index_mciGetErrorStringA, dll_handle, "mciGetErrorStringA"); + load_dll_func(Index_mciGetErrorStringW, dll_handle, "mciGetErrorStringW"); + load_dll_func(Index_mciGetYieldProc, dll_handle, "mciGetYieldProc"); + load_dll_func(Index_mciLoadCommandResource, dll_handle, "mciLoadCommandResource"); + load_dll_func(Index_mciSendCommandA, dll_handle, "mciSendCommandA"); + load_dll_func(Index_mciSendCommandW, dll_handle, "mciSendCommandW"); + load_dll_func(Index_mciSendStringA, dll_handle, "mciSendStringA"); + load_dll_func(Index_mciSendStringW, dll_handle, "mciSendStringW"); + load_dll_func(Index_mciSetDriverData, dll_handle, "mciSetDriverData"); + load_dll_func(Index_mciSetYieldProc, dll_handle, "mciSetYieldProc"); + load_dll_func(Index_midiConnect, dll_handle, "midiConnect"); + load_dll_func(Index_midiDisconnect, dll_handle, "midiDisconnect"); + load_dll_func(Index_midiInAddBuffer, dll_handle, "midiInAddBuffer"); + load_dll_func(Index_midiInClose, dll_handle, "midiInClose"); + load_dll_func(Index_midiInGetDevCapsA, dll_handle, "midiInGetDevCapsA"); + load_dll_func(Index_midiInGetDevCapsW, dll_handle, "midiInGetDevCapsW"); + load_dll_func(Index_midiInGetErrorTextA, dll_handle, "midiInGetErrorTextA"); + load_dll_func(Index_midiInGetErrorTextW, dll_handle, "midiInGetErrorTextW"); + load_dll_func(Index_midiInGetID, dll_handle, "midiInGetID"); + load_dll_func(Index_midiInGetNumDevs, dll_handle, "midiInGetNumDevs"); + load_dll_func(Index_midiInMessage, dll_handle, "midiInMessage"); + load_dll_func(Index_midiInOpen, dll_handle, "midiInOpen"); + load_dll_func(Index_midiInPrepareHeader, dll_handle, "midiInPrepareHeader"); + load_dll_func(Index_midiInReset, dll_handle, "midiInReset"); + load_dll_func(Index_midiInStart, dll_handle, "midiInStart"); + load_dll_func(Index_midiInStop, dll_handle, "midiInStop"); + load_dll_func(Index_midiInUnprepareHeader, dll_handle, "midiInUnprepareHeader"); + load_dll_func(Index_midiOutCacheDrumPatches, dll_handle, "midiOutCacheDrumPatches"); + load_dll_func(Index_midiOutCachePatches, dll_handle, "midiOutCachePatches"); + load_dll_func(Index_midiOutClose, dll_handle, "midiOutClose"); + load_dll_func(Index_midiOutGetDevCapsA, dll_handle, "midiOutGetDevCapsA"); + load_dll_func(Index_midiOutGetDevCapsW, dll_handle, "midiOutGetDevCapsW"); + load_dll_func(Index_midiOutGetErrorTextA, dll_handle, "midiOutGetErrorTextA"); + load_dll_func(Index_midiOutGetErrorTextW, dll_handle, "midiOutGetErrorTextW"); + load_dll_func(Index_midiOutGetID, dll_handle, "midiOutGetID"); + load_dll_func(Index_midiOutGetNumDevs, dll_handle, "midiOutGetNumDevs"); + load_dll_func(Index_midiOutGetVolume, dll_handle, "midiOutGetVolume"); + load_dll_func(Index_midiOutLongMsg, dll_handle, "midiOutLongMsg"); + load_dll_func(Index_midiOutMessage, dll_handle, "midiOutMessage"); + load_dll_func(Index_midiOutOpen, dll_handle, "midiOutOpen"); + load_dll_func(Index_midiOutPrepareHeader, dll_handle, "midiOutPrepareHeader"); + load_dll_func(Index_midiOutReset, dll_handle, "midiOutReset"); + load_dll_func(Index_midiOutSetVolume, dll_handle, "midiOutSetVolume"); + load_dll_func(Index_midiOutShortMsg, dll_handle, "midiOutShortMsg"); + load_dll_func(Index_midiOutUnprepareHeader, dll_handle, "midiOutUnprepareHeader"); + load_dll_func(Index_midiStreamClose, dll_handle, "midiStreamClose"); + load_dll_func(Index_midiStreamOpen, dll_handle, "midiStreamOpen"); + load_dll_func(Index_midiStreamOut, dll_handle, "midiStreamOut"); + load_dll_func(Index_midiStreamPause, dll_handle, "midiStreamPause"); + load_dll_func(Index_midiStreamPosition, dll_handle, "midiStreamPosition"); + load_dll_func(Index_midiStreamProperty, dll_handle, "midiStreamProperty"); + load_dll_func(Index_midiStreamRestart, dll_handle, "midiStreamRestart"); + load_dll_func(Index_midiStreamStop, dll_handle, "midiStreamStop"); + load_dll_func(Index_mixerClose, dll_handle, "mixerClose"); + load_dll_func(Index_mixerGetControlDetailsA, dll_handle, "mixerGetControlDetailsA"); + load_dll_func(Index_mixerGetControlDetailsW, dll_handle, "mixerGetControlDetailsW"); + load_dll_func(Index_mixerGetDevCapsA, dll_handle, "mixerGetDevCapsA"); + load_dll_func(Index_mixerGetDevCapsW, dll_handle, "mixerGetDevCapsW"); + load_dll_func(Index_mixerGetID, dll_handle, "mixerGetID"); + load_dll_func(Index_mixerGetLineControlsA, dll_handle, "mixerGetLineControlsA"); + load_dll_func(Index_mixerGetLineControlsW, dll_handle, "mixerGetLineControlsW"); + load_dll_func(Index_mixerGetLineInfoA, dll_handle, "mixerGetLineInfoA"); + load_dll_func(Index_mixerGetLineInfoW, dll_handle, "mixerGetLineInfoW"); + load_dll_func(Index_mixerGetNumDevs, dll_handle, "mixerGetNumDevs"); + load_dll_func(Index_mixerMessage, dll_handle, "mixerMessage"); + load_dll_func(Index_mixerOpen, dll_handle, "mixerOpen"); + load_dll_func(Index_mixerSetControlDetails, dll_handle, "mixerSetControlDetails"); + load_dll_func(Index_mmGetCurrentTask, dll_handle, "mmGetCurrentTask"); + load_dll_func(Index_mmTaskBlock, dll_handle, "mmTaskBlock"); + load_dll_func(Index_mmTaskCreate, dll_handle, "mmTaskCreate"); + load_dll_func(Index_mmTaskSignal, dll_handle, "mmTaskSignal"); + load_dll_func(Index_mmTaskYield, dll_handle, "mmTaskYield"); + load_dll_func(Index_mmioAdvance, dll_handle, "mmioAdvance"); + load_dll_func(Index_mmioAscend, dll_handle, "mmioAscend"); + load_dll_func(Index_mmioClose, dll_handle, "mmioClose"); + load_dll_func(Index_mmioCreateChunk, dll_handle, "mmioCreateChunk"); + load_dll_func(Index_mmioDescend, dll_handle, "mmioDescend"); + load_dll_func(Index_mmioFlush, dll_handle, "mmioFlush"); + load_dll_func(Index_mmioGetInfo, dll_handle, "mmioGetInfo"); + load_dll_func(Index_mmioInstallIOProc16, dll_handle, "mmioInstallIOProc16"); + load_dll_func(Index_mmioInstallIOProcA, dll_handle, "mmioInstallIOProcA"); + load_dll_func(Index_mmioInstallIOProcW, dll_handle, "mmioInstallIOProcW"); + load_dll_func(Index_mmioOpenA, dll_handle, "mmioOpenA"); + load_dll_func(Index_mmioOpenW, dll_handle, "mmioOpenW"); + load_dll_func(Index_mmioRead, dll_handle, "mmioRead"); + load_dll_func(Index_mmioRenameA, dll_handle, "mmioRenameA"); + load_dll_func(Index_mmioRenameW, dll_handle, "mmioRenameW"); + load_dll_func(Index_mmioSeek, dll_handle, "mmioSeek"); + load_dll_func(Index_mmioSendMessage, dll_handle, "mmioSendMessage"); + load_dll_func(Index_mmioSetBuffer, dll_handle, "mmioSetBuffer"); + load_dll_func(Index_mmioSetInfo, dll_handle, "mmioSetInfo"); + load_dll_func(Index_mmioStringToFOURCCA, dll_handle, "mmioStringToFOURCCA"); + load_dll_func(Index_mmioStringToFOURCCW, dll_handle, "mmioStringToFOURCCW"); + load_dll_func(Index_mmioWrite, dll_handle, "mmioWrite"); + load_dll_func(Index_mmsystemGetVersion, dll_handle, "mmsystemGetVersion"); + load_dll_func(Index_sndPlaySoundA, dll_handle, "sndPlaySoundA"); + load_dll_func(Index_sndPlaySoundW, dll_handle, "sndPlaySoundW"); + load_dll_func(Index_timeBeginPeriod, dll_handle, "timeBeginPeriod"); + load_dll_func(Index_timeEndPeriod, dll_handle, "timeEndPeriod"); + load_dll_func(Index_timeGetDevCaps, dll_handle, "timeGetDevCaps"); + load_dll_func(Index_timeGetSystemTime, dll_handle, "timeGetSystemTime"); + load_dll_func(Index_timeGetTime, dll_handle, "timeGetTime"); + load_dll_func(Index_timeKillEvent, dll_handle, "timeKillEvent"); + load_dll_func(Index_timeSetEvent, dll_handle, "timeSetEvent"); + load_dll_func(Index_waveInAddBuffer, dll_handle, "waveInAddBuffer"); + load_dll_func(Index_waveInClose, dll_handle, "waveInClose"); + load_dll_func(Index_waveInGetDevCapsA, dll_handle, "waveInGetDevCapsA"); + load_dll_func(Index_waveInGetDevCapsW, dll_handle, "waveInGetDevCapsW"); + load_dll_func(Index_waveInGetErrorTextA, dll_handle, "waveInGetErrorTextA"); + load_dll_func(Index_waveInGetErrorTextW, dll_handle, "waveInGetErrorTextW"); + load_dll_func(Index_waveInGetID, dll_handle, "waveInGetID"); + load_dll_func(Index_waveInGetNumDevs, dll_handle, "waveInGetNumDevs"); + load_dll_func(Index_waveInGetPosition, dll_handle, "waveInGetPosition"); + load_dll_func(Index_waveInMessage, dll_handle, "waveInMessage"); + load_dll_func(Index_waveInOpen, dll_handle, "waveInOpen"); + load_dll_func(Index_waveInPrepareHeader, dll_handle, "waveInPrepareHeader"); + load_dll_func(Index_waveInReset, dll_handle, "waveInReset"); + load_dll_func(Index_waveInStart, dll_handle, "waveInStart"); + load_dll_func(Index_waveInStop, dll_handle, "waveInStop"); + load_dll_func(Index_waveInUnprepareHeader, dll_handle, "waveInUnprepareHeader"); + load_dll_func(Index_waveOutBreakLoop, dll_handle, "waveOutBreakLoop"); + load_dll_func(Index_waveOutClose, dll_handle, "waveOutClose"); + load_dll_func(Index_waveOutGetDevCapsA, dll_handle, "waveOutGetDevCapsA"); + load_dll_func(Index_waveOutGetDevCapsW, dll_handle, "waveOutGetDevCapsW"); + load_dll_func(Index_waveOutGetErrorTextA, dll_handle, "waveOutGetErrorTextA"); + load_dll_func(Index_waveOutGetErrorTextW, dll_handle, "waveOutGetErrorTextW"); + load_dll_func(Index_waveOutGetID, dll_handle, "waveOutGetID"); + load_dll_func(Index_waveOutGetNumDevs, dll_handle, "waveOutGetNumDevs"); + load_dll_func(Index_waveOutGetPitch, dll_handle, "waveOutGetPitch"); + load_dll_func(Index_waveOutGetPlaybackRate, dll_handle, "waveOutGetPlaybackRate"); + load_dll_func(Index_waveOutGetPosition, dll_handle, "waveOutGetPosition"); + load_dll_func(Index_waveOutGetVolume, dll_handle, "waveOutGetVolume"); + load_dll_func(Index_waveOutMessage, dll_handle, "waveOutMessage"); + load_dll_func(Index_waveOutOpen, dll_handle, "waveOutOpen"); + load_dll_func(Index_waveOutPause, dll_handle, "waveOutPause"); + load_dll_func(Index_waveOutPrepareHeader, dll_handle, "waveOutPrepareHeader"); + load_dll_func(Index_waveOutReset, dll_handle, "waveOutReset"); + load_dll_func(Index_waveOutRestart, dll_handle, "waveOutRestart"); + load_dll_func(Index_waveOutSetPitch, dll_handle, "waveOutSetPitch"); + load_dll_func(Index_waveOutSetPlaybackRate, dll_handle, "waveOutSetPlaybackRate"); + load_dll_func(Index_waveOutSetVolume, dll_handle, "waveOutSetVolume"); + load_dll_func(Index_waveOutUnprepareHeader, dll_handle, "waveOutUnprepareHeader"); + load_dll_func(Index_waveOutWrite, dll_handle, "waveOutWrite"); +} diff --git a/MelonProxy/src/proxied_exports.rs b/MelonProxy/src/proxied_exports.rs new file mode 100644 index 000000000..707343232 --- /dev/null +++ b/MelonProxy/src/proxied_exports.rs @@ -0,0 +1,1007 @@ +// Just proxied functions in this file +use proxygen_macros::forward; + +#[forward] +#[export_name="CloseDriver"] +pub extern "C" fn CloseDriver() {} + +#[forward] +#[export_name="DefDriverProc"] +pub extern "C" fn DefDriverProc() {} + +#[forward] +#[export_name="DllCanUnloadNow"] +pub extern "C" fn DllCanUnloadNow() {} + +#[forward] +#[export_name="DllGetClassObject"] +pub extern "C" fn DllGetClassObject() {} + +#[forward] +#[export_name="DllRegisterServer"] +pub extern "C" fn DllRegisterServer() {} + +#[forward] +#[export_name="DllUnregisterServer"] +pub extern "C" fn DllUnregisterServer() {} + +#[forward] +#[export_name="DriverCallback"] +pub extern "C" fn DriverCallback() {} + +#[forward] +#[export_name="DrvClose"] +pub extern "C" fn DrvClose() {} + +#[forward] +#[export_name="DrvDefDriverProc"] +pub extern "C" fn DrvDefDriverProc() {} + +#[forward] +#[export_name="DrvGetModuleHandle"] +pub extern "C" fn DrvGetModuleHandle() {} + +#[forward] +#[export_name="DrvOpen"] +pub extern "C" fn DrvOpen() {} + +#[forward] +#[export_name="DrvOpenA"] +pub extern "C" fn DrvOpenA() {} + +#[forward] +#[export_name="DrvSendMessage"] +pub extern "C" fn DrvSendMessage() {} + +#[forward] +#[export_name="GetDriverFlags"] +pub extern "C" fn GetDriverFlags() {} + +#[forward] +#[export_name="GetDriverModuleHandle"] +pub extern "C" fn GetDriverModuleHandle() {} + +#[forward] +#[export_name="GetFileVersionInfoA"] +pub extern "C" fn GetFileVersionInfoA() {} + +#[forward] +#[export_name="GetFileVersionInfoExA"] +pub extern "C" fn GetFileVersionInfoExA() {} + +#[forward] +#[export_name="GetFileVersionInfoExW"] +pub extern "C" fn GetFileVersionInfoExW() {} + +#[forward] +#[export_name="GetFileVersionInfoSizeA"] +pub extern "C" fn GetFileVersionInfoSizeA() {} + +#[forward] +#[export_name="GetFileVersionInfoSizeExA"] +pub extern "C" fn GetFileVersionInfoSizeExA() {} + +#[forward] +#[export_name="GetFileVersionInfoSizeExW"] +pub extern "C" fn GetFileVersionInfoSizeExW() {} + +#[forward] +#[export_name="GetFileVersionInfoSizeW"] +pub extern "C" fn GetFileVersionInfoSizeW() {} + +#[forward] +#[export_name="GetFileVersionInfoW"] +pub extern "C" fn GetFileVersionInfoW() {} + +#[forward] +#[export_name="OpenDriver"] +pub extern "C" fn OpenDriver() {} + +#[forward] +#[export_name="OpenDriverA"] +pub extern "C" fn OpenDriverA() {} + +#[forward] +#[export_name="PlaySound"] +pub extern "C" fn PlaySound() {} + +#[forward] +#[export_name="PlaySoundA"] +pub extern "C" fn PlaySoundA() {} + +#[forward] +#[export_name="PlaySoundW"] +pub extern "C" fn PlaySoundW() {} + +#[forward] +#[export_name="SendDriverMessage"] +pub extern "C" fn SendDriverMessage() {} + +#[forward] +#[export_name="VerFindFileA"] +pub extern "C" fn VerFindFileA() {} + +#[forward] +#[export_name="VerFindFileW"] +pub extern "C" fn VerFindFileW() {} + +#[forward] +#[export_name="VerInstallFileA"] +pub extern "C" fn VerInstallFileA() {} + +#[forward] +#[export_name="VerInstallFileW"] +pub extern "C" fn VerInstallFileW() {} + +#[forward] +#[export_name="VerLanguageNameA"] +pub extern "C" fn VerLanguageNameA() {} + +#[forward] +#[export_name="VerLanguageNameW"] +pub extern "C" fn VerLanguageNameW() {} + +#[forward] +#[export_name="VerQueryValueA"] +pub extern "C" fn VerQueryValueA() {} + +#[forward] +#[export_name="VerQueryValueW"] +pub extern "C" fn VerQueryValueW() {} + +#[forward] +#[export_name="WinHttpAddRequestHeaders"] +pub extern "C" fn WinHttpAddRequestHeaders() {} + +#[forward] +#[export_name="WinHttpCheckPlatform"] +pub extern "C" fn WinHttpCheckPlatform() {} + +#[forward] +#[export_name="WinHttpCloseHandle"] +pub extern "C" fn WinHttpCloseHandle() {} + +#[forward] +#[export_name="WinHttpConnect"] +pub extern "C" fn WinHttpConnect() {} + +#[forward] +#[export_name="WinHttpCrackUrl"] +pub extern "C" fn WinHttpCrackUrl() {} + +#[forward] +#[export_name="WinHttpCreateProxyResolver"] +pub extern "C" fn WinHttpCreateProxyResolver() {} + +#[forward] +#[export_name="WinHttpCreateUrl"] +pub extern "C" fn WinHttpCreateUrl() {} + +#[forward] +#[export_name="WinHttpDetectAutoProxyConfigUrl"] +pub extern "C" fn WinHttpDetectAutoProxyConfigUrl() {} + +#[forward] +#[export_name="WinHttpFreeProxyResult"] +pub extern "C" fn WinHttpFreeProxyResult() {} + +#[forward] +#[export_name="WinHttpFreeProxyResultEx"] +pub extern "C" fn WinHttpFreeProxyResultEx() {} + +#[forward] +#[export_name="WinHttpFreeProxySettings"] +pub extern "C" fn WinHttpFreeProxySettings() {} + +#[forward] +#[export_name="WinHttpGetDefaultProxyConfiguration"] +pub extern "C" fn WinHttpGetDefaultProxyConfiguration() {} + +#[forward] +#[export_name="WinHttpGetIEProxyConfigForCurrentUser"] +pub extern "C" fn WinHttpGetIEProxyConfigForCurrentUser() {} + +#[forward] +#[export_name="WinHttpGetProxyForUrl"] +pub extern "C" fn WinHttpGetProxyForUrl() {} + +#[forward] +#[export_name="WinHttpGetProxyForUrlEx"] +pub extern "C" fn WinHttpGetProxyForUrlEx() {} + +#[forward] +#[export_name="WinHttpGetProxyForUrlEx2"] +pub extern "C" fn WinHttpGetProxyForUrlEx2() {} + +#[forward] +#[export_name="WinHttpGetProxyResult"] +pub extern "C" fn WinHttpGetProxyResult() {} + +#[forward] +#[export_name="WinHttpGetProxyResultEx"] +pub extern "C" fn WinHttpGetProxyResultEx() {} + +#[forward] +#[export_name="WinHttpGetProxySettingsVersion"] +pub extern "C" fn WinHttpGetProxySettingsVersion() {} + +#[forward] +#[export_name="WinHttpOpen"] +pub extern "C" fn WinHttpOpen() {} + +#[forward] +#[export_name="WinHttpOpenRequest"] +pub extern "C" fn WinHttpOpenRequest() {} + +#[forward] +#[export_name="WinHttpQueryAuthSchemes"] +pub extern "C" fn WinHttpQueryAuthSchemes() {} + +#[forward] +#[export_name="WinHttpQueryDataAvailable"] +pub extern "C" fn WinHttpQueryDataAvailable() {} + +#[forward] +#[export_name="WinHttpQueryHeaders"] +pub extern "C" fn WinHttpQueryHeaders() {} + +#[forward] +#[export_name="WinHttpQueryOption"] +pub extern "C" fn WinHttpQueryOption() {} + +#[forward] +#[export_name="WinHttpReadData"] +pub extern "C" fn WinHttpReadData() {} + +#[forward] +#[export_name="WinHttpReadProxySettings"] +pub extern "C" fn WinHttpReadProxySettings() {} + +#[forward] +#[export_name="WinHttpReceiveResponse"] +pub extern "C" fn WinHttpReceiveResponse() {} + +#[forward] +#[export_name="WinHttpResetAutoProxy"] +pub extern "C" fn WinHttpResetAutoProxy() {} + +#[forward] +#[export_name="WinHttpSendRequest"] +pub extern "C" fn WinHttpSendRequest() {} + +#[forward] +#[export_name="WinHttpSetCredentials"] +pub extern "C" fn WinHttpSetCredentials() {} + +#[forward] +#[export_name="WinHttpSetDefaultProxyConfiguration"] +pub extern "C" fn WinHttpSetDefaultProxyConfiguration() {} + +#[forward] +#[export_name="WinHttpSetOption"] +pub extern "C" fn WinHttpSetOption() {} + +#[forward] +#[export_name="WinHttpSetStatusCallback"] +pub extern "C" fn WinHttpSetStatusCallback() {} + +#[forward] +#[export_name="WinHttpSetTimeouts"] +pub extern "C" fn WinHttpSetTimeouts() {} + +#[forward] +#[export_name="WinHttpTimeFromSystemTime"] +pub extern "C" fn WinHttpTimeFromSystemTime() {} + +#[forward] +#[export_name="WinHttpTimeToSystemTime"] +pub extern "C" fn WinHttpTimeToSystemTime() {} + +#[forward] +#[export_name="WinHttpWebSocketClose"] +pub extern "C" fn WinHttpWebSocketClose() {} + +#[forward] +#[export_name="WinHttpWebSocketCompleteUpgrade"] +pub extern "C" fn WinHttpWebSocketCompleteUpgrade() {} + +#[forward] +#[export_name="WinHttpWebSocketQueryCloseStatus"] +pub extern "C" fn WinHttpWebSocketQueryCloseStatus() {} + +#[forward] +#[export_name="WinHttpWebSocketReceive"] +pub extern "C" fn WinHttpWebSocketReceive() {} + +#[forward] +#[export_name="WinHttpWebSocketSend"] +pub extern "C" fn WinHttpWebSocketSend() {} + +#[forward] +#[export_name="WinHttpWebSocketShutdown"] +pub extern "C" fn WinHttpWebSocketShutdown() {} + +#[forward] +#[export_name="WinHttpWriteData"] +pub extern "C" fn WinHttpWriteData() {} + +#[forward] +#[export_name="WinHttpWriteProxySettings"] +pub extern "C" fn WinHttpWriteProxySettings() {} + +#[forward] +#[export_name="auxGetDevCapsA"] +pub extern "C" fn auxGetDevCapsA() {} + +#[forward] +#[export_name="auxGetDevCapsW"] +pub extern "C" fn auxGetDevCapsW() {} + +#[forward] +#[export_name="auxGetNumDevs"] +pub extern "C" fn auxGetNumDevs() {} + +#[forward] +#[export_name="auxGetVolume"] +pub extern "C" fn auxGetVolume() {} + +#[forward] +#[export_name="auxOutMessage"] +pub extern "C" fn auxOutMessage() {} + +#[forward] +#[export_name="auxSetVolume"] +pub extern "C" fn auxSetVolume() {} + +#[forward] +#[export_name="joyConfigChanged"] +pub extern "C" fn joyConfigChanged() {} + +#[forward] +#[export_name="joyGetDevCapsA"] +pub extern "C" fn joyGetDevCapsA() {} + +#[forward] +#[export_name="joyGetDevCapsW"] +pub extern "C" fn joyGetDevCapsW() {} + +#[forward] +#[export_name="joyGetNumDevs"] +pub extern "C" fn joyGetNumDevs() {} + +#[forward] +#[export_name="joyGetPos"] +pub extern "C" fn joyGetPos() {} + +#[forward] +#[export_name="joyGetPosEx"] +pub extern "C" fn joyGetPosEx() {} + +#[forward] +#[export_name="joyGetThreshold"] +pub extern "C" fn joyGetThreshold() {} + +#[forward] +#[export_name="joyReleaseCapture"] +pub extern "C" fn joyReleaseCapture() {} + +#[forward] +#[export_name="joySetCapture"] +pub extern "C" fn joySetCapture() {} + +#[forward] +#[export_name="joySetThreshold"] +pub extern "C" fn joySetThreshold() {} + +#[forward] +#[export_name="mciDriverNotify"] +pub extern "C" fn mciDriverNotify() {} + +#[forward] +#[export_name="mciDriverYield"] +pub extern "C" fn mciDriverYield() {} + +#[forward] +#[export_name="mciExecute"] +pub extern "C" fn mciExecute() {} + +#[forward] +#[export_name="mciFreeCommandResource"] +pub extern "C" fn mciFreeCommandResource() {} + +#[forward] +#[export_name="mciGetCreatorTask"] +pub extern "C" fn mciGetCreatorTask() {} + +#[forward] +#[export_name="mciGetDeviceIDA"] +pub extern "C" fn mciGetDeviceIDA() {} + +#[forward] +#[export_name="mciGetDeviceIDFromElementIDA"] +pub extern "C" fn mciGetDeviceIDFromElementIDA() {} + +#[forward] +#[export_name="mciGetDeviceIDFromElementIDW"] +pub extern "C" fn mciGetDeviceIDFromElementIDW() {} + +#[forward] +#[export_name="mciGetDeviceIDW"] +pub extern "C" fn mciGetDeviceIDW() {} + +#[forward] +#[export_name="mciGetDriverData"] +pub extern "C" fn mciGetDriverData() {} + +#[forward] +#[export_name="mciGetErrorStringA"] +pub extern "C" fn mciGetErrorStringA() {} + +#[forward] +#[export_name="mciGetErrorStringW"] +pub extern "C" fn mciGetErrorStringW() {} + +#[forward] +#[export_name="mciGetYieldProc"] +pub extern "C" fn mciGetYieldProc() {} + +#[forward] +#[export_name="mciLoadCommandResource"] +pub extern "C" fn mciLoadCommandResource() {} + +#[forward] +#[export_name="mciSendCommandA"] +pub extern "C" fn mciSendCommandA() {} + +#[forward] +#[export_name="mciSendCommandW"] +pub extern "C" fn mciSendCommandW() {} + +#[forward] +#[export_name="mciSendStringA"] +pub extern "C" fn mciSendStringA() {} + +#[forward] +#[export_name="mciSendStringW"] +pub extern "C" fn mciSendStringW() {} + +#[forward] +#[export_name="mciSetDriverData"] +pub extern "C" fn mciSetDriverData() {} + +#[forward] +#[export_name="mciSetYieldProc"] +pub extern "C" fn mciSetYieldProc() {} + +#[forward] +#[export_name="midiConnect"] +pub extern "C" fn midiConnect() {} + +#[forward] +#[export_name="midiDisconnect"] +pub extern "C" fn midiDisconnect() {} + +#[forward] +#[export_name="midiInAddBuffer"] +pub extern "C" fn midiInAddBuffer() {} + +#[forward] +#[export_name="midiInClose"] +pub extern "C" fn midiInClose() {} + +#[forward] +#[export_name="midiInGetDevCapsA"] +pub extern "C" fn midiInGetDevCapsA() {} + +#[forward] +#[export_name="midiInGetDevCapsW"] +pub extern "C" fn midiInGetDevCapsW() {} + +#[forward] +#[export_name="midiInGetErrorTextA"] +pub extern "C" fn midiInGetErrorTextA() {} + +#[forward] +#[export_name="midiInGetErrorTextW"] +pub extern "C" fn midiInGetErrorTextW() {} + +#[forward] +#[export_name="midiInGetID"] +pub extern "C" fn midiInGetID() {} + +#[forward] +#[export_name="midiInGetNumDevs"] +pub extern "C" fn midiInGetNumDevs() {} + +#[forward] +#[export_name="midiInMessage"] +pub extern "C" fn midiInMessage() {} + +#[forward] +#[export_name="midiInOpen"] +pub extern "C" fn midiInOpen() {} + +#[forward] +#[export_name="midiInPrepareHeader"] +pub extern "C" fn midiInPrepareHeader() {} + +#[forward] +#[export_name="midiInReset"] +pub extern "C" fn midiInReset() {} + +#[forward] +#[export_name="midiInStart"] +pub extern "C" fn midiInStart() {} + +#[forward] +#[export_name="midiInStop"] +pub extern "C" fn midiInStop() {} + +#[forward] +#[export_name="midiInUnprepareHeader"] +pub extern "C" fn midiInUnprepareHeader() {} + +#[forward] +#[export_name="midiOutCacheDrumPatches"] +pub extern "C" fn midiOutCacheDrumPatches() {} + +#[forward] +#[export_name="midiOutCachePatches"] +pub extern "C" fn midiOutCachePatches() {} + +#[forward] +#[export_name="midiOutClose"] +pub extern "C" fn midiOutClose() {} + +#[forward] +#[export_name="midiOutGetDevCapsA"] +pub extern "C" fn midiOutGetDevCapsA() {} + +#[forward] +#[export_name="midiOutGetDevCapsW"] +pub extern "C" fn midiOutGetDevCapsW() {} + +#[forward] +#[export_name="midiOutGetErrorTextA"] +pub extern "C" fn midiOutGetErrorTextA() {} + +#[forward] +#[export_name="midiOutGetErrorTextW"] +pub extern "C" fn midiOutGetErrorTextW() {} + +#[forward] +#[export_name="midiOutGetID"] +pub extern "C" fn midiOutGetID() {} + +#[forward] +#[export_name="midiOutGetNumDevs"] +pub extern "C" fn midiOutGetNumDevs() {} + +#[forward] +#[export_name="midiOutGetVolume"] +pub extern "C" fn midiOutGetVolume() {} + +#[forward] +#[export_name="midiOutLongMsg"] +pub extern "C" fn midiOutLongMsg() {} + +#[forward] +#[export_name="midiOutMessage"] +pub extern "C" fn midiOutMessage() {} + +#[forward] +#[export_name="midiOutOpen"] +pub extern "C" fn midiOutOpen() {} + +#[forward] +#[export_name="midiOutPrepareHeader"] +pub extern "C" fn midiOutPrepareHeader() {} + +#[forward] +#[export_name="midiOutReset"] +pub extern "C" fn midiOutReset() {} + +#[forward] +#[export_name="midiOutSetVolume"] +pub extern "C" fn midiOutSetVolume() {} + +#[forward] +#[export_name="midiOutShortMsg"] +pub extern "C" fn midiOutShortMsg() {} + +#[forward] +#[export_name="midiOutUnprepareHeader"] +pub extern "C" fn midiOutUnprepareHeader() {} + +#[forward] +#[export_name="midiStreamClose"] +pub extern "C" fn midiStreamClose() {} + +#[forward] +#[export_name="midiStreamOpen"] +pub extern "C" fn midiStreamOpen() {} + +#[forward] +#[export_name="midiStreamOut"] +pub extern "C" fn midiStreamOut() {} + +#[forward] +#[export_name="midiStreamPause"] +pub extern "C" fn midiStreamPause() {} + +#[forward] +#[export_name="midiStreamPosition"] +pub extern "C" fn midiStreamPosition() {} + +#[forward] +#[export_name="midiStreamProperty"] +pub extern "C" fn midiStreamProperty() {} + +#[forward] +#[export_name="midiStreamRestart"] +pub extern "C" fn midiStreamRestart() {} + +#[forward] +#[export_name="midiStreamStop"] +pub extern "C" fn midiStreamStop() {} + +#[forward] +#[export_name="mixerClose"] +pub extern "C" fn mixerClose() {} + +#[forward] +#[export_name="mixerGetControlDetailsA"] +pub extern "C" fn mixerGetControlDetailsA() {} + +#[forward] +#[export_name="mixerGetControlDetailsW"] +pub extern "C" fn mixerGetControlDetailsW() {} + +#[forward] +#[export_name="mixerGetDevCapsA"] +pub extern "C" fn mixerGetDevCapsA() {} + +#[forward] +#[export_name="mixerGetDevCapsW"] +pub extern "C" fn mixerGetDevCapsW() {} + +#[forward] +#[export_name="mixerGetID"] +pub extern "C" fn mixerGetID() {} + +#[forward] +#[export_name="mixerGetLineControlsA"] +pub extern "C" fn mixerGetLineControlsA() {} + +#[forward] +#[export_name="mixerGetLineControlsW"] +pub extern "C" fn mixerGetLineControlsW() {} + +#[forward] +#[export_name="mixerGetLineInfoA"] +pub extern "C" fn mixerGetLineInfoA() {} + +#[forward] +#[export_name="mixerGetLineInfoW"] +pub extern "C" fn mixerGetLineInfoW() {} + +#[forward] +#[export_name="mixerGetNumDevs"] +pub extern "C" fn mixerGetNumDevs() {} + +#[forward] +#[export_name="mixerMessage"] +pub extern "C" fn mixerMessage() {} + +#[forward] +#[export_name="mixerOpen"] +pub extern "C" fn mixerOpen() {} + +#[forward] +#[export_name="mixerSetControlDetails"] +pub extern "C" fn mixerSetControlDetails() {} + +#[forward] +#[export_name="mmGetCurrentTask"] +pub extern "C" fn mmGetCurrentTask() {} + +#[forward] +#[export_name="mmTaskBlock"] +pub extern "C" fn mmTaskBlock() {} + +#[forward] +#[export_name="mmTaskCreate"] +pub extern "C" fn mmTaskCreate() {} + +#[forward] +#[export_name="mmTaskSignal"] +pub extern "C" fn mmTaskSignal() {} + +#[forward] +#[export_name="mmTaskYield"] +pub extern "C" fn mmTaskYield() {} + +#[forward] +#[export_name="mmioAdvance"] +pub extern "C" fn mmioAdvance() {} + +#[forward] +#[export_name="mmioAscend"] +pub extern "C" fn mmioAscend() {} + +#[forward] +#[export_name="mmioClose"] +pub extern "C" fn mmioClose() {} + +#[forward] +#[export_name="mmioCreateChunk"] +pub extern "C" fn mmioCreateChunk() {} + +#[forward] +#[export_name="mmioDescend"] +pub extern "C" fn mmioDescend() {} + +#[forward] +#[export_name="mmioFlush"] +pub extern "C" fn mmioFlush() {} + +#[forward] +#[export_name="mmioGetInfo"] +pub extern "C" fn mmioGetInfo() {} + +#[forward] +#[export_name="mmioInstallIOProc16"] +pub extern "C" fn mmioInstallIOProc16() {} + +#[forward] +#[export_name="mmioInstallIOProcA"] +pub extern "C" fn mmioInstallIOProcA() {} + +#[forward] +#[export_name="mmioInstallIOProcW"] +pub extern "C" fn mmioInstallIOProcW() {} + +#[forward] +#[export_name="mmioOpenA"] +pub extern "C" fn mmioOpenA() {} + +#[forward] +#[export_name="mmioOpenW"] +pub extern "C" fn mmioOpenW() {} + +#[forward] +#[export_name="mmioRead"] +pub extern "C" fn mmioRead() {} + +#[forward] +#[export_name="mmioRenameA"] +pub extern "C" fn mmioRenameA() {} + +#[forward] +#[export_name="mmioRenameW"] +pub extern "C" fn mmioRenameW() {} + +#[forward] +#[export_name="mmioSeek"] +pub extern "C" fn mmioSeek() {} + +#[forward] +#[export_name="mmioSendMessage"] +pub extern "C" fn mmioSendMessage() {} + +#[forward] +#[export_name="mmioSetBuffer"] +pub extern "C" fn mmioSetBuffer() {} + +#[forward] +#[export_name="mmioSetInfo"] +pub extern "C" fn mmioSetInfo() {} + +#[forward] +#[export_name="mmioStringToFOURCCA"] +pub extern "C" fn mmioStringToFOURCCA() {} + +#[forward] +#[export_name="mmioStringToFOURCCW"] +pub extern "C" fn mmioStringToFOURCCW() {} + +#[forward] +#[export_name="mmioWrite"] +pub extern "C" fn mmioWrite() {} + +#[forward] +#[export_name="mmsystemGetVersion"] +pub extern "C" fn mmsystemGetVersion() {} + +#[forward] +#[export_name="sndPlaySoundA"] +pub extern "C" fn sndPlaySoundA() {} + +#[forward] +#[export_name="sndPlaySoundW"] +pub extern "C" fn sndPlaySoundW() {} + +#[forward] +#[export_name="timeBeginPeriod"] +pub extern "C" fn timeBeginPeriod() {} + +#[forward] +#[export_name="timeEndPeriod"] +pub extern "C" fn timeEndPeriod() {} + +#[forward] +#[export_name="timeGetDevCaps"] +pub extern "C" fn timeGetDevCaps() {} + +#[forward] +#[export_name="timeGetSystemTime"] +pub extern "C" fn timeGetSystemTime() {} + +#[forward] +#[export_name="timeGetTime"] +pub extern "C" fn timeGetTime() {} + +#[forward] +#[export_name="timeKillEvent"] +pub extern "C" fn timeKillEvent() {} + +#[forward] +#[export_name="timeSetEvent"] +pub extern "C" fn timeSetEvent() {} + +#[forward] +#[export_name="waveInAddBuffer"] +pub extern "C" fn waveInAddBuffer() {} + +#[forward] +#[export_name="waveInClose"] +pub extern "C" fn waveInClose() {} + +#[forward] +#[export_name="waveInGetDevCapsA"] +pub extern "C" fn waveInGetDevCapsA() {} + +#[forward] +#[export_name="waveInGetDevCapsW"] +pub extern "C" fn waveInGetDevCapsW() {} + +#[forward] +#[export_name="waveInGetErrorTextA"] +pub extern "C" fn waveInGetErrorTextA() {} + +#[forward] +#[export_name="waveInGetErrorTextW"] +pub extern "C" fn waveInGetErrorTextW() {} + +#[forward] +#[export_name="waveInGetID"] +pub extern "C" fn waveInGetID() {} + +#[forward] +#[export_name="waveInGetNumDevs"] +pub extern "C" fn waveInGetNumDevs() {} + +#[forward] +#[export_name="waveInGetPosition"] +pub extern "C" fn waveInGetPosition() {} + +#[forward] +#[export_name="waveInMessage"] +pub extern "C" fn waveInMessage() {} + +#[forward] +#[export_name="waveInOpen"] +pub extern "C" fn waveInOpen() {} + +#[forward] +#[export_name="waveInPrepareHeader"] +pub extern "C" fn waveInPrepareHeader() {} + +#[forward] +#[export_name="waveInReset"] +pub extern "C" fn waveInReset() {} + +#[forward] +#[export_name="waveInStart"] +pub extern "C" fn waveInStart() {} + +#[forward] +#[export_name="waveInStop"] +pub extern "C" fn waveInStop() {} + +#[forward] +#[export_name="waveInUnprepareHeader"] +pub extern "C" fn waveInUnprepareHeader() {} + +#[forward] +#[export_name="waveOutBreakLoop"] +pub extern "C" fn waveOutBreakLoop() {} + +#[forward] +#[export_name="waveOutClose"] +pub extern "C" fn waveOutClose() {} + +#[forward] +#[export_name="waveOutGetDevCapsA"] +pub extern "C" fn waveOutGetDevCapsA() {} + +#[forward] +#[export_name="waveOutGetDevCapsW"] +pub extern "C" fn waveOutGetDevCapsW() {} + +#[forward] +#[export_name="waveOutGetErrorTextA"] +pub extern "C" fn waveOutGetErrorTextA() {} + +#[forward] +#[export_name="waveOutGetErrorTextW"] +pub extern "C" fn waveOutGetErrorTextW() {} + +#[forward] +#[export_name="waveOutGetID"] +pub extern "C" fn waveOutGetID() {} + +#[forward] +#[export_name="waveOutGetNumDevs"] +pub extern "C" fn waveOutGetNumDevs() {} + +#[forward] +#[export_name="waveOutGetPitch"] +pub extern "C" fn waveOutGetPitch() {} + +#[forward] +#[export_name="waveOutGetPlaybackRate"] +pub extern "C" fn waveOutGetPlaybackRate() {} + +#[forward] +#[export_name="waveOutGetPosition"] +pub extern "C" fn waveOutGetPosition() {} + +#[forward] +#[export_name="waveOutGetVolume"] +pub extern "C" fn waveOutGetVolume() {} + +#[forward] +#[export_name="waveOutMessage"] +pub extern "C" fn waveOutMessage() {} + +#[forward] +#[export_name="waveOutOpen"] +pub extern "C" fn waveOutOpen() {} + +#[forward] +#[export_name="waveOutPause"] +pub extern "C" fn waveOutPause() {} + +#[forward] +#[export_name="waveOutPrepareHeader"] +pub extern "C" fn waveOutPrepareHeader() {} + +#[forward] +#[export_name="waveOutReset"] +pub extern "C" fn waveOutReset() {} + +#[forward] +#[export_name="waveOutRestart"] +pub extern "C" fn waveOutRestart() {} + +#[forward] +#[export_name="waveOutSetPitch"] +pub extern "C" fn waveOutSetPitch() {} + +#[forward] +#[export_name="waveOutSetPlaybackRate"] +pub extern "C" fn waveOutSetPlaybackRate() {} + +#[forward] +#[export_name="waveOutSetVolume"] +pub extern "C" fn waveOutSetVolume() {} + +#[forward] +#[export_name="waveOutUnprepareHeader"] +pub extern "C" fn waveOutUnprepareHeader() {} + +#[forward] +#[export_name="waveOutWrite"] +pub extern "C" fn waveOutWrite() {} + diff --git a/MelonProxy/src/proxy/exports.rs b/MelonProxy/src/proxy/exports.rs deleted file mode 100644 index 800650230..000000000 --- a/MelonProxy/src/proxy/exports.rs +++ /dev/null @@ -1,316 +0,0 @@ -//! all the logic for exporting the functions. - -use std::{ - arch::global_asm, - marker::FnPtr, - ptr::null_mut, - sync::{LazyLock, Mutex}, io, -}; - -use super::hinstance_ext::ProxyDll; -use libloading::Library; -use windows::Win32::Foundation::HINSTANCE; - -// These arrays are accessed by assembly code to jump to the given function. -// TODO: once dynamically sized arrays are implemented, consider starting this off at 17 and dynamically resizing as needed -#[no_mangle] -static mut OriginalFuncs: [*const (); 181] = [null_mut(); 181]; - -// These assembly files define the Windows DLL functions that we're proxying, they are exported through a linked .def file. -#[cfg(target_arch = "x86_64")] -global_asm!(include_str!("../../deps/version.x64.S")); -#[cfg(target_arch = "x86")] -global_asm!(include_str!("../../deps/version.x86.S")); - -#[cfg(target_arch = "x86_64")] -global_asm!(include_str!("../../deps/winhttp.x64.S")); -#[cfg(target_arch = "x86")] -global_asm!(include_str!("../../deps/winhttp.x86.S")); - -#[cfg(target_arch = "x86_64")] -global_asm!(include_str!("../../deps/winmm.x64.S")); -#[cfg(target_arch = "x86")] -global_asm!(include_str!("../../deps/winmm.x86.S")); - -const EXPORTS_VERSION: [&[u8]; 17] = [ - b"GetFileVersionInfoA", - b"GetFileVersionInfoByHandle", - b"GetFileVersionInfoExA", - b"GetFileVersionInfoExW", - b"GetFileVersionInfoSizeA", - b"GetFileVersionInfoSizeExA", - b"GetFileVersionInfoSizeExW", - b"GetFileVersionInfoSizeW", - b"GetFileVersionInfoW", - b"VerFindFileA", - b"VerFindFileW", - b"VerInstallFileA", - b"VerInstallFileW", - b"VerLanguageNameA", - b"VerLanguageNameW", - b"VerQueryValueA", - b"VerQueryValueW", -]; - -const EXPORTS_WINHTTP: [&[u8]; 27] = [ - b"EmptyWorkingSet", - b"EnumDeviceDrivers", - b"EnumPageFilesA", - b"EnumPageFilesW", - b"EnumProcessModules", - b"EnumProcessModulesEx", - b"EnumProcesses", - b"GetDeviceDriverBaseNameA", - b"GetDeviceDriverBaseNameW", - b"GetDeviceDriverFileNameA", - b"GetDeviceDriverFileNameW", - b"GetMappedFileNameA", - b"GetMappedFileNameW", - b"GetModuleBaseNameA", - b"GetModuleBaseNameW", - b"GetModuleFileNameExA", - b"GetModuleFileNameExW", - b"GetModuleInformation", - b"GetPerformanceInfo", - b"GetProcessImageFileNameA", - b"GetProcessImageFileNameW", - b"GetProcessMemoryInfo", - b"GetWsChanges", - b"GetWsChangesEx", - b"InitializeProcessForWsWatch", - b"QueryWorkingSet", - b"QueryWorkingSetEx", -]; - -const EXPORTS_WINMM: [&[u8]; 181] = [ - b"CloseDriver", - b"DefDriverProc", - b"DriverCallback", - b"DrvGetModuleHandle", - b"GetDriverModuleHandle", - b"OpenDriver", - b"PlaySound", - b"PlaySoundA", - b"PlaySoundW", - b"SendDriverMessage", - b"WOWAppExit", - b"auxGetDevCapsA", - b"auxGetDevCapsW", - b"auxGetNumDevs", - b"auxGetVolume", - b"auxOutMessage", - b"auxSetVolume", - b"joyConfigChanged", - b"joyGetDevCapsA", - b"joyGetDevCapsW", - b"joyGetNumDevs", - b"joyGetPos", - b"joyGetPosEx", - b"joyGetThreshold", - b"joyReleaseCapture", - b"joySetCapture", - b"joySetThreshold", - b"mciDriverNotify", - b"mciDriverYield", - b"mciExecute", - b"mciFreeCommandResource", - b"mciGetCreatorTask", - b"mciGetDeviceIDA", - b"mciGetDeviceIDFromElementIDA", - b"mciGetDeviceIDFromElementIDW", - b"mciGetDeviceIDW", - b"mciGetDriverData", - b"mciGetErrorStringA", - b"mciGetErrorStringW", - b"mciGetYieldProc", - b"mciLoadCommandResource", - b"mciSendCommandA", - b"mciSendCommandW", - b"mciSendStringA", - b"mciSendStringW", - b"mciSetDriverData", - b"mciSetYieldProc", - b"midiConnect", - b"midiDisconnect", - b"midiInAddBuffer", - b"midiInClose", - b"midiInGetDevCapsA", - b"midiInGetDevCapsW", - b"midiInGetErrorTextA", - b"midiInGetErrorTextW", - b"midiInGetID", - b"midiInGetNumDevs", - b"midiInMessage", - b"midiInOpen", - b"midiInPrepareHeader", - b"midiInReset", - b"midiInStart", - b"midiInStop", - b"midiInUnprepareHeader", - b"midiOutCacheDrumPatches", - b"midiOutCachePatches", - b"midiOutClose", - b"midiOutGetDevCapsA", - b"midiOutGetDevCapsW", - b"midiOutGetErrorTextA", - b"midiOutGetErrorTextW", - b"midiOutGetID", - b"midiOutGetNumDevs", - b"midiOutGetVolume", - b"midiOutLongMsg", - b"midiOutMessage", - b"midiOutOpen", - b"midiOutPrepareHeader", - b"midiOutReset", - b"midiOutSetVolume", - b"midiOutShortMsg", - b"midiOutUnprepareHeader", - b"midiStreamClose", - b"midiStreamOpen", - b"midiStreamOut", - b"midiStreamPause", - b"midiStreamPosition", - b"midiStreamProperty", - b"midiStreamRestart", - b"midiStreamStop", - b"mixerClose", - b"mixerGetControlDetailsA", - b"mixerGetControlDetailsW", - b"mixerGetDevCapsA", - b"mixerGetDevCapsW", - b"mixerGetID", - b"mixerGetLineControlsA", - b"mixerGetLineControlsW", - b"mixerGetLineInfoA", - b"mixerGetLineInfoW", - b"mixerGetNumDevs", - b"mixerMessage", - b"mixerOpen", - b"mixerSetControlDetails", - b"mmDrvInstall", - b"mmGetCurrentTask", - b"mmTaskBlock", - b"mmTaskCreate", - b"mmTaskSignal", - b"mmTaskYield", - b"mmioAdvance", - b"mmioAscend", - b"mmioClose", - b"mmioCreateChunk", - b"mmioDescend", - b"mmioFlush", - b"mmioGetInfo", - b"mmioInstallIOProcA", - b"mmioInstallIOProcW", - b"mmioOpenA", - b"mmioOpenW", - b"mmioRead", - b"mmioRenameA", - b"mmioRenameW", - b"mmioSeek", - b"mmioSendMessage", - b"mmioSetBuffer", - b"mmioSetInfo", - b"mmioStringToFOURCCA", - b"mmioStringToFOURCCW", - b"mmioWrite", - b"mmsystemGetVersion", - b"sndPlaySoundA", - b"sndPlaySoundW", - b"timeBeginPeriod", - b"timeEndPeriod", - b"timeGetDevCaps", - b"timeGetSystemTime", - b"timeGetTime", - b"timeKillEvent", - b"timeSetEvent", - b"waveInAddBuffer", - b"waveInClose", - b"waveInGetDevCapsA", - b"waveInGetDevCapsW", - b"waveInGetErrorTextA", - b"waveInGetErrorTextW", - b"waveInGetID", - b"waveInGetNumDevs", - b"waveInGetPosition", - b"waveInMessage", - b"waveInOpen", - b"waveInPrepareHeader", - b"waveInReset", - b"waveInStart", - b"waveInStop", - b"waveInUnprepareHeader", - b"waveOutBreakLoop", - b"waveOutClose", - b"waveOutGetDevCapsA", - b"waveOutGetDevCapsW", - b"waveOutGetErrorTextA", - b"waveOutGetErrorTextW", - b"waveOutGetID", - b"waveOutGetNumDevs", - b"waveOutGetPitch", - b"waveOutGetPlaybackRate", - b"waveOutGetPosition", - b"waveOutGetVolume", - b"waveOutMessage", - b"waveOutOpen", - b"waveOutPause", - b"waveOutPrepareHeader", - b"waveOutReset", - b"waveOutRestart", - b"waveOutSetPitch", - b"waveOutSetPlaybackRate", - b"waveOutSetVolume", - b"waveOutUnprepareHeader", - b"waveOutWrite", - b"ExportByOrdinal2", -]; - -// we have to statically store the original library here, because if it gets dropped, the function pointers we get out of it become invalid. -pub static ORIGINAL: LazyLock>> = LazyLock::new(|| Mutex::new(None)); - -/// this function gets called by the #[proxy] macro in our entrypoint. -pub fn initialize(module: HINSTANCE) -> Result<(), io::Error> { - let INVALID_HANDLE: io::Error = io::Error::new(io::ErrorKind::InvalidInput, "Invalid module handle"); - let INVALID_FILE_NAME: io::Error = io::Error::new(io::ErrorKind::InvalidInput, "Invalid file name"); - let POISONED_LOCK: io::Error = io::Error::new(io::ErrorKind::Other, "Poisoned lock"); - - if module.is_invalid() { - return Err(INVALID_HANDLE); - } - - let name = module.get_file_name()?; - let original = module.load_original()?; - - let exports = match name.as_str() { - "version.dll" => EXPORTS_VERSION.to_vec(), - "winhttp.dll" => EXPORTS_WINHTTP.to_vec(), - "winmm.dll" => EXPORTS_WINMM.to_vec(), - _ => return Err(INVALID_FILE_NAME), - }; - - for (i, export) in exports.iter().enumerate() { - unsafe { - OriginalFuncs[i] = get_maybe(&original, export); - } - } - - //store the library so it doesn't get unloaded - let orig = ORIGINAL.try_lock(); - if orig.is_err() { - return Err(POISONED_LOCK); - } - - *orig.unwrap() = Some(original); - - Ok(()) -} - -unsafe fn get_maybe(lib: &Library, name: &[u8]) -> *const () { - let func = lib.get::(name); - - match func.is_err() { - false => func.unwrap().addr(), - true => null_mut(), - } -} diff --git a/MelonProxy/src/proxy/hinstance_ext.rs b/MelonProxy/src/proxy/hinstance_ext.rs deleted file mode 100644 index d7348d0d1..000000000 --- a/MelonProxy/src/proxy/hinstance_ext.rs +++ /dev/null @@ -1,62 +0,0 @@ -use std::{io, path::Path}; - -use super::windows_ext::get_module_path; -use libloading::Library; -use windows::Win32::Foundation::HINSTANCE; - -pub trait ProxyDll { - fn get_file_name(&self) -> Result; - fn is_compatible(&self) -> Result; - fn load_original(&self) -> Result; -} - -impl ProxyDll for HINSTANCE { - fn get_file_name(&self) -> Result { - let path = get_module_path(*self)?; - - let file_name = path - .file_name() - .ok_or(io::Error::new( - io::ErrorKind::NotFound, - "Failed to get File Name", - ))? - .to_str() - .ok_or(io::Error::new( - io::ErrorKind::NotFound, - "Failed to get File Name", - ))?; - - Ok(file_name.to_lowercase().to_string()) - } - - fn is_compatible(&self) -> Result { - let file_name = self.get_file_name()?; - - Ok(file_name.eq("version.dll") || file_name.eq("winhttp.dll") || file_name.eq("winmm.dll")) - } - - fn load_original(&self) -> Result { - - let INVALID_FILE_NAME: io::Error = - io::Error::new(io::ErrorKind::NotFound, "Invalid File Name"); - let LIBRARY_NOT_FOUND: io::Error = - io::Error::new(io::ErrorKind::NotFound, "Failed to load original Proxy DLL"); - if !self.is_compatible()? { - return Err(INVALID_FILE_NAME); - } - - let name = self.get_file_name()?; - let path = Path::new("C:\\Windows\\System32").join(name); - - if !path.exists() { - return Err(LIBRARY_NOT_FOUND); - } - - let lib = unsafe { Library::new(path) }; - - match lib { - Ok(lib) => Ok(lib), - Err(_) => Err(LIBRARY_NOT_FOUND), - } - } -} diff --git a/MelonProxy/src/proxy/mod.rs b/MelonProxy/src/proxy/mod.rs deleted file mode 100644 index ca08762df..000000000 --- a/MelonProxy/src/proxy/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod exports; -pub mod hinstance_ext; -pub mod windows_ext; \ No newline at end of file diff --git a/MelonProxy/src/proxy/windows_ext.rs b/MelonProxy/src/proxy/windows_ext.rs deleted file mode 100644 index c9ccb6e05..000000000 --- a/MelonProxy/src/proxy/windows_ext.rs +++ /dev/null @@ -1,23 +0,0 @@ -use std::{io, path::PathBuf}; - -use windows::Win32::{ - Foundation::{HINSTANCE, MAX_PATH}, - System::LibraryLoader::GetModuleFileNameW, -}; - -pub fn get_module_path(module: HINSTANCE) -> Result { - let mut path = [0u16; MAX_PATH as usize]; - - let len = unsafe { GetModuleFileNameW(module, &mut path) }; - - if len <= 0 { - return Err(io::Error::new( - io::ErrorKind::NotFound, - "Failed to get File Name", - )); - } - - let str = String::from_utf16_lossy(&path[..len as usize]); - - Ok(PathBuf::from(str)) -} From 9b3f7aae1e23bac9cdbb3b04b97521103233fbd9 Mon Sep 17 00:00:00 2001 From: Sarah Date: Thu, 4 Jan 2024 07:20:22 +0100 Subject: [PATCH 68/79] install llvm --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 777ccf7cf..7b3367f7b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -107,7 +107,7 @@ jobs: run: cargo install cargo-xwin - name: install dev dependencies shell: bash - run: sudo apt-get install libgtk-3-dev mingw-w64 binutils-mingw-w64 wine + run: sudo apt-get install libgtk-3-dev mingw-w64 binutils-mingw-w64 wine llvm - name: Build Rust Release | Linux - x64 shell: bash run: cargo build --target x86_64-unknown-linux-gnu --release From 1b2508698967123e2fddc8aa40e244896924f11e Mon Sep 17 00:00:00 2001 From: Sarah Date: Thu, 4 Jan 2024 08:11:36 +0100 Subject: [PATCH 69/79] xwin_arch env var for cross-arch compile --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7b3367f7b..1f961b0e0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -122,10 +122,10 @@ jobs: run: cargo xwin build --package Bootstrap --target x86_64-pc-windows-msvc - name: Build Bootstrap Release | Windows - x86 shell: bash - run: cargo xwin build --package Bootstrap --target i686-pc-windows-msvc --release + run: XWIN_ARCH=x86 cargo xwin build --package Bootstrap --target i686-pc-windows-msvc --release - name: Build Bootstrap Debug | Windows - x86 shell: bash - run: cargo xwin build --package Bootstrap --target i686-pc-windows-msvc + run: XWIN_ARCH=x86 cargo xwin build --package Bootstrap --target i686-pc-windows-msvc - name: Build Proxy Release | Windows - x64 shell: bash run: cargo build --package MelonProxy --target x86_64-pc-windows-gnu --release From 97130964b09ae1249472e747ae84b85d834c4ae5 Mon Sep 17 00:00:00 2001 From: Sarah Date: Thu, 4 Jan 2024 08:23:32 +0100 Subject: [PATCH 70/79] Fix build.yml --- .github/workflows/build.yml | 51 ++----------------------------------- 1 file changed, 2 insertions(+), 49 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1f961b0e0..eec06f5e3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,60 +23,13 @@ jobs: - name: Upload Release Artifact uses: actions/upload-artifact@v3 with: - name: MLRelease + name: MLCoreRelease path: Output/Release/MelonLoader/ - name: Upload Debug Artifact uses: actions/upload-artifact@v3 with: - name: MLDebug + name: MLCoreDebug path: Output/Debug/MelonLoader/ - - name: Upload Proxy Release | Windows x86 - uses: actions/upload-artifact@v3 - with: - name: MLProxyX86-Windows-Release - path: target/i686-pc-windows-msvc/release/version.dll - # Upload Bootstrap Release - x86 - - name: Upload Bootstrap Release | Windows x86 - uses: actions/upload-artifact@v3 - with: - name: MLBootstrapX86-Windows-Release - path: target/i686-pc-windows-msvc/release/Bootstrap.dll - # Upload Proxy Release - x64 - - name: Upload Proxy Release | Windows x64 - uses: actions/upload-artifact@v3 - with: - name: MLProxyX64-Windows-Release - path: target/x86_64-pc-windows-msvc/release/version.dll - # Upload Bootstrap Release - x64 - - name: Upload Bootstrap Release | Windows x64 - uses: actions/upload-artifact@v3 - with: - name: MLBootstrapX64-Windows-Release - path: target/x86_64-pc-windows-msvc/release/Bootstrap.dll - # Upload Proxy Debug - x86 - - name: Upload Proxy Debug | Windows x86 - uses: actions/upload-artifact@v3 - with: - name: MLProxyX86-Windows-Debug - path: target/i686-pc-windows-msvc/debug/version.dll - # Upload Bootstrap Debug - x86 - - name: Upload Bootstrap Debug | Windows x86 - uses: actions/upload-artifact@v3 - with: - name: MLBootstrapX86-Windows-Debug - path: target/i686-pc-windows-msvc/debug/Bootstrap.dll - # Upload Proxy Debug - x64 - - name: Upload Proxy Debug | Windows x64 - uses: actions/upload-artifact@v3 - with: - name: MLProxyX64-Windows-Debug - path: target/x86_64-pc-windows-msvc/debug/version.dll - # Upload Bootstrap Debug - x64 - - name: Upload Bootstrap Debug | Windows x64 - uses: actions/upload-artifact@v3 - with: - name: MLBootstrapX64-Windows-Debug - path: target/x86_64-pc-windows-msvc/debug/Bootstrap.dll build_rust: runs-on: ubuntu-latest steps: From 09fadafc77bffbe5f0e115442c55661abee5b3ff Mon Sep 17 00:00:00 2001 From: Sarah Date: Thu, 4 Jan 2024 19:27:47 +0100 Subject: [PATCH 71/79] bump Il2CppAssetBundleManager --- .../UnityEngine.Il2CppAssetBundleManager.dll | Bin 21504 -> 21504 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/BaseLibs/Managed/UnityEngine.Il2CppAssetBundleManager.dll b/BaseLibs/Managed/UnityEngine.Il2CppAssetBundleManager.dll index d98085b5c96fe450d8211f31be48d907ded9d3af..517ce116579e83393e16e6261a6698eb1cb8226a 100644 GIT binary patch literal 21504 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 Cq_YPA From 9ca6af7b4008b05b22d41423f8528405885dea73 Mon Sep 17 00:00:00 2001 From: Sarah Date: Sat, 6 Jan 2024 00:01:10 +0100 Subject: [PATCH 72/79] Remove invalid Color attributes --- MelonLoader/Attributes/MelonAuthorColorAttribute.cs | 5 +---- MelonLoader/Attributes/MelonColorAttribute.cs | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/MelonLoader/Attributes/MelonAuthorColorAttribute.cs b/MelonLoader/Attributes/MelonAuthorColorAttribute.cs index 0e040eefd..b12864705 100644 --- a/MelonLoader/Attributes/MelonAuthorColorAttribute.cs +++ b/MelonLoader/Attributes/MelonAuthorColorAttribute.cs @@ -25,10 +25,7 @@ public ConsoleColor Color public MelonAuthorColorAttribute() => DrawingColor = MelonLogger.DefaultTextColor; - public MelonAuthorColorAttribute(Color drawingColor) - => DrawingColor = drawingColor; - - [Obsolete("ConsoleColor is obsolete, use the (int, int, int, int) or (Color) constructor instead.")] + [Obsolete("ConsoleColor is obsolete, use the (int, int, int, int) constructor instead.")] public MelonAuthorColorAttribute(ConsoleColor color) => Color = ((color == ConsoleColor.Black) ? LoggerUtils.DrawingColorToConsoleColor(MelonLogger.DefaultMelonColor) : color); diff --git a/MelonLoader/Attributes/MelonColorAttribute.cs b/MelonLoader/Attributes/MelonColorAttribute.cs index 6241cd49e..46af0637d 100644 --- a/MelonLoader/Attributes/MelonColorAttribute.cs +++ b/MelonLoader/Attributes/MelonColorAttribute.cs @@ -25,10 +25,7 @@ public ConsoleColor Color public MelonColorAttribute() => DrawingColor = MelonLogger.DefaultTextColor; - public MelonColorAttribute(Color drawingColor) - => DrawingColor = drawingColor; - - [Obsolete("ConsoleColor is obsolete, use the (int, int, int, int) or (Color) constructor instead.")] + [Obsolete("ConsoleColor is obsolete, use the (int, int, int, int) constructor instead.")] public MelonColorAttribute(ConsoleColor color) => Color = ((color == ConsoleColor.Black) ? LoggerUtils.DrawingColorToConsoleColor(MelonLogger.DefaultMelonColor) : color); From 60577c48a4f404e735b75f415cc667877a5ab331 Mon Sep 17 00:00:00 2001 From: Sarah Date: Tue, 9 Jan 2024 18:17:55 +0100 Subject: [PATCH 73/79] Fetch Windows Directory properly --- MelonProxy/Cargo.toml | 1 + MelonProxy/src/lib.rs | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/MelonProxy/Cargo.toml b/MelonProxy/Cargo.toml index cfb72059e..71b542fc8 100644 --- a/MelonProxy/Cargo.toml +++ b/MelonProxy/Cargo.toml @@ -20,6 +20,7 @@ winapi = { version = "0.3.9", features = [ "processenv", "winbase", "winuser", + "sysinfoapi", "errhandlingapi", ] } diff --git a/MelonProxy/src/lib.rs b/MelonProxy/src/lib.rs index 62e61bc72..468977578 100644 --- a/MelonProxy/src/lib.rs +++ b/MelonProxy/src/lib.rs @@ -76,6 +76,8 @@ static mut ORIGINAL_FUNCS: [FARPROC; TOTAL_EXPORTS] = [std::ptr::null_mut(); TOT #[no_mangle] static mut ORIG_FUNCS_PTR: *const FARPROC = std::ptr::null_mut(); +const INFO_BUFFER_SIZE: u32 = 32767; + /// Indicates once we are ready to accept incoming calls to proxied functions static mut PROXYGEN_READY: bool = false; @@ -130,7 +132,9 @@ unsafe fn get_dll_path() -> Option { /// Called when the thread is spawned #[cfg(target_os = "windows")] unsafe extern "system" fn init(_: *mut c_void) -> u32 { - use std::path::PathBuf; + use std::{path::PathBuf, ffi::c_char}; + + use winapi::shared::ntdef::LPSTR; ORIG_FUNCS_PTR = ORIGINAL_FUNCS.as_ptr(); @@ -141,7 +145,16 @@ unsafe extern "system" fn init(_: *mut c_void) -> u32 { internal_failure!("Failed to get DLL name"); }); - let path = PathBuf::from("C:\\Windows\\System32").join(orig_dll_name); + + let mut lpBuffer: [c_char; INFO_BUFFER_SIZE as usize] = [0; INFO_BUFFER_SIZE as usize]; + let pathLen = winapi::um::sysinfoapi::GetWindowsDirectoryA(lpBuffer.as_mut_ptr(), INFO_BUFFER_SIZE); + if pathLen == 0 { + internal_failure!("Failed to get Windows directory"); + } + + let path = unsafe { + PathBuf::from(String::from_raw_parts(lpBuffer.as_mut_ptr().cast(), pathLen as usize, pathLen as usize)) + }.join("System32").join(orig_dll_name); if !path.exists() { internal_failure!("Original DLL does not exist"); From dfa3b1ae894989ad939df3d3601b98d8c418fdc1 Mon Sep 17 00:00:00 2001 From: Sarah Date: Tue, 9 Jan 2024 18:19:30 +0100 Subject: [PATCH 74/79] cleanup --- MelonProxy/src/lib.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/MelonProxy/src/lib.rs b/MelonProxy/src/lib.rs index 468977578..971567cc1 100644 --- a/MelonProxy/src/lib.rs +++ b/MelonProxy/src/lib.rs @@ -134,8 +134,6 @@ unsafe fn get_dll_path() -> Option { unsafe extern "system" fn init(_: *mut c_void) -> u32 { use std::{path::PathBuf, ffi::c_char}; - use winapi::shared::ntdef::LPSTR; - ORIG_FUNCS_PTR = ORIGINAL_FUNCS.as_ptr(); if let Some(dll_path) = get_dll_path() { @@ -147,14 +145,14 @@ unsafe extern "system" fn init(_: *mut c_void) -> u32 { let mut lpBuffer: [c_char; INFO_BUFFER_SIZE as usize] = [0; INFO_BUFFER_SIZE as usize]; - let pathLen = winapi::um::sysinfoapi::GetWindowsDirectoryA(lpBuffer.as_mut_ptr(), INFO_BUFFER_SIZE); + let pathLen = winapi::um::sysinfoapi::GetSystemDirectoryA(lpBuffer.as_mut_ptr(), INFO_BUFFER_SIZE); if pathLen == 0 { internal_failure!("Failed to get Windows directory"); } let path = unsafe { PathBuf::from(String::from_raw_parts(lpBuffer.as_mut_ptr().cast(), pathLen as usize, pathLen as usize)) - }.join("System32").join(orig_dll_name); + }.join(orig_dll_name); if !path.exists() { internal_failure!("Original DLL does not exist"); From d83e23bde055e6f0850d4e020fcdca97995d265b Mon Sep 17 00:00:00 2001 From: Sarah Date: Tue, 9 Jan 2024 18:40:08 +0100 Subject: [PATCH 75/79] Bump Cpp2IL, backport some NativeHook fixes, and add Changelog --- CHANGELOG.md | 16 ++++++++++ .../Packages/Cpp2IL.cs | 2 +- MelonLoader/MelonLoader.csproj | 2 +- MelonLoader/NativeUtils/NativeHooks.cs | 32 ++++++++++++++++--- 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f0f54db6..561d411b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ | Versions: | | - | +| [v0.6.2](#v061) | | [v0.6.1](#v061) | | [v0.6.0](#v060) | | [v0.5.7](#v057) | @@ -33,6 +34,21 @@ --- +### v0.6.2 + +1. Added a compatibility layer for EOS (Epic Online Services), preventing a crash caused by the Overlay +2. Updated Cpp2IL +3. Updated Tomlet +5. Updated `MelonLoader.NativeUtils.NativeHook` to prevent GC issues +6. Updated Il2CppAssetBundleManager for Il2CppInterop +7. Fix Proxy being unable to find System32 when Windows is not installed in C: +8. Fixed logger sha256 hash (Credits to [Windows10CE](https://github.com/Windows10CE)) +9. Fixed DAB Thread safety & Oculus Profile Pictures (Credits to [SirCoolness](https://github.com/SirCoolness)) +10. Fix Melon Load Order (Credits to [Loukylor](https://github.com/loukylor)) +11. General fixes for Proxy & Bootstrap + +--- + ### v0.6.1 1. Refactored Bootstrap, more informative errors diff --git a/Dependencies/Il2CppAssemblyGenerator/Packages/Cpp2IL.cs b/Dependencies/Il2CppAssemblyGenerator/Packages/Cpp2IL.cs index 06811d827..287aff08e 100644 --- a/Dependencies/Il2CppAssemblyGenerator/Packages/Cpp2IL.cs +++ b/Dependencies/Il2CppAssemblyGenerator/Packages/Cpp2IL.cs @@ -17,7 +17,7 @@ internal Cpp2IL() Version = RemoteAPI.Info.ForceDumperVersion; #endif if (string.IsNullOrEmpty(Version) || Version.Equals("0.0.0.0")) - Version = "2022.1.0-pre-release.10"; + Version = "2022.1.0-pre-release.13"; Name = nameof(Cpp2IL); Destination = Path.Combine(Core.BasePath, Name); diff --git a/MelonLoader/MelonLoader.csproj b/MelonLoader/MelonLoader.csproj index 6d90b8a6f..9d4023eeb 100644 --- a/MelonLoader/MelonLoader.csproj +++ b/MelonLoader/MelonLoader.csproj @@ -36,7 +36,7 @@ - + diff --git a/MelonLoader/NativeUtils/NativeHooks.cs b/MelonLoader/NativeUtils/NativeHooks.cs index 4b4a4cd81..a869c10bf 100644 --- a/MelonLoader/NativeUtils/NativeHooks.cs +++ b/MelonLoader/NativeUtils/NativeHooks.cs @@ -10,6 +10,8 @@ namespace MelonLoader.NativeUtils public class NativeHook where T : Delegate { #region Private Values + private static readonly List _gcProtect = new(); + private IntPtr _targetHandle; private IntPtr _detourHandle; private IntPtr _trampolineHandle; @@ -51,12 +53,30 @@ public IntPtr Detour public T Trampoline { - get + get => _trampoline; + private set { - if (_trampoline == null) - _trampoline = (T)Marshal.GetDelegateForFunctionPointer(_trampolineHandle, typeof(T)); + if (value == null) + throw new ArgumentNullException(nameof(value)); + + if (_trampoline != null) + _gcProtect.Remove(_trampoline); + + _trampoline = value; + _gcProtect.Add(_trampoline); + } + + } + + public IntPtr TrampolineHandle + { + get => _trampolineHandle; + private set + { + if (value == IntPtr.Zero) + throw new ArgumentNullException(nameof(value)); - return _trampoline; + _trampolineHandle = value; } } @@ -91,6 +111,9 @@ public unsafe void Attach() IntPtr trampoline = _targetHandle; BootstrapInterop.NativeHookAttach((IntPtr)(&trampoline), _detourHandle); _trampolineHandle = trampoline; + + _trampoline = (T)Marshal.GetDelegateForFunctionPointer(_trampolineHandle, typeof(T)); + _gcProtect.Add(_trampoline); IsHooked = true; } @@ -107,6 +130,7 @@ public unsafe void Detach() BootstrapInterop.NativeHookDetach((IntPtr)(&original), _detourHandle); IsHooked= false; + _gcProtect.Remove(_trampoline); _trampoline = null; _trampolineHandle = IntPtr.Zero; } From 5fb48ee0e87e7a84a0bf14e7b5417f8be11858ee Mon Sep 17 00:00:00 2001 From: Sarah Date: Tue, 9 Jan 2024 18:56:36 +0100 Subject: [PATCH 76/79] Bump Tomlet --- Dependencies/MelonStartScreen/MelonStartScreen.csproj | 2 +- Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj | 2 +- Dependencies/SupportModules/Mono/Mono.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Dependencies/MelonStartScreen/MelonStartScreen.csproj b/Dependencies/MelonStartScreen/MelonStartScreen.csproj index 8e363e185..4e7f1972b 100644 --- a/Dependencies/MelonStartScreen/MelonStartScreen.csproj +++ b/Dependencies/MelonStartScreen/MelonStartScreen.csproj @@ -19,6 +19,6 @@ - + \ No newline at end of file diff --git a/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj b/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj index e6d53a997..afb9ecd62 100644 --- a/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj +++ b/Dependencies/SupportModules/Il2Cpp/Il2Cpp.csproj @@ -47,6 +47,6 @@ - + \ No newline at end of file diff --git a/Dependencies/SupportModules/Mono/Mono.csproj b/Dependencies/SupportModules/Mono/Mono.csproj index 38ad7eeff..976996954 100644 --- a/Dependencies/SupportModules/Mono/Mono.csproj +++ b/Dependencies/SupportModules/Mono/Mono.csproj @@ -24,6 +24,6 @@ - + \ No newline at end of file From fdf1f17cb4ed70fa367674bafb0838b588c09a02 Mon Sep 17 00:00:00 2001 From: Sarah Date: Tue, 9 Jan 2024 20:17:53 +0100 Subject: [PATCH 77/79] Fix path getting --- .github/workflows/build.yml | 54 +++++++++++++------------------------ MelonProxy/src/lib.rs | 32 +++++++++++++++------- 2 files changed, 41 insertions(+), 45 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eec06f5e3..4ded0c5f9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -44,15 +44,9 @@ jobs: shell: bash run: rustup target add x86_64-unknown-linux-gnu - name: Install Windows target x64 - shell: bash - run: rustup target add x86_64-pc-windows-gnu - - name: Install Windows target x86 - shell: bash - run: rustup target add i686-pc-windows-gnu - - name: Install Windows target x64 (MSVC) shell: bash run: rustup target add x86_64-pc-windows-msvc - - name: Install Windows target x86 (MSVC) + - name: Install Windows target x86 shell: bash run: rustup target add i686-pc-windows-msvc - name: Install x-win @@ -60,62 +54,50 @@ jobs: run: cargo install cargo-xwin - name: install dev dependencies shell: bash - run: sudo apt-get install libgtk-3-dev mingw-w64 binutils-mingw-w64 wine llvm + run: sudo apt-get install libgtk-3-dev wine llvm - name: Build Rust Release | Linux - x64 shell: bash run: cargo build --target x86_64-unknown-linux-gnu --release - name: Build Rust Debug | Linux - x64 shell: bash run: cargo build --target x86_64-unknown-linux-gnu - - name: Build Bootstrap Release | Windows - x64 - shell: bash - run: cargo xwin build --package Bootstrap --target x86_64-pc-windows-msvc --release - - name: Build Bootstrap Debug | Windows - x64 - shell: bash - run: cargo xwin build --package Bootstrap --target x86_64-pc-windows-msvc - - name: Build Bootstrap Release | Windows - x86 - shell: bash - run: XWIN_ARCH=x86 cargo xwin build --package Bootstrap --target i686-pc-windows-msvc --release - - name: Build Bootstrap Debug | Windows - x86 - shell: bash - run: XWIN_ARCH=x86 cargo xwin build --package Bootstrap --target i686-pc-windows-msvc - - name: Build Proxy Release | Windows - x64 + - name: Build Rust Release | Windows - x64 shell: bash - run: cargo build --package MelonProxy --target x86_64-pc-windows-gnu --release - - name: Build Proxy Debug | Windows - x64 + run: cargo xwin build --target x86_64-pc-windows-msvc --release + - name: Build Rust Debug | Windows - x64 shell: bash - run: cargo build --package MelonProxy --target x86_64-pc-windows-gnu - - name: Build Proxy Release | Windows - x86 + run: cargo xwin build --target x86_64-pc-windows-msvc + - name: Build Rust Release | Windows - x86 shell: bash - run: cargo build --package MelonProxy --target i686-pc-windows-gnu --release - - name: Build Proxy Debug | Windows - x86 + run: XWIN_ARCH=x86 cargo xwin build --target i686-pc-windows-msvc --release + - name: Build Rust Debug | Windows - x86 shell: bash - run: cargo build --package MelonProxy --target i686-pc-windows-gnu + run: XWIN_ARCH=x86 cargo xwin build --target i686-pc-windows-msvc - name: Upload Proxy Release | Linux x64 uses: actions/upload-artifact@v3 with: name: MLProxyX64-Linux-Release - path: target/x86_64-unknown-linux-gnu/release/libversion.so + path: target/x86_64-unknown-linux-msvc/release/libversion.so - name: Upload Bootstrap Release | Linux x64 uses: actions/upload-artifact@v3 with: name: MLBootstrapX64-Linux-Release - path: target/x86_64-unknown-linux-gnu/release/libBootstrap.so + path: target/x86_64-unknown-linux-msvc/release/libBootstrap.so - name: Upload Proxy Debug | Linux x64 uses: actions/upload-artifact@v3 with: name: MLProxyX64-Linux-Debug - path: target/x86_64-unknown-linux-gnu/debug/libversion.so + path: target/x86_64-unknown-linux-msvc/debug/libversion.so - name: Upload Bootstrap Debug | Linux x64 uses: actions/upload-artifact@v3 with: name: MLBootstrapX64-Linux-Debug - path: target/x86_64-unknown-linux-gnu/debug/libBootstrap.so + path: target/x86_64-unknown-linux-msvc/debug/libBootstrap.so - name: Upload Proxy Release | Windows x86 uses: actions/upload-artifact@v3 with: name: MLProxyX86-Windows-Release - path: target/i686-pc-windows-gnu/release/version.dll + path: target/i686-pc-windows-msvc/release/version.dll - name: Upload Bootstrap Release | Windows x86 uses: actions/upload-artifact@v3 with: @@ -125,7 +107,7 @@ jobs: uses: actions/upload-artifact@v3 with: name: MLProxyX64-Windows-Release - path: target/x86_64-pc-windows-gnu/release/version.dll + path: target/x86_64-pc-windows-msvc/release/version.dll - name: Upload Bootstrap Release | Windows x64 uses: actions/upload-artifact@v3 with: @@ -135,7 +117,7 @@ jobs: uses: actions/upload-artifact@v3 with: name: MLProxyX86-Windows-Debug - path: target/i686-pc-windows-gnu/debug/version.dll + path: target/i686-pc-windows-msvc/debug/version.dll - name: Upload Bootstrap Debug | Windows x86 uses: actions/upload-artifact@v3 with: @@ -145,7 +127,7 @@ jobs: uses: actions/upload-artifact@v3 with: name: MLProxyX64-Windows-Debug - path: target/x86_64-pc-windows-gnu/debug/version.dll + path: target/x86_64-pc-windows-msvc/debug/version.dll - name: Upload Bootstrap Debug | Windows x64 uses: actions/upload-artifact@v3 with: diff --git a/MelonProxy/src/lib.rs b/MelonProxy/src/lib.rs index 971567cc1..fa3d33386 100644 --- a/MelonProxy/src/lib.rs +++ b/MelonProxy/src/lib.rs @@ -58,6 +58,7 @@ use winapi::{ winbase::{STD_ERROR_HANDLE, STD_OUTPUT_HANDLE}, winnt::{DLL_PROCESS_ATTACH, DLL_PROCESS_DETACH}, winuser::{MessageBoxA, MB_OK}, + sysinfoapi::GetSystemDirectoryW, }, }; @@ -129,10 +130,27 @@ unsafe fn get_dll_path() -> Option { Some(os_string.to_string_lossy().into_owned()) } +#[cfg(target_os = "windows")] +unsafe fn get_system32_path() -> Option { + let mut buffer: Vec = vec![0; INFO_BUFFER_SIZE as usize]; + let size = GetSystemDirectoryW( + buffer.as_mut_ptr(), + buffer.len() as u32, + ); + + if size == 0 { + return None; + } + + buffer.truncate(size as usize); + let os_string = OsString::from_wide(&buffer); + Some(os_string.to_string_lossy().into_owned()) +} + /// Called when the thread is spawned #[cfg(target_os = "windows")] unsafe extern "system" fn init(_: *mut c_void) -> u32 { - use std::{path::PathBuf, ffi::c_char}; + use std::{path::PathBuf, ffi::{c_char, CString, CStr}}; ORIG_FUNCS_PTR = ORIGINAL_FUNCS.as_ptr(); @@ -144,15 +162,11 @@ unsafe extern "system" fn init(_: *mut c_void) -> u32 { }); - let mut lpBuffer: [c_char; INFO_BUFFER_SIZE as usize] = [0; INFO_BUFFER_SIZE as usize]; - let pathLen = winapi::um::sysinfoapi::GetSystemDirectoryA(lpBuffer.as_mut_ptr(), INFO_BUFFER_SIZE); - if pathLen == 0 { - internal_failure!("Failed to get Windows directory"); - } + let system32_path = get_system32_path().unwrap_or_else(|| { + internal_failure!("Failed to get system32 path"); + }); - let path = unsafe { - PathBuf::from(String::from_raw_parts(lpBuffer.as_mut_ptr().cast(), pathLen as usize, pathLen as usize)) - }.join(orig_dll_name); + let path = PathBuf::from(&system32_path).join(orig_dll_name); if !path.exists() { internal_failure!("Original DLL does not exist"); From 86b5c9d407ecfe1c7eda95874d0a6cd08e403de7 Mon Sep 17 00:00:00 2001 From: Sarah Date: Tue, 9 Jan 2024 20:28:24 +0100 Subject: [PATCH 78/79] fix build.yml --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4ded0c5f9..1b7b0d8b0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -77,22 +77,22 @@ jobs: uses: actions/upload-artifact@v3 with: name: MLProxyX64-Linux-Release - path: target/x86_64-unknown-linux-msvc/release/libversion.so + path: target/x86_64-unknown-linux-gnu/release/libversion.so - name: Upload Bootstrap Release | Linux x64 uses: actions/upload-artifact@v3 with: name: MLBootstrapX64-Linux-Release - path: target/x86_64-unknown-linux-msvc/release/libBootstrap.so + path: target/x86_64-unknown-linux-gnu/release/libBootstrap.so - name: Upload Proxy Debug | Linux x64 uses: actions/upload-artifact@v3 with: name: MLProxyX64-Linux-Debug - path: target/x86_64-unknown-linux-msvc/debug/libversion.so + path: target/x86_64-unknown-linux-gnu/debug/libversion.so - name: Upload Bootstrap Debug | Linux x64 uses: actions/upload-artifact@v3 with: name: MLBootstrapX64-Linux-Debug - path: target/x86_64-unknown-linux-msvc/debug/libBootstrap.so + path: target/x86_64-unknown-linux-gnu/debug/libBootstrap.so - name: Upload Proxy Release | Windows x86 uses: actions/upload-artifact@v3 with: From ca74c51f4ab17af96018f4a96a789ed5afd7fa63 Mon Sep 17 00:00:00 2001 From: Sarah Date: Tue, 9 Jan 2024 21:07:49 +0100 Subject: [PATCH 79/79] bump ML version in Bootstrap --- Bootstrap/src/constants.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Bootstrap/src/constants.rs b/Bootstrap/src/constants.rs index 563ad0e7d..a169ff035 100644 --- a/Bootstrap/src/constants.rs +++ b/Bootstrap/src/constants.rs @@ -18,7 +18,7 @@ pub type InvokeFnIl2Cpp = extern "C" fn( pub type InitFnMono = extern "C" fn(*const c_char, *const c_char) -> *mut MonoDomain; pub type InitFnIl2Cpp = extern "C" fn(*const c_char) -> *mut Il2CppDomain; -pub const MELON_VERSION: &str = "0.6.1"; +pub const MELON_VERSION: &str = "0.6.2"; pub const IS_ALPHA: bool = false;

    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 From de8181ea11e198a2279bead0999b3845b42cf369 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Thu, 30 Mar 2023 00:02:05 +0200 Subject: [PATCH 24/79] NativeHook helper class, depracate old Native Hooks --- Dependencies/MelonStartScreen/Core.cs | 12 +-- MelonLoader/MelonUtils.cs | 4 +- MelonLoader/NativeUtils/NativeHooks.cs | 109 +++++++++++++++++++++++++ 3 files changed, 118 insertions(+), 7 deletions(-) create mode 100644 MelonLoader/NativeUtils/NativeHooks.cs diff --git a/Dependencies/MelonStartScreen/Core.cs b/Dependencies/MelonStartScreen/Core.cs index 212c0570f..b5691c24f 100644 --- a/Dependencies/MelonStartScreen/Core.cs +++ b/Dependencies/MelonStartScreen/Core.cs @@ -10,6 +10,7 @@ using System.Threading; using Windows; using MelonLoader.Utils; +using MelonLoader.NativeUtils; namespace MelonLoader.MelonStartScreen { @@ -17,8 +18,8 @@ internal class Core : MelonModule { [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate IntPtr User32SetTimerDelegate(IntPtr hWnd, IntPtr nIDEvent, uint uElapse, IntPtr lpTimerFunc); + private static NativeHook user32Hook = new(); - private static User32SetTimerDelegate user32SetTimerOriginal; private static bool nextSetTimerIsUnity = false; private static IntPtr titleBarTimer; @@ -119,7 +120,6 @@ private static unsafe bool ApplyUser32SetTimerPatch() } // We get a native function pointer to User32SetTimerDetour from our current class - //IntPtr detourPtr = typeof(Core).GetMethod("User32SetTimerDetour", BindingFlags.NonPublic | BindingFlags.Static).MethodHandle.GetFunctionPointer(); #if NET6_0 delegate* unmanaged[Cdecl] detourPtr = &User32SetTimerDetour; #else @@ -131,12 +131,12 @@ private static unsafe bool ApplyUser32SetTimerPatch() return false; } #endif + user32Hook.Target = original; + user32Hook.Detour = (IntPtr)detourPtr; // And we patch SetTimer to replace it by our hook MelonDebug.Msg($"Applying USER32.dll::SetTimer Hook at 0x{original.ToInt64():X}"); - MelonUtils.NativeHookAttach((IntPtr)(&original), (IntPtr) detourPtr); - MelonDebug.Msg($"Creating delegate for original USER32.dll::SetTimer (0x{original.ToInt64():X})"); - user32SetTimerOriginal = (User32SetTimerDelegate)Marshal.GetDelegateForFunctionPointer(original, typeof(User32SetTimerDelegate)); + user32Hook.Attach(); MelonDebug.Msg("Applied USER32.dll::SetTimer patch"); return true; @@ -153,7 +153,7 @@ private unsafe static IntPtr User32SetTimerDetour(IntPtr hWnd, IntPtr nIDEvent, return IntPtr.Zero; } - return user32SetTimerOriginal(hWnd, nIDEvent, uElapse, timerProc); + return user32Hook.Trampoline(hWnd, nIDEvent, uElapse, timerProc); } #endregion diff --git a/MelonLoader/MelonUtils.cs b/MelonLoader/MelonUtils.cs index 9a7542f13..4be065b5a 100644 --- a/MelonLoader/MelonUtils.cs +++ b/MelonLoader/MelonUtils.cs @@ -423,15 +423,17 @@ public static string GetFileProductName(string filepath) return null; } + [Obsolete("Use NativeUtils.NativeHook instead")] public static void NativeHookAttach(IntPtr target, IntPtr detour) => BootstrapInterop.NativeHookAttach(target, detour); #if NET6_0 internal static void NativeHookAttachDirect(IntPtr target, IntPtr detour) => BootstrapInterop.NativeHookAttachDirect(target, detour); #else //On mono, NativeHookAttach *is* direct. + [Obsolete("Use NativeUtils.NativeHook instead")] internal static void NativeHookAttachDirect(IntPtr target, IntPtr detour) => BootstrapInterop.NativeHookAttach(target, detour); #endif - + [Obsolete("Use NativeUtils.NativeHook instead")] public static void NativeHookDetach(IntPtr target, IntPtr detour) => BootstrapInterop.NativeHookDetach(target, detour); diff --git a/MelonLoader/NativeUtils/NativeHooks.cs b/MelonLoader/NativeUtils/NativeHooks.cs new file mode 100644 index 000000000..415cb42b6 --- /dev/null +++ b/MelonLoader/NativeUtils/NativeHooks.cs @@ -0,0 +1,109 @@ +using System; +using System.CodeDom; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace MelonLoader.NativeUtils +{ + public class NativeHook where T : Delegate + { + #region Private Values + private IntPtr _targetHandle; + private IntPtr _detourHandle; + private IntPtr _trampolineHandle; + #endregion + + #region Public Properties + public IntPtr Target + { + get + { + return _targetHandle; + } + + set + { + if (value == IntPtr.Zero) + throw new ArgumentNullException("value"); + + _targetHandle = value; + } + } + + public IntPtr Detour + { + get + { + return _detourHandle; + } + + set + { + if (value == IntPtr.Zero) + throw new ArgumentNullException("value"); + + _detourHandle = value; + } + } + + public T Trampoline + { + get + { + return (T)Marshal.GetDelegateForFunctionPointer(_trampolineHandle, typeof(T)); + } + } + + public bool IsHooked { get; private set; } + #endregion + + public NativeHook() { } + + public NativeHook(IntPtr target, IntPtr detour) + { + if (target == IntPtr.Zero) + throw new ArgumentNullException("target"); + + if (detour == IntPtr.Zero) + throw new ArgumentNullException("detour"); + + _targetHandle = target; + _detourHandle = detour; + } + + public unsafe void Attach() + { + if (IsHooked) + return; + + if (_targetHandle == IntPtr.Zero) + throw new NullReferenceException("The NativeHook's target has not been set!"); + + if (_detourHandle == IntPtr.Zero) + throw new NullReferenceException("The NativeHook's detour has not been set!"); + + IntPtr trampoline = _targetHandle; + BootstrapInterop.NativeHookAttach((IntPtr)(&trampoline), _detourHandle); + _trampolineHandle = trampoline; + + IsHooked = true; + } + + public unsafe void Detach() + { + if (!IsHooked) + return; + + if (_targetHandle == IntPtr.Zero) + throw new NullReferenceException("The NativeHook's target has not been set!"); + + IntPtr original = _targetHandle; + BootstrapInterop.NativeHookDetach((IntPtr)(&original), _detourHandle); + + IsHooked= false; + _trampolineHandle = IntPtr.Zero; + } + } +} From be567435186c57f997aa4c3e9da4bb4b90e3fd45 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Thu, 30 Mar 2023 19:44:45 +0200 Subject: [PATCH 25/79] Store the trampoline after first use --- MelonLoader/NativeUtils/NativeHooks.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/MelonLoader/NativeUtils/NativeHooks.cs b/MelonLoader/NativeUtils/NativeHooks.cs index 415cb42b6..4c72e6cad 100644 --- a/MelonLoader/NativeUtils/NativeHooks.cs +++ b/MelonLoader/NativeUtils/NativeHooks.cs @@ -13,6 +13,7 @@ public class NativeHook where T : Delegate private IntPtr _targetHandle; private IntPtr _detourHandle; private IntPtr _trampolineHandle; + private T _trampoline; #endregion #region Public Properties @@ -52,7 +53,10 @@ public T Trampoline { get { - return (T)Marshal.GetDelegateForFunctionPointer(_trampolineHandle, typeof(T)); + if (_trampoline == null) + _trampoline = (T)Marshal.GetDelegateForFunctionPointer(_trampolineHandle, typeof(T)); + + return _trampoline; } } From a49879bee9ac08ef97b54befca374bd4e2dab0f0 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Tue, 4 Apr 2023 19:33:58 +0200 Subject: [PATCH 26/79] add Changelog --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0380c4d47..6afcee3ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ | Versions: | | - | +| [v0.6.1](#v061) | | [v0.6.0](#v060) | | [v0.5.7](#v057) | | [v0.5.5](#v055) | @@ -32,6 +33,17 @@ --- +### v0.6.1 + +1. Refactored Bootstrap, more informative errors +2. Updated classdata.tpk, fixing some issues when reading game versions +3. Depracated NativeHooks, giving a new utility class `MelonLoader.NativeUtils.NativeHook where T : Delegate` +4. Updated MonoMod, fixing some issues with old Mono Games +5. Fix some compatibility issues for certain Mono Games +6. Implemented some missing launch option + +--- + ### v0.6.0: 1. Added Linux Support (Credits to [RinLovesYou](https://github.com/RinLovesYou) :3) From 170df34b59ba1e8de7b11df65443a5eb3e2d6070 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Tue, 4 Apr 2023 19:34:52 +0200 Subject: [PATCH 27/79] Take MelonLoader out of Alpha --- Bootstrap/src/constants.rs | 2 +- MelonLoader/Core.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Bootstrap/src/constants.rs b/Bootstrap/src/constants.rs index 152fd6063..563ad0e7d 100644 --- a/Bootstrap/src/constants.rs +++ b/Bootstrap/src/constants.rs @@ -20,7 +20,7 @@ pub type InitFnIl2Cpp = extern "C" fn(*const c_char) -> *mut Il2CppDomain; pub const MELON_VERSION: &str = "0.6.1"; -pub const IS_ALPHA: bool = true; +pub const IS_ALPHA: bool = false; pub const RED: Color = Color::TrueColor { r: (255), diff --git a/MelonLoader/Core.cs b/MelonLoader/Core.cs index 23bbb615f..8b2a4d1d3 100644 --- a/MelonLoader/Core.cs +++ b/MelonLoader/Core.cs @@ -21,7 +21,7 @@ internal static class Core { internal static HarmonyLib.Harmony HarmonyInstance; - internal static bool Is_ALPHA_PreRelease = true; + internal static bool Is_ALPHA_PreRelease = false; internal static NativeLibrary.StringDelegate WineGetVersion; From 8978754942b1d095a4ba695c68f6f46f1f2da9d8 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Tue, 4 Apr 2023 21:51:46 +0200 Subject: [PATCH 28/79] remove once_cell feature flag, as it is now stable on nightly --- Bootstrap/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/Bootstrap/src/lib.rs b/Bootstrap/src/lib.rs index 7a8866248..b7f81a541 100755 --- a/Bootstrap/src/lib.rs +++ b/Bootstrap/src/lib.rs @@ -1,5 +1,4 @@ #![feature(is_some_and)] -#![feature(once_cell)] #![allow(non_snake_case)] #![deny( From 797f3343083c78f2bb9af440a7974683bb8d3f75 Mon Sep 17 00:00:00 2001 From: RinLovesYou Date: Tue, 4 Apr 2023 22:15:05 +0200 Subject: [PATCH 29/79] Set start screen to beta --- CHANGELOG.md | 4 ++-- Dependencies/MelonStartScreen/UI/UI_Theme.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6afcee3ed..7f0f54db6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,10 +37,10 @@ 1. Refactored Bootstrap, more informative errors 2. Updated classdata.tpk, fixing some issues when reading game versions -3. Depracated NativeHooks, giving a new utility class `MelonLoader.NativeUtils.NativeHook where T : Delegate` +3. Deprecated NativeHooks, giving a new utility class `MelonLoader.NativeUtils.NativeHook where T : Delegate` 4. Updated MonoMod, fixing some issues with old Mono Games 5. Fix some compatibility issues for certain Mono Games -6. Implemented some missing launch option +6. Implemented some missing launch options --- diff --git a/Dependencies/MelonStartScreen/UI/UI_Theme.cs b/Dependencies/MelonStartScreen/UI/UI_Theme.cs index bab7c1130..d70733222 100644 --- a/Dependencies/MelonStartScreen/UI/UI_Theme.cs +++ b/Dependencies/MelonStartScreen/UI/UI_Theme.cs @@ -179,7 +179,7 @@ public void Defaults() internal class VersionTextSettings : TextSettings { - internal bool Is_ALPHA_PreRelease = true; + internal bool Is_ALPHA_PreRelease = false; public VersionTextSettings() => Defaults(); public void Defaults() { From e562cf0f2c331ff64cb8b761ec28c8ebf8dcaec2 Mon Sep 17 00:00:00 2001 From: Xander Date: Sat, 25 Feb 2023 15:14:38 -0600 Subject: [PATCH 30/79] Added new MelonAdditionalCredits Attribute For if a mod author might want to credit someone or something else when the mod is loaded. Used a separate attribute instead of MelonInfo to not break backwards compatibility. --- .../MelonAdditionalCreditsAttribute.cs | 19 +++++++++++++++++++ MelonLoader/Melons/MelonAssembly.cs | 2 ++ MelonLoader/Melons/MelonBase.cs | 9 +++++++-- MelonLoader/Utils/MelonLogger.cs | 8 +++++++- 4 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 MelonLoader/Attributes/MelonAdditionalCreditsAttribute.cs diff --git a/MelonLoader/Attributes/MelonAdditionalCreditsAttribute.cs b/MelonLoader/Attributes/MelonAdditionalCreditsAttribute.cs new file mode 100644 index 000000000..5af34752e --- /dev/null +++ b/MelonLoader/Attributes/MelonAdditionalCreditsAttribute.cs @@ -0,0 +1,19 @@ +using System; + +namespace MelonLoader { + [AttributeUsage(AttributeTargets.Assembly)] + public class MelonAdditionalCreditsAttribute : Attribute { + ///