diff --git a/Cargo.lock b/Cargo.lock index 15d9bc3..03ce670 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -167,9 +167,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alsa" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed7572b7ba83a31e20d1b48970ee402d2e3e0537dcfe0a3ff4d6eb7508617d43" +checksum = "7c88dbbce13b232b26250e1e2e6ac18b6a891a646b8148285036ebce260ac5c3" dependencies = [ "alsa-sys", "bitflags 2.8.0", @@ -201,7 +201,7 @@ dependencies = [ "jni-sys", "libc", "log", - "ndk 0.9.0", + "ndk", "ndk-context", "ndk-sys 0.6.0+11769913", "num_enum", @@ -647,7 +647,7 @@ dependencies = [ "flate2", "impl-trait-for-tuples", "indexmap", - "itertools 0.14.0", + "itertools", "lz4", "mimalloc", "num-traits", @@ -699,18 +699,16 @@ dependencies = [ "egui_extras", "hound", "image", - "itertools 0.14.0", + "itertools", "mimalloc", "rfd", "rodio", - "serde_json", "three-d", "three-d-asset", "tokio", "wasm-bindgen-futures", "web-sys", "windows 0.62.2", - "winreg", "winres", ] @@ -728,31 +726,13 @@ name = "bilge-impl" version = "0.3.0" source = "git+https://github.com/widberg/bilge.git?branch=schemars#f0c43f988f902600d2bc1db4bb9ee240b5610c5c" dependencies = [ - "itertools 0.14.0", + "itertools", "proc-macro-error2", "proc-macro2", "quote", "syn 2.0.98", ] -[[package]] -name = "bindgen" -version = "0.72.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" -dependencies = [ - "bitflags 2.8.0", - "cexpr", - "clang-sys", - "itertools 0.13.0", - "proc-macro2", - "quote", - "regex", - "rustc-hash 2.1.1", - "shlex", - "syn 2.0.98", -] - [[package]] name = "binrw" version = "0.16.0-pre" @@ -942,15 +922,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - [[package]] name = "cfg-if" version = "1.0.0" @@ -988,22 +959,11 @@ dependencies = [ "num-traits", ] -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading", -] - [[package]] name = "clap" -version = "4.5.28" +version = "4.5.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e77c3243bd94243c03672cb5154667347c457ca271254724f9f393aee1c05ff" +checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" dependencies = [ "clap_builder", "clap_derive", @@ -1011,9 +971,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.27" +version = "4.5.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" +checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" dependencies = [ "anstream", "anstyle", @@ -1023,9 +983,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.28" +version = "4.5.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" +checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" dependencies = [ "heck", "proc-macro2", @@ -1035,9 +995,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.4" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" [[package]] name = "clipboard-win" @@ -1220,45 +1180,46 @@ dependencies = [ [[package]] name = "coreaudio-rs" -version = "0.11.3" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "321077172d79c662f64f5071a03120748d5bb652f5231570141be24cfcd2bace" +checksum = "1aae284fbaf7d27aa0e292f7677dfbe26503b0d555026f702940805a630eac17" dependencies = [ "bitflags 1.3.2", - "core-foundation-sys", - "coreaudio-sys", -] - -[[package]] -name = "coreaudio-sys" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceec7a6067e62d6f931a2baf6f3a751f4a892595bcec1461a3c94ef9949864b6" -dependencies = [ - "bindgen", + "libc", + "objc2-audio-toolbox", + "objc2-core-audio", + "objc2-core-audio-types", + "objc2-core-foundation", ] [[package]] name = "cpal" -version = "0.15.3" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "873dab07c8f743075e57f524c583985fbaf745602acbe916a01539364369a779" +checksum = "5b1f9c7312f19fc2fa12fd7acaf38de54e8320ba10d1a02dcbe21038def51ccb" dependencies = [ "alsa", - "core-foundation-sys", "coreaudio-rs", "dasp_sample", "jni", "js-sys", "libc", "mach2", - "ndk 0.8.0", + "ndk", "ndk-context", - "oboe", + "num-derive", + "num-traits", + "objc2 0.6.3", + "objc2-audio-toolbox", + "objc2-avf-audio", + "objc2-core-audio", + "objc2-core-audio-types", + "objc2-core-foundation", + "objc2-foundation 0.3.0", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows 0.54.0", + "windows 0.62.2", ] [[package]] @@ -1774,7 +1735,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -2023,12 +1984,6 @@ dependencies = [ "xml-rs", ] -[[package]] -name = "glob" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" - [[package]] name = "gloo-timers" version = "0.3.0" @@ -2500,15 +2455,6 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.14.0" @@ -2604,7 +2550,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -2698,9 +2644,9 @@ dependencies = [ [[package]] name = "mach2" -version = "0.4.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" +checksum = "6a1b95cd5421ec55b445b5ae102f5ea0e768de1f82bd3001e11f426c269c3aea" dependencies = [ "libc", ] @@ -2778,12 +2724,6 @@ dependencies = [ "unicase", ] -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - [[package]] name = "miniz_oxide" version = "0.8.8" @@ -2815,20 +2755,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "ndk" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" -dependencies = [ - "bitflags 2.8.0", - "jni-sys", - "log", - "ndk-sys 0.5.0+25.2.9519653", - "num_enum", - "thiserror 1.0.69", -] - [[package]] name = "ndk" version = "0.9.0" @@ -2888,13 +2814,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" [[package]] -name = "nom" -version = "7.1.3" +name = "num-bigint" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ - "memchr", - "minimal-lexical", + "num-integer", + "num-traits", ] [[package]] @@ -2914,6 +2840,26 @@ dependencies = [ "syn 2.0.98", ] +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -3016,6 +2962,31 @@ dependencies = [ "objc2-foundation 0.3.0", ] +[[package]] +name = "objc2-audio-toolbox" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6c2736f2e27584ca5c75640186542e772ce550a3c5182fc358526dea242c068" +dependencies = [ + "bitflags 2.8.0", + "libc", + "objc2 0.6.3", + "objc2-core-audio", + "objc2-core-audio-types", + "objc2-core-foundation", + "objc2-foundation 0.3.0", +] + +[[package]] +name = "objc2-avf-audio" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fe5d8f6b9904a2028e9168bbdc77d06f944a39a0aef7076857db09f1298c9a" +dependencies = [ + "objc2 0.6.3", + "objc2-foundation 0.3.0", +] + [[package]] name = "objc2-cloud-kit" version = "0.2.2" @@ -3040,6 +3011,28 @@ dependencies = [ "objc2-foundation 0.2.2", ] +[[package]] +name = "objc2-core-audio" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38591d29e45e81cb27870334467361ed788e7cfad7f85c1d30b1b6263fff3c2a" +dependencies = [ + "objc2 0.6.3", + "objc2-core-audio-types", + "objc2-core-foundation", + "objc2-foundation 0.3.0", +] + +[[package]] +name = "objc2-core-audio-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a89f2ec274a0cf4a32642b2991e8b351a404d290da87bb6a9a9d8632490bd1c" +dependencies = [ + "bitflags 2.8.0", + "objc2 0.6.3", +] + [[package]] name = "objc2-core-data" version = "0.2.2" @@ -3059,6 +3052,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daeaf60f25471d26948a1c2f840e3f7d86f4109e3af4e8e4b5cd70c39690d925" dependencies = [ "bitflags 2.8.0", + "block2 0.6.2", + "libc", "objc2 0.6.3", ] @@ -3112,6 +3107,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a21c6c9014b82c39515db5b396f91645182611c97d24637cf56ac01e5f8d998" dependencies = [ "bitflags 2.8.0", + "block2 0.6.2", + "libc", "objc2 0.6.3", "objc2-core-foundation", ] @@ -3217,29 +3214,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "oboe" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8b61bebd49e5d43f5f8cc7ee2891c16e0f41ec7954d36bcb6c14c5e0de867fb" -dependencies = [ - "jni", - "ndk 0.8.0", - "ndk-context", - "num-derive", - "num-traits", - "oboe-sys", -] - -[[package]] -name = "oboe-sys" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bb09a4a2b1d668170cfe0a7d5bc103f8999fb316c98099b6a9939c9f2e79d" -dependencies = [ - "cc", -] - [[package]] name = "once_cell" version = "1.20.3" @@ -3696,12 +3670,14 @@ dependencies = [ [[package]] name = "rodio" -version = "0.20.1" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ceb6607dd738c99bc8cb28eff249b7cd5c8ec88b9db96c0608c1480d140fb1" +checksum = "e0e2319f90472c9934314a93df9f7cf81d48cebb6e8b8536a1da7f4dc4a15125" dependencies = [ "cpal", - "hound", + "dasp_sample", + "num-rational", + "thiserror 2.0.11", ] [[package]] @@ -3744,7 +3720,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -4116,7 +4092,7 @@ dependencies = [ "getrandom 0.3.1", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -4849,7 +4825,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -4868,16 +4844,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows" -version = "0.54.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" -dependencies = [ - "windows-core 0.54.0", - "windows-targets 0.52.6", -] - [[package]] name = "windows" version = "0.58.0" @@ -4918,16 +4884,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-core" -version = "0.54.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" -dependencies = [ - "windows-result 0.1.2", - "windows-targets 0.52.6", -] - [[package]] name = "windows-core" version = "0.58.0" @@ -5044,15 +5000,6 @@ dependencies = [ "windows-link 0.2.1", ] -[[package]] -name = "windows-result" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-result" version = "0.2.0" @@ -5426,7 +5373,7 @@ dependencies = [ "js-sys", "libc", "memmap2", - "ndk 0.9.0", + "ndk", "objc2 0.5.2", "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", @@ -5465,16 +5412,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.55.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" -dependencies = [ - "cfg-if", - "windows-sys 0.59.0", -] - [[package]] name = "winres" version = "0.1.12" diff --git a/Cargo.toml b/Cargo.toml index 05b3cd6..de1a68e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] resolver = "2" members = ["bff", "bff-cli", "bff-derive", "bff-gui"] +default-members = ["bff", "bff-cli", "bff-derive"] [workspace.package] version = "0.1.0" @@ -40,5 +41,5 @@ codegen-units = 1 panic = "abort" [profile.dev] -opt-level = 3 +opt-level = 0 debug = 0 diff --git a/bff-cli/Cargo.toml b/bff-cli/Cargo.toml index 8ec6441..ae7a5ec 100644 --- a/bff-cli/Cargo.toml +++ b/bff-cli/Cargo.toml @@ -9,7 +9,7 @@ rust-version.workspace = true [dependencies] bff = { path = "../bff" } -clap = { version = "4.2.4", features = ["derive"] } +clap = { version = "4.5.60", features = ["derive"] } derive_more = { version = "2.0.1", features = ["full"] } indicatif = "0.18.0" mimalloc = { version = "0.1.48", features = ["v3"] } diff --git a/bff-gui/Cargo.toml b/bff-gui/Cargo.toml index 7121700..480d415 100644 --- a/bff-gui/Cargo.toml +++ b/bff-gui/Cargo.toml @@ -9,7 +9,7 @@ rust-version.workspace = true [dependencies] bff = { path = "../bff" } -clap = { version = "4.2.4", features = ["derive"] } +clap = { version = "4.5.60", features = ["derive"] } derive_more = { version = "2.0.1", features = ["full"] } eframe = "0.29.1" egui = "0.29.1" # needs to be old to match three-d @@ -24,16 +24,16 @@ image = { version = "0.25.5", features = [ ], default-features = false } itertools = "0.14.0" rfd = { version = "0.17.2", features = ["file-handle-inner"] } -rodio = { version = "0.20.1", features = ["wav"], default-features = false } -serde_json = "1.0.107" +rodio = { version = "0.22.1", features = ["playback"], default-features = false } three-d = { version = "0.18.2", default-features = false, features = [ "egui_glow", ] } three-d-asset = "0.9.2" [target.'cfg(target_os = "windows")'.dependencies] -winreg = "0.55.0" windows = { version = "0.62.2", features = [ + "Win32_Foundation", + "Win32_System_Registry", "Win32_UI_Shell", ], default-features = false } diff --git a/bff-gui/src/helpers/sound.rs b/bff-gui/src/helpers/sound.rs index 6a2a6ca..6cb9b6d 100644 --- a/bff-gui/src/helpers/sound.rs +++ b/bff-gui/src/helpers/sound.rs @@ -1,30 +1,17 @@ -use std::io::Cursor; use std::sync::Arc; pub fn play_sound(data: Arc>, sample_rate: u32, channels: u16, volume: f32) { std::thread::spawn(move || { - let spec = hound::WavSpec { - channels, - sample_rate, - bits_per_sample: 16, - sample_format: hound::SampleFormat::Int, + let Some(channel_count) = std::num::NonZeroU16::new(channels) else { + return; }; - - let mut bytes = Vec::new(); - let mut write_cursor = Cursor::new(&mut bytes); - let mut parent_writer = hound::WavWriter::new(&mut write_cursor, spec).unwrap(); - let mut sample_writer = parent_writer.get_i16_writer(data.len() as u32); - - for sample in data.iter() { - sample_writer.write_sample(*sample); - } - sample_writer.flush().unwrap(); - parent_writer.finalize().unwrap(); - - let (_stream, stream_handle) = rodio::OutputStream::try_default().unwrap(); - let sink = rodio::Sink::try_new(&stream_handle).unwrap(); - let buf = std::io::BufReader::new(Cursor::new(bytes)); - let source = rodio::Decoder::new_wav(buf).unwrap(); + let Some(sample_rate) = std::num::NonZeroU32::new(sample_rate) else { + return; + }; + let samples: Vec = data.iter().map(|sample| f32::from(*sample) / 32768.0).collect(); + let stream = rodio::DeviceSinkBuilder::open_default_sink().unwrap(); + let sink = rodio::Player::connect_new(stream.mixer()); + let source = rodio::buffer::SamplesBuffer::new(channel_count, sample_rate, samples); sink.set_volume(volume); sink.append(source); sink.sleep_until_end(); diff --git a/bff-gui/src/main.rs b/bff-gui/src/main.rs index c3322bb..4464daf 100644 --- a/bff-gui/src/main.rs +++ b/bff-gui/src/main.rs @@ -104,6 +104,198 @@ fn main() -> BffGuiResult<()> { #[cfg(target_os = "windows")] const PROG_ID: &str = "Widberg.BFF.1"; +#[cfg(target_os = "windows")] +mod registry { + use std::io::{Error, ErrorKind, Result}; + + use windows::core::PCWSTR; + use windows::Win32::Foundation::{ + ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND, ERROR_SUCCESS, WIN32_ERROR, + }; + use windows::Win32::System::Registry::{ + HKEY, KEY_READ, KEY_WRITE, REG_OPTION_NON_VOLATILE, REG_ROUTINE_FLAGS, REG_SAM_FLAGS, + REG_SZ, RRF_RT_REG_SZ, RegCloseKey, RegCreateKeyExW, RegDeleteTreeW, RegDeleteValueW, + RegGetValueW, RegOpenKeyExW, RegSetValueExW, + }; + + pub struct Key(HKEY); + + impl Key { + pub fn raw(&self) -> HKEY { + self.0 + } + } + + impl Drop for Key { + fn drop(&mut self) { + unsafe { + let _ = RegCloseKey(self.0); + } + } + } + + fn is_not_found(status: WIN32_ERROR) -> bool { + status == ERROR_FILE_NOT_FOUND || status == ERROR_PATH_NOT_FOUND + } + + fn win32_error(status: WIN32_ERROR) -> Error { + Error::from_raw_os_error(status.0 as i32) + } + + fn status_to_result(status: WIN32_ERROR) -> Result<()> { + if status == ERROR_SUCCESS { + Ok(()) + } else { + Err(win32_error(status)) + } + } + + fn widestr(value: &str) -> Vec { + value.encode_utf16().chain(std::iter::once(0)).collect() + } + + pub fn open_subkey(parent: HKEY, subkey: &str, access: REG_SAM_FLAGS) -> Result { + let mut handle = HKEY::default(); + let subkey = widestr(subkey); + unsafe { + status_to_result(RegOpenKeyExW( + parent, + PCWSTR(subkey.as_ptr()), + None, + access, + &mut handle, + ))?; + } + Ok(Key(handle)) + } + + pub fn create_subkey(parent: HKEY, subkey: &str) -> Result { + let mut handle = HKEY::default(); + let subkey = widestr(subkey); + unsafe { + status_to_result(RegCreateKeyExW( + parent, + PCWSTR(subkey.as_ptr()), + None, + PCWSTR::null(), + REG_OPTION_NON_VOLATILE, + KEY_READ | KEY_WRITE, + None, + &mut handle, + None, + ))?; + } + Ok(Key(handle)) + } + + pub fn set_default_value_sz(key: HKEY, value: &str) -> Result<()> { + set_value_sz(key, None, value) + } + + pub fn set_value_sz(key: HKEY, name: Option<&str>, value: &str) -> Result<()> { + let value = widestr(value); + let value_bytes = unsafe { + std::slice::from_raw_parts(value.as_ptr() as *const u8, std::mem::size_of_val(value.as_slice())) + }; + unsafe { + match name { + Some(name) => { + let name = widestr(name); + status_to_result(RegSetValueExW( + key, + PCWSTR(name.as_ptr()), + None, + REG_SZ, + Some(value_bytes), + )) + } + None => status_to_result(RegSetValueExW( + key, + PCWSTR::null(), + None, + REG_SZ, + Some(value_bytes), + )), + } + } + } + + pub fn get_default_value_sz(key: HKEY) -> Result> { + let mut data_len = 0u32; + let status = unsafe { + RegGetValueW( + key, + PCWSTR::null(), + PCWSTR::null(), + REG_ROUTINE_FLAGS(RRF_RT_REG_SZ.0), + None, + None, + Some(&mut data_len), + ) + }; + if is_not_found(status) { + return Ok(None); + } + status_to_result(status)?; + + if data_len == 0 { + return Ok(Some(String::new())); + } + + let mut data = vec![0u8; data_len as usize]; + let status = unsafe { + RegGetValueW( + key, + PCWSTR::null(), + PCWSTR::null(), + REG_ROUTINE_FLAGS(RRF_RT_REG_SZ.0), + None, + Some(data.as_mut_ptr() as *mut core::ffi::c_void), + Some(&mut data_len), + ) + }; + status_to_result(status)?; + + let wide_len = (data_len as usize) / std::mem::size_of::(); + let wide = unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u16, wide_len) }; + let value_len = wide.iter().position(|c| *c == 0).unwrap_or(wide.len()); + String::from_utf16(&wide[..value_len]) + .map(Some) + .map_err(|error| Error::new(ErrorKind::InvalidData, error)) + } + + pub fn delete_tree_if_exists(parent: HKEY, subkey: &str) -> Result<()> { + let subkey = widestr(subkey); + let status = unsafe { RegDeleteTreeW(parent, PCWSTR(subkey.as_ptr())) }; + if is_not_found(status) { + Ok(()) + } else { + status_to_result(status) + } + } + + pub fn delete_default_value_if_exists(key: HKEY) -> Result<()> { + delete_value_if_exists(key, None) + } + + pub fn delete_value_if_exists(key: HKEY, name: Option<&str>) -> Result<()> { + let status = unsafe { + match name { + Some(name) => { + let name = widestr(name); + RegDeleteValueW(key, PCWSTR(name.as_ptr())) + } + None => RegDeleteValueW(key, PCWSTR::null()), + } + }; + if is_not_found(status) { + Ok(()) + } else { + status_to_result(status) + } + } +} + #[cfg(target_os = "windows")] fn change_notify() { use windows::Win32::UI::Shell::{SHCNE_ASSOCCHANGED, SHCNF_IDLIST, SHChangeNotify}; @@ -116,26 +308,22 @@ fn change_notify() { #[cfg(target_os = "windows")] fn install() -> BffGuiResult<()> { use std::env::current_exe; - - use winreg::RegKey; - use winreg::enums::HKEY_CURRENT_USER; + use windows::Win32::System::Registry::HKEY_CURRENT_USER; let exe_path = current_exe()?.to_str().unwrap_or_default().to_owned(); - let hkcu = RegKey::predef(HKEY_CURRENT_USER); - let classes = hkcu.open_subkey("Software\\Classes")?; - - let (prog, _) = classes.create_subkey(PROG_ID)?; - prog.set_value("", &TITLE)?; - let (command, _) = prog.create_subkey("Shell\\Open\\Command")?; - command.set_value("", &format!(r#""{}" "%1""#, exe_path))?; + let classes = registry::create_subkey(HKEY_CURRENT_USER, "Software\\Classes")?; + let prog = registry::create_subkey(classes.raw(), PROG_ID)?; + registry::set_default_value_sz(prog.raw(), TITLE)?; + let command = registry::create_subkey(prog.raw(), "Shell\\Open\\Command")?; + registry::set_default_value_sz(command.raw(), &format!(r#""{}" "%1""#, exe_path))?; for extension in bff::bigfile::platforms::extensions() { - let (extension_key, _) = - classes.create_subkey(format!(".{}", extension.to_string_lossy()))?; - extension_key.set_value("", &PROG_ID)?; - let (open_with, _) = extension_key.create_subkey("OpenWithProgids")?; - open_with.set_value(PROG_ID, &"")?; + let extension_key = + registry::create_subkey(classes.raw(), &format!(".{}", extension.to_string_lossy()))?; + registry::set_default_value_sz(extension_key.raw(), PROG_ID)?; + let open_with = registry::create_subkey(extension_key.raw(), "OpenWithProgids")?; + registry::set_value_sz(open_with.raw(), Some(PROG_ID), "")?; } change_notify(); @@ -145,27 +333,26 @@ fn install() -> BffGuiResult<()> { #[cfg(target_os = "windows")] fn uninstall() -> BffGuiResult<()> { - use winreg::RegKey; - use winreg::enums::{HKEY_CURRENT_USER, KEY_ALL_ACCESS}; + use windows::Win32::System::Registry::{HKEY_CURRENT_USER, KEY_READ, KEY_WRITE}; - let hkcu = RegKey::predef(HKEY_CURRENT_USER); - let classes = hkcu.open_subkey("Software\\Classes")?; - - let _ = classes.delete_subkey_all(PROG_ID); + let classes = registry::open_subkey(HKEY_CURRENT_USER, "Software\\Classes", KEY_READ | KEY_WRITE)?; + registry::delete_tree_if_exists(classes.raw(), PROG_ID)?; for extension in bff::bigfile::platforms::extensions() { - if let Ok(default) = classes - .open_subkey_with_flags(format!(".{}", extension.to_string_lossy()), KEY_ALL_ACCESS) + let extension_key_name = format!(".{}", extension.to_string_lossy()); + if let Ok(default) = + registry::open_subkey(classes.raw(), &extension_key_name, KEY_READ | KEY_WRITE) { - if let Ok(prog_id) = default.get_value::("") { + if let Some(prog_id) = registry::get_default_value_sz(default.raw())? { if prog_id == PROG_ID { - default.delete_value("")?; + registry::delete_default_value_if_exists(default.raw())?; } } - if let Ok(open_with) = default.open_subkey_with_flags("OpenWithProgids", KEY_ALL_ACCESS) + if let Ok(open_with) = + registry::open_subkey(default.raw(), "OpenWithProgids", KEY_READ | KEY_WRITE) { - let _ = open_with.delete_value(PROG_ID); + registry::delete_value_if_exists(open_with.raw(), Some(PROG_ID))?; } } }