commit 99e8abef6a18c98ed7dbe8a50a47e29198edcafc Author: skybldev Date: Mon Jan 29 01:50:45 2024 -0500 initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..61c93c0 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,753 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "borsh" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f58b559fd6448c6e2fd0adb5720cd98a2506594cafa4737ff98c396f3e82f667" +dependencies = [ + "borsh-derive", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aadb5b6ccbd078890f6d7003694e33816e6b784358f18e15e7e6d9f065a57cd" +dependencies = [ + "once_cell", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.48", + "syn_derive", +] + +[[package]] +name = "bytecheck" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[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.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "codespan" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3362992a0d9f1dd7c3d0e89e0ab2bb540b7a95fea8cd798090e758fda2899b5e" +dependencies = [ + "codespan-reporting", +] + +[[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 = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "g-code" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6261d157241cc6ec7e015a19f2b8ea37e2018a32980a60d65bda6c7b336349fc" +dependencies = [ + "codespan", + "codespan-reporting", + "paste", + "peg", + "rust_decimal", +] + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "libc" +version = "0.2.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "osu-file-parser" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8bf208710e445c5780b93cf14025de927e8b3e262cd2bd84e3f23a67c830359" +dependencies = [ + "either", + "nom", + "pretty_assertions", + "rust_decimal", + "rust_decimal_macros", + "strum", + "strum_macros", + "thiserror", +] + +[[package]] +name = "osubot" +version = "0.1.0" +dependencies = [ + "g-code", + "osu-file-parser", + "rust_decimal", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "peg" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "400bcab7d219c38abf8bd7cc2054eb9bbbd4312d66f6a5557d572a203f646f61" +dependencies = [ + "peg-macros", + "peg-runtime", +] + +[[package]] +name = "peg-macros" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46e61cce859b76d19090f62da50a9fe92bab7c2a5f09e183763559a2ac392c90" +dependencies = [ + "peg-runtime", + "proc-macro2", + "quote", +] + +[[package]] +name = "peg-runtime" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36bae92c60fa2398ce4678b98b2c4b5a7c61099961ca1fa305aec04a9ad28922" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "pretty_assertions" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +dependencies = [ + "diff", + "yansi", +] + +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit", +] + +[[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", + "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.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[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 = "rend" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2571463863a6bd50c32f94402933f03457a3fbaf697a707c5be741e459f08fd" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "rkyv" +version = "0.7.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527a97cdfef66f65998b5f3b637c26f5a5ec09cc52a3f9932313ac645f4190f5" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5c462a1328c8e67e4d6dbad1eb0355dd43e8ab432c6e227a43657f16ade5033" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rust_decimal" +version = "1.33.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06676aec5ccb8fc1da723cc8c0f9a46549f21ebb8753d3915c6c41db1e7f1dc4" +dependencies = [ + "arrayvec", + "borsh", + "bytes", + "num-traits", + "rand", + "rkyv", + "serde", + "serde_json", +] + +[[package]] +name = "rust_decimal_macros" +version = "1.33.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e43721f4ef7060ebc2c3ede757733209564ca8207f47674181bcd425dd76945" +dependencies = [ + "quote", + "rust_decimal", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "serde" +version = "1.0.195" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.195" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "serde_json" +version = "1.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 1.0.109", +] + +[[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.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" + +[[package]] +name = "toml_edit" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "uuid" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" + +[[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 = "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-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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winnow" +version = "0.5.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16" +dependencies = [ + "memchr", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..87d8763 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "osubot" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +osu-file-parser = "1.1.0" +g-code = "0.3.6" +#matrix = "0.22.0" +rust_decimal = { version = "1.33.1", features = ["maths"] } diff --git a/osu/test2/test2.osu b/osu/test2/test2.osu new file mode 100644 index 0000000..d2ec56b --- /dev/null +++ b/osu/test2/test2.osu @@ -0,0 +1,73 @@ +osu file format v14 + +[General] +AudioLeadIn: 0 +PreviewTime: -1 +Countdown: 1 +SampleSet: Normal +StackLeniency: 0.7 +Mode: 0 +LetterboxInBreaks: 0 +WidescreenStoryboard: 1 +SamplesMatchPlaybackRate: 1 + +[Editor] +DistanceSpacing: 1 +BeatDivisor: 4 +GridSize: 32 +TimelineZoom: 1 + +[Metadata] +Title: test +TitleUnicode: test +Artist: me +ArtistUnicode: me +Creator: Skybbles +Version: goddamnit i've rusted +Source: Source? I made it the hell up! +Tags: tag + +[Difficulty] +HPDrainRate: 5 +CircleSize: 5 +OverallDifficulty: 5 +ApproachRate: 4.4 +SliderMultiplier: 1 +SliderTickRate: 1 + +[Events] +0,0,"wildfire.png",0,0 + +[TimingPoints] +-13,545.4545454545455,4,1,0,100,1,0 + +[HitObjects] +156,120,259,5,0,1:0:0:0: +328,152,532,1,0,1:0:0:0: +204,204,805,1,0,1:0:0:0: +204,204,1350,1,0,1:0:0:0: +204,204,1487,1,0,1:0:0:0: +204,204,1623,1,0,1:0:0:0: +204,204,1759,1,0,1:0:0:0: +204,204,1896,1,0,1:0:0:0: +416,288,2168,1,0,1:0:0:0: +320,288,2441,1,0,1:0:0:0: +223,288,2714,1,0,1:0:0:0: +127,288,2987,1,0,1:0:0:0: +31,31,4350,5,0,1:0:0:0: +448,63,4487,1,0,1:0:0:0: +127,192,4623,1,0,1:0:0:0: +288,63,4759,1,0,1:0:0:0: +255,256,4896,1,0,1:0:0:0: +255,160,5032,1,0,1:0:0:0: +127,127,5168,1,0,1:0:0:0: +416,288,5305,1,0,1:0:0:0: +63,288,5441,1,0,1:0:0:0: +191,192,5577,1,0,1:0:0:0: +384,160,5714,1,0,1:0:0:0: +255,224,5850,1,0,1:0:0:0: +95,95,5987,1,0,1:0:0:0: +288,352,6123,1,0,1:0:0:0: +159,192,6259,1,0,1:0:0:0: +384,127,6396,1,0,1:0:0:0: +255,95,6532,1,0,1:0:0:0: diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..fd9850f --- /dev/null +++ b/src/main.rs @@ -0,0 +1,332 @@ +use osu_file_parser::{ + osu_file::{ + hitobjects::{ + CurveType::*, + HitObjectParams::* + } + }, + OsuFile +}; +use rust_decimal::{Decimal, prelude::ToPrimitive}; + +mod types; +use types::*; + +/* + The input file. +*/ +const OSU: &str = include_str!("../osu/test2/test2.osu"); + +/* + Machine's maximum acceleration in mm/s². +*/ +const MAX_ACCEL: f64 = 110_000.0; +/* + The default acceleration to use for max_v calculation. +*/ +const DEFAULT_ACCEL: f64 = MAX_ACCEL / 2.0; +/* + The acceleration to use for groups of hitobjects. This should be higher as + groups of hitobjects usually have to be traveled with "linear" velocity + across all the hitobjects. +*/ +const GROUP_ACCEL: f64 = MAX_ACCEL; +/* + How much to increase the acceleration if the current acceleration doesn't + produce a result. +*/ +const ACCEL_RETRY_STEP: f64 = 5_000.0; +/* + Since hitobjects' times are rounded to the nearest millisecond, linear + velocity calculations aren't very precise, leading to hitobjects with linear + velocities perceived to be identical not counted for grouping. To compensate + for this, a threshold is set, and any difference between linear velocities + under this threshold are counted. +*/ +const GROUP_VEL_LENIENCY: f64 = 0.2; + +/* + Size of the bounding box for the beatmap in storage. Do not change. +*/ +const BOUNDING_BOX_SIZE: (f64, f64) = (512.0, 384.0); // lazer default ig +/* + Physical limits of the machine +*/ +const PHYSICAL_MAX: (f64, f64) = (105.0, 110.0); +/* + Active area +*/ +const AREA_ORIGIN: (f64, f64) = (10.0, 10.0); +const AREA_MAX: (f64, f64) = (95.0, 95.0); +/* + Rotation in degrees anteclockwise +*/ +const ROTATION: f64 = 90.0; + +const AREA_SIZE: (f64, f64) = ( + AREA_MAX.0 - AREA_ORIGIN.0, + AREA_MAX.1 - AREA_ORIGIN.1 +); +const BOX_TO_AREA_OFFSET: (f64, f64) = ( + AREA_SIZE.0 / 2.0 - BOUNDING_BOX_SIZE.0 / 2.0, + AREA_SIZE.1 / 2.0 - BOUNDING_BOX_SIZE.1 / 2.0 +); + +/* + Calculates the maximum velocity (mm/ms) given the the start and end + position, acceleration, and required total move time. + + This uses an equation from 19aksh https://math.stackexchange.com/a/3304761 + rearranged to solve for v. + + In loose mathematical format: + + 2t - sqrt(4t² - 8d (1 / a - 1 / -a)) + v = ----------------------------------- + 2 (1 / a - 1 / -a) + + Where: + t = time (ms) + d = distance (mm) + a = kinematic acceleration (mm/1000² since 1s == 1000ms) + v = velocity (mm/ms) + + (multiply v by 60_000 to get the feedrate (mm/min)) + + There are some combinations of t, d, and a that can result in no solution, + usually because the acceleration and deceleration periods overlap. In this + case, the acceleration should be increased until a solution is found. To + compensate for the possibility of this exceeding the MAX_ACCEL, this will + normally calculate with half of MAX_ACCEL. If no solution is found even + at MAX_ACCEL, an error will be thrown. + */ +fn max_velocity( + p1: PositionF64, + p2: PositionF64, + accel: f64, + ms: f64, +) -> f64 { + let dist = p1 >> p2; + let accel_divided = accel / 1_000_000.0; // accel / 1000² since 1s == 1000ms + let accel_factor = 1.0 / accel_divided - 1.0 / -accel_divided; + let sqrt_fac = (4.0 * ms.powf(2.0) - 8.0 * dist * accel_factor).sqrt(); + (2.0 * ms - sqrt_fac) / (2.0 * accel_factor) +} + +fn find_bbox_scale_factor( + box_size: (f64, f64), + target_size: (f64, f64), + rotation: f64 +) -> f64 { + let target_size = if rotation == 90.0 || rotation == 270.0 { + (target_size.1, target_size.0) + } else { + target_size + }; + if target_size.0 > target_size.1 { + target_size.1 / box_size.1 + } else { + target_size.0 / box_size.0 + } +} + +fn solve_feedrate_and_accel( + p1: PositionF64, + p2: PositionF64, + starting_accel: f64, + ms: f64 +) -> (f64, f64) { + let mut accel = starting_accel; + let mut max_v: f64; + + loop { + max_v = max_velocity(p1, p2, accel, ms); + if !max_v.is_nan() { break } + accel += ACCEL_RETRY_STEP; + if accel >= MAX_ACCEL { + panic!("couldn't calculate max_v for dist {} within {}ms", p1 >> p2, ms); + } + } + + let speed = max_v * 1_000.0; // mm/s + let feedrate = speed * 60.0; // mm/min + + eprintln!( + "; feedrate: {:.2} ({:.2}mm/s) ({})->({}) dist {}, accel={}, Δms={}", + feedrate, speed, + p1, p2, + p1 >> p2, + accel, + ms + ); + + (feedrate, accel) +} + +fn main() -> Result<(), Box> { + /* Steps + 1. Read beatmap and hitcircles + a. convert curves to lines + b. apply scaling for every point + c. apply rotation for every point + 2. Convert to gcode paths + a. add preliminary gcode + b. calculate F for a given movement + c. write movements to file + */ + + let src = OSU.parse::()?; + let binding = src.hitobjects.expect("Beatmap has no hit objects!"); + let mut hitobjects = binding.0.iter(); + let scale_fac = find_bbox_scale_factor( + BOUNDING_BOX_SIZE, + AREA_SIZE, + ROTATION + ); + + println!("G90\nG0 Z0"); + let corners = [ + AREA_ORIGIN, + (AREA_ORIGIN.0, AREA_MAX.1), + (AREA_MAX.0, AREA_MAX.1), + (AREA_MAX.0, AREA_ORIGIN.1) + ]; + for corner in corners { + println!("G0 X{} Y{} F7500", corner.0, corner.1); + } + + let mut total_time: f64 = 0.0; + + let mut p1 = PositionF64::new(0.0, 0.0); + let mut p1_ms: Decimal = 0.into(); + let mut last_accel: f64 = 0.0; + + /* + These are used for merging notes that share the same angle and linear + velocity into one move. Linear velocity is plainly (dist / ms_delta). + + move_p1 is the starting point of a g-code move. + */ + let mut move_p1: PositionF64 = p1; + let mut move_p1_ms: Decimal = 0.into(); + let mut group_linear_vel = 0.0; + let mut group_slope = 0.0; + let mut group_size = 0; + let mut dwell_state = false; + + for (count, obj) in hitobjects.enumerate() { + let p2_ms = obj.time.get().clone().unwrap_left(); + let p1_to_p2_ms = p2_ms + .checked_sub(p1_ms) + .unwrap() + .to_f64() + .unwrap(); + + let p2 = PositionF64 + ::from_position(obj.position.clone()) + .unwrap() + .convert_to_machine_space( + scale_fac, + BOUNDING_BOX_SIZE, + AREA_ORIGIN, + AREA_SIZE, + ROTATION + ); + + eprintln!("point {}", count); + + if count == 0 { + println!("PAUSE"); + } else if p1 == p2 && !dwell_state { + dwell_state = true; + } else if p1 != p2 { + let move_p1_to_p1_ms = p1_ms + .checked_sub(move_p1_ms) + .unwrap() + .to_f64() + .unwrap(); + + let linear_vel = (p1 >> p2) / p1_to_p2_ms; + let slope = p1 / p2; + + println!("; linear_vel {:.4}, slope {:.4}", linear_vel, slope); + + if + linear_vel - group_linear_vel < GROUP_VEL_LENIENCY + && slope == group_slope + { + println!("; !! merging point"); + group_size += 1; + } else { + let (feedrate, accel) = solve_feedrate_and_accel( + move_p1, + p1, + if group_size > 0 { + GROUP_ACCEL + } else { + DEFAULT_ACCEL + }, + move_p1_to_p1_ms + ); + + if accel != last_accel { + println!("M204 S{}", accel); + } + + println!("G0 F{:.4}\nG0 X{} Y{}", feedrate, p1.x, p1.y); + + last_accel = accel; + group_linear_vel = linear_vel; + group_slope = slope; + group_size = 0; + } + + if dwell_state { + println!("G4 P{}", move_p1_to_p1_ms); + dwell_state = false; + } + + move_p1_ms = p1_ms; + move_p1 = p1; + } + + p1 = p2; + p1_ms = p2_ms; + total_time += p1_to_p2_ms; + } + + eprintln!("total time should be {}ms", total_time); + + Ok(()) +} + + /* + println!( + "x={} y={} Δms={} type={}", + obj.position.x, + obj.position.y let move_p1_to_p1_ms = p1_ms + .checked_sub(move_p1_ms) + .unwrap() + .to_f64() + .unwrap();, + delta_ms, + match obj.obj_params { + HitCircle => String::from("HitCircle"), + Slider(s) => format!( + "Slider type={} points={} slides={} length={}", + match s.curve_type { + Bezier => "Bezier", + Centripetal => "Centripetal", + Linear => "Linear", + PerfectCircle => "PerfectCircle", + _ => "Other" + }, + s.curve_points.len(), + s.slides, + s.length.get().clone() + ), + Spinner => String::from("Spinner"), + _ => String::from("Other") + } + ); + */ \ No newline at end of file diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..17f59db --- /dev/null +++ b/src/types.rs @@ -0,0 +1,129 @@ +use osu_file_parser::Position; +use rust_decimal::{Decimal, prelude::ToPrimitive}; + +pub type DecimalTuple = (Decimal, Decimal); + +#[derive(Copy, Clone, PartialEq)] +pub struct PositionF64 { + pub x: f64, + pub y: f64 +} + +pub enum Axis { + X, + Y +} + +impl PositionF64 { + pub fn new(x: f64, y: f64) -> Self { Self { x, y } } + + pub fn from_tuple(a: (f64, f64)) -> Self { + Self { x: a.0, y: a.1 } + } + + pub fn from_position(a: Position) -> Option { + Some(Self { + x: a.x.get().clone().left()?.to_f64()?, + y: a.y.get().clone().left()?.to_f64()? + }) + } + + pub fn scale(mut self, fac: f64) -> Self { + self.x *= fac; + self.y *= fac; + self + } + + pub fn offset(mut self, x: f64, y: f64) -> Self { + self.x += x; + self.y += y; + self + } + + pub fn rotate(mut self, angle_deg: f64) -> Self { + let theta = angle_deg * std::f64::consts::PI / 180.0; + let sin_theta = theta.sin(); + let cos_theta = theta.cos(); + let x = self.x * cos_theta - self.y * sin_theta; + let y = self.x * sin_theta + self.y * cos_theta; + self.x = x; + self.y = y; + self + } + + pub fn flip(mut self, axis: Axis, bisect: f64) -> Self { + match axis { + Axis::Y => self.x = self.x - (self.x - bisect) * 2.0, + Axis::X => self.y = self.y - (self.y - bisect) * 2.0, + } + self + } + + pub fn convert_to_machine_space( + mut self, + scale_fac: f64, + bbox_size: (f64, f64), + area_origin: (f64, f64), + area_size: (f64, f64), + rotation: f64 + ) -> Self { + self.flip(Axis::X, bbox_size.1 / 2.0) + .scale(scale_fac) + .rotate(rotation) + .offset( + area_origin.0 + area_size.0, + area_origin.1 + area_size.1 / 2.0 - bbox_size.0 * scale_fac / 2.0 + ) + } + + pub fn to_string(&self) -> String { + format!("{:.4}, {:.4}", self.x, self.y) + } +} + +impl std::fmt::Display for PositionF64 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.to_string()); + Ok(()) + } +} + +// Add/offset +impl std::ops::Add for PositionF64 { + type Output = Self; + fn add(self, other: Self) -> Self::Output { + Self { + x: self.x + other.x, + y: self.y + other.y + } + } +} + +// Subtract/difference +impl std::ops::Sub for PositionF64 { + type Output = Self; + fn sub(self, other: Self) -> Self::Output { + Self { + x: self.x - other.x, + y: self.y - other.y + } + } +} + +// Distance +impl std::ops::Shr for PositionF64 { + type Output = f64; + fn shr(self, other: Self) -> Self::Output { + let dist = other - self; + (dist.x.powi(2) + dist.y.powi(2)).sqrt().abs() + } +} + +// Slope +impl std::ops::Div for PositionF64 { + type Output = f64; + fn div(self, rhs: Self) -> Self::Output { + let diff = rhs - self; + diff.y / diff.x + } +} diff --git a/test2.gcode b/test2.gcode new file mode 100644 index 0000000..c4f5c2c --- /dev/null +++ b/test2.gcode @@ -0,0 +1,78 @@ +G90 +G0 Z0 +G0 X10 Y10 F7500 +G0 X10 Y95 F7500 +G0 X95 Y95 F7500 +G0 X95 Y10 F7500 +PAUSE +; linear_vel 0.1064, slope 5.3750 +M204 S55000 +G0 F14734.6525 +G0 X51.171875 Y35.8984375 +; linear_vel 0.0818, slope -2.3846 +G0 F6429.3272 +G0 X56.484375 Y64.453125 +; linear_vel 0.1392, slope 2.5238 +G0 F982.1545 +G0 X65.1171875 Y43.8671875 +G4 P1364 +; linear_vel 0.0584, slope -inf +G0 F8430.0676 +G0 X79.0625 Y79.0625 +; linear_vel 0.0590, slope -inf +; !! merging point +; linear_vel 0.0584, slope -inf +; !! merging point +; linear_vel 0.0334, slope 0.3735 +M204 S110000 +G0 F3509.5833 +G0 X79.0625 Y31.083984375 +; linear_vel 0.5068, slope 13.0312 +M204 S55000 +G0 F2005.8322 +G0 X36.396484375 Y15.146484375000004 +; linear_vel 0.4223, slope -2.4884 +G0 F32785.7872 +G0 X41.70898437500001 Y84.375 +; linear_vel 0.2518, slope -1.2481 +G0 F26957.4090 +G0 X63.125 Y31.083984375000004 +; linear_vel 0.2373, slope -0.1710 +G0 F15656.4508 +G0 X41.708984375 Y57.8125 +; linear_vel 0.1172, slope -0.0000 +G0 F14715.1658 +G0 X73.75 Y52.333984375 +; linear_vel 0.1614, slope 3.8788 +G0 F7145.0001 +G0 X57.8125 Y52.333984375 +; linear_vel 0.4009, slope 1.7950 +G0 F9899.9328 +G0 X52.333984375 Y31.083984375000004 +; linear_vel 0.4309, slope -inf +G0 F25490.3308 +G0 X79.0625 Y79.0625 +; linear_vel 0.1953, slope -1.3333 +G0 F27545.0689 +G0 X79.0625 Y20.458984375 +; linear_vel 0.2371, slope -6.0313 +G0 F12041.8473 +G0 X63.125 Y41.708984375 +; linear_vel 0.1758, slope -2.0156 +G0 F14702.2521 +G0 X57.81250000000001 Y73.75 +; linear_vel 0.2491, slope 1.2403 +G0 F10807.3775 +G0 X68.4375 Y52.333984375 +; linear_vel 0.3923, slope 0.7510 +G0 F15472.8573 +G0 X47.021484375 Y25.771484375000004 +; linear_vel 0.2509, slope 0.8063 +G0 F24924.2159 +G0 X89.6875 Y57.8125 +; linear_vel 0.2838, slope -3.4615 +G0 F15595.0898 +G0 X63.125 Y36.396484375 +; linear_vel 0.1622, slope 4.0312 +G0 F17722.9324 +G0 X52.33398437500001 Y73.75