Compare commits
No commits in common. "f7d23174a5d8047e0195cdc15049ec11174d16ea" and "d751ff4505c5a4215bbc8578fb211ba72e5c9df2" have entirely different histories.
f7d23174a5
...
d751ff4505
@ -22,7 +22,7 @@
|
|||||||
"@tauri-apps/plugin-shell": "^2",
|
"@tauri-apps/plugin-shell": "^2",
|
||||||
"@tauri-apps/plugin-sql": "^2.2.0",
|
"@tauri-apps/plugin-sql": "^2.2.0",
|
||||||
"ky": "^1.7.3",
|
"ky": "^1.7.3",
|
||||||
"lucide-svelte": "^0.469.0",
|
"lucide-svelte": "^0.468.0",
|
||||||
"osu-classes": "^3.1.0",
|
"osu-classes": "^3.1.0",
|
||||||
"osu-parsers": "^4.1.7",
|
"osu-parsers": "^4.1.7",
|
||||||
"radix-icons-svelte": "^1.2.1"
|
"radix-icons-svelte": "^1.2.1"
|
||||||
@ -33,7 +33,7 @@
|
|||||||
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
||||||
"@tauri-apps/cli": "^2",
|
"@tauri-apps/cli": "^2",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"bits-ui": "^1.0.0-next.74",
|
"bits-ui": "^1.0.0-next.70",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"sass-embedded": "^1.82.0",
|
"sass-embedded": "^1.82.0",
|
||||||
"svelte": "^5.0.0",
|
"svelte": "^5.0.0",
|
||||||
|
227
src-tauri/Cargo.lock
generated
227
src-tauri/Cargo.lock
generated
@ -1097,26 +1097,12 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "eyre"
|
|
||||||
version = "0.6.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec"
|
|
||||||
dependencies = [
|
|
||||||
"indenter",
|
|
||||||
"once_cell",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ezpplauncher"
|
name = "ezpplauncher"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"eyre",
|
|
||||||
"rosu-mem",
|
|
||||||
"rosu-pp",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_repr",
|
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-build",
|
"tauri-build",
|
||||||
"tauri-plugin-dialog",
|
"tauri-plugin-dialog",
|
||||||
@ -1124,7 +1110,6 @@ dependencies = [
|
|||||||
"tauri-plugin-shell",
|
"tauri-plugin-shell",
|
||||||
"tauri-plugin-single-instance",
|
"tauri-plugin-single-instance",
|
||||||
"tauri-plugin-sql",
|
"tauri-plugin-sql",
|
||||||
"tracy-client",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1430,19 +1415,6 @@ dependencies = [
|
|||||||
"x11",
|
"x11",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "generator"
|
|
||||||
version = "0.8.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cc6bd114ceda131d3b1d665eba35788690ad37f5916457286b32ab6fd3c438dd"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"log",
|
|
||||||
"rustversion",
|
|
||||||
"windows 0.58.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
version = "0.14.7"
|
version = "0.14.7"
|
||||||
@ -1981,12 +1953,6 @@ dependencies = [
|
|||||||
"icu_properties",
|
"icu_properties",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "indenter"
|
|
||||||
version = "0.3.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "1.9.3"
|
version = "1.9.3"
|
||||||
@ -2269,19 +2235,6 @@ version = "0.4.22"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "loom"
|
|
||||||
version = "0.7.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"generator",
|
|
||||||
"scoped-tls",
|
|
||||||
"tracing",
|
|
||||||
"tracing-subscriber",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mac"
|
name = "mac"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@ -2311,15 +2264,6 @@ dependencies = [
|
|||||||
"tendril",
|
"tendril",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "matchers"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
|
|
||||||
dependencies = [
|
|
||||||
"regex-automata 0.1.10",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "matches"
|
name = "matches"
|
||||||
version = "0.1.10"
|
version = "0.1.10"
|
||||||
@ -2440,18 +2384,6 @@ version = "1.0.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
|
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "nix"
|
|
||||||
version = "0.25.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"bitflags 1.3.2",
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nix"
|
name = "nix"
|
||||||
version = "0.27.1"
|
version = "0.27.1"
|
||||||
@ -2493,16 +2425,6 @@ dependencies = [
|
|||||||
"minimal-lexical",
|
"minimal-lexical",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "nu-ansi-term"
|
|
||||||
version = "0.46.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
|
|
||||||
dependencies = [
|
|
||||||
"overload",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-bigint-dig"
|
name = "num-bigint-dig"
|
||||||
version = "0.8.4"
|
version = "0.8.4"
|
||||||
@ -2571,7 +2493,7 @@ version = "0.7.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
|
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro-crate 3.2.0",
|
"proc-macro-crate 1.3.1",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.90",
|
"syn 2.0.90",
|
||||||
@ -2857,12 +2779,6 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "overload"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pango"
|
name = "pango"
|
||||||
version = "0.18.3"
|
version = "0.18.3"
|
||||||
@ -3399,17 +3315,8 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
"regex-automata 0.4.9",
|
"regex-automata",
|
||||||
"regex-syntax 0.8.5",
|
"regex-syntax",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex-automata"
|
|
||||||
version = "0.1.10"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
|
||||||
dependencies = [
|
|
||||||
"regex-syntax 0.6.29",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3420,15 +3327,9 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
"regex-syntax 0.8.5",
|
"regex-syntax",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex-syntax"
|
|
||||||
version = "0.6.29"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
@ -3495,22 +3396,6 @@ dependencies = [
|
|||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rosu-mem"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "git+https://github.com/486c/rosu-mem.git?tag=v1.0.0#2349cb845e3149c71873bfeda7ec81afec94c8c0"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"nix 0.25.1",
|
|
||||||
"paste",
|
|
||||||
"windows 0.48.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rosu-pp"
|
|
||||||
version = "0.10.0"
|
|
||||||
source = "git+https://github.com/486c/rosu-pp.git?branch=main#a82dd41e5008944893b2434901d62dd5e6160d74"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rsa"
|
name = "rsa"
|
||||||
version = "0.9.7"
|
version = "0.9.7"
|
||||||
@ -3559,12 +3444,6 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustversion"
|
|
||||||
version = "1.0.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.18"
|
version = "1.0.18"
|
||||||
@ -3818,15 +3697,6 @@ dependencies = [
|
|||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sharded-slab"
|
|
||||||
version = "0.1.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
|
||||||
dependencies = [
|
|
||||||
"lazy_static",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shared_child"
|
name = "shared_child"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@ -4334,7 +4204,7 @@ dependencies = [
|
|||||||
"tao-macros",
|
"tao-macros",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
"url",
|
"url",
|
||||||
"windows 0.58.0",
|
"windows",
|
||||||
"windows-core 0.58.0",
|
"windows-core 0.58.0",
|
||||||
"windows-version",
|
"windows-version",
|
||||||
"x11-dl",
|
"x11-dl",
|
||||||
@ -4404,7 +4274,7 @@ dependencies = [
|
|||||||
"webkit2gtk",
|
"webkit2gtk",
|
||||||
"webview2-com",
|
"webview2-com",
|
||||||
"window-vibrancy",
|
"window-vibrancy",
|
||||||
"windows 0.58.0",
|
"windows",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4599,7 +4469,7 @@ dependencies = [
|
|||||||
"tauri-utils",
|
"tauri-utils",
|
||||||
"thiserror 2.0.6",
|
"thiserror 2.0.6",
|
||||||
"url",
|
"url",
|
||||||
"windows 0.58.0",
|
"windows",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4624,7 +4494,7 @@ dependencies = [
|
|||||||
"url",
|
"url",
|
||||||
"webkit2gtk",
|
"webkit2gtk",
|
||||||
"webview2-com",
|
"webview2-com",
|
||||||
"windows 0.58.0",
|
"windows",
|
||||||
"wry",
|
"wry",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -4745,16 +4615,6 @@ dependencies = [
|
|||||||
"syn 2.0.90",
|
"syn 2.0.90",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "thread_local"
|
|
||||||
version = "1.1.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"once_cell",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.3.37"
|
version = "0.3.37"
|
||||||
@ -4947,56 +4807,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
|
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"valuable",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracing-log"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
|
|
||||||
dependencies = [
|
|
||||||
"log",
|
|
||||||
"once_cell",
|
|
||||||
"tracing-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracing-subscriber"
|
|
||||||
version = "0.3.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
|
|
||||||
dependencies = [
|
|
||||||
"matchers",
|
|
||||||
"nu-ansi-term",
|
|
||||||
"once_cell",
|
|
||||||
"regex",
|
|
||||||
"sharded-slab",
|
|
||||||
"smallvec",
|
|
||||||
"thread_local",
|
|
||||||
"tracing",
|
|
||||||
"tracing-core",
|
|
||||||
"tracing-log",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracy-client"
|
|
||||||
version = "0.16.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "307e6b7030112fe9640fdd87988a40795549ba75c355f59485d14e6b444d2987"
|
|
||||||
dependencies = [
|
|
||||||
"loom",
|
|
||||||
"once_cell",
|
|
||||||
"tracy-client-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracy-client-sys"
|
|
||||||
version = "0.22.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9d104d610dfa9dd154535102cc9c6164ae1fa37842bc2d9e83f9ac82b0ae0882"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -5181,12 +4991,6 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "valuable"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vcpkg"
|
name = "vcpkg"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
@ -5464,7 +5268,7 @@ checksum = "6f61ff3d9d0ee4efcb461b14eb3acfda2702d10dc329f339303fc3e57215ae2c"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"webview2-com-macros",
|
"webview2-com-macros",
|
||||||
"webview2-com-sys",
|
"webview2-com-sys",
|
||||||
"windows 0.58.0",
|
"windows",
|
||||||
"windows-core 0.58.0",
|
"windows-core 0.58.0",
|
||||||
"windows-implement",
|
"windows-implement",
|
||||||
"windows-interface",
|
"windows-interface",
|
||||||
@ -5488,7 +5292,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "a3a3e2eeb58f82361c93f9777014668eb3d07e7d174ee4c819575a9208011886"
|
checksum = "a3a3e2eeb58f82361c93f9777014668eb3d07e7d174ee4c819575a9208011886"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
"windows 0.58.0",
|
"windows",
|
||||||
"windows-core 0.58.0",
|
"windows-core 0.58.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -5547,15 +5351,6 @@ dependencies = [
|
|||||||
"windows-version",
|
"windows-version",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows"
|
|
||||||
version = "0.48.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
|
|
||||||
dependencies = [
|
|
||||||
"windows-targets 0.48.5",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows"
|
name = "windows"
|
||||||
version = "0.58.0"
|
version = "0.58.0"
|
||||||
@ -5940,7 +5735,7 @@ dependencies = [
|
|||||||
"webkit2gtk",
|
"webkit2gtk",
|
||||||
"webkit2gtk-sys",
|
"webkit2gtk-sys",
|
||||||
"webview2-com",
|
"webview2-com",
|
||||||
"windows 0.58.0",
|
"windows",
|
||||||
"windows-core 0.58.0",
|
"windows-core 0.58.0",
|
||||||
"windows-version",
|
"windows-version",
|
||||||
"x11-dl",
|
"x11-dl",
|
||||||
|
@ -18,15 +18,10 @@ crate-type = ["staticlib", "cdylib", "rlib"]
|
|||||||
tauri-build = { version = "2", features = [] }
|
tauri-build = { version = "2", features = [] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rosu-mem = { git = "https://github.com/486c/rosu-mem.git", tag = "v1.0.0" }
|
|
||||||
rosu-pp = { git = "https://github.com/486c/rosu-pp.git", branch = "main", features = ["gradual"] }
|
|
||||||
tracy-client = { version = "0.16.4", default-features = false }
|
|
||||||
eyre = "0.6.12"
|
|
||||||
tauri = { version = "2", features = [] }
|
tauri = { version = "2", features = [] }
|
||||||
tauri-plugin-shell = "2"
|
tauri-plugin-shell = "2"
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
serde_repr = "0.1.17"
|
|
||||||
tauri-plugin-sql = "2.2.0"
|
tauri-plugin-sql = "2.2.0"
|
||||||
tauri-plugin-dialog = "2"
|
tauri-plugin-dialog = "2"
|
||||||
tauri-plugin-fs = "2"
|
tauri-plugin-fs = "2"
|
||||||
|
@ -1,23 +1,8 @@
|
|||||||
mod reading_loop;
|
|
||||||
mod structs;
|
|
||||||
|
|
||||||
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
||||||
use crate::reading_loop::process_reading_loop;
|
|
||||||
use crate::structs::{State, StaticAddresses};
|
|
||||||
use eyre::Report;
|
|
||||||
use rosu_mem::{
|
|
||||||
error::ProcessError,
|
|
||||||
process::{Process, ProcessTraits},
|
|
||||||
};
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
use std::{thread, time::Duration};
|
|
||||||
use structs::{InnerValues, OutputValues};
|
|
||||||
use tauri::Manager;
|
use tauri::Manager;
|
||||||
|
|
||||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
pub fn run() {
|
pub fn run() {
|
||||||
println!("starting ezpplauncher!");
|
|
||||||
osu_memory_reading();
|
|
||||||
let mut builder = tauri::Builder::default().plugin(tauri_plugin_fs::init());
|
let mut builder = tauri::Builder::default().plugin(tauri_plugin_fs::init());
|
||||||
#[cfg(desktop)]
|
#[cfg(desktop)]
|
||||||
{
|
{
|
||||||
@ -36,78 +21,3 @@ pub fn run() {
|
|||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn osu_memory_reading() {
|
|
||||||
thread::spawn(|| {
|
|
||||||
let output_values = Arc::new(Mutex::new(OutputValues::default()));
|
|
||||||
let inner_values = InnerValues::default();
|
|
||||||
|
|
||||||
let mut state = State {
|
|
||||||
addresses: StaticAddresses::default(),
|
|
||||||
ivalues: inner_values,
|
|
||||||
values: output_values,
|
|
||||||
};
|
|
||||||
|
|
||||||
'init_loop: loop {
|
|
||||||
println!("Searching for osu! process");
|
|
||||||
let p = match Process::initialize("osu!.exe") {
|
|
||||||
Ok(p) => p,
|
|
||||||
Err(e) => {
|
|
||||||
println!("{:?}", Report::new(e));
|
|
||||||
thread::sleep(Duration::from_millis(5000));
|
|
||||||
continue 'init_loop;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
println!("Reading static signatures...");
|
|
||||||
match StaticAddresses::new(&p) {
|
|
||||||
Ok(v) => state.addresses = v,
|
|
||||||
Err(e) => {
|
|
||||||
match e.downcast_ref::<ProcessError>() {
|
|
||||||
Some(&ProcessError::ProcessNotFound) => {
|
|
||||||
thread::sleep(Duration::from_millis(2000));
|
|
||||||
continue 'init_loop
|
|
||||||
},
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
Some(&ProcessError::OsError{ .. }) => {
|
|
||||||
println!("{:?}", e);
|
|
||||||
thread::sleep(Duration::from_millis(2000));
|
|
||||||
continue 'init_loop
|
|
||||||
},
|
|
||||||
Some(_) | None => {
|
|
||||||
println!("{:?}", e);
|
|
||||||
thread::sleep(Duration::from_millis(2000));
|
|
||||||
continue 'init_loop
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
println!("osu! process found, Starting reading loop");
|
|
||||||
|
|
||||||
'main_loop: loop {
|
|
||||||
if let Err(e) = process_reading_loop(&p, &mut state) {
|
|
||||||
match e.downcast_ref::<ProcessError>() {
|
|
||||||
Some(&ProcessError::ProcessNotFound) => {
|
|
||||||
thread::sleep(Duration::from_millis(5000));
|
|
||||||
continue 'init_loop;
|
|
||||||
},
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
Some(&ProcessError::OsError{ .. }) => {
|
|
||||||
println!("{:?}", e);
|
|
||||||
thread::sleep(Duration::from_millis(5000));
|
|
||||||
continue 'init_loop
|
|
||||||
},
|
|
||||||
Some(_) | None => {
|
|
||||||
println!("{:?}", e);
|
|
||||||
thread::sleep(Duration::from_millis(5000));
|
|
||||||
continue 'main_loop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!("{:?}", state.values.clone());
|
|
||||||
thread::sleep(Duration::from_millis(2000));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
@ -1,462 +0,0 @@
|
|||||||
use std::{borrow::Cow, mem::size_of};
|
|
||||||
|
|
||||||
use rosu_pp::Beatmap;
|
|
||||||
use tracy_client::*;
|
|
||||||
use eyre::Result;
|
|
||||||
|
|
||||||
use rosu_mem::process::{Process, ProcessTraits};
|
|
||||||
|
|
||||||
use crate::structs::{State, GameState, BeatmapStatus, OutputValues};
|
|
||||||
|
|
||||||
|
|
||||||
/// Here cases when key overlay is not gonna be available for reading:
|
|
||||||
/// 1. Map is not fully loaded
|
|
||||||
/// 2. If key overlay is not enabled in settings
|
|
||||||
pub fn process_key_overlay(
|
|
||||||
p: &Process,
|
|
||||||
values: &mut OutputValues,
|
|
||||||
ruleset_addr: i32,
|
|
||||||
) -> Result<()> {
|
|
||||||
let keyoverlay_ptr = p.read_i32(ruleset_addr + 0xB0)?;
|
|
||||||
|
|
||||||
if keyoverlay_ptr == 0 {
|
|
||||||
return Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO optimize using batches?
|
|
||||||
|
|
||||||
let keyoverlay_addr = p.read_i32(
|
|
||||||
p.read_i32(keyoverlay_ptr + 0x10)? + 0x4
|
|
||||||
)?;
|
|
||||||
|
|
||||||
values.keyoverlay.k1_pressed = p.read_i8(
|
|
||||||
p.read_i32(keyoverlay_addr + 0x8)? + 0x1C
|
|
||||||
)? != 0;
|
|
||||||
|
|
||||||
values.keyoverlay.k1_count = p.read_i32(
|
|
||||||
p.read_i32(keyoverlay_addr + 0x8)? + 0x14
|
|
||||||
)? as u32;
|
|
||||||
|
|
||||||
values.keyoverlay.k2_pressed = p.read_i8(
|
|
||||||
p.read_i32(keyoverlay_addr + 0xC)? + 0x1C
|
|
||||||
)? != 0;
|
|
||||||
|
|
||||||
values.keyoverlay.k2_count = p.read_i32(
|
|
||||||
p.read_i32(keyoverlay_addr + 0xC)? + 0x14
|
|
||||||
)? as u32;
|
|
||||||
|
|
||||||
values.keyoverlay.m1_pressed = p.read_i8(
|
|
||||||
p.read_i32(keyoverlay_addr + 0x10)? + 0x1C
|
|
||||||
)? != 0;
|
|
||||||
|
|
||||||
values.keyoverlay.m1_count = p.read_i32(
|
|
||||||
p.read_i32(keyoverlay_addr + 0x10)? + 0x14
|
|
||||||
)? as u32;
|
|
||||||
|
|
||||||
values.keyoverlay.m2_pressed = p.read_i8(
|
|
||||||
p.read_i32(keyoverlay_addr + 0x14)? + 0x1C
|
|
||||||
)? != 0;
|
|
||||||
|
|
||||||
values.keyoverlay.m2_count = p.read_i32(
|
|
||||||
p.read_i32(keyoverlay_addr + 0x14)? + 0x14
|
|
||||||
)? as u32;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn process_gameplay(
|
|
||||||
p: &Process,
|
|
||||||
state: &mut State,
|
|
||||||
values: &mut OutputValues,
|
|
||||||
ruleset_addr: i32,
|
|
||||||
) -> Result<()> {
|
|
||||||
let _span = span!("Gameplay data");
|
|
||||||
|
|
||||||
if values.prev_playtime > values.playtime {
|
|
||||||
values.reset_gameplay();
|
|
||||||
state.ivalues.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
values.prev_playtime = values.playtime;
|
|
||||||
|
|
||||||
if ruleset_addr == 0 {
|
|
||||||
return Ok(())
|
|
||||||
};
|
|
||||||
|
|
||||||
let gameplay_base =
|
|
||||||
p.read_i32(ruleset_addr + 0x68)?;
|
|
||||||
|
|
||||||
if gameplay_base == 0 {
|
|
||||||
return Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
let score_base = p.read_i32(gameplay_base + 0x38)?;
|
|
||||||
|
|
||||||
let hp_base = p.read_i32(gameplay_base + 0x40)?;
|
|
||||||
|
|
||||||
// Random value but seems to work pretty well
|
|
||||||
// TODO sometimes playtime is >150 but game doesn't have
|
|
||||||
// values yet unreal to debug, occurs rarely and randomly
|
|
||||||
if values.playtime > 150 {
|
|
||||||
values.gameplay.current_hp = p.read_f64(hp_base + 0x1C)?;
|
|
||||||
values.gameplay.current_hp_smooth =
|
|
||||||
p.read_f64(hp_base + 0x14)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let hit_errors_base = p.read_i32(score_base + 0x38)?;
|
|
||||||
|
|
||||||
p.read_i32_array(
|
|
||||||
hit_errors_base,
|
|
||||||
&mut values.gameplay.hit_errors
|
|
||||||
)?;
|
|
||||||
|
|
||||||
values.gameplay.unstable_rate =
|
|
||||||
values.gameplay.calculate_unstable_rate();
|
|
||||||
|
|
||||||
// TODO batch
|
|
||||||
values.gameplay.mode = p.read_i32(score_base + 0x64)?;
|
|
||||||
|
|
||||||
// TODO batch
|
|
||||||
values.gameplay.hit_300 = p.read_i16(score_base + 0x8a)?;
|
|
||||||
values.gameplay.hit_100 = p.read_i16(score_base + 0x88)?;
|
|
||||||
values.gameplay.hit_50 = p.read_i16(score_base + 0x8c)?;
|
|
||||||
|
|
||||||
values.gameplay.username = p.read_string(score_base + 0x28)?;
|
|
||||||
|
|
||||||
// TODO batch
|
|
||||||
values.gameplay.hit_geki = p.read_i16(score_base + 0x8e)?;
|
|
||||||
values.gameplay.hit_katu = p.read_i16(score_base + 0x90)?;
|
|
||||||
values.gameplay.hit_miss = p.read_i16(score_base + 0x92)?;
|
|
||||||
|
|
||||||
let passed_objects = values.gameplay.passed_objects()?;
|
|
||||||
|
|
||||||
values.gameplay.passed_objects = passed_objects;
|
|
||||||
|
|
||||||
values.gameplay.update_accuracy();
|
|
||||||
|
|
||||||
values.gameplay.score = p.read_i32(score_base + 0x78)?;
|
|
||||||
|
|
||||||
values.gameplay.combo = p.read_i16(score_base + 0x94)?;
|
|
||||||
values.gameplay.max_combo = p.read_i16(score_base + 0x68)?;
|
|
||||||
|
|
||||||
if values.gameplay.combo < values.prev_combo
|
|
||||||
&& values.gameplay.hit_miss == values.prev_hit_miss {
|
|
||||||
values.gameplay.slider_breaks += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
values.prev_hit_miss = values.gameplay.hit_miss;
|
|
||||||
|
|
||||||
let mods_xor_base = p.read_i32(score_base + 0x1C)?;
|
|
||||||
|
|
||||||
let mods_raw = p.read_u64(mods_xor_base + 0x8)?;
|
|
||||||
|
|
||||||
let mods_xor1 = mods_raw & 0xFFFFFFFF;
|
|
||||||
let mods_xor2 = mods_raw >> 32;
|
|
||||||
|
|
||||||
// Read key overlay
|
|
||||||
process_key_overlay(
|
|
||||||
p,
|
|
||||||
values,
|
|
||||||
ruleset_addr
|
|
||||||
)?;
|
|
||||||
|
|
||||||
values.gameplay.mods = (mods_xor1 ^ mods_xor2) as u32;
|
|
||||||
values.update_readable_mods();
|
|
||||||
|
|
||||||
// Calculate pp
|
|
||||||
values.update_current_pp(&mut state.ivalues);
|
|
||||||
values.update_fc_pp(&mut state.ivalues);
|
|
||||||
|
|
||||||
values.prev_passed_objects = passed_objects;
|
|
||||||
values.prev_combo = values.gameplay.combo;
|
|
||||||
|
|
||||||
values.gameplay.grade = values.gameplay.get_current_grade();
|
|
||||||
values.update_current_bpm();
|
|
||||||
values.update_kiai();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn process_reading_loop(
|
|
||||||
p: &Process,
|
|
||||||
state: &mut State
|
|
||||||
) -> Result<()> {
|
|
||||||
let _span = span!("reading loop");
|
|
||||||
|
|
||||||
let values = state.values.clone();
|
|
||||||
let mut values = values.lock().unwrap();
|
|
||||||
|
|
||||||
let menu_mods_ptr = p.read_i32(
|
|
||||||
state.addresses.menu_mods + 0x9
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let menu_mods = p.read_u32(menu_mods_ptr)?;
|
|
||||||
values.menu_mods = menu_mods;
|
|
||||||
|
|
||||||
let playtime_ptr = p.read_i32(state.addresses.playtime + 0x5)?;
|
|
||||||
values.playtime = p.read_i32(playtime_ptr)?;
|
|
||||||
|
|
||||||
let beatmap_ptr = p.read_i32(state.addresses.base - 0xC)?;
|
|
||||||
let beatmap_addr = p.read_i32(beatmap_ptr)?;
|
|
||||||
|
|
||||||
let status_ptr = p.read_i32(state.addresses.status - 0x4)?;
|
|
||||||
|
|
||||||
let skin_ptr = p.read_i32(state.addresses.skin + 0x4)?;
|
|
||||||
let skin_data = p.read_i32(skin_ptr)?;
|
|
||||||
values.skin = p.read_string(skin_data + 0x44)?;
|
|
||||||
|
|
||||||
values.state = GameState::from(
|
|
||||||
p.read_u32(status_ptr)?
|
|
||||||
);
|
|
||||||
|
|
||||||
// Handle leaving `Playing` state
|
|
||||||
if values.prev_state == GameState::Playing
|
|
||||||
&& values.state != GameState::Playing {
|
|
||||||
values.reset_gameplay();
|
|
||||||
state.ivalues.reset();
|
|
||||||
values.update_stars_and_ss_pp();
|
|
||||||
}
|
|
||||||
|
|
||||||
if beatmap_addr == 0 {
|
|
||||||
return Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
if values.state != GameState::MultiplayerLobby {
|
|
||||||
let mut beatmap_stats_buff = [0u8; size_of::<f32>() * 4];
|
|
||||||
|
|
||||||
p.read(
|
|
||||||
beatmap_addr + 0x2c,
|
|
||||||
size_of::<f32>() * 4,
|
|
||||||
&mut beatmap_stats_buff
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Safety: unwrap here because buff is already initialized
|
|
||||||
// and filled with zeros, the worst case scenario is
|
|
||||||
// ar, cs, od, hp going to be zero's
|
|
||||||
values.beatmap.ar = f32::from_le_bytes(
|
|
||||||
beatmap_stats_buff[0..4].try_into().unwrap()
|
|
||||||
);
|
|
||||||
|
|
||||||
values.beatmap.cs = f32::from_le_bytes(
|
|
||||||
beatmap_stats_buff[4..8].try_into().unwrap()
|
|
||||||
);
|
|
||||||
|
|
||||||
values.beatmap.hp = f32::from_le_bytes(
|
|
||||||
beatmap_stats_buff[8..12].try_into().unwrap()
|
|
||||||
);
|
|
||||||
|
|
||||||
values.beatmap.od = f32::from_le_bytes(
|
|
||||||
beatmap_stats_buff[12..].try_into().unwrap()
|
|
||||||
);
|
|
||||||
|
|
||||||
let plays_addr = p.read_i32(state.addresses.base - 0x33)? + 0xC;
|
|
||||||
values.plays = p.read_i32(plays_addr)?;
|
|
||||||
|
|
||||||
values.beatmap.artist = p.read_string(beatmap_addr + 0x18)?;
|
|
||||||
values.beatmap.title = p.read_string(beatmap_addr + 0x24)?;
|
|
||||||
values.beatmap.creator = p.read_string(beatmap_addr + 0x7C)?;
|
|
||||||
values.beatmap.difficulty = p.read_string(beatmap_addr + 0xAC)?;
|
|
||||||
values.beatmap.map_id = p.read_i32(beatmap_addr + 0xC8)?; // TODO batch
|
|
||||||
values.beatmap.mapset_id = p.read_i32(beatmap_addr + 0xCC)?; // TODO batch
|
|
||||||
}
|
|
||||||
|
|
||||||
values.beatmap.beatmap_status = BeatmapStatus::from(
|
|
||||||
p.read_i16(beatmap_addr + 0x12C)?
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut new_map = false;
|
|
||||||
|
|
||||||
// All time values that available everywhere
|
|
||||||
values.chat_enabled = p.read_i8(
|
|
||||||
state.addresses.chat_checker - 0x20
|
|
||||||
)? != 0;
|
|
||||||
|
|
||||||
// Skin folder
|
|
||||||
let skin_data_ptr = p.read_i32(
|
|
||||||
p.read_i32(state.addresses.skin + 4)?
|
|
||||||
)?;
|
|
||||||
|
|
||||||
values.skin_folder = p.read_string(
|
|
||||||
skin_data_ptr + 68
|
|
||||||
)?;
|
|
||||||
|
|
||||||
if values.state != GameState::PreSongSelect
|
|
||||||
&& values.state != GameState::MultiplayerLobby
|
|
||||||
&& values.state != GameState::MultiplayerResultScreen {
|
|
||||||
let menu_mode_addr = p.read_i32(state.addresses.base - 0x33)?;
|
|
||||||
|
|
||||||
let beatmap_file = p.read_string(beatmap_addr + 0x90)?;
|
|
||||||
let beatmap_folder = p.read_string(beatmap_addr + 0x78)?;
|
|
||||||
let audio_file = p.read_string(beatmap_addr + 0x64)?;
|
|
||||||
values.menu_mode = p.read_i32(menu_mode_addr)?;
|
|
||||||
|
|
||||||
values.beatmap.paths.beatmap_full_path
|
|
||||||
= values.osu_path.join("Songs/");
|
|
||||||
|
|
||||||
values.beatmap.paths.beatmap_full_path.push(&beatmap_folder);
|
|
||||||
values.beatmap.paths.beatmap_full_path.push(&beatmap_file);
|
|
||||||
|
|
||||||
values.beatmap.md5 =
|
|
||||||
p.read_string(beatmap_addr + 0x6C)?;
|
|
||||||
|
|
||||||
// Check if beatmap changed
|
|
||||||
if (beatmap_folder != values.beatmap.paths.beatmap_folder
|
|
||||||
|| beatmap_file != values.beatmap.paths.beatmap_file
|
|
||||||
|| values.prev_menu_mode != values.menu_mode)
|
|
||||||
&& values.beatmap.paths.beatmap_full_path.exists() {
|
|
||||||
let current_beatmap = match Beatmap::from_path(
|
|
||||||
&values.beatmap.paths.beatmap_full_path
|
|
||||||
) {
|
|
||||||
Ok(beatmap) => {
|
|
||||||
new_map = true;
|
|
||||||
|
|
||||||
values.beatmap.paths.background_file.clone_from(
|
|
||||||
&beatmap.background.filename
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(hobj) = beatmap.hit_objects.last() {
|
|
||||||
values.beatmap.last_obj_time = hobj.start_time;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(hobj) = beatmap.hit_objects.first() {
|
|
||||||
values.beatmap.first_obj_time = hobj.start_time;
|
|
||||||
}
|
|
||||||
|
|
||||||
values.beatmap.bpm = beatmap.bpm();
|
|
||||||
|
|
||||||
Some(beatmap)
|
|
||||||
},
|
|
||||||
Err(_) => {
|
|
||||||
println!("Failed to parse beatmap");
|
|
||||||
None
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
values.current_beatmap = current_beatmap;
|
|
||||||
|
|
||||||
values.beatmap.paths.beatmap_folder = beatmap_folder;
|
|
||||||
values.beatmap.paths.beatmap_file = beatmap_file;
|
|
||||||
values.beatmap.paths.audio_file = audio_file;
|
|
||||||
|
|
||||||
values.update_min_max_bpm();
|
|
||||||
values.update_full_paths();
|
|
||||||
values.adjust_bpm();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// store the converted map so it's not converted
|
|
||||||
// everytime it's used for pp calc
|
|
||||||
if new_map {
|
|
||||||
if let Some(map) = &values.current_beatmap {
|
|
||||||
if let Cow::Owned(converted) = map
|
|
||||||
.convert_mode(values.menu_gamemode())
|
|
||||||
{
|
|
||||||
values.current_beatmap = Some(converted);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
values.update_stars_and_ss_pp();
|
|
||||||
values.update_current_pp(&mut state.ivalues);
|
|
||||||
}
|
|
||||||
|
|
||||||
let ruleset_addr = p.read_i32(
|
|
||||||
p.read_i32(state.addresses.rulesets - 0xb)? + 0x4
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let audio_time_ptr = p.read_i32(state.addresses.audio_time_base + 0x9)?;
|
|
||||||
values.precise_audio_time = p.read_i32(audio_time_ptr)?;
|
|
||||||
|
|
||||||
// If this happened there is zero sense to continue
|
|
||||||
// reading because all the values depends on this
|
|
||||||
// address
|
|
||||||
if ruleset_addr == 0 {
|
|
||||||
return Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process result screen
|
|
||||||
// TODO handle situations when result screen is not ready
|
|
||||||
if values.state == GameState::ResultScreen {
|
|
||||||
let result_base = p.read_i32(ruleset_addr+ 0x38)?;
|
|
||||||
|
|
||||||
values.result_screen.username = p.read_string(result_base + 0x28)?;
|
|
||||||
|
|
||||||
let mods_xor_base = p.read_i32(result_base + 0x1C)?;
|
|
||||||
|
|
||||||
// TODO batch
|
|
||||||
let mods_xor1 = p.read_i32(mods_xor_base + 0xC)?;
|
|
||||||
let mods_xor2 = p.read_i32(mods_xor_base + 0x8)?;
|
|
||||||
|
|
||||||
values.result_screen.mods = (mods_xor1 ^ mods_xor2) as u32;
|
|
||||||
values.result_screen.mode = p.read_i32(result_base + 0x64)? as u8;
|
|
||||||
values.result_screen.score = p.read_i32(result_base + 0x78)?;
|
|
||||||
|
|
||||||
// TODO batch
|
|
||||||
values.result_screen.hit_300 = p.read_i16(result_base + 0x8A)?;
|
|
||||||
values.result_screen.hit_100 = p.read_i16(result_base + 0x88)?;
|
|
||||||
values.result_screen.hit_50 = p.read_i16(result_base + 0x8C)?;
|
|
||||||
values.result_screen.hit_geki = p.read_i16(result_base + 0x8E)?;
|
|
||||||
values.result_screen.hit_katu = p.read_i16(result_base + 0x90)?;
|
|
||||||
|
|
||||||
values.result_screen.update_accuracy();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process gameplay
|
|
||||||
if values.state == GameState::Playing {
|
|
||||||
let res = process_gameplay(
|
|
||||||
p,
|
|
||||||
state,
|
|
||||||
&mut values,
|
|
||||||
ruleset_addr
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Err(e) = res {
|
|
||||||
println!("{:?}", e);
|
|
||||||
println!("Skipped gameplay reading, probably it's not ready yet");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handling entering `ResultScreen` state
|
|
||||||
if values.prev_state != GameState::ResultScreen
|
|
||||||
&& values.state == GameState::ResultScreen {
|
|
||||||
if values.prev_state != GameState::Playing {
|
|
||||||
values.update_current_pp(&mut state.ivalues);
|
|
||||||
}
|
|
||||||
|
|
||||||
values.update_stars_and_ss_pp();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handling entering `SongSelect` state
|
|
||||||
if values.prev_state != GameState::SongSelect
|
|
||||||
&& values.state == GameState::SongSelect {
|
|
||||||
// Reseting pp's from result screen
|
|
||||||
if values.prev_state == GameState::ResultScreen {
|
|
||||||
values.current_pp = 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
values.update_current_pp(&mut state.ivalues);
|
|
||||||
values.update_stars_and_ss_pp();
|
|
||||||
values.adjust_bpm();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update stars when entering `Playing` state
|
|
||||||
if values.prev_state != GameState::Playing
|
|
||||||
&& values.state == GameState::Playing {
|
|
||||||
values.reset_gameplay();
|
|
||||||
values.update_stars_and_ss_pp();
|
|
||||||
values.adjust_bpm();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle mods changes inside `SongSelect` state
|
|
||||||
if values.state == GameState::SongSelect
|
|
||||||
&& values.prev_menu_mods != values.menu_mods {
|
|
||||||
values.update_stars_and_ss_pp();
|
|
||||||
values.update_current_pp(&mut state.ivalues);
|
|
||||||
values.adjust_bpm();
|
|
||||||
}
|
|
||||||
|
|
||||||
values.prev_menu_mode = values.menu_mode;
|
|
||||||
values.prev_menu_mods = menu_mods;
|
|
||||||
values.prev_state = values.state;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -5,13 +5,8 @@
|
|||||||
<div
|
<div
|
||||||
class="absolute w-screen h-screen overflow-hidden pointer-events-none rounded"
|
class="absolute w-screen h-screen overflow-hidden pointer-events-none rounded"
|
||||||
>
|
>
|
||||||
<img
|
<div
|
||||||
src={background}
|
|
||||||
alt="background"
|
|
||||||
class="absolute top-0 left-0 w-full h-full !bg-cover -z-10 pointer-events-none blur opacity-10 rounded"
|
|
||||||
/>
|
|
||||||
<!-- <div
|
|
||||||
style="background: url(https://osu.direct/api/media/background/2226722)"
|
style="background: url(https://osu.direct/api/media/background/2226722)"
|
||||||
class="absolute top-0 left-0 w-full h-full !bg-cover -z-10 pointer-events-none blur opacity-10 rounded"
|
class="absolute top-0 left-0 w-full h-full !bg-cover -z-10 pointer-events-none blur opacity-10 rounded"
|
||||||
></div> -->
|
></div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
<script lang="ts" module>
|
|
||||||
import { type VariantProps, tv } from "tailwind-variants";
|
|
||||||
|
|
||||||
export const badgeVariants = tv({
|
|
||||||
base: "focus:ring-ring inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2",
|
|
||||||
variants: {
|
|
||||||
variant: {
|
|
||||||
default:
|
|
||||||
"bg-primary text-primary-foreground hover:bg-primary/80 border-transparent",
|
|
||||||
secondary:
|
|
||||||
"bg-secondary text-secondary-foreground hover:bg-secondary/80 border-transparent",
|
|
||||||
destructive:
|
|
||||||
"bg-destructive text-destructive-foreground hover:bg-destructive/80 border-transparent",
|
|
||||||
outline: "text-foreground",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultVariants: {
|
|
||||||
variant: "default",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export type BadgeVariant = VariantProps<typeof badgeVariants>["variant"];
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import type { WithElementRef } from "bits-ui";
|
|
||||||
import type { HTMLAnchorAttributes } from "svelte/elements";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
href,
|
|
||||||
class: className,
|
|
||||||
variant = "default",
|
|
||||||
children,
|
|
||||||
...restProps
|
|
||||||
}: WithElementRef<HTMLAnchorAttributes> & {
|
|
||||||
variant?: BadgeVariant;
|
|
||||||
} = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<svelte:element
|
|
||||||
this={href ? "a" : "span"}
|
|
||||||
bind:this={ref}
|
|
||||||
{href}
|
|
||||||
class={cn(badgeVariants({ variant, className }))}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</svelte:element>
|
|
@ -1,2 +0,0 @@
|
|||||||
export { default as Badge } from "./badge.svelte";
|
|
||||||
export { badgeVariants, type BadgeVariant } from "./badge.svelte";
|
|
@ -1,40 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { DropdownMenu as DropdownMenuPrimitive, type WithoutChildrenOrChild } from "bits-ui";
|
|
||||||
import Check from "lucide-svelte/icons/check";
|
|
||||||
import Minus from "lucide-svelte/icons/minus";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
import type { Snippet } from "svelte";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
checked = $bindable(false),
|
|
||||||
indeterminate = $bindable(false),
|
|
||||||
class: className,
|
|
||||||
children: childrenProp,
|
|
||||||
...restProps
|
|
||||||
}: WithoutChildrenOrChild<DropdownMenuPrimitive.CheckboxItemProps> & {
|
|
||||||
children?: Snippet;
|
|
||||||
} = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<DropdownMenuPrimitive.CheckboxItem
|
|
||||||
bind:ref
|
|
||||||
bind:checked
|
|
||||||
bind:indeterminate
|
|
||||||
class={cn(
|
|
||||||
"data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{#snippet children({ checked, indeterminate })}
|
|
||||||
<span class="absolute left-2 flex size-3.5 items-center justify-center">
|
|
||||||
{#if indeterminate}
|
|
||||||
<Minus class="size-4" />
|
|
||||||
{:else}
|
|
||||||
<Check class={cn("size-4", !checked && "text-transparent")} />
|
|
||||||
{/if}
|
|
||||||
</span>
|
|
||||||
{@render childrenProp?.()}
|
|
||||||
{/snippet}
|
|
||||||
</DropdownMenuPrimitive.CheckboxItem>
|
|
@ -1,26 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
sideOffset = 4,
|
|
||||||
portalProps,
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: DropdownMenuPrimitive.ContentProps & {
|
|
||||||
portalProps?: DropdownMenuPrimitive.PortalProps;
|
|
||||||
} = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<DropdownMenuPrimitive.Portal {...portalProps}>
|
|
||||||
<DropdownMenuPrimitive.Content
|
|
||||||
bind:ref
|
|
||||||
{sideOffset}
|
|
||||||
class={cn(
|
|
||||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-md outline-none",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
</DropdownMenuPrimitive.Portal>
|
|
@ -1,19 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
inset,
|
|
||||||
...restProps
|
|
||||||
}: DropdownMenuPrimitive.GroupHeadingProps & {
|
|
||||||
inset?: boolean;
|
|
||||||
} = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<DropdownMenuPrimitive.GroupHeading
|
|
||||||
bind:ref
|
|
||||||
class={cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
@ -1,23 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
inset,
|
|
||||||
...restProps
|
|
||||||
}: DropdownMenuPrimitive.ItemProps & {
|
|
||||||
inset?: boolean;
|
|
||||||
} = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<DropdownMenuPrimitive.Item
|
|
||||||
bind:ref
|
|
||||||
class={cn(
|
|
||||||
"data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
||||||
inset && "pl-8",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
@ -1,23 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
import { type WithElementRef } from "bits-ui";
|
|
||||||
import type { HTMLAttributes } from "svelte/elements";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
inset,
|
|
||||||
children,
|
|
||||||
...restProps
|
|
||||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
|
|
||||||
inset?: boolean;
|
|
||||||
} = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div
|
|
||||||
bind:this={ref}
|
|
||||||
class={cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</div>
|
|
@ -1,30 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { DropdownMenu as DropdownMenuPrimitive, type WithoutChild } from "bits-ui";
|
|
||||||
import Circle from "lucide-svelte/icons/circle";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
children: childrenProp,
|
|
||||||
...restProps
|
|
||||||
}: WithoutChild<DropdownMenuPrimitive.RadioItemProps> = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<DropdownMenuPrimitive.RadioItem
|
|
||||||
bind:ref
|
|
||||||
class={cn(
|
|
||||||
"data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{#snippet children({ checked })}
|
|
||||||
<span class="absolute left-2 flex size-3.5 items-center justify-center">
|
|
||||||
{#if checked}
|
|
||||||
<Circle class="size-2 fill-current" />
|
|
||||||
{/if}
|
|
||||||
</span>
|
|
||||||
{@render childrenProp?.({ checked })}
|
|
||||||
{/snippet}
|
|
||||||
</DropdownMenuPrimitive.RadioItem>
|
|
@ -1,16 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: DropdownMenuPrimitive.SeparatorProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<DropdownMenuPrimitive.Separator
|
|
||||||
bind:ref
|
|
||||||
class={cn("bg-muted -mx-1 my-1 h-px", className)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
@ -1,20 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import type { HTMLAttributes } from "svelte/elements";
|
|
||||||
import { type WithElementRef } from "bits-ui";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
children,
|
|
||||||
...restProps
|
|
||||||
}: WithElementRef<HTMLAttributes<HTMLSpanElement>> = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<span
|
|
||||||
bind:this={ref}
|
|
||||||
class={cn("ml-auto text-xs tracking-widest opacity-60", className)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</span>
|
|
@ -1,19 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: DropdownMenuPrimitive.SubContentProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<DropdownMenuPrimitive.SubContent
|
|
||||||
bind:ref
|
|
||||||
class={cn(
|
|
||||||
"bg-popover text-popover-foreground z-50 min-w-[8rem] rounded-md border p-1 shadow-lg focus:outline-none",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
@ -1,28 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
|
|
||||||
import ChevronRight from "lucide-svelte/icons/chevron-right";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
inset,
|
|
||||||
children,
|
|
||||||
...restProps
|
|
||||||
}: DropdownMenuPrimitive.SubTriggerProps & {
|
|
||||||
inset?: boolean;
|
|
||||||
} = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<DropdownMenuPrimitive.SubTrigger
|
|
||||||
bind:ref
|
|
||||||
class={cn(
|
|
||||||
"data-[highlighted]:bg-accent data-[state=open]:bg-accent flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
||||||
inset && "pl-8",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
<ChevronRight class="ml-auto" />
|
|
||||||
</DropdownMenuPrimitive.SubTrigger>
|
|
@ -1,50 +0,0 @@
|
|||||||
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
|
|
||||||
import CheckboxItem from "./dropdown-menu-checkbox-item.svelte";
|
|
||||||
import Content from "./dropdown-menu-content.svelte";
|
|
||||||
import GroupHeading from "./dropdown-menu-group-heading.svelte";
|
|
||||||
import Item from "./dropdown-menu-item.svelte";
|
|
||||||
import Label from "./dropdown-menu-label.svelte";
|
|
||||||
import RadioItem from "./dropdown-menu-radio-item.svelte";
|
|
||||||
import Separator from "./dropdown-menu-separator.svelte";
|
|
||||||
import Shortcut from "./dropdown-menu-shortcut.svelte";
|
|
||||||
import SubContent from "./dropdown-menu-sub-content.svelte";
|
|
||||||
import SubTrigger from "./dropdown-menu-sub-trigger.svelte";
|
|
||||||
|
|
||||||
const Sub = DropdownMenuPrimitive.Sub;
|
|
||||||
const Root = DropdownMenuPrimitive.Root;
|
|
||||||
const Trigger = DropdownMenuPrimitive.Trigger;
|
|
||||||
const Group = DropdownMenuPrimitive.Group;
|
|
||||||
const RadioGroup = DropdownMenuPrimitive.RadioGroup;
|
|
||||||
|
|
||||||
export {
|
|
||||||
CheckboxItem,
|
|
||||||
Content,
|
|
||||||
Root as DropdownMenu,
|
|
||||||
CheckboxItem as DropdownMenuCheckboxItem,
|
|
||||||
Content as DropdownMenuContent,
|
|
||||||
Group as DropdownMenuGroup,
|
|
||||||
GroupHeading as DropdownMenuGroupHeading,
|
|
||||||
Item as DropdownMenuItem,
|
|
||||||
Label as DropdownMenuLabel,
|
|
||||||
RadioGroup as DropdownMenuRadioGroup,
|
|
||||||
RadioItem as DropdownMenuRadioItem,
|
|
||||||
Separator as DropdownMenuSeparator,
|
|
||||||
Shortcut as DropdownMenuShortcut,
|
|
||||||
Sub as DropdownMenuSub,
|
|
||||||
SubContent as DropdownMenuSubContent,
|
|
||||||
SubTrigger as DropdownMenuSubTrigger,
|
|
||||||
Trigger as DropdownMenuTrigger,
|
|
||||||
Group,
|
|
||||||
GroupHeading,
|
|
||||||
Item,
|
|
||||||
Label,
|
|
||||||
RadioGroup,
|
|
||||||
RadioItem,
|
|
||||||
Root,
|
|
||||||
Separator,
|
|
||||||
Shortcut,
|
|
||||||
Sub,
|
|
||||||
SubContent,
|
|
||||||
SubTrigger,
|
|
||||||
Trigger,
|
|
||||||
};
|
|
@ -1,6 +1,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { onMount } from "svelte";
|
||||||
import ezppLogo from "../../../../assets/logo.png";
|
import ezppLogo from "../../../../assets/logo.png";
|
||||||
|
import { osudirect } from "@/api/osudirect";
|
||||||
import { playAudio } from "@/utils";
|
import { playAudio } from "@/utils";
|
||||||
|
import { BeatmapDecoder } from "osu-parsers";
|
||||||
|
|
||||||
type logoProps = {
|
type logoProps = {
|
||||||
extended: boolean;
|
extended: boolean;
|
||||||
@ -10,7 +13,57 @@
|
|||||||
let { extended, onclick }: logoProps = $props();
|
let { extended, onclick }: logoProps = $props();
|
||||||
|
|
||||||
let hovered = $state(false);
|
let hovered = $state(false);
|
||||||
const bpm = $state(130);
|
let bpm = $state(150); // 1000 * 60 / bpm
|
||||||
|
let lastTimeout: number | undefined = undefined;
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
const beatmapId = 2226722;
|
||||||
|
const beatmapData = await osudirect.osu(beatmapId);
|
||||||
|
if (beatmapData) {
|
||||||
|
const decoder = new BeatmapDecoder();
|
||||||
|
const beatmap = decoder.decodeFromString(beatmapData);
|
||||||
|
console.log(beatmap);
|
||||||
|
|
||||||
|
const audio = new Audio(
|
||||||
|
`https://osu.direct/api/media/audio/${beatmapId}`
|
||||||
|
);
|
||||||
|
audio.volume = 0.3;
|
||||||
|
|
||||||
|
// Function to play the heartbeat sound
|
||||||
|
const playHeartbeat = () => {
|
||||||
|
playAudio("/audio/menuHeartbeat.mp3", hovered ? 1 : 0.3);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to synchronize the heartbeat with the song
|
||||||
|
const syncHeartbeat = () => {
|
||||||
|
const currentTime = audio.currentTime * 1000; // Convert to milliseconds
|
||||||
|
const timingPoint = beatmap.controlPoints.timingPointAt(currentTime);
|
||||||
|
if (timingPoint && bpm !== timingPoint.bpm) {
|
||||||
|
bpm = timingPoint.bpm;
|
||||||
|
|
||||||
|
if (lastTimeout) {
|
||||||
|
clearTimeout(lastTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
const interval = (1000 * 60) / bpm; // Interval in milliseconds
|
||||||
|
const nextBeat = interval - (currentTime % interval); // Time to the next beat
|
||||||
|
setTimeout(() => {
|
||||||
|
playHeartbeat();
|
||||||
|
lastTimeout = setInterval(playHeartbeat, interval);
|
||||||
|
}, nextBeat);
|
||||||
|
}
|
||||||
|
// Continue syncing
|
||||||
|
requestAnimationFrame(syncHeartbeat);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Start playback and syncing
|
||||||
|
audio.addEventListener("play", () => {
|
||||||
|
syncHeartbeat();
|
||||||
|
});
|
||||||
|
|
||||||
|
await audio.play();
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@ -30,9 +83,9 @@
|
|||||||
onmouseleave={() => (hovered = false)}
|
onmouseleave={() => (hovered = false)}
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
if (extended) {
|
if (extended) {
|
||||||
playAudio("/audio/menuBack.wav", 0.35);
|
playAudio("/audio/menuBack.wav", 1);
|
||||||
} else {
|
} else {
|
||||||
playAudio("/audio/menuHit.wav", 0.35);
|
playAudio("/audio/menuHit.wav", 1);
|
||||||
}
|
}
|
||||||
onclick();
|
onclick();
|
||||||
}}
|
}}
|
||||||
|
@ -3,4 +3,3 @@
|
|||||||
// See: https://v2.tauri.app/start/frontend/sveltekit/ for more info
|
// See: https://v2.tauri.app/start/frontend/sveltekit/ for more info
|
||||||
export const prerender = true;
|
export const prerender = true;
|
||||||
export const ssr = false;
|
export const ssr = false;
|
||||||
|
|
||||||
|
@ -3,56 +3,18 @@
|
|||||||
import Button from "@/components/ui/button/button.svelte";
|
import Button from "@/components/ui/button/button.svelte";
|
||||||
import Logo from "@/components/ui/logo/logo.svelte";
|
import Logo from "@/components/ui/logo/logo.svelte";
|
||||||
import * as Avatar from "@/components/ui/avatar";
|
import * as Avatar from "@/components/ui/avatar";
|
||||||
import * as DropdownMenu from "@/components/ui/dropdown-menu";
|
|
||||||
import Progressbar from "@/components/ui/progressbar/progressbar.svelte";
|
import Progressbar from "@/components/ui/progressbar/progressbar.svelte";
|
||||||
import Settings from "lucide-svelte/icons/settings";
|
|
||||||
import LogOut from "lucide-svelte/icons/log-out";
|
|
||||||
import Heart from "lucide-svelte/icons/heart";
|
|
||||||
import { badgeVariants } from "@/components/ui/badge";
|
|
||||||
import { twMerge } from "tailwind-merge";
|
|
||||||
let progress = $state(0);
|
let progress = $state(0);
|
||||||
let extended = $state(false);
|
let extended = $state(false);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="relative h-screen w-screen">
|
<div class="relative h-screen w-screen">
|
||||||
<Background />
|
<Background />
|
||||||
<div class="absolute z-20 top-2 right-2 py-7">
|
<div class="absolute top-2 right-2 py-7">
|
||||||
<DropdownMenu.Root>
|
<Avatar.Root>
|
||||||
<DropdownMenu.Trigger>
|
<Avatar.AvatarFallback>U</Avatar.AvatarFallback>
|
||||||
<div class="relative">
|
<Avatar.AvatarImage src="https://a.ez-pp.farm/1001"></Avatar.AvatarImage>
|
||||||
<p
|
</Avatar.Root>
|
||||||
class={twMerge(
|
|
||||||
badgeVariants(),
|
|
||||||
"p-0 h-5 w-5 absolute -right-0.5 -top-0.5 z-50 !bg-pink-600 border-2 border-pink-800 text-white"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Heart class="h-3 w-3 m-auto p-0" />
|
|
||||||
</p>
|
|
||||||
<Avatar.Root class="border-[3px] z-40">
|
|
||||||
<Avatar.AvatarFallback>U</Avatar.AvatarFallback>
|
|
||||||
<Avatar.AvatarImage src="https://a.ez-pp.farm/1001"
|
|
||||||
></Avatar.AvatarImage>
|
|
||||||
</Avatar.Root>
|
|
||||||
</div>
|
|
||||||
</DropdownMenu.Trigger>
|
|
||||||
<DropdownMenu.Content class="w-48 max-w-48 mx-2" side="bottom">
|
|
||||||
<DropdownMenu.Group>
|
|
||||||
<DropdownMenu.GroupHeading class="truncate">Hello, Quetzalcoatl!</DropdownMenu.GroupHeading>
|
|
||||||
<DropdownMenu.Separator />
|
|
||||||
<DropdownMenu.Group>
|
|
||||||
<DropdownMenu.Item class="cursor-pointer">
|
|
||||||
<Settings class="mr-2 size-4" />
|
|
||||||
<span>Settings</span>
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
</DropdownMenu.Group>
|
|
||||||
<DropdownMenu.Separator />
|
|
||||||
<DropdownMenu.Item class="cursor-pointer">
|
|
||||||
<LogOut class="mr-2 size-4" />
|
|
||||||
<span>Log out</span>
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
</DropdownMenu.Group>
|
|
||||||
</DropdownMenu.Content>
|
|
||||||
</DropdownMenu.Root>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
Loading…
x
Reference in New Issue
Block a user