diff --git a/Cargo.lock b/Cargo.lock index 36a28ce..a8f3a6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,10 +3,44 @@ version = 3 [[package]] -name = "anyhow" -version = "1.0.76" +name = "aho-corasick" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59d2a3357dde987206219e78ecfbbb6e8dad06cbb65292758d3270e6254f7355" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "aimeio-yubideck" +version = "0.1.0" +dependencies = [ + "cfg_aliases", + "faster-hex", + "lazy_static", + "log", + "rand", + "shared_memory", + "winapi", + "yubideck-common", +] + +[[package]] +name = "anyhow" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca87830a3e3fb156dc96cfbd31cb620265dd053be734723f22b760d6cc3c3051" + +[[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" @@ -20,6 +54,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + [[package]] name = "cc" version = "1.0.83" @@ -29,6 +69,12 @@ dependencies = [ "libc", ] +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + [[package]] name = "cfg-if" version = "1.0.0" @@ -46,13 +92,53 @@ name = "chuniio-yubideck" version = "0.1.0" dependencies = [ "anyhow", + "cfg-if 1.0.0", "cfg_aliases", - "env_logger", "lazy_static", "log", "rusb", "shared_memory", "winapi", + "yubideck-common", +] + +[[package]] +name = "clap" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +dependencies = [ + "atty", + "bitflags 1.3.2", + "clap_derive", + "clap_lex", + "indexmap", + "once_cell", + "strsim", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap_derive" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", ] [[package]] @@ -61,7 +147,95 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" dependencies = [ + "humantime", + "is-terminal", "log", + "regex", + "termcolor", +] + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "faster-hex" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2a2b11eda1d40935b26cf18f6833c526845ae8c41e58d09af6adeb6f0269183" +dependencies = [ + "serde", +] + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "is-terminal" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" +dependencies = [ + "hermit-abi 0.3.3", + "rustix", + "windows-sys", ] [[package]] @@ -88,12 +262,24 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + [[package]] name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + [[package]] name = "memoffset" version = "0.6.5" @@ -109,19 +295,162 @@ version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cc", - "cfg-if", + "cfg-if 1.0.0", "libc", "memoffset", ] +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.4.1", + "cfg-if 1.0.0", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" + [[package]] name = "pkg-config" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dd5e8a1f1029c43224ad5898e50140c2aebb1705f19e67c918ebf5b9e797fe1" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a37c9326af5ed140c86a46655b5278de879853be5573c01df185b6f49a580a" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "raw_sync" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a34bde3561f980a51c70495164200569a11662644fe5af017f0b5d7015688cc" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "nix 0.27.1", + "rand", + "winapi", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + [[package]] name = "rusb" version = "0.9.3" @@ -132,22 +461,120 @@ dependencies = [ "libusb1-sys", ] +[[package]] +name = "rustix" +version = "0.38.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "serde" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.45", +] + [[package]] name = "shared_memory" version = "0.12.4-chuniio" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", + "clap", + "env_logger", "libc", - "nix", + "log", + "nix 0.23.2", + "raw_sync", "win-sys", ] +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eae3c679c56dc214320b67a1bc04ef3dfbd6411f6443974b5e4893231298e66" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "win-sys" version = "0.3.1" @@ -173,6 +600,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -185,39 +621,116 @@ version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45296b64204227616fdbf2614cefa4c236b98ee64dfaaaa435207ed99fe7829f" dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", + "windows_aarch64_msvc 0.34.0", + "windows_i686_gnu 0.34.0", + "windows_i686_msvc 0.34.0", + "windows_x86_64_gnu 0.34.0", + "windows_x86_64_msvc 0.34.0", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm", + "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", + "windows_x86_64_msvc 0.52.0", +] + +[[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.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" +[[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.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" +[[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.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" +[[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.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" +[[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.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "yubideck-common" +version = "0.1.0" +dependencies = [ + "anyhow", + "env_logger", + "log", + "shared_memory", + "winapi", +] diff --git a/Cargo.toml b/Cargo.toml index c7c1b0b..20e1550 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,6 @@ -[package] -name = "chuniio-yubideck" -version = "0.1.0" -edition = "2021" - -[lib] -crate-type = ["cdylib"] +[workspace] +members = ["aimeio", "chuniio", "common"] +resolver = "2" [profile.release] strip = true # Automatically strip symbols from the binary. @@ -13,18 +9,9 @@ lto = true codegen-units = 1 panic = "abort" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[features] -chusan = [] - -[dependencies] +[workspace.dependencies] anyhow = "1.0.76" -env_logger = { version = "0.10.1", default-features = false } lazy_static = "1.4.0" log = "0.4.20" -rusb = "0.9.3" shared_memory = { path = "vendor/shared_memory" } -winapi = { version = "0.3.9", features = ["minwindef", "winnt", "winerror", "debugapi", "winbase", "winuser"] } - -[build-dependencies] -cfg_aliases = "0.2.0" +winapi = "0.3.9" diff --git a/aimeio/Cargo.toml b/aimeio/Cargo.toml new file mode 100644 index 0000000..e4731a0 --- /dev/null +++ b/aimeio/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "aimeio-yubideck" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +chusan = [] + +[dependencies] +faster-hex = "0.9.0" +lazy_static = { workspace = true } +log = { workspace = true } +rand = "0.8.5" +shared_memory = { workspace = true } +winapi = { workspace = true, features = ["winbase", "winerror", "winnt", "winuser"] } +yubideck-common = { path = "../common" } + +[build-dependencies] +cfg_aliases = "0.2.0" diff --git a/aimeio/src/configuration.rs b/aimeio/src/configuration.rs new file mode 100644 index 0000000..7dd1e68 --- /dev/null +++ b/aimeio/src/configuration.rs @@ -0,0 +1,10 @@ +use std::path::PathBuf; + +#[derive(Debug, Clone)] +pub struct Configuration { + pub aime_path: PathBuf, + pub felica_path: PathBuf, + pub aime_gen: bool, + pub felica_gen: bool, + pub vk_scan: u32, +} diff --git a/aimeio/src/lib.rs b/aimeio/src/lib.rs new file mode 100644 index 0000000..7baffa8 --- /dev/null +++ b/aimeio/src/lib.rs @@ -0,0 +1,406 @@ +#![allow(clippy::missing_safety_doc)] + +mod configuration; + +use std::{ + ffi::{c_char, CStr, CString}, + path::PathBuf, + rc::Rc, + sync::{ + atomic::{AtomicBool, Ordering}, + Mutex, + }, +}; + +use configuration::Configuration; +use log::error; +use shared_memory::Shmem; +use winapi::{ + shared::{ + minwindef::{BOOL, DWORD, HINSTANCE, LPVOID, MAX_PATH, TRUE}, + winerror::{E_FAIL, S_FALSE, S_OK}, + }, + um::{ + winbase::{GetPrivateProfileIntA, GetPrivateProfileStringA}, + winnt::{DLL_PROCESS_ATTACH, HRESULT}, + winuser::{GetAsyncKeyState, VK_RETURN}, + }, +}; +use yubideck_common::{create_shared_memory, init_logger, INPUT_SHMEM_SIZE, OUTPUT_SHMEM_SIZE}; + +static mut INPUT_SHMEM: Option> = None; +static mut OUTPUT_SHMEM: Option>> = None; + +static AIME_ID_PRESENT: AtomicBool = AtomicBool::new(false); +static FELICA_ID_PRESENT: AtomicBool = AtomicBool::new(false); +static mut REMOTE_CARD_ID: [u8; 10] = [0u8; 10]; + +lazy_static::lazy_static! { + static ref CONFIGURATION: Configuration = { + let aime = CString::new("aime").unwrap(); + + let aime_path = CString::new("aimePath").unwrap(); + let aime_path_default = CString::new("DEVICE\\aime.txt").unwrap(); + + let felica_path = CString::new("felicaPath").unwrap(); + let felica_path_default = CString::new("DEVICE\\felica.txt").unwrap(); + + let felica_gen = CString::new("felicaGen").unwrap(); + let aime_gen = CString::new("aimeGen").unwrap(); + + let scan = CString::new("scan").unwrap(); + + let cfg_file = CString::new(".\\segatools.ini").unwrap(); + + let mut aime_path_buf: [c_char; MAX_PATH] = [0; MAX_PATH]; + let mut felica_path_buf: [c_char; MAX_PATH] = [0; MAX_PATH]; + + unsafe { + GetPrivateProfileStringA( + aime.as_ptr(), + aime_path.as_ptr(), + aime_path_default.as_ptr(), + aime_path_buf.as_mut_ptr(), + MAX_PATH as u32, + cfg_file.as_ptr() + ); + GetPrivateProfileStringA( + aime.as_ptr(), + felica_path.as_ptr(), + felica_path_default.as_ptr(), + felica_path_buf.as_mut_ptr(), + MAX_PATH as u32, + cfg_file.as_ptr() + ); + + Configuration { + aime_path: CStr::from_ptr(aime_path_buf.as_ptr()).to_str().unwrap().into(), + felica_path: CStr::from_ptr(felica_path_buf.as_ptr()).to_str().unwrap().into(), + aime_gen: GetPrivateProfileIntA( + aime.as_ptr(), + aime_gen.as_ptr(), + 1, + cfg_file.as_ptr() + ) == 1, + felica_gen: GetPrivateProfileIntA( + aime.as_ptr(), + felica_gen.as_ptr(), + 1, + cfg_file.as_ptr() + ) == 1, + vk_scan: GetPrivateProfileIntA( + aime.as_ptr(), + scan.as_ptr(), + VK_RETURN, + cfg_file.as_ptr() + ), + } + } + }; +} + +#[no_mangle] +extern "system" fn DllMain(_dll_module: HINSTANCE, call_reason: DWORD, _reserved: LPVOID) -> BOOL { + if call_reason == DLL_PROCESS_ATTACH { + init_logger() + } + + TRUE +} + +/// Get the version of the Aime IO API that this DLL supports. This function +/// should return a positive 16-bit integer, where the high byte is the major +/// version and the low byte is the minor version (as defined by the Semantic +/// Versioning standard). + +/// The latest API version as of this writing is 0x0100. +#[no_mangle] +pub extern "C" fn aime_io_get_api_version() -> u16 { + 0x0100 +} + +/// Initialize Aime IO provider DLL. Only called once, before any other +/// functions exported from this DLL are called (except for +/// aime_io_get_api_version). +/// +/// Minimum API version: 0x0100 +#[no_mangle] +pub extern "C" fn aime_io_init() -> HRESULT { + match create_shared_memory("YubideckInput", INPUT_SHMEM_SIZE, false) { + Ok(s) => unsafe { INPUT_SHMEM = Some(Rc::new(s)) }, + Err(e) => { + error!("Could not acquire shared input memory: {e:#?}"); + return E_FAIL; + } + } + + match create_shared_memory("YubideckOutput", OUTPUT_SHMEM_SIZE, false) { + Ok(s) => { + unsafe { OUTPUT_SHMEM = Some(Rc::new(Mutex::new(s))) }; + S_OK + } + Err(e) => { + error!("Could not obtain shared output memory: {e:#?}"); + E_FAIL + } + } +} + +fn aime_io_read_id_file(path: &PathBuf) -> Option<[u8; N]> { + let min_length = N * 2; + let id_string = match std::fs::read_to_string(path) { + Ok(s) => s, + Err(_) => return None, + }; + + if id_string.len() < min_length { + return None; + } + + let mut out = [0u8; N]; + + if faster_hex::hex_decode(id_string[0..min_length].as_bytes(), &mut out).is_err() { + return None; + } + + Some(out) +} + +/// # IMPORTANT +/// This is not how Aime access codes are created! This is literally +/// just 10 random bytes! Do not use this for anything serious! +fn aime_io_generate_aime(path: &PathBuf) -> Option<[u8; 10]> { + let mut access_code = [0u8; 10]; + + loop { + for nib in access_code.iter_mut() { + *nib = ((rand::random::() % 10) << 4) | (rand::random::() % 10); + } + + if access_code[0] >> 4 != 3 { + break; + } + } + + let mut encoded_ac = [0u8; 20]; + + if let Err(e) = faster_hex::hex_encode(&access_code, &mut encoded_ac) { + error!("Encoding access code failed: {e:#?}"); + return None; + } + + if let Err(e) = std::fs::write(path, encoded_ac) { + error!("Could not write generated access code to file: {e:#?}"); + return None; + } + + Some(access_code) +} + +/// # IMPORTANT +/// This is not how FeliCa IDms are created! This is literally +/// just 8 random bytes! Do not use this for anything serious! +fn aime_io_generate_felica(path: &PathBuf) -> Option<[u8; 8]> { + let mut idm = [0u8; 8]; + + for nib in idm.iter_mut() { + *nib = rand::random::(); + } + + // FeliCa IDm values should have a 0 in their high nibble. I think. + idm[0] &= 0x0F; + + let mut encoded_idm = [0u8; 16]; + + if let Err(e) = faster_hex::hex_encode(&idm, &mut encoded_idm) { + error!("Encoding Felica IDm failed: {e:#?}"); + return None; + } + + if let Err(e) = std::fs::write(path, encoded_idm) { + error!("Could not write generated FeliCa IDm to file: {e:#?}"); + return None; + } + + Some(idm) +} + +/// Poll for IC cards in the vicinity. +/// +/// - unit_no: Always 0 as of the current API version +/// +/// Minimum API version: 0x0100 +#[no_mangle] +pub extern "C" fn aime_io_nfc_poll(unit_no: u8) -> HRESULT { + if unit_no != 0 { + return S_OK; + } + + let Some(input_shmem) = (unsafe { &INPUT_SHMEM }) else { + return S_OK; + }; + let input = unsafe { input_shmem.as_slice() }; + + AIME_ID_PRESENT.store(false, Ordering::Relaxed); + FELICA_ID_PRESENT.store(false, Ordering::Relaxed); + + if input[34] != 0 { + match input[34] { + 1 => AIME_ID_PRESENT.store(true, Ordering::Relaxed), + 2 => FELICA_ID_PRESENT.store(true, Ordering::Relaxed), + _ => {} + } + + unsafe { + REMOTE_CARD_ID.copy_from_slice(&input[35..45]); + } + + return S_OK; + } + + if unsafe { GetAsyncKeyState(CONFIGURATION.vk_scan as i32) } == 0 { + return S_OK; + } + + if let Some(aime_id) = aime_io_read_id_file::<10>(&CONFIGURATION.aime_path) { + AIME_ID_PRESENT.store(true, Ordering::Relaxed); + + unsafe { REMOTE_CARD_ID.copy_from_slice(&aime_id) } + + return S_OK; + } + + if CONFIGURATION.aime_gen { + if let Some(aime_id) = aime_io_generate_aime(&CONFIGURATION.aime_path) { + AIME_ID_PRESENT.store(true, Ordering::Relaxed); + + unsafe { REMOTE_CARD_ID.copy_from_slice(&aime_id) } + + return S_OK; + } + + return E_FAIL; + } + + if let Some(felica_id) = aime_io_read_id_file::<8>(&CONFIGURATION.felica_path) { + FELICA_ID_PRESENT.store(true, Ordering::Relaxed); + + unsafe { + REMOTE_CARD_ID[..8].copy_from_slice(&felica_id); + } + + return S_OK; + } + + if CONFIGURATION.felica_gen { + if let Some(felica_id) = aime_io_generate_felica(&CONFIGURATION.felica_path) { + FELICA_ID_PRESENT.store(true, Ordering::Relaxed); + + unsafe { REMOTE_CARD_ID[..8].copy_from_slice(&felica_id) } + + return S_OK; + } + + return E_FAIL; + } + + S_OK +} + +/// Attempt to read out a classic Aime card ID +/// +/// - unit_no: Always 0 as of the current API version +/// - luid: Pointer to a ten-byte buffer that will receive the ID +/// - luid_size: Size of the buffer at *luid. Always 10. +/// +/// Returns: +/// +/// - S_OK if a classic Aime is present and was read successfully +/// - S_FALSE if no classic Aime card is present (*luid will be ignored) +/// - Any HRESULT error if an error occured. +/// +/// Minimum API version: 0x0100 +#[no_mangle] +pub unsafe extern "C" fn aime_io_nfc_get_aime_id( + unit_no: u8, + access_code: *mut u8, + access_code_size: usize, +) -> HRESULT { + if access_code.is_null() || access_code_size != 10 { + return S_FALSE; + } + + if unit_no != 0 || !AIME_ID_PRESENT.load(Ordering::Relaxed) { + return S_FALSE; + } + + let ac_slice = std::slice::from_raw_parts_mut(access_code, access_code_size); + ac_slice.copy_from_slice(&REMOTE_CARD_ID); + + S_OK +} + +/// Attempt to read out a FeliCa card ID ("IDm"). The following are examples +/// of FeliCa cards: +/// +/// - Amuse IC (which includes new-style Aime-branded cards, among others) +/// - Smartphones with FeliCa NFC capability (uncommon outside Japan) +/// - Various Japanese e-cash cards and train passes +/// +/// Parameters: +/// +/// - unit_no: Always 0 as of the current API version +/// - IDm: Output parameter that will receive the card ID +/// +/// Returns: +/// +/// - S_OK if a FeliCa device is present and was read successfully +/// - S_FALSE if no FeliCa device is present (*IDm will be ignored) +/// - Any HRESULT error if an error occured. +/// +/// Minimum API version: 0x0100 +#[no_mangle] +pub unsafe extern "C" fn aime_io_nfc_get_felica_id(unit_no: u8, idm: *mut u64) -> HRESULT { + if idm.is_null() { + return S_FALSE; + } + + if unit_no != 0 || !FELICA_ID_PRESENT.load(Ordering::Relaxed) { + return S_FALSE; + } + + *idm = 0; + + for nib in REMOTE_CARD_ID.iter().take(8) { + *idm = (*idm << 8) | (*nib as u64); + } + + S_OK +} + +/// Change the color and brightness of the card reader's RGB lighting +/// +/// - unit_no: Always 0 as of the current API version +/// - r, g, b: Primary color intensity, from 0 to 255 inclusive. +/// +/// Minimum API version: 0x0100 +#[no_mangle] +pub extern "C" fn aime_io_led_set_color(unit_no: u8, r: u8, g: u8, b: u8) { + if unit_no != 0 { + return; + } + + let Some(output_shmem) = (unsafe { &OUTPUT_SHMEM }) else { + return; + }; + let Ok(mut output_shmem) = output_shmem.lock() else { + error!("Could not acquire mutex of output shared memory"); + return; + }; + let buf = unsafe { output_shmem.as_slice_mut() }; + + buf[101] = r; + buf[102] = g; + buf[103] = b; + buf[122] = 1; +} diff --git a/chuniio/Cargo.lock b/chuniio/Cargo.lock new file mode 100644 index 0000000..6433a49 --- /dev/null +++ b/chuniio/Cargo.lock @@ -0,0 +1,224 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59d2a3357dde987206219e78ecfbbb6e8dad06cbb65292758d3270e6254f7355" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e53693616d3075149f4ead59bdeecd204ac6b8192d8969757601b74bddf00f" + +[[package]] +name = "chuniio-yubideck" +version = "0.1.0" +dependencies = [ + "anyhow", + "cfg-if", + "cfg_aliases", + "env_logger", + "lazy_static", + "log", + "rusb", + "shared_memory", + "winapi", +] + +[[package]] +name = "env_logger" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" +dependencies = [ + "log", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" + +[[package]] +name = "libusb1-sys" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d0e2afce4245f2c9a418511e5af8718bcaf2fa408aefb259504d1a9cb25f27" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "nix" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" +dependencies = [ + "bitflags", + "cc", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "pkg-config" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" + +[[package]] +name = "rusb" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45fff149b6033f25e825cbb7b2c625a11ee8e6dac09264d49beb125e39aa97bf" +dependencies = [ + "libc", + "libusb1-sys", +] + +[[package]] +name = "shared_memory" +version = "0.12.4-chuniio" +dependencies = [ + "cfg-if", + "libc", + "nix", + "win-sys", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "win-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b7b128a98c1cfa201b09eb49ba285887deb3cbe7466a98850eb1adabb452be5" +dependencies = [ + "windows", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45296b64204227616fdbf2614cefa4c236b98ee64dfaaaa435207ed99fe7829f" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" + +[[package]] +name = "windows_i686_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" + +[[package]] +name = "windows_i686_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" diff --git a/chuniio/Cargo.toml b/chuniio/Cargo.toml new file mode 100644 index 0000000..a202d05 --- /dev/null +++ b/chuniio/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "chuniio-yubideck" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +chusan = [] + +[dependencies] +anyhow = { workspace = true } +cfg-if = "1.0.0" +lazy_static = { workspace = true } +log = { workspace = true } +rusb = "0.9.3" +shared_memory = { workspace = true } +yubideck-common = { path = "../common" } +winapi = { workspace = true, features = ["minwindef", "winnt", "winerror", "winbase", "winuser"] } + +[build-dependencies] +cfg_aliases = "0.2.0" diff --git a/build.rs b/chuniio/build.rs similarity index 99% rename from build.rs rename to chuniio/build.rs index 4333d2e..c5761a9 100644 --- a/build.rs +++ b/chuniio/build.rs @@ -6,4 +6,4 @@ fn main() { amdaemon: { all(feature = "chusan", target_pointer_width = "64") }, chusanapp: { all(feature = "chusan", target_pointer_width = "32") }, } -} \ No newline at end of file +} diff --git a/src/configuration.rs b/chuniio/src/configuration.rs similarity index 95% rename from src/configuration.rs rename to chuniio/src/configuration.rs index 8732dbf..6e00b5a 100644 --- a/src/configuration.rs +++ b/chuniio/src/configuration.rs @@ -1,6 +1,6 @@ -#[derive(Debug, Clone, Copy)] -pub struct Configuration { - pub test_key: u32, - pub service_key: u32, - pub coin_key: u32, -} +#[derive(Debug, Clone, Copy)] +pub struct Configuration { + pub test_key: u32, + pub service_key: u32, + pub coin_key: u32, +} diff --git a/src/lib.rs b/chuniio/src/lib.rs similarity index 54% rename from src/lib.rs rename to chuniio/src/lib.rs index fac3d2c..a46133e 100644 --- a/src/lib.rs +++ b/chuniio/src/lib.rs @@ -1,109 +1,103 @@ -mod configuration; -mod log; +#![allow(clippy::missing_safety_doc)] + +mod configuration; -#[cfg(any(chuni, amdaemon))] -use std::{ffi::c_int, sync::atomic::AtomicU16}; use std::{ - ffi::{c_void, CString}, - fmt::Display, + ffi::c_void, + rc::Rc, sync::{ atomic::{AtomicBool, Ordering}, - Arc, + Mutex, }, }; -#[cfg(any(chuni, chusanapp))] -use std::{ - sync::{Mutex, OnceLock}, - thread::{self, JoinHandle}, - time::Duration, -}; use ::log::error; use anyhow::{anyhow, Result}; -use lazy_static::lazy_static; -use shared_memory::{Shmem, ShmemConf, ShmemError}; -#[cfg(any(chuni, amdaemon))] -use winapi::um::winuser::GetAsyncKeyState; +use shared_memory::Shmem; use winapi::{ shared::{ minwindef::{BOOL, DWORD, HINSTANCE, LPVOID, TRUE}, winerror::{E_FAIL, S_OK}, }, - um::{ - winbase::GetPrivateProfileIntA, - winnt::{DLL_PROCESS_ATTACH, HRESULT}, - }, -}; -#[cfg(any(chuni, chusanapp))] -use { - ::log::info, - rusb::{DeviceHandle, GlobalContext}, + um::winnt::{DLL_PROCESS_ATTACH, HRESULT}, }; +use yubideck_common::{create_shared_memory, init_logger, INPUT_SHMEM_SIZE, OUTPUT_SHMEM_SIZE}; -use crate::{configuration::Configuration, log::init_logger}; +static mut INPUT_SHMEM: Option> = None; +static mut OUTPUT_SHMEM: Option>> = None; -static mut INPUT_SHMEM: Option> = None; +cfg_if::cfg_if! { + if #[cfg(any(chuni, chusanapp))] { + use std::{ + sync::OnceLock, + thread::{self, JoinHandle}, + time::Duration, + }; -#[cfg(any(chuni, chusanapp))] -type SliderCallbackFn = unsafe extern "C" fn(data: *const u8); + use ::log::info; + use rusb::{DeviceHandle, GlobalContext}; -#[cfg(any(chuni, chusanapp))] -static DEVICE: OnceLock> = OnceLock::new(); + type SliderCallbackFn = unsafe extern "C" fn(data: *const u8); -#[cfg(any(chuni, chusanapp))] -static mut SLIDER_THREAD: OnceLock> = OnceLock::new(); + const TIMEOUT: Duration = Duration::from_millis(20); -#[cfg(any(chuni, chusanapp))] -static SLIDER_ACTIVE: AtomicBool = AtomicBool::new(false); + static DEVICE: OnceLock> = OnceLock::new(); + static mut SLIDER_THREAD: OnceLock> = OnceLock::new(); + static SLIDER_ACTIVE: AtomicBool = AtomicBool::new(false); + } +} -#[cfg(any(chuni, amdaemon))] -static COIN_COUNT: AtomicU16 = AtomicU16::new(0); +cfg_if::cfg_if! { + if #[cfg(any(chuni, amdaemon))] { + use std::{ffi::{c_int, CString}, sync::atomic::AtomicU16}; -#[cfg(any(chuni, amdaemon))] -static COIN_PRESSED: AtomicBool = AtomicBool::new(false); + use lazy_static::lazy_static; + use winapi::um::{winuser::GetAsyncKeyState, winbase::GetPrivateProfileIntA}; -#[cfg(any(chuni, chusanapp))] -static mut OUTPUT_SHMEM: Option>> = None; + use crate::configuration::Configuration; -#[cfg(any(chuni, amdaemon))] -lazy_static! { - static ref CONFIGURATION: Configuration = { - let io3 = CString::new("io3").unwrap(); - let test = CString::new("test").unwrap(); - let service = CString::new("service").unwrap(); - let coin = CString::new("coin").unwrap(); - let cfg_file = CString::new(".\\segatools.ini").unwrap(); + static COIN_COUNT: AtomicU16 = AtomicU16::new(0); + static COIN_PRESSED: AtomicBool = AtomicBool::new(false); - unsafe { - Configuration { - test_key: GetPrivateProfileIntA( - io3.as_ptr(), - test.as_ptr(), - 0x31, - cfg_file.as_ptr(), - ), - service_key: GetPrivateProfileIntA( - io3.as_ptr(), - service.as_ptr(), - 0x32, - cfg_file.as_ptr(), - ), - coin_key: GetPrivateProfileIntA( - io3.as_ptr(), - coin.as_ptr(), - 0x33, - cfg_file.as_ptr(), - ), - } + lazy_static! { + static ref CONFIGURATION: Configuration = { + let io3 = CString::new("io3").unwrap(); + let test = CString::new("test").unwrap(); + let service = CString::new("service").unwrap(); + let coin = CString::new("coin").unwrap(); + let cfg_file = CString::new(".\\segatools.ini").unwrap(); + + unsafe { + Configuration { + test_key: GetPrivateProfileIntA( + io3.as_ptr(), + test.as_ptr(), + 0x31, + cfg_file.as_ptr(), + ), + service_key: GetPrivateProfileIntA( + io3.as_ptr(), + service.as_ptr(), + 0x32, + cfg_file.as_ptr(), + ), + coin_key: GetPrivateProfileIntA( + io3.as_ptr(), + coin.as_ptr(), + 0x33, + cfg_file.as_ptr(), + ), + } + } + }; } - }; + } } #[no_mangle] extern "system" fn DllMain(_dll_module: HINSTANCE, call_reason: DWORD, _reserved: LPVOID) -> BOOL { - match call_reason { - DLL_PROCESS_ATTACH => init_logger(), - _ => {} + if call_reason == DLL_PROCESS_ATTACH { + init_logger() } TRUE @@ -115,9 +109,24 @@ pub extern "C" fn chuni_io_get_api_version() -> u16 { } #[no_mangle] -#[cfg(any(chuni, amdaemon))] pub extern "C" fn chuni_io_jvs_init() -> HRESULT { - #[cfg(not(feature = "chusan"))] + match create_shared_memory("YubideckInput", INPUT_SHMEM_SIZE, true) { + Ok(s) => unsafe { INPUT_SHMEM = Some(Rc::new(s)) }, + Err(e) => { + error!("Could not acquire shared memory for YubiDeck input: {e:#?}"); + return E_FAIL; + } + } + + match create_shared_memory("YubideckOutput", OUTPUT_SHMEM_SIZE, true) { + Ok(s) => unsafe { OUTPUT_SHMEM = Some(Rc::new(Mutex::new(s))) }, + Err(e) => { + error!("Could not obtain shared memory for YubiDeck output: {e:#?}"); + return E_FAIL; + } + } + + #[cfg(not(amdaemon))] { if let Err(e) = yubideck_init() { error!("Could not initialize YubiDeck: {e:#?}"); @@ -125,18 +134,12 @@ pub extern "C" fn chuni_io_jvs_init() -> HRESULT { } } - return create_input_shared_memory(); -} - -#[no_mangle] -#[cfg(chusanapp)] -pub extern "C" fn chuni_io_jvs_init() -> HRESULT { S_OK } #[no_mangle] #[cfg(any(chuni, amdaemon))] -pub extern "C" fn chuni_io_jvs_poll(opbtn: *mut u8, beams: *mut u8) { +pub unsafe extern "C" fn chuni_io_jvs_poll(opbtn: *mut u8, beams: *mut u8) { if opbtn.is_null() || beams.is_null() { return; } @@ -148,18 +151,16 @@ pub extern "C" fn chuni_io_jvs_poll(opbtn: *mut u8, beams: *mut u8) { let ir_value = input[0]; let mut buttons = input[1] & 3; // Buttons are in order: coin, service, test. We take the last 2 bits - unsafe { - if GetAsyncKeyState(CONFIGURATION.test_key as c_int) != 0 { - buttons |= 1; - } - - if GetAsyncKeyState(CONFIGURATION.service_key as c_int) != 0 { - buttons |= 2; - } - - *opbtn = buttons; - *beams = ir_value; + if GetAsyncKeyState(CONFIGURATION.test_key as c_int) != 0 { + buttons |= 1; } + + if GetAsyncKeyState(CONFIGURATION.service_key as c_int) != 0 { + buttons |= 2; + } + + *opbtn = buttons; + *beams = ir_value; } #[no_mangle] @@ -168,7 +169,7 @@ pub extern "C" fn chuni_io_jvs_poll(_opbtn: *mut u8, _beams: *mut u8) {} #[no_mangle] #[cfg(any(chuni, amdaemon))] -pub extern "C" fn chuni_io_jvs_read_coin_counter(total: *mut u16) { +pub unsafe extern "C" fn chuni_io_jvs_read_coin_counter(total: *mut u16) { if total.is_null() { return; } @@ -188,9 +189,7 @@ pub extern "C" fn chuni_io_jvs_read_coin_counter(total: *mut u16) { COIN_PRESSED.store(false, Ordering::Relaxed); } - unsafe { - *total = COIN_COUNT.load(Ordering::Relaxed); - } + *total = COIN_COUNT.load(Ordering::Relaxed); } #[no_mangle] @@ -198,34 +197,25 @@ pub extern "C" fn chuni_io_jvs_read_coin_counter(total: *mut u16) { pub extern "C" fn chuni_io_jvs_read_coin_counter(_total: *mut u16) {} #[no_mangle] -#[allow(unreachable_code)] -#[cfg(any(chuni, chusanapp))] -pub unsafe extern "C" fn chuni_io_slider_init() -> HRESULT { - match create_shared_memory("Local\\YubideckOutput", 122, true) { - Ok(s) => OUTPUT_SHMEM = Some(Arc::new(Mutex::new(s))), - Err(e) => { - error!("Could not obtain shared memory: {e:#?}"); - return E_FAIL; - } - } - - #[cfg(not(feature = "chusan"))] +pub extern "C" fn chuni_io_slider_init() -> HRESULT { + #[cfg(any(chuni, chusanapp))] { - // Already initialized in chuni_io_jvs_init() - return S_OK; + let Some(out_shmem) = (unsafe { &OUTPUT_SHMEM }) else { + error!("OUTPUT_SHMEM is unset."); + return E_FAIL; + }; + + let Ok(mut out_shmem) = out_shmem.lock() else { + error!("Could not acquire mutex of output shared memory"); + return E_FAIL; + }; + + let buf = unsafe { out_shmem.as_slice_mut() }; + + buf[0] = 0; + buf[61] = 1; } - if let Err(e) = yubideck_init() { - error!("Failed to initialize YubiDeck: {e:#?}"); - return E_FAIL; - } - - return create_input_shared_memory(); -} - -#[no_mangle] -#[cfg(amdaemon)] -pub extern "C" fn chuni_io_slider_init(_callback: *const c_void) -> HRESULT { S_OK } @@ -251,10 +241,8 @@ pub unsafe extern "C" fn chuni_io_slider_start(callback: *const c_void) { let mut pressure = [0u8; 32]; while SLIDER_ACTIVE.load(Ordering::Relaxed) { - pressure.copy_from_slice(&usb_in[2..34]); - - for i in 0..16 { - pressure.swap(i * 2, i * 2 + 1); + for (i, p) in usb_in.iter().skip(2).take(32).enumerate() { + pressure[if i % 2 == 0 { 30 - i } else { 32 - i }] = *p } callback(pressure.as_ptr()); @@ -304,9 +292,6 @@ pub unsafe extern "C" fn chuni_io_slider_set_leds(rgb: *const u8) { let buf = out_shmem.as_slice_mut(); let ground = std::slice::from_raw_parts(rgb, 93); - buf[0] = 0; - buf[61] = 1; - for (buf_chunk, state_chunk) in buf[1..61] .chunks_mut(3) .zip(ground.chunks(3).skip(11).take(20).rev()) @@ -325,11 +310,11 @@ pub unsafe extern "C" fn chuni_io_slider_set_leds(rgb: *const u8) { buf_chunk[2] = state_chunk[2]; } - if let Err(e) = device.write_interrupt(0x02, &buf[0..61], Duration::from_millis(20)) { + if let Err(e) = device.write_interrupt(0x02, &buf[0..61], TIMEOUT) { error!("Error writing first batch of output data: {e:#?}"); } - if let Err(e) = device.write_interrupt(0x02, &buf[61..], Duration::from_millis(20)) { + if let Err(e) = device.write_interrupt(0x02, &buf[61..122], TIMEOUT) { error!("Error writing second batch of output data: {e:#?}"); } } @@ -358,8 +343,6 @@ pub unsafe extern "C" fn chuni_io_led_set_colors(board: u8, rgb: *const u8) { let buf = out_shmem.as_slice_mut(); let data = std::slice::from_raw_parts(rgb, 183); - buf[61] = 1; - match board { 0 => { // left air @@ -376,7 +359,7 @@ pub unsafe extern "C" fn chuni_io_led_set_colors(board: u8, rgb: *const u8) { _ => {} } - if let Err(e) = device.write_interrupt(0x02, &buf[61..], Duration::from_millis(20)) { + if let Err(e) = device.write_interrupt(0x02, &buf[61..122], TIMEOUT) { error!("Error writing second batch of output data: {e:#?}"); } } @@ -385,42 +368,6 @@ pub unsafe extern "C" fn chuni_io_led_set_colors(board: u8, rgb: *const u8) { #[cfg(amdaemon)] pub extern "C" fn chuni_io_led_set_colors(_rgb: *const u8) {} -fn create_input_shared_memory() -> HRESULT { - match create_shared_memory("Local\\YubideckInput", 45, false) { - Ok(s) => { - unsafe { INPUT_SHMEM = Some(Arc::new(s)) }; - S_OK - } - Err(e) => { - error!("Could not acquire shared memory: {e:#?}"); - E_FAIL - } - } -} - -fn create_shared_memory(os_id: S, size: usize, is_owner: bool) -> Result -where - S: AsRef + Display + Copy, -{ - let shmem_conf = ShmemConf::new().size(size).os_id(os_id); - let shmem_result = match shmem_conf.clone().create() { - Ok(s) => Ok(s), - Err(ShmemError::MappingIdExists) => shmem_conf.open(), - Err(e) => { - return Err(anyhow!( - "Failed to create/open shared memory {os_id}: {e:#}" - )); - } - }; - - shmem_result - .map(|mut m| { - m.set_owner(is_owner); - m - }) - .map_err(|e| anyhow!("Failed to create/open shared memory {os_id}: {e:#}")) -} - #[cfg(any(chuni, chusanapp))] fn yubideck_init() -> Result<()> { let Some(mut device) = rusb::open_device_with_vid_pid(0x1973, 0x2001) else { @@ -439,22 +386,41 @@ fn yubideck_init() -> Result<()> { } #[cfg(any(chuni, chusanapp))] -fn input_thread_proc() { +fn input_thread_proc() { info!("Input thread started"); - let mut shmem = match create_shared_memory("Local\\YubideckInput", 45, true) { + let device = DEVICE.get().unwrap(); + + let mut input_shmem = match create_shared_memory("YubideckInput", INPUT_SHMEM_SIZE, true) { Ok(s) => s, Err(e) => { error!("Could not obtain shared memory for YubiDeck input: {e:#}"); return; } }; - let usb_in = unsafe { shmem.as_slice_mut() }; - let device = DEVICE.get().unwrap(); + let usb_in = unsafe { input_shmem.as_slice_mut() }; + + let mut output_shmem = match create_shared_memory("YubideckOutput", OUTPUT_SHMEM_SIZE, true) { + Ok(s) => s, + Err(e) => { + error!("Could not obtain shared memory for YubiDeck output: {e:#?}"); + return; + } + }; + let usb_out = unsafe { output_shmem.as_slice_mut() }; loop { - if let Err(e) = device.read_interrupt(0x81, usb_in, Duration::from_millis(20)) { + if let Err(e) = device.read_interrupt(0x81, usb_in, TIMEOUT) { error!("Failed to read data from YubiDeck: {e:#}"); } + + // Update reader LED data + if usb_out[122] == 1 { + usb_out[122] = 0; + + if let Err(e) = device.write_interrupt(0x02, &usb_out[61..122], TIMEOUT) { + error!("Error writing second batch of output data: {e:#?}"); + } + } } } diff --git a/common/Cargo.toml b/common/Cargo.toml new file mode 100644 index 0000000..17aa464 --- /dev/null +++ b/common/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "yubideck-common" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = { workspace = true } +env_logger = { version = "0.10.1", default-features = false } +log = { workspace = true } +shared_memory = { workspace = true } +winapi = { workspace = true, features = ["debugapi"] } diff --git a/common/src/lib.rs b/common/src/lib.rs new file mode 100644 index 0000000..8d432e9 --- /dev/null +++ b/common/src/lib.rs @@ -0,0 +1,61 @@ +pub mod log; +use std::fmt::Display; + +use anyhow::{anyhow, Result}; +pub use log::init_logger; +use shared_memory::{Shmem, ShmemConf, ShmemError}; + +// #[repr(C)] +// struct YubideckInput { +// pub ir_value: u8, +// pub buttons: u8, +// pub touch_data: [u8; 32], +// pub card_status: u8, +// pub card_id: [u8; 10], +// } +pub const INPUT_SHMEM_SIZE: usize = 45; + +// #[repr(C)] +// struct YubideckOutputGaosan { +// // Always 0 +// pub packet_type: u8, +// pub slider_led_data: [u8; 60], +// } +// +// #[repr(C)] +// struct YubideckOutputDasi { +// // Always 1 +// pub packet_type: u8, +// pub slider_led_data: [u8; 33], +// pub left_air_led: [u8; 3], +// pub right_air_led: [u8; 3], +// pub card_reader_led: [u8; 3], +// pub padding: [u8; 18], +// } +// +// There's an extra byte (byte 122) for AimeIO to communicate to ChuniIO +// that reader LED has changed and ChuniIO should update it. +pub const OUTPUT_SHMEM_SIZE: usize = 123; + +pub fn create_shared_memory(os_id: S, size: usize, is_owner: bool) -> Result +where + S: AsRef + Display + Copy, +{ + let shmem_conf = ShmemConf::new().size(size).os_id(os_id); + let shmem_result = match shmem_conf.clone().create() { + Ok(s) => Ok(s), + Err(ShmemError::MappingIdExists) => shmem_conf.open(), + Err(e) => { + return Err(anyhow!( + "Failed to create/open shared memory {os_id}: {e:#}" + )); + } + }; + + shmem_result + .map(|mut m| { + m.set_owner(is_owner); + m + }) + .map_err(|e| anyhow!("Failed to create/open shared memory {os_id}: {e:#}")) +} diff --git a/src/log.rs b/common/src/log.rs similarity index 100% rename from src/log.rs rename to common/src/log.rs diff --git a/vendor/shared_memory/examples/mutex.rs b/vendor/shared_memory/examples/mutex.rs index 47554bb..f23e428 100644 --- a/vendor/shared_memory/examples/mutex.rs +++ b/vendor/shared_memory/examples/mutex.rs @@ -1,5 +1,7 @@ -use std::sync::atomic::{AtomicU8, Ordering}; -use std::thread; +use std::{ + sync::atomic::{AtomicU8, Ordering}, + thread, +}; use clap::Parser; use raw_sync::locks::*; diff --git a/vendor/shared_memory/src/lib.rs b/vendor/shared_memory/src/lib.rs index 2a4126d..43cc659 100644 --- a/vendor/shared_memory/src/lib.rs +++ b/vendor/shared_memory/src/lib.rs @@ -2,11 +2,11 @@ //! //! For help on how to get started, take a look at the [examples](https://github.com/elast0ny/shared_memory-rs/tree/master/examples) ! -use std::fs::{File, OpenOptions}; -use std::io::{ErrorKind, Read, Write}; - -use std::fs::remove_file; -use std::path::{Path, PathBuf}; +use std::{ + fs::{remove_file, File, OpenOptions}, + io::{ErrorKind, Read, Write}, + path::{Path, PathBuf}, +}; use cfg_if::cfg_if; diff --git a/vendor/shared_memory/src/unix.rs b/vendor/shared_memory/src/unix.rs index 695b5f5..ef38954 100644 --- a/vendor/shared_memory/src/unix.rs +++ b/vendor/shared_memory/src/unix.rs @@ -1,13 +1,15 @@ -use std::os::unix::io::RawFd; -use std::ptr::null_mut; +use std::{os::unix::io::RawFd, ptr::null_mut}; -use crate::log::*; -use nix::fcntl::OFlag; -use nix::sys::mman::{mmap, munmap, shm_open, shm_unlink, MapFlags, ProtFlags}; -use nix::sys::stat::{fstat, Mode}; -use nix::unistd::{close, ftruncate}; +use nix::{ + fcntl::OFlag, + sys::{ + mman::{mmap, munmap, shm_open, shm_unlink, MapFlags, ProtFlags}, + stat::{fstat, Mode}, + }, + unistd::{close, ftruncate}, +}; -use crate::ShmemError; +use crate::{log::*, ShmemError}; #[derive(Clone, Default)] pub struct ShmemConfExt; diff --git a/vendor/shared_memory/src/windows.rs b/vendor/shared_memory/src/windows.rs index 96f4a59..93f34c4 100644 --- a/vendor/shared_memory/src/windows.rs +++ b/vendor/shared_memory/src/windows.rs @@ -1,12 +1,13 @@ -use std::fs::{File, OpenOptions}; -use std::io::ErrorKind; -use std::os::windows::{fs::OpenOptionsExt, io::AsRawHandle}; -use std::path::PathBuf; +use std::{ + fs::{File, OpenOptions}, + io::ErrorKind, + os::windows::{fs::OpenOptionsExt, io::AsRawHandle}, + path::PathBuf, +}; -use crate::{log::*, ShmemConf}; use win_sys::*; -use crate::ShmemError; +use crate::{log::*, ShmemConf, ShmemError}; #[derive(Clone, Default)] pub struct ShmemConfExt { diff --git a/vendor/shared_memory/tests/posix_semantics.rs b/vendor/shared_memory/tests/posix_semantics.rs index b48cd13..93bf455 100644 --- a/vendor/shared_memory/tests/posix_semantics.rs +++ b/vendor/shared_memory/tests/posix_semantics.rs @@ -1,6 +1,6 @@ +use std::{sync::mpsc::channel, thread}; + use shared_memory::ShmemConf; -use std::sync::mpsc::channel; -use std::thread; #[test] fn persistence() {