A collection of memory-reading utilities for the game Noita
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

A giant blob of me rewriting everything into an egui app

Now doing small atomic commits, I promise

+8712 -372
+1
.envrc
··· 1 + use flake
+2
.gitignore
··· 1 1 /target 2 + /.direnv 3 + /result
+4470 -56
Cargo.lock
··· 3 3 version = 3 4 4 5 5 [[package]] 6 + name = "ab_glyph" 7 + version = "0.2.29" 8 + source = "registry+https://github.com/rust-lang/crates.io-index" 9 + checksum = "ec3672c180e71eeaaac3a541fbbc5f5ad4def8b747c595ad30d674e43049f7b0" 10 + dependencies = [ 11 + "ab_glyph_rasterizer", 12 + "owned_ttf_parser", 13 + ] 14 + 15 + [[package]] 16 + name = "ab_glyph_rasterizer" 17 + version = "0.1.8" 18 + source = "registry+https://github.com/rust-lang/crates.io-index" 19 + checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" 20 + 21 + [[package]] 22 + name = "accesskit" 23 + version = "0.16.1" 24 + source = "registry+https://github.com/rust-lang/crates.io-index" 25 + checksum = "ef7442f1f520649b8e11ee3af6caeec24123fed4b63bc36a85b67308d8514fdf" 26 + dependencies = [ 27 + "enumn", 28 + "serde", 29 + ] 30 + 31 + [[package]] 32 + name = "accesskit_atspi_common" 33 + version = "0.9.1" 34 + source = "registry+https://github.com/rust-lang/crates.io-index" 35 + checksum = "ab9d2b364833ae6e2eae9359a374108b1c5488877a9f43d26f0cb700560af9ae" 36 + dependencies = [ 37 + "accesskit", 38 + "accesskit_consumer", 39 + "atspi-common", 40 + "serde", 41 + "thiserror", 42 + "zvariant", 43 + ] 44 + 45 + [[package]] 46 + name = "accesskit_consumer" 47 + version = "0.24.1" 48 + source = "registry+https://github.com/rust-lang/crates.io-index" 49 + checksum = "7072a4f17b8e7440a88ce08eb657d1ec84be061b1a94be78f9699aa18e583885" 50 + dependencies = [ 51 + "accesskit", 52 + "immutable-chunkmap", 53 + ] 54 + 55 + [[package]] 56 + name = "accesskit_macos" 57 + version = "0.17.1" 58 + source = "registry+https://github.com/rust-lang/crates.io-index" 59 + checksum = "716698a26b5113812348a9f99ec250cb7b4154c89a83bc55a8b7d8678a1ecf02" 60 + dependencies = [ 61 + "accesskit", 62 + "accesskit_consumer", 63 + "objc2", 64 + "objc2-app-kit", 65 + "objc2-foundation", 66 + "once_cell", 67 + ] 68 + 69 + [[package]] 70 + name = "accesskit_unix" 71 + version = "0.12.1" 72 + source = "registry+https://github.com/rust-lang/crates.io-index" 73 + checksum = "fd552c7bae0cd2ba1131fb0cedad32f8915743e0ed086f989fd706431641f56e" 74 + dependencies = [ 75 + "accesskit", 76 + "accesskit_atspi_common", 77 + "async-channel", 78 + "async-executor", 79 + "async-task", 80 + "atspi", 81 + "futures-lite 1.13.0", 82 + "futures-util", 83 + "serde", 84 + "zbus", 85 + ] 86 + 87 + [[package]] 88 + name = "accesskit_windows" 89 + version = "0.23.0" 90 + source = "registry+https://github.com/rust-lang/crates.io-index" 91 + checksum = "4d134e9eb16c98b35d1ff7056a027d7482968bcab0d8b625e7e72895b748d705" 92 + dependencies = [ 93 + "accesskit", 94 + "accesskit_consumer", 95 + "paste", 96 + "static_assertions", 97 + "windows 0.58.0", 98 + "windows-core 0.58.0", 99 + ] 100 + 101 + [[package]] 102 + name = "accesskit_winit" 103 + version = "0.22.1" 104 + source = "registry+https://github.com/rust-lang/crates.io-index" 105 + checksum = "156c5066e7b3ac9a82fb80fc18dcee42f79967c47e0ff431bae23e7ee8412a83" 106 + dependencies = [ 107 + "accesskit", 108 + "accesskit_macos", 109 + "accesskit_unix", 110 + "accesskit_windows", 111 + "raw-window-handle", 112 + "winit", 113 + ] 114 + 115 + [[package]] 116 + name = "addr2line" 117 + version = "0.21.0" 118 + source = "registry+https://github.com/rust-lang/crates.io-index" 119 + checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" 120 + dependencies = [ 121 + "gimli", 122 + ] 123 + 124 + [[package]] 125 + name = "adler" 126 + version = "1.0.2" 127 + source = "registry+https://github.com/rust-lang/crates.io-index" 128 + checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 129 + 130 + [[package]] 131 + name = "adler2" 132 + version = "2.0.0" 133 + source = "registry+https://github.com/rust-lang/crates.io-index" 134 + checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 135 + 136 + [[package]] 137 + name = "ahash" 138 + version = "0.8.11" 139 + source = "registry+https://github.com/rust-lang/crates.io-index" 140 + checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" 141 + dependencies = [ 142 + "cfg-if", 143 + "getrandom", 144 + "once_cell", 145 + "serde", 146 + "version_check", 147 + "zerocopy", 148 + ] 149 + 150 + [[package]] 151 + name = "aho-corasick" 152 + version = "1.1.3" 153 + source = "registry+https://github.com/rust-lang/crates.io-index" 154 + checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 155 + dependencies = [ 156 + "memchr", 157 + ] 158 + 159 + [[package]] 160 + name = "allocator-api2" 161 + version = "0.2.18" 162 + source = "registry+https://github.com/rust-lang/crates.io-index" 163 + checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" 164 + 165 + [[package]] 166 + name = "android-activity" 167 + version = "0.6.0" 168 + source = "registry+https://github.com/rust-lang/crates.io-index" 169 + checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" 170 + dependencies = [ 171 + "android-properties", 172 + "bitflags 2.6.0", 173 + "cc", 174 + "cesu8", 175 + "jni", 176 + "jni-sys", 177 + "libc", 178 + "log", 179 + "ndk", 180 + "ndk-context", 181 + "ndk-sys 0.6.0+11769913", 182 + "num_enum", 183 + "thiserror", 184 + ] 185 + 186 + [[package]] 187 + name = "android-properties" 188 + version = "0.2.2" 189 + source = "registry+https://github.com/rust-lang/crates.io-index" 190 + checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" 191 + 192 + [[package]] 193 + name = "android-tzdata" 194 + version = "0.1.1" 195 + source = "registry+https://github.com/rust-lang/crates.io-index" 196 + checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 197 + 198 + [[package]] 199 + name = "android_system_properties" 200 + version = "0.1.5" 201 + source = "registry+https://github.com/rust-lang/crates.io-index" 202 + checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 203 + dependencies = [ 204 + "libc", 205 + ] 206 + 207 + [[package]] 6 208 name = "anstream" 7 209 version = "0.6.15" 8 210 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 38 240 source = "registry+https://github.com/rust-lang/crates.io-index" 39 241 checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" 40 242 dependencies = [ 41 - "windows-sys", 243 + "windows-sys 0.52.0", 42 244 ] 43 245 44 246 [[package]] ··· 48 250 checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" 49 251 dependencies = [ 50 252 "anstyle", 51 - "windows-sys", 253 + "windows-sys 0.52.0", 52 254 ] 53 255 54 256 [[package]] ··· 58 260 checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" 59 261 60 262 [[package]] 263 + name = "arboard" 264 + version = "3.4.1" 265 + source = "registry+https://github.com/rust-lang/crates.io-index" 266 + checksum = "df099ccb16cd014ff054ac1bf392c67feeef57164b05c42f037cd40f5d4357f4" 267 + dependencies = [ 268 + "clipboard-win", 269 + "log", 270 + "objc2", 271 + "objc2-app-kit", 272 + "objc2-foundation", 273 + "parking_lot", 274 + "x11rb", 275 + ] 276 + 277 + [[package]] 278 + name = "arrayref" 279 + version = "0.3.9" 280 + source = "registry+https://github.com/rust-lang/crates.io-index" 281 + checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" 282 + 283 + [[package]] 284 + name = "arrayvec" 285 + version = "0.7.6" 286 + source = "registry+https://github.com/rust-lang/crates.io-index" 287 + checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" 288 + 289 + [[package]] 290 + name = "as-raw-xcb-connection" 291 + version = "1.0.1" 292 + source = "registry+https://github.com/rust-lang/crates.io-index" 293 + checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" 294 + 295 + [[package]] 296 + name = "ash" 297 + version = "0.38.0+1.3.281" 298 + source = "registry+https://github.com/rust-lang/crates.io-index" 299 + checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" 300 + dependencies = [ 301 + "libloading", 302 + ] 303 + 304 + [[package]] 305 + name = "async-broadcast" 306 + version = "0.5.1" 307 + source = "registry+https://github.com/rust-lang/crates.io-index" 308 + checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b" 309 + dependencies = [ 310 + "event-listener 2.5.3", 311 + "futures-core", 312 + ] 313 + 314 + [[package]] 315 + name = "async-channel" 316 + version = "2.3.1" 317 + source = "registry+https://github.com/rust-lang/crates.io-index" 318 + checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" 319 + dependencies = [ 320 + "concurrent-queue", 321 + "event-listener-strategy", 322 + "futures-core", 323 + "pin-project-lite", 324 + ] 325 + 326 + [[package]] 327 + name = "async-executor" 328 + version = "1.13.1" 329 + source = "registry+https://github.com/rust-lang/crates.io-index" 330 + checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" 331 + dependencies = [ 332 + "async-task", 333 + "concurrent-queue", 334 + "fastrand 2.1.1", 335 + "futures-lite 2.3.0", 336 + "slab", 337 + ] 338 + 339 + [[package]] 340 + name = "async-fs" 341 + version = "1.6.0" 342 + source = "registry+https://github.com/rust-lang/crates.io-index" 343 + checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" 344 + dependencies = [ 345 + "async-lock 2.8.0", 346 + "autocfg", 347 + "blocking", 348 + "futures-lite 1.13.0", 349 + ] 350 + 351 + [[package]] 352 + name = "async-io" 353 + version = "1.13.0" 354 + source = "registry+https://github.com/rust-lang/crates.io-index" 355 + checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" 356 + dependencies = [ 357 + "async-lock 2.8.0", 358 + "autocfg", 359 + "cfg-if", 360 + "concurrent-queue", 361 + "futures-lite 1.13.0", 362 + "log", 363 + "parking", 364 + "polling 2.8.0", 365 + "rustix 0.37.27", 366 + "slab", 367 + "socket2 0.4.10", 368 + "waker-fn", 369 + ] 370 + 371 + [[package]] 372 + name = "async-io" 373 + version = "2.3.4" 374 + source = "registry+https://github.com/rust-lang/crates.io-index" 375 + checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" 376 + dependencies = [ 377 + "async-lock 3.4.0", 378 + "cfg-if", 379 + "concurrent-queue", 380 + "futures-io", 381 + "futures-lite 2.3.0", 382 + "parking", 383 + "polling 3.7.3", 384 + "rustix 0.38.37", 385 + "slab", 386 + "tracing", 387 + "windows-sys 0.59.0", 388 + ] 389 + 390 + [[package]] 391 + name = "async-lock" 392 + version = "2.8.0" 393 + source = "registry+https://github.com/rust-lang/crates.io-index" 394 + checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" 395 + dependencies = [ 396 + "event-listener 2.5.3", 397 + ] 398 + 399 + [[package]] 400 + name = "async-lock" 401 + version = "3.4.0" 402 + source = "registry+https://github.com/rust-lang/crates.io-index" 403 + checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" 404 + dependencies = [ 405 + "event-listener 5.3.1", 406 + "event-listener-strategy", 407 + "pin-project-lite", 408 + ] 409 + 410 + [[package]] 411 + name = "async-process" 412 + version = "1.8.1" 413 + source = "registry+https://github.com/rust-lang/crates.io-index" 414 + checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" 415 + dependencies = [ 416 + "async-io 1.13.0", 417 + "async-lock 2.8.0", 418 + "async-signal", 419 + "blocking", 420 + "cfg-if", 421 + "event-listener 3.1.0", 422 + "futures-lite 1.13.0", 423 + "rustix 0.38.37", 424 + "windows-sys 0.48.0", 425 + ] 426 + 427 + [[package]] 428 + name = "async-recursion" 429 + version = "1.1.1" 430 + source = "registry+https://github.com/rust-lang/crates.io-index" 431 + checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" 432 + dependencies = [ 433 + "proc-macro2", 434 + "quote", 435 + "syn 2.0.79", 436 + ] 437 + 438 + [[package]] 439 + name = "async-signal" 440 + version = "0.2.10" 441 + source = "registry+https://github.com/rust-lang/crates.io-index" 442 + checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" 443 + dependencies = [ 444 + "async-io 2.3.4", 445 + "async-lock 3.4.0", 446 + "atomic-waker", 447 + "cfg-if", 448 + "futures-core", 449 + "futures-io", 450 + "rustix 0.38.37", 451 + "signal-hook-registry", 452 + "slab", 453 + "windows-sys 0.59.0", 454 + ] 455 + 456 + [[package]] 457 + name = "async-stream" 458 + version = "0.3.6" 459 + source = "registry+https://github.com/rust-lang/crates.io-index" 460 + checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" 461 + dependencies = [ 462 + "async-stream-impl", 463 + "futures-core", 464 + "pin-project-lite", 465 + ] 466 + 467 + [[package]] 468 + name = "async-stream-impl" 469 + version = "0.3.6" 470 + source = "registry+https://github.com/rust-lang/crates.io-index" 471 + checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" 472 + dependencies = [ 473 + "proc-macro2", 474 + "quote", 475 + "syn 2.0.79", 476 + ] 477 + 478 + [[package]] 479 + name = "async-task" 480 + version = "4.7.1" 481 + source = "registry+https://github.com/rust-lang/crates.io-index" 482 + checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" 483 + 484 + [[package]] 485 + name = "async-trait" 486 + version = "0.1.83" 487 + source = "registry+https://github.com/rust-lang/crates.io-index" 488 + checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" 489 + dependencies = [ 490 + "proc-macro2", 491 + "quote", 492 + "syn 2.0.79", 493 + ] 494 + 495 + [[package]] 496 + name = "atomic-waker" 497 + version = "1.1.2" 498 + source = "registry+https://github.com/rust-lang/crates.io-index" 499 + checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 500 + 501 + [[package]] 502 + name = "atspi" 503 + version = "0.19.0" 504 + source = "registry+https://github.com/rust-lang/crates.io-index" 505 + checksum = "6059f350ab6f593ea00727b334265c4dfc7fd442ee32d264794bd9bdc68e87ca" 506 + dependencies = [ 507 + "atspi-common", 508 + "atspi-connection", 509 + "atspi-proxies", 510 + ] 511 + 512 + [[package]] 513 + name = "atspi-common" 514 + version = "0.3.0" 515 + source = "registry+https://github.com/rust-lang/crates.io-index" 516 + checksum = "92af95f966d2431f962bc632c2e68eda7777330158bf640c4af4249349b2cdf5" 517 + dependencies = [ 518 + "enumflags2", 519 + "serde", 520 + "static_assertions", 521 + "zbus", 522 + "zbus_names", 523 + "zvariant", 524 + ] 525 + 526 + [[package]] 527 + name = "atspi-connection" 528 + version = "0.3.0" 529 + source = "registry+https://github.com/rust-lang/crates.io-index" 530 + checksum = "a0c65e7d70f86d4c0e3b2d585d9bf3f979f0b19d635a336725a88d279f76b939" 531 + dependencies = [ 532 + "atspi-common", 533 + "atspi-proxies", 534 + "futures-lite 1.13.0", 535 + "zbus", 536 + ] 537 + 538 + [[package]] 539 + name = "atspi-proxies" 540 + version = "0.3.0" 541 + source = "registry+https://github.com/rust-lang/crates.io-index" 542 + checksum = "6495661273703e7a229356dcbe8c8f38223d697aacfaf0e13590a9ac9977bb52" 543 + dependencies = [ 544 + "atspi-common", 545 + "serde", 546 + "zbus", 547 + ] 548 + 549 + [[package]] 550 + name = "autocfg" 551 + version = "1.4.0" 552 + source = "registry+https://github.com/rust-lang/crates.io-index" 553 + checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 554 + 555 + [[package]] 556 + name = "backtrace" 557 + version = "0.3.71" 558 + source = "registry+https://github.com/rust-lang/crates.io-index" 559 + checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" 560 + dependencies = [ 561 + "addr2line", 562 + "cc", 563 + "cfg-if", 564 + "libc", 565 + "miniz_oxide 0.7.4", 566 + "object", 567 + "rustc-demangle", 568 + ] 569 + 570 + [[package]] 571 + name = "base64" 572 + version = "0.21.7" 573 + source = "registry+https://github.com/rust-lang/crates.io-index" 574 + checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" 575 + 576 + [[package]] 577 + name = "base64" 578 + version = "0.22.1" 579 + source = "registry+https://github.com/rust-lang/crates.io-index" 580 + checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 581 + 582 + [[package]] 583 + name = "bit-set" 584 + version = "0.6.0" 585 + source = "registry+https://github.com/rust-lang/crates.io-index" 586 + checksum = "f0481a0e032742109b1133a095184ee93d88f3dc9e0d28a5d033dc77a073f44f" 587 + dependencies = [ 588 + "bit-vec", 589 + ] 590 + 591 + [[package]] 592 + name = "bit-vec" 593 + version = "0.7.0" 594 + source = "registry+https://github.com/rust-lang/crates.io-index" 595 + checksum = "d2c54ff287cfc0a34f38a6b832ea1bd8e448a330b3e40a50859e6488bee07f22" 596 + 597 + [[package]] 598 + name = "bitflags" 599 + version = "1.3.2" 600 + source = "registry+https://github.com/rust-lang/crates.io-index" 601 + checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 602 + 603 + [[package]] 604 + name = "bitflags" 605 + version = "2.6.0" 606 + source = "registry+https://github.com/rust-lang/crates.io-index" 607 + checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 608 + dependencies = [ 609 + "serde", 610 + ] 611 + 612 + [[package]] 613 + name = "block" 614 + version = "0.1.6" 615 + source = "registry+https://github.com/rust-lang/crates.io-index" 616 + checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" 617 + 618 + [[package]] 619 + name = "block-buffer" 620 + version = "0.10.4" 621 + source = "registry+https://github.com/rust-lang/crates.io-index" 622 + checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 623 + dependencies = [ 624 + "generic-array", 625 + ] 626 + 627 + [[package]] 628 + name = "block2" 629 + version = "0.5.1" 630 + source = "registry+https://github.com/rust-lang/crates.io-index" 631 + checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" 632 + dependencies = [ 633 + "objc2", 634 + ] 635 + 636 + [[package]] 637 + name = "blocking" 638 + version = "1.6.1" 639 + source = "registry+https://github.com/rust-lang/crates.io-index" 640 + checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" 641 + dependencies = [ 642 + "async-channel", 643 + "async-task", 644 + "futures-io", 645 + "futures-lite 2.3.0", 646 + "piper", 647 + ] 648 + 649 + [[package]] 650 + name = "bumpalo" 651 + version = "3.16.0" 652 + source = "registry+https://github.com/rust-lang/crates.io-index" 653 + checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 654 + 655 + [[package]] 61 656 name = "bytemuck" 62 657 version = "1.18.0" 63 658 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 68 663 69 664 [[package]] 70 665 name = "bytemuck_derive" 71 - version = "1.7.1" 666 + version = "1.8.0" 72 667 source = "registry+https://github.com/rust-lang/crates.io-index" 73 - checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26" 668 + checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec" 74 669 dependencies = [ 75 670 "proc-macro2", 76 671 "quote", 77 - "syn", 672 + "syn 2.0.79", 673 + ] 674 + 675 + [[package]] 676 + name = "byteorder" 677 + version = "1.5.0" 678 + source = "registry+https://github.com/rust-lang/crates.io-index" 679 + checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 680 + 681 + [[package]] 682 + name = "byteorder-lite" 683 + version = "0.1.0" 684 + source = "registry+https://github.com/rust-lang/crates.io-index" 685 + checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" 686 + 687 + [[package]] 688 + name = "bytes" 689 + version = "1.7.2" 690 + source = "registry+https://github.com/rust-lang/crates.io-index" 691 + checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" 692 + 693 + [[package]] 694 + name = "calloop" 695 + version = "0.13.0" 696 + source = "registry+https://github.com/rust-lang/crates.io-index" 697 + checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" 698 + dependencies = [ 699 + "bitflags 2.6.0", 700 + "log", 701 + "polling 3.7.3", 702 + "rustix 0.38.37", 703 + "slab", 704 + "thiserror", 705 + ] 706 + 707 + [[package]] 708 + name = "calloop-wayland-source" 709 + version = "0.3.0" 710 + source = "registry+https://github.com/rust-lang/crates.io-index" 711 + checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" 712 + dependencies = [ 713 + "calloop", 714 + "rustix 0.38.37", 715 + "wayland-backend", 716 + "wayland-client", 717 + ] 718 + 719 + [[package]] 720 + name = "cc" 721 + version = "1.1.28" 722 + source = "registry+https://github.com/rust-lang/crates.io-index" 723 + checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1" 724 + dependencies = [ 725 + "jobserver", 726 + "libc", 727 + "shlex", 728 + ] 729 + 730 + [[package]] 731 + name = "cesu8" 732 + version = "1.1.0" 733 + source = "registry+https://github.com/rust-lang/crates.io-index" 734 + checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" 735 + 736 + [[package]] 737 + name = "cfg-if" 738 + version = "1.0.0" 739 + source = "registry+https://github.com/rust-lang/crates.io-index" 740 + checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 741 + 742 + [[package]] 743 + name = "cfg_aliases" 744 + version = "0.1.1" 745 + source = "registry+https://github.com/rust-lang/crates.io-index" 746 + checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" 747 + 748 + [[package]] 749 + name = "cfg_aliases" 750 + version = "0.2.1" 751 + source = "registry+https://github.com/rust-lang/crates.io-index" 752 + checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 753 + 754 + [[package]] 755 + name = "cgl" 756 + version = "0.3.2" 757 + source = "registry+https://github.com/rust-lang/crates.io-index" 758 + checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" 759 + dependencies = [ 760 + "libc", 761 + ] 762 + 763 + [[package]] 764 + name = "chrono" 765 + version = "0.4.38" 766 + source = "registry+https://github.com/rust-lang/crates.io-index" 767 + checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" 768 + dependencies = [ 769 + "android-tzdata", 770 + "iana-time-zone", 771 + "num-traits", 772 + "serde", 773 + "windows-targets 0.52.6", 78 774 ] 79 775 80 776 [[package]] 81 777 name = "clap" 82 - version = "4.5.17" 778 + version = "4.5.19" 83 779 source = "registry+https://github.com/rust-lang/crates.io-index" 84 - checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" 780 + checksum = "7be5744db7978a28d9df86a214130d106a89ce49644cbc4e3f0c22c3fba30615" 85 781 dependencies = [ 86 782 "clap_builder", 87 783 "clap_derive", ··· 89 785 90 786 [[package]] 91 787 name = "clap_builder" 92 - version = "4.5.17" 788 + version = "4.5.19" 93 789 source = "registry+https://github.com/rust-lang/crates.io-index" 94 - checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" 790 + checksum = "a5fbc17d3ef8278f55b282b2a2e75ae6f6c7d4bb70ed3d0382375104bfafdb4b" 95 791 dependencies = [ 96 792 "anstream", 97 793 "anstyle", ··· 101 797 102 798 [[package]] 103 799 name = "clap_derive" 104 - version = "4.5.13" 800 + version = "4.5.18" 105 801 source = "registry+https://github.com/rust-lang/crates.io-index" 106 - checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" 802 + checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" 107 803 dependencies = [ 108 804 "heck", 109 805 "proc-macro2", 110 806 "quote", 111 - "syn", 807 + "syn 2.0.79", 112 808 ] 113 809 114 810 [[package]] ··· 118 814 checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" 119 815 120 816 [[package]] 817 + name = "clipboard-win" 818 + version = "5.4.0" 819 + source = "registry+https://github.com/rust-lang/crates.io-index" 820 + checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" 821 + dependencies = [ 822 + "error-code", 823 + ] 824 + 825 + [[package]] 826 + name = "codespan-reporting" 827 + version = "0.11.1" 828 + source = "registry+https://github.com/rust-lang/crates.io-index" 829 + checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" 830 + dependencies = [ 831 + "termcolor", 832 + "unicode-width", 833 + ] 834 + 835 + [[package]] 836 + name = "color-eyre" 837 + version = "0.6.3" 838 + source = "registry+https://github.com/rust-lang/crates.io-index" 839 + checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" 840 + dependencies = [ 841 + "backtrace", 842 + "color-spantrace", 843 + "eyre", 844 + "indenter", 845 + "once_cell", 846 + "owo-colors", 847 + "tracing-error", 848 + ] 849 + 850 + [[package]] 851 + name = "color-spantrace" 852 + version = "0.2.1" 853 + source = "registry+https://github.com/rust-lang/crates.io-index" 854 + checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" 855 + dependencies = [ 856 + "once_cell", 857 + "owo-colors", 858 + "tracing-core", 859 + "tracing-error", 860 + ] 861 + 862 + [[package]] 121 863 name = "colorchoice" 122 864 version = "1.0.2" 123 865 source = "registry+https://github.com/rust-lang/crates.io-index" 124 866 checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" 125 867 126 868 [[package]] 869 + name = "com" 870 + version = "0.6.0" 871 + source = "registry+https://github.com/rust-lang/crates.io-index" 872 + checksum = "7e17887fd17353b65b1b2ef1c526c83e26cd72e74f598a8dc1bee13a48f3d9f6" 873 + dependencies = [ 874 + "com_macros", 875 + ] 876 + 877 + [[package]] 878 + name = "com_macros" 879 + version = "0.6.0" 880 + source = "registry+https://github.com/rust-lang/crates.io-index" 881 + checksum = "d375883580a668c7481ea6631fc1a8863e33cc335bf56bfad8d7e6d4b04b13a5" 882 + dependencies = [ 883 + "com_macros_support", 884 + "proc-macro2", 885 + "syn 1.0.109", 886 + ] 887 + 888 + [[package]] 889 + name = "com_macros_support" 890 + version = "0.6.0" 891 + source = "registry+https://github.com/rust-lang/crates.io-index" 892 + checksum = "ad899a1087a9296d5644792d7cb72b8e34c1bec8e7d4fbc002230169a6e8710c" 893 + dependencies = [ 894 + "proc-macro2", 895 + "quote", 896 + "syn 1.0.109", 897 + ] 898 + 899 + [[package]] 900 + name = "combine" 901 + version = "4.6.7" 902 + source = "registry+https://github.com/rust-lang/crates.io-index" 903 + checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" 904 + dependencies = [ 905 + "bytes", 906 + "memchr", 907 + ] 908 + 909 + [[package]] 910 + name = "concurrent-queue" 911 + version = "2.5.0" 912 + source = "registry+https://github.com/rust-lang/crates.io-index" 913 + checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" 914 + dependencies = [ 915 + "crossbeam-utils", 916 + ] 917 + 918 + [[package]] 919 + name = "core-foundation" 920 + version = "0.9.4" 921 + source = "registry+https://github.com/rust-lang/crates.io-index" 922 + checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 923 + dependencies = [ 924 + "core-foundation-sys", 925 + "libc", 926 + ] 927 + 928 + [[package]] 929 + name = "core-foundation" 930 + version = "0.10.0" 931 + source = "registry+https://github.com/rust-lang/crates.io-index" 932 + checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" 933 + dependencies = [ 934 + "core-foundation-sys", 935 + "libc", 936 + ] 937 + 938 + [[package]] 127 939 name = "core-foundation-sys" 128 940 version = "0.8.7" 129 941 source = "registry+https://github.com/rust-lang/crates.io-index" 130 942 checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 131 943 132 944 [[package]] 945 + name = "core-graphics" 946 + version = "0.23.2" 947 + source = "registry+https://github.com/rust-lang/crates.io-index" 948 + checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" 949 + dependencies = [ 950 + "bitflags 1.3.2", 951 + "core-foundation 0.9.4", 952 + "core-graphics-types", 953 + "foreign-types", 954 + "libc", 955 + ] 956 + 957 + [[package]] 958 + name = "core-graphics-types" 959 + version = "0.1.3" 960 + source = "registry+https://github.com/rust-lang/crates.io-index" 961 + checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" 962 + dependencies = [ 963 + "bitflags 1.3.2", 964 + "core-foundation 0.9.4", 965 + "libc", 966 + ] 967 + 968 + [[package]] 969 + name = "cpufeatures" 970 + version = "0.2.14" 971 + source = "registry+https://github.com/rust-lang/crates.io-index" 972 + checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" 973 + dependencies = [ 974 + "libc", 975 + ] 976 + 977 + [[package]] 978 + name = "crc32fast" 979 + version = "1.4.2" 980 + source = "registry+https://github.com/rust-lang/crates.io-index" 981 + checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" 982 + dependencies = [ 983 + "cfg-if", 984 + ] 985 + 986 + [[package]] 133 987 name = "crossbeam-deque" 134 988 version = "0.8.5" 135 989 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 155 1009 checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" 156 1010 157 1011 [[package]] 1012 + name = "crypto-common" 1013 + version = "0.1.6" 1014 + source = "registry+https://github.com/rust-lang/crates.io-index" 1015 + checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 1016 + dependencies = [ 1017 + "generic-array", 1018 + "typenum", 1019 + ] 1020 + 1021 + [[package]] 1022 + name = "cursor-icon" 1023 + version = "1.1.0" 1024 + source = "registry+https://github.com/rust-lang/crates.io-index" 1025 + checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" 1026 + 1027 + [[package]] 1028 + name = "darling" 1029 + version = "0.20.10" 1030 + source = "registry+https://github.com/rust-lang/crates.io-index" 1031 + checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" 1032 + dependencies = [ 1033 + "darling_core", 1034 + "darling_macro", 1035 + ] 1036 + 1037 + [[package]] 1038 + name = "darling_core" 1039 + version = "0.20.10" 1040 + source = "registry+https://github.com/rust-lang/crates.io-index" 1041 + checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" 1042 + dependencies = [ 1043 + "fnv", 1044 + "ident_case", 1045 + "proc-macro2", 1046 + "quote", 1047 + "strsim", 1048 + "syn 2.0.79", 1049 + ] 1050 + 1051 + [[package]] 1052 + name = "darling_macro" 1053 + version = "0.20.10" 1054 + source = "registry+https://github.com/rust-lang/crates.io-index" 1055 + checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" 1056 + dependencies = [ 1057 + "darling_core", 1058 + "quote", 1059 + "syn 2.0.79", 1060 + ] 1061 + 1062 + [[package]] 1063 + name = "data-encoding" 1064 + version = "2.6.0" 1065 + source = "registry+https://github.com/rust-lang/crates.io-index" 1066 + checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" 1067 + 1068 + [[package]] 1069 + name = "deranged" 1070 + version = "0.3.11" 1071 + source = "registry+https://github.com/rust-lang/crates.io-index" 1072 + checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" 1073 + dependencies = [ 1074 + "powerfmt", 1075 + "serde", 1076 + ] 1077 + 1078 + [[package]] 1079 + name = "derivative" 1080 + version = "2.2.0" 1081 + source = "registry+https://github.com/rust-lang/crates.io-index" 1082 + checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" 1083 + dependencies = [ 1084 + "proc-macro2", 1085 + "quote", 1086 + "syn 1.0.109", 1087 + ] 1088 + 1089 + [[package]] 1090 + name = "derive_more" 1091 + version = "1.0.0" 1092 + source = "registry+https://github.com/rust-lang/crates.io-index" 1093 + checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" 1094 + dependencies = [ 1095 + "derive_more-impl", 1096 + ] 1097 + 1098 + [[package]] 1099 + name = "derive_more-impl" 1100 + version = "1.0.0" 1101 + source = "registry+https://github.com/rust-lang/crates.io-index" 1102 + checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" 1103 + dependencies = [ 1104 + "proc-macro2", 1105 + "quote", 1106 + "syn 2.0.79", 1107 + "unicode-xid", 1108 + ] 1109 + 1110 + [[package]] 1111 + name = "digest" 1112 + version = "0.10.7" 1113 + source = "registry+https://github.com/rust-lang/crates.io-index" 1114 + checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 1115 + dependencies = [ 1116 + "block-buffer", 1117 + "crypto-common", 1118 + ] 1119 + 1120 + [[package]] 1121 + name = "dispatch" 1122 + version = "0.2.0" 1123 + source = "registry+https://github.com/rust-lang/crates.io-index" 1124 + checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" 1125 + 1126 + [[package]] 1127 + name = "dlib" 1128 + version = "0.5.2" 1129 + source = "registry+https://github.com/rust-lang/crates.io-index" 1130 + checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" 1131 + dependencies = [ 1132 + "libloading", 1133 + ] 1134 + 1135 + [[package]] 1136 + name = "document-features" 1137 + version = "0.2.10" 1138 + source = "registry+https://github.com/rust-lang/crates.io-index" 1139 + checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0" 1140 + dependencies = [ 1141 + "litrs", 1142 + ] 1143 + 1144 + [[package]] 1145 + name = "downcast-rs" 1146 + version = "1.2.1" 1147 + source = "registry+https://github.com/rust-lang/crates.io-index" 1148 + checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" 1149 + 1150 + [[package]] 1151 + name = "dpi" 1152 + version = "0.1.1" 1153 + source = "registry+https://github.com/rust-lang/crates.io-index" 1154 + checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" 1155 + 1156 + [[package]] 1157 + name = "ecolor" 1158 + version = "0.29.1" 1159 + source = "registry+https://github.com/rust-lang/crates.io-index" 1160 + checksum = "775cfde491852059e386c4e1deb4aef381c617dc364184c6f6afee99b87c402b" 1161 + dependencies = [ 1162 + "bytemuck", 1163 + "emath", 1164 + "serde", 1165 + ] 1166 + 1167 + [[package]] 1168 + name = "eframe" 1169 + version = "0.29.1" 1170 + source = "registry+https://github.com/rust-lang/crates.io-index" 1171 + checksum = "8ac2645a9bf4826eb4e91488b1f17b8eaddeef09396706b2f14066461338e24f" 1172 + dependencies = [ 1173 + "ahash", 1174 + "bytemuck", 1175 + "document-features", 1176 + "egui", 1177 + "egui-wgpu", 1178 + "egui-winit", 1179 + "egui_glow", 1180 + "glow 0.14.1", 1181 + "glutin", 1182 + "glutin-winit", 1183 + "home", 1184 + "image", 1185 + "js-sys", 1186 + "log", 1187 + "objc2", 1188 + "objc2-app-kit", 1189 + "objc2-foundation", 1190 + "parking_lot", 1191 + "percent-encoding", 1192 + "pollster", 1193 + "raw-window-handle", 1194 + "ron", 1195 + "serde", 1196 + "static_assertions", 1197 + "wasm-bindgen", 1198 + "wasm-bindgen-futures", 1199 + "web-sys", 1200 + "web-time", 1201 + "wgpu", 1202 + "winapi", 1203 + "windows-sys 0.52.0", 1204 + "winit", 1205 + ] 1206 + 1207 + [[package]] 1208 + name = "egui" 1209 + version = "0.29.1" 1210 + source = "registry+https://github.com/rust-lang/crates.io-index" 1211 + checksum = "53eafabcce0cb2325a59a98736efe0bf060585b437763f8c476957fb274bb974" 1212 + dependencies = [ 1213 + "accesskit", 1214 + "ahash", 1215 + "emath", 1216 + "epaint", 1217 + "log", 1218 + "nohash-hasher", 1219 + "ron", 1220 + "serde", 1221 + ] 1222 + 1223 + [[package]] 1224 + name = "egui-wgpu" 1225 + version = "0.29.1" 1226 + source = "registry+https://github.com/rust-lang/crates.io-index" 1227 + checksum = "d00fd5d06d8405397e64a928fa0ef3934b3c30273ea7603e3dc4627b1f7a1a82" 1228 + dependencies = [ 1229 + "ahash", 1230 + "bytemuck", 1231 + "document-features", 1232 + "egui", 1233 + "epaint", 1234 + "log", 1235 + "thiserror", 1236 + "type-map", 1237 + "web-time", 1238 + "wgpu", 1239 + "winit", 1240 + ] 1241 + 1242 + [[package]] 1243 + name = "egui-winit" 1244 + version = "0.29.1" 1245 + source = "registry+https://github.com/rust-lang/crates.io-index" 1246 + checksum = "0a9c430f4f816340e8e8c1b20eec274186b1be6bc4c7dfc467ed50d57abc36c6" 1247 + dependencies = [ 1248 + "accesskit_winit", 1249 + "ahash", 1250 + "arboard", 1251 + "egui", 1252 + "log", 1253 + "raw-window-handle", 1254 + "serde", 1255 + "smithay-clipboard", 1256 + "web-time", 1257 + "webbrowser", 1258 + "winit", 1259 + ] 1260 + 1261 + [[package]] 1262 + name = "egui_extras" 1263 + version = "0.29.1" 1264 + source = "registry+https://github.com/rust-lang/crates.io-index" 1265 + checksum = "bf3c1f5cd8dfe2ade470a218696c66cf556fcfd701e7830fa2e9f4428292a2a1" 1266 + dependencies = [ 1267 + "ahash", 1268 + "egui", 1269 + "enum-map", 1270 + "log", 1271 + "mime_guess2", 1272 + ] 1273 + 1274 + [[package]] 1275 + name = "egui_glow" 1276 + version = "0.29.1" 1277 + source = "registry+https://github.com/rust-lang/crates.io-index" 1278 + checksum = "0e39bccc683cd43adab530d8f21a13eb91e80de10bcc38c3f1c16601b6f62b26" 1279 + dependencies = [ 1280 + "ahash", 1281 + "bytemuck", 1282 + "egui", 1283 + "glow 0.14.1", 1284 + "log", 1285 + "memoffset 0.9.1", 1286 + "wasm-bindgen", 1287 + "web-sys", 1288 + "winit", 1289 + ] 1290 + 1291 + [[package]] 158 1292 name = "either" 159 1293 version = "1.13.0" 160 1294 source = "registry+https://github.com/rust-lang/crates.io-index" 161 1295 checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" 162 1296 163 1297 [[package]] 1298 + name = "emath" 1299 + version = "0.29.1" 1300 + source = "registry+https://github.com/rust-lang/crates.io-index" 1301 + checksum = "b1fe0049ce51d0fb414d029e668dd72eb30bc2b739bf34296ed97bd33df544f3" 1302 + dependencies = [ 1303 + "bytemuck", 1304 + "serde", 1305 + ] 1306 + 1307 + [[package]] 1308 + name = "enum-map" 1309 + version = "2.7.3" 1310 + source = "registry+https://github.com/rust-lang/crates.io-index" 1311 + checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9" 1312 + dependencies = [ 1313 + "enum-map-derive", 1314 + "serde", 1315 + ] 1316 + 1317 + [[package]] 1318 + name = "enum-map-derive" 1319 + version = "0.17.0" 1320 + source = "registry+https://github.com/rust-lang/crates.io-index" 1321 + checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" 1322 + dependencies = [ 1323 + "proc-macro2", 1324 + "quote", 1325 + "syn 2.0.79", 1326 + ] 1327 + 1328 + [[package]] 1329 + name = "enumflags2" 1330 + version = "0.7.10" 1331 + source = "registry+https://github.com/rust-lang/crates.io-index" 1332 + checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d" 1333 + dependencies = [ 1334 + "enumflags2_derive", 1335 + "serde", 1336 + ] 1337 + 1338 + [[package]] 1339 + name = "enumflags2_derive" 1340 + version = "0.7.10" 1341 + source = "registry+https://github.com/rust-lang/crates.io-index" 1342 + checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" 1343 + dependencies = [ 1344 + "proc-macro2", 1345 + "quote", 1346 + "syn 2.0.79", 1347 + ] 1348 + 1349 + [[package]] 1350 + name = "enumn" 1351 + version = "0.1.14" 1352 + source = "registry+https://github.com/rust-lang/crates.io-index" 1353 + checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" 1354 + dependencies = [ 1355 + "proc-macro2", 1356 + "quote", 1357 + "syn 2.0.79", 1358 + ] 1359 + 1360 + [[package]] 1361 + name = "epaint" 1362 + version = "0.29.1" 1363 + source = "registry+https://github.com/rust-lang/crates.io-index" 1364 + checksum = "a32af8da821bd4f43f2c137e295459ee2e1661d87ca8779dfa0eaf45d870e20f" 1365 + dependencies = [ 1366 + "ab_glyph", 1367 + "ahash", 1368 + "bytemuck", 1369 + "ecolor", 1370 + "emath", 1371 + "epaint_default_fonts", 1372 + "log", 1373 + "nohash-hasher", 1374 + "parking_lot", 1375 + "serde", 1376 + ] 1377 + 1378 + [[package]] 1379 + name = "epaint_default_fonts" 1380 + version = "0.29.1" 1381 + source = "registry+https://github.com/rust-lang/crates.io-index" 1382 + checksum = "483440db0b7993cf77a20314f08311dbe95675092405518c0677aa08c151a3ea" 1383 + 1384 + [[package]] 164 1385 name = "equivalent" 165 1386 version = "1.0.1" 166 1387 source = "registry+https://github.com/rust-lang/crates.io-index" 167 1388 checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 1389 + 1390 + [[package]] 1391 + name = "errno" 1392 + version = "0.3.9" 1393 + source = "registry+https://github.com/rust-lang/crates.io-index" 1394 + checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" 1395 + dependencies = [ 1396 + "libc", 1397 + "windows-sys 0.52.0", 1398 + ] 1399 + 1400 + [[package]] 1401 + name = "error-code" 1402 + version = "3.3.1" 1403 + source = "registry+https://github.com/rust-lang/crates.io-index" 1404 + checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" 1405 + 1406 + [[package]] 1407 + name = "event-listener" 1408 + version = "2.5.3" 1409 + source = "registry+https://github.com/rust-lang/crates.io-index" 1410 + checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" 1411 + 1412 + [[package]] 1413 + name = "event-listener" 1414 + version = "3.1.0" 1415 + source = "registry+https://github.com/rust-lang/crates.io-index" 1416 + checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" 1417 + dependencies = [ 1418 + "concurrent-queue", 1419 + "parking", 1420 + "pin-project-lite", 1421 + ] 1422 + 1423 + [[package]] 1424 + name = "event-listener" 1425 + version = "5.3.1" 1426 + source = "registry+https://github.com/rust-lang/crates.io-index" 1427 + checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" 1428 + dependencies = [ 1429 + "concurrent-queue", 1430 + "parking", 1431 + "pin-project-lite", 1432 + ] 1433 + 1434 + [[package]] 1435 + name = "event-listener-strategy" 1436 + version = "0.5.2" 1437 + source = "registry+https://github.com/rust-lang/crates.io-index" 1438 + checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" 1439 + dependencies = [ 1440 + "event-listener 5.3.1", 1441 + "pin-project-lite", 1442 + ] 1443 + 1444 + [[package]] 1445 + name = "eyre" 1446 + version = "0.6.12" 1447 + source = "registry+https://github.com/rust-lang/crates.io-index" 1448 + checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" 1449 + dependencies = [ 1450 + "indenter", 1451 + "once_cell", 1452 + ] 1453 + 1454 + [[package]] 1455 + name = "fastrand" 1456 + version = "1.9.0" 1457 + source = "registry+https://github.com/rust-lang/crates.io-index" 1458 + checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" 1459 + dependencies = [ 1460 + "instant", 1461 + ] 1462 + 1463 + [[package]] 1464 + name = "fastrand" 1465 + version = "2.1.1" 1466 + source = "registry+https://github.com/rust-lang/crates.io-index" 1467 + checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" 1468 + 1469 + [[package]] 1470 + name = "fdeflate" 1471 + version = "0.3.5" 1472 + source = "registry+https://github.com/rust-lang/crates.io-index" 1473 + checksum = "d8090f921a24b04994d9929e204f50b498a33ea6ba559ffaa05e04f7ee7fb5ab" 1474 + dependencies = [ 1475 + "simd-adler32", 1476 + ] 1477 + 1478 + [[package]] 1479 + name = "flate2" 1480 + version = "1.0.34" 1481 + source = "registry+https://github.com/rust-lang/crates.io-index" 1482 + checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" 1483 + dependencies = [ 1484 + "crc32fast", 1485 + "miniz_oxide 0.8.0", 1486 + ] 1487 + 1488 + [[package]] 1489 + name = "fnv" 1490 + version = "1.0.7" 1491 + source = "registry+https://github.com/rust-lang/crates.io-index" 1492 + checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 1493 + 1494 + [[package]] 1495 + name = "foreign-types" 1496 + version = "0.5.0" 1497 + source = "registry+https://github.com/rust-lang/crates.io-index" 1498 + checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" 1499 + dependencies = [ 1500 + "foreign-types-macros", 1501 + "foreign-types-shared", 1502 + ] 1503 + 1504 + [[package]] 1505 + name = "foreign-types-macros" 1506 + version = "0.2.3" 1507 + source = "registry+https://github.com/rust-lang/crates.io-index" 1508 + checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" 1509 + dependencies = [ 1510 + "proc-macro2", 1511 + "quote", 1512 + "syn 2.0.79", 1513 + ] 1514 + 1515 + [[package]] 1516 + name = "foreign-types-shared" 1517 + version = "0.3.1" 1518 + source = "registry+https://github.com/rust-lang/crates.io-index" 1519 + checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" 1520 + 1521 + [[package]] 1522 + name = "form_urlencoded" 1523 + version = "1.2.1" 1524 + source = "registry+https://github.com/rust-lang/crates.io-index" 1525 + checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 1526 + dependencies = [ 1527 + "percent-encoding", 1528 + ] 1529 + 1530 + [[package]] 1531 + name = "futures" 1532 + version = "0.3.31" 1533 + source = "registry+https://github.com/rust-lang/crates.io-index" 1534 + checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" 1535 + dependencies = [ 1536 + "futures-channel", 1537 + "futures-core", 1538 + "futures-executor", 1539 + "futures-io", 1540 + "futures-sink", 1541 + "futures-task", 1542 + "futures-util", 1543 + ] 1544 + 1545 + [[package]] 1546 + name = "futures-channel" 1547 + version = "0.3.31" 1548 + source = "registry+https://github.com/rust-lang/crates.io-index" 1549 + checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 1550 + dependencies = [ 1551 + "futures-core", 1552 + "futures-sink", 1553 + ] 1554 + 1555 + [[package]] 1556 + name = "futures-core" 1557 + version = "0.3.31" 1558 + source = "registry+https://github.com/rust-lang/crates.io-index" 1559 + checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 1560 + 1561 + [[package]] 1562 + name = "futures-executor" 1563 + version = "0.3.31" 1564 + source = "registry+https://github.com/rust-lang/crates.io-index" 1565 + checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" 1566 + dependencies = [ 1567 + "futures-core", 1568 + "futures-task", 1569 + "futures-util", 1570 + ] 1571 + 1572 + [[package]] 1573 + name = "futures-io" 1574 + version = "0.3.31" 1575 + source = "registry+https://github.com/rust-lang/crates.io-index" 1576 + checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 1577 + 1578 + [[package]] 1579 + name = "futures-lite" 1580 + version = "1.13.0" 1581 + source = "registry+https://github.com/rust-lang/crates.io-index" 1582 + checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" 1583 + dependencies = [ 1584 + "fastrand 1.9.0", 1585 + "futures-core", 1586 + "futures-io", 1587 + "memchr", 1588 + "parking", 1589 + "pin-project-lite", 1590 + "waker-fn", 1591 + ] 1592 + 1593 + [[package]] 1594 + name = "futures-lite" 1595 + version = "2.3.0" 1596 + source = "registry+https://github.com/rust-lang/crates.io-index" 1597 + checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" 1598 + dependencies = [ 1599 + "fastrand 2.1.1", 1600 + "futures-core", 1601 + "futures-io", 1602 + "parking", 1603 + "pin-project-lite", 1604 + ] 1605 + 1606 + [[package]] 1607 + name = "futures-macro" 1608 + version = "0.3.31" 1609 + source = "registry+https://github.com/rust-lang/crates.io-index" 1610 + checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 1611 + dependencies = [ 1612 + "proc-macro2", 1613 + "quote", 1614 + "syn 2.0.79", 1615 + ] 1616 + 1617 + [[package]] 1618 + name = "futures-sink" 1619 + version = "0.3.31" 1620 + source = "registry+https://github.com/rust-lang/crates.io-index" 1621 + checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 1622 + 1623 + [[package]] 1624 + name = "futures-task" 1625 + version = "0.3.31" 1626 + source = "registry+https://github.com/rust-lang/crates.io-index" 1627 + checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 1628 + 1629 + [[package]] 1630 + name = "futures-util" 1631 + version = "0.3.31" 1632 + source = "registry+https://github.com/rust-lang/crates.io-index" 1633 + checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 1634 + dependencies = [ 1635 + "futures-channel", 1636 + "futures-core", 1637 + "futures-io", 1638 + "futures-macro", 1639 + "futures-sink", 1640 + "futures-task", 1641 + "memchr", 1642 + "pin-project-lite", 1643 + "pin-utils", 1644 + "slab", 1645 + ] 1646 + 1647 + [[package]] 1648 + name = "generic-array" 1649 + version = "0.14.7" 1650 + source = "registry+https://github.com/rust-lang/crates.io-index" 1651 + checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 1652 + dependencies = [ 1653 + "typenum", 1654 + "version_check", 1655 + ] 1656 + 1657 + [[package]] 1658 + name = "gethostname" 1659 + version = "0.4.3" 1660 + source = "registry+https://github.com/rust-lang/crates.io-index" 1661 + checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" 1662 + dependencies = [ 1663 + "libc", 1664 + "windows-targets 0.48.5", 1665 + ] 1666 + 1667 + [[package]] 1668 + name = "getrandom" 1669 + version = "0.2.15" 1670 + source = "registry+https://github.com/rust-lang/crates.io-index" 1671 + checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 1672 + dependencies = [ 1673 + "cfg-if", 1674 + "libc", 1675 + "wasi", 1676 + ] 1677 + 1678 + [[package]] 1679 + name = "gimli" 1680 + version = "0.28.1" 1681 + source = "registry+https://github.com/rust-lang/crates.io-index" 1682 + checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" 1683 + 1684 + [[package]] 1685 + name = "gl_generator" 1686 + version = "0.14.0" 1687 + source = "registry+https://github.com/rust-lang/crates.io-index" 1688 + checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" 1689 + dependencies = [ 1690 + "khronos_api", 1691 + "log", 1692 + "xml-rs", 1693 + ] 1694 + 1695 + [[package]] 1696 + name = "glow" 1697 + version = "0.13.1" 1698 + source = "registry+https://github.com/rust-lang/crates.io-index" 1699 + checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1" 1700 + dependencies = [ 1701 + "js-sys", 1702 + "slotmap", 1703 + "wasm-bindgen", 1704 + "web-sys", 1705 + ] 1706 + 1707 + [[package]] 1708 + name = "glow" 1709 + version = "0.14.1" 1710 + source = "registry+https://github.com/rust-lang/crates.io-index" 1711 + checksum = "2f4a888dbe8181a7535853469c21c67ca9a1cea9460b16808fc018ea9e55d248" 1712 + dependencies = [ 1713 + "js-sys", 1714 + "slotmap", 1715 + "wasm-bindgen", 1716 + "web-sys", 1717 + ] 1718 + 1719 + [[package]] 1720 + name = "glutin" 1721 + version = "0.32.1" 1722 + source = "registry+https://github.com/rust-lang/crates.io-index" 1723 + checksum = "ec69412a0bf07ea7607e638b415447857a808846c2b685a43c8aa18bc6d5e499" 1724 + dependencies = [ 1725 + "bitflags 2.6.0", 1726 + "cfg_aliases 0.2.1", 1727 + "cgl", 1728 + "core-foundation 0.9.4", 1729 + "dispatch", 1730 + "glutin_egl_sys", 1731 + "glutin_glx_sys", 1732 + "glutin_wgl_sys", 1733 + "libloading", 1734 + "objc2", 1735 + "objc2-app-kit", 1736 + "objc2-foundation", 1737 + "once_cell", 1738 + "raw-window-handle", 1739 + "wayland-sys", 1740 + "windows-sys 0.52.0", 1741 + "x11-dl", 1742 + ] 1743 + 1744 + [[package]] 1745 + name = "glutin-winit" 1746 + version = "0.5.0" 1747 + source = "registry+https://github.com/rust-lang/crates.io-index" 1748 + checksum = "85edca7075f8fc728f28cb8fbb111a96c3b89e930574369e3e9c27eb75d3788f" 1749 + dependencies = [ 1750 + "cfg_aliases 0.2.1", 1751 + "glutin", 1752 + "raw-window-handle", 1753 + "winit", 1754 + ] 1755 + 1756 + [[package]] 1757 + name = "glutin_egl_sys" 1758 + version = "0.7.0" 1759 + source = "registry+https://github.com/rust-lang/crates.io-index" 1760 + checksum = "cae99fff4d2850dbe6fb8c1fa8e4fead5525bab715beaacfccf3fb994e01c827" 1761 + dependencies = [ 1762 + "gl_generator", 1763 + "windows-sys 0.52.0", 1764 + ] 1765 + 1766 + [[package]] 1767 + name = "glutin_glx_sys" 1768 + version = "0.6.0" 1769 + source = "registry+https://github.com/rust-lang/crates.io-index" 1770 + checksum = "9c2b2d3918e76e18e08796b55eb64e8fe6ec67d5a6b2e2a7e2edce224ad24c63" 1771 + dependencies = [ 1772 + "gl_generator", 1773 + "x11-dl", 1774 + ] 1775 + 1776 + [[package]] 1777 + name = "glutin_wgl_sys" 1778 + version = "0.6.0" 1779 + source = "registry+https://github.com/rust-lang/crates.io-index" 1780 + checksum = "0a4e1951bbd9434a81aa496fe59ccc2235af3820d27b85f9314e279609211e2c" 1781 + dependencies = [ 1782 + "gl_generator", 1783 + ] 1784 + 1785 + [[package]] 1786 + name = "gpu-alloc" 1787 + version = "0.6.0" 1788 + source = "registry+https://github.com/rust-lang/crates.io-index" 1789 + checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" 1790 + dependencies = [ 1791 + "bitflags 2.6.0", 1792 + "gpu-alloc-types", 1793 + ] 1794 + 1795 + [[package]] 1796 + name = "gpu-alloc-types" 1797 + version = "0.3.0" 1798 + source = "registry+https://github.com/rust-lang/crates.io-index" 1799 + checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" 1800 + dependencies = [ 1801 + "bitflags 2.6.0", 1802 + ] 1803 + 1804 + [[package]] 1805 + name = "gpu-allocator" 1806 + version = "0.26.0" 1807 + source = "registry+https://github.com/rust-lang/crates.io-index" 1808 + checksum = "fdd4240fc91d3433d5e5b0fc5b67672d771850dc19bbee03c1381e19322803d7" 1809 + dependencies = [ 1810 + "log", 1811 + "presser", 1812 + "thiserror", 1813 + "winapi", 1814 + "windows 0.52.0", 1815 + ] 1816 + 1817 + [[package]] 1818 + name = "gpu-descriptor" 1819 + version = "0.3.0" 1820 + source = "registry+https://github.com/rust-lang/crates.io-index" 1821 + checksum = "9c08c1f623a8d0b722b8b99f821eb0ba672a1618f0d3b16ddbee1cedd2dd8557" 1822 + dependencies = [ 1823 + "bitflags 2.6.0", 1824 + "gpu-descriptor-types", 1825 + "hashbrown 0.14.5", 1826 + ] 1827 + 1828 + [[package]] 1829 + name = "gpu-descriptor-types" 1830 + version = "0.2.0" 1831 + source = "registry+https://github.com/rust-lang/crates.io-index" 1832 + checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" 1833 + dependencies = [ 1834 + "bitflags 2.6.0", 1835 + ] 1836 + 1837 + [[package]] 1838 + name = "hashbrown" 1839 + version = "0.12.3" 1840 + source = "registry+https://github.com/rust-lang/crates.io-index" 1841 + checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 168 1842 169 1843 [[package]] 170 1844 name = "hashbrown" 171 1845 version = "0.14.5" 172 1846 source = "registry+https://github.com/rust-lang/crates.io-index" 173 1847 checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 1848 + dependencies = [ 1849 + "ahash", 1850 + "allocator-api2", 1851 + ] 1852 + 1853 + [[package]] 1854 + name = "hashbrown" 1855 + version = "0.15.0" 1856 + source = "registry+https://github.com/rust-lang/crates.io-index" 1857 + checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" 1858 + 1859 + [[package]] 1860 + name = "hassle-rs" 1861 + version = "0.11.0" 1862 + source = "registry+https://github.com/rust-lang/crates.io-index" 1863 + checksum = "af2a7e73e1f34c48da31fb668a907f250794837e08faa144fd24f0b8b741e890" 1864 + dependencies = [ 1865 + "bitflags 2.6.0", 1866 + "com", 1867 + "libc", 1868 + "libloading", 1869 + "thiserror", 1870 + "widestring", 1871 + "winapi", 1872 + ] 174 1873 175 1874 [[package]] 176 1875 name = "heck" ··· 179 1878 checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 180 1879 181 1880 [[package]] 1881 + name = "hermit-abi" 1882 + version = "0.3.9" 1883 + source = "registry+https://github.com/rust-lang/crates.io-index" 1884 + checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 1885 + 1886 + [[package]] 1887 + name = "hermit-abi" 1888 + version = "0.4.0" 1889 + source = "registry+https://github.com/rust-lang/crates.io-index" 1890 + checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" 1891 + 1892 + [[package]] 1893 + name = "hex" 1894 + version = "0.4.3" 1895 + source = "registry+https://github.com/rust-lang/crates.io-index" 1896 + checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 1897 + 1898 + [[package]] 1899 + name = "hexf-parse" 1900 + version = "0.2.1" 1901 + source = "registry+https://github.com/rust-lang/crates.io-index" 1902 + checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" 1903 + 1904 + [[package]] 1905 + name = "home" 1906 + version = "0.5.9" 1907 + source = "registry+https://github.com/rust-lang/crates.io-index" 1908 + checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" 1909 + dependencies = [ 1910 + "windows-sys 0.52.0", 1911 + ] 1912 + 1913 + [[package]] 1914 + name = "http" 1915 + version = "1.1.0" 1916 + source = "registry+https://github.com/rust-lang/crates.io-index" 1917 + checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" 1918 + dependencies = [ 1919 + "bytes", 1920 + "fnv", 1921 + "itoa", 1922 + ] 1923 + 1924 + [[package]] 1925 + name = "httparse" 1926 + version = "1.9.5" 1927 + source = "registry+https://github.com/rust-lang/crates.io-index" 1928 + checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" 1929 + 1930 + [[package]] 1931 + name = "iana-time-zone" 1932 + version = "0.1.61" 1933 + source = "registry+https://github.com/rust-lang/crates.io-index" 1934 + checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" 1935 + dependencies = [ 1936 + "android_system_properties", 1937 + "core-foundation-sys", 1938 + "iana-time-zone-haiku", 1939 + "js-sys", 1940 + "wasm-bindgen", 1941 + "windows-core 0.52.0", 1942 + ] 1943 + 1944 + [[package]] 1945 + name = "iana-time-zone-haiku" 1946 + version = "0.1.2" 1947 + source = "registry+https://github.com/rust-lang/crates.io-index" 1948 + checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 1949 + dependencies = [ 1950 + "cc", 1951 + ] 1952 + 1953 + [[package]] 1954 + name = "iced-x86" 1955 + version = "1.21.0" 1956 + source = "registry+https://github.com/rust-lang/crates.io-index" 1957 + checksum = "7c447cff8c7f384a7d4f741cfcff32f75f3ad02b406432e8d6c878d56b1edf6b" 1958 + dependencies = [ 1959 + "lazy_static", 1960 + ] 1961 + 1962 + [[package]] 1963 + name = "ident_case" 1964 + version = "1.0.1" 1965 + source = "registry+https://github.com/rust-lang/crates.io-index" 1966 + checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 1967 + 1968 + [[package]] 1969 + name = "idna" 1970 + version = "0.5.0" 1971 + source = "registry+https://github.com/rust-lang/crates.io-index" 1972 + checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" 1973 + dependencies = [ 1974 + "unicode-bidi", 1975 + "unicode-normalization", 1976 + ] 1977 + 1978 + [[package]] 1979 + name = "image" 1980 + version = "0.25.2" 1981 + source = "registry+https://github.com/rust-lang/crates.io-index" 1982 + checksum = "99314c8a2152b8ddb211f924cdae532d8c5e4c8bb54728e12fff1b0cd5963a10" 1983 + dependencies = [ 1984 + "bytemuck", 1985 + "byteorder-lite", 1986 + "num-traits", 1987 + "png", 1988 + ] 1989 + 1990 + [[package]] 1991 + name = "immutable-chunkmap" 1992 + version = "2.0.5" 1993 + source = "registry+https://github.com/rust-lang/crates.io-index" 1994 + checksum = "4419f022e55cc63d5bbd6b44b71e1d226b9c9480a47824c706e9d54e5c40c5eb" 1995 + dependencies = [ 1996 + "arrayvec", 1997 + ] 1998 + 1999 + [[package]] 2000 + name = "indenter" 2001 + version = "0.3.3" 2002 + source = "registry+https://github.com/rust-lang/crates.io-index" 2003 + checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" 2004 + 2005 + [[package]] 182 2006 name = "indexmap" 183 - version = "2.5.0" 2007 + version = "1.9.3" 184 2008 source = "registry+https://github.com/rust-lang/crates.io-index" 185 - checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" 2009 + checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" 2010 + dependencies = [ 2011 + "autocfg", 2012 + "hashbrown 0.12.3", 2013 + "serde", 2014 + ] 2015 + 2016 + [[package]] 2017 + name = "indexmap" 2018 + version = "2.6.0" 2019 + source = "registry+https://github.com/rust-lang/crates.io-index" 2020 + checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" 186 2021 dependencies = [ 187 2022 "equivalent", 188 - "hashbrown", 2023 + "hashbrown 0.15.0", 2024 + "serde", 2025 + ] 2026 + 2027 + [[package]] 2028 + name = "instant" 2029 + version = "0.1.13" 2030 + source = "registry+https://github.com/rust-lang/crates.io-index" 2031 + checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" 2032 + dependencies = [ 2033 + "cfg-if", 2034 + ] 2035 + 2036 + [[package]] 2037 + name = "io-lifetimes" 2038 + version = "1.0.11" 2039 + source = "registry+https://github.com/rust-lang/crates.io-index" 2040 + checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" 2041 + dependencies = [ 2042 + "hermit-abi 0.3.9", 2043 + "libc", 2044 + "windows-sys 0.48.0", 189 2045 ] 190 2046 191 2047 [[package]] ··· 195 2051 checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 196 2052 197 2053 [[package]] 2054 + name = "itoa" 2055 + version = "1.0.11" 2056 + source = "registry+https://github.com/rust-lang/crates.io-index" 2057 + checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 2058 + 2059 + [[package]] 2060 + name = "jni" 2061 + version = "0.21.1" 2062 + source = "registry+https://github.com/rust-lang/crates.io-index" 2063 + checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" 2064 + dependencies = [ 2065 + "cesu8", 2066 + "cfg-if", 2067 + "combine", 2068 + "jni-sys", 2069 + "log", 2070 + "thiserror", 2071 + "walkdir", 2072 + "windows-sys 0.45.0", 2073 + ] 2074 + 2075 + [[package]] 2076 + name = "jni-sys" 2077 + version = "0.3.0" 2078 + source = "registry+https://github.com/rust-lang/crates.io-index" 2079 + checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" 2080 + 2081 + [[package]] 2082 + name = "jobserver" 2083 + version = "0.1.32" 2084 + source = "registry+https://github.com/rust-lang/crates.io-index" 2085 + checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" 2086 + dependencies = [ 2087 + "libc", 2088 + ] 2089 + 2090 + [[package]] 2091 + name = "js-sys" 2092 + version = "0.3.70" 2093 + source = "registry+https://github.com/rust-lang/crates.io-index" 2094 + checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" 2095 + dependencies = [ 2096 + "wasm-bindgen", 2097 + ] 2098 + 2099 + [[package]] 2100 + name = "khronos-egl" 2101 + version = "6.0.0" 2102 + source = "registry+https://github.com/rust-lang/crates.io-index" 2103 + checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" 2104 + dependencies = [ 2105 + "libc", 2106 + "libloading", 2107 + "pkg-config", 2108 + ] 2109 + 2110 + [[package]] 2111 + name = "khronos_api" 2112 + version = "3.1.0" 2113 + source = "registry+https://github.com/rust-lang/crates.io-index" 2114 + checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" 2115 + 2116 + [[package]] 2117 + name = "lazy-regex" 2118 + version = "3.3.0" 2119 + source = "registry+https://github.com/rust-lang/crates.io-index" 2120 + checksum = "8d8e41c97e6bc7ecb552016274b99fbb5d035e8de288c582d9b933af6677bfda" 2121 + dependencies = [ 2122 + "lazy-regex-proc_macros", 2123 + "once_cell", 2124 + "regex", 2125 + ] 2126 + 2127 + [[package]] 2128 + name = "lazy-regex-proc_macros" 2129 + version = "3.3.0" 2130 + source = "registry+https://github.com/rust-lang/crates.io-index" 2131 + checksum = "76e1d8b05d672c53cb9c7b920bbba8783845ae4f0b076e02a3db1d02c81b4163" 2132 + dependencies = [ 2133 + "proc-macro2", 2134 + "quote", 2135 + "regex", 2136 + "syn 2.0.79", 2137 + ] 2138 + 2139 + [[package]] 2140 + name = "lazy_static" 2141 + version = "1.5.0" 2142 + source = "registry+https://github.com/rust-lang/crates.io-index" 2143 + checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 2144 + 2145 + [[package]] 198 2146 name = "libc" 199 - version = "0.2.158" 2147 + version = "0.2.159" 2148 + source = "registry+https://github.com/rust-lang/crates.io-index" 2149 + checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" 2150 + 2151 + [[package]] 2152 + name = "libloading" 2153 + version = "0.8.5" 2154 + source = "registry+https://github.com/rust-lang/crates.io-index" 2155 + checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" 2156 + dependencies = [ 2157 + "cfg-if", 2158 + "windows-targets 0.52.6", 2159 + ] 2160 + 2161 + [[package]] 2162 + name = "libredox" 2163 + version = "0.0.2" 2164 + source = "registry+https://github.com/rust-lang/crates.io-index" 2165 + checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607" 2166 + dependencies = [ 2167 + "bitflags 2.6.0", 2168 + "libc", 2169 + "redox_syscall 0.4.1", 2170 + ] 2171 + 2172 + [[package]] 2173 + name = "linux-raw-sys" 2174 + version = "0.3.8" 2175 + source = "registry+https://github.com/rust-lang/crates.io-index" 2176 + checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" 2177 + 2178 + [[package]] 2179 + name = "linux-raw-sys" 2180 + version = "0.4.14" 2181 + source = "registry+https://github.com/rust-lang/crates.io-index" 2182 + checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" 2183 + 2184 + [[package]] 2185 + name = "litrs" 2186 + version = "0.4.1" 2187 + source = "registry+https://github.com/rust-lang/crates.io-index" 2188 + checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" 2189 + 2190 + [[package]] 2191 + name = "lock_api" 2192 + version = "0.4.12" 2193 + source = "registry+https://github.com/rust-lang/crates.io-index" 2194 + checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 2195 + dependencies = [ 2196 + "autocfg", 2197 + "scopeguard", 2198 + ] 2199 + 2200 + [[package]] 2201 + name = "log" 2202 + version = "0.4.22" 200 2203 source = "registry+https://github.com/rust-lang/crates.io-index" 201 - checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" 2204 + checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 202 2205 203 2206 [[package]] 204 2207 name = "mach" ··· 210 2213 ] 211 2214 212 2215 [[package]] 2216 + name = "malloc_buf" 2217 + version = "0.0.6" 2218 + source = "registry+https://github.com/rust-lang/crates.io-index" 2219 + checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" 2220 + dependencies = [ 2221 + "libc", 2222 + ] 2223 + 2224 + [[package]] 2225 + name = "matchers" 2226 + version = "0.1.0" 2227 + source = "registry+https://github.com/rust-lang/crates.io-index" 2228 + checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" 2229 + dependencies = [ 2230 + "regex-automata 0.1.10", 2231 + ] 2232 + 2233 + [[package]] 213 2234 name = "memchr" 214 2235 version = "2.7.4" 215 2236 source = "registry+https://github.com/rust-lang/crates.io-index" 216 2237 checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 217 2238 218 2239 [[package]] 219 - name = "noita-counter" 2240 + name = "memmap2" 2241 + version = "0.9.5" 2242 + source = "registry+https://github.com/rust-lang/crates.io-index" 2243 + checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" 2244 + dependencies = [ 2245 + "libc", 2246 + ] 2247 + 2248 + [[package]] 2249 + name = "memoffset" 2250 + version = "0.7.1" 2251 + source = "registry+https://github.com/rust-lang/crates.io-index" 2252 + checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" 2253 + dependencies = [ 2254 + "autocfg", 2255 + ] 2256 + 2257 + [[package]] 2258 + name = "memoffset" 2259 + version = "0.9.1" 2260 + source = "registry+https://github.com/rust-lang/crates.io-index" 2261 + checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" 2262 + dependencies = [ 2263 + "autocfg", 2264 + ] 2265 + 2266 + [[package]] 2267 + name = "metal" 2268 + version = "0.29.0" 2269 + source = "registry+https://github.com/rust-lang/crates.io-index" 2270 + checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" 2271 + dependencies = [ 2272 + "bitflags 2.6.0", 2273 + "block", 2274 + "core-graphics-types", 2275 + "foreign-types", 2276 + "log", 2277 + "objc", 2278 + "paste", 2279 + ] 2280 + 2281 + [[package]] 2282 + name = "mime" 2283 + version = "0.3.17" 2284 + source = "registry+https://github.com/rust-lang/crates.io-index" 2285 + checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 2286 + 2287 + [[package]] 2288 + name = "mime_guess2" 2289 + version = "2.0.5" 2290 + source = "registry+https://github.com/rust-lang/crates.io-index" 2291 + checksum = "25a3333bb1609500601edc766a39b4c1772874a4ce26022f4d866854dc020c41" 2292 + dependencies = [ 2293 + "mime", 2294 + "unicase", 2295 + ] 2296 + 2297 + [[package]] 2298 + name = "miniz_oxide" 2299 + version = "0.7.4" 2300 + source = "registry+https://github.com/rust-lang/crates.io-index" 2301 + checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" 2302 + dependencies = [ 2303 + "adler", 2304 + ] 2305 + 2306 + [[package]] 2307 + name = "miniz_oxide" 2308 + version = "0.8.0" 2309 + source = "registry+https://github.com/rust-lang/crates.io-index" 2310 + checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" 2311 + dependencies = [ 2312 + "adler2", 2313 + "simd-adler32", 2314 + ] 2315 + 2316 + [[package]] 2317 + name = "mio" 2318 + version = "1.0.2" 2319 + source = "registry+https://github.com/rust-lang/crates.io-index" 2320 + checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" 2321 + dependencies = [ 2322 + "hermit-abi 0.3.9", 2323 + "libc", 2324 + "wasi", 2325 + "windows-sys 0.52.0", 2326 + ] 2327 + 2328 + [[package]] 2329 + name = "naga" 2330 + version = "22.1.0" 2331 + source = "registry+https://github.com/rust-lang/crates.io-index" 2332 + checksum = "8bd5a652b6faf21496f2cfd88fc49989c8db0825d1f6746b1a71a6ede24a63ad" 2333 + dependencies = [ 2334 + "arrayvec", 2335 + "bit-set", 2336 + "bitflags 2.6.0", 2337 + "cfg_aliases 0.1.1", 2338 + "codespan-reporting", 2339 + "hexf-parse", 2340 + "indexmap 2.6.0", 2341 + "log", 2342 + "rustc-hash", 2343 + "spirv", 2344 + "termcolor", 2345 + "thiserror", 2346 + "unicode-xid", 2347 + ] 2348 + 2349 + [[package]] 2350 + name = "ndk" 2351 + version = "0.9.0" 2352 + source = "registry+https://github.com/rust-lang/crates.io-index" 2353 + checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" 2354 + dependencies = [ 2355 + "bitflags 2.6.0", 2356 + "jni-sys", 2357 + "log", 2358 + "ndk-sys 0.6.0+11769913", 2359 + "num_enum", 2360 + "raw-window-handle", 2361 + "thiserror", 2362 + ] 2363 + 2364 + [[package]] 2365 + name = "ndk-context" 2366 + version = "0.1.1" 2367 + source = "registry+https://github.com/rust-lang/crates.io-index" 2368 + checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" 2369 + 2370 + [[package]] 2371 + name = "ndk-sys" 2372 + version = "0.5.0+25.2.9519653" 2373 + source = "registry+https://github.com/rust-lang/crates.io-index" 2374 + checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" 2375 + dependencies = [ 2376 + "jni-sys", 2377 + ] 2378 + 2379 + [[package]] 2380 + name = "ndk-sys" 2381 + version = "0.6.0+11769913" 2382 + source = "registry+https://github.com/rust-lang/crates.io-index" 2383 + checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" 2384 + dependencies = [ 2385 + "jni-sys", 2386 + ] 2387 + 2388 + [[package]] 2389 + name = "nix" 2390 + version = "0.26.4" 2391 + source = "registry+https://github.com/rust-lang/crates.io-index" 2392 + checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" 2393 + dependencies = [ 2394 + "bitflags 1.3.2", 2395 + "cfg-if", 2396 + "libc", 2397 + "memoffset 0.7.1", 2398 + ] 2399 + 2400 + [[package]] 2401 + name = "nohash-hasher" 2402 + version = "0.2.0" 2403 + source = "registry+https://github.com/rust-lang/crates.io-index" 2404 + checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" 2405 + 2406 + [[package]] 2407 + name = "noita-utility-box" 220 2408 version = "0.1.0" 221 2409 dependencies = [ 222 2410 "anyhow", 223 - "bytemuck", 224 2411 "clap", 225 - "process-memory", 2412 + "color-eyre", 2413 + "derive_more", 2414 + "eframe", 2415 + "egui_extras", 2416 + "fastrand 2.1.1", 2417 + "futures", 2418 + "iced-x86", 2419 + "lazy-regex", 2420 + "memchr", 2421 + "obws", 2422 + "rayon", 2423 + "read-process-memory", 2424 + "serde", 2425 + "smart-default", 226 2426 "strfmt", 2427 + "strum", 227 2428 "sysinfo", 2429 + "thiserror", 2430 + "tokio", 228 2431 "toml", 2432 + "tracing", 2433 + "tracing-subscriber", 2434 + "zerocopy", 229 2435 ] 230 2436 231 2437 [[package]] ··· 238 2444 ] 239 2445 240 2446 [[package]] 2447 + name = "nu-ansi-term" 2448 + version = "0.46.0" 2449 + source = "registry+https://github.com/rust-lang/crates.io-index" 2450 + checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" 2451 + dependencies = [ 2452 + "overload", 2453 + "winapi", 2454 + ] 2455 + 2456 + [[package]] 2457 + name = "num-conv" 2458 + version = "0.1.0" 2459 + source = "registry+https://github.com/rust-lang/crates.io-index" 2460 + checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 2461 + 2462 + [[package]] 2463 + name = "num-traits" 2464 + version = "0.2.19" 2465 + source = "registry+https://github.com/rust-lang/crates.io-index" 2466 + checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 2467 + dependencies = [ 2468 + "autocfg", 2469 + ] 2470 + 2471 + [[package]] 2472 + name = "num_enum" 2473 + version = "0.7.3" 2474 + source = "registry+https://github.com/rust-lang/crates.io-index" 2475 + checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" 2476 + dependencies = [ 2477 + "num_enum_derive", 2478 + ] 2479 + 2480 + [[package]] 2481 + name = "num_enum_derive" 2482 + version = "0.7.3" 2483 + source = "registry+https://github.com/rust-lang/crates.io-index" 2484 + checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" 2485 + dependencies = [ 2486 + "proc-macro-crate 3.2.0", 2487 + "proc-macro2", 2488 + "quote", 2489 + "syn 2.0.79", 2490 + ] 2491 + 2492 + [[package]] 2493 + name = "objc" 2494 + version = "0.2.7" 2495 + source = "registry+https://github.com/rust-lang/crates.io-index" 2496 + checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" 2497 + dependencies = [ 2498 + "malloc_buf", 2499 + ] 2500 + 2501 + [[package]] 2502 + name = "objc-sys" 2503 + version = "0.3.5" 2504 + source = "registry+https://github.com/rust-lang/crates.io-index" 2505 + checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" 2506 + 2507 + [[package]] 2508 + name = "objc2" 2509 + version = "0.5.2" 2510 + source = "registry+https://github.com/rust-lang/crates.io-index" 2511 + checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" 2512 + dependencies = [ 2513 + "objc-sys", 2514 + "objc2-encode", 2515 + ] 2516 + 2517 + [[package]] 2518 + name = "objc2-app-kit" 2519 + version = "0.2.2" 2520 + source = "registry+https://github.com/rust-lang/crates.io-index" 2521 + checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" 2522 + dependencies = [ 2523 + "bitflags 2.6.0", 2524 + "block2", 2525 + "libc", 2526 + "objc2", 2527 + "objc2-core-data", 2528 + "objc2-core-image", 2529 + "objc2-foundation", 2530 + "objc2-quartz-core", 2531 + ] 2532 + 2533 + [[package]] 2534 + name = "objc2-cloud-kit" 2535 + version = "0.2.2" 2536 + source = "registry+https://github.com/rust-lang/crates.io-index" 2537 + checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" 2538 + dependencies = [ 2539 + "bitflags 2.6.0", 2540 + "block2", 2541 + "objc2", 2542 + "objc2-core-location", 2543 + "objc2-foundation", 2544 + ] 2545 + 2546 + [[package]] 2547 + name = "objc2-contacts" 2548 + version = "0.2.2" 2549 + source = "registry+https://github.com/rust-lang/crates.io-index" 2550 + checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" 2551 + dependencies = [ 2552 + "block2", 2553 + "objc2", 2554 + "objc2-foundation", 2555 + ] 2556 + 2557 + [[package]] 2558 + name = "objc2-core-data" 2559 + version = "0.2.2" 2560 + source = "registry+https://github.com/rust-lang/crates.io-index" 2561 + checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" 2562 + dependencies = [ 2563 + "bitflags 2.6.0", 2564 + "block2", 2565 + "objc2", 2566 + "objc2-foundation", 2567 + ] 2568 + 2569 + [[package]] 2570 + name = "objc2-core-image" 2571 + version = "0.2.2" 2572 + source = "registry+https://github.com/rust-lang/crates.io-index" 2573 + checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" 2574 + dependencies = [ 2575 + "block2", 2576 + "objc2", 2577 + "objc2-foundation", 2578 + "objc2-metal", 2579 + ] 2580 + 2581 + [[package]] 2582 + name = "objc2-core-location" 2583 + version = "0.2.2" 2584 + source = "registry+https://github.com/rust-lang/crates.io-index" 2585 + checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" 2586 + dependencies = [ 2587 + "block2", 2588 + "objc2", 2589 + "objc2-contacts", 2590 + "objc2-foundation", 2591 + ] 2592 + 2593 + [[package]] 2594 + name = "objc2-encode" 2595 + version = "4.0.3" 2596 + source = "registry+https://github.com/rust-lang/crates.io-index" 2597 + checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8" 2598 + 2599 + [[package]] 2600 + name = "objc2-foundation" 2601 + version = "0.2.2" 2602 + source = "registry+https://github.com/rust-lang/crates.io-index" 2603 + checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" 2604 + dependencies = [ 2605 + "bitflags 2.6.0", 2606 + "block2", 2607 + "dispatch", 2608 + "libc", 2609 + "objc2", 2610 + ] 2611 + 2612 + [[package]] 2613 + name = "objc2-link-presentation" 2614 + version = "0.2.2" 2615 + source = "registry+https://github.com/rust-lang/crates.io-index" 2616 + checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" 2617 + dependencies = [ 2618 + "block2", 2619 + "objc2", 2620 + "objc2-app-kit", 2621 + "objc2-foundation", 2622 + ] 2623 + 2624 + [[package]] 2625 + name = "objc2-metal" 2626 + version = "0.2.2" 2627 + source = "registry+https://github.com/rust-lang/crates.io-index" 2628 + checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" 2629 + dependencies = [ 2630 + "bitflags 2.6.0", 2631 + "block2", 2632 + "objc2", 2633 + "objc2-foundation", 2634 + ] 2635 + 2636 + [[package]] 2637 + name = "objc2-quartz-core" 2638 + version = "0.2.2" 2639 + source = "registry+https://github.com/rust-lang/crates.io-index" 2640 + checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" 2641 + dependencies = [ 2642 + "bitflags 2.6.0", 2643 + "block2", 2644 + "objc2", 2645 + "objc2-foundation", 2646 + "objc2-metal", 2647 + ] 2648 + 2649 + [[package]] 2650 + name = "objc2-symbols" 2651 + version = "0.2.2" 2652 + source = "registry+https://github.com/rust-lang/crates.io-index" 2653 + checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" 2654 + dependencies = [ 2655 + "objc2", 2656 + "objc2-foundation", 2657 + ] 2658 + 2659 + [[package]] 2660 + name = "objc2-ui-kit" 2661 + version = "0.2.2" 2662 + source = "registry+https://github.com/rust-lang/crates.io-index" 2663 + checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" 2664 + dependencies = [ 2665 + "bitflags 2.6.0", 2666 + "block2", 2667 + "objc2", 2668 + "objc2-cloud-kit", 2669 + "objc2-core-data", 2670 + "objc2-core-image", 2671 + "objc2-core-location", 2672 + "objc2-foundation", 2673 + "objc2-link-presentation", 2674 + "objc2-quartz-core", 2675 + "objc2-symbols", 2676 + "objc2-uniform-type-identifiers", 2677 + "objc2-user-notifications", 2678 + ] 2679 + 2680 + [[package]] 2681 + name = "objc2-uniform-type-identifiers" 2682 + version = "0.2.2" 2683 + source = "registry+https://github.com/rust-lang/crates.io-index" 2684 + checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" 2685 + dependencies = [ 2686 + "block2", 2687 + "objc2", 2688 + "objc2-foundation", 2689 + ] 2690 + 2691 + [[package]] 2692 + name = "objc2-user-notifications" 2693 + version = "0.2.2" 2694 + source = "registry+https://github.com/rust-lang/crates.io-index" 2695 + checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" 2696 + dependencies = [ 2697 + "bitflags 2.6.0", 2698 + "block2", 2699 + "objc2", 2700 + "objc2-core-location", 2701 + "objc2-foundation", 2702 + ] 2703 + 2704 + [[package]] 2705 + name = "object" 2706 + version = "0.32.2" 2707 + source = "registry+https://github.com/rust-lang/crates.io-index" 2708 + checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" 2709 + dependencies = [ 2710 + "memchr", 2711 + ] 2712 + 2713 + [[package]] 2714 + name = "obws" 2715 + version = "0.13.0" 2716 + source = "registry+https://github.com/rust-lang/crates.io-index" 2717 + checksum = "815d78d5e0a99aa961c05aa07b52d82b2bd2d4414b0ff0a2a3db7c12332fa807" 2718 + dependencies = [ 2719 + "async-stream", 2720 + "base64 0.22.1", 2721 + "bitflags 2.6.0", 2722 + "futures-util", 2723 + "rgb", 2724 + "semver", 2725 + "serde", 2726 + "serde_json", 2727 + "serde_repr", 2728 + "serde_with", 2729 + "sha2", 2730 + "thiserror", 2731 + "time", 2732 + "tokio", 2733 + "tokio-tungstenite", 2734 + "tracing", 2735 + "uuid", 2736 + ] 2737 + 2738 + [[package]] 2739 + name = "once_cell" 2740 + version = "1.20.2" 2741 + source = "registry+https://github.com/rust-lang/crates.io-index" 2742 + checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" 2743 + 2744 + [[package]] 2745 + name = "orbclient" 2746 + version = "0.3.47" 2747 + source = "registry+https://github.com/rust-lang/crates.io-index" 2748 + checksum = "52f0d54bde9774d3a51dcf281a5def240c71996bc6ca05d2c847ec8b2b216166" 2749 + dependencies = [ 2750 + "libredox", 2751 + ] 2752 + 2753 + [[package]] 2754 + name = "ordered-stream" 2755 + version = "0.2.0" 2756 + source = "registry+https://github.com/rust-lang/crates.io-index" 2757 + checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" 2758 + dependencies = [ 2759 + "futures-core", 2760 + "pin-project-lite", 2761 + ] 2762 + 2763 + [[package]] 2764 + name = "overload" 2765 + version = "0.1.1" 2766 + source = "registry+https://github.com/rust-lang/crates.io-index" 2767 + checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" 2768 + 2769 + [[package]] 2770 + name = "owned_ttf_parser" 2771 + version = "0.25.0" 2772 + source = "registry+https://github.com/rust-lang/crates.io-index" 2773 + checksum = "22ec719bbf3b2a81c109a4e20b1f129b5566b7dce654bc3872f6a05abf82b2c4" 2774 + dependencies = [ 2775 + "ttf-parser", 2776 + ] 2777 + 2778 + [[package]] 2779 + name = "owo-colors" 2780 + version = "3.5.0" 2781 + source = "registry+https://github.com/rust-lang/crates.io-index" 2782 + checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" 2783 + 2784 + [[package]] 2785 + name = "parking" 2786 + version = "2.2.1" 2787 + source = "registry+https://github.com/rust-lang/crates.io-index" 2788 + checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" 2789 + 2790 + [[package]] 2791 + name = "parking_lot" 2792 + version = "0.12.3" 2793 + source = "registry+https://github.com/rust-lang/crates.io-index" 2794 + checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 2795 + dependencies = [ 2796 + "lock_api", 2797 + "parking_lot_core", 2798 + ] 2799 + 2800 + [[package]] 2801 + name = "parking_lot_core" 2802 + version = "0.9.10" 2803 + source = "registry+https://github.com/rust-lang/crates.io-index" 2804 + checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 2805 + dependencies = [ 2806 + "cfg-if", 2807 + "libc", 2808 + "redox_syscall 0.5.7", 2809 + "smallvec", 2810 + "windows-targets 0.52.6", 2811 + ] 2812 + 2813 + [[package]] 2814 + name = "paste" 2815 + version = "1.0.15" 2816 + source = "registry+https://github.com/rust-lang/crates.io-index" 2817 + checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" 2818 + 2819 + [[package]] 2820 + name = "percent-encoding" 2821 + version = "2.3.1" 2822 + source = "registry+https://github.com/rust-lang/crates.io-index" 2823 + checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 2824 + 2825 + [[package]] 2826 + name = "pin-project" 2827 + version = "1.1.6" 2828 + source = "registry+https://github.com/rust-lang/crates.io-index" 2829 + checksum = "baf123a161dde1e524adf36f90bc5d8d3462824a9c43553ad07a8183161189ec" 2830 + dependencies = [ 2831 + "pin-project-internal", 2832 + ] 2833 + 2834 + [[package]] 2835 + name = "pin-project-internal" 2836 + version = "1.1.6" 2837 + source = "registry+https://github.com/rust-lang/crates.io-index" 2838 + checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8" 2839 + dependencies = [ 2840 + "proc-macro2", 2841 + "quote", 2842 + "syn 2.0.79", 2843 + ] 2844 + 2845 + [[package]] 2846 + name = "pin-project-lite" 2847 + version = "0.2.14" 2848 + source = "registry+https://github.com/rust-lang/crates.io-index" 2849 + checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" 2850 + 2851 + [[package]] 2852 + name = "pin-utils" 2853 + version = "0.1.0" 2854 + source = "registry+https://github.com/rust-lang/crates.io-index" 2855 + checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 2856 + 2857 + [[package]] 2858 + name = "piper" 2859 + version = "0.2.4" 2860 + source = "registry+https://github.com/rust-lang/crates.io-index" 2861 + checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" 2862 + dependencies = [ 2863 + "atomic-waker", 2864 + "fastrand 2.1.1", 2865 + "futures-io", 2866 + ] 2867 + 2868 + [[package]] 2869 + name = "pkg-config" 2870 + version = "0.3.31" 2871 + source = "registry+https://github.com/rust-lang/crates.io-index" 2872 + checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" 2873 + 2874 + [[package]] 2875 + name = "png" 2876 + version = "0.17.14" 2877 + source = "registry+https://github.com/rust-lang/crates.io-index" 2878 + checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0" 2879 + dependencies = [ 2880 + "bitflags 1.3.2", 2881 + "crc32fast", 2882 + "fdeflate", 2883 + "flate2", 2884 + "miniz_oxide 0.8.0", 2885 + ] 2886 + 2887 + [[package]] 2888 + name = "polling" 2889 + version = "2.8.0" 2890 + source = "registry+https://github.com/rust-lang/crates.io-index" 2891 + checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" 2892 + dependencies = [ 2893 + "autocfg", 2894 + "bitflags 1.3.2", 2895 + "cfg-if", 2896 + "concurrent-queue", 2897 + "libc", 2898 + "log", 2899 + "pin-project-lite", 2900 + "windows-sys 0.48.0", 2901 + ] 2902 + 2903 + [[package]] 2904 + name = "polling" 2905 + version = "3.7.3" 2906 + source = "registry+https://github.com/rust-lang/crates.io-index" 2907 + checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" 2908 + dependencies = [ 2909 + "cfg-if", 2910 + "concurrent-queue", 2911 + "hermit-abi 0.4.0", 2912 + "pin-project-lite", 2913 + "rustix 0.38.37", 2914 + "tracing", 2915 + "windows-sys 0.59.0", 2916 + ] 2917 + 2918 + [[package]] 2919 + name = "pollster" 2920 + version = "0.3.0" 2921 + source = "registry+https://github.com/rust-lang/crates.io-index" 2922 + checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" 2923 + 2924 + [[package]] 2925 + name = "powerfmt" 2926 + version = "0.2.0" 2927 + source = "registry+https://github.com/rust-lang/crates.io-index" 2928 + checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 2929 + 2930 + [[package]] 2931 + name = "ppv-lite86" 2932 + version = "0.2.20" 2933 + source = "registry+https://github.com/rust-lang/crates.io-index" 2934 + checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" 2935 + dependencies = [ 2936 + "zerocopy", 2937 + ] 2938 + 2939 + [[package]] 2940 + name = "presser" 2941 + version = "0.3.1" 2942 + source = "registry+https://github.com/rust-lang/crates.io-index" 2943 + checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" 2944 + 2945 + [[package]] 2946 + name = "proc-macro-crate" 2947 + version = "1.3.1" 2948 + source = "registry+https://github.com/rust-lang/crates.io-index" 2949 + checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" 2950 + dependencies = [ 2951 + "once_cell", 2952 + "toml_edit 0.19.15", 2953 + ] 2954 + 2955 + [[package]] 2956 + name = "proc-macro-crate" 2957 + version = "3.2.0" 2958 + source = "registry+https://github.com/rust-lang/crates.io-index" 2959 + checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" 2960 + dependencies = [ 2961 + "toml_edit 0.22.22", 2962 + ] 2963 + 2964 + [[package]] 241 2965 name = "proc-macro2" 242 2966 version = "1.0.86" 243 2967 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 247 2971 ] 248 2972 249 2973 [[package]] 250 - name = "process-memory" 251 - version = "0.5.0" 2974 + name = "profiling" 2975 + version = "1.0.15" 252 2976 source = "registry+https://github.com/rust-lang/crates.io-index" 253 - checksum = "ae9599c34fcc8067c3105dc746c0ce85e3ea61784568b8234179fad490b1dcc1" 2977 + checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58" 2978 + 2979 + [[package]] 2980 + name = "quick-xml" 2981 + version = "0.36.2" 2982 + source = "registry+https://github.com/rust-lang/crates.io-index" 2983 + checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" 254 2984 dependencies = [ 255 - "libc", 256 - "mach", 257 - "winapi", 2985 + "memchr", 258 2986 ] 259 2987 260 2988 [[package]] ··· 267 2995 ] 268 2996 269 2997 [[package]] 2998 + name = "rand" 2999 + version = "0.8.5" 3000 + source = "registry+https://github.com/rust-lang/crates.io-index" 3001 + checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 3002 + dependencies = [ 3003 + "libc", 3004 + "rand_chacha", 3005 + "rand_core", 3006 + ] 3007 + 3008 + [[package]] 3009 + name = "rand_chacha" 3010 + version = "0.3.1" 3011 + source = "registry+https://github.com/rust-lang/crates.io-index" 3012 + checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 3013 + dependencies = [ 3014 + "ppv-lite86", 3015 + "rand_core", 3016 + ] 3017 + 3018 + [[package]] 3019 + name = "rand_core" 3020 + version = "0.6.4" 3021 + source = "registry+https://github.com/rust-lang/crates.io-index" 3022 + checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 3023 + dependencies = [ 3024 + "getrandom", 3025 + ] 3026 + 3027 + [[package]] 3028 + name = "raw-window-handle" 3029 + version = "0.6.2" 3030 + source = "registry+https://github.com/rust-lang/crates.io-index" 3031 + checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" 3032 + 3033 + [[package]] 270 3034 name = "rayon" 271 3035 version = "1.10.0" 272 3036 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 287 3051 ] 288 3052 289 3053 [[package]] 3054 + name = "read-process-memory" 3055 + version = "0.1.6" 3056 + source = "registry+https://github.com/rust-lang/crates.io-index" 3057 + checksum = "8497683b2f0b6887786f1928c118f26ecc6bb3d78bbb6ed23e8e7ba110af3bb0" 3058 + dependencies = [ 3059 + "libc", 3060 + "log", 3061 + "mach", 3062 + "winapi", 3063 + ] 3064 + 3065 + [[package]] 3066 + name = "redox_syscall" 3067 + version = "0.4.1" 3068 + source = "registry+https://github.com/rust-lang/crates.io-index" 3069 + checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" 3070 + dependencies = [ 3071 + "bitflags 1.3.2", 3072 + ] 3073 + 3074 + [[package]] 3075 + name = "redox_syscall" 3076 + version = "0.5.7" 3077 + source = "registry+https://github.com/rust-lang/crates.io-index" 3078 + checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" 3079 + dependencies = [ 3080 + "bitflags 2.6.0", 3081 + ] 3082 + 3083 + [[package]] 3084 + name = "regex" 3085 + version = "1.11.0" 3086 + source = "registry+https://github.com/rust-lang/crates.io-index" 3087 + checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" 3088 + dependencies = [ 3089 + "aho-corasick", 3090 + "memchr", 3091 + "regex-automata 0.4.8", 3092 + "regex-syntax 0.8.5", 3093 + ] 3094 + 3095 + [[package]] 3096 + name = "regex-automata" 3097 + version = "0.1.10" 3098 + source = "registry+https://github.com/rust-lang/crates.io-index" 3099 + checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 3100 + dependencies = [ 3101 + "regex-syntax 0.6.29", 3102 + ] 3103 + 3104 + [[package]] 3105 + name = "regex-automata" 3106 + version = "0.4.8" 3107 + source = "registry+https://github.com/rust-lang/crates.io-index" 3108 + checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" 3109 + dependencies = [ 3110 + "aho-corasick", 3111 + "memchr", 3112 + "regex-syntax 0.8.5", 3113 + ] 3114 + 3115 + [[package]] 3116 + name = "regex-syntax" 3117 + version = "0.6.29" 3118 + source = "registry+https://github.com/rust-lang/crates.io-index" 3119 + checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" 3120 + 3121 + [[package]] 3122 + name = "regex-syntax" 3123 + version = "0.8.5" 3124 + source = "registry+https://github.com/rust-lang/crates.io-index" 3125 + checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 3126 + 3127 + [[package]] 3128 + name = "renderdoc-sys" 3129 + version = "1.1.0" 3130 + source = "registry+https://github.com/rust-lang/crates.io-index" 3131 + checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" 3132 + 3133 + [[package]] 3134 + name = "rgb" 3135 + version = "0.8.50" 3136 + source = "registry+https://github.com/rust-lang/crates.io-index" 3137 + checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" 3138 + 3139 + [[package]] 3140 + name = "ron" 3141 + version = "0.8.1" 3142 + source = "registry+https://github.com/rust-lang/crates.io-index" 3143 + checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" 3144 + dependencies = [ 3145 + "base64 0.21.7", 3146 + "bitflags 2.6.0", 3147 + "serde", 3148 + "serde_derive", 3149 + ] 3150 + 3151 + [[package]] 3152 + name = "rustc-demangle" 3153 + version = "0.1.24" 3154 + source = "registry+https://github.com/rust-lang/crates.io-index" 3155 + checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 3156 + 3157 + [[package]] 3158 + name = "rustc-hash" 3159 + version = "1.1.0" 3160 + source = "registry+https://github.com/rust-lang/crates.io-index" 3161 + checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 3162 + 3163 + [[package]] 3164 + name = "rustix" 3165 + version = "0.37.27" 3166 + source = "registry+https://github.com/rust-lang/crates.io-index" 3167 + checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" 3168 + dependencies = [ 3169 + "bitflags 1.3.2", 3170 + "errno", 3171 + "io-lifetimes", 3172 + "libc", 3173 + "linux-raw-sys 0.3.8", 3174 + "windows-sys 0.48.0", 3175 + ] 3176 + 3177 + [[package]] 3178 + name = "rustix" 3179 + version = "0.38.37" 3180 + source = "registry+https://github.com/rust-lang/crates.io-index" 3181 + checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" 3182 + dependencies = [ 3183 + "bitflags 2.6.0", 3184 + "errno", 3185 + "libc", 3186 + "linux-raw-sys 0.4.14", 3187 + "windows-sys 0.52.0", 3188 + ] 3189 + 3190 + [[package]] 3191 + name = "rustversion" 3192 + version = "1.0.17" 3193 + source = "registry+https://github.com/rust-lang/crates.io-index" 3194 + checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" 3195 + 3196 + [[package]] 3197 + name = "ryu" 3198 + version = "1.0.18" 3199 + source = "registry+https://github.com/rust-lang/crates.io-index" 3200 + checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 3201 + 3202 + [[package]] 3203 + name = "same-file" 3204 + version = "1.0.6" 3205 + source = "registry+https://github.com/rust-lang/crates.io-index" 3206 + checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 3207 + dependencies = [ 3208 + "winapi-util", 3209 + ] 3210 + 3211 + [[package]] 3212 + name = "scoped-tls" 3213 + version = "1.0.1" 3214 + source = "registry+https://github.com/rust-lang/crates.io-index" 3215 + checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" 3216 + 3217 + [[package]] 3218 + name = "scopeguard" 3219 + version = "1.2.0" 3220 + source = "registry+https://github.com/rust-lang/crates.io-index" 3221 + checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 3222 + 3223 + [[package]] 3224 + name = "sctk-adwaita" 3225 + version = "0.10.1" 3226 + source = "registry+https://github.com/rust-lang/crates.io-index" 3227 + checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec" 3228 + dependencies = [ 3229 + "ab_glyph", 3230 + "log", 3231 + "memmap2", 3232 + "smithay-client-toolkit", 3233 + "tiny-skia", 3234 + ] 3235 + 3236 + [[package]] 3237 + name = "semver" 3238 + version = "1.0.23" 3239 + source = "registry+https://github.com/rust-lang/crates.io-index" 3240 + checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" 3241 + dependencies = [ 3242 + "serde", 3243 + ] 3244 + 3245 + [[package]] 290 3246 name = "serde" 291 3247 version = "1.0.210" 292 3248 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 303 3259 dependencies = [ 304 3260 "proc-macro2", 305 3261 "quote", 306 - "syn", 3262 + "syn 2.0.79", 3263 + ] 3264 + 3265 + [[package]] 3266 + name = "serde_json" 3267 + version = "1.0.128" 3268 + source = "registry+https://github.com/rust-lang/crates.io-index" 3269 + checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" 3270 + dependencies = [ 3271 + "itoa", 3272 + "memchr", 3273 + "ryu", 3274 + "serde", 3275 + ] 3276 + 3277 + [[package]] 3278 + name = "serde_repr" 3279 + version = "0.1.19" 3280 + source = "registry+https://github.com/rust-lang/crates.io-index" 3281 + checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" 3282 + dependencies = [ 3283 + "proc-macro2", 3284 + "quote", 3285 + "syn 2.0.79", 307 3286 ] 308 3287 309 3288 [[package]] 310 3289 name = "serde_spanned" 311 - version = "0.6.7" 3290 + version = "0.6.8" 3291 + source = "registry+https://github.com/rust-lang/crates.io-index" 3292 + checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" 3293 + dependencies = [ 3294 + "serde", 3295 + ] 3296 + 3297 + [[package]] 3298 + name = "serde_with" 3299 + version = "3.11.0" 312 3300 source = "registry+https://github.com/rust-lang/crates.io-index" 313 - checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" 3301 + checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" 314 3302 dependencies = [ 3303 + "base64 0.22.1", 3304 + "chrono", 3305 + "hex", 3306 + "indexmap 1.9.3", 3307 + "indexmap 2.6.0", 315 3308 "serde", 3309 + "serde_derive", 3310 + "serde_json", 3311 + "serde_with_macros", 3312 + "time", 316 3313 ] 317 3314 318 3315 [[package]] 3316 + name = "serde_with_macros" 3317 + version = "3.11.0" 3318 + source = "registry+https://github.com/rust-lang/crates.io-index" 3319 + checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" 3320 + dependencies = [ 3321 + "darling", 3322 + "proc-macro2", 3323 + "quote", 3324 + "syn 2.0.79", 3325 + ] 3326 + 3327 + [[package]] 3328 + name = "sha1" 3329 + version = "0.10.6" 3330 + source = "registry+https://github.com/rust-lang/crates.io-index" 3331 + checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" 3332 + dependencies = [ 3333 + "cfg-if", 3334 + "cpufeatures", 3335 + "digest", 3336 + ] 3337 + 3338 + [[package]] 3339 + name = "sha2" 3340 + version = "0.10.8" 3341 + source = "registry+https://github.com/rust-lang/crates.io-index" 3342 + checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" 3343 + dependencies = [ 3344 + "cfg-if", 3345 + "cpufeatures", 3346 + "digest", 3347 + ] 3348 + 3349 + [[package]] 3350 + name = "sharded-slab" 3351 + version = "0.1.7" 3352 + source = "registry+https://github.com/rust-lang/crates.io-index" 3353 + checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 3354 + dependencies = [ 3355 + "lazy_static", 3356 + ] 3357 + 3358 + [[package]] 3359 + name = "shlex" 3360 + version = "1.3.0" 3361 + source = "registry+https://github.com/rust-lang/crates.io-index" 3362 + checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 3363 + 3364 + [[package]] 3365 + name = "signal-hook-registry" 3366 + version = "1.4.2" 3367 + source = "registry+https://github.com/rust-lang/crates.io-index" 3368 + checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" 3369 + dependencies = [ 3370 + "libc", 3371 + ] 3372 + 3373 + [[package]] 3374 + name = "simd-adler32" 3375 + version = "0.3.7" 3376 + source = "registry+https://github.com/rust-lang/crates.io-index" 3377 + checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" 3378 + 3379 + [[package]] 3380 + name = "slab" 3381 + version = "0.4.9" 3382 + source = "registry+https://github.com/rust-lang/crates.io-index" 3383 + checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 3384 + dependencies = [ 3385 + "autocfg", 3386 + ] 3387 + 3388 + [[package]] 3389 + name = "slotmap" 3390 + version = "1.0.7" 3391 + source = "registry+https://github.com/rust-lang/crates.io-index" 3392 + checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" 3393 + dependencies = [ 3394 + "version_check", 3395 + ] 3396 + 3397 + [[package]] 3398 + name = "smallvec" 3399 + version = "1.13.2" 3400 + source = "registry+https://github.com/rust-lang/crates.io-index" 3401 + checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 3402 + 3403 + [[package]] 3404 + name = "smart-default" 3405 + version = "0.7.1" 3406 + source = "registry+https://github.com/rust-lang/crates.io-index" 3407 + checksum = "0eb01866308440fc64d6c44d9e86c5cc17adfe33c4d6eed55da9145044d0ffc1" 3408 + dependencies = [ 3409 + "proc-macro2", 3410 + "quote", 3411 + "syn 2.0.79", 3412 + ] 3413 + 3414 + [[package]] 3415 + name = "smithay-client-toolkit" 3416 + version = "0.19.2" 3417 + source = "registry+https://github.com/rust-lang/crates.io-index" 3418 + checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" 3419 + dependencies = [ 3420 + "bitflags 2.6.0", 3421 + "calloop", 3422 + "calloop-wayland-source", 3423 + "cursor-icon", 3424 + "libc", 3425 + "log", 3426 + "memmap2", 3427 + "rustix 0.38.37", 3428 + "thiserror", 3429 + "wayland-backend", 3430 + "wayland-client", 3431 + "wayland-csd-frame", 3432 + "wayland-cursor", 3433 + "wayland-protocols", 3434 + "wayland-protocols-wlr", 3435 + "wayland-scanner", 3436 + "xkeysym", 3437 + ] 3438 + 3439 + [[package]] 3440 + name = "smithay-clipboard" 3441 + version = "0.7.2" 3442 + source = "registry+https://github.com/rust-lang/crates.io-index" 3443 + checksum = "cc8216eec463674a0e90f29e0ae41a4db573ec5b56b1c6c1c71615d249b6d846" 3444 + dependencies = [ 3445 + "libc", 3446 + "smithay-client-toolkit", 3447 + "wayland-backend", 3448 + ] 3449 + 3450 + [[package]] 3451 + name = "smol_str" 3452 + version = "0.2.2" 3453 + source = "registry+https://github.com/rust-lang/crates.io-index" 3454 + checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" 3455 + dependencies = [ 3456 + "serde", 3457 + ] 3458 + 3459 + [[package]] 3460 + name = "socket2" 3461 + version = "0.4.10" 3462 + source = "registry+https://github.com/rust-lang/crates.io-index" 3463 + checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" 3464 + dependencies = [ 3465 + "libc", 3466 + "winapi", 3467 + ] 3468 + 3469 + [[package]] 3470 + name = "socket2" 3471 + version = "0.5.7" 3472 + source = "registry+https://github.com/rust-lang/crates.io-index" 3473 + checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" 3474 + dependencies = [ 3475 + "libc", 3476 + "windows-sys 0.52.0", 3477 + ] 3478 + 3479 + [[package]] 3480 + name = "spirv" 3481 + version = "0.3.0+sdk-1.3.268.0" 3482 + source = "registry+https://github.com/rust-lang/crates.io-index" 3483 + checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" 3484 + dependencies = [ 3485 + "bitflags 2.6.0", 3486 + ] 3487 + 3488 + [[package]] 3489 + name = "static_assertions" 3490 + version = "1.1.0" 3491 + source = "registry+https://github.com/rust-lang/crates.io-index" 3492 + checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 3493 + 3494 + [[package]] 319 3495 name = "strfmt" 320 3496 version = "0.2.4" 321 3497 source = "registry+https://github.com/rust-lang/crates.io-index" 322 3498 checksum = "7a8348af2d9fc3258c8733b8d9d8db2e56f54b2363a4b5b81585c7875ed65e65" 323 3499 324 3500 [[package]] 3501 + name = "strict-num" 3502 + version = "0.1.1" 3503 + source = "registry+https://github.com/rust-lang/crates.io-index" 3504 + checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" 3505 + 3506 + [[package]] 325 3507 name = "strsim" 326 3508 version = "0.11.1" 327 3509 source = "registry+https://github.com/rust-lang/crates.io-index" 328 3510 checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 329 3511 330 3512 [[package]] 3513 + name = "strum" 3514 + version = "0.26.3" 3515 + source = "registry+https://github.com/rust-lang/crates.io-index" 3516 + checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" 3517 + dependencies = [ 3518 + "strum_macros", 3519 + ] 3520 + 3521 + [[package]] 3522 + name = "strum_macros" 3523 + version = "0.26.4" 3524 + source = "registry+https://github.com/rust-lang/crates.io-index" 3525 + checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" 3526 + dependencies = [ 3527 + "heck", 3528 + "proc-macro2", 3529 + "quote", 3530 + "rustversion", 3531 + "syn 2.0.79", 3532 + ] 3533 + 3534 + [[package]] 331 3535 name = "syn" 332 - version = "2.0.77" 3536 + version = "1.0.109" 333 3537 source = "registry+https://github.com/rust-lang/crates.io-index" 334 - checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" 3538 + checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 3539 + dependencies = [ 3540 + "proc-macro2", 3541 + "quote", 3542 + "unicode-ident", 3543 + ] 3544 + 3545 + [[package]] 3546 + name = "syn" 3547 + version = "2.0.79" 3548 + source = "registry+https://github.com/rust-lang/crates.io-index" 3549 + checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" 335 3550 dependencies = [ 336 3551 "proc-macro2", 337 3552 "quote", ··· 349 3564 "memchr", 350 3565 "ntapi", 351 3566 "rayon", 352 - "windows", 3567 + "windows 0.57.0", 3568 + ] 3569 + 3570 + [[package]] 3571 + name = "tempfile" 3572 + version = "3.13.0" 3573 + source = "registry+https://github.com/rust-lang/crates.io-index" 3574 + checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" 3575 + dependencies = [ 3576 + "cfg-if", 3577 + "fastrand 2.1.1", 3578 + "once_cell", 3579 + "rustix 0.38.37", 3580 + "windows-sys 0.59.0", 3581 + ] 3582 + 3583 + [[package]] 3584 + name = "termcolor" 3585 + version = "1.4.1" 3586 + source = "registry+https://github.com/rust-lang/crates.io-index" 3587 + checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" 3588 + dependencies = [ 3589 + "winapi-util", 3590 + ] 3591 + 3592 + [[package]] 3593 + name = "thiserror" 3594 + version = "1.0.64" 3595 + source = "registry+https://github.com/rust-lang/crates.io-index" 3596 + checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" 3597 + dependencies = [ 3598 + "thiserror-impl", 3599 + ] 3600 + 3601 + [[package]] 3602 + name = "thiserror-impl" 3603 + version = "1.0.64" 3604 + source = "registry+https://github.com/rust-lang/crates.io-index" 3605 + checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" 3606 + dependencies = [ 3607 + "proc-macro2", 3608 + "quote", 3609 + "syn 2.0.79", 3610 + ] 3611 + 3612 + [[package]] 3613 + name = "thread_local" 3614 + version = "1.1.8" 3615 + source = "registry+https://github.com/rust-lang/crates.io-index" 3616 + checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" 3617 + dependencies = [ 3618 + "cfg-if", 3619 + "once_cell", 3620 + ] 3621 + 3622 + [[package]] 3623 + name = "time" 3624 + version = "0.3.36" 3625 + source = "registry+https://github.com/rust-lang/crates.io-index" 3626 + checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" 3627 + dependencies = [ 3628 + "deranged", 3629 + "itoa", 3630 + "num-conv", 3631 + "powerfmt", 3632 + "serde", 3633 + "time-core", 3634 + "time-macros", 3635 + ] 3636 + 3637 + [[package]] 3638 + name = "time-core" 3639 + version = "0.1.2" 3640 + source = "registry+https://github.com/rust-lang/crates.io-index" 3641 + checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" 3642 + 3643 + [[package]] 3644 + name = "time-macros" 3645 + version = "0.2.18" 3646 + source = "registry+https://github.com/rust-lang/crates.io-index" 3647 + checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" 3648 + dependencies = [ 3649 + "num-conv", 3650 + "time-core", 3651 + ] 3652 + 3653 + [[package]] 3654 + name = "tiny-skia" 3655 + version = "0.11.4" 3656 + source = "registry+https://github.com/rust-lang/crates.io-index" 3657 + checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" 3658 + dependencies = [ 3659 + "arrayref", 3660 + "arrayvec", 3661 + "bytemuck", 3662 + "cfg-if", 3663 + "log", 3664 + "tiny-skia-path", 3665 + ] 3666 + 3667 + [[package]] 3668 + name = "tiny-skia-path" 3669 + version = "0.11.4" 3670 + source = "registry+https://github.com/rust-lang/crates.io-index" 3671 + checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" 3672 + dependencies = [ 3673 + "arrayref", 3674 + "bytemuck", 3675 + "strict-num", 3676 + ] 3677 + 3678 + [[package]] 3679 + name = "tinyvec" 3680 + version = "1.8.0" 3681 + source = "registry+https://github.com/rust-lang/crates.io-index" 3682 + checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" 3683 + dependencies = [ 3684 + "tinyvec_macros", 3685 + ] 3686 + 3687 + [[package]] 3688 + name = "tinyvec_macros" 3689 + version = "0.1.1" 3690 + source = "registry+https://github.com/rust-lang/crates.io-index" 3691 + checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 3692 + 3693 + [[package]] 3694 + name = "tokio" 3695 + version = "1.40.0" 3696 + source = "registry+https://github.com/rust-lang/crates.io-index" 3697 + checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" 3698 + dependencies = [ 3699 + "backtrace", 3700 + "bytes", 3701 + "libc", 3702 + "mio", 3703 + "pin-project-lite", 3704 + "socket2 0.5.7", 3705 + "windows-sys 0.52.0", 3706 + ] 3707 + 3708 + [[package]] 3709 + name = "tokio-tungstenite" 3710 + version = "0.24.0" 3711 + source = "registry+https://github.com/rust-lang/crates.io-index" 3712 + checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" 3713 + dependencies = [ 3714 + "futures-util", 3715 + "log", 3716 + "tokio", 3717 + "tungstenite", 353 3718 ] 354 3719 355 3720 [[package]] ··· 361 3726 "serde", 362 3727 "serde_spanned", 363 3728 "toml_datetime", 364 - "toml_edit", 3729 + "toml_edit 0.22.22", 365 3730 ] 366 3731 367 3732 [[package]] ··· 375 3740 376 3741 [[package]] 377 3742 name = "toml_edit" 378 - version = "0.22.21" 3743 + version = "0.19.15" 379 3744 source = "registry+https://github.com/rust-lang/crates.io-index" 380 - checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" 3745 + checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" 381 3746 dependencies = [ 382 - "indexmap", 3747 + "indexmap 2.6.0", 3748 + "toml_datetime", 3749 + "winnow 0.5.40", 3750 + ] 3751 + 3752 + [[package]] 3753 + name = "toml_edit" 3754 + version = "0.22.22" 3755 + source = "registry+https://github.com/rust-lang/crates.io-index" 3756 + checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" 3757 + dependencies = [ 3758 + "indexmap 2.6.0", 383 3759 "serde", 384 3760 "serde_spanned", 385 3761 "toml_datetime", 386 - "winnow", 3762 + "winnow 0.6.20", 3763 + ] 3764 + 3765 + [[package]] 3766 + name = "tracing" 3767 + version = "0.1.40" 3768 + source = "registry+https://github.com/rust-lang/crates.io-index" 3769 + checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" 3770 + dependencies = [ 3771 + "pin-project-lite", 3772 + "tracing-attributes", 3773 + "tracing-core", 3774 + ] 3775 + 3776 + [[package]] 3777 + name = "tracing-attributes" 3778 + version = "0.1.27" 3779 + source = "registry+https://github.com/rust-lang/crates.io-index" 3780 + checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" 3781 + dependencies = [ 3782 + "proc-macro2", 3783 + "quote", 3784 + "syn 2.0.79", 3785 + ] 3786 + 3787 + [[package]] 3788 + name = "tracing-core" 3789 + version = "0.1.32" 3790 + source = "registry+https://github.com/rust-lang/crates.io-index" 3791 + checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" 3792 + dependencies = [ 3793 + "once_cell", 3794 + "valuable", 3795 + ] 3796 + 3797 + [[package]] 3798 + name = "tracing-error" 3799 + version = "0.2.0" 3800 + source = "registry+https://github.com/rust-lang/crates.io-index" 3801 + checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" 3802 + dependencies = [ 3803 + "tracing", 3804 + "tracing-subscriber", 3805 + ] 3806 + 3807 + [[package]] 3808 + name = "tracing-log" 3809 + version = "0.2.0" 3810 + source = "registry+https://github.com/rust-lang/crates.io-index" 3811 + checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" 3812 + dependencies = [ 3813 + "log", 3814 + "once_cell", 3815 + "tracing-core", 3816 + ] 3817 + 3818 + [[package]] 3819 + name = "tracing-subscriber" 3820 + version = "0.3.18" 3821 + source = "registry+https://github.com/rust-lang/crates.io-index" 3822 + checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" 3823 + dependencies = [ 3824 + "matchers", 3825 + "nu-ansi-term", 3826 + "once_cell", 3827 + "regex", 3828 + "sharded-slab", 3829 + "smallvec", 3830 + "thread_local", 3831 + "tracing", 3832 + "tracing-core", 3833 + "tracing-log", 3834 + ] 3835 + 3836 + [[package]] 3837 + name = "ttf-parser" 3838 + version = "0.25.0" 3839 + source = "registry+https://github.com/rust-lang/crates.io-index" 3840 + checksum = "5902c5d130972a0000f60860bfbf46f7ca3db5391eddfedd1b8728bd9dc96c0e" 3841 + 3842 + [[package]] 3843 + name = "tungstenite" 3844 + version = "0.24.0" 3845 + source = "registry+https://github.com/rust-lang/crates.io-index" 3846 + checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" 3847 + dependencies = [ 3848 + "byteorder", 3849 + "bytes", 3850 + "data-encoding", 3851 + "http", 3852 + "httparse", 3853 + "log", 3854 + "rand", 3855 + "sha1", 3856 + "thiserror", 3857 + "utf-8", 3858 + ] 3859 + 3860 + [[package]] 3861 + name = "type-map" 3862 + version = "0.5.0" 3863 + source = "registry+https://github.com/rust-lang/crates.io-index" 3864 + checksum = "deb68604048ff8fa93347f02441e4487594adc20bb8a084f9e564d2b827a0a9f" 3865 + dependencies = [ 3866 + "rustc-hash", 3867 + ] 3868 + 3869 + [[package]] 3870 + name = "typenum" 3871 + version = "1.17.0" 3872 + source = "registry+https://github.com/rust-lang/crates.io-index" 3873 + checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 3874 + 3875 + [[package]] 3876 + name = "uds_windows" 3877 + version = "1.1.0" 3878 + source = "registry+https://github.com/rust-lang/crates.io-index" 3879 + checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" 3880 + dependencies = [ 3881 + "memoffset 0.9.1", 3882 + "tempfile", 3883 + "winapi", 3884 + ] 3885 + 3886 + [[package]] 3887 + name = "unicase" 3888 + version = "2.7.0" 3889 + source = "registry+https://github.com/rust-lang/crates.io-index" 3890 + checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" 3891 + dependencies = [ 3892 + "version_check", 387 3893 ] 388 3894 389 3895 [[package]] 3896 + name = "unicode-bidi" 3897 + version = "0.3.17" 3898 + source = "registry+https://github.com/rust-lang/crates.io-index" 3899 + checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" 3900 + 3901 + [[package]] 390 3902 name = "unicode-ident" 391 3903 version = "1.0.13" 392 3904 source = "registry+https://github.com/rust-lang/crates.io-index" 393 3905 checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" 394 3906 395 3907 [[package]] 3908 + name = "unicode-normalization" 3909 + version = "0.1.24" 3910 + source = "registry+https://github.com/rust-lang/crates.io-index" 3911 + checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" 3912 + dependencies = [ 3913 + "tinyvec", 3914 + ] 3915 + 3916 + [[package]] 3917 + name = "unicode-segmentation" 3918 + version = "1.12.0" 3919 + source = "registry+https://github.com/rust-lang/crates.io-index" 3920 + checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" 3921 + 3922 + [[package]] 3923 + name = "unicode-width" 3924 + version = "0.1.14" 3925 + source = "registry+https://github.com/rust-lang/crates.io-index" 3926 + checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" 3927 + 3928 + [[package]] 3929 + name = "unicode-xid" 3930 + version = "0.2.6" 3931 + source = "registry+https://github.com/rust-lang/crates.io-index" 3932 + checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" 3933 + 3934 + [[package]] 3935 + name = "url" 3936 + version = "2.5.2" 3937 + source = "registry+https://github.com/rust-lang/crates.io-index" 3938 + checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" 3939 + dependencies = [ 3940 + "form_urlencoded", 3941 + "idna", 3942 + "percent-encoding", 3943 + ] 3944 + 3945 + [[package]] 3946 + name = "utf-8" 3947 + version = "0.7.6" 3948 + source = "registry+https://github.com/rust-lang/crates.io-index" 3949 + checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" 3950 + 3951 + [[package]] 396 3952 name = "utf8parse" 397 3953 version = "0.2.2" 398 3954 source = "registry+https://github.com/rust-lang/crates.io-index" 399 3955 checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 400 3956 401 3957 [[package]] 3958 + name = "uuid" 3959 + version = "1.10.0" 3960 + source = "registry+https://github.com/rust-lang/crates.io-index" 3961 + checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" 3962 + dependencies = [ 3963 + "serde", 3964 + ] 3965 + 3966 + [[package]] 3967 + name = "valuable" 3968 + version = "0.1.0" 3969 + source = "registry+https://github.com/rust-lang/crates.io-index" 3970 + checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" 3971 + 3972 + [[package]] 3973 + name = "version_check" 3974 + version = "0.9.5" 3975 + source = "registry+https://github.com/rust-lang/crates.io-index" 3976 + checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 3977 + 3978 + [[package]] 3979 + name = "waker-fn" 3980 + version = "1.2.0" 3981 + source = "registry+https://github.com/rust-lang/crates.io-index" 3982 + checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" 3983 + 3984 + [[package]] 3985 + name = "walkdir" 3986 + version = "2.5.0" 3987 + source = "registry+https://github.com/rust-lang/crates.io-index" 3988 + checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 3989 + dependencies = [ 3990 + "same-file", 3991 + "winapi-util", 3992 + ] 3993 + 3994 + [[package]] 3995 + name = "wasi" 3996 + version = "0.11.0+wasi-snapshot-preview1" 3997 + source = "registry+https://github.com/rust-lang/crates.io-index" 3998 + checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 3999 + 4000 + [[package]] 4001 + name = "wasm-bindgen" 4002 + version = "0.2.93" 4003 + source = "registry+https://github.com/rust-lang/crates.io-index" 4004 + checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" 4005 + dependencies = [ 4006 + "cfg-if", 4007 + "once_cell", 4008 + "wasm-bindgen-macro", 4009 + ] 4010 + 4011 + [[package]] 4012 + name = "wasm-bindgen-backend" 4013 + version = "0.2.93" 4014 + source = "registry+https://github.com/rust-lang/crates.io-index" 4015 + checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" 4016 + dependencies = [ 4017 + "bumpalo", 4018 + "log", 4019 + "once_cell", 4020 + "proc-macro2", 4021 + "quote", 4022 + "syn 2.0.79", 4023 + "wasm-bindgen-shared", 4024 + ] 4025 + 4026 + [[package]] 4027 + name = "wasm-bindgen-futures" 4028 + version = "0.4.43" 4029 + source = "registry+https://github.com/rust-lang/crates.io-index" 4030 + checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" 4031 + dependencies = [ 4032 + "cfg-if", 4033 + "js-sys", 4034 + "wasm-bindgen", 4035 + "web-sys", 4036 + ] 4037 + 4038 + [[package]] 4039 + name = "wasm-bindgen-macro" 4040 + version = "0.2.93" 4041 + source = "registry+https://github.com/rust-lang/crates.io-index" 4042 + checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" 4043 + dependencies = [ 4044 + "quote", 4045 + "wasm-bindgen-macro-support", 4046 + ] 4047 + 4048 + [[package]] 4049 + name = "wasm-bindgen-macro-support" 4050 + version = "0.2.93" 4051 + source = "registry+https://github.com/rust-lang/crates.io-index" 4052 + checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" 4053 + dependencies = [ 4054 + "proc-macro2", 4055 + "quote", 4056 + "syn 2.0.79", 4057 + "wasm-bindgen-backend", 4058 + "wasm-bindgen-shared", 4059 + ] 4060 + 4061 + [[package]] 4062 + name = "wasm-bindgen-shared" 4063 + version = "0.2.93" 4064 + source = "registry+https://github.com/rust-lang/crates.io-index" 4065 + checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" 4066 + 4067 + [[package]] 4068 + name = "wayland-backend" 4069 + version = "0.3.7" 4070 + source = "registry+https://github.com/rust-lang/crates.io-index" 4071 + checksum = "056535ced7a150d45159d3a8dc30f91a2e2d588ca0b23f70e56033622b8016f6" 4072 + dependencies = [ 4073 + "cc", 4074 + "downcast-rs", 4075 + "rustix 0.38.37", 4076 + "scoped-tls", 4077 + "smallvec", 4078 + "wayland-sys", 4079 + ] 4080 + 4081 + [[package]] 4082 + name = "wayland-client" 4083 + version = "0.31.6" 4084 + source = "registry+https://github.com/rust-lang/crates.io-index" 4085 + checksum = "e3f45d1222915ef1fd2057220c1d9d9624b7654443ea35c3877f7a52bd0a5a2d" 4086 + dependencies = [ 4087 + "bitflags 2.6.0", 4088 + "rustix 0.38.37", 4089 + "wayland-backend", 4090 + "wayland-scanner", 4091 + ] 4092 + 4093 + [[package]] 4094 + name = "wayland-csd-frame" 4095 + version = "0.3.0" 4096 + source = "registry+https://github.com/rust-lang/crates.io-index" 4097 + checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" 4098 + dependencies = [ 4099 + "bitflags 2.6.0", 4100 + "cursor-icon", 4101 + "wayland-backend", 4102 + ] 4103 + 4104 + [[package]] 4105 + name = "wayland-cursor" 4106 + version = "0.31.6" 4107 + source = "registry+https://github.com/rust-lang/crates.io-index" 4108 + checksum = "3a94697e66e76c85923b0d28a0c251e8f0666f58fc47d316c0f4da6da75d37cb" 4109 + dependencies = [ 4110 + "rustix 0.38.37", 4111 + "wayland-client", 4112 + "xcursor", 4113 + ] 4114 + 4115 + [[package]] 4116 + name = "wayland-protocols" 4117 + version = "0.32.4" 4118 + source = "registry+https://github.com/rust-lang/crates.io-index" 4119 + checksum = "2b5755d77ae9040bb872a25026555ce4cb0ae75fd923e90d25fba07d81057de0" 4120 + dependencies = [ 4121 + "bitflags 2.6.0", 4122 + "wayland-backend", 4123 + "wayland-client", 4124 + "wayland-scanner", 4125 + ] 4126 + 4127 + [[package]] 4128 + name = "wayland-protocols-plasma" 4129 + version = "0.3.4" 4130 + source = "registry+https://github.com/rust-lang/crates.io-index" 4131 + checksum = "8a0a41a6875e585172495f7a96dfa42ca7e0213868f4f15c313f7c33221a7eff" 4132 + dependencies = [ 4133 + "bitflags 2.6.0", 4134 + "wayland-backend", 4135 + "wayland-client", 4136 + "wayland-protocols", 4137 + "wayland-scanner", 4138 + ] 4139 + 4140 + [[package]] 4141 + name = "wayland-protocols-wlr" 4142 + version = "0.3.4" 4143 + source = "registry+https://github.com/rust-lang/crates.io-index" 4144 + checksum = "dad87b5fd1b1d3ca2f792df8f686a2a11e3fe1077b71096f7a175ab699f89109" 4145 + dependencies = [ 4146 + "bitflags 2.6.0", 4147 + "wayland-backend", 4148 + "wayland-client", 4149 + "wayland-protocols", 4150 + "wayland-scanner", 4151 + ] 4152 + 4153 + [[package]] 4154 + name = "wayland-scanner" 4155 + version = "0.31.5" 4156 + source = "registry+https://github.com/rust-lang/crates.io-index" 4157 + checksum = "597f2001b2e5fc1121e3d5b9791d3e78f05ba6bfa4641053846248e3a13661c3" 4158 + dependencies = [ 4159 + "proc-macro2", 4160 + "quick-xml", 4161 + "quote", 4162 + ] 4163 + 4164 + [[package]] 4165 + name = "wayland-sys" 4166 + version = "0.31.5" 4167 + source = "registry+https://github.com/rust-lang/crates.io-index" 4168 + checksum = "efa8ac0d8e8ed3e3b5c9fc92c7881406a268e11555abe36493efabe649a29e09" 4169 + dependencies = [ 4170 + "dlib", 4171 + "log", 4172 + "once_cell", 4173 + "pkg-config", 4174 + ] 4175 + 4176 + [[package]] 4177 + name = "web-sys" 4178 + version = "0.3.70" 4179 + source = "registry+https://github.com/rust-lang/crates.io-index" 4180 + checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" 4181 + dependencies = [ 4182 + "js-sys", 4183 + "wasm-bindgen", 4184 + ] 4185 + 4186 + [[package]] 4187 + name = "web-time" 4188 + version = "1.1.0" 4189 + source = "registry+https://github.com/rust-lang/crates.io-index" 4190 + checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" 4191 + dependencies = [ 4192 + "js-sys", 4193 + "wasm-bindgen", 4194 + ] 4195 + 4196 + [[package]] 4197 + name = "webbrowser" 4198 + version = "1.0.2" 4199 + source = "registry+https://github.com/rust-lang/crates.io-index" 4200 + checksum = "2e5f07fb9bc8de2ddfe6b24a71a75430673fd679e568c48b52716cef1cfae923" 4201 + dependencies = [ 4202 + "block2", 4203 + "core-foundation 0.10.0", 4204 + "home", 4205 + "jni", 4206 + "log", 4207 + "ndk-context", 4208 + "objc2", 4209 + "objc2-foundation", 4210 + "url", 4211 + "web-sys", 4212 + ] 4213 + 4214 + [[package]] 4215 + name = "wgpu" 4216 + version = "22.1.0" 4217 + source = "registry+https://github.com/rust-lang/crates.io-index" 4218 + checksum = "e1d1c4ba43f80542cf63a0a6ed3134629ae73e8ab51e4b765a67f3aa062eb433" 4219 + dependencies = [ 4220 + "arrayvec", 4221 + "cfg_aliases 0.1.1", 4222 + "document-features", 4223 + "js-sys", 4224 + "log", 4225 + "naga", 4226 + "parking_lot", 4227 + "profiling", 4228 + "raw-window-handle", 4229 + "smallvec", 4230 + "static_assertions", 4231 + "wasm-bindgen", 4232 + "wasm-bindgen-futures", 4233 + "web-sys", 4234 + "wgpu-core", 4235 + "wgpu-hal", 4236 + "wgpu-types", 4237 + ] 4238 + 4239 + [[package]] 4240 + name = "wgpu-core" 4241 + version = "22.1.0" 4242 + source = "registry+https://github.com/rust-lang/crates.io-index" 4243 + checksum = "0348c840d1051b8e86c3bcd31206080c5e71e5933dabd79be1ce732b0b2f089a" 4244 + dependencies = [ 4245 + "arrayvec", 4246 + "bit-vec", 4247 + "bitflags 2.6.0", 4248 + "cfg_aliases 0.1.1", 4249 + "document-features", 4250 + "indexmap 2.6.0", 4251 + "log", 4252 + "naga", 4253 + "once_cell", 4254 + "parking_lot", 4255 + "profiling", 4256 + "raw-window-handle", 4257 + "rustc-hash", 4258 + "smallvec", 4259 + "thiserror", 4260 + "wgpu-hal", 4261 + "wgpu-types", 4262 + ] 4263 + 4264 + [[package]] 4265 + name = "wgpu-hal" 4266 + version = "22.0.0" 4267 + source = "registry+https://github.com/rust-lang/crates.io-index" 4268 + checksum = "f6bbf4b4de8b2a83c0401d9e5ae0080a2792055f25859a02bf9be97952bbed4f" 4269 + dependencies = [ 4270 + "android_system_properties", 4271 + "arrayvec", 4272 + "ash", 4273 + "bitflags 2.6.0", 4274 + "block", 4275 + "cfg_aliases 0.1.1", 4276 + "core-graphics-types", 4277 + "glow 0.13.1", 4278 + "glutin_wgl_sys", 4279 + "gpu-alloc", 4280 + "gpu-allocator", 4281 + "gpu-descriptor", 4282 + "hassle-rs", 4283 + "js-sys", 4284 + "khronos-egl", 4285 + "libc", 4286 + "libloading", 4287 + "log", 4288 + "metal", 4289 + "naga", 4290 + "ndk-sys 0.5.0+25.2.9519653", 4291 + "objc", 4292 + "once_cell", 4293 + "parking_lot", 4294 + "profiling", 4295 + "raw-window-handle", 4296 + "renderdoc-sys", 4297 + "rustc-hash", 4298 + "smallvec", 4299 + "thiserror", 4300 + "wasm-bindgen", 4301 + "web-sys", 4302 + "wgpu-types", 4303 + "winapi", 4304 + ] 4305 + 4306 + [[package]] 4307 + name = "wgpu-types" 4308 + version = "22.0.0" 4309 + source = "registry+https://github.com/rust-lang/crates.io-index" 4310 + checksum = "bc9d91f0e2c4b51434dfa6db77846f2793149d8e73f800fa2e41f52b8eac3c5d" 4311 + dependencies = [ 4312 + "bitflags 2.6.0", 4313 + "js-sys", 4314 + "web-sys", 4315 + ] 4316 + 4317 + [[package]] 4318 + name = "widestring" 4319 + version = "1.1.0" 4320 + source = "registry+https://github.com/rust-lang/crates.io-index" 4321 + checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" 4322 + 4323 + [[package]] 402 4324 name = "winapi" 403 4325 version = "0.3.9" 404 4326 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 415 4337 checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 416 4338 417 4339 [[package]] 4340 + name = "winapi-util" 4341 + version = "0.1.9" 4342 + source = "registry+https://github.com/rust-lang/crates.io-index" 4343 + checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 4344 + dependencies = [ 4345 + "windows-sys 0.59.0", 4346 + ] 4347 + 4348 + [[package]] 418 4349 name = "winapi-x86_64-pc-windows-gnu" 419 4350 version = "0.4.0" 420 4351 source = "registry+https://github.com/rust-lang/crates.io-index" 421 4352 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 4353 + 4354 + [[package]] 4355 + name = "windows" 4356 + version = "0.52.0" 4357 + source = "registry+https://github.com/rust-lang/crates.io-index" 4358 + checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" 4359 + dependencies = [ 4360 + "windows-core 0.52.0", 4361 + "windows-targets 0.52.6", 4362 + ] 422 4363 423 4364 [[package]] 424 4365 name = "windows" ··· 426 4367 source = "registry+https://github.com/rust-lang/crates.io-index" 427 4368 checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" 428 4369 dependencies = [ 429 - "windows-core", 430 - "windows-targets", 4370 + "windows-core 0.57.0", 4371 + "windows-targets 0.52.6", 4372 + ] 4373 + 4374 + [[package]] 4375 + name = "windows" 4376 + version = "0.58.0" 4377 + source = "registry+https://github.com/rust-lang/crates.io-index" 4378 + checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" 4379 + dependencies = [ 4380 + "windows-core 0.58.0", 4381 + "windows-targets 0.52.6", 4382 + ] 4383 + 4384 + [[package]] 4385 + name = "windows-core" 4386 + version = "0.52.0" 4387 + source = "registry+https://github.com/rust-lang/crates.io-index" 4388 + checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 4389 + dependencies = [ 4390 + "windows-targets 0.52.6", 431 4391 ] 432 4392 433 4393 [[package]] ··· 436 4396 source = "registry+https://github.com/rust-lang/crates.io-index" 437 4397 checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" 438 4398 dependencies = [ 439 - "windows-implement", 440 - "windows-interface", 441 - "windows-result", 442 - "windows-targets", 4399 + "windows-implement 0.57.0", 4400 + "windows-interface 0.57.0", 4401 + "windows-result 0.1.2", 4402 + "windows-targets 0.52.6", 4403 + ] 4404 + 4405 + [[package]] 4406 + name = "windows-core" 4407 + version = "0.58.0" 4408 + source = "registry+https://github.com/rust-lang/crates.io-index" 4409 + checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" 4410 + dependencies = [ 4411 + "windows-implement 0.58.0", 4412 + "windows-interface 0.58.0", 4413 + "windows-result 0.2.0", 4414 + "windows-strings", 4415 + "windows-targets 0.52.6", 443 4416 ] 444 4417 445 4418 [[package]] ··· 450 4423 dependencies = [ 451 4424 "proc-macro2", 452 4425 "quote", 453 - "syn", 4426 + "syn 2.0.79", 4427 + ] 4428 + 4429 + [[package]] 4430 + name = "windows-implement" 4431 + version = "0.58.0" 4432 + source = "registry+https://github.com/rust-lang/crates.io-index" 4433 + checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" 4434 + dependencies = [ 4435 + "proc-macro2", 4436 + "quote", 4437 + "syn 2.0.79", 454 4438 ] 455 4439 456 4440 [[package]] ··· 461 4445 dependencies = [ 462 4446 "proc-macro2", 463 4447 "quote", 464 - "syn", 4448 + "syn 2.0.79", 4449 + ] 4450 + 4451 + [[package]] 4452 + name = "windows-interface" 4453 + version = "0.58.0" 4454 + source = "registry+https://github.com/rust-lang/crates.io-index" 4455 + checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" 4456 + dependencies = [ 4457 + "proc-macro2", 4458 + "quote", 4459 + "syn 2.0.79", 465 4460 ] 466 4461 467 4462 [[package]] ··· 470 4465 source = "registry+https://github.com/rust-lang/crates.io-index" 471 4466 checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" 472 4467 dependencies = [ 473 - "windows-targets", 4468 + "windows-targets 0.52.6", 4469 + ] 4470 + 4471 + [[package]] 4472 + name = "windows-result" 4473 + version = "0.2.0" 4474 + source = "registry+https://github.com/rust-lang/crates.io-index" 4475 + checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" 4476 + dependencies = [ 4477 + "windows-targets 0.52.6", 4478 + ] 4479 + 4480 + [[package]] 4481 + name = "windows-strings" 4482 + version = "0.1.0" 4483 + source = "registry+https://github.com/rust-lang/crates.io-index" 4484 + checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" 4485 + dependencies = [ 4486 + "windows-result 0.2.0", 4487 + "windows-targets 0.52.6", 4488 + ] 4489 + 4490 + [[package]] 4491 + name = "windows-sys" 4492 + version = "0.45.0" 4493 + source = "registry+https://github.com/rust-lang/crates.io-index" 4494 + checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" 4495 + dependencies = [ 4496 + "windows-targets 0.42.2", 4497 + ] 4498 + 4499 + [[package]] 4500 + name = "windows-sys" 4501 + version = "0.48.0" 4502 + source = "registry+https://github.com/rust-lang/crates.io-index" 4503 + checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 4504 + dependencies = [ 4505 + "windows-targets 0.48.5", 474 4506 ] 475 4507 476 4508 [[package]] ··· 479 4511 source = "registry+https://github.com/rust-lang/crates.io-index" 480 4512 checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 481 4513 dependencies = [ 482 - "windows-targets", 4514 + "windows-targets 0.52.6", 4515 + ] 4516 + 4517 + [[package]] 4518 + name = "windows-sys" 4519 + version = "0.59.0" 4520 + source = "registry+https://github.com/rust-lang/crates.io-index" 4521 + checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 4522 + dependencies = [ 4523 + "windows-targets 0.52.6", 4524 + ] 4525 + 4526 + [[package]] 4527 + name = "windows-targets" 4528 + version = "0.42.2" 4529 + source = "registry+https://github.com/rust-lang/crates.io-index" 4530 + checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" 4531 + dependencies = [ 4532 + "windows_aarch64_gnullvm 0.42.2", 4533 + "windows_aarch64_msvc 0.42.2", 4534 + "windows_i686_gnu 0.42.2", 4535 + "windows_i686_msvc 0.42.2", 4536 + "windows_x86_64_gnu 0.42.2", 4537 + "windows_x86_64_gnullvm 0.42.2", 4538 + "windows_x86_64_msvc 0.42.2", 4539 + ] 4540 + 4541 + [[package]] 4542 + name = "windows-targets" 4543 + version = "0.48.5" 4544 + source = "registry+https://github.com/rust-lang/crates.io-index" 4545 + checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 4546 + dependencies = [ 4547 + "windows_aarch64_gnullvm 0.48.5", 4548 + "windows_aarch64_msvc 0.48.5", 4549 + "windows_i686_gnu 0.48.5", 4550 + "windows_i686_msvc 0.48.5", 4551 + "windows_x86_64_gnu 0.48.5", 4552 + "windows_x86_64_gnullvm 0.48.5", 4553 + "windows_x86_64_msvc 0.48.5", 483 4554 ] 484 4555 485 4556 [[package]] ··· 488 4559 source = "registry+https://github.com/rust-lang/crates.io-index" 489 4560 checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 490 4561 dependencies = [ 491 - "windows_aarch64_gnullvm", 492 - "windows_aarch64_msvc", 493 - "windows_i686_gnu", 4562 + "windows_aarch64_gnullvm 0.52.6", 4563 + "windows_aarch64_msvc 0.52.6", 4564 + "windows_i686_gnu 0.52.6", 494 4565 "windows_i686_gnullvm", 495 - "windows_i686_msvc", 496 - "windows_x86_64_gnu", 497 - "windows_x86_64_gnullvm", 498 - "windows_x86_64_msvc", 4566 + "windows_i686_msvc 0.52.6", 4567 + "windows_x86_64_gnu 0.52.6", 4568 + "windows_x86_64_gnullvm 0.52.6", 4569 + "windows_x86_64_msvc 0.52.6", 499 4570 ] 500 4571 501 4572 [[package]] 502 4573 name = "windows_aarch64_gnullvm" 4574 + version = "0.42.2" 4575 + source = "registry+https://github.com/rust-lang/crates.io-index" 4576 + checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" 4577 + 4578 + [[package]] 4579 + name = "windows_aarch64_gnullvm" 4580 + version = "0.48.5" 4581 + source = "registry+https://github.com/rust-lang/crates.io-index" 4582 + checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 4583 + 4584 + [[package]] 4585 + name = "windows_aarch64_gnullvm" 503 4586 version = "0.52.6" 504 4587 source = "registry+https://github.com/rust-lang/crates.io-index" 505 4588 checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 506 4589 507 4590 [[package]] 508 4591 name = "windows_aarch64_msvc" 4592 + version = "0.42.2" 4593 + source = "registry+https://github.com/rust-lang/crates.io-index" 4594 + checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" 4595 + 4596 + [[package]] 4597 + name = "windows_aarch64_msvc" 4598 + version = "0.48.5" 4599 + source = "registry+https://github.com/rust-lang/crates.io-index" 4600 + checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 4601 + 4602 + [[package]] 4603 + name = "windows_aarch64_msvc" 509 4604 version = "0.52.6" 510 4605 source = "registry+https://github.com/rust-lang/crates.io-index" 511 4606 checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 512 4607 513 4608 [[package]] 514 4609 name = "windows_i686_gnu" 4610 + version = "0.42.2" 4611 + source = "registry+https://github.com/rust-lang/crates.io-index" 4612 + checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" 4613 + 4614 + [[package]] 4615 + name = "windows_i686_gnu" 4616 + version = "0.48.5" 4617 + source = "registry+https://github.com/rust-lang/crates.io-index" 4618 + checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 4619 + 4620 + [[package]] 4621 + name = "windows_i686_gnu" 515 4622 version = "0.52.6" 516 4623 source = "registry+https://github.com/rust-lang/crates.io-index" 517 4624 checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" ··· 521 4628 version = "0.52.6" 522 4629 source = "registry+https://github.com/rust-lang/crates.io-index" 523 4630 checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 4631 + 4632 + [[package]] 4633 + name = "windows_i686_msvc" 4634 + version = "0.42.2" 4635 + source = "registry+https://github.com/rust-lang/crates.io-index" 4636 + checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" 4637 + 4638 + [[package]] 4639 + name = "windows_i686_msvc" 4640 + version = "0.48.5" 4641 + source = "registry+https://github.com/rust-lang/crates.io-index" 4642 + checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 524 4643 525 4644 [[package]] 526 4645 name = "windows_i686_msvc" ··· 530 4649 531 4650 [[package]] 532 4651 name = "windows_x86_64_gnu" 4652 + version = "0.42.2" 4653 + source = "registry+https://github.com/rust-lang/crates.io-index" 4654 + checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" 4655 + 4656 + [[package]] 4657 + name = "windows_x86_64_gnu" 4658 + version = "0.48.5" 4659 + source = "registry+https://github.com/rust-lang/crates.io-index" 4660 + checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 4661 + 4662 + [[package]] 4663 + name = "windows_x86_64_gnu" 533 4664 version = "0.52.6" 534 4665 source = "registry+https://github.com/rust-lang/crates.io-index" 535 4666 checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 536 4667 537 4668 [[package]] 538 4669 name = "windows_x86_64_gnullvm" 4670 + version = "0.42.2" 4671 + source = "registry+https://github.com/rust-lang/crates.io-index" 4672 + checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" 4673 + 4674 + [[package]] 4675 + name = "windows_x86_64_gnullvm" 4676 + version = "0.48.5" 4677 + source = "registry+https://github.com/rust-lang/crates.io-index" 4678 + checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 4679 + 4680 + [[package]] 4681 + name = "windows_x86_64_gnullvm" 539 4682 version = "0.52.6" 540 4683 source = "registry+https://github.com/rust-lang/crates.io-index" 541 4684 checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 542 4685 543 4686 [[package]] 544 4687 name = "windows_x86_64_msvc" 4688 + version = "0.42.2" 4689 + source = "registry+https://github.com/rust-lang/crates.io-index" 4690 + checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" 4691 + 4692 + [[package]] 4693 + name = "windows_x86_64_msvc" 4694 + version = "0.48.5" 4695 + source = "registry+https://github.com/rust-lang/crates.io-index" 4696 + checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 4697 + 4698 + [[package]] 4699 + name = "windows_x86_64_msvc" 545 4700 version = "0.52.6" 546 4701 source = "registry+https://github.com/rust-lang/crates.io-index" 547 4702 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 4703 + 4704 + [[package]] 4705 + name = "winit" 4706 + version = "0.30.5" 4707 + source = "registry+https://github.com/rust-lang/crates.io-index" 4708 + checksum = "0be9e76a1f1077e04a411f0b989cbd3c93339e1771cb41e71ac4aee95bfd2c67" 4709 + dependencies = [ 4710 + "ahash", 4711 + "android-activity", 4712 + "atomic-waker", 4713 + "bitflags 2.6.0", 4714 + "block2", 4715 + "bytemuck", 4716 + "calloop", 4717 + "cfg_aliases 0.2.1", 4718 + "concurrent-queue", 4719 + "core-foundation 0.9.4", 4720 + "core-graphics", 4721 + "cursor-icon", 4722 + "dpi", 4723 + "js-sys", 4724 + "libc", 4725 + "memmap2", 4726 + "ndk", 4727 + "objc2", 4728 + "objc2-app-kit", 4729 + "objc2-foundation", 4730 + "objc2-ui-kit", 4731 + "orbclient", 4732 + "percent-encoding", 4733 + "pin-project", 4734 + "raw-window-handle", 4735 + "redox_syscall 0.4.1", 4736 + "rustix 0.38.37", 4737 + "sctk-adwaita", 4738 + "smithay-client-toolkit", 4739 + "smol_str", 4740 + "tracing", 4741 + "unicode-segmentation", 4742 + "wasm-bindgen", 4743 + "wasm-bindgen-futures", 4744 + "wayland-backend", 4745 + "wayland-client", 4746 + "wayland-protocols", 4747 + "wayland-protocols-plasma", 4748 + "web-sys", 4749 + "web-time", 4750 + "windows-sys 0.52.0", 4751 + "x11-dl", 4752 + "x11rb", 4753 + "xkbcommon-dl", 4754 + ] 548 4755 549 4756 [[package]] 550 4757 name = "winnow" 551 - version = "0.6.18" 4758 + version = "0.5.40" 552 4759 source = "registry+https://github.com/rust-lang/crates.io-index" 553 - checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" 4760 + checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" 554 4761 dependencies = [ 555 4762 "memchr", 556 4763 ] 4764 + 4765 + [[package]] 4766 + name = "winnow" 4767 + version = "0.6.20" 4768 + source = "registry+https://github.com/rust-lang/crates.io-index" 4769 + checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" 4770 + dependencies = [ 4771 + "memchr", 4772 + ] 4773 + 4774 + [[package]] 4775 + name = "x11-dl" 4776 + version = "2.21.0" 4777 + source = "registry+https://github.com/rust-lang/crates.io-index" 4778 + checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" 4779 + dependencies = [ 4780 + "libc", 4781 + "once_cell", 4782 + "pkg-config", 4783 + ] 4784 + 4785 + [[package]] 4786 + name = "x11rb" 4787 + version = "0.13.1" 4788 + source = "registry+https://github.com/rust-lang/crates.io-index" 4789 + checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" 4790 + dependencies = [ 4791 + "as-raw-xcb-connection", 4792 + "gethostname", 4793 + "libc", 4794 + "libloading", 4795 + "once_cell", 4796 + "rustix 0.38.37", 4797 + "x11rb-protocol", 4798 + ] 4799 + 4800 + [[package]] 4801 + name = "x11rb-protocol" 4802 + version = "0.13.1" 4803 + source = "registry+https://github.com/rust-lang/crates.io-index" 4804 + checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" 4805 + 4806 + [[package]] 4807 + name = "xcursor" 4808 + version = "0.3.8" 4809 + source = "registry+https://github.com/rust-lang/crates.io-index" 4810 + checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61" 4811 + 4812 + [[package]] 4813 + name = "xdg-home" 4814 + version = "1.3.0" 4815 + source = "registry+https://github.com/rust-lang/crates.io-index" 4816 + checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" 4817 + dependencies = [ 4818 + "libc", 4819 + "windows-sys 0.59.0", 4820 + ] 4821 + 4822 + [[package]] 4823 + name = "xkbcommon-dl" 4824 + version = "0.4.2" 4825 + source = "registry+https://github.com/rust-lang/crates.io-index" 4826 + checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" 4827 + dependencies = [ 4828 + "bitflags 2.6.0", 4829 + "dlib", 4830 + "log", 4831 + "once_cell", 4832 + "xkeysym", 4833 + ] 4834 + 4835 + [[package]] 4836 + name = "xkeysym" 4837 + version = "0.2.1" 4838 + source = "registry+https://github.com/rust-lang/crates.io-index" 4839 + checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" 4840 + 4841 + [[package]] 4842 + name = "xml-rs" 4843 + version = "0.8.22" 4844 + source = "registry+https://github.com/rust-lang/crates.io-index" 4845 + checksum = "af4e2e2f7cba5a093896c1e150fbfe177d1883e7448200efb81d40b9d339ef26" 4846 + 4847 + [[package]] 4848 + name = "zbus" 4849 + version = "3.15.2" 4850 + source = "registry+https://github.com/rust-lang/crates.io-index" 4851 + checksum = "675d170b632a6ad49804c8cf2105d7c31eddd3312555cffd4b740e08e97c25e6" 4852 + dependencies = [ 4853 + "async-broadcast", 4854 + "async-executor", 4855 + "async-fs", 4856 + "async-io 1.13.0", 4857 + "async-lock 2.8.0", 4858 + "async-process", 4859 + "async-recursion", 4860 + "async-task", 4861 + "async-trait", 4862 + "blocking", 4863 + "byteorder", 4864 + "derivative", 4865 + "enumflags2", 4866 + "event-listener 2.5.3", 4867 + "futures-core", 4868 + "futures-sink", 4869 + "futures-util", 4870 + "hex", 4871 + "nix", 4872 + "once_cell", 4873 + "ordered-stream", 4874 + "rand", 4875 + "serde", 4876 + "serde_repr", 4877 + "sha1", 4878 + "static_assertions", 4879 + "tracing", 4880 + "uds_windows", 4881 + "winapi", 4882 + "xdg-home", 4883 + "zbus_macros", 4884 + "zbus_names", 4885 + "zvariant", 4886 + ] 4887 + 4888 + [[package]] 4889 + name = "zbus_macros" 4890 + version = "3.15.2" 4891 + source = "registry+https://github.com/rust-lang/crates.io-index" 4892 + checksum = "7131497b0f887e8061b430c530240063d33bf9455fa34438f388a245da69e0a5" 4893 + dependencies = [ 4894 + "proc-macro-crate 1.3.1", 4895 + "proc-macro2", 4896 + "quote", 4897 + "regex", 4898 + "syn 1.0.109", 4899 + "zvariant_utils", 4900 + ] 4901 + 4902 + [[package]] 4903 + name = "zbus_names" 4904 + version = "2.6.1" 4905 + source = "registry+https://github.com/rust-lang/crates.io-index" 4906 + checksum = "437d738d3750bed6ca9b8d423ccc7a8eb284f6b1d6d4e225a0e4e6258d864c8d" 4907 + dependencies = [ 4908 + "serde", 4909 + "static_assertions", 4910 + "zvariant", 4911 + ] 4912 + 4913 + [[package]] 4914 + name = "zerocopy" 4915 + version = "0.7.35" 4916 + source = "registry+https://github.com/rust-lang/crates.io-index" 4917 + checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 4918 + dependencies = [ 4919 + "byteorder", 4920 + "zerocopy-derive", 4921 + ] 4922 + 4923 + [[package]] 4924 + name = "zerocopy-derive" 4925 + version = "0.7.35" 4926 + source = "registry+https://github.com/rust-lang/crates.io-index" 4927 + checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 4928 + dependencies = [ 4929 + "proc-macro2", 4930 + "quote", 4931 + "syn 2.0.79", 4932 + ] 4933 + 4934 + [[package]] 4935 + name = "zvariant" 4936 + version = "3.15.2" 4937 + source = "registry+https://github.com/rust-lang/crates.io-index" 4938 + checksum = "4eef2be88ba09b358d3b58aca6e41cd853631d44787f319a1383ca83424fb2db" 4939 + dependencies = [ 4940 + "byteorder", 4941 + "enumflags2", 4942 + "libc", 4943 + "serde", 4944 + "static_assertions", 4945 + "zvariant_derive", 4946 + ] 4947 + 4948 + [[package]] 4949 + name = "zvariant_derive" 4950 + version = "3.15.2" 4951 + source = "registry+https://github.com/rust-lang/crates.io-index" 4952 + checksum = "37c24dc0bed72f5f90d1f8bb5b07228cbf63b3c6e9f82d82559d4bae666e7ed9" 4953 + dependencies = [ 4954 + "proc-macro-crate 1.3.1", 4955 + "proc-macro2", 4956 + "quote", 4957 + "syn 1.0.109", 4958 + "zvariant_utils", 4959 + ] 4960 + 4961 + [[package]] 4962 + name = "zvariant_utils" 4963 + version = "1.0.1" 4964 + source = "registry+https://github.com/rust-lang/crates.io-index" 4965 + checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" 4966 + dependencies = [ 4967 + "proc-macro2", 4968 + "quote", 4969 + "syn 1.0.109", 4970 + ]
+25 -3
Cargo.toml
··· 1 1 [package] 2 - name = "noita-counter" 2 + name = "noita-utility-box" 3 3 version = "0.1.0" 4 4 edition = "2021" 5 5 6 6 [dependencies] 7 7 anyhow = "1" 8 - bytemuck = { version = "1", features = ["derive", "extern_crate_alloc"] } 9 8 clap = { version = "4", features = ["derive"] } 10 - process-memory = "0.5" 9 + color-eyre = "0.6" 10 + derive_more = { version = "1", default-features = false, features = [ 11 + "debug", 12 + "deref", 13 + "display", 14 + ] } 15 + eframe = { version = "0.29", features = ["persistence", "ron", "wgpu"] } 16 + egui_extras = "0.29" 17 + fastrand = "2" 18 + futures = "0.3" 19 + iced-x86 = "1" 20 + lazy-regex = "3.3.0" 21 + memchr = "2" 22 + obws = { version = "0.13", features = ["events"] } 23 + rayon = "1.10.0" 24 + read-process-memory = "0.1" 25 + serde = { version = "1", features = ["derive"] } 26 + smart-default = "0.7" 11 27 strfmt = "0.2" 28 + strum = { version = "0.26", features = ["derive"] } 12 29 sysinfo = "0.31" 30 + thiserror = "1" 31 + tokio = { version = "1", features = ["rt-multi-thread"] } 13 32 toml = "0.8" 33 + tracing = "0.1" 34 + tracing-subscriber = { version = "0.3", features = ["env-filter"] } 35 + zerocopy = { version = "0.7", default-features = false, features = ["alloc"] }
+121
flake.lock
··· 1 + { 2 + "nodes": { 3 + "fenix": { 4 + "inputs": { 5 + "nixpkgs": [ 6 + "nixpkgs" 7 + ], 8 + "rust-analyzer-src": "rust-analyzer-src" 9 + }, 10 + "locked": { 11 + "lastModified": 1728455642, 12 + "narHash": "sha256-abYGwrL6ak5sBRqwPh+V3CPJ6Pa89p378t51b7BO1lE=", 13 + "owner": "nix-community", 14 + "repo": "fenix", 15 + "rev": "3b47535a5c782e4f4ad59cd4bdb23636b6926e03", 16 + "type": "github" 17 + }, 18 + "original": { 19 + "owner": "nix-community", 20 + "repo": "fenix", 21 + "type": "github" 22 + } 23 + }, 24 + "flake-utils": { 25 + "inputs": { 26 + "systems": "systems" 27 + }, 28 + "locked": { 29 + "lastModified": 1726560853, 30 + "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", 31 + "owner": "numtide", 32 + "repo": "flake-utils", 33 + "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", 34 + "type": "github" 35 + }, 36 + "original": { 37 + "owner": "numtide", 38 + "repo": "flake-utils", 39 + "type": "github" 40 + } 41 + }, 42 + "naersk": { 43 + "inputs": { 44 + "nixpkgs": [ 45 + "nixpkgs" 46 + ] 47 + }, 48 + "locked": { 49 + "lastModified": 1721727458, 50 + "narHash": "sha256-r/xppY958gmZ4oTfLiHN0ZGuQ+RSTijDblVgVLFi1mw=", 51 + "owner": "nix-community", 52 + "repo": "naersk", 53 + "rev": "3fb418eaf352498f6b6c30592e3beb63df42ef11", 54 + "type": "github" 55 + }, 56 + "original": { 57 + "owner": "nix-community", 58 + "repo": "naersk", 59 + "type": "github" 60 + } 61 + }, 62 + "nixpkgs": { 63 + "locked": { 64 + "lastModified": 1728409405, 65 + "narHash": "sha256-kk530XBUGDpt0DQbyUb3yDpSddPqF9PA5KTo/nsmmg0=", 66 + "owner": "nixos", 67 + "repo": "nixpkgs", 68 + "rev": "1366d1af8f58325602280e43ed6233849fb92216", 69 + "type": "github" 70 + }, 71 + "original": { 72 + "owner": "nixos", 73 + "ref": "nixpkgs-unstable", 74 + "repo": "nixpkgs", 75 + "type": "github" 76 + } 77 + }, 78 + "root": { 79 + "inputs": { 80 + "fenix": "fenix", 81 + "flake-utils": "flake-utils", 82 + "naersk": "naersk", 83 + "nixpkgs": "nixpkgs" 84 + } 85 + }, 86 + "rust-analyzer-src": { 87 + "flake": false, 88 + "locked": { 89 + "lastModified": 1728386838, 90 + "narHash": "sha256-Lk64EoJkvp3WMGVJK3CR1TYcNghX0/BqHPLW5zdvmLE=", 91 + "owner": "rust-lang", 92 + "repo": "rust-analyzer", 93 + "rev": "efaf8bd5de34e2f47bd57425b83e0c7974902176", 94 + "type": "github" 95 + }, 96 + "original": { 97 + "owner": "rust-lang", 98 + "ref": "nightly", 99 + "repo": "rust-analyzer", 100 + "type": "github" 101 + } 102 + }, 103 + "systems": { 104 + "locked": { 105 + "lastModified": 1681028828, 106 + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 107 + "owner": "nix-systems", 108 + "repo": "default", 109 + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 110 + "type": "github" 111 + }, 112 + "original": { 113 + "owner": "nix-systems", 114 + "repo": "default", 115 + "type": "github" 116 + } 117 + } 118 + }, 119 + "root": "root", 120 + "version": 7 121 + }
+143
flake.nix
··· 1 + { 2 + description = "Noita Utility Box - a collection of memory-reading utilities for the game Noita"; 3 + 4 + # I don't understand like a quarter of this flake, haven't slept for the last 48 hours lol 5 + # Adapted from https://gitlab.com/mud-rs/milk/-/blob/56f03874c577261f2c520461aebddd47c649ea30/flake.nix 6 + # But suprisingly, it worked quickly, not complaining 7 + 8 + inputs = { 9 + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; 10 + flake-utils.url = "github:numtide/flake-utils"; 11 + naersk = { 12 + url = "github:nix-community/naersk"; 13 + inputs.nixpkgs.follows = "nixpkgs"; 14 + }; 15 + fenix = { 16 + url = "github:nix-community/fenix"; 17 + inputs.nixpkgs.follows = "nixpkgs"; 18 + }; 19 + }; 20 + 21 + nixConfig = { 22 + extra-substituters = [ "https://necauqua.cachix.org" ]; 23 + extra-trusted-public-keys = [ "necauqua.cachix.org-1:XG5McOG0XwQ9kayUuEiEn0cPoLAMvc2TVs3fXqv/7Uc=" ]; 24 + }; 25 + 26 + outputs = { self, nixpkgs, naersk, flake-utils, fenix }: 27 + let 28 + cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml); 29 + name = cargoToml.package.name; 30 + version = cargoToml.package.version; 31 + in 32 + flake-utils.lib.eachDefaultSystem 33 + (system: 34 + let 35 + pkgs = import nixpkgs { 36 + inherit system; 37 + overlays = [ fenix.overlays.default ]; 38 + }; 39 + lib = pkgs.lib; 40 + toolchain = with pkgs.fenix; 41 + combine [ 42 + (complete.withComponents [ 43 + "cargo" 44 + "clippy" 45 + "rust-src" 46 + "rustc" 47 + ]) 48 + targets.x86_64-unknown-linux-musl.latest.rust-std 49 + targets.x86_64-pc-windows-gnu.latest.rust-std 50 + ]; 51 + # Make naersk aware of the tool chain which is to be used. 52 + naersk-lib = naersk.lib.${system}.override { 53 + cargo = toolchain; 54 + rustc = toolchain; 55 + }; 56 + buildPackage = target: { nativeBuildInputs ? [ ], ... }@args: 57 + naersk-lib.buildPackage ( 58 + { 59 + inherit name version; 60 + src = ./.; 61 + doCheck = false; # a test or two that I left in there are *not* unit tests lol 62 + strictDeps = true; 63 + } 64 + // (lib.optionalAttrs (target != system) { 65 + CARGO_BUILD_TARGET = target; 66 + }) 67 + // args 68 + // { 69 + nativeBuildInputs = [ pkgs.fenix.complete.rustfmt-preview ] ++ nativeBuildInputs; 70 + } 71 + ); 72 + in 73 + rec { 74 + packages = { 75 + default = buildPackage system { 76 + # todo make sure this is less cringe 77 + nativeBuildInputs = [ pkgs.makeWrapper ]; 78 + postInstall = '' 79 + wrapProgram $out/bin/${name} \ 80 + --prefix LD_LIBRARY_PATH : ${with pkgs; lib.makeLibraryPath [ 81 + vulkan-loader 82 + libxkbcommon 83 + wayland 84 + 85 + # not sure those are exactly what's needed on X11 86 + xorg.libX11 87 + xorg.libXcursor 88 + xorg.libXi 89 + xorg.libXrandr 90 + ]} 91 + ''; 92 + }; 93 + x86_64-unknown-linux-musl = buildPackage "x86_64-unknown-linux-musl" { 94 + nativeBuildInputs = with pkgs; [ pkgsStatic.stdenv.cc ]; 95 + CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_RUSTFLAGS = "-C target-feature=+crt-static"; 96 + }; 97 + x86_64-pc-windows-gnu = buildPackage "x86_64-pc-windows-gnu" { 98 + 99 + # we can run tests with wine ig, cool 100 + # this needs some fixing tho 101 + # doCheck = system == "x86_64-linux"; 102 + # nativeBuildInputs = lib.optional doCheck pkgs.wineWowPackages.stable; 103 + # CARGO_TARGET_X86_64_PC_WINDOWS_GNU_RUNNER = pkgs.writeScript "wine-wrapper" '' 104 + # # Without this, wine will error out when attempting to create the 105 + # # prefix in the build's homeless shelter. 106 + # export WINEPREFIX="$(mktemp -d)" 107 + # exec wine64 $@ 108 + # ''; 109 + 110 + depsBuildBuild = with pkgs.pkgsCross.mingwW64; [ 111 + stdenv.cc 112 + windows.pthreads 113 + ]; 114 + }; 115 + }; 116 + 117 + apps.default = { 118 + type = "app"; 119 + program = "${packages.default}/bin/${name}"; 120 + }; 121 + 122 + devShells.default = pkgs.mkShell { 123 + inputsFrom = builtins.attrValues packages; 124 + nativeBuildInputs = with pkgs; [ 125 + rust-analyzer-nightly 126 + cargo-udeps 127 + cargo-nextest 128 + 129 + wineWowPackages.staging 130 + ]; 131 + 132 + RUST_BACKTRACE = "full"; 133 + RUST_LOG = "info,wgpu_core=warn,wgpu_hal=warn,zbus=warn,noita_utility_box=trace"; 134 + }; 135 + } 136 + ) // { 137 + # huh?. we doing hydra now? 138 + hydraJobs = { 139 + inherit (self.packages) x86_64-linux; 140 + }; 141 + }; 142 + } 143 +
+23 -4
readme.md
··· 1 - ## noita-counter 1 + ## noita-utility-box 2 2 3 - This is a little cheatengine-style memory reader that reads a few statistics 3 + This is a cheatengine-style memory reader that reads useful data 4 4 directly from a running instance of [Noita](https://noitagame.com). 5 5 6 - This is useful if you want **no** mods to be installed yet want to have an 7 - automatic counter for winstreaking, for example. 6 + This is useful if you want **no** mods to be installed yet want to do dome 7 + advanced stuff. 8 + 9 + ### Tools 10 + #### Orb Radar 11 + It has an orb radar tool which fully automatically finds 34th orb GTG locations 12 + for the running game and tracks player coords in realtime. 13 + 14 + #### Live Stats 15 + Automatically gets current death/win/streak/best-streak counts, formats them 16 + and sets an OBS text input (through obs-websocket which you can enable in OBS 17 + menus) 18 + 19 + #### .. more coming 20 + There are plans for more stuff to come, some low handing fruits like hitless 21 + checker, less low ones like modless streamer wands, I want to do a git backup 22 + manager, a mod update manager, etc etc 23 + 24 + ## License 25 + It's MIT, please have a copy of the LICENSE file in your derivatives so that my 26 + name is there lol
+161
src/app.rs
··· 1 + use std::sync::Arc; 2 + 3 + use eframe::{ 4 + egui::{self, Align, Layout, RichText, ViewportBuilder}, 5 + get_value, icon_data, set_value, NativeOptions, 6 + }; 7 + use noita_utility_box::noita::{Noita, Seed}; 8 + use serde::{Deserialize, Serialize}; 9 + use strum::{EnumIter, EnumMessage, IntoEnumIterator}; 10 + 11 + use crate::{ 12 + tools::{ 13 + address_maps::AddressMaps, live_stats::LiveStats, material_pipette::MaterialPipette, 14 + orb_radar::OrbRadar, process_panel::ProcessPanel, settings::Settings, 15 + }, 16 + util::persist, 17 + }; 18 + 19 + #[derive(Default)] 20 + pub struct AppState { 21 + pub current_tab: CurrentTab, 22 + 23 + pub settings: Settings, 24 + pub address_maps: AddressMaps, 25 + 26 + pub noita: Option<Noita>, 27 + pub seed: Option<Seed>, 28 + } 29 + 30 + persist!(AppState { 31 + current_tab: CurrentTab, 32 + settings: Settings, 33 + address_maps: AddressMaps, 34 + }); 35 + 36 + #[derive(Default, Serialize, Deserialize)] 37 + pub struct NoitaUtilityBox { 38 + show_process_panel: bool, 39 + 40 + process_panel: ProcessPanel, 41 + #[serde(skip)] 42 + orb_radar: OrbRadar, 43 + live_stats: LiveStats, 44 + material_pipette: MaterialPipette, 45 + state: AppState, 46 + 47 + #[cfg(debug_assertions)] 48 + #[serde(skip)] 49 + repaints: u64, 50 + } 51 + 52 + #[derive(Default, Debug, Clone, Copy, PartialEq, Serialize, Deserialize, EnumIter, EnumMessage)] 53 + pub enum CurrentTab { 54 + /// Orb Radar 55 + #[default] 56 + OrbRadar, 57 + /// Live Stats 58 + LiveStats, 59 + /// Material Pipette 60 + MaterialPipette, 61 + /// Address Maps 62 + AddressMaps, 63 + /// Settings 64 + Settings, 65 + } 66 + 67 + impl eframe::App for NoitaUtilityBox { 68 + fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { 69 + egui::TopBottomPanel::top("tabs").show(ctx, |ui| { 70 + ui.add_space(0.5); 71 + ui.horizontal_wrapped(|ui| { 72 + egui::widgets::global_theme_preference_switch(ui); 73 + ui.separator(); 74 + ui.toggle_value(&mut self.show_process_panel, "Process"); 75 + ui.separator(); 76 + 77 + // meh 78 + if self.state.current_tab == CurrentTab::MaterialPipette 79 + && !self.state.settings.pipette 80 + { 81 + self.state.current_tab = CurrentTab::AddressMaps; 82 + } 83 + 84 + for tab in CurrentTab::iter() { 85 + if tab == CurrentTab::MaterialPipette && !self.state.settings.pipette { 86 + continue; 87 + } 88 + let name = tab.get_documentation().unwrap_or_default(); 89 + ui.selectable_value(&mut self.state.current_tab, tab, name); 90 + } 91 + }); 92 + ui.add_space(0.5); 93 + }); 94 + 95 + // update noita regardless of if it's panel is open/visible 96 + self.process_panel.update(ctx, &mut self.state); 97 + egui::SidePanel::left("left").show_animated(ctx, self.show_process_panel, |ui| { 98 + self.process_panel.ui(ui, &mut self.state) 99 + }); 100 + 101 + egui::CentralPanel::default().show(ctx, |ui| { 102 + use CurrentTab as T; 103 + match self.state.current_tab { 104 + T::OrbRadar => self.orb_radar.ui(ui, &mut self.state), 105 + T::LiveStats => self.live_stats.ui(ui, &mut self.state), 106 + T::MaterialPipette => self.material_pipette.ui(ui, &mut self.state), 107 + T::AddressMaps => self.state.address_maps.ui(ui), 108 + T::Settings => self.state.settings.ui(ui), 109 + } 110 + 111 + #[cfg(debug_assertions)] 112 + { 113 + ui.with_layout(Layout::bottom_up(Align::RIGHT), |ui| { 114 + self.repaints += 1; 115 + ui.label(RichText::new(format!("Repaints: {}", self.repaints)).small()); 116 + 117 + let text = RichText::new("⚠ Debug build ⚠") 118 + .small() 119 + .color(ui.visuals().warn_fg_color); 120 + ui.label(text) 121 + // .on_hover_text(format!("Commit: {}", env!("JJ_COMMIT"))) 122 + .on_hover_text("Commit: todo extract build commit+branch"); 123 + }); 124 + } 125 + }); 126 + } 127 + 128 + fn save(&mut self, storage: &mut dyn eframe::Storage) { 129 + set_value(storage, eframe::APP_KEY, &self); 130 + } 131 + } 132 + 133 + impl NoitaUtilityBox { 134 + pub fn run() -> eframe::Result { 135 + let rt = tokio::runtime::Runtime::new().unwrap(); 136 + let _guard = rt.enter(); 137 + 138 + eframe::run_native( 139 + "noita-utility-box", 140 + NativeOptions { 141 + viewport: ViewportBuilder { 142 + title: Some("Noita Utility Box".into()), 143 + icon: Some(Arc::new( 144 + icon_data::from_png_bytes(include_bytes!("icon.png")).unwrap(), 145 + )), 146 + ..Default::default() 147 + }, 148 + ..Default::default() 149 + }, 150 + Box::new(|cc| { 151 + let app: Self = cc 152 + .storage 153 + .as_ref() 154 + .and_then(|s| get_value(*s, eframe::APP_KEY)) 155 + .unwrap_or_default(); 156 + 157 + Ok(Box::new(app)) 158 + }), 159 + ) 160 + } 161 + }
src/icon.png

This is a binary file and will not be displayed.

+3
src/lib.rs
··· 1 + pub mod memory; 2 + pub mod noita; 3 + pub mod util;
+20 -309
src/main.rs
··· 1 - use std::collections::HashMap; 2 - use std::marker::PhantomData; 3 - use std::mem::size_of; 4 - use std::path::{Path, PathBuf}; 5 - use std::sync::RwLock; 6 - use std::time::Duration; 7 - use std::{cmp::Ordering, fmt::Debug}; 1 + #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] 8 2 9 - use anyhow::{anyhow, bail, Context, Result}; 10 - use bytemuck::{AnyBitPattern, NoUninit}; 11 - use clap::Parser; 12 - use process_memory::{CopyAddress, Pid, ProcessHandle, TryIntoProcessHandle}; 13 - use strfmt::{FmtError, Format}; 14 - use sysinfo::ProcessesToUpdate; 3 + use anyhow::{anyhow, Result}; 4 + use app::NoitaUtilityBox; 5 + use tracing_subscriber::{fmt::format::FmtSpan, EnvFilter}; 15 6 16 - #[derive(AnyBitPattern, Clone, Copy)] 17 - #[repr(transparent)] 18 - struct RemotePtr<T> { 19 - addr: u32, 20 - _phantom: PhantomData<T>, 21 - } 7 + mod app; 8 + mod orb_searcher; 9 + mod tools; 10 + mod util; 22 11 23 - // The derive macro hates the generic, but it's typelevel only (phantom) 24 - // RemotePtr is just a u32 25 - unsafe impl<T: Copy + 'static> NoUninit for RemotePtr<T> {} 12 + fn main() -> Result<()> { 13 + color_eyre::install().unwrap(); 26 14 27 - impl<T> Debug for RemotePtr<T> { 28 - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 29 - write!(f, "0x{:x}", self.addr) 30 - } 31 - } 32 - 33 - impl<T> RemotePtr<T> { 34 - fn new(addr: u32) -> Self { 35 - Self { 36 - addr, 37 - _phantom: PhantomData, 38 - } 39 - } 40 - 41 - fn read(&self, handle: ProcessHandle) -> Result<T> 42 - where 43 - T: AnyBitPattern, 44 - { 45 - let mut buf = vec![0; size_of::<T>()]; 46 - 47 - handle.copy_address(self.addr as usize, &mut buf)?; 48 - 49 - Ok(bytemuck::pod_read_unaligned(&buf)) 50 - } 51 - } 52 - 53 - #[derive(AnyBitPattern, NoUninit, Clone, Copy)] 54 - #[repr(C)] 55 - struct StdString { 56 - buf: [u8; 16], 57 - len: u32, 58 - cap: u32, 59 - } 60 - 61 - static DEBUG_HANDLE: RwLock<Option<ProcessHandle>> = RwLock::new(None); 62 - 63 - impl StdString { 64 - fn read(&self, handle: ProcessHandle) -> Result<String> { 65 - if self.len <= 15 { 66 - let data = &self.buf[..self.len as usize]; 67 - return Ok(String::from_utf8(data.to_owned())?); 68 - } 69 - 70 - let ptr = u32::from_le_bytes(self.buf[..4].try_into().unwrap()); 71 - let mut buf = vec![0; self.len as usize]; 72 - 73 - handle.copy_address(ptr as usize, &mut buf)?; 74 - 75 - Ok(String::from_utf8(buf)?) 76 - } 77 - } 78 - 79 - impl Debug for StdString { 80 - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 81 - { 82 - let debug_handle = DEBUG_HANDLE.read().unwrap(); 83 - if let Some(handle) = *debug_handle { 84 - return self.read(handle).unwrap().fmt(f); 85 - } 86 - } 87 - if self.len <= 15 { 88 - let data = &self.buf[..self.len as usize]; 89 - 90 - return match std::str::from_utf8(data) { 91 - Ok(str) => write!(f, "inline:{str:?}"), 92 - Err(_) => write!(f, "inline:{data:?}"), 93 - }; 94 - } 95 - write!( 96 - f, 97 - "heap:0x{:x}", 98 - u32::from_le_bytes(self.buf[..4].try_into().unwrap()) 15 + tracing_subscriber::fmt() 16 + .with_env_filter( 17 + EnvFilter::builder().parse( 18 + std::env::var(EnvFilter::DEFAULT_ENV) 19 + .as_deref() 20 + .unwrap_or("info,wgpu_core=warn,wgpu_hal=warn,zbus=warn"), 21 + )?, 99 22 ) 100 - } 101 - } 102 - 103 - #[derive(Debug, AnyBitPattern, Clone, Copy)] 104 - #[repr(C)] 105 - struct StringIntMapNode { 106 - left: RemotePtr<StringIntMapNode>, 107 - parent: RemotePtr<StringIntMapNode>, 108 - right: RemotePtr<StringIntMapNode>, 109 - _meta: u32, // color+pad or smth 110 - key: StdString, 111 - value: u32, 112 - } 23 + .with_span_events(FmtSpan::CLOSE) 24 + .init(); 113 25 114 - impl RemotePtr<StringIntMapNode> { 115 - fn get(&self, handle: ProcessHandle, key: &str) -> Result<Option<u32>> { 116 - let map = self.read(handle)?; 117 - 118 - // The toplevel map node is special, map.parent is the actual root node 119 - // of the binary tree and map.left/map.right are first/last I think 120 - let mut node = map.parent.read(handle)?; 121 - 122 - // not a loop{} just in case idk 123 - for _ in 0..100 { 124 - let node_key = node.key.read(handle)?; 125 - 126 - let next = match key.cmp(&node_key) { 127 - Ordering::Less => node.left, 128 - Ordering::Greater => node.right, 129 - Ordering::Equal => return Ok(Some(node.value)), 130 - }; 131 - if next.addr == self.addr { 132 - return Ok(None); 133 - } 134 - node = next.read(handle)?; 135 - } 136 - Ok(None) 137 - } 138 - } 139 - 140 - #[derive(Debug, AnyBitPattern, Clone, Copy)] 141 - #[repr(C)] 142 - struct Entity { 143 - id: u32, 144 - _skip1: [u8; 16], 145 - name: StdString, 146 - _skip2: [u8; 4], 147 - tags_bitset: [u8; 32], 148 - pos_x: f32, 149 - pos_y: f32, 150 - // more skipped.. 151 - // it's ok cuz the entities are vec of pointers 152 - } 153 - 154 - #[derive(Debug, AnyBitPattern, Clone, Copy)] 155 - #[repr(C)] 156 - struct RemoteVec<T> { 157 - start: RemotePtr<T>, 158 - end: RemotePtr<T>, 159 - cap: RemotePtr<T>, 160 - } 161 - 162 - impl<T> RemoteVec<T> { 163 - fn read_all(&self, handle: ProcessHandle) -> Result<Vec<T>> 164 - where 165 - T: AnyBitPattern + NoUninit, 166 - { 167 - let byte_len = (self.end.addr - self.start.addr) as usize; 168 - 169 - let mut buf = vec![0; byte_len]; 170 - 171 - handle.copy_address(self.start.addr as usize, &mut buf)?; 172 - 173 - Ok(bytemuck::allocation::pod_collect_to_vec(&buf)) 174 - } 175 - } 176 - 177 - #[derive(Debug, AnyBitPattern, Clone, Copy)] 178 - #[repr(C)] 179 - struct EntityManager { 180 - _ignoring1: [u8; 20], 181 - entities: RemoteVec<RemotePtr<Entity>>, 182 - } 183 - 184 - #[derive(clap::Parser)] 185 - struct Args { 186 - /// Format of the output 187 - #[arg( 188 - default_value = "Deaths: {deaths}\nWins: {wins}\nStreak: {streak}\nRecord: {streak-pb}" 189 - )] 190 - format: String, 191 - /// Show player position 192 - #[arg(long, conflicts_with = "format")] 193 - track_player: bool, 194 - /// Do not look for noita.exe process and use the given pid instead 195 - #[arg(long)] 196 - pid: Option<u32>, 197 - /// Use a custom address map read from a given file. When no argument is given prints the default one 198 - #[arg(long)] 199 - address_map: Option<Option<PathBuf>>, 200 - } 201 - 202 - fn read_address_map(path: &Path) -> Result<toml::Table> { 203 - Ok(toml::from_str(&std::fs::read_to_string(path)?)?) 204 - } 205 - 206 - fn ptr<T>(address_map: &toml::Table, name: &str) -> Result<Option<RemotePtr<T>>> { 207 - address_map 208 - .get(name) 209 - .map(|v| match v { 210 - toml::Value::Integer(addr) => Ok(RemotePtr::<T>::new(*addr as u32)), 211 - _ => Err(anyhow!("Invalid address map: `{name}` is not a number")), 212 - }) 213 - .transpose() 214 - } 215 - 216 - fn main() -> Result<()> { 217 - let args = Args::parse(); 218 - 219 - let address_map = include_str!("address-map.toml"); 220 - let mut address_map = match args.address_map { 221 - Some(None) => { 222 - println!("{address_map}"); 223 - return Ok(()); 224 - } 225 - Some(Some(custom)) => read_address_map(&custom).context("Reading custom address map")?, 226 - None => toml::from_str(address_map)?, 227 - }; 228 - 229 - let noita_pid = match args.pid { 230 - Some(pid) => pid, 231 - None => { 232 - let mut system = sysinfo::System::new(); 233 - system.refresh_processes(ProcessesToUpdate::All); 234 - 235 - let mut processes = system.processes_by_exact_name("noita.exe".as_ref()); 236 - 237 - processes 238 - .find(|p| p.thread_kind().is_none()) 239 - .map(|p| p.pid()) 240 - .context("Noita process not found")? 241 - .as_u32() 242 - } 243 - } as Pid; 244 - 245 - let handle = noita_pid.try_into_process_handle()?; 246 - *DEBUG_HANDLE.write().unwrap() = Some(handle); 247 - 248 - if args.track_player { 249 - let Some(entity_manager) = ptr::<RemotePtr<EntityManager>>(&address_map, "entity-manager")? 250 - else { 251 - bail!("entity-manager address location is unknown"); 252 - }; 253 - 254 - let entity_manager = entity_manager.read(handle)?.read(handle)?; 255 - let entities = entity_manager.entities.read_all(handle)?; 256 - 257 - let mut player_entity = None; 258 - for entity_ref in entities { 259 - if entity_ref.addr == 0 { 260 - continue; 261 - } 262 - let entity = entity_ref.read(handle)?; 263 - if entity.name.read(handle)? == "DEBUG_NAME:player" { 264 - player_entity = Some(entity_ref); 265 - break; 266 - } 267 - } 268 - 269 - let Some(player) = player_entity else { 270 - bail!("player entity not found") 271 - }; 272 - 273 - eprintln!(); 274 - loop { 275 - let player = player.read(handle)?; 276 - eprintln!( 277 - "\x1b[F\x1b[Kplayer pos: {:.02} {:.02}", 278 - player.pos_x, player.pos_y 279 - ); 280 - std::thread::sleep(Duration::from_millis(30)); 281 - } 282 - } 283 - 284 - let u32s = match address_map.remove("u32") { 285 - Some(toml::Value::Table(u32s)) => u32s, 286 - Some(_) => bail!("Invalid address map: `u32` is not a table"), 287 - None => toml::Table::new(), 288 - }; 289 - 290 - let mut data = HashMap::new(); 291 - for (k, v) in u32s { 292 - let toml::Value::Integer(addr) = v else { 293 - bail!("Invalid address map: `u32.{k}` is not a number") 294 - }; 295 - let ptr = RemotePtr::<u32>::new(addr as u32); 296 - 297 - data.insert(k, ptr.read(handle)?); 298 - } 299 - 300 - if let Some(stats) = ptr::<RemotePtr<StringIntMapNode>>(&address_map, "stats-map")? { 301 - let stats = stats.read(handle)?; 302 - data.insert( 303 - "wins".to_owned(), 304 - stats.get(handle, "progress_ending0")?.unwrap_or_default() 305 - + stats.get(handle, "progress_ending1")?.unwrap_or_default(), 306 - ); 307 - } 308 - 309 - let formatted = args.format.format(&data).map_err(|e| match e { 310 - FmtError::Invalid(msg) | FmtError::KeyError(msg) | FmtError::TypeError(msg) => { 311 - anyhow!("Result format error: {msg}") 312 - } 313 - })?; 314 - 315 - println!("{formatted}"); 26 + NoitaUtilityBox::run().map_err(|e| anyhow!("{e:#}"))?; 316 27 317 28 Ok(()) 318 29 }
+289
src/memory/exe_image.rs
··· 1 + use std::{ffi::CStr, io, ops::Range}; 2 + 3 + use iced_x86::{Code, Decoder, DecoderOptions, Instruction}; 4 + use memchr::memmem; 5 + use thiserror::Error; 6 + use zerocopy::{AsBytes, FromBytes, FromZeroes}; 7 + 8 + use crate::memory::{Ibo, MemoryStorage, ProcessRef}; 9 + 10 + #[derive(Error, Debug)] 11 + pub enum ReadImageError { 12 + #[error("No MZ header, not win32")] 13 + InvalidMzHeader, 14 + #[error("Invalid PE header")] 15 + InvalidPeHeader, 16 + #[error("Unexpected PE Optional Header size: {0}")] 17 + UnexpectedOptionalHeaderSize(u16), 18 + #[error("Bad .text range in header {0:?}")] 19 + BadCodeRange(Range<usize>), 20 + #[error("Bad .rdata range in header {0:?}")] 21 + BadDataRange(Range<usize>), 22 + #[error("Export directory RVA is not present")] 23 + NoExportDirectory, 24 + #[error("Failed to read export exe name")] 25 + BadExportName, 26 + #[error(transparent)] 27 + Io(#[from] std::io::Error), 28 + } 29 + 30 + #[derive(AsBytes, FromBytes, FromZeroes, Debug)] 31 + #[repr(C)] 32 + struct DosHeaderData { 33 + magic: [u8; 2], 34 + _skip: [u8; 0x3a], 35 + e_lfanew: Ibo<PeHeaderData>, 36 + } 37 + 38 + #[derive(AsBytes, FromBytes, FromZeroes, Debug)] 39 + #[repr(C)] 40 + struct PeHeaderData { 41 + magic: [u8; 4], 42 + machine: u16, 43 + number_of_sections: u16, 44 + time_date_stamp: u32, 45 + pointer_to_symbol_table: u32, 46 + number_of_symbols: u32, 47 + size_of_optional_header: u16, 48 + characteristics: u16, 49 + 50 + // optional header 51 + magic_and_linker_version: u32, 52 + size_of_code: u32, 53 + size_of_initialized_data: u32, 54 + size_of_uninitialized_data: u32, 55 + address_of_entry_point: u32, 56 + base_of_code: u32, 57 + base_of_data: u32, 58 + image_base: u32, 59 + _skip: [u8; 0x18], 60 + size_of_image: u32, 61 + _skip2: [u8; 0x20], 62 + num_of_rvas: u32, 63 + 64 + // we're only interested in the export directory, which is first 65 + export_directory_addr: Ibo<ExportDirectoryData>, 66 + export_directory_size: u32, 67 + } 68 + 69 + #[derive(AsBytes, FromBytes, FromZeroes, Debug)] 70 + #[repr(C)] 71 + struct ExportDirectoryData { 72 + _skip: [u8; 0x0c], 73 + name: Ibo<[u8; 256]>, // eh, cstrings 74 + } 75 + 76 + #[derive(Debug, Clone)] 77 + pub struct PeHeader { 78 + timestamp: u32, 79 + export_name: Vec<u8>, 80 + text: Range<usize>, 81 + rdata: Range<usize>, 82 + image_base: u32, 83 + size_of_image: u32, 84 + } 85 + 86 + impl PeHeader { 87 + pub fn timestamp(&self) -> u32 { 88 + self.timestamp 89 + } 90 + 91 + pub fn export_name(&self) -> &[u8] { 92 + &self.export_name 93 + } 94 + 95 + pub fn read(proc: &ProcessRef) -> Result<Self, ReadImageError> { 96 + let dos_header = Ibo::<DosHeaderData>::of(0x0).read(proc)?; 97 + if dos_header.magic != *b"MZ" { 98 + return Err(ReadImageError::InvalidMzHeader); 99 + } 100 + 101 + let pe = dos_header.e_lfanew.read(proc)?; 102 + if pe.magic != *b"PE\0\0" { 103 + return Err(ReadImageError::InvalidPeHeader); 104 + } 105 + 106 + if pe.size_of_optional_header != 0xe0 { 107 + return Err(ReadImageError::UnexpectedOptionalHeaderSize( 108 + pe.size_of_optional_header, 109 + )); 110 + } 111 + 112 + let base_of_code = pe.base_of_code as usize; 113 + let size_of_code = pe.size_of_code as usize; 114 + let text = base_of_code..base_of_code + size_of_code; 115 + 116 + let base_of_data = pe.base_of_data as usize; 117 + let size_of_data = pe.size_of_initialized_data as usize; 118 + let rdata = base_of_data..base_of_data + size_of_data; 119 + 120 + let size_of_image = pe.size_of_image as usize; 121 + 122 + if text.start > size_of_image || text.end > size_of_image { 123 + return Err(ReadImageError::BadCodeRange(text)); 124 + } 125 + if rdata.start > size_of_image || rdata.end > size_of_image { 126 + return Err(ReadImageError::BadDataRange(rdata)); 127 + } 128 + 129 + if pe.num_of_rvas < 1 || pe.export_directory_size < size_of::<ExportDirectoryData>() as u32 130 + { 131 + return Err(ReadImageError::NoExportDirectory); 132 + } 133 + 134 + let export_dir = pe.export_directory_addr.read(proc)?; 135 + 136 + // If only there was some sort of `CString::from_prefix_before_first_nul`.. 137 + let export_name = export_dir 138 + .name 139 + .read(proc)? 140 + .split_inclusive(|b| *b == 0) 141 + .next() 142 + .ok_or(ReadImageError::BadExportName)? 143 + .to_owned(); 144 + 145 + Ok(Self { 146 + timestamp: pe.time_date_stamp, 147 + export_name, 148 + text, 149 + rdata, 150 + image_base: pe.image_base, 151 + size_of_image: pe.size_of_image, 152 + }) 153 + } 154 + 155 + /// This is relatively slow, as we read the entire executable (according to 156 + /// it's image size from the PE header) from the process memory 157 + pub fn read_image(self, proc: &ProcessRef) -> Result<ExeImage, io::Error> { 158 + let image = proc.read_multiple(self.image_base, self.size_of_image)?; 159 + 160 + let image = ExeImage { 161 + header: self, 162 + image, 163 + // cached_strings: HashMap::new(), 164 + // cached_string_pushes: HashMap::new(), 165 + }; 166 + 167 + // 168 + // The Aho-Corasick setup and search here takes a whopping ~1 second on 169 + // my machine; maybe I'm doing something horribly wrong (like having 170 + // two of them), but for now brute force wins 171 + // 172 + // Oh and the commented out code also doesn't work, probably some errors 173 + // in offsets that I never bothered to fix cuz it's so slow anyway 174 + // 175 + // Limiting the searches to .rdata/.text helped immensely though 176 + // 177 + 178 + // let mut cached_strings = HashMap::new(); 179 + // let mut cached_string_pushes = HashMap::new(); 180 + 181 + // let strings = [ 182 + // b"SetRandomSeed\0".as_ref(), 183 + // b"GamePrint\0", 184 + // b"AddFlagPersistent\0", 185 + // b"EntityGetParent\0", 186 + // b"EntityHasTag\0", 187 + // b"EntityGetComponent\0", 188 + // b"progress_ending1\0", 189 + // b"Noita - Build ", 190 + // ]; 191 + // let aho_corasick = aho_corasick::AhoCorasick::new(strings).unwrap(); 192 + 193 + // let mut pushes = Vec::new(); 194 + 195 + // for m in aho_corasick.find_iter(image.rdata()) { 196 + // let addr = image.header.image_base + image.header.rdata.start as u32 + m.start() as u32; 197 + // cached_strings.insert(strings[m.pattern()].to_vec(), addr); 198 + 199 + // // skip last two lul 200 + // if m.pattern().as_u32() <= 5 { 201 + // let [a, b, c, d] = addr.to_le_bytes(); 202 + // pushes.push([0x68, a, b, c, d]); 203 + // } 204 + // } 205 + // let aho_corasick = aho_corasick::AhoCorasick::new(&pushes).unwrap(); 206 + // for m in aho_corasick.find_iter(image.text()) { 207 + // cached_string_pushes.insert(strings[m.pattern()].to_vec(), m.start()); 208 + // } 209 + 210 + // image.cached_strings = cached_strings; 211 + // image.cached_string_pushes = cached_string_pushes; 212 + 213 + Ok(image) 214 + } 215 + } 216 + 217 + #[derive(Debug)] 218 + pub struct ExeImage { 219 + header: PeHeader, 220 + image: Vec<u8>, 221 + // cached_strings: HashMap<Vec<u8>, u32>, 222 + // cached_string_pushes: HashMap<Vec<u8>, usize>, 223 + } 224 + 225 + impl ExeImage { 226 + pub fn text(&self) -> &[u8] { 227 + &self.image[self.header.text.clone()] 228 + } 229 + 230 + pub fn rdata(&self) -> &[u8] { 231 + &self.image[self.header.rdata.clone()] 232 + } 233 + 234 + pub fn header(&self) -> &PeHeader { 235 + &self.header 236 + } 237 + 238 + /// Find the program address of the given C string in rdata 239 + pub fn find_string(&self, needle: &CStr) -> Option<u32> { 240 + // if let Some(&res) = self.cached_strings.get(needle.to_bytes_with_nul()) { 241 + // tracing::debug!("Found string {needle:?} at 0x{res:x}"); 242 + // return Some(res); 243 + // } 244 + 245 + let res = memmem::find(self.rdata(), needle.to_bytes_with_nul()) 246 + .map(|pos| (pos + self.header.rdata.start + self.header.image_base as usize) as u32); 247 + if let Some(res) = res { 248 + tracing::debug!("Found string {needle:?} at 0x{res:x}"); 249 + } else { 250 + tracing::warn!("Did not find string {needle:?}"); 251 + } 252 + res 253 + } 254 + 255 + /// Returns position *relative to .text*, not to the image base 256 + pub fn find_push_str_pos(&self, needle: &CStr) -> Option<usize> { 257 + // if let Some(&res) = self.cached_string_pushes.get(needle.to_bytes_with_nul()) { 258 + // tracing::debug!("Found PUSH {needle:?} at offset 0x{res:x}",); 259 + // return Some(res); 260 + // } 261 + 262 + let [a, b, c, d] = self.find_string(needle)?.to_le_bytes(); 263 + let res = memmem::find(self.text(), &[0x68, a, b, c, d]); 264 + 265 + if let Some(res) = res { 266 + tracing::debug!("Found PUSH {needle:?} at offset 0x{res:x}",); 267 + } else { 268 + tracing::warn!("Did not find PUSH {needle:?}"); 269 + } 270 + res 271 + } 272 + 273 + /// Not guaranteed to end at the current function, as we only check for a few return opcodes and int3 274 + pub fn decode_fn(&self, addr: u32) -> impl Iterator<Item = Instruction> + '_ { 275 + Decoder::with_ip( 276 + 32, 277 + &self.text() 278 + [addr as usize - self.header.image_base as usize - self.header.text.start..], 279 + addr as u64, 280 + DecoderOptions::NONE, 281 + ) 282 + .into_iter() 283 + .take_while(|instr| { 284 + instr.code() != Code::Int3 285 + && instr.code() != Code::Retnd 286 + && instr.code() != Code::Retnd_imm16 287 + }) 288 + } 289 + }
+454
src/memory/mod.rs
··· 1 + use std::{ 2 + any::type_name, 3 + borrow::{Borrow, Cow}, 4 + cell::RefCell, 5 + cmp::Ordering, 6 + fmt::{self, Debug, Display}, 7 + io, 8 + }; 9 + 10 + use lazy_regex::regex_replace_all; 11 + use zerocopy::{AsBytes, FromBytes, FromZeroes}; 12 + 13 + mod process_ref; 14 + mod win32ptr; 15 + 16 + pub mod exe_image; 17 + 18 + pub use process_ref::{Pod, ProcessRef}; 19 + pub use win32ptr::{Ibo, Ptr, RawPtr}; 20 + 21 + #[derive(AsBytes, FromBytes, FromZeroes, Clone, Copy)] 22 + #[repr(C, packed)] 23 + pub struct PadBool<const PAD: usize = 0>(u8, [u8; PAD]); 24 + 25 + pub type ByteBool = PadBool<0>; 26 + 27 + impl<const PAD: usize> PadBool<PAD> { 28 + pub fn as_bool(&self) -> bool { 29 + self.0 != 0 30 + } 31 + } 32 + 33 + impl<const PAD: usize> Debug for PadBool<PAD> { 34 + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 35 + match self.0 { 36 + 0 => write!(f, "false"), 37 + 1 => write!(f, "true"), 38 + x => write!(f, "ByteBool({x}, {:?})", { self.1 }), 39 + } 40 + } 41 + } 42 + 43 + impl<const PAD: usize> Display for PadBool<PAD> { 44 + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 45 + Display::fmt(&self.as_bool(), f) 46 + } 47 + } 48 + 49 + impl<const PAD: usize> From<bool> for PadBool<PAD> { 50 + fn from(b: bool) -> Self { 51 + Self(b as u8, [0; PAD]) 52 + } 53 + } 54 + 55 + impl<const PAD: usize> From<PadBool<PAD>> for bool { 56 + fn from(b: PadBool<PAD>) -> Self { 57 + b.as_bool() 58 + } 59 + } 60 + 61 + // A hack to make zerocopy shut up 62 + #[derive(AsBytes, FromBytes, FromZeroes)] 63 + #[repr(transparent)] 64 + pub struct RealignedF64([u32; 2]); 65 + 66 + impl RealignedF64 { 67 + pub fn as_f64(&self) -> f64 { 68 + f64::from_bits(self.0[0] as u64 | (self.0[1] as u64) << 32) 69 + } 70 + } 71 + 72 + impl Debug for RealignedF64 { 73 + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 74 + Debug::fmt(&self.as_f64(), f) 75 + } 76 + } 77 + 78 + impl Display for RealignedF64 { 79 + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 80 + Display::fmt(&self.as_f64(), f) 81 + } 82 + } 83 + 84 + impl From<f64> for RealignedF64 { 85 + fn from(f: f64) -> Self { 86 + let bits = f.to_bits(); 87 + Self([bits as u32, (bits >> 32) as u32]) 88 + } 89 + } 90 + 91 + impl From<RealignedF64> for f64 { 92 + fn from(f: RealignedF64) -> Self { 93 + f.as_f64() 94 + } 95 + } 96 + 97 + pub trait MemoryStorage: Pod { 98 + type Value; 99 + 100 + fn read(&self, proc: &ProcessRef) -> io::Result<Self::Value>; 101 + 102 + fn bind(self, proc: ProcessRef) -> Remote<Self> 103 + where 104 + Self: Sized, 105 + { 106 + Remote::new(proc, self) 107 + } 108 + } 109 + 110 + // specialization (where the default is passthrough like this and only few 111 + // select types actually read foreign memory) would've been nice 112 + macro_rules! primitives { 113 + ($($t:ty),*) => { 114 + $( 115 + impl MemoryStorage for $t { 116 + type Value = Self; 117 + 118 + fn read(&self, _: &ProcessRef) -> io::Result<Self::Value> { 119 + Ok(*self) 120 + } 121 + } 122 + 123 + impl<const N: usize> MemoryStorage for [$t; N] { 124 + type Value = Self; 125 + 126 + fn read(&self, _: &ProcessRef) -> io::Result<Self::Value> { 127 + Ok(*self) 128 + } 129 + } 130 + )* 131 + }; 132 + } 133 + 134 + primitives!(u8, u16, u32, u64, i8, i16, i32, i64, f32, f64); 135 + 136 + #[derive(AsBytes, FromBytes, FromZeroes, Clone, Copy)] 137 + #[repr(C)] 138 + pub struct StdString { 139 + buf: [u8; 16], 140 + len: u32, 141 + cap: u32, 142 + } 143 + 144 + #[derive(Clone, Copy)] 145 + pub enum DecodedStdString<'a> { 146 + Inline(&'a [u8]), 147 + Heap(RawPtr), 148 + } 149 + 150 + impl StdString { 151 + pub fn len(&self) -> u32 { 152 + self.len 153 + } 154 + 155 + pub fn is_empty(&self) -> bool { 156 + self.len == 0 157 + } 158 + 159 + pub fn decode(&self) -> DecodedStdString { 160 + if let Some(inline) = self.buf[..15].get(..self.len as usize) { 161 + DecodedStdString::Inline(inline) 162 + } else { 163 + DecodedStdString::Heap(RawPtr::of(u32::read_from_prefix(&self.buf).unwrap())) 164 + } 165 + } 166 + } 167 + 168 + // thread local because on win Handle is not Sync 169 + // and a RefCell because it's not Copy 170 + thread_local! { 171 + static DEBUG_PROCESS: RefCell<Option<ProcessRef>> = const { RefCell::new(None) }; 172 + } 173 + pub fn set_debug_process(proc: ProcessRef) { 174 + DEBUG_PROCESS.set(Some(proc)); 175 + } 176 + 177 + impl Debug for StdString { 178 + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 179 + if let Some(s) = 180 + DEBUG_PROCESS.with_borrow(|proc| proc.as_ref().and_then(|h| self.read(h).ok())) 181 + { 182 + return Debug::fmt(&s, f); 183 + } 184 + 185 + match self.decode() { 186 + DecodedStdString::Inline(s) => match std::str::from_utf8(s) { 187 + Ok(str) => write!(f, "inline:{str:?}"), 188 + Err(_) => write!(f, "inline:{s:?}"), 189 + }, 190 + DecodedStdString::Heap(ptr) if self.len != 0 => write!(f, "heap:{ptr:?}"), 191 + _ => write!(f, "heap:\"\""), 192 + } 193 + } 194 + } 195 + 196 + impl MemoryStorage for StdString { 197 + type Value = String; 198 + 199 + fn read(&self, proc: &ProcessRef) -> io::Result<Self::Value> { 200 + match self.decode() { 201 + DecodedStdString::Inline(b) => std::str::from_utf8(b) 202 + .map(|s| s.to_owned()) // lifetimes are super fun and cool and dandy if you try to have Cow here lul 203 + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)), 204 + DecodedStdString::Heap(ptr) => { 205 + if self.len == 0 { 206 + return Ok(String::new()); 207 + } 208 + String::from_utf8(proc.read_multiple(ptr.addr(), self.len)?) 209 + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) 210 + } 211 + } 212 + } 213 + } 214 + 215 + #[derive(AsBytes, FromBytes, FromZeroes, Clone, Copy)] 216 + #[repr(transparent)] 217 + pub struct CString(RawPtr); 218 + 219 + impl CString { 220 + pub fn is_null(&self) -> bool { 221 + self.0.is_null() 222 + } 223 + } 224 + 225 + impl Debug for CString { 226 + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 227 + if let Some(s) = 228 + DEBUG_PROCESS.with_borrow(|proc| proc.as_ref().and_then(|h| self.read(h).ok())) 229 + { 230 + return Debug::fmt(&s, f); 231 + } 232 + 233 + f.debug_tuple("CString").field(&self.0).finish() 234 + } 235 + } 236 + 237 + impl From<CString> for RawPtr { 238 + fn from(c: CString) -> Self { 239 + c.0 240 + } 241 + } 242 + 243 + impl From<RawPtr> for CString { 244 + fn from(p: RawPtr) -> Self { 245 + Self(p) 246 + } 247 + } 248 + 249 + impl MemoryStorage for CString { 250 + type Value = String; 251 + 252 + fn read(&self, proc: &ProcessRef) -> io::Result<Self::Value> { 253 + let mut size = 64; // idk seems reasonable we'll very rarely hit the doubling even once 254 + 255 + while size != 2048 { 256 + let mut buf = self.0.read_multiple(proc, size)?; 257 + if let Some(len) = buf.iter().position(|&b| b == 0) { 258 + buf.truncate(len); 259 + return String::from_utf8(buf) 260 + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)); 261 + } 262 + size *= 2; 263 + } 264 + 265 + Err(io::Error::new( 266 + io::ErrorKind::InvalidData, 267 + format!("CString too long (at {:?})", self.0), 268 + )) 269 + } 270 + } 271 + 272 + #[derive(AsBytes, FromBytes, FromZeroes)] 273 + #[repr(C, packed)] 274 + pub struct StdVec<T> { 275 + start: Ptr<T>, 276 + end: Ptr<T>, 277 + cap: Ptr<T>, 278 + } 279 + 280 + impl<T> Clone for StdVec<T> { 281 + fn clone(&self) -> Self { 282 + *self 283 + } 284 + } 285 + impl<T> Copy for StdVec<T> {} 286 + 287 + impl<T> StdVec<T> { 288 + pub fn len(&self) -> u32 { 289 + self.end.addr().wrapping_sub(self.start.addr()) / size_of::<T>() as u32 290 + } 291 + 292 + pub fn is_empty(&self) -> bool { 293 + self.start.addr() == self.end.addr() 294 + } 295 + 296 + pub fn get(&self, index: u32) -> Option<Ptr<T>> { 297 + if index < self.len() { 298 + Some(Ptr::of(self.start.addr() + index * size_of::<T>() as u32)) 299 + } else { 300 + None 301 + } 302 + } 303 + 304 + pub fn truncated(&self, len: u32) -> StdVec<T> { 305 + if len >= self.len() { 306 + return *self; 307 + } 308 + StdVec { 309 + start: self.start, 310 + end: self.get(len).unwrap(), // just checked that len is always in bounds 311 + cap: self.cap, 312 + } 313 + } 314 + 315 + pub fn read_at(&self, index: u32, proc: &ProcessRef) -> io::Result<Option<T>> 316 + where 317 + T: Pod, 318 + { 319 + self.get(index).map(|p| p.read(proc)).transpose() 320 + } 321 + } 322 + 323 + impl<T> Debug for StdVec<T> 324 + where 325 + T: Pod + Debug, 326 + { 327 + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 328 + if let Some(s) = 329 + DEBUG_PROCESS.with_borrow(|proc| proc.as_ref().and_then(|h| self.read(h).ok())) 330 + { 331 + return Debug::fmt(&s, f); 332 + } 333 + write!(f, "StdVec[{} * {}]", self.len(), debug_type::<T>()) 334 + } 335 + } 336 + 337 + impl<T: MemoryStorage> StdVec<T> { 338 + pub fn read_storage(&self, proc: &ProcessRef) -> io::Result<Vec<T::Value>> { 339 + let len = self.len(); 340 + let mut vec = Vec::with_capacity(len as usize); 341 + for i in 0..len { 342 + vec.push(self.get(i).unwrap().read(proc)?.read(proc)?); 343 + } 344 + Ok(vec) 345 + } 346 + } 347 + 348 + impl<T: Pod> MemoryStorage for StdVec<T> { 349 + type Value = Vec<T>; 350 + 351 + fn read(&self, proc: &ProcessRef) -> io::Result<Self::Value> { 352 + proc.read_multiple(self.start.addr(), self.len()) 353 + } 354 + } 355 + 356 + #[derive(Debug, AsBytes, FromBytes, FromZeroes)] 357 + #[repr(C, packed)] 358 + pub struct StdMapNode<K, V> { 359 + left: Ptr<StdMapNode<K, V>>, 360 + parent: Ptr<StdMapNode<K, V>>, 361 + right: Ptr<StdMapNode<K, V>>, 362 + _meta: u32, // color+pad or smth 363 + key: K, 364 + value: V, 365 + } 366 + 367 + #[derive(AsBytes, FromBytes, FromZeroes, Clone, Copy)] 368 + #[repr(C, packed)] 369 + pub struct StdMap<K, V> { 370 + root: Ptr<StdMapNode<K, V>>, 371 + len: u32, 372 + } 373 + 374 + impl<K, V> StdMap<K, V> { 375 + pub fn len(&self) -> u32 { 376 + self.len 377 + } 378 + 379 + pub fn is_empty(&self) -> bool { 380 + self.len == 0 381 + } 382 + } 383 + 384 + impl<K, V> Debug for StdMap<K, V> { 385 + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 386 + // todo when we learn to iterate over the tree, impl complete debug 387 + write!( 388 + f, 389 + "StdMap[{} * ({} => {})]", 390 + self.len(), 391 + debug_type::<K>(), 392 + debug_type::<V>() 393 + ) 394 + } 395 + } 396 + 397 + // why did I have to overengineer this pos lolol 398 + // the whole MemoryStorage thing only exists because of this 399 + impl<K: MemoryStorage, V: MemoryStorage> StdMap<K, V> { 400 + pub fn get<Q>(&self, proc: &ProcessRef, key: &Q) -> io::Result<Option<V::Value>> 401 + where 402 + Q: Ord + ?Sized, 403 + K::Value: Borrow<Q>, 404 + { 405 + let root_ptr = self.root; 406 + let root = root_ptr.read(proc)?; 407 + 408 + // The root node is special, root.parent is the actual root node 409 + // of the binary tree and root.left/root.right are first/last I think 410 + let mut node = { root.parent }.read(proc)?; 411 + 412 + // not a loop{} just in case idk, nasa told me to do so 413 + for _ in 0..100 { 414 + let node_key = node.key; 415 + let node_key = node_key.read(proc)?; 416 + 417 + let next = match key.cmp(node_key.borrow()) { 418 + Ordering::Less => node.left, 419 + Ordering::Greater => node.right, 420 + Ordering::Equal => return Ok(Some({ node.value }.read(proc)?)), 421 + }; 422 + // the root pointer is used as the sentinel (and not just null?. huh) 423 + if next == root_ptr || next.is_null() { 424 + return Ok(None); 425 + } 426 + node = next.read(proc)?; 427 + } 428 + Ok(None) 429 + } 430 + } 431 + 432 + #[derive(Debug)] 433 + pub struct Remote<T> { 434 + proc: ProcessRef, 435 + thing: T, 436 + } 437 + 438 + impl<T> Remote<T> { 439 + pub const fn new(proc: ProcessRef, thing: T) -> Self { 440 + Self { proc, thing } 441 + } 442 + } 443 + 444 + impl<T: MemoryStorage> Remote<T> { 445 + pub fn read(&self) -> io::Result<T::Value> { 446 + self.thing.read(&self.proc) 447 + } 448 + } 449 + 450 + pub type RemotePtr<T> = Remote<Ptr<T>>; 451 + 452 + pub(crate) fn debug_type<T>() -> Cow<'static, str> { 453 + regex_replace_all!(r"(?:\w+::)+", type_name::<T>(), "") 454 + }
+65
src/memory/process_ref.rs
··· 1 + use std::{ 2 + fmt::{self, Debug}, 3 + io, 4 + }; 5 + 6 + use read_process_memory::{CopyAddress, Pid, ProcessHandle}; 7 + use zerocopy::{AsBytes, FromBytes}; 8 + 9 + /// A a bit of a nicer wrapper over `read_process_memory` crate API. 10 + /// Could reimplement it/swap it here. 11 + #[derive(Clone)] 12 + pub struct ProcessRef { 13 + /// pid is used for debug and equality 14 + pid: u32, 15 + /// On Linux handle is just the pid again, but on win it's a HANDLE 16 + /// The win handle is in an Arc so it's cheap to clone around 17 + handle: ProcessHandle, 18 + } 19 + 20 + impl PartialEq for ProcessRef { 21 + fn eq(&self, other: &Self) -> bool { 22 + self.pid == other.pid 23 + } 24 + } 25 + impl Eq for ProcessRef {} 26 + 27 + impl Debug for ProcessRef { 28 + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 29 + f.debug_tuple("ProcessRef").field(&self.pid).finish() 30 + } 31 + } 32 + 33 + impl ProcessRef { 34 + pub fn connect(pid: u32) -> io::Result<Self> { 35 + Ok(Self { 36 + pid, 37 + handle: (pid as Pid).try_into()?, 38 + }) 39 + } 40 + 41 + pub fn read_multiple<T: Pod>(&self, addr: u32, len: u32) -> io::Result<Vec<T>> { 42 + let mut v = T::new_vec_zeroed(len as usize); 43 + self.handle.copy_address(addr as usize, v.as_bytes_mut())?; 44 + Ok(v) 45 + } 46 + 47 + pub fn read<T: Pod>(&self, addr: u32) -> io::Result<T> { 48 + let mut t = T::new_zeroed(); 49 + self.handle.copy_address(addr as usize, t.as_bytes_mut())?; 50 + Ok(t) 51 + } 52 + } 53 + 54 + impl TryFrom<u32> for ProcessRef { 55 + type Error = io::Error; 56 + 57 + fn try_from(pid: u32) -> Result<Self, Self::Error> { 58 + Self::connect(pid) 59 + } 60 + } 61 + 62 + /// A shortcut for the zerocopy traits and sanity bounds 63 + pub trait Pod: AsBytes + FromBytes + Sized + 'static {} 64 + 65 + impl<T: AsBytes + FromBytes + Sized + 'static> Pod for T {}
+137
src/memory/win32ptr.rs
··· 1 + use core::fmt; 2 + use std::{fmt::Debug, io, marker::PhantomData}; 3 + 4 + use zerocopy::{AsBytes, FromBytes, FromZeroes}; 5 + 6 + use crate::memory::debug_type; 7 + 8 + use super::{process_ref::Pod, MemoryStorage, ProcessRef}; 9 + 10 + #[derive(AsBytes, FromBytes, FromZeroes, Clone, Copy, PartialEq, Eq)] 11 + #[repr(transparent)] 12 + pub struct RawPtr(u32); 13 + 14 + impl RawPtr { 15 + pub const fn of(addr: u32) -> Self { 16 + Self(addr) 17 + } 18 + 19 + pub const fn cast<T>(self) -> Ptr<T> { 20 + Ptr::of(self.0) 21 + } 22 + 23 + pub const fn addr(self) -> u32 { 24 + self.0 25 + } 26 + 27 + pub const fn is_null(self) -> bool { 28 + self.0 == 0 29 + } 30 + 31 + pub fn read_multiple<T: Pod>(self, proc: &ProcessRef, len: u32) -> io::Result<Vec<T>> { 32 + proc.read_multiple(self.0, len) 33 + } 34 + 35 + pub fn read_at<T: Pod>(self, offset: u32, proc: &ProcessRef) -> io::Result<T> { 36 + proc.read(self.0 + offset) 37 + } 38 + 39 + pub fn read<T: Pod>(self, proc: &ProcessRef) -> io::Result<T> { 40 + proc.read(self.0) 41 + } 42 + } 43 + 44 + impl Debug for RawPtr { 45 + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 46 + write!(f, "0x{:08x}", self.0) 47 + } 48 + } 49 + 50 + impl From<u32> for RawPtr { 51 + fn from(addr: u32) -> Self { 52 + Self::of(addr) 53 + } 54 + } 55 + 56 + #[derive(AsBytes, FromBytes, FromZeroes)] 57 + #[repr(transparent)] 58 + pub struct Ptr<T, const BASE: u32 = 0> { 59 + raw: RawPtr, 60 + _phantom: PhantomData<T>, 61 + } 62 + 63 + pub type Ibo<T> = Ptr<T, 0x0040_0000>; 64 + 65 + impl<T, const BASE: u32> Ptr<T, BASE> { 66 + pub const fn of(addr: u32) -> Self { 67 + Self { 68 + raw: RawPtr::of(addr), 69 + _phantom: PhantomData, 70 + } 71 + } 72 + } 73 + 74 + impl<T> Ptr<T> { 75 + pub const fn addr(self) -> u32 { 76 + self.raw.addr() 77 + } 78 + 79 + pub const fn is_null(&self) -> bool { 80 + self.addr() == 0 81 + } 82 + 83 + pub const fn raw(self) -> RawPtr { 84 + RawPtr::of(self.addr()) 85 + } 86 + } 87 + 88 + impl<T, const BASE: u32> Clone for Ptr<T, BASE> { 89 + fn clone(&self) -> Self { 90 + *self 91 + } 92 + } 93 + 94 + impl<T, const BASE: u32> Copy for Ptr<T, BASE> {} 95 + 96 + impl<T, const BASE: u32> PartialEq for Ptr<T, BASE> { 97 + fn eq(&self, other: &Self) -> bool { 98 + self.raw == other.raw 99 + } 100 + } 101 + 102 + impl<T, const BASE: u32> Eq for Ptr<T, BASE> {} 103 + 104 + impl<T, const BASE: u32> Debug for Ptr<T, BASE> { 105 + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 106 + if BASE == 0 { 107 + write!(f, "{:?} as {}", self.raw, debug_type::<T>()) 108 + } else { 109 + write!(f, "0x{BASE:08x}+{:?} as {}", self.raw, debug_type::<T>()) 110 + } 111 + } 112 + } 113 + 114 + impl<T, const BASE: u32> From<u32> for Ptr<T, BASE> { 115 + fn from(addr: u32) -> Self { 116 + Self::of(addr) 117 + } 118 + } 119 + 120 + impl<T: Pod, const BASE: u32> MemoryStorage for Ptr<T, BASE> { 121 + type Value = T; 122 + 123 + fn read(&self, proc: &ProcessRef) -> io::Result<Self::Value> { 124 + self.raw.read_at(BASE, proc) 125 + } 126 + } 127 + 128 + // Sadly, this is a specialization, for it to work we need a blanket noop impl 129 + // for MemoryStorage, which would conflict with this 130 + // 131 + // impl<T: MemoryStorage, const BASE: u32> MemoryStorage for Ptr<T, BASE> { 132 + // type Value = T::Value; 133 + 134 + // fn read(&self, proc: &ProcessRef) -> io::Result<Self::Value> { 135 + // self.raw.read_at::<T>(BASE, proc)?.read(proc) 136 + // } 137 + // }
+302
src/noita/discovery.rs
··· 1 + use std::{borrow::Cow, ffi::CStr}; 2 + 3 + use iced_x86::{Code, Register}; 4 + use memchr::memmem; 5 + 6 + use crate::memory::exe_image::ExeImage; 7 + 8 + use super::NoitaGlobals; 9 + 10 + /// Assuming Lua API functions are set up like this.. 11 + /// ```c 12 + /// lua_pushcclosure(L,function_pointer,0); 13 + /// lua_setfield(L,LUA_GLOBALSINDEX,"UniqueString"); 14 + /// ``` 15 + /// ..we look for the `PUSH imm32` of the unique string given as `name`, and 16 + /// then we look if there is a `PUSH imm32` at 8 bytes before that 17 + /// (`CALL EDI => lua_pushcclosure` and `PUSH EBX` being 3 bytes, and 18 + /// 5 bytes for the `PUSH imm32` image), and return it's argument. 19 + /// 20 + /// Note that this completely breaks (already) with noita_dev.exe lol 21 + fn find_lua_api_fn(image: &ExeImage, name: &CStr) -> Option<u32> { 22 + match image.text()[image.find_push_str_pos(name)? - 8..] { 23 + [0x68, a, b, c, d, ..] => { 24 + let addr = u32::from_le_bytes([a, b, c, d]); 25 + tracing::debug!("Found Lua API function {name:?} at 0x{addr:x}"); 26 + Some(addr) 27 + } 28 + _ => { 29 + tracing::warn!("Did not find Lua API function {name:?}"); 30 + None 31 + } 32 + } 33 + } 34 + 35 + /// We look for the `SetRandomSeed` Lua API function and then we look for 36 + /// the `mov eax, [addr]` and `add eax, [addr]` instructions, which 37 + /// correspond to WORLD_SEED + NEW_GAME_PLUS_COUNT being passed as a second 38 + /// parameter of a (SetRandomSeedImpl) function call. 39 + fn find_seed_pointers(image: &ExeImage) -> Option<(u32, u32)> { 40 + let mut state = None; 41 + for instr in image.decode_fn(find_lua_api_fn(image, c"SetRandomSeed")?) { 42 + state = match state { 43 + None if instr.code() == Code::Mov_EAX_moffs32 => Some(instr.memory_displacement32()), 44 + // allow the `add esp, 0x10` thing in between 45 + Some(addr) if instr.code() == Code::Add_rm32_imm8 => Some(addr), 46 + Some(addr) 47 + if instr.code() == Code::Add_r32_rm32 && instr.op0_register() == Register::EAX => 48 + { 49 + return Some((addr, instr.memory_displacement32())); 50 + } 51 + _ => None, 52 + }; 53 + } 54 + None 55 + } 56 + 57 + /// We look for the `GamePrint` Lua API function and then we look at the third 58 + /// `CALL rel32` instruction from the end, which is a call to `GetGameGlobal` 59 + /// (as I call it). 60 + /// 61 + /// Then we look for the `MOV moffs32, EAX` instruction which is the assignment 62 + /// to the pointer of the GameGlobal structure. 63 + fn find_game_global_pointer(image: &ExeImage) -> Option<u32> { 64 + let third_from_last_call_rel = image 65 + .decode_fn(find_lua_api_fn(image, c"GamePrint")?) 66 + .filter(|instr| instr.code() == Code::Call_rel32_32) 67 + .collect::<Vec<_>>() 68 + .into_iter() 69 + .rev() 70 + .nth(2)?; 71 + 72 + image 73 + .decode_fn(third_from_last_call_rel.near_branch32()) 74 + .find(|instr| { 75 + instr.code() == Code::Mov_moffs32_EAX && instr.segment_prefix() == Register::None 76 + }) 77 + .map(|instr| instr.memory_displacement32()) 78 + } 79 + 80 + /// We look for the `AddFlagPersistent` Lua API function and then we look 81 + /// for second-to-last `CALL rel32`, the last being some C++ exception 82 + /// thing, and the second-to-last being a call to `AddFlagPersistentImpl`, 83 + /// as I call it. 84 + /// 85 + /// Then inside of that we look for `MOV ECX imm32` which is specifically 86 + /// after `CALL rel32` which is after `MOV EDX, "progress_ending1"`. 87 + /// The call being to a string equality check and our MOV being an 88 + /// argument to a following call which is the global KEY_VALUE_STATS map 89 + /// pointer. 90 + fn find_stats_map_pointer(image: &ExeImage) -> Option<u32> { 91 + let mut before_last_call_rel = None; 92 + let mut last_call_rel = None; 93 + for instr in image.decode_fn(find_lua_api_fn(image, c"AddFlagPersistent")?) { 94 + if instr.code() == Code::Call_rel32_32 { 95 + before_last_call_rel = last_call_rel; 96 + last_call_rel = Some(instr.near_branch32()); 97 + } 98 + } 99 + 100 + let end1_addr = image.find_string(c"progress_ending1")?; 101 + 102 + enum State { 103 + Init, 104 + FoundProgressEnding1, 105 + FoundStreqCall, 106 + } 107 + let mut state = State::Init; 108 + 109 + for instr in image.decode_fn(before_last_call_rel?) { 110 + match state { 111 + State::Init 112 + if instr.code() == Code::Mov_r32_imm32 113 + && instr.op0_register() == Register::EDX 114 + && instr.immediate32() == end1_addr => 115 + { 116 + state = State::FoundProgressEnding1; 117 + } 118 + State::FoundProgressEnding1 if instr.code() == Code::Call_rel32_32 => { 119 + state = State::FoundStreqCall; 120 + } 121 + State::FoundStreqCall 122 + if instr.code() == Code::Mov_r32_imm32 && instr.op0_register() == Register::ECX => 123 + { 124 + return Some(instr.immediate32()); 125 + } 126 + _ => {} 127 + }; 128 + } 129 + None 130 + } 131 + 132 + /// We look for the `EntityGetParent` Lua API function and there we look 133 + /// for `MOV ECX, [addr]` which immediately follows a Lua call - that MOV 134 + /// happens to be setting up an argument to a following relative call that 135 + /// is the pointer to the entity manager global. 136 + fn find_entity_manager_pointer(image: &ExeImage) -> Option<u32> { 137 + let mut state = false; 138 + 139 + for instr in image.decode_fn(find_lua_api_fn(image, c"EntityGetParent")?) { 140 + state = match state { 141 + false if instr.code() == Code::Call_rm32 => true, 142 + true if instr.code() == Code::Mov_r32_rm32 && instr.op0_register() == Register::ECX => { 143 + return Some(instr.memory_displacement32()); 144 + } 145 + _ => false, 146 + }; 147 + } 148 + None 149 + } 150 + 151 + /// Look for the `EntityHasTag` Lua API function and then look for the 152 + /// second to last `CALL rel32` again, which is a call that accepts the 153 + /// entity tag manager global in ECX 154 + fn find_entity_tag_manager_pointer(image: &ExeImage) -> Option<u32> { 155 + let mut before_last_call_rel = None; 156 + let mut last_call_rel = None; 157 + 158 + let instrs = image 159 + .decode_fn(find_lua_api_fn(image, c"EntityHasTag")?) 160 + .enumerate() 161 + .map(|(i, instr)| { 162 + if instr.code() == Code::Call_rel32_32 { 163 + before_last_call_rel = last_call_rel; 164 + last_call_rel = Some(i); 165 + } 166 + instr 167 + }) 168 + .collect::<Vec<_>>(); 169 + 170 + instrs[..before_last_call_rel?] 171 + .iter() 172 + .rev() 173 + .find(|instr| instr.code() == Code::Mov_r32_rm32 && instr.op0_register() == Register::ECX) 174 + .map(|instr| instr.memory_displacement32()) 175 + } 176 + 177 + /// Look for the `EntityGetComponent` Lua API function and then look for 178 + /// a `CALL rel32` instruction that immediately follows a `PUSH EAX`, 179 + /// it's a call to `GetComponentTypeManager` (as I call it). 180 + /// 181 + /// Then we look for the `MOV EAX, imm32` instruction which the return 182 + /// of the component type manager global pointer. 183 + fn find_component_type_manager_pointer(image: &ExeImage) -> Option<u32> { 184 + let mut state = false; 185 + let mut found = None; 186 + 187 + for instr in image.decode_fn(find_lua_api_fn(image, c"EntityGetComponent")?) { 188 + state = match state { 189 + false if instr.code() == Code::Push_r32 && instr.op0_register() == Register::EAX => { 190 + true 191 + } 192 + true if instr.code() == Code::Call_rel32_32 => { 193 + found = Some(instr.near_branch32()); 194 + break; 195 + } 196 + _ => false, 197 + }; 198 + } 199 + 200 + image 201 + .decode_fn(found?) 202 + .find(|instr| instr.code() == Code::Mov_r32_imm32) 203 + .map(|instr| instr.immediate32()) 204 + } 205 + 206 + /// It's actually almost same as the PE timestamp I've been using, but 207 + /// they might have some more human-readable stuff here. 208 + pub fn find_noita_build(image: &ExeImage) -> Option<Cow<str>> { 209 + let pos = memmem::find(image.rdata(), b"Noita - Build ")?; 210 + 211 + // + 8 to skip the "Noita - " part 212 + let prefix = image.rdata()[pos + 8..].split(|b| *b == 0).next()?; 213 + Some(String::from_utf8_lossy(prefix)) 214 + } 215 + 216 + pub fn run(image: &ExeImage) -> NoitaGlobals { 217 + let mut g = NoitaGlobals::default(); 218 + 219 + let seed = find_seed_pointers(image); 220 + g.world_seed = seed.map(|(seed, _)| seed).map(|p| p.into()); 221 + g.ng_count = seed.map(|(_, ng)| ng).map(|p| p.into()); 222 + g.global_stats = find_stats_map_pointer(image).map(|p| (p - 0x18).into()); 223 + g.game_global = find_game_global_pointer(image).map(|p| p.into()); 224 + g.entity_manager = find_entity_manager_pointer(image).map(|p| p.into()); 225 + g.entity_tag_manager = find_entity_tag_manager_pointer(image).map(|p| p.into()); 226 + g.component_type_manager = find_component_type_manager_pointer(image).map(|p| p.into()); 227 + 228 + g 229 + } 230 + 231 + #[cfg(test)] 232 + mod tests { 233 + use crate::memory::exe_image::PeHeader; 234 + 235 + use super::*; 236 + 237 + use std::time::Instant; 238 + 239 + use sysinfo::ProcessesToUpdate; 240 + use tracing::level_filters::LevelFilter; 241 + use tracing_subscriber::EnvFilter; 242 + 243 + #[test] 244 + fn test() -> anyhow::Result<()> { 245 + tracing_subscriber::fmt() 246 + .with_env_filter( 247 + EnvFilter::builder() 248 + .with_default_directive(LevelFilter::DEBUG.into()) 249 + .from_env()?, 250 + ) 251 + .init(); 252 + 253 + let mut system = sysinfo::System::new(); 254 + system.refresh_processes(ProcessesToUpdate::All); 255 + 256 + let Some(noita_pid) = system 257 + .processes_by_exact_name("noita.exe".as_ref()) 258 + .find(|p| p.thread_kind().is_none()) 259 + else { 260 + eprintln!("Noita process not found"); 261 + return Ok(()); 262 + }; 263 + 264 + let proc = noita_pid.pid().as_u32().try_into()?; 265 + let header = PeHeader::read(&proc)?; 266 + if header.timestamp() != 0x66ba59d6 { 267 + eprintln!("Timestamp mismatch: 0x{:x}", header.timestamp()); 268 + return Ok(()); 269 + } 270 + 271 + let instant = Instant::now(); 272 + let image = header.read_image(&proc)?; 273 + println!("Image read in {:?}", instant.elapsed()); 274 + 275 + let instant = Instant::now(); 276 + let globals = run(&image); 277 + println!("Pointers found in {:?}", instant.elapsed()); 278 + 279 + println!("{globals:#?}"); 280 + 281 + // destructure so we know to update this when growing the list lol 282 + let NoitaGlobals { 283 + world_seed, 284 + ng_count, 285 + global_stats, 286 + game_global, 287 + entity_manager, 288 + entity_tag_manager, 289 + component_type_manager, 290 + } = NoitaGlobals::debug(); 291 + 292 + assert_eq!(globals.world_seed, world_seed); 293 + assert_eq!(globals.ng_count, ng_count); 294 + assert_eq!(globals.global_stats, global_stats); 295 + assert_eq!(globals.game_global, game_global); 296 + assert_eq!(globals.entity_manager, entity_manager); 297 + assert_eq!(globals.entity_tag_manager, entity_tag_manager); 298 + assert_eq!(globals.component_type_manager, component_type_manager); 299 + 300 + Ok(()) 301 + } 302 + }
+345
src/noita/mod.rs
··· 1 + use std::{collections::HashMap, io, marker::PhantomData}; 2 + 3 + use derive_more::{derive::Display, Debug}; 4 + use types::{ 5 + components::ComponentName, ComponentBuffer, ComponentTypeManager, Entity, EntityManager, 6 + GameGlobal, GlobalStats, TagManager, 7 + }; 8 + 9 + use crate::memory::{MemoryStorage, Pod, ProcessRef, Ptr}; 10 + 11 + pub mod discovery; 12 + pub mod rng; 13 + pub mod types; 14 + 15 + #[derive(Debug, Clone)] 16 + pub struct Noita { 17 + proc: ProcessRef, 18 + g: NoitaGlobals, 19 + entity_tag_cache: HashMap<String, u8>, 20 + no_player_not_polied: bool, 21 + 22 + materials: Vec<String>, 23 + material_ui_names: Vec<String>, 24 + } 25 + 26 + #[derive(Debug, Default, Clone)] 27 + pub struct NoitaGlobals { 28 + pub world_seed: Option<Ptr<u32>>, 29 + pub ng_count: Option<Ptr<u32>>, 30 + pub global_stats: Option<Ptr<GlobalStats>>, 31 + pub game_global: Option<Ptr<Ptr<GameGlobal>>>, 32 + pub entity_manager: Option<Ptr<Ptr<EntityManager>>>, 33 + pub entity_tag_manager: Option<Ptr<Ptr<TagManager>>>, 34 + pub component_type_manager: Option<Ptr<ComponentTypeManager>>, 35 + } 36 + 37 + impl NoitaGlobals { 38 + pub fn debug() -> Self { 39 + Self { 40 + world_seed: Some(Ptr::of(0x1202fe4)), 41 + ng_count: Some(Ptr::of(0x1203004)), 42 + global_stats: Some(Ptr::of(0x1206920)), 43 + game_global: Some(Ptr::of(0x0122172c)), 44 + entity_manager: Some(Ptr::of(0x1202b78)), 45 + entity_tag_manager: Some(Ptr::of(0x1204fbc)), 46 + component_type_manager: Some(Ptr::of(0x01221c08)), 47 + } 48 + } 49 + } 50 + 51 + macro_rules! not_found { 52 + ($($args:tt)*) => { 53 + || ::std::io::Error::new(::std::io::ErrorKind::NotFound, format!($($args)*)) 54 + }; 55 + } 56 + 57 + macro_rules! read_ptr { 58 + ($self:ident.$ident:ident) => { 59 + $self 60 + .g 61 + .$ident 62 + .ok_or_else(not_found!(concat!("No ", stringify!($ident), " pointer")))? 63 + .read(&$self.proc)? 64 + }; 65 + } 66 + 67 + pub trait TagRef { 68 + fn get_tag_index(&self, noita: &mut Noita) -> io::Result<Option<u8>>; 69 + } 70 + 71 + impl TagRef for str { 72 + fn get_tag_index(&self, noita: &mut Noita) -> io::Result<Option<u8>> { 73 + noita.get_entity_tag_index(self) 74 + } 75 + } 76 + 77 + impl TagRef for u8 { 78 + fn get_tag_index(&self, _: &mut Noita) -> io::Result<Option<u8>> { 79 + Ok(Some(*self)) 80 + } 81 + } 82 + 83 + impl TagRef for Option<u8> { 84 + fn get_tag_index(&self, _: &mut Noita) -> io::Result<Option<u8>> { 85 + Ok(*self) 86 + } 87 + } 88 + 89 + impl Noita { 90 + pub fn new(proc: ProcessRef, g: NoitaGlobals) -> Self { 91 + Self { 92 + proc, 93 + g, 94 + entity_tag_cache: HashMap::new(), 95 + no_player_not_polied: false, 96 + materials: Vec::new(), 97 + material_ui_names: Vec::new(), 98 + } 99 + } 100 + 101 + pub const fn proc(&self) -> &ProcessRef { 102 + &self.proc 103 + } 104 + 105 + pub fn read_seed(&self) -> io::Result<Option<Seed>> { 106 + let world_seed = read_ptr!(self.world_seed).read(&self.proc)?; 107 + if world_seed == 0 { 108 + return Ok(None); 109 + } 110 + Ok(Some(Seed { 111 + world_seed, 112 + ng_count: read_ptr!(self.ng_count).read(&self.proc)?, 113 + })) 114 + } 115 + 116 + pub fn read_stats(&self) -> io::Result<GlobalStats> { 117 + Ok(read_ptr!(self.global_stats)) 118 + } 119 + 120 + pub fn get_player(&mut self) -> io::Result<Option<(Entity, bool)>> { 121 + let Some(player_unit_idx) = self.get_entity_tag_index("player_unit")? else { 122 + // no player_unit means definitely no player 123 + return Ok(None); 124 + }; 125 + 126 + if let Some(player) = self.get_first_tagged_entity(player_unit_idx)? { 127 + self.no_player_not_polied = false; 128 + return Ok(Some((player, false))); 129 + } 130 + 131 + // avoid repeatedly trying to look up the polymorphed_player tag if it wasn't created yet 132 + if self.no_player_not_polied { 133 + return Ok(None); 134 + } 135 + 136 + let Some(polymorphed_player_idx) = self.get_entity_tag_index("polymorphed_player")? else { 137 + // no polymorphed_player means player was never polymorphed, 138 + // and without a player it means there's no player lol 139 + self.no_player_not_polied = true; 140 + return Ok(None); 141 + }; 142 + Ok(self 143 + .get_first_tagged_entity(polymorphed_player_idx)? 144 + .map(|p| (p, true))) 145 + } 146 + 147 + pub fn get_first_tagged_entity(&mut self, tag: impl TagRef) -> io::Result<Option<Entity>> { 148 + let entity_manager = read_ptr!(self.entity_manager).read(&self.proc)?; 149 + 150 + let Some(tag_idx) = tag.get_tag_index(self)? else { 151 + return Ok(None); 152 + }; 153 + let Some(bucket) = entity_manager.entity_buckets.get(tag_idx as u32) else { 154 + return Ok(None); 155 + }; 156 + let Some(entity) = bucket.read(&self.proc)?.get(0) else { 157 + return Ok(None); 158 + }; 159 + let entity = entity.read(&self.proc)?; 160 + if entity.is_null() { 161 + return Ok(None); 162 + } 163 + Ok(Some(entity.read(&self.proc)?)) 164 + } 165 + 166 + /// Can store the index and check entity bitset directly to avoid hashmap 167 + /// lookups 168 + pub fn get_entity_tag_index(&mut self, tag: &str) -> io::Result<Option<u8>> { 169 + if let Some(idx) = self.entity_tag_cache.get(tag) { 170 + return Ok(Some(*idx)); 171 + } 172 + 173 + let idx = read_ptr!(self.entity_tag_manager) 174 + .read(&self.proc)? 175 + .tag_indices 176 + .get(&self.proc, tag)?; 177 + 178 + if let Some(index) = idx { 179 + self.entity_tag_cache.insert(tag.to_string(), index); 180 + 181 + tracing::debug!("Found {tag} index: {index}"); 182 + } else { 183 + // this can spam when the tag was never touched yet and thus doesn't exist 184 + tracing::trace!("Did not find {tag} index"); 185 + } 186 + 187 + Ok(idx) 188 + } 189 + 190 + pub fn has_tag(&mut self, entity: &Entity, tag: impl TagRef) -> io::Result<bool> { 191 + Ok(entity.tags[tag.get_tag_index(self)?]) 192 + } 193 + 194 + pub fn materials(&mut self) -> io::Result<&[String]> { 195 + if !self.materials.is_empty() { 196 + return Ok(&self.materials); 197 + } 198 + 199 + let material_ptrs = read_ptr!(self.game_global) 200 + .read(&self.proc)? 201 + .cell_factory 202 + .read(&self.proc)? 203 + .materials 204 + .read(&self.proc)?; 205 + 206 + let mut materials = Vec::with_capacity(material_ptrs.len()); 207 + for ptr in material_ptrs { 208 + materials.push(ptr.read(&self.proc)?); 209 + } 210 + self.materials = materials; 211 + Ok(&self.materials) 212 + } 213 + 214 + pub fn get_material_name(&mut self, index: u32) -> io::Result<Option<String>> { 215 + Ok(self.materials()?.get(index as usize).cloned()) 216 + } 217 + 218 + pub fn get_material_ui_name(&mut self, index: u32) -> io::Result<Option<String>> { 219 + if !self.material_ui_names.is_empty() { 220 + return Ok(self.material_ui_names.get(index as usize).cloned()); 221 + } 222 + 223 + let cell_factory = read_ptr!(self.game_global) 224 + .read(&self.proc)? 225 + .cell_factory 226 + .read(&self.proc)?; 227 + let material_descs = cell_factory 228 + .material_descs_maybe 229 + .truncated(cell_factory.number_of_materials) 230 + .read(&self.proc)?; 231 + 232 + let mut material_ui_names = Vec::with_capacity(material_descs.len()); 233 + for desc in material_descs { 234 + material_ui_names.push(desc.ui_name.read(&self.proc)?); 235 + } 236 + self.material_ui_names = material_ui_names; 237 + Ok(self.material_ui_names.get(index as usize).cloned()) 238 + } 239 + 240 + pub fn component_store<T: ComponentName>(&self) -> io::Result<ComponentStore<T>> { 241 + let index = read_ptr!(self.component_type_manager) 242 + .component_indices 243 + .get(&self.proc, T::NAME)? 244 + .ok_or_else(not_found!("Component type index not found"))?; 245 + 246 + let buffer = read_ptr!(self.entity_manager) 247 + .read(&self.proc)? 248 + .component_buffers 249 + .get(index) 250 + .ok_or_else(not_found!( 251 + "Component buffer not found for index {index} ({})", 252 + T::NAME 253 + ))? 254 + .read(&self.proc)?; 255 + 256 + Ok(ComponentStore { 257 + proc: self.proc.clone(), 258 + buffer, 259 + _marker: PhantomData, 260 + }) 261 + } 262 + } 263 + 264 + #[derive(Display, Debug, Clone, Copy)] 265 + #[display("{world_seed}+{ng_count}")] 266 + pub struct Seed { 267 + pub world_seed: u32, 268 + pub ng_count: u32, 269 + } 270 + 271 + impl Seed { 272 + pub fn sum(&self) -> u32 { 273 + self.world_seed.wrapping_add(self.ng_count) 274 + } 275 + } 276 + 277 + #[derive(Debug)] 278 + pub struct ComponentStore<T> { 279 + proc: ProcessRef, 280 + buffer: Ptr<ComponentBuffer>, 281 + _marker: PhantomData<T>, 282 + } 283 + 284 + impl<T> ComponentStore<T> 285 + where 286 + T: ComponentName + Pod, 287 + { 288 + pub fn get(&self, entity: &Entity) -> io::Result<Option<T>> { 289 + let buffer = self.buffer.read(&self.proc)?; 290 + 291 + let idx = buffer 292 + .indices 293 + .get(entity.comp_idx) 294 + .map(|i| i.read(&self.proc)) 295 + .transpose()? 296 + .unwrap_or(buffer.default_index); 297 + 298 + let Some(ptr) = buffer.storage.get(idx.read(&self.proc)?) else { 299 + return Ok(None); 300 + }; 301 + 302 + let ptr = ptr.read(&self.proc)?; 303 + // not sure it could be null, but just in case 304 + if ptr.is_null() { 305 + return Ok(None); 306 + } 307 + Ok(Some(ptr.read(&self.proc)?)) 308 + } 309 + } 310 + 311 + #[cfg(test)] 312 + #[test] 313 + fn test() -> anyhow::Result<()> { 314 + use sysinfo::ProcessesToUpdate; 315 + use tracing::level_filters::LevelFilter; 316 + use tracing_subscriber::EnvFilter; 317 + 318 + tracing_subscriber::fmt() 319 + .with_env_filter( 320 + EnvFilter::builder() 321 + .with_default_directive(LevelFilter::DEBUG.into()) 322 + .from_env()?, 323 + ) 324 + .init(); 325 + 326 + let mut system = sysinfo::System::new(); 327 + system.refresh_processes(ProcessesToUpdate::All); 328 + 329 + let Some(noita_pid) = system 330 + .processes_by_exact_name("noita.exe".as_ref()) 331 + .find(|p| p.thread_kind().is_none()) 332 + else { 333 + eprintln!("Noita process not found"); 334 + return Ok(()); 335 + }; 336 + 337 + let proc = noita_pid.pid().as_u32().try_into()?; 338 + let noita = Noita::new(proc, NoitaGlobals::debug()); 339 + 340 + let stats = noita.read_stats()?; 341 + 342 + println!("{:#?}", stats); 343 + 344 + Ok(()) 345 + }
+92
src/noita/rng.rs
··· 1 + #[derive(Debug, Clone)] 2 + pub struct NoitaRng(i64); 3 + 4 + impl NoitaRng { 5 + /// The random function itself is super standard, the secret sauce was 6 + /// getting the state from the world seed and position 7 + pub fn random(&mut self) -> f64 { 8 + let hi = self.0 / 127773; 9 + let lo = self.0 - hi * 127773; 10 + self.0 = lo * 16807 - hi * 2836; 11 + if self.0 <= 0 { 12 + self.0 += 0x7fffffff; 13 + } 14 + self.0 as f64 * 4.656612875e-10 15 + } 16 + 17 + pub fn from_pos(seed_plus_ng: u32, x: f64, y: f64) -> Self { 18 + let xo = x + ((seed_plus_ng ^ 0x93262e6f) & 0xfff) as f64; 19 + let yo = y + (((seed_plus_ng ^ 0x93262e6f) >> 12) & 0xfff) as f64; 20 + 21 + let xi = to_int_kinda(xo * 134217727.0); 22 + let yi = to_int_kinda(if yo.abs() >= 102400.0 || xo.abs() <= 1.0 { 23 + yo * 134217727.0 24 + } else { 25 + yo * (yo * 3483.328 + xi as f64) 26 + }); 27 + 28 + let mixed = mix(xi as i32, yi as i32, seed_plus_ng); 29 + 30 + let mut state = (mixed as f64) / 4294967295.0 * 2147483639.0 + 1.0; 31 + if state >= 2147483647.0 { 32 + state *= 0.5; 33 + } 34 + 35 + let mut rng = Self(state as i64); 36 + rng.random(); 37 + 38 + for _ in 0..(seed_plus_ng & 3) { 39 + rng.random(); 40 + } 41 + rng 42 + } 43 + } 44 + 45 + // wrapping_sub soup 46 + fn mix(a: i32, b: i32, c: u32) -> u32 { 47 + let mut x = (a.wrapping_sub(b) as u32).wrapping_sub(c) ^ c >> 13; 48 + let mut y = (b as u32).wrapping_sub(x).wrapping_sub(c) ^ x << 8; 49 + let mut z = c.wrapping_sub(x).wrapping_sub(y) ^ y >> 13; 50 + x = x.wrapping_sub(y).wrapping_sub(z) ^ z >> 12; 51 + y = y.wrapping_sub(x).wrapping_sub(z) ^ x << 16; 52 + z = z.wrapping_sub(x).wrapping_sub(y) ^ y >> 5; 53 + x = x.wrapping_sub(y).wrapping_sub(z) ^ z >> 3; 54 + y = y.wrapping_sub(x).wrapping_sub(z) ^ x << 10; 55 + z.wrapping_sub(x).wrapping_sub(y) ^ y >> 15 56 + } 57 + 58 + // pretty sure this was some bog standard double->int conversion function of stl or something 59 + fn to_int_kinda(input: f64) -> u64 { 60 + // let is_normal_finite = ((bits >> 32) & 0x7fff_ffff) < 0x7ff0_0000; 61 + let is_normal_finite = input.is_finite(); 62 + let valid_range = (-9.223372036854776e18..9.223372036854776e18).contains(&input); 63 + 64 + // could remove this check ig, I've never seen that warning 65 + if !is_normal_finite || !valid_range { 66 + tracing::warn!("invalid float received"); 67 + return (-0.0f64).to_bits(); 68 + } 69 + 70 + let abs_bits = input.to_bits() & !(1 << 63); 71 + let in_abs = f64::from_bits(abs_bits); 72 + if in_abs == 0.0 { 73 + return 0; 74 + } 75 + 76 + let exponent = abs_bits >> 52; 77 + let norm_mantissa = (abs_bits & ((1 << 52) - 1)) | (1 << 52); 78 + 79 + let shift = 0x433 - exponent as i32; 80 + let mut result = if shift > 0 { 81 + norm_mantissa >> (shift & 63) 82 + } else { 83 + norm_mantissa << (-shift & 63) 84 + }; 85 + 86 + // restore the sign lol 87 + if input != in_abs { 88 + result = result.wrapping_neg(); 89 + } 90 + 91 + result & 0xffff_ffff 92 + }
+120
src/noita/types/components.rs
··· 1 + use zerocopy::{AsBytes, FromBytes, FromZeroes}; 2 + 3 + use crate::memory::{CString, PadBool, RawPtr, RealignedF64, StdString, StdVec}; 4 + 5 + use super::{Bitset256, Vec2, Vec2i}; 6 + 7 + #[derive(AsBytes, FromBytes, FromZeroes, Debug)] 8 + #[repr(C)] 9 + pub struct Component { 10 + pub vftable: RawPtr, 11 + _field_0x4: u32, 12 + pub type_name: CString, 13 + pub type_id: u32, 14 + pub instance_id: u32, 15 + pub enabled: PadBool<3>, 16 + pub tags: Bitset256, 17 + some_vec: StdVec<u32>, // no idea what this is yet, 18 + _field_0x44: u32, 19 + } 20 + 21 + pub trait ComponentName { 22 + const NAME: &str; 23 + } 24 + 25 + #[derive(AsBytes, FromBytes, FromZeroes, Debug)] 26 + #[repr(C)] 27 + pub struct WalletComponent { 28 + pub parent: Component, 29 + pub money: u64, 30 + pub money_spent: u64, 31 + pub money_prev_frame: u64, 32 + pub money_infinite: PadBool<7>, 33 + } 34 + 35 + impl ComponentName for WalletComponent { 36 + const NAME: &str = "WalletComponent"; 37 + } 38 + 39 + #[derive(AsBytes, FromBytes, FromZeroes, Debug)] 40 + #[repr(C)] 41 + pub struct ItemComponent { 42 + pub parent: Component, 43 + pub item_name: StdString, 44 + pub is_stackable: PadBool, 45 + pub is_consumable: PadBool, 46 + pub stats_count_as_item_pick_up: PadBool, 47 + pub auto_pickup: PadBool, 48 + pub permanently_attached: PadBool<3>, 49 + pub uses_remaining: i32, 50 + pub is_identified: PadBool, 51 + pub is_frozen: PadBool, 52 + pub collect_nondefault_actions: PadBool, 53 + pub remove_on_death: PadBool, 54 + pub remove_on_death_if_empty: PadBool, 55 + pub remove_default_child_actions_on_death: PadBool, 56 + pub play_hover_animation: PadBool, 57 + pub play_spinning_animation: PadBool, 58 + pub is_equipable_forced: PadBool, 59 + pub play_pick_sound: PadBool, 60 + pub drinkable: PadBool<1>, 61 + pub spawn_pos: Vec2, 62 + pub max_child_items: i32, 63 + pub ui_sprite: StdString, 64 + pub ui_description: StdString, 65 + pub preferred_inventory: u32, 66 + pub enable_orb_hacks: u8, 67 + pub is_all_spells_book: u8, 68 + pub always_use_item_name_in_ui: PadBool<1>, 69 + pub custom_pickup_string: StdString, 70 + pub ui_display_description_on_pick_up_hint: PadBool<3>, 71 + pub inventory_slot: Vec2i, 72 + pub next_frame_pickable: i32, 73 + pub npc_next_frame_pickable: i32, 74 + pub is_pickable: PadBool, 75 + pub is_hittable_always: PadBool<2>, 76 + pub item_pickup_radius: f32, 77 + pub camera_max_distance: f32, 78 + pub camera_smooth_speed_multiplier: f32, 79 + pub has_been_picked_by_player: PadBool<3>, 80 + pub m_frame_picked_up: i32, 81 + pub m_item_uid: i32, 82 + pub m_is_identified: PadBool<3>, 83 + } 84 + 85 + impl ComponentName for ItemComponent { 86 + const NAME: &str = "ItemComponent"; 87 + } 88 + 89 + #[derive(AsBytes, FromBytes, FromZeroes, Debug)] 90 + #[repr(C)] 91 + pub struct MaterialInventoryComponent { 92 + pub parent: Component, 93 + pub drop_as_item: PadBool, 94 + pub on_death_spill: PadBool, 95 + pub leak_gently: PadBool<1>, 96 + pub leak_on_damage_percent: f32, 97 + pub leak_pressure_min: f32, 98 + pub leak_pressure_max: f32, 99 + pub min_damage_to_leak: f32, 100 + pub b2_force_on_leak: f32, 101 + pub death_throw_particle_velocity_coeff: f32, 102 + pub kill_when_empty: PadBool, 103 + pub halftime_materials: PadBool<2>, 104 + pub do_reactions: i32, 105 + pub do_reactions_explosions: PadBool, 106 + pub do_reactions_entities: PadBool<2>, 107 + pub reaction_speed: i32, 108 + pub reactions_shaking_speeds_up: PadBool<3>, 109 + pub max_capacity: RealignedF64, 110 + pub count_per_material_type: StdVec<f64>, 111 + pub audio_collision_size_modifier_amount: f32, 112 + pub is_death_handled: PadBool<3>, 113 + pub last_frame_drank: i32, 114 + pub ex_position: Vec2, 115 + pub ex_angle: f32, 116 + } 117 + 118 + impl ComponentName for MaterialInventoryComponent { 119 + const NAME: &str = "MaterialInventoryComponent"; 120 + }
+232
src/noita/types/mod.rs
··· 1 + use std::{ 2 + fmt::{self, Debug, Write as _}, 3 + io, 4 + ops::Index, 5 + }; 6 + 7 + use zerocopy::{AsBytes, FromBytes, FromZeroes}; 8 + 9 + use crate::memory::{ 10 + ByteBool, MemoryStorage, PadBool, ProcessRef, Ptr, RawPtr, StdMap, StdString, StdVec, 11 + }; 12 + 13 + pub mod components; 14 + 15 + #[derive(AsBytes, FromBytes, FromZeroes, Clone, Copy)] 16 + #[repr(C)] 17 + pub struct Bitset256([u8; 32]); 18 + 19 + impl Index<u8> for Bitset256 { 20 + type Output = bool; 21 + 22 + // this actually never fails 23 + fn index(&self, index: u8) -> &Self::Output { 24 + if self.0[(index / 8) as usize] & (1 << (index % 8)) != 0 { 25 + &true 26 + } else { 27 + &false 28 + } 29 + } 30 + } 31 + 32 + impl Index<Option<u8>> for Bitset256 { 33 + type Output = bool; 34 + 35 + fn index(&self, index: Option<u8>) -> &Self::Output { 36 + index.map_or(&false, |i| &self[i]) 37 + } 38 + } 39 + 40 + impl Debug for Bitset256 { 41 + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 42 + let mut res = String::with_capacity(256); 43 + for b in &self.0 { 44 + write!(&mut res, "{:08b}", b)?; 45 + } 46 + let cut = res.bytes().rposition(|b| b != b'0').unwrap_or(0); 47 + write!(f, "{}", &res[..=cut]) 48 + } 49 + } 50 + 51 + #[derive(AsBytes, FromBytes, FromZeroes, Clone, Copy)] 52 + #[repr(C)] 53 + pub struct Vec2 { 54 + pub x: f32, 55 + pub y: f32, 56 + } 57 + 58 + impl Debug for Vec2 { 59 + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 60 + write!(f, "({}, {})", self.x, self.y) 61 + } 62 + } 63 + 64 + #[derive(AsBytes, FromBytes, FromZeroes, Clone, Copy)] 65 + #[repr(C)] 66 + pub struct Vec2i { 67 + pub x: i32, 68 + pub y: i32, 69 + } 70 + 71 + impl Debug for Vec2i { 72 + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 73 + write!(f, "({}, {})", self.x, self.y) 74 + } 75 + } 76 + 77 + #[derive(AsBytes, FromBytes, FromZeroes, Debug)] 78 + #[repr(C)] 79 + pub struct EntityTransform { 80 + pub pos: Vec2, 81 + pub rot: Vec2, 82 + pub rot90: Vec2, 83 + pub scale: Vec2, 84 + } 85 + 86 + #[derive(AsBytes, FromBytes, FromZeroes, Debug)] 87 + #[repr(C)] 88 + pub struct Entity { 89 + pub id: u32, 90 + pub comp_idx: u32, 91 + pub filename_idx: u32, 92 + pub dead: PadBool<3>, 93 + field_0x10: u32, 94 + pub name: StdString, 95 + field_0x2c: u32, 96 + pub tags: Bitset256, 97 + pub transform: EntityTransform, 98 + pub children: Ptr<StdVec<Ptr<Entity>>>, 99 + pub parent: Ptr<Entity>, 100 + } 101 + 102 + #[derive(AsBytes, FromBytes, FromZeroes, Debug)] 103 + #[repr(C)] 104 + pub struct EntityManager { 105 + pub vftable: RawPtr, 106 + pub next_entity_id: u32, 107 + pub free_ids: StdVec<u32>, 108 + pub entities: StdVec<Ptr<Entity>>, 109 + pub entity_buckets: StdVec<StdVec<Ptr<Entity>>>, 110 + pub component_buffers: StdVec<Ptr<ComponentBuffer>>, 111 + } 112 + 113 + impl EntityManager { 114 + pub fn get_first_tagged_entity( 115 + &self, 116 + p: &ProcessRef, 117 + tag_index: u8, 118 + ) -> io::Result<Option<Ptr<Entity>>> { 119 + let Some(bucket) = self.entity_buckets.get(tag_index as u32) else { 120 + return Ok(None); 121 + }; 122 + let Some(first) = bucket.read(p)?.get(0) else { 123 + return Ok(None); 124 + }; 125 + Ok(Some(first.read(p)?)) 126 + } 127 + } 128 + 129 + #[derive(AsBytes, FromBytes, FromZeroes, Debug)] 130 + #[repr(C)] 131 + pub struct TagManager { 132 + pub tags: StdVec<StdString>, 133 + pub tag_indices: StdMap<StdString, u8>, 134 + pub max_tag_count: u32, // this is always 256 lul (and can't really be more cuz both bitset<256> and entity bucked idx being a byte) 135 + pub name: StdString, 136 + } 137 + 138 + #[derive(AsBytes, FromBytes, FromZeroes, Debug)] 139 + #[repr(C)] 140 + pub struct CellFactory { 141 + field_0x0: u32, 142 + pub materials: StdVec<StdString>, 143 + pub material_indices: StdMap<StdString, u32>, 144 + pub material_descs_maybe: StdVec<MaterialDesc>, 145 + pub number_of_materials: u32, // I mean this is the same as materials.len() but ok 146 + field_0x28: u32, 147 + } 148 + 149 + #[derive(AsBytes, FromBytes, FromZeroes, Debug)] 150 + #[repr(C)] 151 + pub struct MaterialDesc { 152 + _skip1: [u8; 0x18], 153 + pub ui_name: StdString, 154 + _woohoo_thats_alotta_bytes: [u8; 0x260], 155 + } 156 + const _: () = assert!(std::mem::size_of::<MaterialDesc>() == 0x290); 157 + 158 + #[derive(AsBytes, FromBytes, FromZeroes, Debug)] 159 + #[repr(C)] 160 + pub struct GameGlobal { 161 + pub frame_counter: u32, 162 + _skip: [u8; 0x14], 163 + pub cell_factory: Ptr<CellFactory>, 164 + } 165 + 166 + #[derive(AsBytes, FromBytes, FromZeroes, Debug)] 167 + #[repr(C)] 168 + pub struct ComponentTypeManager { 169 + pub next_id: u32, 170 + pub component_indices: StdMap<StdString, u32>, 171 + } 172 + 173 + #[derive(AsBytes, FromBytes, FromZeroes, Debug)] 174 + #[repr(C)] 175 + pub struct ComponentBuffer { 176 + pub vftable: RawPtr, 177 + pub default_index: u32, 178 + _skip1: [u8; 8], 179 + pub indices: StdVec<u32>, 180 + _skip2: [u8; 0x24], 181 + pub storage: StdVec<RawPtr>, 182 + } 183 + 184 + #[derive(AsBytes, FromBytes, FromZeroes, Debug)] 185 + #[repr(C)] 186 + pub struct GlobalStats { 187 + pub vftable: RawPtr, 188 + pub stats_version: u32, 189 + pub debug_tracker: u32, 190 + pub debug: PadBool<3>, 191 + pub debug_reseet_counter: u32, 192 + pub fix_stats_flag: ByteBool, 193 + pub session_dead: PadBool<2>, 194 + pub key_value_stats: StdMap<StdString, u32>, 195 + pub session: GameStats, 196 + pub highest: GameStats, 197 + pub global: GameStats, 198 + pub prev_best: GameStats, 199 + } 200 + 201 + #[derive(AsBytes, FromBytes, FromZeroes, Debug)] 202 + #[repr(C)] 203 + pub struct GameStats { 204 + pub vftable: RawPtr, 205 + pub dead: PadBool<3>, 206 + pub death_count: u32, 207 + pub streaks: u32, 208 + pub world_seed: u32, 209 + pub killed_by: StdString, 210 + pub killed_by_extra: StdString, 211 + pub death_pos: Vec2, 212 + field_0x4c: u32, // 8-align padding?. 213 + pub playtime: f64, 214 + pub playtime_str: StdString, 215 + pub places_visited: u32, 216 + pub enemies_killed: u32, 217 + pub heart_containers: u32, 218 + field_0x7c: u32, // same? 219 + pub hp: i64, 220 + pub gold: i64, 221 + pub gold_all: i64, 222 + pub gold_infinite: PadBool<3>, 223 + pub items: u32, 224 + pub projectiles_shot: u32, 225 + pub kicks: u32, 226 + pub damage_taken: f64, 227 + pub healed: f64, 228 + pub teleports: u32, 229 + pub wands_edited: u32, 230 + pub biomes_visited_with_wands: u32, 231 + field_0xc4: u32, // same? 232 + }
+97
src/orb_searcher.rs
··· 1 + use std::collections::HashSet; 2 + 3 + use eframe::egui::{pos2, Context, Pos2}; 4 + use noita_utility_box::noita::{rng::NoitaRng, Seed}; 5 + use rayon::iter::{IntoParallelIterator, ParallelIterator}; 6 + use smart_default::SmartDefault; 7 + use tracing::Instrument; 8 + 9 + use crate::util::Promise; 10 + 11 + #[derive(Debug, SmartDefault)] 12 + pub struct OrbSearcher { 13 + #[default(1024)] 14 + chunk_size: u32, 15 + #[default([10, 3])] 16 + search_range: [i32; 2], 17 + searched_chunks: HashSet<(i32, i32)>, 18 + known_orbs: Vec<Pos2>, 19 + search_task: Promise<Vec<(i32, i32)>>, 20 + } 21 + 22 + impl OrbSearcher { 23 + pub fn known_orbs(&self) -> &[Pos2] { 24 + &self.known_orbs 25 + } 26 + 27 + pub fn searched_chunks(&self) -> usize { 28 + self.searched_chunks.len() 29 + } 30 + 31 + pub fn chunk_size(&self) -> u32 { 32 + self.chunk_size 33 + } 34 + 35 + pub fn reset(&mut self) { 36 + self.known_orbs.clear(); 37 + self.searched_chunks.clear(); 38 + } 39 + 40 + fn next_chunk(&mut self, pos: Pos2) -> Option<(i32, i32)> { 41 + let xc = pos.x as i32 / self.chunk_size as i32; 42 + let yc = pos.y as i32 / self.chunk_size as i32; 43 + //meh 44 + for x in xc - self.search_range[0]..=xc + self.search_range[0] { 45 + for y in yc - self.search_range[1]..=yc + self.search_range[1] { 46 + if self.searched_chunks.insert((x, y)) { 47 + return Some((x, y)); 48 + } 49 + } 50 + } 51 + None 52 + } 53 + 54 + pub fn poll_search(&mut self, ctx: &Context, seed: Seed, pos: Pos2) { 55 + if self.search_task.is_taken() { 56 + if let Some((x, y)) = self.next_chunk(pos) { 57 + let size = self.chunk_size; 58 + let x = x * size as i32; 59 + let y = y * size as i32; 60 + let ctx = ctx.clone(); 61 + self.search_task = Promise::spawn( 62 + async move { 63 + let orbs = find_orbs(seed.sum(), x, y, size, size); 64 + ctx.request_repaint(); 65 + orbs 66 + } 67 + .instrument(tracing::trace_span!("search", %seed, x, y, size)), 68 + ); 69 + } 70 + } else if let Some(orbs) = self.search_task.poll_take() { 71 + self.known_orbs 72 + .extend(orbs.into_iter().map(|(x, y)| pos2(x as f32, y as f32))); 73 + } 74 + self.known_orbs.sort_unstable_by_key(|orb| { 75 + let dir = *orb - pos; 76 + dir.length_sq() as i32 77 + }); 78 + } 79 + } 80 + 81 + fn find_orbs(world_seed: u32, x: i32, y: i32, x_size: u32, y_size: u32) -> Vec<(i32, i32)> { 82 + (0..x_size * y_size) 83 + .into_par_iter() 84 + .filter_map(|i| { 85 + let xi = x + (i % x_size) as i32; 86 + let yi = y + (i / x_size) as i32; 87 + 88 + let mut rng = NoitaRng::from_pos(world_seed, xi as f64, yi as f64); 89 + 90 + if (rng.random() * 100001.0) as u32 == 100000 && (rng.random() * 1001.0) as u32 == 999 { 91 + tracing::debug!(x = xi, y = yi, "orb found"); 92 + return Some((xi, yi)); 93 + } 94 + None 95 + }) 96 + .collect() 97 + }
+336
src/tools/address_maps.rs
··· 1 + use std::{ 2 + io, 3 + sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}, 4 + }; 5 + 6 + use derive_more::Debug; 7 + use eframe::egui::{ 8 + collapsing_header::CollapsingState, Align, Button, CollapsingHeader, Id, TextEdit, Ui, Vec2, 9 + Widget, 10 + }; 11 + use egui_extras::{Column, TableBuilder}; 12 + use noita_utility_box::{ 13 + memory::{exe_image::PeHeader, ProcessRef, Ptr}, 14 + noita::{discovery, NoitaGlobals}, 15 + }; 16 + use serde::{Deserialize, Serialize}; 17 + use smart_default::SmartDefault; 18 + 19 + #[derive(Debug, Default, Serialize, Deserialize)] 20 + #[serde(default)] 21 + pub struct AddressMaps { 22 + maps: Vec<AddressMap>, 23 + } 24 + 25 + #[derive(Debug, Clone, Serialize, Deserialize)] 26 + struct AddressEntry { 27 + name: String, 28 + address: u32, 29 + comment: String, 30 + } 31 + 32 + #[derive(SmartDefault, Debug, Serialize, Deserialize)] 33 + pub struct AddressMapData { 34 + name: String, 35 + noita_ts: u32, 36 + entries: Vec<AddressEntry>, 37 + #[default(Id::new(fastrand::u64(..)))] 38 + ui_id: Id, 39 + } 40 + 41 + #[derive(Default, Clone, Debug, Serialize, Deserialize)] 42 + pub struct AddressMap { 43 + data: Arc<RwLock<AddressMapData>>, 44 + } 45 + 46 + impl AddressMap { 47 + pub fn data(&self) -> RwLockReadGuard<AddressMapData> { 48 + self.data.read().unwrap() 49 + } 50 + 51 + pub fn data_mut(&self) -> RwLockWriteGuard<AddressMapData> { 52 + self.data.write().unwrap() 53 + } 54 + 55 + fn get<T>(&self, name: &str) -> Option<Ptr<T>> { 56 + self.data() 57 + .entries 58 + .iter() 59 + .find(|e| e.name == name) 60 + .map(|e| Ptr::of(e.address)) 61 + } 62 + 63 + pub fn as_noita_globals(&self) -> NoitaGlobals { 64 + NoitaGlobals { 65 + world_seed: self.get("seed"), 66 + ng_count: self.get("ng-plus-count"), 67 + global_stats: self.get("global-stats"), 68 + game_global: self.get("game-global"), 69 + entity_manager: self.get("entity-manager"), 70 + entity_tag_manager: self.get("entity-tag-manager"), 71 + component_type_manager: self.get("component-type-manager"), 72 + } 73 + } 74 + } 75 + 76 + fn hex_input(value: &mut u32) -> impl Widget + '_ { 77 + move |ui: &mut Ui| { 78 + let mut ts = format!("0x{:x}", value); 79 + let response = ui.add( 80 + TextEdit::singleline(&mut ts) 81 + .horizontal_align(Align::Center) 82 + .desired_width(75.0), 83 + ); 84 + // allow text input to be empty 85 + if ts.is_empty() { 86 + *value = 0; 87 + } else if let Ok(ts) = ts.parse() { 88 + *value = ts; 89 + } else if let Some(ts) = ts.strip_prefix("0x").and_then(|ts| { 90 + // allow typing in 0x 91 + if ts.is_empty() { 92 + Some(0) 93 + } else { 94 + u32::from_str_radix(ts, 16).ok() 95 + } 96 + }) { 97 + *value = ts; 98 + } 99 + response 100 + } 101 + } 102 + 103 + impl AddressMaps { 104 + pub fn get(&self, noita_ts: u32) -> Option<AddressMap> { 105 + self.maps 106 + .iter() 107 + .find(|m| m.data.read().unwrap().noita_ts == noita_ts) 108 + .cloned() 109 + } 110 + 111 + pub fn discover(&mut self, proc: &ProcessRef, header: &PeHeader) -> io::Result<()> { 112 + fn add_entry<T>( 113 + entries: &mut Vec<AddressEntry>, 114 + name: &str, 115 + ptr: Option<Ptr<T>>, 116 + comment: &str, 117 + ) { 118 + if let Some(ptr) = ptr { 119 + entries.push(AddressEntry { 120 + name: name.to_owned(), 121 + address: ptr.addr(), 122 + comment: comment.to_owned(), 123 + }); 124 + } else { 125 + tracing::warn!("{name} pointer not found"); 126 + } 127 + } 128 + 129 + let image = header.clone().read_image(proc)?; 130 + let discovered = discovery::run(&image); 131 + 132 + let mut entries = Vec::new(); 133 + add_entry( 134 + &mut entries, 135 + "seed", 136 + discovered.world_seed, 137 + "Current world seed", 138 + ); 139 + add_entry( 140 + &mut entries, 141 + "ng-plus-count", 142 + discovered.ng_count, 143 + "New Game Plus counter", 144 + ); 145 + add_entry( 146 + &mut entries, 147 + "global-stats", 148 + discovered.global_stats, 149 + "Used to get all the stats", 150 + ); 151 + add_entry( 152 + &mut entries, 153 + "game-global", 154 + discovered.game_global, 155 + "Stores global game state, like the list of materials", 156 + ); 157 + add_entry( 158 + &mut entries, 159 + "entity-manager", 160 + discovered.entity_manager, 161 + "Entity manager, used to find the player or whatever it got polymorphed into", 162 + ); 163 + add_entry( 164 + &mut entries, 165 + "entity-tag-manager", 166 + discovered.entity_tag_manager, 167 + "Entity tag manager, also used to find the player", 168 + ); 169 + add_entry( 170 + &mut entries, 171 + "component-type-manager", 172 + discovered.component_type_manager, 173 + "Component type manager, used to get entity components", 174 + ); 175 + 176 + if !entries.is_empty() { 177 + let name = match discovery::find_noita_build(&image) { 178 + Some(noita) => format!("Autodiscovered - {noita}"), 179 + None => "Autodiscovered (no noita build string found!)".into(), 180 + }; 181 + 182 + self.maps.push(AddressMap { 183 + data: Arc::new(RwLock::new(AddressMapData { 184 + name, 185 + noita_ts: header.timestamp(), 186 + entries, 187 + ui_id: Id::new(fastrand::u64(..)), 188 + })), 189 + }); 190 + } 191 + 192 + Ok(()) 193 + } 194 + 195 + pub fn ui(&mut self, ui: &mut Ui) { 196 + ui.heading("Address Maps"); 197 + ui.separator(); 198 + 199 + let mut removed = None; 200 + 201 + for (i, map) in self.maps.iter_mut().enumerate() { 202 + let mut map = map.data_mut(); 203 + CollapsingHeader::new(format!("(0x{:x}) {}", map.noita_ts, map.name)) 204 + .id_salt(map.ui_id) 205 + .show(ui, |ui| { 206 + ui.horizontal(|ui| { 207 + ui.label("Executable timestamp: "); 208 + ui.add(hex_input(&mut map.noita_ts)); 209 + }); 210 + ui.horizontal(|ui| { 211 + ui.label("Comment: "); 212 + ui.text_edit_singleline(&mut map.name); 213 + }); 214 + 215 + // oof 216 + let header_id = ui 217 + .stack() 218 + .parent 219 + .as_ref() 220 + .unwrap() 221 + .parent 222 + .as_ref() 223 + .unwrap() 224 + .id 225 + .with(map.ui_id); 226 + 227 + let confirm_id = ui.make_persistent_id("confirm"); 228 + 229 + let confirm = ui.data(|d| d.get_temp(confirm_id).unwrap_or_default()); 230 + 231 + if confirm { 232 + ui.horizontal(|ui| { 233 + ui.label("Are you sure?"); 234 + if ui.button("Yes").clicked() { 235 + ui.data_mut(|d| d.remove::<bool>(confirm_id)); 236 + removed = Some((i, header_id)); 237 + } 238 + if ui.button("No").clicked() { 239 + ui.data_mut(|d| d.remove::<bool>(confirm_id)); 240 + } 241 + }); 242 + } else if ui.button("Delete").clicked() { 243 + ui.data_mut(|d| d.insert_temp(confirm_id, true)); 244 + } 245 + 246 + ui.separator(); 247 + 248 + ui.vertical(|ui| { 249 + TableBuilder::new(ui) 250 + .striped(true) 251 + .column(Column::auto()) 252 + .column(Column::auto().resizable(true)) 253 + .column(Column::auto()) 254 + .column(Column::remainder().clip(true)) 255 + .header(20.0, |mut header| { 256 + header.col(|_| {}); 257 + header.col(|ui| { 258 + ui.label("Name"); 259 + }); 260 + header.col(|ui| { 261 + ui.label("Address"); 262 + }); 263 + header.col(|ui| { 264 + ui.label("Comment"); 265 + }); 266 + }) 267 + .body(|mut body| { 268 + let mut removed = None; 269 + for (i, entry) in map.entries.iter_mut().enumerate() { 270 + let AddressEntry { 271 + name, 272 + address, 273 + comment, 274 + } = entry; 275 + 276 + body.row(20.0, |mut row| { 277 + row.col(|ui| { 278 + if ui 279 + .add(Button::new(" -").min_size(Vec2::splat(18.0))) 280 + .clicked() 281 + { 282 + removed = Some(i); 283 + } 284 + }); 285 + row.col(|ui| { 286 + ui.add_space(0.5); 287 + ui.add(TextEdit::singleline(name)); 288 + ui.add_space(0.5); 289 + }); 290 + row.col(|ui| { 291 + ui.add_space(0.5); 292 + ui.add(hex_input(address)); 293 + ui.add_space(0.5); 294 + }); 295 + row.col(|ui| { 296 + ui.add_space(0.5); 297 + ui.add(TextEdit::singleline(comment)); 298 + ui.add_space(0.5); 299 + }); 300 + }); 301 + } 302 + if let Some(i) = removed { 303 + map.entries.remove(i); 304 + } 305 + body.row(20.0, |mut row| { 306 + row.col(|ui| { 307 + if ui 308 + .add(Button::new(" +").min_size(Vec2::splat(18.0))) 309 + .clicked() 310 + { 311 + map.entries.push(AddressEntry { 312 + name: "new".to_owned(), 313 + address: 0, 314 + comment: String::new(), 315 + }); 316 + } 317 + }); 318 + }) 319 + }) 320 + }); 321 + }); 322 + } 323 + 324 + if let Some((i, header_id)) = removed { 325 + self.maps.remove(i); 326 + 327 + // cleanup the collapsing state at this id 328 + CollapsingState::load_with_default_open(ui.ctx(), header_id, false).remove(ui.ctx()); 329 + ui.ctx().animate_bool_with_time(header_id, false, 0.0); 330 + } 331 + 332 + if ui.button("Add").clicked() { 333 + self.maps.push(AddressMap::default()); 334 + } 335 + } 336 + }
+337
src/tools/live_stats.rs
··· 1 + use std::{borrow::Cow, collections::HashMap, sync::Arc}; 2 + 3 + use eframe::egui::{ComboBox, DragValue, Grid, RichText, TextEdit, Ui}; 4 + use futures::{pin_mut, StreamExt}; 5 + use noita_utility_box::{memory::MemoryStorage, noita::Noita}; 6 + use obws::{events::Event, requests::inputs::SetSettings, responses::inputs::InputId}; 7 + use smart_default::SmartDefault; 8 + use strfmt::{FmtError, Format}; 9 + 10 + use crate::{ 11 + app::AppState, 12 + tools::settings::{Interval, Timer}, 13 + util::{persist, Promise}, 14 + }; 15 + use derive_more::Debug; 16 + 17 + #[derive(Debug, Default)] 18 + enum ObsState { 19 + #[default] 20 + NotConnected, 21 + Connecting(#[debug(skip)] Promise<obws::Result<obws::Client>>), 22 + Connected(#[debug(skip)] Arc<obws::Client>, Promise<()>), 23 + Error(String), 24 + } 25 + 26 + #[derive(Debug, Clone, PartialEq, Eq)] 27 + struct Stats { 28 + deaths: u32, 29 + wins: u32, 30 + streak: u32, 31 + record: u32, 32 + actual_playtime: String, 33 + } 34 + 35 + #[derive(Debug, SmartDefault)] 36 + pub struct LiveStats { 37 + stats: Option<Stats>, 38 + 39 + obs_ws: ObsState, 40 + text_sources: Promise<Vec<InputId>>, 41 + 42 + format_error: Option<String>, 43 + /// Force setting the OBS text source on next update (normally it only happens when the stats change) 44 + pending_obs_update: bool, 45 + 46 + #[default(Timer::new(Interval::LiveStats))] 47 + timer: Timer, 48 + 49 + #[default("localhost")] 50 + obs_address: String, 51 + #[default(4455)] 52 + obs_port: u16, 53 + obs_password: String, 54 + selected: Option<InputId>, 55 + #[default = "{deaths}/{wins}/{streak}({streak-pb})"] 56 + format: String, 57 + running: bool, 58 + 59 + /// Used for persistence 60 + was_connected: bool, 61 + } 62 + 63 + persist!(LiveStats { 64 + obs_address: String, 65 + obs_port: u16, 66 + obs_password: String, 67 + selected: Option<InputId>, 68 + format: String, 69 + running: bool, 70 + was_connected: bool, 71 + }); 72 + 73 + impl LiveStats { 74 + fn update(&mut self, noita: &Noita) { 75 + let new_stats = noita 76 + .read_stats() 77 + .and_then(|global| { 78 + let end0 = global 79 + .key_value_stats 80 + .get(noita.proc(), "progress_ending0")? 81 + .unwrap_or_default(); 82 + let end1 = global 83 + .key_value_stats 84 + .get(noita.proc(), "progress_ending1")? 85 + .unwrap_or_default(); 86 + 87 + Ok(Stats { 88 + deaths: global.global.death_count, 89 + wins: end0 + end1, 90 + streak: global.session.streaks, 91 + record: global.highest.streaks, 92 + actual_playtime: global.global.playtime_str.read(noita.proc())?, 93 + }) 94 + }) 95 + .map_err(|e| { 96 + tracing::warn!("Failed to read stats: {e:#}"); 97 + e 98 + }) 99 + .ok(); 100 + 101 + if new_stats == self.stats && !self.pending_obs_update { 102 + return; 103 + } 104 + self.pending_obs_update = false; 105 + self.stats = new_stats; 106 + 107 + if !self.running { 108 + return; 109 + } 110 + if let (Some(stats), Some(selected), ObsState::Connected(client, _)) = 111 + (&self.stats, &self.selected, &self.obs_ws) 112 + { 113 + let data = HashMap::from([ 114 + ("deaths".to_owned(), stats.deaths), 115 + ("wins".to_owned(), stats.wins), 116 + ("streak".to_owned(), stats.streak), 117 + ("streak-pb".to_owned(), stats.record), 118 + ]); 119 + 120 + let formatted = match self.format.format(&data) { 121 + Err( 122 + FmtError::Invalid(msg) | FmtError::KeyError(msg) | FmtError::TypeError(msg), 123 + ) => { 124 + self.format_error = Some(format!("Bad format: {msg}")); 125 + return; 126 + } 127 + Ok(f) => f, 128 + }; 129 + 130 + let src = selected.clone(); 131 + let client = client.clone(); 132 + tokio::spawn(async move { 133 + tracing::info!( 134 + src.name, 135 + src.uuid = src.uuid.to_string(), 136 + text = formatted, 137 + "updating OBS text source" 138 + ); 139 + let params = SetSettings { 140 + input: (&src).into(), 141 + settings: &HashMap::from([("text", formatted)]), 142 + overlay: None, 143 + }; 144 + if let Err(e) = client.inputs().set_settings(params).await { 145 + tracing::error!( 146 + src.name, 147 + src.uuid = src.uuid.to_string(), 148 + "failed to update OBS text source: {e:#}", 149 + ); 150 + } 151 + }); 152 + } 153 + } 154 + 155 + fn connect(&mut self) { 156 + self.obs_ws = ObsState::Connecting(Promise::spawn(obws::Client::connect( 157 + self.obs_address.clone(), 158 + self.obs_port, 159 + Some(std::mem::take(&mut self.obs_password)), 160 + ))); 161 + } 162 + 163 + fn disconnect(&mut self) { 164 + self.obs_ws = ObsState::NotConnected; 165 + self.was_connected = false; 166 + } 167 + 168 + pub fn ui(&mut self, ui: &mut Ui, state: &mut AppState) { 169 + if let Some(noita) = &state.noita { 170 + if self.timer.go(ui.ctx(), state) { 171 + self.update(noita); 172 + } 173 + }; 174 + 175 + ui.heading("Live Stats"); 176 + ui.separator(); 177 + 178 + Grid::new("live_stats").show(ui, |ui| { 179 + const NA: Cow<str> = Cow::Borrowed("N/A"); 180 + let s = self.stats.as_ref(); 181 + 182 + ui.label("Deaths: "); 183 + ui.label(s.map_or(NA, |s| s.deaths.to_string().into())); 184 + ui.end_row(); 185 + 186 + ui.label("Wins: "); 187 + ui.label(s.map_or(NA, |s| s.wins.to_string().into())); 188 + ui.end_row(); 189 + 190 + ui.label("Streak: "); 191 + ui.label(s.map_or(NA, |s| s.streak.to_string().into())); 192 + ui.end_row(); 193 + 194 + ui.label("Record: "); 195 + ui.label(s.map_or(NA, |s| s.record.to_string().into())); 196 + ui.end_row(); 197 + }); 198 + 199 + if let Some(ref s) = self.stats { 200 + ui.label(format!( 201 + "Your actual playtime (without AFK and pausing) as recorded by Noita is: {}", 202 + s.actual_playtime 203 + )); 204 + ui.end_row(); 205 + } 206 + 207 + ui.separator(); 208 + 209 + ui.label("Format:"); 210 + 211 + if ui.add(TextEdit::multiline(&mut self.format)).changed() { 212 + self.format_error = None; 213 + self.pending_obs_update = true; 214 + } 215 + 216 + if let Some(format_error) = &self.format_error { 217 + ui.label(RichText::new(format_error).color(ui.style().visuals.error_fg_color)); 218 + } 219 + 220 + ui.separator(); 221 + 222 + match &mut self.obs_ws { 223 + ObsState::NotConnected => { 224 + ui.label("Connect to OBS"); 225 + 226 + Grid::new("obs_connect").show(ui, |ui| { 227 + ui.label("Address:"); 228 + 229 + ui.horizontal(|ui| { 230 + ui.style_mut().spacing.item_spacing = [2.0, 0.0].into(); 231 + ui.add( 232 + TextEdit::singleline(&mut self.obs_address), // .min_size([ui.available_width(), 20.0].into()), 233 + ); 234 + 235 + ui.add(DragValue::new(&mut self.obs_port)); 236 + }); 237 + ui.end_row(); 238 + 239 + ui.label("Password:"); 240 + ui.add(TextEdit::singleline(&mut self.obs_password).password(true)); 241 + ui.end_row(); 242 + }); 243 + if ui.button("Connect").clicked() || self.was_connected { 244 + self.connect(); 245 + } 246 + } 247 + ObsState::Connecting(p) => match p.poll_take() { 248 + None => { 249 + ui.horizontal(|ui| { 250 + ui.spinner(); 251 + ui.label("Connecting to OBS..."); 252 + }); 253 + } 254 + Some(Err(e)) => { 255 + self.obs_ws = ObsState::Error(format!("{e:#}")); 256 + } 257 + Some(Ok(client)) => { 258 + self.obs_ws = match client.events() { 259 + Ok(events) => { 260 + let ctx = ui.ctx().clone(); 261 + let end_promise = Promise::spawn(async move { 262 + pin_mut!(events); 263 + while let Some(event) = events.next().await { 264 + if let Event::ServerStopping = event { 265 + ctx.request_repaint(); 266 + break; 267 + } 268 + } 269 + }); 270 + self.was_connected = true; 271 + ObsState::Connected(Arc::new(client), end_promise) 272 + } 273 + Err(e) => ObsState::Error(format!("{e:#}")), 274 + } 275 + } 276 + }, 277 + ObsState::Connected(client, end_promise) => { 278 + if end_promise.poll().is_some() { 279 + self.disconnect(); 280 + return; 281 + } 282 + // stop referencing self.obs_ws via this client through the big match 283 + let client = (*client).clone(); 284 + 285 + Grid::new("obs_connected").show(ui, |ui| { 286 + ui.label("Connected to OBS"); 287 + if ui.button("Disconnect").clicked() { 288 + self.disconnect(); 289 + } 290 + ui.end_row(); 291 + 292 + ui.label("Select text source"); 293 + let r = ComboBox::from_id_salt("obs_text_source") 294 + .selected_text(self.selected.as_ref().map_or("", |id| &id.name)) 295 + .show_ui(ui, |ui| { 296 + for source in self.text_sources.poll_or_default::<[_]>() { 297 + ui.selectable_value( 298 + &mut self.selected, 299 + Some(source.clone()), 300 + &source.name, 301 + ); 302 + } 303 + }); 304 + if r.response.clicked() { 305 + let client = client.clone(); 306 + self.text_sources = Promise::spawn(async move { 307 + client 308 + .inputs() 309 + .list(Some("text_ft2_source_v2")) 310 + .await 311 + .map(|inputs| inputs.into_iter().map(|input| input.id).collect()) 312 + .unwrap_or_default() 313 + }); 314 + self.running = false; 315 + } 316 + 317 + ui.end_row(); 318 + }); 319 + 320 + if ui.toggle_value(&mut self.running, "Run").clicked() { 321 + self.pending_obs_update = true; 322 + } 323 + } 324 + ObsState::Error(e) => { 325 + ui.label(RichText::new(&*e).color(ui.style().visuals.error_fg_color)); 326 + ui.horizontal(|ui| { 327 + if ui.button("Retry").clicked() { 328 + self.connect(); 329 + } 330 + if ui.button("Cancel").clicked() { 331 + self.disconnect(); 332 + } 333 + }); 334 + } 335 + } 336 + } 337 + }
+195
src/tools/material_pipette.rs
··· 1 + use std::collections::HashSet; 2 + 3 + use anyhow::{Context, Ok}; 4 + use eframe::egui::{CollapsingHeader, Grid, ScrollArea, Ui}; 5 + use noita_utility_box::{ 6 + memory::MemoryStorage, 7 + noita::types::components::{ItemComponent, MaterialInventoryComponent}, 8 + }; 9 + use serde::{Deserialize, Serialize}; 10 + 11 + use crate::app::AppState; 12 + 13 + #[derive(Debug, Default, Serialize, Deserialize)] 14 + pub struct MaterialPipette { 15 + realtime: bool, 16 + checked: HashSet<String>, 17 + } 18 + 19 + impl MaterialPipette { 20 + pub fn ui(&mut self, ui: &mut Ui, state: &mut AppState) { 21 + ui.heading("Material Pipette"); 22 + ui.separator(); 23 + 24 + ui.checkbox(&mut self.realtime, "Realtime"); 25 + if self.realtime { 26 + ui.ctx().request_repaint(); 27 + } 28 + 29 + ui.separator(); 30 + 31 + let Some(noita) = state.noita.as_mut() else { 32 + ui.label("Noita not connected"); 33 + return; 34 + }; 35 + 36 + // just do it on every redraw, whatever (todo add at least a timer here lol) 37 + let res = (|| { 38 + let player = match noita.get_player()? { 39 + Some((player, false)) => player, 40 + Some((_, true)) => { 41 + ui.label("Polymorphed LOL"); 42 + return Ok(()); 43 + } 44 + None => { 45 + ui.label("Player not found"); 46 + return Ok(()); 47 + } 48 + }; 49 + 50 + let p = noita.proc().clone(); 51 + 52 + let mut inv_quick = None; 53 + for child in player.children.read(&p)?.read(&p)? { 54 + let child = child.read(&p)?; 55 + if child.name.read(&p)? == "inventory_quick" { 56 + inv_quick = Some(child); 57 + break; 58 + } 59 + } 60 + let inv_quick = inv_quick.context("Player has no inventory?")?; 61 + 62 + let potion = noita.get_entity_tag_index("potion")?; 63 + let powder_stash = noita.get_entity_tag_index("powder_stash")?; 64 + 65 + let mut containers = Vec::new(); 66 + 67 + let store = noita.component_store::<ItemComponent>()?; 68 + 69 + for child in inv_quick.children.read(&p)?.read(&p)? { 70 + let child = child.read(&p)?; 71 + 72 + if child.tags[potion] { 73 + let Some(item_comp) = store.get(&child)? else { 74 + tracing::warn!(entity = child.id, "Potion has no ItemComponent?"); 75 + continue; 76 + }; 77 + 78 + containers.push(("Flask", item_comp.inventory_slot, child)); 79 + } else if child.tags[powder_stash] { 80 + let Some(item_comp) = store.get(&child)? else { 81 + tracing::warn!(entity = child.id, "Flask has no ItemComponent?"); 82 + continue; 83 + }; 84 + 85 + containers.push(("Pouch", item_comp.inventory_slot, child)); 86 + } 87 + } 88 + 89 + let store = noita.component_store::<MaterialInventoryComponent>()?; 90 + 91 + ScrollArea::both() 92 + .show(ui, |ui| { 93 + for (name, slot, container) in containers { 94 + let mat_inv = store 95 + .get(&container)? 96 + .context("Container has no MaterialInventoryComponent?")?; 97 + 98 + let mats = mat_inv 99 + .count_per_material_type 100 + .read(&p)? 101 + .into_iter() 102 + .enumerate() 103 + .filter_map(|(i, f)| (f > 0.0).then_some((i as u32, f))) 104 + .collect::<Vec<_>>(); 105 + 106 + let title = match slot.y { 107 + 0 => format!("{name} (slot {})", slot.x + 1), 108 + y => format!("{name} (slot x:{} y:{})", slot.x + 1, y + 1), 109 + }; 110 + 111 + CollapsingHeader::new(title) 112 + .id_salt(container.id) // whatever lul 113 + .default_open(true) 114 + .show(ui, |ui| { 115 + Grid::new(container.id) 116 + .num_columns(2) 117 + .show(ui, |ui| { 118 + if mats.is_empty() { 119 + ui.label("<Empty>"); 120 + ui.end_row(); 121 + return Ok(()); 122 + } 123 + for (idx, amount) in mats { 124 + let name = 125 + noita.get_material_name(idx)?.unwrap_or_else( 126 + || format!("unknown material (index {idx})"), 127 + ); 128 + ui.label(format!("{name:?}")); 129 + ui.label(format!("{:.2}", amount)); 130 + ui.end_row(); 131 + } 132 + Ok(()) 133 + }) 134 + .inner 135 + }) 136 + .body_returned 137 + .transpose()?; 138 + } 139 + 140 + if state.settings.pipette_checklist { 141 + ui.separator(); 142 + 143 + CollapsingHeader::new("Material checklist") 144 + .show(ui, |ui| { 145 + if ui.button("Reset").clicked() { 146 + self.checked.clear(); 147 + } 148 + ui.add_space(0.5); 149 + Grid::new("all_materials") 150 + .num_columns(4) 151 + .striped(true) 152 + .show(ui, |ui| { 153 + for idx in 0..noita.materials()?.len() as u32 { 154 + let name = noita.get_material_name(idx)?.unwrap(); 155 + 156 + ui.label(idx.to_string()); 157 + 158 + let mut checked = self.checked.contains(&name); 159 + ui.checkbox(&mut checked, ""); 160 + 161 + ui.label(format!("{name:?}")); 162 + 163 + if checked { 164 + self.checked.insert(name); 165 + } else { 166 + self.checked.remove(&name); 167 + } 168 + 169 + if let Some(ui_name) = 170 + noita.get_material_ui_name(idx)? 171 + { 172 + ui.label(ui_name); 173 + } 174 + 175 + ui.end_row(); 176 + } 177 + Ok(()) 178 + }) 179 + .inner 180 + }) 181 + .body_returned 182 + .transpose() 183 + .map(|_| ()) 184 + } else { 185 + Ok(()) 186 + } 187 + }) 188 + .inner 189 + })(); 190 + 191 + if let Err(e) = res { 192 + ui.label(format!("Error: {e}")); 193 + } 194 + } 195 + }
+6
src/tools/mod.rs
··· 1 + pub mod address_maps; 2 + pub mod live_stats; 3 + pub mod material_pipette; 4 + pub mod orb_radar; 5 + pub mod process_panel; 6 + pub mod settings;
+189
src/tools/orb_radar.rs
··· 1 + use std::fmt::Write as _; 2 + 3 + use crate::{app::AppState, orb_searcher::OrbSearcher}; 4 + use eframe::egui::{ 5 + pos2, vec2, Align, Align2, Color32, FontId, Layout, Rect, Rounding, Stroke, Ui, 6 + }; 7 + 8 + #[derive(Debug, Default)] 9 + pub struct OrbRadar { 10 + realtime: bool, 11 + orb_searcher: OrbSearcher, 12 + } 13 + 14 + impl OrbRadar { 15 + pub fn ui(&mut self, ui: &mut Ui, state: &mut AppState) { 16 + ui.heading("Orb Radar"); 17 + ui.separator(); 18 + 19 + ui.with_layout(Layout::bottom_up(Align::Min), |ui| { 20 + ui.horizontal(|ui| { 21 + ui.checkbox(&mut self.realtime, "Realtime"); 22 + if ui.button("Reset").clicked() { 23 + self.orb_searcher.reset(); 24 + } 25 + }); 26 + 27 + if self.realtime { 28 + ui.ctx().request_repaint(); 29 + } 30 + 31 + let (_, rect) = ui.allocate_space(ui.available_size()); 32 + 33 + let mut painter = ui.painter_at(rect); 34 + 35 + let text_color = ui.style().visuals.text_color(); 36 + let stroke = Stroke::new(2.0, text_color); 37 + 38 + let tracer = Stroke::new( 39 + 1.0 / ui.ctx().pixels_per_point(), 40 + ui.style().visuals.weak_text_color(), 41 + ); 42 + let tracer_bright = Stroke::new( 43 + 1.0 / ui.ctx().pixels_per_point(), 44 + ui.style().visuals.strong_text_color(), 45 + ); 46 + 47 + let rect = rect.shrink(stroke.width); 48 + painter.rect( 49 + rect, 50 + Rounding::same(0.0), 51 + ui.style().visuals.extreme_bg_color, 52 + stroke, 53 + ); 54 + painter.set_clip_rect(rect); 55 + 56 + let pos = state.noita.as_mut().and_then(|n| { 57 + n.get_player() 58 + .map_err(|e| { 59 + tracing::warn!(%e, "failed to read player pos"); 60 + e 61 + }) 62 + .ok() 63 + .flatten() 64 + .map(|(player, p)| { 65 + let pos = player.transform.pos; 66 + (pos2(pos.x, pos.y), p) 67 + }) 68 + }); 69 + 70 + let Some(((pos, p), seed)) = pos.zip(state.seed) else { 71 + painter.text( 72 + rect.center(), 73 + Align2::CENTER_CENTER, 74 + "NO DATA", 75 + FontId::monospace(16.0), 76 + ui.style().visuals.warn_fg_color, 77 + ); 78 + 79 + return; 80 + }; 81 + if p { 82 + painter.text( 83 + rect.left_top() + vec2(5.0, 5.0), 84 + Align2::LEFT_TOP, 85 + "POLYMORPHED LOL", 86 + FontId::proportional(16.0), 87 + ui.style().visuals.strong_text_color(), 88 + ); 89 + } 90 + 91 + self.orb_searcher.poll_search(ui.ctx(), seed, pos); 92 + 93 + for (i, orb) in self.orb_searcher.known_orbs().iter().enumerate() { 94 + let dir = *orb - pos; 95 + let pos = rect.center() + dir; 96 + 97 + if rect.contains(pos) { 98 + painter.circle_stroke( 99 + pos, 100 + 6.0, 101 + Stroke::new(1.0, ui.style().visuals.strong_text_color()), 102 + ); 103 + painter.rect( 104 + Rect::from_center_size(pos, vec2(2.0, 2.0)), 105 + Rounding::same(0.0), 106 + ui.style().visuals.strong_text_color(), 107 + Stroke::NONE, 108 + ); 109 + break; 110 + } 111 + 112 + let dist = dir.length(); 113 + let dir = dir.normalized(); 114 + 115 + painter.line_segment( 116 + [rect.center() + dir * 10.0, pos], 117 + if i == 0 { tracer_bright } else { tracer }, 118 + ); 119 + 120 + painter.text( 121 + rect.center() + dir * (rect.width().min(rect.height()) / 4.0), 122 + Align2::CENTER_CENTER, 123 + format!("{dist:.1} px"), 124 + FontId::monospace(6.0), 125 + text_color, 126 + ); 127 + } 128 + 129 + let c = rect.center(); 130 + let c_from = 2.0; 131 + let c_to = 5.0; 132 + 133 + let r = |p| painter.round_pos_to_pixels(p); 134 + 135 + painter.line_segment([r(c - vec2(c_from, 0.0)), r(c - vec2(c_to, 0.0))], stroke); 136 + painter.line_segment([r(c + vec2(c_from, 0.0)), r(c + vec2(c_to, 0.0))], stroke); 137 + painter.line_segment([r(c - vec2(0.0, c_from)), r(c - vec2(0.0, c_to))], stroke); 138 + painter.line_segment([r(c + vec2(0.0, c_from)), r(c + vec2(0.0, c_to))], stroke); 139 + 140 + let mut text = format!( 141 + "pos: x:{:.1} y:{:.1}\nchunks searched: {}\nchunk size: {}\norbs found: {}\n", 142 + pos.x, 143 + pos.y, 144 + self.orb_searcher.searched_chunks(), 145 + self.orb_searcher.chunk_size(), 146 + self.orb_searcher.known_orbs().len(), 147 + ); 148 + for orb in self.orb_searcher.known_orbs() { 149 + writeln!(&mut text, " ({: >5.0}, {: >5.0})", orb.x, orb.y).unwrap(); 150 + } 151 + 152 + painter.text( 153 + rect.right_top() + vec2(-5.0, 5.0), 154 + Align2::RIGHT_TOP, 155 + text, 156 + FontId::monospace(6.0), 157 + ui.style().visuals.weak_text_color(), 158 + ); 159 + 160 + let diameter = 25.0; 161 + let offset = 10.0; 162 + 163 + let radius = diameter / 2.0; 164 + let circle_pos = rect.left_bottom() + vec2(radius + offset, -radius - offset); 165 + 166 + if let Some(orb) = self.orb_searcher.known_orbs().first() { 167 + if pos.x.round() == orb.x.round() && pos.y.round() == orb.y.round() { 168 + painter.circle(circle_pos, radius, Color32::from_rgb(40, 255, 40), stroke); 169 + } else { 170 + painter.circle_stroke(circle_pos, radius, stroke); 171 + let dir = *orb - pos; 172 + let len = dir.length(); 173 + let arrow = dir * (diameter - 10.0) / len; 174 + painter.arrow(circle_pos - arrow / 2.0, arrow, stroke); 175 + 176 + painter.text( 177 + circle_pos + vec2(radius + offset, 0.0), 178 + Align2::LEFT_CENTER, 179 + format!("{len:.1} px"), 180 + FontId::monospace(8.0), 181 + text_color, 182 + ); 183 + } 184 + } else { 185 + painter.circle_stroke(circle_pos, radius, stroke); 186 + } 187 + }); 188 + } 189 + }
+301
src/tools/process_panel.rs
··· 1 + use anyhow::Result; 2 + use derive_more::Debug; 3 + use eframe::egui::{ 4 + text::LayoutJob, ComboBox, Context, Grid, Hyperlink, RichText, TextFormat, TextStyle, Ui, 5 + }; 6 + use noita_utility_box::{ 7 + memory::{ 8 + exe_image::{PeHeader, ReadImageError}, 9 + ProcessRef, 10 + }, 11 + noita::Noita, 12 + }; 13 + use smart_default::SmartDefault; 14 + use sysinfo::{ProcessRefreshKind, ProcessesToUpdate, System, UpdateKind}; 15 + use thiserror::Error; 16 + 17 + use crate::{ 18 + app::AppState, 19 + tools::settings::{Interval, Timer}, 20 + util::persist, 21 + }; 22 + 23 + #[derive(Debug)] 24 + pub struct NoitaData { 25 + pid: sysinfo::Pid, 26 + exe_name: Option<String>, 27 + timestamp: u32, 28 + 29 + noita: Noita, 30 + } 31 + 32 + #[derive(Error, Debug)] 33 + enum NoitaError { 34 + #[error("Not Noita\nExport exe name: {name:?}, not wizard_physics.exe")] 35 + NotNoita { name: String }, 36 + #[error("Unmapped Noita version (timestamp 0x{:x})", header.timestamp())] 37 + Unmapped { 38 + #[debug(skip)] 39 + proc: ProcessRef, 40 + header: PeHeader, 41 + }, 42 + #[error(transparent)] 43 + BadProcess(#[from] ReadImageError), 44 + #[error(transparent)] 45 + Io(#[from] std::io::Error), 46 + } 47 + 48 + impl NoitaData { 49 + fn connect( 50 + pid: sysinfo::Pid, 51 + exe_name: Option<String>, 52 + state: &AppState, 53 + ) -> Result<Self, NoitaError> { 54 + let proc = pid.as_u32().try_into()?; 55 + let header = PeHeader::read(&proc)?; 56 + 57 + if state.settings.check_export_name { 58 + let export_name = header.export_name(); 59 + if export_name != b"wizard_physics.exe\0" { 60 + let name = String::from_utf8_lossy(&export_name[..export_name.len() - 1]); 61 + return Err(NoitaError::NotNoita { name: name.into() }); 62 + } 63 + } 64 + 65 + let timestamp = header.timestamp(); 66 + 67 + let Some(address_map) = state.address_maps.get(timestamp) else { 68 + return Err(NoitaError::Unmapped { proc, header }); 69 + }; 70 + 71 + let noita = Noita::new(proc, address_map.as_noita_globals()); 72 + 73 + Ok(Self { 74 + pid, 75 + exe_name, 76 + timestamp, 77 + noita, 78 + }) 79 + } 80 + } 81 + 82 + #[derive(Debug, SmartDefault)] 83 + pub struct ProcessPanel { 84 + #[default(true)] 85 + look_for_noita: bool, 86 + 87 + #[default(System::new())] 88 + system_info: System, 89 + #[default(Timer::new(Interval::NoitaSearch))] 90 + update_timer: Timer, 91 + 92 + #[default(Ok(None))] 93 + noita: Result<Option<NoitaData>, NoitaError>, 94 + selected_process: Option<(sysinfo::Pid, Option<String>)>, 95 + } 96 + 97 + persist!(ProcessPanel { 98 + look_for_noita: bool, 99 + }); 100 + 101 + impl ProcessPanel { 102 + fn set_noita( 103 + &mut self, 104 + ctx: &Context, 105 + state: &mut AppState, 106 + noita: Result<Option<NoitaData>, NoitaError>, 107 + ) { 108 + // update the global handle to be used by things 109 + if let Ok(Some(ref data)) = noita { 110 + state.noita = Some(data.noita.clone()); 111 + } else { 112 + state.noita = None; 113 + } 114 + self.noita = noita; 115 + self.selected_process = None; 116 + ctx.request_repaint(); 117 + } 118 + 119 + fn processes_box(&mut self, ui: &mut Ui, state: &mut AppState) { 120 + let mut combo = ComboBox::from_id_salt("processes").height(400.0); 121 + 122 + if let Some((pid, exe)) = &self.selected_process { 123 + combo = combo.selected_text(process_label(ui, *pid, exe.as_deref())); 124 + } else { 125 + combo = combo.selected_text(RichText::new("Select process").italics()); 126 + } 127 + 128 + let response = combo.show_ui(ui, |ui| { 129 + let mut processes = self 130 + .system_info 131 + .processes() 132 + .iter() 133 + .filter(|(_, p)| p.thread_kind().is_none()) 134 + .collect::<Vec<_>>(); 135 + 136 + processes.sort_unstable_by_key(|(pid, _)| *pid); 137 + 138 + for (pid, p) in processes { 139 + let exe = p 140 + .exe() 141 + .and_then(|p| p.file_name().map(|f| f.to_string_lossy().into_owned())); 142 + let label = process_label(ui, *pid, exe.as_deref()); 143 + ui.selectable_value(&mut self.selected_process, Some((*pid, exe)), label); 144 + } 145 + }); 146 + 147 + if response.response.clicked() { 148 + self.system_info.refresh_processes_specifics( 149 + ProcessesToUpdate::All, 150 + ProcessRefreshKind::new().with_exe(UpdateKind::OnlyIfNotSet), 151 + ); 152 + } 153 + 154 + if let Some((pid, exe)) = self.selected_process.clone() { 155 + self.set_noita( 156 + ui.ctx(), 157 + state, 158 + NoitaData::connect(pid, exe, state).map(Some), 159 + ); 160 + } 161 + } 162 + 163 + pub fn update(&mut self, ctx: &Context, state: &mut AppState) { 164 + let Ok(noita) = &self.noita else { 165 + return; 166 + }; 167 + if (noita.is_none() && !self.look_for_noita) || !self.update_timer.go(ctx, state) { 168 + return; 169 + } 170 + 171 + // Has to be all because either we don't have noita and we're looking 172 + // for it or we have it, but we want to check if it's still there, for 173 + // which refresh all is required 174 + self.system_info.refresh_processes_specifics( 175 + ProcessesToUpdate::All, 176 + ProcessRefreshKind::new().with_exe(UpdateKind::OnlyIfNotSet), 177 + ); 178 + 179 + if let Some(noita) = noita { 180 + // check that we still have it 181 + if self.system_info.process(noita.pid).is_none() { 182 + self.set_noita(ctx, state, Ok(None)); 183 + return; 184 + } 185 + 186 + state.seed = noita.noita.read_seed().ok().flatten(); 187 + 188 + return; 189 + } 190 + 191 + // no noita and we're looking for it 192 + 193 + let Some(p) = self 194 + .system_info 195 + .processes_by_exact_name("noita.exe".as_ref()) 196 + .find(|p| p.thread_kind().is_none()) 197 + else { 198 + return; 199 + }; 200 + let exe = p 201 + .exe() 202 + .and_then(|p| p.file_name().map(|f| f.to_string_lossy().into_owned())); 203 + 204 + self.set_noita( 205 + ctx, 206 + state, 207 + NoitaData::connect(p.pid(), exe, state).map(Some), 208 + ); 209 + } 210 + 211 + pub fn ui(&mut self, ui: &mut Ui, state: &mut AppState) { 212 + ui.heading("Noita"); 213 + ui.separator(); 214 + 215 + match &self.noita { 216 + Err(e) => { 217 + ui.label(RichText::new(e.to_string()).color(ui.style().visuals.error_fg_color)); 218 + 219 + if let NoitaError::Unmapped { proc, header } = e { 220 + if ui.button("Run auto-discovery").clicked() { 221 + if let Err(e) = state.address_maps.discover(proc, header) { 222 + self.set_noita(ui.ctx(), state, Err(e.into())) 223 + } else { 224 + self.set_noita(ui.ctx(), state, Ok(None)) 225 + } 226 + } 227 + if !self.look_for_noita { 228 + self.processes_box(ui, state); 229 + } 230 + } else if self.look_for_noita { 231 + self.set_noita(ui.ctx(), state, Ok(None)); 232 + } else { 233 + self.processes_box(ui, state); 234 + } 235 + } 236 + Ok(None) => { 237 + if self.look_for_noita { 238 + ui.label("Noita process not found"); 239 + } else { 240 + self.processes_box(ui, state); 241 + } 242 + } 243 + Ok(Some(noita)) => { 244 + Grid::new("noita").show(ui, |ui| { 245 + ui.label("Process:"); 246 + ui.label(process_label(ui, noita.pid, noita.exe_name.as_deref())); 247 + ui.end_row(); 248 + 249 + ui.label("Version:"); 250 + ui.label(format!("0x{:x}", noita.timestamp)); 251 + ui.end_row(); 252 + 253 + if let Some(s) = &state.seed { 254 + ui.label("Seed:"); 255 + let seed = s.world_seed.to_string(); 256 + let link = format!("https://noitool.com/info?seed={seed}"); 257 + 258 + ui.add(Hyperlink::from_label_and_url(seed, link).open_in_new_tab(true)) 259 + .on_hover_text("Open the seed in noitool"); 260 + 261 + ui.end_row(); 262 + 263 + ui.label("NG+ count:"); 264 + ui.label(s.ng_count.to_string()); 265 + ui.end_row(); 266 + } 267 + }); 268 + 269 + if !self.look_for_noita && ui.button("Disconnect").clicked() { 270 + self.set_noita(ui.ctx(), state, Ok(None)); 271 + } 272 + } 273 + } 274 + 275 + ui.checkbox(&mut self.look_for_noita, "Auto-detect Noita process"); 276 + } 277 + } 278 + 279 + fn process_label(ui: &Ui, pid: sysinfo::Pid, fname: Option<&str>) -> LayoutJob { 280 + let mut job = LayoutJob::default(); 281 + job.append( 282 + &pid.to_string(), 283 + 0.0, 284 + TextFormat { 285 + font_id: TextStyle::Monospace.resolve(ui.style()), 286 + ..Default::default() 287 + }, 288 + ); 289 + if let Some(name) = fname { 290 + job.append(": ", 0.0, TextFormat::default()); 291 + job.append( 292 + name, 293 + 0.0, 294 + TextFormat { 295 + italics: true, 296 + ..Default::default() 297 + }, 298 + ) 299 + } 300 + job 301 + }
+111
src/tools/settings.rs
··· 1 + use std::time::Instant; 2 + 3 + use eframe::egui::{Checkbox, CollapsingHeader, Context, DragValue, Grid, ScrollArea}; 4 + use serde::{Deserialize, Serialize}; 5 + use smart_default::SmartDefault; 6 + use strum::{EnumCount, EnumIter, EnumMessage, IntoEnumIterator}; 7 + 8 + use crate::app::AppState; 9 + 10 + #[derive(Debug, Serialize, Deserialize, Clone, SmartDefault)] 11 + pub struct Settings { 12 + #[default([0.5; Interval::COUNT])] 13 + intervals: [f32; Interval::COUNT], 14 + #[default(true)] 15 + pub check_export_name: bool, 16 + #[default(true)] 17 + pub pipette: bool, 18 + #[default(true)] 19 + pub pipette_checklist: bool, 20 + } 21 + 22 + #[derive(Debug, Clone, Copy, EnumCount, EnumIter, EnumMessage)] 23 + #[repr(usize)] 24 + pub enum Interval { 25 + /// Noita process search interval 26 + NoitaSearch, 27 + /// Live stats polling 28 + LiveStats, 29 + } 30 + 31 + #[derive(Debug)] 32 + pub struct Timer { 33 + interval: Interval, 34 + last_tick: Instant, 35 + } 36 + 37 + impl Timer { 38 + pub fn new(interval: Interval) -> Self { 39 + Self { 40 + interval, 41 + last_tick: Instant::now(), 42 + } 43 + } 44 + 45 + pub fn interval(&self, state: &AppState) -> f32 { 46 + state.settings.intervals[self.interval as usize] 47 + } 48 + 49 + pub fn go(&mut self, ctx: &Context, state: &AppState) -> bool { 50 + let interval = self.interval(state); 51 + ctx.request_repaint_after_secs(interval); 52 + if self.last_tick.elapsed().as_secs_f32() >= interval { 53 + self.last_tick = Instant::now(); 54 + return true; 55 + } 56 + false 57 + } 58 + } 59 + 60 + impl Settings { 61 + pub fn ui(&mut self, ui: &mut eframe::egui::Ui) { 62 + ui.heading("Settings"); 63 + ui.separator(); 64 + 65 + ScrollArea::vertical().show(ui, |ui| { 66 + ui.label("There will be a few more settings, and the intervals are a bit wonky atm."); 67 + ui.label("You can play with egui for now I guess "); 68 + 69 + ui.separator(); 70 + 71 + Grid::new("settings").show(ui, |ui| { 72 + for interval in Interval::iter() { 73 + let Some(doc) = interval.get_documentation() else { 74 + continue; 75 + }; 76 + ui.label(doc); 77 + ui.add( 78 + DragValue::new(&mut self.intervals[interval as usize]) 79 + .range(0.0..=60.0) 80 + .speed(0.02) 81 + .suffix(" s"), 82 + ); 83 + ui.end_row(); 84 + } 85 + 86 + ui.checkbox(&mut self.check_export_name, "Check export name") 87 + .on_hover_text("When detecting noita, check that the executable export name is 'wizard_physics.exe'"); 88 + ui.end_row(); 89 + 90 + ui.checkbox(&mut self.pipette, "Enable Material Pipette") 91 + .on_hover_text("The pipette is obviously very targeted at Fury and most people wont need it"); 92 + ui.end_row(); 93 + 94 + ui.add_enabled(self.pipette, Checkbox::new(&mut self.pipette_checklist, "Add all materials checklist")) 95 + .on_hover_text("Adds a checklist of all materials to the material pipette"); 96 + ui.end_row(); 97 + }); 98 + 99 + CollapsingHeader::new("egui").show(ui, |ui| { 100 + let prev_options = ui.ctx().options(|o| o.clone()); 101 + let mut options = prev_options.clone(); 102 + 103 + options.ui(ui); 104 + 105 + if options != prev_options { 106 + ui.ctx().options_mut(move |o| *o = options); 107 + } 108 + }); 109 + }); 110 + } 111 + }
+135
src/util.rs
··· 1 + use derive_more::Debug; 2 + use std::{borrow::Borrow, future::Future}; 3 + use tokio::sync::oneshot::{error::TryRecvError, Receiver}; 4 + 5 + /// A variant of poll-promise that can be used as storage. Uses tokio. 6 + #[derive(Debug)] 7 + pub enum Promise<T> { 8 + Pending(Receiver<T>), 9 + Done(T), 10 + Taken, 11 + } 12 + 13 + // this could happen when the tokio runtime shuts down ig 14 + fn no_sender() -> ! { 15 + panic!("Promise sender dropped"); 16 + } 17 + 18 + impl<T> Promise<T> { 19 + pub fn spawn<F>(future: F) -> Self 20 + where 21 + F: Future<Output = T> + Send + 'static, 22 + T: Send + 'static, 23 + { 24 + // we use tokio and not pollster or something because 25 + // obws brings (and depends on) tokio anyway 26 + let (tx, rx) = tokio::sync::oneshot::channel(); 27 + tokio::spawn(async { tx.send(future.await) }); 28 + Self::Pending(rx) 29 + } 30 + 31 + /// Borrow the value if the promise is complete, otherwise return None. 32 + /// Panics if the promise value was taken. 33 + pub fn poll<Q>(&mut self) -> Option<&Q> 34 + where 35 + Q: ?Sized, 36 + T: Borrow<Q>, 37 + { 38 + match self { 39 + Promise::Pending(rx) => match rx.try_recv() { 40 + Ok(t) => { 41 + *self = Promise::Done(t); 42 + // recurse into the outer match lol 43 + self.poll() 44 + } 45 + Err(TryRecvError::Empty) => None, 46 + Err(TryRecvError::Closed) => no_sender(), 47 + }, 48 + Promise::Done(ref t) => Some(t.borrow()), 49 + Promise::Taken => panic!("Promise was taken"), 50 + } 51 + } 52 + 53 + /// A shorthand for `self.poll().unwrap_or_default()`. 54 + pub fn poll_or_default<Q>(&mut self) -> &Q 55 + where 56 + Q: ?Sized, 57 + T: Borrow<Q> + Default, 58 + for<'a> &'a Q: Default, 59 + { 60 + self.poll().unwrap_or_default() 61 + } 62 + 63 + /// Take the value if the promise is complete, otherwise return None. 64 + /// Subsequent calls to `poll_take` or `poll` will panic. 65 + pub fn poll_take(&mut self) -> Option<T> { 66 + match std::mem::replace(self, Promise::Taken) { 67 + Promise::Pending(mut rx) => match rx.try_recv() { 68 + Ok(t) => Some(t), 69 + Err(TryRecvError::Empty) => { 70 + *self = Promise::Pending(rx); 71 + None 72 + } 73 + Err(TryRecvError::Closed) => no_sender(), 74 + }, 75 + Promise::Done(t) => Some(t), 76 + Promise::Taken => panic!("Promise was already taken"), 77 + } 78 + } 79 + 80 + pub fn is_taken(&self) -> bool { 81 + matches!(self, Promise::Taken) 82 + } 83 + } 84 + 85 + impl<T: Default> Default for Promise<T> { 86 + fn default() -> Self { 87 + Self::Done(Default::default()) 88 + } 89 + } 90 + 91 + /// Implement [serde::Serialize] and [serde::Deserialize] for a struct, only 92 + /// writing/reading the specified fields and using Default when reading. 93 + #[allow(unused_macros)] // false positive?. it's definitely used 94 + macro_rules! persist { 95 + (__ref_of $lt:lifetime, String) => { 96 + &$lt str 97 + }; 98 + (__ref_of $lt:lifetime,$t:ty) => { 99 + &$lt $t 100 + }; 101 + ($t:ident { $($field:ident: $field_t:ty),* $(,)? }) => { 102 + impl ::serde::Serialize for $t { 103 + fn serialize<S: ::serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { 104 + 105 + #[derive(::serde::Serialize)] 106 + struct Persisted<'a> { 107 + $($field: persist!(__ref_of 'a, $field_t),)* 108 + #[serde(skip)] 109 + _phantom: ::std::marker::PhantomData<&'a ()>, 110 + } 111 + 112 + Persisted { $($field: &self.$field,)* _phantom: ::std::marker::PhantomData }.serialize(serializer) 113 + } 114 + } 115 + impl<'de> ::serde::Deserialize<'de> for $t { 116 + fn deserialize<D: ::serde::Deserializer<'de>>( 117 + deserializer: D, 118 + ) -> Result<Self, D::Error> { 119 + #[derive(::serde::Deserialize)] 120 + struct Persisted { 121 + $($field: $field_t,)* 122 + } 123 + let _persisted = Persisted::deserialize(deserializer)?; 124 + #[allow(clippy::needless_update)] 125 + ::std::result::Result::Ok($t { 126 + $($field: _persisted.$field,)* 127 + ..Default::default() 128 + }) 129 + } 130 + } 131 + }; 132 + } 133 + 134 + #[allow(unused_imports)] // same as above 135 + pub(crate) use persist;