diff --git a/.gitignore b/.gitignore index abe84e2..d0d18e6 100644 --- a/.gitignore +++ b/.gitignore @@ -12,8 +12,10 @@ # Added by Idea ItelliJ .idea *.iml +Folder.DotSettings.user # Misc imgui.ini ROM log +debug diff --git a/Cargo.lock b/Cargo.lock index b39cd2e..ac11313 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "ab_glyph" -version = "0.2.20" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe21446ad43aa56417a767f3e2f3d7c4ca522904de1dd640529a76e9c5c3b33c" +checksum = "8e08104bebc65a46f8bc7aa733d39ea6874bfa7156f41a46b805785e3af1587d" dependencies = [ "ab_glyph_rasterizer", "owned_ttf_parser", @@ -20,75 +20,76 @@ checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" [[package]] name = "accesskit" -version = "0.9.0" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4803cf8c252f374ae6bfbb341e49e5a37f7601f2ce74a105927a663eba952c67" +checksum = "74a4b14f3d99c1255dcba8f45621ab1a2e7540a0009652d33989005a4d0bfc6b" +dependencies = [ + "enumn", + "serde", +] [[package]] name = "accesskit_consumer" -version = "0.13.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee8cf1202a4f94d31837f1902ab0a75c77b65bf59719e093703abe83efd74ec" +checksum = "8c17cca53c09fbd7288667b22a201274b9becaa27f0b91bf52a526db95de45e6" dependencies = [ "accesskit", - "parking_lot", ] [[package]] name = "accesskit_macos" -version = "0.5.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10be25f2b27bc33aa1647072e86b948b41596f1af1ae43a2b4b9be5d2011cbda" +checksum = "cd3b6ae1eabbfbced10e840fd3fce8a93ae84f174b3e4ba892ab7bcb42e477a7" dependencies = [ "accesskit", "accesskit_consumer", - "objc2", + "objc2 0.3.0-beta.3.patch-leaks.3", "once_cell", - "parking_lot", ] [[package]] name = "accesskit_unix" -version = "0.2.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630e7ee8f93c6246478bf0df6760db899b28d9ad54353a5f2d3157138ba817fc" +checksum = "09f46c18d99ba61ad7123dd13eeb0c104436ab6af1df6a1cd8c11054ed394a08" dependencies = [ "accesskit", "accesskit_consumer", "async-channel", + "async-once-cell", "atspi", - "futures-lite", - "parking_lot", + "futures-lite 1.13.0", + "once_cell", "serde", - "zbus", + "zbus 3.15.1", ] [[package]] name = "accesskit_windows" -version = "0.12.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13c462fabdd950ef14308a9390b07fa2e2e3aabccba1f3ea36ea2231bb942ab" +checksum = "afcae27ec0974fc7c3b0b318783be89fd1b2e66dd702179fe600166a38ff4a0b" dependencies = [ "accesskit", "accesskit_consumer", - "arrayvec", "once_cell", - "parking_lot", "paste", - "windows 0.42.0", + "static_assertions", + "windows 0.48.0", ] [[package]] name = "accesskit_winit" -version = "0.10.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17727888757ec027ec221db33070e226ee07df44425b583bc67684204d35eff9" +checksum = "5284218aca17d9e150164428a0ebc7b955f70e3a9a78b4c20894513aabf98a67" dependencies = [ "accesskit", "accesskit_macos", "accesskit_unix", "accesskit_windows", - "parking_lot", "winit", ] @@ -100,40 +101,52 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.8.3" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", + "getrandom", "once_cell", + "serde", "version_check", + "zerocopy", ] [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + [[package]] name = "android-activity" -version = "0.4.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c77a0045eda8b888c76ea473c2b0515ba6f471d318f8927c5c72240937035a6" +checksum = "ee91c0c2905bae44f84bfa4e044536541df26b7703fd0888deeb9060fcc44289" dependencies = [ "android-properties", - "bitflags", + "bitflags 2.5.0", "cc", + "cesu8", + "jni", "jni-sys", "libc", "log", "ndk", "ndk-context", "ndk-sys", - "num_enum 0.5.11", + "num_enum", + "thiserror", ] [[package]] @@ -142,6 +155,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -153,33 +172,31 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.70" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" [[package]] name = "arboard" -version = "3.2.0" +version = "3.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6041616acea41d67c4a984709ddab1587fd0b10efe5cc563fee954d2f011854" +checksum = "a2041f1943049c7978768d84e6d0fd95de98b76d6c4727b09e78ec253d29fa58" dependencies = [ "clipboard-win", "log", "objc", "objc-foundation", "objc_id", - "once_cell", "parking_lot", "thiserror", - "winapi", "x11rb", ] [[package]] name = "arc-swap" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" [[package]] name = "arrayref" @@ -189,9 +206,42 @@ checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" [[package]] name = "arrayvec" -version = "0.7.2" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "as-raw-xcb-connection" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" + +[[package]] +name = "ash" +version = "0.37.3+1.3.251" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a" +dependencies = [ + "libloading 0.7.4", +] + +[[package]] +name = "ashpd" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd884d7c72877a94102c3715f3b1cd09ff4fac28221add3e57cfbe25c236d093" +dependencies = [ + "async-fs 2.1.1", + "async-net", + "enumflags2", + "futures-channel", + "futures-util", + "rand", + "serde", + "serde_repr", + "url", + "zbus 4.1.2", +] [[package]] name = "async-broadcast" @@ -199,32 +249,46 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b" dependencies = [ - "event-listener", + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-broadcast" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258b52a1aa741b9f09783b2d86cf0aeeb617bbf847f6933340a39644227acbdb" +dependencies = [ + "event-listener 5.3.0", + "event-listener-strategy 0.5.1", "futures-core", + "pin-project-lite", ] [[package]] name = "async-channel" -version = "1.8.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" +checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" dependencies = [ "concurrent-queue", - "event-listener", + "event-listener 5.3.0", + "event-listener-strategy 0.5.1", "futures-core", + "pin-project-lite", ] [[package]] name = "async-executor" -version = "1.5.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" +checksum = "5f98c37cf288e302c16ef6c8472aad1e034c6c84ce5ea7b8101c98eb4a802fee" dependencies = [ - "async-lock", + "async-lock 3.3.0", "async-task", "concurrent-queue", - "fastrand", - "futures-lite", + "fastrand 2.0.2", + "futures-lite 2.3.0", "slab", ] @@ -234,10 +298,21 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" dependencies = [ - "async-lock", + "async-lock 2.8.0", "autocfg", "blocking", - "futures-lite", + "futures-lite 1.13.0", +] + +[[package]] +name = "async-fs" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc19683171f287921f2405677dd2ed2549c3b3bda697a563ebc3a121ace2aba1" +dependencies = [ + "async-lock 3.3.0", + "blocking", + "futures-lite 2.3.0", ] [[package]] @@ -246,118 +321,233 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ - "async-lock", + "async-lock 2.8.0", "autocfg", "cfg-if", "concurrent-queue", - "futures-lite", + "futures-lite 1.13.0", "log", "parking", - "polling", - "rustix", + "polling 2.8.0", + "rustix 0.37.27", "slab", "socket2", "waker-fn", ] +[[package]] +name = "async-io" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884" +dependencies = [ + "async-lock 3.3.0", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite 2.3.0", + "parking", + "polling 3.6.0", + "rustix 0.38.32", + "slab", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "async-lock" -version = "2.7.0" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener 2.5.3", +] + +[[package]] +name = "async-lock" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" +dependencies = [ + "event-listener 4.0.3", + "event-listener-strategy 0.4.0", + "pin-project-lite", +] + +[[package]] +name = "async-net" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" +dependencies = [ + "async-io 2.3.2", + "blocking", + "futures-lite 2.3.0", +] + +[[package]] +name = "async-once-cell" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" +checksum = "9338790e78aa95a416786ec8389546c4b6a1dfc3dc36071ed9518a9413a542eb" + +[[package]] +name = "async-process" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" +dependencies = [ + "async-io 1.13.0", + "async-lock 2.8.0", + "async-signal", + "blocking", + "cfg-if", + "event-listener 3.1.0", + "futures-lite 1.13.0", + "rustix 0.38.32", + "windows-sys 0.48.0", +] + +[[package]] +name = "async-process" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d999d925640d51b662b7b4e404224dd81de70f4aa4a199383c2c5e5b86885fa3" dependencies = [ - "event-listener", + "async-channel", + "async-io 2.3.2", + "async-lock 3.3.0", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener 5.3.0", + "futures-lite 2.3.0", + "rustix 0.38.32", + "tracing", + "windows-sys 0.52.0", ] [[package]] name = "async-recursion" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" +checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.58", +] + +[[package]] +name = "async-signal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" +dependencies = [ + "async-io 2.3.2", + "async-lock 2.8.0", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 0.38.32", + "signal-hook-registry", + "slab", + "windows-sys 0.48.0", ] [[package]] name = "async-task" -version = "4.4.0" +version = "4.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" +checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" [[package]] name = "async-trait" -version = "0.1.68" +version = "0.1.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.58", ] [[package]] -name = "atk-sys" -version = "0.16.0" +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "atspi" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ad703eb64dc058024f0e57ccfa069e15a413b98dbd50a1a950e743b7f11148" +checksum = "6059f350ab6f593ea00727b334265c4dfc7fd442ee32d264794bd9bdc68e87ca" dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", + "atspi-common", + "atspi-connection", + "atspi-proxies", ] [[package]] -name = "atomic-waker" -version = "1.1.1" +name = "atspi-common" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" +checksum = "92af95f966d2431f962bc632c2e68eda7777330158bf640c4af4249349b2cdf5" +dependencies = [ + "enumflags2", + "serde", + "static_assertions", + "zbus 3.15.1", + "zbus_names 2.6.1", + "zvariant 3.15.1", +] [[package]] -name = "atomic_refcell" -version = "0.1.9" +name = "atspi-connection" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "857253367827bd9d0fd973f0ef15506a96e79e41b0ad7aa691203a4e3214f6c8" +checksum = "a0c65e7d70f86d4c0e3b2d585d9bf3f979f0b19d635a336725a88d279f76b939" +dependencies = [ + "atspi-common", + "atspi-proxies", + "futures-lite 1.13.0", + "zbus 3.15.1", +] [[package]] -name = "atspi" -version = "0.8.7" +name = "atspi-proxies" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab84c09a770065868da0d713f1f4b35af85d96530a868f1c1a6c249178379187" +checksum = "6495661273703e7a229356dcbe8c8f38223d697aacfaf0e13590a9ac9977bb52" dependencies = [ - "async-recursion", - "async-trait", - "atspi-macros", - "enumflags2", - "futures-lite", + "atspi-common", "serde", - "tracing", - "zbus", - "zbus_names", + "zbus 3.15.1", ] [[package]] -name = "atspi-macros" -version = "0.1.4" +name = "autocfg" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" + +[[package]] +name = "bit-set" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3ebc5a6f61f6996eca56a4cece7b3fe7da3b86f0473c7b71ab44e229f3acce4" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" dependencies = [ - "proc-macro2", - "quote", - "serde", - "syn 1.0.109", - "zbus", - "zbus_names", - "zvariant", + "bit-vec", ] [[package]] -name = "autocfg" -version = "1.1.0" +name = "bit-vec" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" [[package]] name = "bitflags" @@ -365,6 +555,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + [[package]] name = "block" version = "0.1.6" @@ -386,7 +582,16 @@ version = "0.1.0-beta.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa55741ee90902547802152aaf3f8e5248aab7e21468089560d4c8840561146" dependencies = [ - "objc-sys", + "objc-sys 0.2.0-beta.2", +] + +[[package]] +name = "block-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae85a0696e7ea3b835a453750bf002770776609115e6d25c6d2ff28a8200f7e7" +dependencies = [ + "objc-sys 0.3.2", ] [[package]] @@ -395,93 +600,108 @@ version = "0.2.0-alpha.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8dd9e63c1744f755c2f60332b88de39d341e5e86239014ad839bd71c106dec42" dependencies = [ - "block-sys", - "objc2-encode", + "block-sys 0.1.0-beta.1", + "objc2-encode 2.0.0-pre.2", +] + +[[package]] +name = "block2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b55663a85f33501257357e6421bb33e769d5c9ffb5ba0921c975a123e35e68" +dependencies = [ + "block-sys 0.2.1", + "objc2 0.4.1", ] [[package]] name = "blocking" -version = "1.3.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" +checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" dependencies = [ "async-channel", - "async-lock", + "async-lock 3.3.0", "async-task", - "atomic-waker", - "fastrand", - "futures-lite", - "log", + "fastrand 2.0.2", + "futures-io", + "futures-lite 2.3.0", + "piper", + "tracing", ] [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.13.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" +checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.4.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdde5c9cd29ebd706ce1b35600920a33550e402fc998a2e53ad3b42c3c47a192" +checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.58", ] [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.4.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] -name = "cairo-sys-rs" -version = "0.16.3" +name = "calloop" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c48f4af05fabdcfa9658178e1326efa061853f040ce7d72e33af6885196f421" +checksum = "fba7adb4dd5aa98e5553510223000e7148f621165ec5f9acd7113f6ca4995298" dependencies = [ - "libc", - "system-deps", + "bitflags 2.5.0", + "log", + "polling 3.6.0", + "rustix 0.38.32", + "slab", + "thiserror", ] [[package]] -name = "calloop" -version = "0.10.5" +name = "calloop-wayland-source" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a59225be45a478d772ce015d9743e49e92798ece9e34eda9a6aa2a6a7f40192" +checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02" dependencies = [ - "log", - "nix 0.25.1", - "slotmap", - "thiserror", - "vec_map", + "calloop", + "rustix 0.38.32", + "wayland-backend", + "wayland-client", ] [[package]] name = "cc" -version = "1.0.79" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "2678b2e3449475e95b0aa6f9b506a28e61b3dc8996592b983695e8ebb58a8b41" dependencies = [ "jobserver", + "libc", ] [[package]] @@ -490,15 +710,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" -[[package]] -name = "cfg-expr" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35b255461940a32985c627ce82900867c61db1659764d3675ea81963f72a4c6" -dependencies = [ - "smallvec", -] - [[package]] name = "cfg-if" version = "1.0.0" @@ -522,64 +733,126 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.24" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" dependencies = [ + "android-tzdata", "iana-time-zone", - "js-sys", - "num-integer", "num-traits", - "time", - "wasm-bindgen", - "winapi", + "windows-targets 0.52.4", ] [[package]] name = "clipboard-win" -version = "4.5.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362" +checksum = "d517d4b86184dbb111d3556a10f1c8a04da7428d2987bf1081602bf11c3aa9ee" dependencies = [ "error-code", - "str-buf", - "winapi", ] [[package]] -name = "codespan-reporting" -version = "0.11.1" +name = "cocoa" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" dependencies = [ - "termcolor", - "unicode-width", + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types", + "libc", + "objc", ] [[package]] -name = "combine" -version = "4.6.6" +name = "cocoa-foundation" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" dependencies = [ - "bytes", - "memchr", + "bitflags 1.3.2", + "block", + "core-foundation", + "core-graphics-types", + "libc", + "objc", ] [[package]] -name = "concurrent-queue" -version = "2.2.0" +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "com" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e17887fd17353b65b1b2ef1c526c83e26cd72e74f598a8dc1bee13a48f3d9f6" +dependencies = [ + "com_macros", +] + +[[package]] +name = "com_macros" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" +checksum = "d375883580a668c7481ea6631fc1a8863e33cc335bf56bfad8d7e6d4b04b13a5" +dependencies = [ + "com_macros_support", + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "com_macros_support" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad899a1087a9296d5644792d7cb72b8e34c1bec8e7d4fbc002230169a6e8710c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" dependencies = [ "crossbeam-utils", ] [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -587,17 +860,17 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "core-graphics" -version = "0.22.3" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-graphics-types", "foreign-types", @@ -606,42 +879,38 @@ dependencies = [ [[package]] name = "core-graphics-types" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", - "foreign-types", "libc", ] [[package]] name = "cpufeatures" -version = "0.2.6" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if", ] [[package]] name = "crossbeam-utils" -version = "0.8.15" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" -dependencies = [ - "cfg-if", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crypto-common" @@ -654,48 +923,10 @@ dependencies = [ ] [[package]] -name = "cxx" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn 2.0.15", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.94" +name = "cursor-icon" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] +checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" [[package]] name = "derivative" @@ -716,54 +947,43 @@ checksum = "3c877555693c14d2f84191cfd3ad8582790fc52b5e2274b40b59cf5f5cea25c7" [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", ] [[package]] -name = "dirs" -version = "4.0.0" +name = "dispatch" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" -dependencies = [ - "dirs-sys", -] +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" [[package]] -name = "dirs-sys" -version = "0.3.7" +name = "dlib" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libc", - "redox_users", - "winapi", + "libloading 0.8.3", ] [[package]] -name = "dispatch" -version = "0.2.0" +name = "document-features" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" - -[[package]] -name = "dlib" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" +checksum = "ef5282ad69563b5fc40319526ba27e0e7363d552a896f0297d54f767717f9b95" dependencies = [ - "libloading", + "litrs", ] [[package]] name = "downcast-rs" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] name = "duplicate" @@ -777,133 +997,225 @@ dependencies = [ [[package]] name = "ecolor" -version = "0.21.0" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f99fe3cac305af9d6d92971af60d0f7ea4d783201ef1673571567b6699964d9" +checksum = "03cfe80b1890e1a8cdbffc6044d6872e814aaf6011835a2a5e2db0e5c5c4ef4e" + +[[package]] +name = "ecolor" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20930a432bbd57a6d55e07976089708d4893f3d556cf42a0d79e9e321fa73b10" dependencies = [ "bytemuck", + "serde", ] [[package]] name = "eframe" -version = "0.21.3" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3df3ce60931e5f2d83bab4484d1a283908534d5308cc6b0c5c22c59cd15ee7cc" +checksum = "020e2ccef6bbcec71dbc542f7eed64a5846fc3076727f5746da8fd307c91bab2" dependencies = [ "bytemuck", - "egui", + "cocoa", + "document-features", + "egui 0.27.2", + "egui-wgpu", "egui-winit", "egui_glow", "glow", "glutin", "glutin-winit", + "image", "js-sys", + "log", + "objc", + "parking_lot", "percent-encoding", - "raw-window-handle", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.0", + "static_assertions", "thiserror", - "tracing", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "web-time", + "winapi", "winit", ] [[package]] name = "egui" -version = "0.21.0" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180f595432a5b615fc6b74afef3955249b86cfea72607b40740a4cd60d5297d0" +dependencies = [ + "ahash", + "epaint 0.26.2", + "nohash-hasher", +] + +[[package]] +name = "egui" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6412a21e0bde7c0918f7fb44bbbb86b5e1f88e63c026a4e747cc7af02f76dfbe" +checksum = "584c5d1bf9a67b25778a3323af222dbe1a1feb532190e103901187f92c7fe29a" dependencies = [ "accesskit", "ahash", - "epaint", + "epaint 0.27.2", + "log", "nohash-hasher", - "tracing", + "serde", +] + +[[package]] +name = "egui-phosphor" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99eee0cd30e48fa2678a6ac3520601ada8ddb3c860405df43a094e6adbeebd40" +dependencies = [ + "egui 0.26.2", +] + +[[package]] +name = "egui-phosphor" +version = "0.5.0" +source = "git+https://github.com/Adanos020/egui-phosphor?branch=egui-0.27#02cc3b6001bcf28d592078cc921008e5980e5334" +dependencies = [ + "egui 0.27.2", +] + +[[package]] +name = "egui-wgpu" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469ff65843f88a702b731a1532b7d03b0e8e96d283e70f3a22b0e06c46cb9b37" +dependencies = [ + "bytemuck", + "document-features", + "egui 0.27.2", + "epaint 0.27.2", + "log", + "thiserror", + "type-map", + "web-time", + "wgpu", + "winit", ] [[package]] name = "egui-winit" -version = "0.21.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab43597ba41f0ce39a364ad83185594578bfd8b3409b99dbcbb01df23afc3dbb" +checksum = "2e3da0cbe020f341450c599b35b92de4af7b00abde85624fd16f09c885573609" dependencies = [ "accesskit_winit", - "android-activity", "arboard", - "egui", - "instant", + "egui 0.27.2", + "log", + "raw-window-handle 0.6.0", "smithay-clipboard", - "tracing", + "web-time", "webbrowser", "winit", ] [[package]] name = "egui_dock" -version = "0.4.2" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be7e6eb63cb936413bd2a4f54be4a9ef53a48252f25864f5f946d4954d7332bd" +checksum = "c3b8d9a54c0ed60f2670ad387c269663b4771431f090fa586906cf5f0bc586f4" dependencies = [ - "egui", + "duplicate", + "egui 0.27.2", + "paste", ] [[package]] name = "egui_extras" -version = "0.21.0" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f051342e97dfa2445107cb7d2e720617f5c840199b5cb4fe0ffcf481fcf5cce" +checksum = "1b78779f35ded1a853786c9ce0b43fe1053e10a21ea3b23ebea411805ce41593" dependencies = [ - "egui", + "egui 0.27.2", + "enum-map", + "log", + "mime_guess2", "serde", ] [[package]] name = "egui_glow" -version = "0.21.0" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8257332fb168a965b3dca81d6a344e053153773c889cabdba0b3b76f1629ae81" +checksum = "e0e5d975f3c86edc3d35b1db88bb27c15dde7c55d3b5af164968ab5ede3f44ca" dependencies = [ "bytemuck", - "egui", + "egui 0.27.2", "glow", - "memoffset 0.6.5", - "tracing", + "log", + "memoffset 0.9.1", "wasm-bindgen", "web-sys", + "winit", ] [[package]] name = "either" -version = "1.8.1" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" + +[[package]] +name = "emath" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "6916301ecf80448f786cdf3eb51d9dbdd831538732229d49119e2d4312eaaf09" [[package]] name = "emath" -version = "0.21.0" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ecd80612937e0267909d5351770fe150004e24dab93954f69ca62eecd3f77e" +checksum = "e4c3a552cfca14630702449d35f41c84a0d15963273771c6059175a803620f3f" dependencies = [ "bytemuck", + "serde", ] [[package]] -name = "enum_dispatch" -version = "0.3.11" +name = "endi" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11f36e95862220b211a6e2aa5eca09b4fa391b13cd52ceb8035a24bf65a79de2" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" + +[[package]] +name = "enum-map" +version = "2.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9" +dependencies = [ + "enum-map-derive", + "serde", +] + +[[package]] +name = "enum-map-derive" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" dependencies = [ - "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.58", ] [[package]] name = "enumflags2" -version = "0.7.6" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0044ebdf7fbb2a772e0c0233a9d3173c5cd8af8ae7078d4c5188af44ffffaa4b" +checksum = "3278c9d5fb675e0a51dabcf4c0d355f692b064171535ba72361be1528a9d8e8d" dependencies = [ "enumflags2_derive", "serde", @@ -911,67 +1223,137 @@ dependencies = [ [[package]] name = "enumflags2_derive" -version = "0.7.6" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "enumn" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d2c772ccdbdfd1967b4f5d79d17c98ebf92009fdcc838db7aa434462f600c26" +checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.58", ] [[package]] name = "epaint" -version = "0.21.0" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12e78b5c58a1f7f621f9d546add2adce20636422c9b251e29f749e8a2f713c95" +checksum = "77b9fdf617dd7f58b0c8e6e9e4a1281f730cde0831d40547da446b2bb76a47af" +dependencies = [ + "ab_glyph", + "ahash", + "ecolor 0.26.2", + "emath 0.26.2", + "nohash-hasher", + "parking_lot", +] + +[[package]] +name = "epaint" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b381f8b149657a4acf837095351839f32cd5c4aec1817fc4df84e18d76334176" dependencies = [ "ab_glyph", "ahash", - "atomic_refcell", "bytemuck", - "ecolor", - "emath", + "ecolor 0.27.2", + "emath 0.27.2", + "log", "nohash-hasher", "parking_lot", + "serde", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" -version = "0.3.1" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ - "errno-dragonfly", "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] -name = "errno-dragonfly" -version = "0.1.2" +name = "error-code" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b" + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" dependencies = [ - "cc", - "libc", + "concurrent-queue", + "parking", + "pin-project-lite", ] [[package]] -name = "error-code" -version = "2.3.1" +name = "event-listener" +version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21" +checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" dependencies = [ - "libc", - "str-buf", + "concurrent-queue", + "parking", + "pin-project-lite", ] [[package]] name = "event-listener" -version = "2.5.3" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +dependencies = [ + "event-listener 4.0.3", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "332f51cb23d20b0de8458b86580878211da09bcd4503cb579c225b3d124cabb3" +dependencies = [ + "event-listener 5.3.0", + "pin-project-lite", +] [[package]] name = "fastrand" @@ -982,23 +1364,29 @@ dependencies = [ "instant", ] +[[package]] +name = "fastrand" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" + [[package]] name = "fdeflate" -version = "0.3.0" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" dependencies = [ "simd-adler32", ] [[package]] name = "flate2" -version = "1.0.25" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", - "miniz_oxide 0.6.2", + "miniz_oxide", ] [[package]] @@ -1009,39 +1397,60 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foreign-types" -version = "0.3.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" dependencies = [ + "foreign-types-macros", "foreign-types-shared", ] +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "foreign-types-shared" -version = "0.1.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", +] + [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-lite" @@ -1049,7 +1458,7 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" dependencies = [ - "fastrand", + "fastrand 1.9.0", "futures-core", "futures-io", "memchr", @@ -1058,26 +1467,51 @@ dependencies = [ "waker-fn", ] +[[package]] +name = "futures-lite" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +dependencies = [ + "fastrand 2.0.2", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-core", "futures-io", + "futures-macro", "futures-sink", "futures-task", "memchr", @@ -1086,36 +1520,6 @@ dependencies = [ "slab", ] -[[package]] -name = "gdk-pixbuf-sys" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3092cf797a5f1210479ea38070d9ae8a5b8e9f8f1be9f32f4643c529c7d70016" -dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "gdk-sys" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76354f97a913e55b984759a997b693aa7dc71068c9e98bcce51aa167a0a5c5a" -dependencies = [ - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "pkg-config", - "system-deps", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -1128,36 +1532,23 @@ dependencies = [ [[package]] name = "gethostname" -version = "0.2.3" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" dependencies = [ "libc", - "winapi", + "windows-targets 0.48.5", ] [[package]] name = "getrandom" -version = "0.2.9" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", -] - -[[package]] -name = "gio-sys" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9b693b8e39d042a95547fc258a7b07349b1f0b48f4b2fa3108ba3c51c0b5229" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", - "winapi", + "wasi", ] [[package]] @@ -1171,21 +1562,11 @@ dependencies = [ "xml-rs", ] -[[package]] -name = "glib-sys" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61a4f46316d06bfa33a7ac22df6f0524c8be58e3db2d9ca99ccb1f357b62a65" -dependencies = [ - "libc", - "system-deps", -] - [[package]] name = "glow" -version = "0.12.1" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e007a07a24de5ecae94160f141029e9a347282cfe25d1d58d85d845cf3130f1" +checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1" dependencies = [ "js-sys", "slotmap", @@ -1195,11 +1576,11 @@ dependencies = [ [[package]] name = "glutin" -version = "0.30.7" +version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f89bab9ec7715de13d5d5402238e66f48e3a5ae636ebb45aba4013c962e2ff15" +checksum = "18fcd4ae4e86d991ad1300b8f57166e5be0c95ef1f63f3f5b827f8a164548746" dependencies = [ - "bitflags", + "bitflags 2.5.0", "cfg_aliases", "cgl", "core-foundation", @@ -1207,42 +1588,43 @@ dependencies = [ "glutin_egl_sys", "glutin_glx_sys", "glutin_wgl_sys", - "libloading", - "objc2", + "icrate", + "libloading 0.8.3", + "objc2 0.4.1", "once_cell", - "raw-window-handle", - "wayland-sys 0.30.1", - "windows-sys 0.45.0", + "raw-window-handle 0.5.2", + "wayland-sys", + "windows-sys 0.48.0", "x11-dl", ] [[package]] name = "glutin-winit" -version = "0.3.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629a873fc04062830bfe8f97c03773bcd7b371e23bcc465d0a61448cd1588fa4" +checksum = "1ebcdfba24f73b8412c5181e56f092b5eff16671c514ce896b258a0a64bd7735" dependencies = [ "cfg_aliases", "glutin", - "raw-window-handle", + "raw-window-handle 0.5.2", "winit", ] [[package]] name = "glutin_egl_sys" -version = "0.4.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5aaf0abb5c4148685b33101ae326a207946b4d3764d6cdc79f8316cdaa8367d" +checksum = "77cc5623f5309ef433c3dd4ca1223195347fe62c413da8e2fdd0eb76db2d9bcd" dependencies = [ "gl_generator", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "glutin_glx_sys" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b53cb5fe568964aa066a3ba91eac5ecbac869fb0842cd0dc9e412434f1a1494" +checksum = "a165fd686c10dcc2d45380b35796e577eacfd43d4660ee741ec8ebe2201b3b4f" dependencies = [ "gl_generator", "x11-dl", @@ -1250,47 +1632,89 @@ dependencies = [ [[package]] name = "glutin_wgl_sys" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef89398e90033fc6bc65e9bd42fd29bbbfd483bda5b56dc5562f455550618165" +checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead" dependencies = [ "gl_generator", ] [[package]] -name = "gobject-sys" -version = "0.16.3" +name = "gpu-alloc" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3520bb9c07ae2a12c7f2fbb24d4efc11231c8146a86956413fb1a79bb760a0f1" +checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" dependencies = [ - "glib-sys", - "libc", - "system-deps", + "bitflags 2.5.0", + "gpu-alloc-types", ] [[package]] -name = "gtk-sys" -version = "0.16.0" +name = "gpu-alloc-types" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b5f8946685d5fe44497007786600c2f368ff6b1e61a16251c89f72a97520a3" +checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" dependencies = [ - "atk-sys", - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "system-deps", + "bitflags 2.5.0", +] + +[[package]] +name = "gpu-allocator" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f56f6318968d03c18e1bcf4857ff88c61157e9da8e47c5f29055d60e1228884" +dependencies = [ + "log", + "presser", + "thiserror", + "winapi", + "windows 0.52.0", +] + +[[package]] +name = "gpu-descriptor" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c" +dependencies = [ + "bitflags 2.5.0", + "gpu-descriptor-types", + "hashbrown", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bf0b36e6f090b7e1d8a4b49c0cb81c1f8376f72198c65dd3ad9ff3556b8b78c" +dependencies = [ + "bitflags 2.5.0", ] [[package]] name = "hashbrown" -version = "0.12.3" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hassle-rs" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +checksum = "af2a7e73e1f34c48da31fb668a907f250794837e08faa144fd24f0b8b741e890" +dependencies = [ + "bitflags 2.5.0", + "com", + "libc", + "libloading 0.8.3", + "thiserror", + "widestring", + "winapi", +] [[package]] name = "heck" @@ -1300,9 +1724,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.1" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" @@ -1310,6 +1734,21 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hexf-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "humantime" version = "2.1.0" @@ -1318,55 +1757,79 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "iana-time-zone" -version = "0.1.56" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows 0.48.0", + "windows-core", ] [[package]] name = "iana-time-zone-haiku" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "cxx", - "cxx-build", + "cc", +] + +[[package]] +name = "icrate" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d3aaff8a54577104bafdf686ff18565c3b6903ca5782a2026ef06e2c7aa319" +dependencies = [ + "block2 0.3.0", + "dispatch", + "objc2 0.4.1", ] [[package]] name = "idna" -version = "0.3.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", ] +[[package]] +name = "image" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "num-traits", + "png", +] + [[package]] name = "indexmap" -version = "1.9.3" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ - "autocfg", + "equivalent", "hashbrown", ] [[package]] name = "inline_tweak" -version = "1.0.8" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7033e97b20277cc0d043226d1940fa7719ff08d2305d1fc7421e53066d00eb4b" +checksum = "c6acddbefae08bfba73e27f55513f491f35c365d84bf3002bf85ba9b916c5e5f" dependencies = [ "lazy_static", + "rustc-hash", ] [[package]] @@ -1376,16 +1839,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", ] [[package]] name = "io-lifetimes" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi", "libc", @@ -1394,18 +1854,27 @@ dependencies = [ [[package]] name = "itertools" -version = "0.10.5" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ "either", ] [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jni" @@ -1431,22 +1900,33 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.26" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] +[[package]] +name = "khronos-egl" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" +dependencies = [ + "libc", + "libloading 0.8.3", + "pkg-config", +] + [[package]] name = "khronos_api" version = "3.1.0" @@ -1461,9 +1941,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.141" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libloading" @@ -1476,31 +1956,49 @@ dependencies = [ ] [[package]] -name = "link-cplusplus" -version = "1.0.8" +name = "libloading" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ - "cc", + "cfg-if", + "windows-targets 0.52.4", ] [[package]] -name = "linked-hash-map" -version = "0.5.6" +name = "libredox" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607" +dependencies = [ + "bitflags 2.5.0", + "libc", + "redox_syscall 0.4.1", +] + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] -name = "linux-raw-sys" -version = "0.3.1" +name = "litrs" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -1508,11 +2006,10 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" dependencies = [ - "cfg-if", "serde", ] @@ -1524,9 +2021,9 @@ checksum = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7" [[package]] name = "log4rs" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d36ca1786d9e79b8193a68d480a0907b612f109537115c6ff655a3a1967533fd" +checksum = "0816135ae15bd0391cf284eab37e6e3ee0a6ee63d2ceeb659862bd8d0a984ca6" dependencies = [ "anyhow", "arc-swap", @@ -1537,7 +2034,9 @@ dependencies = [ "libc", "log", "log-mdc", + "once_cell", "parking_lot", + "rand", "serde", "serde-value", "serde_json", @@ -1559,85 +2058,117 @@ dependencies = [ [[package]] name = "memchr" -version = "2.5.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memmap2" -version = "0.5.10" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" dependencies = [ "libc", ] [[package]] name = "memoffset" -version = "0.6.5" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" dependencies = [ "autocfg", ] [[package]] name = "memoffset" -version = "0.7.1" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] [[package]] -name = "minimal-lexical" -version = "0.2.1" +name = "metal" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +checksum = "c43f73953f8cbe511f021b58f18c3ce1c3d1ae13fe953293e13345bf83217f25" +dependencies = [ + "bitflags 2.5.0", + "block", + "core-graphics-types", + "foreign-types", + "log", + "objc", + "paste", +] [[package]] -name = "miniz_oxide" -version = "0.6.2" +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess2" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +checksum = "25a3333bb1609500601edc766a39b4c1772874a4ce26022f4d866854dc020c41" dependencies = [ - "adler", + "mime", + "unicase", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", "simd-adler32", ] [[package]] -name = "mio" -version = "0.8.6" +name = "naga" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "50e3524642f53d9af419ab5e8dd29d3ba155708267667c2f3f06c88c9e130843" dependencies = [ - "libc", + "bit-set", + "bitflags 2.5.0", + "codespan-reporting", + "hexf-parse", + "indexmap", "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.45.0", + "num-traits", + "rustc-hash", + "spirv", + "termcolor", + "thiserror", + "unicode-xid", ] [[package]] name = "ndk" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" dependencies = [ - "bitflags", + "bitflags 2.5.0", "jni-sys", + "log", "ndk-sys", - "num_enum 0.5.11", - "raw-window-handle", + "num_enum", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.0", "thiserror", ] @@ -1649,50 +2180,36 @@ checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" [[package]] name = "ndk-sys" -version = "0.4.1+23.1.7779620" +version = "0.5.0+25.2.9519653" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" +checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" dependencies = [ "jni-sys", ] [[package]] name = "nix" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" -dependencies = [ - "bitflags", - "cfg-if", - "libc", - "memoffset 0.6.5", -] - -[[package]] -name = "nix" -version = "0.25.1" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" dependencies = [ - "autocfg", - "bitflags", + "bitflags 1.3.2", "cfg-if", "libc", - "memoffset 0.6.5", + "memoffset 0.7.1", ] [[package]] name = "nix" -version = "0.26.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ - "bitflags", + "bitflags 2.5.0", "cfg-if", + "cfg_aliases", "libc", - "memoffset 0.7.1", - "pin-utils", - "static_assertions", + "memoffset 0.9.1", ] [[package]] @@ -1713,9 +2230,9 @@ dependencies = [ [[package]] name = "num" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" +checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" dependencies = [ "num-bigint", "num-complex", @@ -1727,9 +2244,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ "autocfg", "num-integer", @@ -1738,28 +2255,27 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" dependencies = [ "num-traits", ] [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" dependencies = [ "autocfg", "num-integer", @@ -1780,53 +2296,32 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", ] [[package]] name = "num_enum" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" -dependencies = [ - "num_enum_derive 0.5.11", -] - -[[package]] -name = "num_enum" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0fa9d8a04aa0af7b5845b514a828f829ae3f0ec3f60d9842e1dfaeb49a0e68b" -dependencies = [ - "num_enum_derive 0.6.0", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.11" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", + "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.6.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e51dcc6bafb7f3ac88b65d2ad21f4b53d878e496712060e23011862ebd2d2d1" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.58", ] [[package]] @@ -1836,6 +2331,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" dependencies = [ "malloc_buf", + "objc_exception", ] [[package]] @@ -1855,15 +2351,31 @@ version = "0.2.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7" +[[package]] +name = "objc-sys" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c71324e4180d0899963fc83d9d241ac39e699609fc1025a850aadac8257459" + [[package]] name = "objc2" -version = "0.3.0-beta.3" +version = "0.3.0-beta.3.patch-leaks.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe31e5425d3d0b89a15982c024392815da40689aceb34bad364d58732bcfd649" +checksum = "7e01640f9f2cb1220bbe80325e179e532cb3379ebcd1bf2279d703c19fe3a468" dependencies = [ - "block2", - "objc-sys", - "objc2-encode", + "block2 0.2.0-alpha.6", + "objc-sys 0.2.0-beta.2", + "objc2-encode 2.0.0-pre.2", +] + +[[package]] +name = "objc2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d" +dependencies = [ + "objc-sys 0.3.2", + "objc2-encode 3.0.0", ] [[package]] @@ -1872,7 +2384,22 @@ version = "2.0.0-pre.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abfcac41015b00a120608fdaa6938c44cb983fee294351cc4bac7638b4e50512" dependencies = [ - "objc-sys", + "objc-sys 0.2.0-beta.2", +] + +[[package]] +name = "objc2-encode" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666" + +[[package]] +name = "objc_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", ] [[package]] @@ -1886,27 +2413,24 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "orbclient" -version = "0.3.44" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e9829e16c5e112e94efb5e2ad1fe17f8c1c99bb0fcdc8c65c44e935d904767d" +checksum = "52f0d54bde9774d3a51dcf281a5def240c71996bc6ca05d2c847ec8b2b216166" dependencies = [ - "cfg-if", - "redox_syscall 0.2.16", - "wasm-bindgen", - "web-sys", + "libredox", ] [[package]] name = "ordered-float" -version = "2.10.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" dependencies = [ "num-traits", ] @@ -1923,30 +2447,18 @@ dependencies = [ [[package]] name = "owned_ttf_parser" -version = "0.18.1" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e25e9fb15717794fae58ab55c26e044103aad13186fbb625893f9a3bbcc24228" +checksum = "d4586edfe4c648c71797a74c84bacb32b52b212eff5dfe2bb9f2c599844023e7" dependencies = [ "ttf-parser", ] -[[package]] -name = "pango-sys" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e134909a9a293e04d2cc31928aa95679c5e4df954d0b85483159bd20d8f047f" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - [[package]] name = "parking" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" @@ -1960,34 +2472,34 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.7" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.2.16", + "redox_syscall 0.4.1", "smallvec", - "windows-sys 0.45.0", + "windows-targets 0.48.5", ] [[package]] name = "paste" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -1995,33 +2507,44 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "piper" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +dependencies = [ + "atomic-waker", + "fastrand 2.0.2", + "futures-io", +] + [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "png" -version = "0.17.8" +version = "0.17.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaeebc51f9e7d2c150d3f3bfeb667f2aa985db5ef1e3d212847bdedb488beeaa" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crc32fast", "fdeflate", "flate2", - "miniz_oxide 0.7.1", + "miniz_oxide", ] [[package]] name = "polling" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be1c66a6add46bff50935c313dae30a5030cf8385c5206e8a95e9e9def974aa" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ "autocfg", - "bitflags", + "bitflags 1.3.2", "cfg-if", "concurrent-queue", "libc", @@ -2030,12 +2553,39 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "polling" +version = "3.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0c976a60b2d7e99d6f229e414670a9b85d13ac305cc6d1e9c134de58c5aaaf6" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix 0.38.32", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "pollster" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" + [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "presser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" + [[package]] name = "proc-macro-crate" version = "1.3.1" @@ -2043,7 +2593,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "toml_edit", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit 0.21.1", ] [[package]] @@ -2072,18 +2631,33 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] +[[package]] +name = "profiling" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58" + +[[package]] +name = "quick-xml" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" +dependencies = [ + "memchr", +] + [[package]] name = "quote" -version = "1.0.26" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -2125,13 +2699,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" [[package]] -name = "redox_syscall" -version = "0.2.16" +name = "raw-window-handle" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", -] +checksum = "42a9830a0e1b9fb145ebb365b8bc4ccd75f290f98c0247deafbbe2c75cefb544" [[package]] name = "redox_syscall" @@ -2139,25 +2710,35 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] -name = "redox_users" -version = "0.4.3" +name = "redox_syscall" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ - "getrandom", - "redox_syscall 0.2.16", - "thiserror", + "bitflags 1.3.2", ] [[package]] name = "regex" -version = "1.7.3" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", @@ -2166,54 +2747,77 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.29" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "renderdoc-sys" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" [[package]] name = "rfd" -version = "0.11.3" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cb2988ec50c9bcdb0c012b89643a6094a35a785a37897211ee62e1639342f7b" +checksum = "25a73a7337fc24366edfca76ec521f51877b114e42dab584008209cca6719251" dependencies = [ - "async-io", + "ashpd", "block", "dispatch", - "futures-util", - "glib-sys", - "gobject-sys", - "gtk-sys", "js-sys", "log", "objc", "objc-foundation", "objc_id", - "raw-window-handle", + "pollster", + "raw-window-handle 0.6.0", + "urlencoding", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows 0.44.0", + "windows-sys 0.48.0", ] +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustix" -version = "0.37.11" +version = "0.37.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77" +checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", - "linux-raw-sys", + "linux-raw-sys 0.3.8", "windows-sys 0.48.0", ] +[[package]] +name = "rustix" +version = "0.38.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys 0.4.13", + "windows-sys 0.52.0", +] + [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "same-file" @@ -2232,21 +2836,15 @@ checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" [[package]] name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "scratch" -version = "1.0.5" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sctk-adwaita" -version = "0.5.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda4e97be1fd174ccc2aae81c8b694e803fa99b34e8fd0f057a9d70698e3ed09" +checksum = "82b2eaf3a5b264a521b988b2e73042e742df700c4f962cde845d1541adb46550" dependencies = [ "ab_glyph", "log", @@ -2257,9 +2855,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.160" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] @@ -2274,34 +2872,22 @@ dependencies = [ "serde", ] -[[package]] -name = "serde-xml-rs" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0bf1ba0696ccf0872866277143ff1fd14d22eec235d2b23702f95e6660f7dfa" -dependencies = [ - "log", - "serde", - "thiserror", - "xml-rs", -] - [[package]] name = "serde_derive" -version = "1.0.160" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.58", ] [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" dependencies = [ "itoa", "ryu", @@ -2310,104 +2896,134 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.12" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", -] - -[[package]] -name = "serde_spanned" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4" -dependencies = [ - "serde", + "syn 2.0.58", ] [[package]] name = "serde_yaml" -version = "0.8.26" +version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ "indexmap", + "itoa", "ryu", "serde", - "yaml-rust", + "unsafe-libyaml", ] [[package]] name = "sha1" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", "digest", ] +[[package]] +name = "shrinkwraprs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e63e6744142336dfb606fe2b068afa2e1cca1ee6a5d8377277a92945d81fa331" +dependencies = [ + "bitflags 1.3.2", + "itertools 0.8.2", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + [[package]] name = "simd-adler32" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] name = "slab" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "slotmap" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" dependencies = [ "version_check", ] [[package]] name = "smallvec" -version = "1.10.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "smithay-client-toolkit" -version = "0.16.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f307c47d32d2715eb2e0ece5589057820e0e5e70d07c247d1063e844e107f454" +checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a" dependencies = [ - "bitflags", + "bitflags 2.5.0", "calloop", - "dlib", - "lazy_static", + "calloop-wayland-source", + "cursor-icon", + "libc", "log", "memmap2", - "nix 0.24.3", - "pkg-config", + "rustix 0.38.32", + "thiserror", + "wayland-backend", "wayland-client", + "wayland-csd-frame", "wayland-cursor", "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "xkeysym", ] [[package]] name = "smithay-clipboard" -version = "0.6.6" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a345c870a1fae0b1b779085e81b51e614767c239e93503588e54c5b17f4b0e8" +checksum = "c091e7354ea8059d6ad99eace06dd13ddeedbb0ac72d40a9a6e7ff790525882d" dependencies = [ + "libc", "smithay-client-toolkit", - "wayland-client", + "wayland-backend", +] + +[[package]] +name = "smol_str" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6845563ada680337a52d43bb0b29f396f2d911616f6573012645b9e3d048a49" +dependencies = [ + "serde", ] [[package]] @@ -2415,29 +3031,68 @@ name = "smw-editor" version = "0.1.0" dependencies = [ "anyhow", + "duplicate", "eframe", - "egui", + "egui 0.27.2", + "egui-phosphor 0.5.0", "egui_dock", "egui_extras", - "enum_dispatch", + "egui_glow", + "glow", "inline_tweak", - "itertools", + "itertools 0.12.1", "log", "log4rs", "num", - "num_enum 0.6.0", + "num_enum", + "paste", "rfd", - "smwe-project", + "serde", + "serde_json", + "shrinkwraprs", + "smwe-emu", + "smwe-math", + "smwe-render", "smwe-rom", "smwe-widgets", "thiserror", + "wdc65816", + "zstd", ] [[package]] -name = "smwe-project" +name = "smwe-emu" version = "0.1.0" dependencies = [ - "smwe-rom", + "wdc65816", +] + +[[package]] +name = "smwe-math" +version = "0.1.0" +dependencies = [ + "duplicate", + "emath 0.27.2", + "shrinkwraprs", + "wrapped_pos2_derive", + "wrapped_rect_derive", + "wrapped_vec2_derive", +] + +[[package]] +name = "smwe-render" +version = "0.1.0" +dependencies = [ + "egui_glow", + "emath 0.27.2", + "epaint 0.27.2", + "itertools 0.12.1", + "log", + "serde", + "shrinkwraprs", + "smwe-emu", + "smwe-math", + "thiserror", ] [[package]] @@ -2446,16 +3101,17 @@ version = "0.1.0" dependencies = [ "anyhow", "duplicate", - "epaint", - "itertools", + "epaint 0.27.2", + "itertools 0.12.1", "log", "nom", "num-traits", - "num_enum 0.6.0", + "num_enum", "paste", "serde", "serde_json", "smallvec", + "smwe-render", "thiserror", ] @@ -2464,38 +3120,47 @@ name = "smwe-widgets" version = "0.1.0" dependencies = [ "anyhow", - "egui", - "itertools", + "egui 0.27.2", + "egui-phosphor 0.4.0", + "egui_glow", + "glow", + "inline_tweak", + "itertools 0.12.1", + "smwe-emu", + "smwe-render", "thiserror", ] [[package]] name = "socket2" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ "libc", "winapi", ] [[package]] -name = "static_assertions" -version = "1.1.0" +name = "spirv" +version = "0.3.0+sdk-1.3.268.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" +dependencies = [ + "bitflags 2.5.0", +] [[package]] -name = "str-buf" -version = "1.0.6" +name = "static_assertions" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "strict-num" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9df65f20698aeed245efdde3628a6b559ea1239bbb871af1b6e3b58c413b2bd1" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" [[package]] name = "syn" @@ -2510,111 +3175,85 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "system-deps" -version = "6.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555fc8147af6256f3931a36bb83ad0023240ce9cf2b319dec8236fd1f220b05f" -dependencies = [ - "cfg-expr", - "heck", - "pkg-config", - "toml", - "version-compare", -] - [[package]] name = "tempfile" -version = "3.5.0" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", - "fastrand", - "redox_syscall 0.3.5", - "rustix", - "windows-sys 0.45.0", + "fastrand 2.0.2", + "rustix 0.38.32", + "windows-sys 0.52.0", ] [[package]] name = "termcolor" -version = "1.2.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.58", ] [[package]] name = "thread-id" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fdfe0627923f7411a43ec9ec9c39c3a9b4151be313e0922042581fb6c9b717f" -dependencies = [ - "libc", - "redox_syscall 0.2.16", - "winapi", -] - -[[package]] -name = "time" -version = "0.1.45" +version = "4.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +checksum = "f0ec81c46e9eb50deaa257be2f148adf052d1fb7701cfd55ccfab2525280b70b" dependencies = [ "libc", - "wasi 0.10.0+wasi-snapshot-preview1", "winapi", ] [[package]] name = "tiny-skia" -version = "0.8.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfef3412c6975196fdfac41ef232f910be2bb37b9dd3313a49a1a6bc815a5bdb" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" dependencies = [ "arrayref", "arrayvec", "bytemuck", "cfg-if", - "png", + "log", "tiny-skia-path", ] [[package]] name = "tiny-skia-path" -version = "0.8.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b5edac058fc98f51c935daea4d805b695b38e2f151241cad125ade2a2ac20d" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" dependencies = [ "arrayref", "bytemuck", @@ -2637,46 +3276,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] -name = "toml" -version = "0.7.3" +name = "toml_datetime" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" [[package]] -name = "toml_datetime" -version = "0.6.1" +name = "toml_edit" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "serde", + "indexmap", + "toml_datetime", + "winnow", ] [[package]] name = "toml_edit" -version = "0.19.8" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ "indexmap", - "serde", - "serde_spanned", "toml_datetime", "winnow", ] [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -2684,29 +3316,38 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.58", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", ] [[package]] name = "ttf-parser" -version = "0.18.1" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" + +[[package]] +name = "type-map" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0609f771ad9c6155384897e1df4d948e692667cc0588548b68eb44d052b27633" +checksum = "deb68604048ff8fa93347f02441e4487594adc20bb8a084f9e564d2b827a0a9f" +dependencies = [ + "rustc-hash", +] [[package]] name = "typemap-ors" @@ -2719,46 +3360,68 @@ dependencies = [ [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "uds_windows" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce65604324d3cce9b966701489fbd0cf318cb1f7bd9dd07ac9a4ee6fb791930d" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" dependencies = [ + "memoffset 0.9.1", "tempfile", "winapi", ] +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + [[package]] name = "unicode-width" -version = "0.1.10" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "unicode-xid" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "unsafe-any-ors" @@ -2769,28 +3432,29 @@ dependencies = [ "destructure_traitobject", ] +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "url" -version = "2.3.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - -[[package]] -name = "version-compare" -version = "0.1.1" +name = "urlencoding" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] name = "version_check" @@ -2800,26 +3464,20 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "waker-fn" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" +checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" [[package]] name = "walkdir" -version = "2.3.3" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", ] -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2828,9 +3486,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2838,24 +3496,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.58", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.34" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", @@ -2865,9 +3523,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2875,134 +3533,280 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.58", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] -name = "wayland-client" -version = "0.29.5" +name = "wayland-backend" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" +checksum = "9d50fa61ce90d76474c87f5fc002828d81b32677340112b4ef08079a9d459a40" dependencies = [ - "bitflags", + "cc", "downcast-rs", - "libc", - "nix 0.24.3", + "rustix 0.38.32", "scoped-tls", - "wayland-commons", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82fb96ee935c2cea6668ccb470fb7771f6215d1691746c2d896b447a00ad3f1f" +dependencies = [ + "bitflags 2.5.0", + "rustix 0.38.32", + "wayland-backend", "wayland-scanner", - "wayland-sys 0.29.5", ] [[package]] -name = "wayland-commons" -version = "0.29.5" +name = "wayland-csd-frame" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "nix 0.24.3", - "once_cell", - "smallvec", - "wayland-sys 0.29.5", + "bitflags 2.5.0", + "cursor-icon", + "wayland-backend", ] [[package]] name = "wayland-cursor" -version = "0.29.5" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" +checksum = "71ce5fa868dd13d11a0d04c5e2e65726d0897be8de247c0c5a65886e283231ba" dependencies = [ - "nix 0.24.3", + "rustix 0.38.32", "wayland-client", "xcursor", ] [[package]] name = "wayland-protocols" -version = "0.29.5" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" +dependencies = [ + "bitflags 2.5.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-plasma" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479" +dependencies = [ + "bitflags 2.5.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" +checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" dependencies = [ - "bitflags", + "bitflags 2.5.0", + "wayland-backend", "wayland-client", - "wayland-commons", + "wayland-protocols", "wayland-scanner", ] [[package]] name = "wayland-scanner" -version = "0.29.5" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" +checksum = "63b3a62929287001986fb58c789dce9b67604a397c15c611ad9f747300b6c283" dependencies = [ "proc-macro2", + "quick-xml", "quote", - "xml-rs", ] [[package]] name = "wayland-sys" -version = "0.29.5" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" +checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af" dependencies = [ "dlib", - "lazy_static", + "log", + "once_cell", "pkg-config", ] [[package]] -name = "wayland-sys" -version = "0.30.1" +name = "wdc65816" +version = "0.1.0" + +[[package]] +name = "web-sys" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b2a02ac608e07132978689a6f9bf4214949c85998c247abadd4f4129b1aa06" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ - "dlib", - "lazy_static", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webbrowser" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd595fb70f33583ac61644820ebc144a26c96028b625b96cafcd861f4743fbc8" +dependencies = [ + "core-foundation", + "home", + "jni", "log", - "pkg-config", + "ndk-context", + "objc", + "raw-window-handle 0.5.2", + "url", + "web-sys", +] + +[[package]] +name = "wgpu" +version = "0.19.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4b1213b52478a7631d6e387543ed8f642bc02c578ef4e3b49aca2a29a7df0cb" +dependencies = [ + "arrayvec", + "cfg-if", + "cfg_aliases", + "js-sys", + "log", + "parking_lot", + "profiling", + "raw-window-handle 0.6.0", + "smallvec", + "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "wgpu-core", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-core" +version = "0.19.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9f6b033c2f00ae0bc8ea872c5989777c60bc241aac4e58b24774faa8b391f78" +dependencies = [ + "arrayvec", + "bit-vec", + "bitflags 2.5.0", + "cfg_aliases", + "codespan-reporting", + "indexmap", + "log", + "naga", + "once_cell", + "parking_lot", + "profiling", + "raw-window-handle 0.6.0", + "rustc-hash", + "smallvec", + "thiserror", + "web-sys", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-hal" +version = "0.19.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f972c280505ab52ffe17e94a7413d9d54b58af0114ab226b9fc4999a47082e" +dependencies = [ + "android_system_properties", + "arrayvec", + "ash", + "bitflags 2.5.0", + "cfg_aliases", + "core-graphics-types", + "glow", + "glutin_wgl_sys", + "gpu-alloc", + "gpu-allocator", + "gpu-descriptor", + "hassle-rs", + "js-sys", + "khronos-egl", + "libc", + "libloading 0.8.3", + "log", + "metal", + "naga", + "ndk-sys", + "objc", + "once_cell", + "parking_lot", + "profiling", + "raw-window-handle 0.6.0", + "renderdoc-sys", + "rustc-hash", + "smallvec", + "thiserror", + "wasm-bindgen", + "web-sys", + "wgpu-types", + "winapi", ] [[package]] -name = "web-sys" -version = "0.3.61" +name = "wgpu-types" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "b671ff9fb03f78b46ff176494ee1ebe7d603393f42664be55b64dc8d53969805" dependencies = [ + "bitflags 2.5.0", "js-sys", - "wasm-bindgen", + "web-sys", ] [[package]] -name = "webbrowser" -version = "0.8.9" +name = "widestring" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b692165700260bbd40fbc5ff23766c03e339fbaca907aeea5cb77bf0a553ca83" -dependencies = [ - "core-foundation", - "dirs", - "jni", - "log", - "ndk-context", - "objc", - "raw-window-handle", - "url", - "web-sys", -] +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" [[package]] name = "winapi" @@ -3022,18 +3826,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-wsapoll" -version = "0.1.1" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c17110f57155602a80dca10be03852116403c9ff3cd25b079d666f2aa3df6e" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] @@ -3046,43 +3841,50 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.42.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0286ba339aa753e70765d521bb0242cc48e1194562bfa2a2ad7ac8a6de28f5d5" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ "windows-implement", - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-interface", + "windows-targets 0.48.5", ] [[package]] name = "windows" -version = "0.44.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ - "windows-targets 0.42.2", + "windows-core", + "windows-targets 0.52.4", ] [[package]] -name = "windows" -version = "0.48.0" +name = "windows-core" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.48.0", + "windows-targets 0.52.4", ] [[package]] name = "windows-implement" -version = "0.42.0" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e2ee588991b9e7e6c8338edf3333fbe4da35dc72092643958ebb43f0ab2c49c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "windows-interface" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9539b6bd3eadbd9de66c9666b22d802b833da7e996bc06896142e09854a61767" +checksum = "e6fb8df20c9bcaa8ad6ab513f7b40104840c8867d5751126e4df3b08388d0cc7" dependencies = [ "proc-macro2", "quote", @@ -3104,7 +3906,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.4", ] [[package]] @@ -3124,17 +3935,32 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", ] [[package]] @@ -3145,9 +3971,15 @@ checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" [[package]] name = "windows_aarch64_msvc" @@ -3157,9 +3989,15 @@ checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" [[package]] name = "windows_i686_gnu" @@ -3169,9 +4007,15 @@ checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" [[package]] name = "windows_i686_msvc" @@ -3181,9 +4025,15 @@ checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" [[package]] name = "windows_x86_64_gnu" @@ -3193,9 +4043,15 @@ checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" [[package]] name = "windows_x86_64_gnullvm" @@ -3205,9 +4061,15 @@ checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" [[package]] name = "windows_x86_64_msvc" @@ -3217,54 +4079,101 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" [[package]] name = "winit" -version = "0.28.3" +version = "0.29.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f504e8c117b9015f618774f8d58cd4781f5a479bc41079c064f974cbb253874" +checksum = "0d59ad965a635657faf09c8f062badd885748428933dad8e8bdd64064d92e5ca" dependencies = [ + "ahash", "android-activity", - "bitflags", + "atomic-waker", + "bitflags 2.5.0", + "bytemuck", + "calloop", "cfg_aliases", "core-foundation", "core-graphics", - "dispatch", - "instant", + "cursor-icon", + "icrate", + "js-sys", "libc", "log", - "mio", + "memmap2", "ndk", - "objc2", + "ndk-sys", + "objc2 0.4.1", "once_cell", "orbclient", "percent-encoding", - "raw-window-handle", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.0", "redox_syscall 0.3.5", + "rustix 0.38.32", "sctk-adwaita", "smithay-client-toolkit", + "smol_str", + "unicode-segmentation", "wasm-bindgen", + "wasm-bindgen-futures", + "wayland-backend", "wayland-client", - "wayland-commons", "wayland-protocols", - "wayland-scanner", + "wayland-protocols-plasma", "web-sys", - "windows-sys 0.45.0", + "web-time", + "windows-sys 0.48.0", "x11-dl", + "x11rb", + "xkbcommon-dl", ] [[package]] name = "winnow" -version = "0.4.1" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] +[[package]] +name = "wrapped_pos2_derive" +version = "0.1.0" +dependencies = [ + "emath 0.27.2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "wrapped_rect_derive" +version = "0.1.0" +dependencies = [ + "emath 0.27.2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "wrapped_vec2_derive" +version = "0.1.0" +dependencies = [ + "emath 0.27.2", + "quote", + "syn 2.0.58", +] + [[package]] name = "x11-dl" version = "2.21.0" @@ -3278,97 +4187,167 @@ dependencies = [ [[package]] name = "x11rb" -version = "0.10.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "592b4883219f345e712b3209c62654ebda0bb50887f330cbd018d0f654bfd507" +checksum = "f8f25ead8c7e4cba123243a6367da5d3990e0d3affa708ea19dce96356bd9f1a" dependencies = [ + "as-raw-xcb-connection", "gethostname", - "nix 0.24.3", - "winapi", - "winapi-wsapoll", + "libc", + "libloading 0.8.3", + "once_cell", + "rustix 0.38.32", "x11rb-protocol", ] [[package]] name = "x11rb-protocol" -version = "0.10.0" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e63e71c4b8bd9ffec2c963173a4dc4cbde9ee96961d4fcb4429db9929b606c34" + +[[package]] +name = "xcursor" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a0ccd7b4a5345edfcd0c3535718a4e9ff7798ffc536bb5b5a0e26ff84732911" + +[[package]] +name = "xdg-home" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56b245751c0ac9db0e006dc812031482784e434630205a93c73cfefcaabeac67" +checksum = "21e5a325c3cb8398ad6cf859c1135b25dd29e186679cf2da7581d9679f63b38e" dependencies = [ - "nix 0.24.3", + "libc", + "winapi", ] [[package]] -name = "xcursor" -version = "0.3.4" +name = "xkbcommon-dl" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" +checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "nom", + "bitflags 2.5.0", + "dlib", + "log", + "once_cell", + "xkeysym", ] [[package]] -name = "xml-rs" -version = "0.8.4" +name = "xkeysym" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" +checksum = "054a8e68b76250b253f671d1268cb7f1ae089ec35e195b2efb2a4e9a836d0621" [[package]] -name = "yaml-rust" -version = "0.4.5" +name = "xml-rs" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] +checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193" [[package]] name = "zbus" -version = "3.11.1" +version = "3.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dc29e76f558b2cb94190e8605ecfe77dd40f5df8c072951714b4b71a97f5848" +checksum = "5acecd3f8422f198b1a2f954bcc812fe89f3fa4281646f3da1da7925db80085d" dependencies = [ - "async-broadcast", + "async-broadcast 0.5.1", "async-executor", - "async-fs", - "async-io", - "async-lock", + "async-fs 1.6.0", + "async-io 1.13.0", + "async-lock 2.8.0", + "async-process 1.8.1", "async-recursion", "async-task", "async-trait", + "blocking", "byteorder", "derivative", - "dirs", "enumflags2", - "event-listener", + "event-listener 2.5.3", "futures-core", "futures-sink", "futures-util", "hex", - "nix 0.26.2", + "nix 0.26.4", "once_cell", "ordered-stream", "rand", "serde", - "serde-xml-rs", "serde_repr", "sha1", "static_assertions", "tracing", "uds_windows", "winapi", - "zbus_macros", - "zbus_names", - "zvariant", + "xdg-home", + "zbus_macros 3.15.1", + "zbus_names 2.6.1", + "zvariant 3.15.1", +] + +[[package]] +name = "zbus" +version = "4.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9ff46f2a25abd690ed072054733e0bc3157e3d4c45f41bd183dce09c2ff8ab9" +dependencies = [ + "async-broadcast 0.7.0", + "async-executor", + "async-fs 2.1.1", + "async-io 2.3.2", + "async-lock 3.3.0", + "async-process 2.2.0", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "derivative", + "enumflags2", + "event-listener 5.3.0", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix 0.28.0", + "ordered-stream", + "rand", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tracing", + "uds_windows", + "windows-sys 0.52.0", + "xdg-home", + "zbus_macros 4.1.2", + "zbus_names 3.0.0", + "zvariant 4.0.2", +] + +[[package]] +name = "zbus_macros" +version = "3.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2207eb71efebda17221a579ca78b45c4c5f116f074eb745c3a172e688ccf89f5" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", + "zvariant_utils", ] [[package]] name = "zbus_macros" -version = "3.11.1" +version = "4.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62a80fd82c011cd08459eaaf1fd83d3090c1b61e6d5284360074a7475af3a85d" +checksum = "4e0e3852c93dcdb49c9462afe67a2a468f7bd464150d866e861eaf06208633e0" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", "regex", @@ -3378,36 +4357,122 @@ dependencies = [ [[package]] name = "zbus_names" -version = "2.5.0" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "437d738d3750bed6ca9b8d423ccc7a8eb284f6b1d6d4e225a0e4e6258d864c8d" +dependencies = [ + "serde", + "static_assertions", + "zvariant 3.15.1", +] + +[[package]] +name = "zbus_names" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f34f314916bd89bdb9934154627fab152f4f28acdda03e7c4c68181b214fe7e3" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" dependencies = [ "serde", "static_assertions", - "zvariant", + "zvariant 4.0.2", +] + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "zstd" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.10+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" +dependencies = [ + "cc", + "pkg-config", ] [[package]] name = "zvariant" -version = "3.12.0" +version = "3.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe4914a985446d6fd287019b5fceccce38303d71407d9e6e711d44954a05d8" +checksum = "c5b4fcf3660d30fc33ae5cd97e2017b23a96e85afd7a1dd014534cd0bf34ba67" dependencies = [ "byteorder", "enumflags2", "libc", "serde", "static_assertions", - "zvariant_derive", + "zvariant_derive 3.15.1", +] + +[[package]] +name = "zvariant" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c1b3ca6db667bfada0f1ebfc94b2b1759ba25472ee5373d4551bb892616389a" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "url", + "zvariant_derive 4.0.2", +] + +[[package]] +name = "zvariant_derive" +version = "3.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0277758a8a0afc0e573e80ed5bfd9d9c2b48bd3108ffe09384f9f738c83f4a55" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", + "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "3.12.0" +version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34c20260af4b28b3275d6676c7e2a6be0d4332e8e0aba4616d34007fd84e462a" +checksum = "b7a4b236063316163b69039f77ce3117accb41a09567fd24c168e43491e521bc" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", "syn 1.0.109", @@ -3416,9 +4481,9 @@ dependencies = [ [[package]] name = "zvariant_utils" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b22993dbc4d128a17a3b6c92f1c63872dd67198537ee728d8b5d7c40640a8b" +checksum = "00bedb16a193cc12451873fee2a1bc6550225acece0e36f333e68326c73c8172" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 23eb8a4..ec11776 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,36 +1,54 @@ [workspace] members = [ - "smwe-project", - "smwe-rom", - "smwe-widgets", + "crates/smwe-emu", + "crates/smwe-math", + "crates/smwe-math/wrapped_vec2_derive", + "crates/smwe-math/wrapped_pos2_derive", + "crates/smwe-math/wrapped_rect_derive", + "crates/smwe-render", + "crates/smwe-rom", + "crates/smwe-widgets", + "crates/wdc65816", ] [package] name = "smw-editor" version = "0.1.0" -authors = ["Adam Gąsior (Adanos020)", "Raven Szewczyk (eigenraven)"] +authors = ["Adam Gąsior (Adanos020)", "Raven Szewczyk (eigenraven)", "Selicre"] license = "MIT" edition = "2021" readme = "README.md" -rust-version = "1.65" +rust-version = "1.70" [dependencies] -smwe-project = { path = "smwe-project" } -smwe-rom = { path = "smwe-rom" } -smwe-widgets = { path = "smwe-widgets" } +smwe-emu = { path = "crates/smwe-emu" } +smwe-math = { path = "crates/smwe-math" } +smwe-render = { path = "crates/smwe-render" } +smwe-rom = { path = "crates/smwe-rom" } +smwe-widgets = { path = "crates/smwe-widgets" } +wdc65816 = { path = "crates/wdc65816" } anyhow = "1.0" -eframe = "0.21" -egui = "0.21" -egui_dock = "0.4" -egui_extras = "0.21" -enum_dispatch = "0.3" +duplicate = "1.0" +eframe = "0.27" +egui = { version = "0.27", features = ["serde"] } +egui_dock = "0.12" +egui_extras = "0.27" +egui_glow = "0.27" +#egui-phosphor = "0.4" +egui-phosphor = { git = "https://github.com/Adanos020/egui-phosphor", branch = "egui-0.27" } +glow = "0.13" inline_tweak = "1.0" -itertools = "0.10" +itertools = "0.12" log = "0.4" log4rs = "1.0" num = "0.4" -num_enum = "0.6" -rfd = "0.11" +num_enum = "0.7" +paste = "1.0" +rfd = "0.14" +serde = "1.0" +serde_json = "1.0" +shrinkwraprs = "0.3" thiserror = "1.0" +zstd = { version = "0.13", features = ["experimental"] } diff --git a/smwe-project/Cargo.toml b/crates/smwe-emu/Cargo.toml similarity index 51% rename from smwe-project/Cargo.toml rename to crates/smwe-emu/Cargo.toml index 69715a6..bccea68 100644 --- a/smwe-project/Cargo.toml +++ b/crates/smwe-emu/Cargo.toml @@ -1,11 +1,12 @@ [package] -name = "smwe-project" +name = "smwe-emu" version = "0.1.0" -authors = ["Adam Gąsior (Adanos020)", "Raven Szewczyk (eigenraven)"] +authors = ["Selicre"] +license = "MIT" edition = "2021" -rust-version = "1.65" +rust-version = "1.70" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -smwe-rom = { path = "../smwe-rom" } +wdc65816 = { path = "../wdc65816" } diff --git a/crates/smwe-emu/src/emu.rs b/crates/smwe-emu/src/emu.rs new file mode 100644 index 0000000..74a1f46 --- /dev/null +++ b/crates/smwe-emu/src/emu.rs @@ -0,0 +1,424 @@ +#![allow(clippy::identity_op)] + +use std::{collections::HashSet, sync::Arc}; + +use wdc65816::{Cpu, Mem}; + +use crate::rom::Rom; + +#[derive(Debug, Clone)] +pub struct CheckedMem { + pub cart: Arc, + pub wram: Vec, + pub regs: Vec, + pub vram: Vec, + pub cgram: Vec, + pub extram: Vec, + pub uninit: HashSet, + pub error: Option, + pub err_value: Option, + pub last_store: Option, +} + +impl CheckedMem { + pub fn new(rom: Arc) -> Self { + Self { + cart: rom, + wram: Vec::from([0; 0x20000]), + regs: Vec::from([0; 0x6000]), + vram: Vec::from([0; 0x10000]), + cgram: Vec::from([0; 0x200]), + extram: Vec::from([0; 0x10000]), + uninit: HashSet::new(), + error: None, + err_value: None, + last_store: None, + } + } + + pub fn load_u8(&mut self, addr: u32) -> u8 { + self.load(addr) + } + + pub fn store_u8(&mut self, addr: u32, value: u8) { + self.store(addr, value) + } + + pub fn load_u16(&mut self, addr: u32) -> u16 { + let l = self.load(addr); + let h = self.load(addr + 1); + u16::from_le_bytes([l, h]) + } + + pub fn load_u24(&mut self, addr: u32) -> u32 { + let l = self.load(addr); + let h = self.load(addr + 1); + let b = self.load(addr + 2); + u32::from_le_bytes([l, h, b, 0]) + } + + pub fn store_u16(&mut self, addr: u32, val: u16) { + let val = val.to_le_bytes(); + self.store(addr, val[0]); + self.store(addr + 1, val[1]); + } + + pub fn store_u24(&mut self, addr: u32, val: u32) { + let val = val.to_le_bytes(); + self.store(addr, val[0]); + self.store(addr + 1, val[1]); + self.store(addr + 2, val[2]); + } + + pub fn process_dma_ch(&mut self, ch: u32) { + let a = self.load_u24(0x4302 + ch); + let size = self.load_u16(0x4305 + ch) as u32; + let b = self.load(0x4301 + ch); + let params = self.load(0x4300 + ch); + // TODO: turn this into reg writes + #[allow(clippy::overly_complex_bool_expr)] + if b == 0x18 { + let dest = self.load_u16(0x2116) as u32; + //println!("DMA size {:04X}: VRAM ${:02X}:{:04X} => ${:04X}", size, a_bank, a, dest); + if params & 0x8 != 0 { // fill transfer + /*let value = self.load(a_bank, a); + for i in dest..dest+size { + self.vram[i as usize * 2] = value; + }*/ + } else { + for i in 0..size { + self.vram[(dest * 2 + i) as usize] = self.load(a + i); + } + } + self.store_u16(0x2116, (dest + size) as u16); + } else if false && b == 0x19 { + let _dest = self.load_u16(0x2116); + //println!("DMA size {:04X}: VRAMh ${:02X}:{:04X} => ${:04X}", size, a_bank, a, dest); + if params & 0x8 != 0 { // fill transfer + /*let value = self.load(a_bank, a); + for i in dest..dest+size { + self.vram[i as usize * 2] = value; + }*/ + } + } else if b == 0x22 { + let dest = self.load(0x2121) as u32; + // cgram + for i in 0..size { + self.cgram[(dest * 2 + i) as usize] = self.load(a + i); + } + self.store_u16(0x2121, (dest + size) as u16); + } else { + println!("DMA size {size:04X}: ${b:02X} ${a:06X}"); + } + } + + pub fn process_dma(&mut self) { + let dma = self.load(0x420B); + if dma != 0 { + for i in 0..8 { + if dma & (1 << i) != 0 { + self.process_dma_ch(i * 0x10); + } + } + self.store(0x420B, 0); + } + } + + pub fn map(&mut self, addr: u32, write: Option) -> u8 { + let track_uninit = false; + let bank = addr >> 16; + let mutable = if bank & 0xFE == 0x7E { + let ptr = (addr & 0x1FFFF) as usize; + if track_uninit { + if write.is_none() && !self.uninit.contains(&ptr) { + println!("Uninit read: ${:06X}", 0x7E0000 + ptr); + } + self.uninit.insert(ptr); + } + &mut self.wram[ptr] + } else if bank == 0x60 { + let ptr = (addr & 0xFFFF) as usize; + &mut self.extram[ptr] + } else if addr & 0xFFFF < 0x2000 { + let ptr = (addr & 0x1FFF) as usize; + if track_uninit { + if write.is_none() && !self.uninit.contains(&ptr) { + println!("Uninit read: ${:06X}", 0x7E0000 + ptr); + } + self.uninit.insert(ptr); + } + &mut self.wram[ptr] + } else if addr & 0xFFFF < 0x8000 { + let ptr = (addr & 0x7FFF) as usize; + if track_uninit { + if write.is_none() && !self.uninit.contains(&ptr) { + //println!("Uninit read: ${:04X}", ptr); + } + self.uninit.insert(ptr); + } + // TODO: be more accurate + if let Some(value) = write { + if ptr == 0x2118 { + let addr = self.load_u16(0x2116); + self.vram[(addr as usize) * 2 + 0] = value; + } else if ptr == 0x2119 { + let addr = self.load_u16(0x2116); + self.vram[(addr as usize) * 2 + 1] = value; + self.store_u16(0x2116, addr + 1); + } + } + &mut self.regs[ptr - 0x2000] + } else if addr & 0xFFFF >= 0x8000 { + if let Some(c) = self.cart.read(addr) { + return c; + } else { + self.error = Some(addr); + self.err_value.get_or_insert(0) + } + } else { + self.error = Some(addr); + self.err_value.get_or_insert(0) + }; + if let Some(c) = write { + *mutable = c; + } + *mutable + } +} +impl Mem for CheckedMem { + #[allow(clippy::let_and_return)] + fn load(&mut self, addr: u32) -> u8 { + let value = self.map(addr, None); + // println!("ld ${:06X} = {:02X}", addr, value); + value + } + + fn store(&mut self, addr: u32, value: u8) { + //println!("st ${:06X} = {:02X}", addr, value); + self.map(addr, Some(value)); + self.last_store = Some(addr); + } +} + +pub fn fetch_anim_frame(cpu: &mut Cpu) -> u64 { + cpu.s = 0x1FF; + cpu.pc = 0x2000; + cpu.pbr = 0x00; + cpu.dbr = 0x00; + cpu.trace = false; + // quasi-loader bytecode + let routines = [ + "CODE_05BB39", // set up frames + "CODE_00A390", // upload them + ]; + let mut addr = 0x2000; + for symbol in routines { + cpu.mem.store(addr, 0x22); + cpu.mem.store_u24(addr + 1, cpu.mem.cart.resolve(symbol).unwrap_or_else(|| panic!("no symbol: {symbol}"))); + addr += 4; + } + let mut cy = 0; + loop { + cy += cpu.dispatch() as u64; + //if cy > cy_limit { break; } + if cpu.ill { + println!("ILLEGAL INSTR"); + break; + } + if cpu.pc == addr as u16 { + break; + } + cpu.mem.process_dma(); + } + cy +} + +pub fn exec_sprite_id(cpu: &mut Cpu, id: u8) -> u64 { + let now = std::time::Instant::now(); + cpu.emulation = false; + cpu.mem.store(0x9E, id); + cpu.mem.store(0x1A, 0x00); + cpu.mem.store(0x1C, 0x00); + cpu.mem.store(0xD8, 0x80); + cpu.mem.store(0xE4, 0x80); + for i in 0..12 { + cpu.mem.store(0x14C8 + i, 0); + } + cpu.mem.store(0x14C8, 1); + cpu.y = 0; + cpu.x = 0; + cpu.s = 0x1FF; + cpu.pc = 0x2000; + cpu.pbr = 0x00; + cpu.dbr = 0x01; + cpu.trace = false; + // quasi-loader bytecode + let routines = ["InitSpriteTables", "CODE_01808C", "CODE_01808C"]; + let mut addr = 0x2000; + for i in routines { + cpu.mem.store(addr, 0x22); + cpu.mem.store_u24(addr + 1, cpu.mem.cart.resolve(i).unwrap_or_else(|| panic!("no symbol: {}", i))); + addr += 4; + } + let mut cy = 0; + loop { + cy += cpu.dispatch() as u64; + //if cy > cy_limit { break; } + if cpu.ill { + println!("ILLEGAL INSTR"); + break; + } + if cpu.pc == addr as u16 { + break; + } + if cy > 10000000 { + println!("took too long"); + break; + } + cpu.mem.process_dma(); + } + println!("took {}µs", now.elapsed().as_micros()); + cy +} +pub fn exec_sprites(cpu: &mut Cpu) -> u64 { + let now = std::time::Instant::now(); + cpu.emulation = false; + /* + cpu.mem.store(0x9E, id); + cpu.mem.store(0xAA, 0x80); + cpu.mem.store(0xE4, 0x80); + cpu.mem.store(0x14C8, 1);*/ + //cpu.x = slot as u16; + cpu.s = 0x1FF; + cpu.pc = 0x2000; + cpu.pbr = 0x00; + cpu.dbr = 0x01; + cpu.trace = false; + // quasi-loader bytecode + let routines = ["CODE_01808C"]; + let mut addr = 0x2000; + for i in routines { + cpu.mem.store(addr, 0x22); + cpu.mem.store_u24(addr + 1, cpu.mem.cart.resolve(i).unwrap_or_else(|| panic!("no symbol: {}", i))); + addr += 4; + } + let mut cy = 0; + loop { + cy += cpu.dispatch() as u64; + //if cy > cy_limit { break; } + if cpu.ill { + println!("ILLEGAL INSTR"); + break; + } + if cpu.pc == addr as u16 { + break; + } + cpu.mem.process_dma(); + } + println!("took {}µs", now.elapsed().as_micros()); + cy +} +pub fn decompress_sublevel(cpu: &mut Cpu, id: u16) -> u64 { + let now = std::time::Instant::now(); + cpu.emulation = false; + // set submap + cpu.mem.store(0x1F11, (id >> 8) as _); + cpu.mem.store(0x141A, 1); + cpu.s = 0x1FF; + cpu.pc = 0x2000; + cpu.pbr = 0x00; + cpu.dbr = 0x00; + cpu.trace = false; + // quasi-loader bytecode + let routines = [ + "CODE_00A993", // init layer 3 / sp0 + "CODE_00B888", // init gfx32/33 + "CODE_05D796", // init pointers + "CODE_05801E", // decompress level + "UploadSpriteGFX", // upload graphics + "LoadPalette", // init palette + "CODE_00922F", // upload palette + ]; + let mut addr = 0x2000; + for i in routines { + cpu.mem.store(addr, 0x22); + cpu.mem.store_u24(addr + 1, cpu.mem.cart.resolve(i).unwrap_or_else(|| panic!("no symbol: {}", i))); + addr += 4; + } + let mut cy = 0; + let layer1_data_ptr = cpu.mem.cart.resolve("Layer1DataPtr").unwrap(); + loop { + cy += cpu.dispatch() as u64; + //if cy > cy_limit { break; } + if cpu.ill { + println!("ILLEGAL INSTR"); + break; + } + if cpu.pc == 0xD8B7 && cpu.pbr == 0x05 { + cpu.mem.store_u16(0xE, id); + } + if cpu.pc == addr as u16 { + break; + } + if cpu.pc == 0x200C { + let _layer1_ptr = cpu.mem.load_u24(layer1_data_ptr); + cpu.mem.store_u24(layer1_data_ptr, 0x600000); + // todo: properly load level data from RAM at `layer1_ptr` + let level_data = std::fs::read("debug/levels/105_YI1main.bin").unwrap(); + cpu.mem.extram[..level_data.len()].copy_from_slice(&level_data); + } + cpu.mem.process_dma(); + } + println!("took {}µs", now.elapsed().as_micros()); + cy +} +pub fn decompress_extram(cpu: &mut Cpu, id: u16) -> u64 { + let now = std::time::Instant::now(); + cpu.emulation = false; + // set submap + cpu.mem.store(0x1F11, (id >> 8) as _); + cpu.mem.store(0x141A, 1); + cpu.s = 0x1FF; + cpu.pc = 0x2000; + cpu.pbr = 0x00; + cpu.dbr = 0x00; + cpu.trace = false; + // quasi-loader bytecode + let routines = [ + "CODE_00A993", // init layer 3 / sp0 + "CODE_00B888", // init gfx32/33 + "CODE_05D796", // init pointers + "CODE_05801E", // decompress level + "UploadSpriteGFX", // upload graphics + "LoadPalette", // init palette + "CODE_00922F", // upload palette + ]; + let mut addr = 0x2000; + for i in routines { + cpu.mem.store(addr, 0x22); + cpu.mem.store_u24(addr + 1, cpu.mem.cart.resolve(i).unwrap_or_else(|| panic!("no symbol: {}", i))); + addr += 4; + } + let mut cy = 0; + let layer1_data_ptr = cpu.mem.cart.resolve("Layer1DataPtr").unwrap(); + loop { + cy += cpu.dispatch() as u64; + //if cy > cy_limit { break; } + if cpu.ill { + println!("ILLEGAL INSTR"); + break; + } + if cpu.pc == 0xD8B7 && cpu.pbr == 0x05 { + cpu.mem.store_u16(0xE, id); + } + if cpu.pc == addr as u16 { + break; + } + if cpu.pc == 0x200C { + cpu.mem.store_u24(layer1_data_ptr, 0x600000); + } + cpu.mem.process_dma(); + } + println!("took {}µs", now.elapsed().as_micros()); + cy +} diff --git a/crates/smwe-emu/src/lib.rs b/crates/smwe-emu/src/lib.rs new file mode 100644 index 0000000..9ca8cea --- /dev/null +++ b/crates/smwe-emu/src/lib.rs @@ -0,0 +1,6 @@ +//! Game emulation, used to run various aspects of the game. + +pub mod emu; +pub mod rom; + +pub type Cpu = wdc65816::Cpu; diff --git a/crates/smwe-emu/src/rom.rs b/crates/smwe-emu/src/rom.rs new file mode 100644 index 0000000..258c3d8 --- /dev/null +++ b/crates/smwe-emu/src/rom.rs @@ -0,0 +1,116 @@ +#![allow(clippy::identity_op)] + +//! Storage for ROM files, mapper support, etc. + +use std::collections::HashMap; + +#[derive(Debug, Copy, Clone)] +pub enum Mapper { + NoRom, + LoRom, + HiRom, +} + +impl Mapper { + pub fn map_to_file(&self, addr: usize) -> Option { + match self { + Mapper::NoRom => Some(addr), + Mapper::LoRom => { + if (addr&0xFE0000)==0x7E0000 //wram + || (addr&0x408000)==0x000000 //hardware regs, ram mirrors, other strange junk + || (addr&0x708000)==0x700000 + { + //sram (low parts of banks 70-7D) + None + } else { + Some((addr & 0x7F0000) >> 1 | (addr & 0x7FFF)) + } + } + Mapper::HiRom => { + if (addr&0xFE0000)==0x7E0000 //wram + || (addr&0x408000)==0x000000 + { + //hardware regs, ram mirrors, other strange junk + None + } else { + Some(addr & 0x3FFFFF) + } + } + } + } + + pub fn map_to_addr(&self, offset: usize) -> usize { + match self { + Mapper::NoRom => offset, + Mapper::LoRom => { + let in_bank = offset & 0x7FFF; + let bank = offset >> 15; + (bank << 16) + in_bank + 0x8000 + } + Mapper::HiRom => offset | 0xC00000, + } + } +} + +#[derive(Debug, Clone)] +pub struct Rom { + buf: Vec, + mapper: Mapper, + symbols: HashMap, +} + +impl Rom { + pub fn new(buf: Vec) -> Self { + Self { buf, mapper: Mapper::LoRom, symbols: HashMap::new() } + } + + pub fn load_symbols(&mut self, data: &str) { + for i in data.lines() { + let i = if let Some(comment) = i.find(';') { &i[..comment] } else { i }.trim(); + if i.is_empty() { + continue; + } + if let Some(v) = i.find(' ') { + match u32::from_str_radix(&i[..v], 16) { + Ok(addr) => { + self.symbols.insert(i[v + 1..].to_string(), addr); + } + Err(_e) => {} + } + } + } + } + + pub fn resolve(&self, symbol: &str) -> Option { + self.symbols.get(symbol).copied() + } + + pub fn read(&self, addr: u32) -> Option { + self.mapper.map_to_file(addr as _).and_then(|c| self.buf.get(c).copied()) + } + + pub fn read_u16(&self, addr: u32) -> Option { + Some(u16::from_le_bytes([self.read(addr + 0)?, self.read(addr + 1)?])) + } + + pub fn read_u32(&self, addr: u32) -> Option { + Some(u32::from_le_bytes([self.read(addr + 0)?, self.read(addr + 1)?, self.read(addr + 2)?, 0])) + } + + pub fn resize(&mut self, new_size: usize) { + self.buf.resize(new_size, 0); + } + + pub fn mapper(&self) -> Mapper { + self.mapper + } + + pub fn as_slice(&self) -> &[u8] { + &self.buf + } + + pub fn checksum(&self) -> u16 { + // TODO: npo2 roms + self.buf.iter().map(|c| *c as u16).sum() + } +} diff --git a/crates/smwe-math/Cargo.toml b/crates/smwe-math/Cargo.toml new file mode 100644 index 0000000..6310507 --- /dev/null +++ b/crates/smwe-math/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "smwe-math" +version = "0.1.0" +authors = ["Adam Gąsior (Adanos020)"] +license = "MIT" +edition = "2021" +rust-version = "1.70" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +wrapped_vec2_derive = { path = "wrapped_vec2_derive" } +wrapped_pos2_derive = { path = "wrapped_pos2_derive" } +wrapped_rect_derive = { path = "wrapped_rect_derive" } +duplicate = "1.0" +emath = "0.27" +shrinkwraprs = "0.3" diff --git a/crates/smwe-math/src/coordinates/mod.rs b/crates/smwe-math/src/coordinates/mod.rs new file mode 100644 index 0000000..60112f5 --- /dev/null +++ b/crates/smwe-math/src/coordinates/mod.rs @@ -0,0 +1,39 @@ +pub mod pos2; +pub mod rect; +pub mod vec2; + +use std::ops::*; + +use shrinkwraprs::Shrinkwrap; +use wrapped_pos2_derive::WrappedPos2; +use wrapped_rect_derive::WrappedRect; +use wrapped_vec2_derive::WrappedVec2; + +/// # Coordinate systems +/// +/// ## Screen +/// The area of your display; units are points. +/// +/// ## Canvas +/// The editing area; units are canvas pixels. +/// +/// ## Grid +/// The editing area divided into square cells; units are cell indices. + +#[derive( + Copy, Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd, Shrinkwrap, WrappedVec2, WrappedPos2, WrappedRect, +)] +#[shrinkwrap(mutable)] +pub struct OnScreen(pub T); + +#[derive( + Copy, Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd, Shrinkwrap, WrappedVec2, WrappedPos2, WrappedRect, +)] +#[shrinkwrap(mutable)] +pub struct OnCanvas(pub T); + +#[derive( + Copy, Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd, Shrinkwrap, WrappedVec2, WrappedPos2, WrappedRect, +)] +#[shrinkwrap(mutable)] +pub struct OnGrid(pub T); diff --git a/crates/smwe-math/src/coordinates/pos2.rs b/crates/smwe-math/src/coordinates/pos2.rs new file mode 100644 index 0000000..a65b2f8 --- /dev/null +++ b/crates/smwe-math/src/coordinates/pos2.rs @@ -0,0 +1,39 @@ +use emath::*; + +use super::{OnCanvas, OnGrid, OnScreen}; + +impl OnScreen { + #[inline(always)] + pub fn to_canvas(self, pixels_per_point: f32, zoom: f32) -> OnCanvas { + self.to_vec2().to_canvas(pixels_per_point, zoom).to_pos2() + } + + #[inline(always)] + pub fn to_grid(self, pixels_per_point: f32, zoom: f32, tile_size: f32) -> OnGrid { + self.to_vec2().to_grid(pixels_per_point, zoom, tile_size).to_pos2() + } +} + +impl OnCanvas { + #[inline(always)] + pub fn to_screen(self, pixels_per_point: f32, zoom: f32) -> OnScreen { + self.to_vec2().to_screen(pixels_per_point, zoom).to_pos2() + } + + #[inline(always)] + pub fn to_grid(self, tile_size: f32) -> OnGrid { + self.to_vec2().to_grid(tile_size).to_pos2() + } +} + +impl OnGrid { + #[inline(always)] + pub fn to_screen(self, pixels_per_point: f32, zoom: f32, tile_size: f32) -> OnScreen { + self.to_vec2().to_screen(pixels_per_point, zoom, tile_size).to_pos2() + } + + #[inline(always)] + pub fn to_canvas(self, tile_size: f32) -> OnCanvas { + self.to_vec2().to_canvas(tile_size).to_pos2() + } +} diff --git a/crates/smwe-math/src/coordinates/rect.rs b/crates/smwe-math/src/coordinates/rect.rs new file mode 100644 index 0000000..7c1098d --- /dev/null +++ b/crates/smwe-math/src/coordinates/rect.rs @@ -0,0 +1,58 @@ +use emath::*; + +use super::{OnCanvas, OnGrid, OnScreen}; + +impl OnScreen { + #[inline(always)] + pub fn to_canvas(self, pixels_per_point: f32, zoom: f32) -> OnCanvas { + OnCanvas(Rect::from_min_max( + OnScreen(self.0.min).to_canvas(pixels_per_point, zoom).0, + OnScreen(self.0.max).to_canvas(pixels_per_point, zoom).0, + )) + } + + #[inline(always)] + pub fn to_grid(self, pixels_per_point: f32, zoom: f32, tile_size: f32) -> OnGrid { + OnGrid(Rect::from_min_max( + OnScreen(self.0.min).to_grid(pixels_per_point, zoom, tile_size).0, + OnScreen(self.0.max).to_grid(pixels_per_point, zoom, tile_size).0, + )) + } +} + +impl OnCanvas { + #[inline(always)] + pub fn to_screen(self, pixels_per_point: f32, zoom: f32) -> OnScreen { + OnScreen(Rect::from_min_max( + OnCanvas(self.0.min).to_screen(pixels_per_point, zoom).0, + OnCanvas(self.0.max).to_screen(pixels_per_point, zoom).0, + )) + } + + #[inline(always)] + pub fn to_grid(self, tile_size: f32) -> OnGrid { + OnGrid(Rect::from_min_max( + // + OnCanvas(self.0.min).to_grid(tile_size).0, + OnCanvas(self.0.max).to_grid(tile_size).0, + )) + } +} + +impl OnGrid { + #[inline(always)] + pub fn to_screen(self, pixels_per_point: f32, zoom: f32, tile_size: f32) -> OnScreen { + OnScreen(Rect::from_min_max( + OnGrid(self.0.min).to_screen(pixels_per_point, zoom, tile_size).0, + OnGrid(self.0.max).to_screen(pixels_per_point, zoom, tile_size).0, + )) + } + + #[inline(always)] + pub fn to_canvas(self, tile_size: f32) -> OnCanvas { + OnCanvas(Rect::from_min_max( + OnGrid(self.0.min).to_canvas(tile_size).0, + OnGrid(self.0.max).to_canvas(tile_size).0, + )) + } +} diff --git a/crates/smwe-math/src/coordinates/vec2.rs b/crates/smwe-math/src/coordinates/vec2.rs new file mode 100644 index 0000000..6884ae9 --- /dev/null +++ b/crates/smwe-math/src/coordinates/vec2.rs @@ -0,0 +1,39 @@ +use emath::*; + +use super::{OnCanvas, OnGrid, OnScreen}; + +impl OnScreen { + #[inline(always)] + pub fn to_canvas(self, pixels_per_point: f32, zoom: f32) -> OnCanvas { + OnCanvas(self.0 * pixels_per_point / zoom).floor() + } + + #[inline(always)] + pub fn to_grid(self, pixels_per_point: f32, zoom: f32, tile_size: f32) -> OnGrid { + self.to_canvas(pixels_per_point, zoom).to_grid(tile_size) + } +} + +impl OnCanvas { + #[inline(always)] + pub fn to_screen(self, pixels_per_point: f32, zoom: f32) -> OnScreen { + OnScreen(self.0 * zoom / pixels_per_point) + } + + #[inline(always)] + pub fn to_grid(self, tile_size: f32) -> OnGrid { + OnGrid(self.0 / tile_size).floor() + } +} + +impl OnGrid { + #[inline(always)] + pub fn to_screen(self, pixels_per_point: f32, zoom: f32, tile_size: f32) -> OnScreen { + self.to_canvas(tile_size).to_screen(pixels_per_point, zoom) + } + + #[inline(always)] + pub fn to_canvas(self, tile_size: f32) -> OnCanvas { + OnCanvas(self.0 * tile_size) + } +} diff --git a/crates/smwe-math/src/lib.rs b/crates/smwe-math/src/lib.rs new file mode 100644 index 0000000..9b50734 --- /dev/null +++ b/crates/smwe-math/src/lib.rs @@ -0,0 +1 @@ +pub mod coordinates; diff --git a/crates/smwe-math/wrapped_pos2_derive/Cargo.toml b/crates/smwe-math/wrapped_pos2_derive/Cargo.toml new file mode 100644 index 0000000..a408ccb --- /dev/null +++ b/crates/smwe-math/wrapped_pos2_derive/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "wrapped_pos2_derive" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +emath = "0.27" +quote = "1.0" +syn = "2.0" diff --git a/crates/smwe-math/wrapped_pos2_derive/src/lib.rs b/crates/smwe-math/wrapped_pos2_derive/src/lib.rs new file mode 100644 index 0000000..c340cef --- /dev/null +++ b/crates/smwe-math/wrapped_pos2_derive/src/lib.rs @@ -0,0 +1,118 @@ +use proc_macro::TokenStream; +use quote::quote; + +#[proc_macro_derive(WrappedPos2)] +pub fn wrapped_pos2_derive(input: TokenStream) -> TokenStream { + let ast = syn::parse(input).unwrap(); + impl_wrapped_pos2(&ast) +} + +fn impl_wrapped_pos2(ast: &syn::DeriveInput) -> TokenStream { + let wrapper_name = &ast.ident; + + let gen = quote! { + impl #wrapper_name { + pub const ZERO: Self = Self::new(0., 0.); + + #[inline(always)] + pub const fn new(x: f32, y: f32) -> Self { + Self(emath::Pos2::new(x, y)) + } + + #[inline(always)] + pub fn to_vec2(self) -> #wrapper_name { + #wrapper_name(self.0.to_vec2()) + } + + #[inline(always)] + pub fn relative_to(self, other: Self) -> Self { + Self(self.0 - other.0.to_vec2()) + } + + #[inline] + pub fn distance(self, other: Self) -> f32 { + self.0.distance(other.0) + } + + #[inline] + pub fn distance_sq(self, other: Self) -> f32 { + self.0.distance_sq(other.0) + } + + pub fn lerp(self, other: Self, t: f32) -> Self { + Self(self.0.lerp(other.0, t)) + } + + #[inline(always)] + pub fn floor(self) -> Self { + Self(self.0.floor()) + } + + #[inline(always)] + pub fn round(self) -> Self { + Self(self.0.round()) + } + + #[inline(always)] + pub fn ceil(self) -> Self { + Self(self.0.ceil()) + } + + #[inline] + #[must_use] + pub fn min(self, other: Self) -> Self { + Self(self.0.min(other.0)) + } + + #[inline] + #[must_use] + pub fn max(self, other: Self) -> Self { + Self(self.0.max(other.0)) + } + + #[inline] + #[must_use] + pub fn clamp(self, min: Self, max: Self) -> Self { + Self(self.0.clamp(min.0, max.0)) + } + } + + impl Sub for #wrapper_name { + type Output = #wrapper_name; + + fn sub(self, rhs: Self) -> Self::Output { + #wrapper_name(self.0.sub(rhs.0)) + } + } + + impl AddAssign<#wrapper_name> for #wrapper_name { + fn add_assign(&mut self, rhs: #wrapper_name) { + self.0.add_assign(rhs.0); + } + } + + impl SubAssign<#wrapper_name> for #wrapper_name { + fn sub_assign(&mut self, rhs: #wrapper_name) { + self.0.sub_assign(rhs.0); + } + } + + impl Add<#wrapper_name> for #wrapper_name { + type Output = Self; + + fn add(self, rhs: #wrapper_name) -> Self::Output { + Self(self.0.add(rhs.0)) + } + } + + impl Sub<#wrapper_name> for #wrapper_name { + type Output = Self; + + fn sub(self, rhs: #wrapper_name) -> Self::Output { + Self(self.0.sub(rhs.0)) + } + } + }; + + gen.into() +} diff --git a/crates/smwe-math/wrapped_rect_derive/Cargo.toml b/crates/smwe-math/wrapped_rect_derive/Cargo.toml new file mode 100644 index 0000000..5596c63 --- /dev/null +++ b/crates/smwe-math/wrapped_rect_derive/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "wrapped_rect_derive" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +emath = "0.27" +quote = "1.0" +syn = "2.0" diff --git a/crates/smwe-math/wrapped_rect_derive/src/lib.rs b/crates/smwe-math/wrapped_rect_derive/src/lib.rs new file mode 100644 index 0000000..ae676d7 --- /dev/null +++ b/crates/smwe-math/wrapped_rect_derive/src/lib.rs @@ -0,0 +1,247 @@ +use proc_macro::TokenStream; +use quote::quote; + +#[proc_macro_derive(WrappedRect)] +pub fn wrapped_rect_derive(input: TokenStream) -> TokenStream { + let ast = syn::parse(input).unwrap(); + impl_wrapped_rect(&ast) +} + +fn impl_wrapped_rect(ast: &syn::DeriveInput) -> TokenStream { + let wrapper_name = &ast.ident; + + let gen = quote! { + impl #wrapper_name { + #[inline(always)] + pub const fn from_min_max(min: #wrapper_name, max: #wrapper_name) -> Self { + Self(emath::Rect::from_min_max(min.0, max.0)) + } + + #[inline(always)] + pub fn from_min_size(min: #wrapper_name, size: #wrapper_name) -> Self { + Self(emath::Rect::from_min_size(min.0, size.0)) + } + + #[inline(always)] + pub fn from_center_size(center: #wrapper_name, size: #wrapper_name) -> Self { + Self(emath::Rect::from_center_size(center.0, size.0)) + } + + #[inline(always)] + pub fn from_x_y_ranges(x_range: impl Into, y_range: impl Into) -> Self { + Self(emath::Rect::from_x_y_ranges(x_range, y_range)) + } + + #[inline(always)] + pub fn from_two_pos(a: #wrapper_name, b: #wrapper_name) -> Self { + Self(emath::Rect::from_two_pos(a.0, b.0)) + } + + pub fn from_points(points: &[#wrapper_name]) -> Self { + // Can't call emath::Rect::from_points using the slice of wrapped emath::Pos2 without an extra allocation. + let mut rect = emath::Rect::NOTHING; + for p in points { + rect.extend_with(p.0); + } + Self(rect) + } + + #[inline] + pub fn everything_right_of(left_x: f32) -> Self { + Self(emath::Rect::everything_right_of(left_x)) + } + + #[inline] + pub fn everything_left_of(right_x: f32) -> Self { + Self(emath::Rect::everything_left_of(right_x)) + } + + #[inline] + pub fn everything_below(top_y: f32) -> Self { + Self(emath::Rect::everything_below(top_y)) + } + + #[inline] + pub fn everything_above(bottom_y: f32) -> Self { + Self(emath::Rect::everything_above(bottom_y)) + } + + #[must_use] + pub fn expand(self, amnt: f32) -> Self { + Self(self.0.expand(amnt)) + } + + #[must_use] + pub fn expand2(self, amnt: #wrapper_name) -> Self { + Self(self.0.expand2(amnt.0)) + } + + #[must_use] + pub fn shrink(self, amnt: f32) -> Self { + Self(self.0.shrink(amnt)) + } + + #[must_use] + pub fn shrink2(self, amnt: #wrapper_name) -> Self { + Self(self.0.shrink2(amnt.0)) + } + + #[inline] + #[must_use] + pub fn translate(self, amnt: #wrapper_name) -> Self { + Self(self.0.translate(amnt.0)) + } + + #[inline] + #[must_use] + pub fn rotate_bb(self, rot: emath::Rot2) -> Self { + Self(self.0.rotate_bb(rot)) + } + + #[inline] + #[must_use] + pub fn intersects(self, other: Self) -> bool { + self.0.intersects(other.0) + } + + #[inline(always)] + pub fn set_center(&mut self, center: #wrapper_name) { + self.0.set_center(center.0) + } + + #[inline] + #[must_use] + pub fn contains(self, other: #wrapper_name) -> bool { + self.0.contains(other.0) + } + + #[inline] + #[must_use] + pub fn contains_rect(self, other: Self) -> bool { + self.0.contains_rect(other.0) + } + + #[must_use] + pub fn clamp(self, p: #wrapper_name) -> #wrapper_name { + #wrapper_name(self.0.clamp(p.0)) + } + + #[inline(always)] + pub fn extend_with(&mut self, p: #wrapper_name) { + self.0.extend_with(p.0) + } + + #[inline(always)] + #[must_use] + pub fn union(self, other: Self) -> Self { + Self(self.0.union(other.0)) + } + + #[inline(always)] + #[must_use] + pub fn intersect(self, other: Self) -> Self { + Self(self.0.intersect(other.0)) + } + + #[inline(always)] + pub fn center(self) -> #wrapper_name { + #wrapper_name(self.0.center()) + } + + #[inline(always)] + pub fn size(self) -> #wrapper_name { + #wrapper_name(self.0.size()) + } + + #[inline(always)] + pub fn square_proportions(self) -> #wrapper_name { + #wrapper_name(self.0.square_proportions()) + } + + #[inline] + pub fn distance_to_pos(self, p: #wrapper_name) -> f32 { + self.0.distance_to_pos(p.0) + } + + #[inline] + pub fn distance_sq_to_pos(self, p: #wrapper_name) -> f32 { + self.0.distance_sq_to_pos(p.0) + } + + #[inline] + pub fn signed_distance_to_pos(self, p: #wrapper_name) -> f32 { + self.0.signed_distance_to_pos(p.0) + } + + pub fn lerp_inside(self, t: #wrapper_name) -> #wrapper_name { + #wrapper_name(self.0.lerp_inside(t.0)) + } + + pub fn lerp_towards(self, other: &Self, t: f32) -> Self { + Self(self.0.lerp_towards(&other.0, t)) + } + + #[inline(always)] + pub fn left_top(&self) -> #wrapper_name { + #wrapper_name(self.0.left_top()) + } + + #[inline(always)] + pub fn center_top(&self) -> #wrapper_name { + #wrapper_name(self.0.center_top()) + } + + #[inline(always)] + pub fn right_top(&self) -> #wrapper_name { + #wrapper_name(self.0.right_top()) + } + + #[inline(always)] + pub fn left_center(&self) -> #wrapper_name { + #wrapper_name(self.0.left_center()) + } + + #[inline(always)] + pub fn right_center(&self) -> #wrapper_name { + #wrapper_name(self.0.right_center()) + } + + #[inline(always)] + pub fn left_bottom(&self) -> #wrapper_name { + #wrapper_name(self.0.left_bottom()) + } + + #[inline(always)] + pub fn center_bottom(&self) -> #wrapper_name { + #wrapper_name(self.0.center_bottom()) + } + + #[inline(always)] + pub fn right_bottom(&self) -> #wrapper_name { + #wrapper_name(self.0.right_bottom()) + } + + pub fn split_left_right_at_fraction(&self, t: f32) -> (Self, Self) { + let (a, b) = self.0.split_left_right_at_fraction(t); + (Self(a), Self(b)) + } + + pub fn split_left_right_at_x(&self, split_x: f32) -> (Self, Self) { + let (a, b) = self.0.split_left_right_at_x(split_x); + (Self(a), Self(b)) + } + + pub fn split_top_bottom_at_fraction(&self, t: f32) -> (Self, Self) { + let (a, b) = self.0.split_top_bottom_at_fraction(t); + (Self(a), Self(b)) + } + + pub fn split_top_bottom_at_y(&self, split_y: f32) -> (Self, Self) { + let (a, b) = self.0.split_top_bottom_at_y(split_y); + (Self(a), Self(b)) + } + } + }; + + gen.into() +} diff --git a/crates/smwe-math/wrapped_vec2_derive/Cargo.toml b/crates/smwe-math/wrapped_vec2_derive/Cargo.toml new file mode 100644 index 0000000..2cab437 --- /dev/null +++ b/crates/smwe-math/wrapped_vec2_derive/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "wrapped_vec2_derive" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +emath = "0.27" +quote = "1.0" +syn = "2.0" diff --git a/crates/smwe-math/wrapped_vec2_derive/src/lib.rs b/crates/smwe-math/wrapped_vec2_derive/src/lib.rs new file mode 100644 index 0000000..298ac75 --- /dev/null +++ b/crates/smwe-math/wrapped_vec2_derive/src/lib.rs @@ -0,0 +1,176 @@ +use proc_macro::TokenStream; +use quote::quote; + +#[proc_macro_derive(WrappedVec2)] +pub fn wrapped_vec2_derive(input: TokenStream) -> TokenStream { + let ast = syn::parse(input).unwrap(); + impl_wrapped_vec2(&ast) +} + +fn impl_wrapped_vec2(ast: &syn::DeriveInput) -> TokenStream { + let wrapper_name = &ast.ident; + + let gen = quote! { + impl #wrapper_name { + pub const ZERO: Self = Self::new(0., 0.); + + #[inline(always)] + pub const fn new(x: f32, y: f32) -> Self { + Self(emath::Vec2::new(x, y)) + } + + #[inline(always)] + pub fn splat(v: f32) -> Self { + Self(emath::Vec2::splat(v)) + } + + #[inline(always)] + pub fn to_pos2(self) -> #wrapper_name { + #wrapper_name(self.0.to_pos2()) + } + + #[inline(always)] + pub fn relative_to(self, other: Self) -> Self { + Self(self.0 - other.0) + } + + #[inline(always)] + #[must_use] + pub fn normalized(self) -> Self { + Self(self.0.normalized()) + } + + #[inline(always)] + pub fn rot90(self) -> Self { + Self(self.0.rot90()) + } + + #[inline(always)] + pub fn angled(angle: f32) -> Self { + Self(emath::Vec2::angled(angle)) + } + + #[inline] + pub fn dot(self, other: Self) -> f32 { + self.0.dot(other.0) + } + + #[inline(always)] + pub fn floor(self) -> Self { + Self(self.0.floor()) + } + + #[inline(always)] + pub fn round(self) -> Self { + Self(self.0.round()) + } + + #[inline(always)] + pub fn ceil(self) -> Self { + Self(self.0.ceil()) + } + + #[inline] + #[must_use] + pub fn min(self, other: Self) -> Self { + Self(self.0.min(other.0)) + } + + #[inline] + #[must_use] + pub fn max(self, other: Self) -> Self { + Self(self.0.max(other.0)) + } + + #[inline] + #[must_use] + pub fn clamp(self, min: Self, max: Self) -> Self { + Self(self.0.clamp(min.0, max.0)) + } + } + + impl Neg for #wrapper_name { + type Output = Self; + + fn neg(self) -> Self::Output { + Self(self.0.neg()) + } + } + + impl Mul for #wrapper_name { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + Self(self.0.mul(rhs.0)) + } + } + + impl MulAssign for #wrapper_name { + fn mul_assign(&mut self, rhs: f32) { + self.0.mul_assign(rhs); + } + } + + impl Mul for #wrapper_name { + type Output = Self; + + fn mul(self, rhs: f32) -> Self::Output { + Self(self.0.mul(rhs)) + } + } + + impl Mul<#wrapper_name> for f32 { + type Output = #wrapper_name; + + fn mul(self, rhs: #wrapper_name) -> Self::Output { + #wrapper_name(self.mul(rhs.0)) + } + } + + impl Div for #wrapper_name { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + Self(self.0.div(rhs.0)) + } + } + + impl Div for #wrapper_name { + type Output = Self; + + fn div(self, rhs: f32) -> Self::Output { + Self(self.0.div(rhs)) + } + } + + impl AddAssign<#wrapper_name> for #wrapper_name { + fn add_assign(&mut self, rhs: #wrapper_name) { + self.0.add_assign(rhs.0); + } + } + + impl SubAssign<#wrapper_name> for #wrapper_name { + fn sub_assign(&mut self, rhs: #wrapper_name) { + self.0.sub_assign(rhs.0); + } + } + + impl Add<#wrapper_name> for #wrapper_name { + type Output = Self; + + fn add(self, rhs: #wrapper_name) -> Self::Output { + Self(self.0.add(rhs.0)) + } + } + + impl Sub<#wrapper_name> for #wrapper_name { + type Output = Self; + + fn sub(self, rhs: #wrapper_name) -> Self::Output { + Self(self.0.sub(rhs.0)) + } + } + }; + + gen.into() +} diff --git a/crates/smwe-render/Cargo.toml b/crates/smwe-render/Cargo.toml new file mode 100644 index 0000000..02e3fb1 --- /dev/null +++ b/crates/smwe-render/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "smwe-render" +version = "0.1.0" +authors = ["Adam Gąsior (Adanos020)", "Selicre"] +license = "MIT" +edition = "2021" +rust-version = "1.70" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +smwe-emu = { path = "../smwe-emu" } +smwe-math = { path = "../smwe-math" } + +emath = "0.27" +epaint = "0.27" +egui_glow = "0.27" +itertools = "0.12" +log = "0.4" +thiserror = "1.0" +serde = { version = "1.0", features = ["derive"] } +shrinkwraprs = "0.3" diff --git a/crates/smwe-render/src/basic_renderer.rs b/crates/smwe-render/src/basic_renderer.rs new file mode 100644 index 0000000..1649972 --- /dev/null +++ b/crates/smwe-render/src/basic_renderer.rs @@ -0,0 +1,135 @@ +use egui_glow::glow::*; +use itertools::Itertools; + +#[derive(Clone, Debug)] +pub struct BasicRenderer { + shader_program: Program, + vao: VertexArray, + vbo: Buffer, + vertex_count: usize, + primitive_type: u32, + destroyed: bool, +} + +#[derive(Copy, Clone, Debug)] +pub struct ShaderSources { + pub vertex_shader: &'static str, + pub geometry_shader: Option<&'static str>, + pub fragment_shader: &'static str, +} + +#[derive(Copy, Clone, Debug)] +pub struct GlVertexAttribute { + pub index: u32, + pub size: i32, + pub data_type: u32, + pub stride: i32, + pub offset: i32, +} + +pub trait BindUniforms { + /// # Safety + /// Calls to unsafe glow functions. + unsafe fn bind_uniforms(&self, gl: &Context, shader_program: Program); +} + +impl BasicRenderer { + pub fn new( + gl: &Context, shader_sources: ShaderSources, vertex_attribute: GlVertexAttribute, primitive_type: u32, + ) -> Self { + let shader_program = unsafe { gl.create_program().expect("Failed to create shader program") }; + + let mut shader_infos = + vec![(VERTEX_SHADER, shader_sources.vertex_shader), (FRAGMENT_SHADER, shader_sources.fragment_shader)]; + if let Some(geometry_shader) = shader_sources.geometry_shader { + shader_infos.push((GEOMETRY_SHADER, geometry_shader)); + } + + let shaders = shader_infos + .into_iter() + .map(|(shader_type, shader_source)| unsafe { + let shader = gl.create_shader(shader_type).expect("Failed to create shader"); + gl.shader_source(shader, shader_source); + gl.compile_shader(shader); + + debug_assert!( + gl.get_shader_compile_status(shader), + "Failed to compile {shader_type}: {}", + gl.get_shader_info_log(shader), + ); + + gl.attach_shader(shader_program, shader); + shader + }) + .collect_vec(); + + unsafe { + gl.link_program(shader_program); + assert!(gl.get_program_link_status(shader_program), "{}", gl.get_program_info_log(shader_program)); + } + + shaders.into_iter().for_each(|shader| unsafe { + gl.detach_shader(shader_program, shader); + gl.delete_shader(shader); + }); + + let vao = unsafe { gl.create_vertex_array().expect("Failed to create vertex array for TileRenderer") }; + + let vbo = unsafe { + let buf = gl.create_buffer().expect("Failed to create vertex buffer for TileRenderer"); + gl.bind_vertex_array(Some(vao)); + gl.bind_buffer(ARRAY_BUFFER, Some(buf)); + gl.enable_vertex_attrib_array(0); + gl.vertex_attrib_pointer_i32( + vertex_attribute.index, + vertex_attribute.size, + vertex_attribute.data_type, + vertex_attribute.stride, + vertex_attribute.offset, + ); + buf + }; + + Self { shader_program, vao, vbo, vertex_count: 0, primitive_type, destroyed: false } + } + + pub fn destroy(&mut self, gl: &Context) { + if self.destroyed { + log::warn!("Attempted to destroy BasicRenderer after it was already destroyed"); + return; + } + unsafe { + gl.delete_program(self.shader_program); + gl.delete_vertex_array(self.vao); + gl.delete_buffer(self.vbo); + } + self.destroyed = true; + } + + pub fn paint(&self, gl: &Context, uniforms: &impl BindUniforms) { + if self.destroyed { + log::warn!("Attempted to paint BasicRenderer after it was already destroyed"); + return; + } + unsafe { + gl.use_program(Some(self.shader_program)); + uniforms.bind_uniforms(gl, self.shader_program); + gl.bind_vertex_array(Some(self.vao)); + gl.bind_buffer(ARRAY_BUFFER, Some(self.vbo)); + gl.draw_arrays(self.primitive_type, 0, self.vertex_count as i32); + } + } + + pub fn set_vertices(&mut self, gl: &Context, vertices: Vec) { + if self.destroyed { + log::warn!("Attempted to set vertices in BasicRenderer after it was already destroyed"); + return; + } + self.vertex_count = vertices.len(); + unsafe { + gl.bind_vertex_array(Some(self.vao)); + gl.bind_buffer(ARRAY_BUFFER, Some(self.vbo)); + gl.buffer_data_u8_slice(ARRAY_BUFFER, vertices.align_to().1, DYNAMIC_DRAW); + } + } +} diff --git a/smwe-rom/src/graphics/color.rs b/crates/smwe-render/src/color.rs similarity index 79% rename from smwe-rom/src/graphics/color.rs rename to crates/smwe-render/src/color.rs index b1bd429..1de07f7 100644 --- a/smwe-rom/src/graphics/color.rs +++ b/crates/smwe-render/src/color.rs @@ -1,3 +1,4 @@ +#![allow(clippy::identity_op)] #![allow(clippy::unusual_byte_groupings)] #![allow(dead_code)] @@ -51,17 +52,23 @@ impl From for Abgr1555 { impl From for Rgba { fn from(color: Abgr1555) -> Self { let cmf = SNES_BGR_CHANNEL_MAX as f32; - Rgba::from_srgba_unmultiplied( - ((((color.0 >> 0x0) & SNES_BGR_CHANNEL_MAX) as f32 / cmf) * 255.0) as u8, - ((((color.0 >> 0x5) & SNES_BGR_CHANNEL_MAX) as f32 / cmf) * 255.0) as u8, - ((((color.0 >> 0xA) & SNES_BGR_CHANNEL_MAX) as f32 / cmf) * 255.0) as u8, - (1 - ((color.0 >> 0xF) & 1) as u8) * 255, + Rgba::from_rgba_unmultiplied( + ((color.0 >> 0x0) & SNES_BGR_CHANNEL_MAX) as f32 / cmf, + ((color.0 >> 0x5) & SNES_BGR_CHANNEL_MAX) as f32 / cmf, + ((color.0 >> 0xA) & SNES_BGR_CHANNEL_MAX) as f32 / cmf, + 1.0 - ((color.0 >> 0xF) & 1) as f32, ) } } impl From for Color32 { fn from(color: Abgr1555) -> Self { - Color32::from(Rgba::from(color)) + let cmf = SNES_BGR_CHANNEL_MAX as f32; + Color32::from_rgba_unmultiplied( + ((((color.0 >> 0x0) & SNES_BGR_CHANNEL_MAX) as f32 / cmf) * 255.) as u8, + ((((color.0 >> 0x5) & SNES_BGR_CHANNEL_MAX) as f32 / cmf) * 255.) as u8, + ((((color.0 >> 0xA) & SNES_BGR_CHANNEL_MAX) as f32 / cmf) * 255.) as u8, + ((1.0 - ((color.0 >> 0xF) & 1) as f32) * 255.) as u8, + ) } } diff --git a/crates/smwe-render/src/gfx_buffers.rs b/crates/smwe-render/src/gfx_buffers.rs new file mode 100644 index 0000000..69fd535 --- /dev/null +++ b/crates/smwe-render/src/gfx_buffers.rs @@ -0,0 +1,59 @@ +use egui_glow::glow::*; +use epaint::Rgba; +use itertools::Itertools; + +use crate::color::Abgr1555; + +#[derive(Copy, Clone, Debug)] +pub struct GfxBuffers { + pub palette_buf: Buffer, + pub vram_buf: Buffer, +} + +impl GfxBuffers { + pub fn new(gl: &Context) -> Self { + let make_buffer = |size, index| unsafe { + let buf = gl.create_buffer().expect("Failed to create buffer"); + gl.bind_buffer(ARRAY_BUFFER, Some(buf)); + gl.buffer_data_size(ARRAY_BUFFER, size, DYNAMIC_DRAW); + gl.bind_buffer_base(UNIFORM_BUFFER, index, Some(buf)); + buf + }; + let palette_buf = make_buffer(256 * 16, 0); + let vram_buf = make_buffer(0x2000, 1); + Self::from_buffers(palette_buf, vram_buf) + } + + pub fn from_buffers(palette_buf: Buffer, vram_buf: Buffer) -> Self { + Self { palette_buf, vram_buf } + } + + pub fn destroy(&self, gl: &Context) { + unsafe { + gl.delete_buffer(self.vram_buf); + gl.delete_buffer(self.palette_buf); + } + } + + pub fn upload_vram(&self, gl: &Context, data: &[u8]) { + unsafe { + gl.bind_buffer(ARRAY_BUFFER, Some(self.vram_buf)); + gl.buffer_data_u8_slice(ARRAY_BUFFER, data, DYNAMIC_DRAW); + } + } + + pub fn upload_palette(&self, gl: &Context, data: &[u8]) { + let colors = data + .iter() + .tuples::<(&u8, &u8)>() + .map(|(b1, b2)| u16::from_le_bytes([*b1, *b2])) + .map(Abgr1555) + .map(Rgba::from) + .flat_map(|color| color.to_array()) + .collect_vec(); + unsafe { + gl.bind_buffer(ARRAY_BUFFER, Some(self.palette_buf)); + gl.buffer_data_u8_slice(ARRAY_BUFFER, colors.align_to().1, DYNAMIC_DRAW); + } + } +} diff --git a/crates/smwe-render/src/lib.rs b/crates/smwe-render/src/lib.rs new file mode 100644 index 0000000..52a3a8f --- /dev/null +++ b/crates/smwe-render/src/lib.rs @@ -0,0 +1,5 @@ +pub mod basic_renderer; +pub mod color; +pub mod gfx_buffers; +pub mod palette_renderer; +pub mod tile_renderer; diff --git a/crates/smwe-render/src/palette_renderer/mod.rs b/crates/smwe-render/src/palette_renderer/mod.rs new file mode 100644 index 0000000..e0fb71a --- /dev/null +++ b/crates/smwe-render/src/palette_renderer/mod.rs @@ -0,0 +1,55 @@ +use egui_glow::glow::*; + +use crate::basic_renderer::{BasicRenderer, BindUniforms, GlVertexAttribute, ShaderSources}; + +const VERTEX_SHADER_SRC: &str = include_str!("palette.vs.glsl"); +const FRAGMENT_SHADER_SRC: &str = include_str!("palette.fs.glsl"); + +#[derive(Debug)] +pub struct PaletteRenderer { + renderer: BasicRenderer, +} + +#[derive(Debug)] +pub struct PaletteUniforms { + pub palette_buf: Buffer, + pub viewed_palettes: u32, +} + +impl PaletteRenderer { + pub fn new(gl: &Context) -> Self { + let shader_sources = ShaderSources { + vertex_shader: VERTEX_SHADER_SRC, + geometry_shader: None, + fragment_shader: FRAGMENT_SHADER_SRC, + }; + let vertex_attribute = + GlVertexAttribute { index: 0, size: 1, data_type: INT, stride: 0, offset: 0 }; + let mut renderer = BasicRenderer::new(gl, shader_sources, vertex_attribute, TRIANGLE_STRIP); + + let vertices = vec![0b00, 0b10, 0b01, 0b11]; + renderer.set_vertices(gl, vertices); + + Self { renderer } + } + + pub fn destroy(&mut self, gl: &Context) { + self.renderer.destroy(gl); + } + + pub fn paint(&self, gl: &Context, uniforms: &PaletteUniforms) { + self.renderer.paint(gl, uniforms); + } +} + +impl BindUniforms for PaletteUniforms { + unsafe fn bind_uniforms(&self, gl: &Context, shader_program: Program) { + let u = gl.get_uniform_location(shader_program, "u_viewed_palettes"); + gl.uniform_1_u32(u.as_ref(), self.viewed_palettes); + + gl.bind_buffer_base(UNIFORM_BUFFER, 0, Some(self.palette_buf)); + let palette_block = + gl.get_uniform_block_index(shader_program, "Color").expect("Failed to get uniform block 'Color'"); + gl.uniform_block_binding(shader_program, palette_block, 0); + } +} diff --git a/crates/smwe-render/src/palette_renderer/palette.fs.glsl b/crates/smwe-render/src/palette_renderer/palette.fs.glsl new file mode 100644 index 0000000..1bb7351 --- /dev/null +++ b/crates/smwe-render/src/palette_renderer/palette.fs.glsl @@ -0,0 +1,20 @@ +#version 400 + +in vec2 v_tex_coords; + +layout(std140) uniform Color { + vec4 colors[0x100]; +}; + +out vec4 out_color; + +void main() { + uint color_col = uint(v_tex_coords.x * 0x10); + uint color_row = uint(v_tex_coords.y * 0x10); + if (color_col == 0) { + discard; + } else { + uint color_idx = color_col + color_row * 0x10; + out_color = colors[color_idx]; + } +} diff --git a/crates/smwe-render/src/palette_renderer/palette.vs.glsl b/crates/smwe-render/src/palette_renderer/palette.vs.glsl new file mode 100644 index 0000000..e0ae867 --- /dev/null +++ b/crates/smwe-render/src/palette_renderer/palette.vs.glsl @@ -0,0 +1,33 @@ +#version 400 + +#define VIEW_ALL_PALETTES 0 +#define VIEW_BACKGROUND_PALETTES 1 +#define VIEW_SPRITE_PALETTES 2 + +layout (location = 0) in int vertex_index; + +uniform uint u_viewed_palettes; + +out vec2 v_tex_coords; + +void main() { + float x = float(vertex_index & 2); + float y = float(vertex_index & 1); + + switch (u_viewed_palettes) { + case VIEW_ALL_PALETTES: + v_tex_coords = vec2(x, y); + break; + case VIEW_BACKGROUND_PALETTES: + v_tex_coords = vec2(x, y * 0.5); + break; + case VIEW_SPRITE_PALETTES: + v_tex_coords = vec2(x, (y * 0.5) + 0.5); + break; + default: + v_tex_coords = vec2(0); + break; + } + + gl_Position = vec4((2 * x) - 1, 1 - (2 * y), 0, 1); +} diff --git a/crates/smwe-render/src/tile_renderer/mod.rs b/crates/smwe-render/src/tile_renderer/mod.rs new file mode 100644 index 0000000..7bf8522 --- /dev/null +++ b/crates/smwe-render/src/tile_renderer/mod.rs @@ -0,0 +1,224 @@ +use egui_glow::glow::*; +use emath::*; +use serde::{Deserialize, Serialize}; +use shrinkwraprs::Shrinkwrap; +use smwe_math::coordinates::{OnCanvas, OnScreen}; +use thiserror::Error; + +use crate::{ + basic_renderer::{BasicRenderer, BindUniforms, GlVertexAttribute, ShaderSources}, + gfx_buffers::GfxBuffers, +}; + +const VERTEX_SHADER_SRC: &str = include_str!("tile.vs.glsl"); +const GEOMETRY_SHADER_SRC: &str = include_str!("tile.gs.glsl"); +const FRAGMENT_SHADER_SRC: &str = include_str!("tile.fs.glsl"); + +#[derive(Debug)] +pub struct TileRenderer { + renderer: BasicRenderer, +} + +#[derive(Debug)] +pub struct TileUniforms { + pub gfx_bufs: GfxBuffers, + pub screen_size: Vec2, + pub offset: Vec2, + pub zoom: f32, +} + +#[derive(Copy, Clone, Debug, Default, Shrinkwrap)] +#[shrinkwrap(mutable)] +pub struct Tile(pub [u32; 4]); + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct TileJson { + x: u32, + y: u32, + tile_id: u32, + scale: u8, + color_row: u8, + flip_x: bool, + flip_y: bool, +} + +#[derive(Debug, Error)] +#[error("Could not deserialize tile")] +pub struct TileDeserializeError; + +impl TileRenderer { + pub fn new(gl: &Context) -> Self { + let shader_sources = ShaderSources { + vertex_shader: VERTEX_SHADER_SRC, + geometry_shader: Some(GEOMETRY_SHADER_SRC), + fragment_shader: FRAGMENT_SHADER_SRC, + }; + let vertex_attribute = + GlVertexAttribute { index: 0, size: 4, data_type: INT, stride: 0, offset: 0 }; + let renderer = BasicRenderer::new(gl, shader_sources, vertex_attribute, POINTS); + Self { renderer } + } + + pub fn destroy(&mut self, gl: &Context) { + self.renderer.destroy(gl); + } + + pub fn paint(&self, gl: &Context, uniforms: &TileUniforms) { + self.renderer.paint(gl, uniforms); + } + + pub fn set_tiles(&mut self, gl: &Context, tiles: Vec) { + self.renderer.set_vertices(gl, tiles); + } +} + +impl BindUniforms for TileUniforms { + unsafe fn bind_uniforms(&self, gl: &Context, shader_program: Program) { + let u = gl.get_uniform_location(shader_program, "offset"); + gl.uniform_2_f32(u.as_ref(), self.offset.x, self.offset.y); + + let u = gl.get_uniform_location(shader_program, "screen_size"); + gl.uniform_2_f32(u.as_ref(), self.screen_size.x, self.screen_size.y); + + let u = gl.get_uniform_location(shader_program, "zoom"); + gl.uniform_1_f32(u.as_ref(), self.zoom); + + gl.bind_buffer_base(UNIFORM_BUFFER, 0, Some(self.gfx_bufs.palette_buf)); + let palette_block = + gl.get_uniform_block_index(shader_program, "Color").expect("Failed to get uniform block 'Color'"); + gl.uniform_block_binding(shader_program, palette_block, 0); + + gl.bind_buffer_base(UNIFORM_BUFFER, 1, Some(self.gfx_bufs.vram_buf)); + let vram_block = + gl.get_uniform_block_index(shader_program, "Graphics").expect("Failed to get uniform block 'Graphics'"); + gl.uniform_block_binding(shader_program, vram_block, 1); + } +} + +impl Tile { + #[inline] + pub fn from_le_bytes(bytes: [u8; 16]) -> Self { + Self([ + u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]), + u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]), + u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]), + u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]), + ]) + } + + #[inline] + pub fn pos(self) -> OnCanvas { + OnCanvas(pos2(self.0[0] as f32, self.0[1] as f32)) + } + + #[inline] + pub fn rect(self) -> OnCanvas { + let min = self.pos().0; + let size = Vec2::splat(self.scale() as f32); + OnCanvas(Rect::from_min_size(min, size)) + } + + #[inline] + pub fn tile_num(self) -> u32 { + self.0[2] + } + + #[inline] + pub fn scale(self) -> u32 { + self.0[3] & 0xFF + } + + #[inline] + pub fn color_row(self) -> u32 { + (self.0[3] >> 8) & 0xF + } + + #[inline] + pub fn flip_x(self) -> bool { + (self.0[3] & 0x4000) != 0 + } + + #[inline] + pub fn flip_y(self) -> bool { + (self.0[3] & 0x8000) != 0 + } + + #[inline] + pub fn toggle_flip_x(&mut self) { + let flag = ((self.0[3] & 0x4000) == 0) as i32; + self.0[3] ^= ((-flag) as u32 ^ self.0[3]) & 0x4000; + } + + #[inline] + pub fn toggle_flip_y(&mut self) { + let flag = ((self.0[3] & 0x8000) == 0) as i32; + self.0[3] ^= ((-flag) as u32 ^ self.0[3]) & 0x8000; + } + + #[inline] + pub fn move_by(&mut self, offset: OnCanvas) { + self.0[0] = (self.0[0] as i32 + offset.0[0] as i32) as u32; + self.0[1] = (self.0[1] as i32 + offset.0[1] as i32) as u32; + } + + #[inline] + pub fn move_to(&mut self, point: OnCanvas) { + self.0[0] = point.floor().0[0] as u32; + self.0[1] = point.floor().0[1] as u32; + } + + #[inline] + pub fn snap_to_grid(&mut self, cell_size: u32, cell_origin: OnScreen) { + let cell_size = cell_size as f32; + self.0[0] = { + let origin_pos = self.0[0] as f32 + cell_origin.0.x; + let unscaled = origin_pos / cell_size; + let cell_coord = unscaled.floor(); + (cell_coord * cell_size) as u32 + }; + self.0[1] = { + let origin_pos = self.0[1] as f32 + cell_origin.0.y; + let unscaled = origin_pos / cell_size; + let cell_coord = unscaled.floor(); + (cell_coord * cell_size) as u32 + }; + } + + #[inline] + pub fn contains_point(self, point: OnCanvas) -> bool { + self.rect().contains(point) + } + + #[inline] + pub fn intersects_rect(self, rect: OnCanvas) -> bool { + self.rect().intersects(rect) + } +} + +impl From for TileJson { + fn from(value: Tile) -> Self { + Self { + x: value[0], + y: value[1], + tile_id: value.tile_num(), + scale: value.scale() as u8, + color_row: value.color_row() as u8, + flip_x: value.flip_x(), + flip_y: value.flip_y(), + } + } +} + +impl From for Tile { + fn from(value: TileJson) -> Self { + Self([ + value.x, + value.y, + value.tile_id, + value.scale as u32 + | ((value.color_row as u32 & 0xF) << 8) + | (0x4000 * value.flip_x as u32) + | (0x8000 * value.flip_y as u32), + ]) + } +} diff --git a/crates/smwe-render/src/tile_renderer/tile.fs.glsl b/crates/smwe-render/src/tile_renderer/tile.fs.glsl new file mode 100644 index 0000000..1182f2d --- /dev/null +++ b/crates/smwe-render/src/tile_renderer/tile.fs.glsl @@ -0,0 +1,56 @@ +#version 400 +flat in int g_tile_id; +flat in int g_params; +in vec2 g_tex_coords; + +uniform vec2 screen_size; +uniform float zoom; + +layout(std140) uniform Graphics { + uvec4 graphics[0x1000]; +}; +layout(std140) uniform Color { + vec4 colors[0x100]; +}; + +out vec4 out_color; + +void main() { + int scale = g_params & 0xFF; + + int tile_id = g_tile_id; + int color_row = (g_params >> 8) & 0xF; + ivec2 icoord = ivec2(g_tex_coords) * 8 / int(scale * zoom); + + bool flip_x = (g_params & 0x4000) != 0; + bool flip_y = (g_params & 0x8000) != 0; + + if (flip_y) { + icoord.y = 7 - icoord.y; + } + if (flip_x) { + icoord.x = 7 - icoord.x; + } + + uvec4 part1 = graphics[tile_id * 2 + 0]; + uvec4 part2 = graphics[tile_id * 2 + 1]; + + uint lpart1 = part1[icoord.y / 2]; + uint lpart2 = part2[icoord.y / 2]; + + int line1 = int(lpart1 >> ((icoord.y % 4) * 16)); + int line2 = int(lpart2 >> ((icoord.y % 4) * 16)); + + int color_col = 0; + color_col |= ((line1 >> ( 7 - icoord.x)) & 0x1) << 0; + color_col |= ((line1 >> (15 - icoord.x)) & 0x1) << 1; + color_col |= ((line2 >> ( 7 - icoord.x)) & 0x1) << 2; + color_col |= ((line2 >> (15 - icoord.x)) & 0x1) << 3; + + if (color_col == 0) { + discard; + } else { + uint color_idx = color_col + color_row * 0x10; + out_color = colors[color_idx]; + } +} diff --git a/crates/smwe-render/src/tile_renderer/tile.gs.glsl b/crates/smwe-render/src/tile_renderer/tile.gs.glsl new file mode 100644 index 0000000..a57c6a0 --- /dev/null +++ b/crates/smwe-render/src/tile_renderer/tile.gs.glsl @@ -0,0 +1,49 @@ +#version 400 +layout(points) in; +in int v_tile_id[1]; +in int v_params[1]; + +uniform vec2 screen_size; +uniform float zoom; + +layout(triangle_strip, max_vertices = 4) out; +flat out int g_tile_id; +flat out int g_params; +out vec2 g_tex_coords; + +void main() { + vec2 position = gl_in[0].gl_Position.xy; + + g_tile_id = v_tile_id[0]; + g_params = v_params[0]; + float scale = float(v_params[0] & 0xFF) * zoom; + + vec2 pos; + vec2 p; + + g_tex_coords = vec2(0.0, 0.0); + pos = position + vec2(0.0, 0.0); + p = (pos / screen_size * 2.0 - 1.0) * vec2(1.0, -1.0); + gl_Position = vec4(p, 0.0, 1.0); + EmitVertex(); + + g_tex_coords = vec2(scale, 0.0); + pos = position + vec2(scale, 0.0); + p = (pos / screen_size * 2.0 - 1.0) * vec2(1.0, -1.0); + gl_Position = vec4(p, 0.0, 1.0); + EmitVertex(); + + g_tex_coords = vec2(0.0, scale); + pos = position + vec2(0.0, scale); + p = (pos / screen_size * 2.0 - 1.0) * vec2(1.0, -1.0); + gl_Position = vec4(p, 0.0, 1.0); + EmitVertex(); + + g_tex_coords = vec2(scale); + pos = position + vec2(scale); + p = (pos / screen_size * 2.0 - 1.0) * vec2(1.0, -1.0); + gl_Position = vec4(p, 0.0, 1.0); + EmitVertex(); + + EndPrimitive(); +} diff --git a/crates/smwe-render/src/tile_renderer/tile.vs.glsl b/crates/smwe-render/src/tile_renderer/tile.vs.glsl new file mode 100644 index 0000000..a658620 --- /dev/null +++ b/crates/smwe-render/src/tile_renderer/tile.vs.glsl @@ -0,0 +1,15 @@ +#version 400 +layout (location = 0) in ivec4 position; + +uniform vec2 screen_size; +uniform vec2 offset; +uniform float zoom; + +out int v_tile_id; +out int v_params; + +void main() { + v_tile_id = position.z; + v_params = position.w; + gl_Position = vec4((vec2(position.xy) + offset) * zoom, 0.0, 1.0); +} diff --git a/smwe-rom/Cargo.toml b/crates/smwe-rom/Cargo.toml similarity index 81% rename from smwe-rom/Cargo.toml rename to crates/smwe-rom/Cargo.toml index 027fa31..5cfa7b3 100644 --- a/smwe-rom/Cargo.toml +++ b/crates/smwe-rom/Cargo.toml @@ -3,19 +3,21 @@ name = "smwe-rom" version = "0.1.0" authors = ["Adam Gąsior (Adanos020)", "Raven Szewczyk (eigenraven)"] edition = "2021" -rust-version = "1.65" +rust-version = "1.70" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +smwe-render = { path = "../smwe-render" } + anyhow = "1.0" duplicate = "1.0" -epaint = "0.21" -itertools = "0.10" +epaint = "0.27" +itertools = "0.12" log = "0.4" nom = "7.0" num-traits = "0.2" -num_enum = "0.6" +num_enum = "0.7" paste = "1.0" thiserror = "1.0" serde = { version = "1.0", features = ["derive"] } diff --git a/smwe-rom/src/compression/lc_lz2.rs b/crates/smwe-rom/src/compression/lc_lz2.rs similarity index 100% rename from smwe-rom/src/compression/lc_lz2.rs rename to crates/smwe-rom/src/compression/lc_lz2.rs diff --git a/smwe-rom/src/compression/lc_rle1.rs b/crates/smwe-rom/src/compression/lc_rle1.rs similarity index 100% rename from smwe-rom/src/compression/lc_rle1.rs rename to crates/smwe-rom/src/compression/lc_rle1.rs diff --git a/smwe-rom/src/compression/mod.rs b/crates/smwe-rom/src/compression/mod.rs similarity index 100% rename from smwe-rom/src/compression/mod.rs rename to crates/smwe-rom/src/compression/mod.rs diff --git a/smwe-rom/src/disassembler/binary_block.rs b/crates/smwe-rom/src/disassembler/binary_block.rs similarity index 100% rename from smwe-rom/src/disassembler/binary_block.rs rename to crates/smwe-rom/src/disassembler/binary_block.rs diff --git a/smwe-rom/src/disassembler/instruction.rs b/crates/smwe-rom/src/disassembler/instruction.rs similarity index 100% rename from smwe-rom/src/disassembler/instruction.rs rename to crates/smwe-rom/src/disassembler/instruction.rs diff --git a/smwe-rom/src/disassembler/jump_tables/data.rs b/crates/smwe-rom/src/disassembler/jump_tables/data.rs similarity index 100% rename from smwe-rom/src/disassembler/jump_tables/data.rs rename to crates/smwe-rom/src/disassembler/jump_tables/data.rs diff --git a/smwe-rom/src/disassembler/jump_tables/mod.rs b/crates/smwe-rom/src/disassembler/jump_tables/mod.rs similarity index 100% rename from smwe-rom/src/disassembler/jump_tables/mod.rs rename to crates/smwe-rom/src/disassembler/jump_tables/mod.rs diff --git a/smwe-rom/src/disassembler/mod.rs b/crates/smwe-rom/src/disassembler/mod.rs similarity index 99% rename from smwe-rom/src/disassembler/mod.rs rename to crates/smwe-rom/src/disassembler/mod.rs index 98b2dc9..74dc12b 100644 --- a/smwe-rom/src/disassembler/mod.rs +++ b/crates/smwe-rom/src/disassembler/mod.rs @@ -139,7 +139,7 @@ impl RomDisassembly { None } }) - .unwrap_or(Vec::new()) + .unwrap_or_default() }, } } @@ -505,7 +505,7 @@ impl RomAssemblyWalker { .map(|&a| AddrPc::try_from(a).unwrap()) .filter(|&a| sub.analysed_blocks.insert(a)) .collect_vec(); - sub.remaining_blocks.extend(pending_blocks.into_iter()); + sub.remaining_blocks.extend(pending_blocks); } } diff --git a/smwe-rom/src/disassembler/opcodes/data.rs b/crates/smwe-rom/src/disassembler/opcodes/data.rs similarity index 100% rename from smwe-rom/src/disassembler/opcodes/data.rs rename to crates/smwe-rom/src/disassembler/opcodes/data.rs diff --git a/smwe-rom/src/disassembler/opcodes/mod.rs b/crates/smwe-rom/src/disassembler/opcodes/mod.rs similarity index 100% rename from smwe-rom/src/disassembler/opcodes/mod.rs rename to crates/smwe-rom/src/disassembler/opcodes/mod.rs diff --git a/smwe-rom/src/disassembler/processor.rs b/crates/smwe-rom/src/disassembler/processor.rs similarity index 100% rename from smwe-rom/src/disassembler/processor.rs rename to crates/smwe-rom/src/disassembler/processor.rs diff --git a/smwe-rom/src/disassembler/registers.rs b/crates/smwe-rom/src/disassembler/registers.rs similarity index 100% rename from smwe-rom/src/disassembler/registers.rs rename to crates/smwe-rom/src/disassembler/registers.rs diff --git a/smwe-rom/src/disassembler/serialization.rs b/crates/smwe-rom/src/disassembler/serialization.rs similarity index 91% rename from smwe-rom/src/disassembler/serialization.rs rename to crates/smwe-rom/src/disassembler/serialization.rs index d9f1fc1..5629f5a 100644 --- a/smwe-rom/src/disassembler/serialization.rs +++ b/crates/smwe-rom/src/disassembler/serialization.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -#[derive(Deserialize, Serialize)] +#[derive(Debug, Deserialize, Serialize)] #[serde(untagged)] pub enum LineKind { Meta { diff --git a/smwe-rom/src/graphics/gfx_file/data.rs b/crates/smwe-rom/src/graphics/gfx_file/data.rs similarity index 100% rename from smwe-rom/src/graphics/gfx_file/data.rs rename to crates/smwe-rom/src/graphics/gfx_file/data.rs diff --git a/smwe-rom/src/graphics/gfx_file/mod.rs b/crates/smwe-rom/src/graphics/gfx_file/mod.rs similarity index 99% rename from smwe-rom/src/graphics/gfx_file/mod.rs rename to crates/smwe-rom/src/graphics/gfx_file/mod.rs index 724421a..d286258 100644 --- a/smwe-rom/src/graphics/gfx_file/mod.rs +++ b/crates/smwe-rom/src/graphics/gfx_file/mod.rs @@ -5,11 +5,11 @@ use std::fmt::{self, Display, Formatter}; pub(crate) use data::GFX_FILES_META; use epaint::Rgba; use nom::{bytes::complete::take, combinator::map_parser, multi::many1, IResult}; +use smwe_render::color::Abgr1555; use thiserror::Error; use crate::{ compression::{lc_lz2, DecompressionError}, - graphics::color::Abgr1555, DataBlock, DataKind, RomDisassembly, @@ -48,7 +48,7 @@ pub struct Tile { pub color_indices: Box<[u8]>, } -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct GfxFile { pub tile_format: TileFormat, pub tiles: Vec, diff --git a/smwe-rom/src/graphics/mod.rs b/crates/smwe-rom/src/graphics/mod.rs similarity index 99% rename from smwe-rom/src/graphics/mod.rs rename to crates/smwe-rom/src/graphics/mod.rs index e44dfb1..8111ddc 100644 --- a/smwe-rom/src/graphics/mod.rs +++ b/crates/smwe-rom/src/graphics/mod.rs @@ -18,7 +18,6 @@ use crate::{ RomInternalHeader, }; -pub mod color; pub mod gfx_file; pub mod palette; @@ -30,11 +29,13 @@ pub struct TileFromWramError(AddrSnes); // ------------------------------------------------------------------------------------------------- +#[derive(Debug)] pub enum BlockGfx<'t> { Animated(Vec<[&'t Tile; 4]>), Static([&'t Tile; 4]), } +#[derive(Debug)] pub struct Gfx { pub files: Vec, pub color_palettes: ColorPalettes, diff --git a/smwe-rom/src/graphics/palette.rs b/crates/smwe-rom/src/graphics/palette.rs similarity index 97% rename from smwe-rom/src/graphics/palette.rs rename to crates/smwe-rom/src/graphics/palette.rs index 3e51aff..89efc6c 100644 --- a/smwe-rom/src/graphics/palette.rs +++ b/crates/smwe-rom/src/graphics/palette.rs @@ -1,12 +1,12 @@ -use std::{convert::TryInto, ops::RangeInclusive}; +use std::ops::RangeInclusive; use duplicate::duplicate; use nom::{combinator::map, multi::many1, number::complete::le_u16}; use paste::paste; +use smwe_render::color::{Abgr1555, ABGR1555_SIZE}; use thiserror::Error; use crate::{ - graphics::color::{Abgr1555, ABGR1555_SIZE}, level::{headers::PrimaryHeader, Level}, snes_utils::{addr::AddrSnes, rom_slice::SnesSlice}, DataBlock, @@ -145,7 +145,7 @@ macro_rules! impl_color_palette { // ------------------------------------------------------------------------------------------------- -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct ColorPalettes { pub players: Box<[Abgr1555]>, @@ -162,14 +162,14 @@ pub struct ColorPalettes { pub lv_specific_set: LevelColorPaletteSet, } -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct OverworldColorPaletteSet { pub layer2_pre_special: Vec>, pub layer2_post_special: Vec>, pub layer2_indices: Vec, } -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct LevelColorPaletteSet { pub back_area_colors: Vec, pub bg_palettes: Vec>, @@ -177,7 +177,7 @@ pub struct LevelColorPaletteSet { pub sprite_palettes: Vec>, } -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct SpecificLevelColorPalette { pub back_area_color: Abgr1555, pub background: Box<[Abgr1555]>, @@ -190,7 +190,7 @@ pub struct SpecificLevelColorPalette { pub animated: Box<[Abgr1555]>, } -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct SpecificOverworldColorPalette { pub layer1: Box<[Abgr1555]>, pub layer2: Box<[Abgr1555]>, @@ -200,7 +200,7 @@ pub struct SpecificOverworldColorPalette { pub wtf: Box<[Abgr1555]>, } -#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum OverworldState { PreSpecial, PostSpecial, @@ -349,9 +349,9 @@ impl LevelColorPaletteSet { } palette_set.back_area_colors[idx_bc] = bc[0]; - palette_set.bg_palettes[idx_bg] = bg.try_into().unwrap(); - palette_set.fg_palettes[idx_fg] = fg.try_into().unwrap(); - palette_set.sprite_palettes[idx_sp] = sp.try_into().unwrap(); + palette_set.bg_palettes[idx_bg] = bg.into(); + palette_set.fg_palettes[idx_fg] = fg.into(); + palette_set.sprite_palettes[idx_sp] = sp.into(); } Ok(palette_set) diff --git a/smwe-rom/src/internal_header.rs b/crates/smwe-rom/src/internal_header.rs similarity index 98% rename from smwe-rom/src/internal_header.rs rename to crates/smwe-rom/src/internal_header.rs index 0832d54..98f50e3 100644 --- a/smwe-rom/src/internal_header.rs +++ b/crates/smwe-rom/src/internal_header.rs @@ -69,6 +69,7 @@ pub mod sizes { // ------------------------------------------------------------------------------------------------- +#[derive(Debug)] pub struct RomInternalHeader { pub internal_rom_name: String, pub map_mode: MapMode, @@ -94,7 +95,7 @@ pub enum MapMode { FastExHiRom = 0b110100, } -#[derive(Copy, Clone, IntoPrimitive, TryFromPrimitive)] +#[derive(Copy, Clone, Debug, IntoPrimitive, TryFromPrimitive)] #[repr(u8)] pub enum RomType { Rom = 0x00, @@ -138,7 +139,7 @@ pub enum RomType { RomCustomSram = 0xF6, } -#[derive(TryFromPrimitive)] +#[derive(Debug, TryFromPrimitive)] #[repr(u8)] pub enum RegionCode { Japan = 0x00, @@ -216,7 +217,7 @@ impl RomInternalHeader { .with_error_mapper(InternalHeaderParseError::ReadEmulationModeInterruptVectors) .slice_pc(vectors_slice.skip_forward(1).offset_forward(4)) .parse(&mut parse_vectors)?; - native.into_iter().chain(emulation.into_iter()).collect() + native.into_iter().chain(emulation).collect() }, }) } diff --git a/smwe-rom/src/level/background.rs b/crates/smwe-rom/src/level/background.rs similarity index 96% rename from smwe-rom/src/level/background.rs rename to crates/smwe-rom/src/level/background.rs index 331eca6..8a88879 100644 --- a/smwe-rom/src/level/background.rs +++ b/crates/smwe-rom/src/level/background.rs @@ -4,7 +4,7 @@ use crate::compression::{lc_rle1, DecompressionError}; pub type BackgroundTileID = u8; -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct BackgroundData { _tile_ids: Vec, } diff --git a/smwe-rom/src/level/headers.rs b/crates/smwe-rom/src/level/headers.rs similarity index 96% rename from smwe-rom/src/level/headers.rs rename to crates/smwe-rom/src/level/headers.rs index f7b7490..31620dd 100644 --- a/smwe-rom/src/level/headers.rs +++ b/crates/smwe-rom/src/level/headers.rs @@ -13,14 +13,14 @@ pub const PRIMARY_HEADER_SIZE: usize = 5; pub const SECONDARY_HEADER_SIZE: usize = 4; pub const SPRITE_HEADER_SIZE: usize = 1; -#[derive(Clone)] -pub struct PrimaryHeader([u8; PRIMARY_HEADER_SIZE]); +#[derive(Debug, Clone)] +pub struct PrimaryHeader(pub [u8; PRIMARY_HEADER_SIZE]); -#[derive(Clone)] -pub struct SecondaryHeader([u8; SECONDARY_HEADER_SIZE]); +#[derive(Debug, Clone)] +pub struct SecondaryHeader(pub [u8; SECONDARY_HEADER_SIZE]); -#[derive(Clone)] -pub struct SpriteHeader(u8); +#[derive(Debug, Clone)] +pub struct SpriteHeader(pub u8); impl PrimaryHeader { pub fn new(bytes: &[u8]) -> Self { @@ -110,7 +110,7 @@ impl SecondaryHeader { pub fn read_from_rom(disasm: &mut RomDisassembly, level_num: u32) -> Result { let mut bytes = [0; 4]; let byte_table_addrs = [0x05F000, 0x05F200, 0x05F400, 0x05F600]; - for (byte, addr) in bytes.iter_mut().zip(byte_table_addrs.into_iter()) { + for (byte, addr) in bytes.iter_mut().zip(byte_table_addrs) { let data_block = DataBlock { slice: SnesSlice::new(AddrSnes(addr), 0x200), kind: DataKind::LevelHeaderSecondaryByteTable, diff --git a/smwe-rom/src/level/mod.rs b/crates/smwe-rom/src/level/mod.rs similarity index 99% rename from smwe-rom/src/level/mod.rs rename to crates/smwe-rom/src/level/mod.rs index 95a02dd..6af23b9 100644 --- a/smwe-rom/src/level/mod.rs +++ b/crates/smwe-rom/src/level/mod.rs @@ -62,13 +62,13 @@ pub const LEVEL_COUNT: usize = 0x200; // ------------------------------------------------------------------------------------------------- -#[derive(Clone)] +#[derive(Debug, Clone)] pub enum Layer2Data { Background(BackgroundData), Objects(ObjectLayer), } -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct Level { pub primary_header: PrimaryHeader, pub secondary_header: SecondaryHeader, diff --git a/smwe-rom/src/level/object_layer.rs b/crates/smwe-rom/src/level/object_layer.rs similarity index 95% rename from smwe-rom/src/level/object_layer.rs rename to crates/smwe-rom/src/level/object_layer.rs index 732f8b4..14b5308 100644 --- a/smwe-rom/src/level/object_layer.rs +++ b/crates/smwe-rom/src/level/object_layer.rs @@ -1,3 +1,5 @@ +#![allow(dead_code)] + use std::convert::TryInto; use nom::{ @@ -12,32 +14,32 @@ pub const EXIT_INSTANCE_SIZE: usize = 4; pub type StandardObjectID = u8; pub type ExtendedObjectID = u8; -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct StandardObject([u8; NON_EXIT_INSTANCE_SIZE]); -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct ExitObject([u8; EXIT_INSTANCE_SIZE]); -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct ScreenJumpObject([u8; NON_EXIT_INSTANCE_SIZE]); -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct ExtendedOtherObject([u8; NON_EXIT_INSTANCE_SIZE]); -#[derive(Clone)] +#[derive(Debug, Clone)] pub enum ExtendedInstance { Exit(ExitObject), ScreenJump(ScreenJumpObject), Other(ExtendedOtherObject), } -#[derive(Clone)] +#[derive(Debug, Clone)] pub enum ObjectInstance { Standard(StandardObject), Extended(ExtendedInstance), } -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct ObjectLayer { _objects: Vec, } diff --git a/smwe-rom/src/level/secondary_entrance.rs b/crates/smwe-rom/src/level/secondary_entrance.rs similarity index 99% rename from smwe-rom/src/level/secondary_entrance.rs rename to crates/smwe-rom/src/level/secondary_entrance.rs index 056b2c9..6d0a019 100644 --- a/smwe-rom/src/level/secondary_entrance.rs +++ b/crates/smwe-rom/src/level/secondary_entrance.rs @@ -7,6 +7,7 @@ use crate::{ pub const SECONDARY_ENTRANCE_TABLE: SnesSlice = SnesSlice::new(AddrSnes(0x05F800), 512); +#[derive(Debug)] pub struct SecondaryEntrance([u8; 4]); impl SecondaryEntrance { diff --git a/smwe-rom/src/level/sprite_layer.rs b/crates/smwe-rom/src/level/sprite_layer.rs similarity index 97% rename from smwe-rom/src/level/sprite_layer.rs rename to crates/smwe-rom/src/level/sprite_layer.rs index 7026f63..caa2437 100644 --- a/smwe-rom/src/level/sprite_layer.rs +++ b/crates/smwe-rom/src/level/sprite_layer.rs @@ -10,10 +10,10 @@ pub const SPRITE_INSTANCE_SIZE: usize = 3; pub type SpriteID = u8; -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct SpriteInstance([u8; SPRITE_INSTANCE_SIZE]); -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct SpriteLayer { _sprites: Vec, } diff --git a/smwe-rom/src/lib.rs b/crates/smwe-rom/src/lib.rs similarity index 99% rename from smwe-rom/src/lib.rs rename to crates/smwe-rom/src/lib.rs index 1846f44..4a6e068 100644 --- a/smwe-rom/src/lib.rs +++ b/crates/smwe-rom/src/lib.rs @@ -32,6 +32,7 @@ use crate::{ // ------------------------------------------------------------------------------------------------- +#[derive(Debug)] pub struct SmwRom { pub disassembly: RomDisassembly, pub internal_header: RomInternalHeader, diff --git a/smwe-rom/src/objects/animated_tile_data.rs b/crates/smwe-rom/src/objects/animated_tile_data.rs similarity index 99% rename from smwe-rom/src/objects/animated_tile_data.rs rename to crates/smwe-rom/src/objects/animated_tile_data.rs index 448d7af..13b9342 100644 --- a/smwe-rom/src/objects/animated_tile_data.rs +++ b/crates/smwe-rom/src/objects/animated_tile_data.rs @@ -27,6 +27,7 @@ const ANIM_BEHAVIOUR_TABLE: SnesSlice = SnesSlice::new(AddrSnes(0x05B96B), 46); // ------------------------------------------------------------------------------------------------- +#[derive(Debug)] pub struct AnimatedTileData { pub src_addresses: Vec, pub dst_addresses: Vec, diff --git a/smwe-rom/src/objects/map16.rs b/crates/smwe-rom/src/objects/map16.rs similarity index 100% rename from smwe-rom/src/objects/map16.rs rename to crates/smwe-rom/src/objects/map16.rs diff --git a/crates/smwe-rom/src/objects/mod.rs b/crates/smwe-rom/src/objects/mod.rs new file mode 100644 index 0000000..5560805 --- /dev/null +++ b/crates/smwe-rom/src/objects/mod.rs @@ -0,0 +1,172 @@ +pub mod animated_tile_data; +pub mod map16; +pub mod object_gfx_list; +pub mod tilesets; + +/// # Object format +/// +/// ## Standard +/// +/// `NBBYYYYY bbbbXXXX SSSSSSSS` +/// +/// | Value | Comment | +/// |------------|------------------------| +/// | `N` | New Screen flag | +/// | `BBbbbb` | Standard object number | +/// | `YYYYY` | Y position | +/// | `XXXX` | X position | +/// | `SSSSSSSS` | Settings | +/// +/// ## Extended +/// +/// `N00YYYYY 0000XXXX BBBBBBBB` +/// +/// | Value | Comment | +/// |----------|------------------------| +/// | N | New Screen flag | +/// | YYYYY | Y position | +/// | XXXX | X position | +/// | BBBBBBBB | Extended object number | +/// +/// ## Exit +/// +/// `000ppppp 0000w0sh 00000000 dddddddd` +/// +/// | Value | Comment | +/// |-----------|---------------------------------------| +/// | ppppp | Screen number | +/// | w | Midway | +/// | s | Secondary exit flag | +/// | hdddddddd | Destination level / secondary exit ID | +/// +/// ## Screen jump +/// +/// `000HHHHH 00000000 00000001` +/// +/// | Value | Comment | +/// |-----------|---------------| +/// | HHHHH | Screen number | +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct Object(pub u32); + +impl Object { + pub fn parse_from_ram(buffer: &[u8]) -> Option> { + if buffer.is_empty() { + return None; + } + let mut objects = Vec::new(); + let mut it = &buffer[5..]; + loop { + if it[0] == 0xFF { + break; + } + if it[0] & 0x50 == 0 && it[1] & 0xF0 == 0 && it[2] == 0 { + // Exits + let [b1, b2, b3, b4] = it[..4] else { + return None; + }; + let object = u32::from_be_bytes([b1, b2, b3, b4]); + objects.push(Object(object)); + it = &it[4..]; + } else { + // Non-exits + let [b1, b2, b3] = it[..3] else { + return None; + }; + let object = u32::from_be_bytes([b1, b2, b3, 0]); + objects.push(Object(object)); + it = &it[3..]; + } + } + Some(objects) + } +} + +impl Object { + #[inline(always)] + pub fn is_extended(self) -> bool { + // -00----- 0000---- -------- -------- + (self.0 >> 20) & 0x60F == 0 + } + + #[inline(always)] + pub fn is_standard(self) -> bool { + !self.is_extended() + } + + #[inline(always)] + pub fn is_exit(self) -> bool { + self.is_extended() && self.settings() == 0 + } + + #[inline(always)] + pub fn is_screen_jump(self) -> bool { + self.is_extended() && self.settings() == 1 + } + + #[inline(always)] + pub fn is_new_screen(self) -> bool { + // N------- -------- -------- -------- + (self.0 >> 31) & 1 != 0 + } + + #[inline(always)] + pub fn standard_object_number(self) -> u8 { + // -BB----- bbbb---- -------- -------- + (((self.0 >> 25) & 0x30) | ((self.0 >> 20) & 0x0F)) as u8 + } + + #[inline(always)] + pub fn settings(self) -> u8 { + // -------- -------- SSSSSSSS -------- + (self.0 >> 8) as u8 + } + + #[inline(always)] + pub fn y(self) -> u8 { + debug_assert!(!self.is_exit() && !self.is_screen_jump()); + // ---YYYYY -------- -------- -------- + (self.0 >> 24) as u8 & 0x1F + } + + #[inline(always)] + pub fn x(self) -> u8 { + debug_assert!(!self.is_exit() && !self.is_screen_jump()); + // -------- ----XXXX -------- -------- + (self.0 >> 16) as u8 & 0xF + } + + #[inline(always)] + pub fn xy(self) -> (u8, u8) { + (self.x(), self.y()) + } + + #[inline(always)] + pub fn screen_number(self) -> u8 { + debug_assert!(self.is_exit() || self.is_screen_jump()); + // Exit: ---ppppp -------- -------- -------- + // Screen jump: ---HHHHH -------- -------- -------- + (self.0 >> 24) as u8 & 0x1F + } + + #[inline(always)] + pub fn is_midway(self) -> bool { + debug_assert!(self.is_exit()); + // -------- ----w--- -------- -------- + (self.0 >> 19) & 1 != 0 + } + + #[inline(always)] + pub fn is_secondary_exit(self) -> bool { + debug_assert!(self.is_exit()); + // -------- ------s- -------- -------- + (self.0 >> 17) & 1 != 0 + } + + #[inline(always)] + pub fn exit_id(self) -> u16 { + debug_assert!(self.is_exit()); + // -------- -------h -------- dddddddd + ((self.0 & 0xFF) | ((self.0 >> 8) & 0x100)) as u16 + } +} diff --git a/smwe-rom/src/objects/object_gfx_list.rs b/crates/smwe-rom/src/objects/object_gfx_list.rs similarity index 98% rename from smwe-rom/src/objects/object_gfx_list.rs rename to crates/smwe-rom/src/objects/object_gfx_list.rs index ab69ba2..5c197c9 100644 --- a/smwe-rom/src/objects/object_gfx_list.rs +++ b/crates/smwe-rom/src/objects/object_gfx_list.rs @@ -14,6 +14,7 @@ const OBJECT_GFX_LIST: SnesSlice = SnesSlice::new(AddrSnes(0x00A92B), 26 * 4); // ------------------------------------------------------------------------------------------------- +#[derive(Debug)] pub struct ObjectGfxList { gfx_file_nums: Vec, } diff --git a/smwe-rom/src/objects/tilesets/data.rs b/crates/smwe-rom/src/objects/tilesets/data.rs similarity index 100% rename from smwe-rom/src/objects/tilesets/data.rs rename to crates/smwe-rom/src/objects/tilesets/data.rs diff --git a/smwe-rom/src/objects/tilesets/mod.rs b/crates/smwe-rom/src/objects/tilesets/mod.rs similarity index 99% rename from smwe-rom/src/objects/tilesets/mod.rs rename to crates/smwe-rom/src/objects/tilesets/mod.rs index 3fc6407..7aeecbd 100644 --- a/smwe-rom/src/objects/tilesets/mod.rs +++ b/crates/smwe-rom/src/objects/tilesets/mod.rs @@ -31,10 +31,12 @@ pub const TILESETS_COUNT: usize = 5; // ------------------------------------------------------------------------------------------------- +#[derive(Debug)] pub struct Tilesets { pub tiles: Vec, } +#[derive(Debug)] pub enum Tile { Shared(Block), TilesetSpecific([Block; TILESETS_COUNT]), diff --git a/smwe-rom/src/snes_utils/addr.rs b/crates/smwe-rom/src/snes_utils/addr.rs similarity index 98% rename from smwe-rom/src/snes_utils/addr.rs rename to crates/smwe-rom/src/snes_utils/addr.rs index 58aa8e2..2f62840 100644 --- a/smwe-rom/src/snes_utils/addr.rs +++ b/crates/smwe-rom/src/snes_utils/addr.rs @@ -149,7 +149,6 @@ duplicate! { self.0.count_zeros() } - #[cfg(has_leading_trailing_ones)] #[inline] fn leading_ones(self) -> u32 { self.0.leading_ones() @@ -160,7 +159,6 @@ duplicate! { self.0.leading_zeros() } - #[cfg(has_leading_trailing_ones)] #[inline] fn trailing_ones(self) -> u32 { self.0.trailing_ones() @@ -206,7 +204,6 @@ duplicate! { Self(self.0.swap_bytes()) } - #[cfg(has_reverse_bits)] #[inline] fn reverse_bits(self) -> Self { Self(self.0.reverse_bits()) diff --git a/smwe-rom/src/snes_utils/mod.rs b/crates/smwe-rom/src/snes_utils/mod.rs similarity index 100% rename from smwe-rom/src/snes_utils/mod.rs rename to crates/smwe-rom/src/snes_utils/mod.rs diff --git a/smwe-rom/src/snes_utils/rom.rs b/crates/smwe-rom/src/snes_utils/rom.rs similarity index 98% rename from smwe-rom/src/snes_utils/rom.rs rename to crates/smwe-rom/src/snes_utils/rom.rs index 05ec9f1..599c956 100644 --- a/smwe-rom/src/snes_utils/rom.rs +++ b/crates/smwe-rom/src/snes_utils/rom.rs @@ -220,7 +220,7 @@ where RV: IsDecompressed, Self: Sized, { - pub fn view(&'r self) -> RomViewWithErrorMapper<'r, &EM, ET, DecompressedView<'r>> { + pub fn view(&'r self) -> RomViewWithErrorMapper<'r, &'r EM, ET, DecompressedView<'r>> { self.rom_view.as_decompressed().view().with_error_mapper(&self.error_mapper) } } diff --git a/smwe-rom/src/snes_utils/rom_slice.rs b/crates/smwe-rom/src/snes_utils/rom_slice.rs similarity index 100% rename from smwe-rom/src/snes_utils/rom_slice.rs rename to crates/smwe-rom/src/snes_utils/rom_slice.rs diff --git a/smwe-rom/tests/with_rom_env.rs b/crates/smwe-rom/tests/with_rom_env.rs similarity index 100% rename from smwe-rom/tests/with_rom_env.rs rename to crates/smwe-rom/tests/with_rom_env.rs diff --git a/smwe-widgets/Cargo.toml b/crates/smwe-widgets/Cargo.toml similarity index 58% rename from smwe-widgets/Cargo.toml rename to crates/smwe-widgets/Cargo.toml index d70aed8..1292067 100644 --- a/smwe-widgets/Cargo.toml +++ b/crates/smwe-widgets/Cargo.toml @@ -4,12 +4,19 @@ version = "0.1.0" authors = ["Adam Gąsior (Adanos020)", "Raven Szewczyk (eigenraven)"] license = "MIT" edition = "2021" -rust-version = "1.65" +rust-version = "1.70" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +smwe-emu = { path = "../smwe-emu" } +smwe-render = { path = "../smwe-render" } + anyhow = "1.0" -egui = "0.21" -itertools = "0.10" +egui = "0.27" +egui_glow = "0.27" +egui-phosphor = "0.4" +glow = "0.13" +inline_tweak = "1.0" +itertools = "0.12" thiserror = "1.0" diff --git a/smwe-widgets/src/flipbook.rs b/crates/smwe-widgets/src/flipbook.rs similarity index 99% rename from smwe-widgets/src/flipbook.rs rename to crates/smwe-widgets/src/flipbook.rs index 1fe8f91..27ec19c 100644 --- a/smwe-widgets/src/flipbook.rs +++ b/crates/smwe-widgets/src/flipbook.rs @@ -110,7 +110,7 @@ impl<'a> Widget for Flipbook<'a> { // UI impl<'a> Flipbook<'a> { - pub fn to_shape(self, ui: &mut Ui, rect: Rect) -> Shape { + pub fn to_shape(self, ui: &Ui, rect: Rect) -> Shape { let Self { animation, duration, size: _, looped, reverse } = self; let reset_anim = || ui.ctx().animate_value_with_time(animation.id, if reverse { 1.0 } else { 0.0 }, 0.0); diff --git a/crates/smwe-widgets/src/lib.rs b/crates/smwe-widgets/src/lib.rs new file mode 100644 index 0000000..54ba68f --- /dev/null +++ b/crates/smwe-widgets/src/lib.rs @@ -0,0 +1,4 @@ +pub mod flipbook; +pub mod palette_view; +pub mod value_switcher; +pub mod vram_view; diff --git a/crates/smwe-widgets/src/palette_view.rs b/crates/smwe-widgets/src/palette_view.rs new file mode 100644 index 0000000..0a50715 --- /dev/null +++ b/crates/smwe-widgets/src/palette_view.rs @@ -0,0 +1,98 @@ +use std::sync::{Arc, Mutex}; + +use egui::*; +use egui_glow::{glow::Buffer, CallbackFn}; +use inline_tweak::tweak; +use smwe_render::palette_renderer::{PaletteRenderer, PaletteUniforms}; + +#[derive(Debug)] +pub struct PaletteView<'s> { + renderer: Arc>, + palette_buf: Buffer, + viewed_palettes: ViewedPalettes, + selection: Option>, + size: Vec2, +} + +#[derive(Copy, Clone, Debug)] +#[repr(u32)] +pub enum ViewedPalettes { + All = 0, + BackgroundOnly = 1, + SpritesOnly = 2, +} + +#[derive(Debug)] +pub enum SelectionType<'s> { + Row(&'s mut u32), + Cell(&'s mut (u32, u32)), +} + +impl<'s> PaletteView<'s> { + pub fn new(renderer: Arc>, palette_buf: Buffer, size: Vec2) -> Self { + Self { renderer, palette_buf, viewed_palettes: ViewedPalettes::All, selection: None, size } + } + + pub fn viewed_rows(mut self, viewed_rows: ViewedPalettes) -> Self { + self.viewed_palettes = viewed_rows; + self + } + + pub fn selection(mut self, selection: SelectionType<'s>) -> Self { + self.selection = Some(selection); + self + } +} + +impl Widget for PaletteView<'_> { + fn ui(self, ui: &mut Ui) -> Response { + let palette_renderer = Arc::clone(&self.renderer); + let uniforms = + PaletteUniforms { palette_buf: self.palette_buf, viewed_palettes: self.viewed_palettes as u32 }; + let (view_rect, mut response) = ui.allocate_exact_size(self.size, Sense::click()); + + ui.painter().add(PaintCallback { + rect: view_rect, + callback: Arc::new(CallbackFn::new(move |_info, painter| { + palette_renderer.lock().expect("Cannot lock mutex on palette renderer").paint(painter.gl(), &uniforms); + })), + }); + + let cell_width = self.size.x / 16.; + let row_count = match self.viewed_palettes { + ViewedPalettes::All => 16, + _ => 8, + }; + match self.selection { + Some(SelectionType::Cell((_x, _y))) => { + unimplemented!() + } + Some(SelectionType::Row(row)) => { + let row_size = vec2(self.size.x, self.size.x / 16.); + + if let Some(hover_pos) = response.hover_pos() { + let relative_pos = hover_pos - view_rect.left_top(); + let hovered_row = (relative_pos.y / cell_width).floor().clamp(0., (row_count - 1) as f32); + let highlight_rect = + Rect::from_min_size(view_rect.min + vec2(0., hovered_row * cell_width), row_size); + ui.painter().rect_filled(highlight_rect, Rounding::ZERO, Color32::from_white_alpha(tweak!(100))); + + if response.clicked_by(PointerButton::Primary) && *row != hovered_row as _ { + response.mark_changed(); + *row = hovered_row as _; + } + } + + let selection_rect = Rect::from_min_size(view_rect.min + vec2(0., *row as f32 * cell_width), row_size); + ui.painter().rect_stroke( + selection_rect, + Rounding::same(2.), + Stroke::new(2., Color32::from_rgba_premultiplied(200, 100, 30, 100)), + ); + } + _ => {} + } + + response + } +} diff --git a/crates/smwe-widgets/src/value_switcher.rs b/crates/smwe-widgets/src/value_switcher.rs new file mode 100644 index 0000000..6eb362f --- /dev/null +++ b/crates/smwe-widgets/src/value_switcher.rs @@ -0,0 +1,138 @@ +use std::ops::RangeInclusive; + +use egui::{emath::Numeric, Button, DragValue, Response, Ui, Widget, WidgetText}; +use egui_phosphor::regular as icons; + +type NumFormatter<'a> = Box) -> String>; +type NumParser<'a> = Box Option>; + +#[derive(Copy, Clone, Debug)] +pub enum ValueSwitcherButtons { + MinusPlus, + LeftRight, +} + +pub struct ValueSwitcher<'a, V, L> +where + V: Numeric, + L: Into, +{ + value: &'a mut V, + label: L, + buttons: ValueSwitcherButtons, + range: RangeInclusive, + custom_formatter: Option>, + custom_parser: Option>, +} + +impl<'a, V, L> ValueSwitcher<'a, V, L> +where + V: Numeric, + L: Into, +{ + pub fn new(value: &'a mut V, label: L, buttons: ValueSwitcherButtons) -> Self { + Self { value, label, buttons, range: V::MIN..=V::MAX, custom_formatter: None, custom_parser: None } + } + + pub fn range(mut self, range: RangeInclusive) -> Self { + self.range = range; + self + } + + pub fn custom_formatter(mut self, formatter: impl 'a + Fn(f64, RangeInclusive) -> String) -> Self { + self.custom_formatter = Some(Box::new(formatter)); + self + } + + pub fn custom_parser(mut self, parser: impl 'a + Fn(&str) -> Option) -> Self { + self.custom_parser = Some(Box::new(parser)); + self + } + + pub fn binary(self, min_width: usize, twos_complement: bool) -> Self { + assert!(min_width > 0, "Slider::binary: `min_width` must be greater than 0"); + if twos_complement { + self.custom_formatter(move |n, _| format!("{:0>min_width$b}", n as i64)) + } else { + self.custom_formatter(move |n, _| { + let sign = if n < 0.0 { "-" } else { "" }; + format!("{sign}{:0>min_width$b}", n.abs() as i64) + }) + } + .custom_parser(|s| i64::from_str_radix(s, 2).map(|n| n as f64).ok()) + } + + pub fn octal(self, min_width: usize, twos_complement: bool) -> Self { + assert!(min_width > 0, "Slider::octal: `min_width` must be greater than 0"); + if twos_complement { + self.custom_formatter(move |n, _| format!("{:0>min_width$o}", n as i64)) + } else { + self.custom_formatter(move |n, _| { + let sign = if n < 0.0 { "-" } else { "" }; + format!("{sign}{:0>min_width$o}", n.abs() as i64) + }) + } + .custom_parser(|s| i64::from_str_radix(s, 8).map(|n| n as f64).ok()) + } + + pub fn hexadecimal(self, min_width: usize, twos_complement: bool, upper: bool) -> Self { + assert!(min_width > 0, "Slider::hexadecimal: `min_width` must be greater than 0"); + match (twos_complement, upper) { + (true, true) => self.custom_formatter(move |n, _| format!("{:0>min_width$X}", n as i64)), + (true, false) => self.custom_formatter(move |n, _| format!("{:0>min_width$x}", n as i64)), + (false, true) => self.custom_formatter(move |n, _| { + let sign = if n < 0.0 { "-" } else { "" }; + format!("{sign}{:0>min_width$X}", n.abs() as i64) + }), + (false, false) => self.custom_formatter(move |n, _| { + let sign = if n < 0.0 { "-" } else { "" }; + format!("{sign}{:0>min_width$x}", n.abs() as i64) + }), + } + .custom_parser(|s| i64::from_str_radix(s, 16).map(|n| n as f64).ok()) + } +} + +impl Widget for ValueSwitcher<'_, V, L> +where + V: Numeric, + L: Into, +{ + fn ui(self, ui: &mut Ui) -> Response { + let (label_l, label_r) = match self.buttons { + ValueSwitcherButtons::LeftRight => (icons::ARROW_LEFT, icons::ARROW_RIGHT), + ValueSwitcherButtons::MinusPlus => (icons::MINUS, icons::PLUS), + }; + + let min = *self.range.start(); + let max = *self.range.end(); + + let inner_response = ui.horizontal(|ui| { + let button_l = ui.add_enabled(*self.value > min, Button::new(label_l)); + let mut drag_value = ui.add({ + let mut dv = DragValue::new(self.value).clamp_range(self.range); + if let Some(custom_formatter) = self.custom_formatter { + dv = dv.custom_formatter(custom_formatter); + } + if let Some(custom_parser) = self.custom_parser { + dv = dv.custom_parser(custom_parser); + } + dv + }); + let button_r = ui.add_enabled(*self.value < max, Button::new(label_r)); + ui.label(self.label); + + if button_l.clicked() { + *self.value = V::from_f64(self.value.to_f64() - 1.); + drag_value.mark_changed(); + } + if button_r.clicked() { + *self.value = V::from_f64(self.value.to_f64() + 1.); + drag_value.mark_changed(); + } + drag_value + }); + + inner_response.inner + } +} diff --git a/crates/smwe-widgets/src/vram_view.rs b/crates/smwe-widgets/src/vram_view.rs new file mode 100644 index 0000000..4380042 --- /dev/null +++ b/crates/smwe-widgets/src/vram_view.rs @@ -0,0 +1,140 @@ +use std::sync::{Arc, Mutex}; + +use egui::{vec2, Color32, PaintCallback, PointerButton, Rect, Response, Rounding, Sense, Stroke, Ui, Vec2, Widget}; +use egui_glow::{glow::Context, CallbackFn}; +use inline_tweak::tweak; +use itertools::Itertools; +use smwe_render::{ + gfx_buffers::GfxBuffers, + tile_renderer::{Tile, TileRenderer, TileUniforms}, +}; + +#[derive(Copy, Clone, Debug)] +pub enum ViewedVramTiles { + All, + BackgroundOnly, + SpritesOnly, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum VramSelectionMode { + SingleTile, + TwoByTwoTiles, +} + +#[derive(Debug)] +pub struct VramView<'a> { + renderer: Arc>, + gfx_bufs: GfxBuffers, + viewed_tiles: ViewedVramTiles, + selection: Option<(VramSelectionMode, &'a mut (u32, u32))>, + zoom: f32, +} + +impl<'a> VramView<'a> { + pub fn new(renderer: Arc>, gfx_bufs: GfxBuffers) -> Self { + Self { renderer, gfx_bufs, viewed_tiles: ViewedVramTiles::All, selection: None, zoom: 1. } + } + + pub fn viewed_tiles(mut self, viewed_tiles: ViewedVramTiles) -> Self { + self.viewed_tiles = viewed_tiles; + self + } + + pub fn selection(mut self, mode: VramSelectionMode, selection: &'a mut (u32, u32)) -> Self { + self.selection = Some((mode, selection)); + self + } + + pub fn zoom(mut self, zoom: f32) -> Self { + self.zoom = zoom; + self + } + + pub fn new_renderer(gl: &Context) -> (TileRenderer, Vec) { + let tiles = (0..16 * 64) + .map(|t| { + let scale = 8; + let pos_x = (t % 16) * scale; + let pos_y = (t / 16) * scale; + let (tile, pal) = if t < 16 * 32 { + // background tiles + (t & 0x3FF, 0) + } else { + // sprite tiles + ((t & 0x1FF) + 0x600, 8) + }; + let params = scale | (pal << 8) | (t & 0xC000); + Tile([pos_x, pos_y, tile, params]) + }) + .collect_vec(); + let mut renderer = TileRenderer::new(gl); + renderer.set_tiles(gl, tiles.clone()); + (renderer, tiles) + } +} + +impl Widget for VramView<'_> { + fn ui(self, ui: &mut Ui) -> Response { + let Self { renderer, gfx_bufs, viewed_tiles, selection, zoom } = self; + let scale = tweak!(8.); + let (height, offset) = match viewed_tiles { + ViewedVramTiles::All => (64., Vec2::ZERO), + ViewedVramTiles::BackgroundOnly => (32., Vec2::ZERO), + ViewedVramTiles::SpritesOnly => (32., vec2(0., -32. * scale)), + }; + let px = ui.ctx().pixels_per_point(); + let scale = scale / px; + + let rect_size = vec2(16., height) * scale * zoom; + let (rect, response) = + ui.allocate_exact_size(rect_size, if selection.is_some() { Sense::click() } else { Sense::hover() }); + + // VRAM image + let screen_size = rect.size() * px; + ui.painter().add(PaintCallback { + rect, + callback: Arc::new(CallbackFn::new(move |_info, painter| { + renderer.lock().expect("Cannot lock mutex on VRAM renderer").paint(painter.gl(), &TileUniforms { + gfx_bufs, + screen_size, + offset, + zoom, + }); + })), + }); + + // Hover/select tile + if let Some((mode, selection)) = selection { + let (selection_size, max_selected_tile) = match mode { + VramSelectionMode::SingleTile => (scale, vec2(15., 31.)), + VramSelectionMode::TwoByTwoTiles => (2. * scale, vec2(14., 30.)), + }; + + let selection_rect = Rect::from_min_size(rect.left_top(), Vec2::splat(selection_size * zoom)); + + if let Some(hover_pos) = response.hover_pos() { + let relative_pos = hover_pos - rect.left_top(); + let hovered_tile = (relative_pos / scale / zoom).floor().clamp(vec2(0., 0.), max_selected_tile); + + ui.painter().rect_filled( + selection_rect.translate(hovered_tile * scale * zoom), + Rounding::same(tweak!(3.)), + Color32::from_white_alpha(tweak!(100)), + ); + + if response.clicked_by(PointerButton::Primary) { + *selection = (hovered_tile.x as _, hovered_tile.y as _); + } + } + + ui.painter().rect_stroke( + selection_rect.translate(vec2(selection.0 as f32, selection.1 as f32) * scale * zoom), + Rounding::same(tweak!(3.)), + Stroke::new(tweak!(2.), Color32::from_rgba_premultiplied(200, 100, 30, 100)), + ); + } + + response + } +} diff --git a/crates/wdc65816/Cargo.lock b/crates/wdc65816/Cargo.lock new file mode 100644 index 0000000..34a74ef --- /dev/null +++ b/crates/wdc65816/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "wdc65816" +version = "0.1.0" diff --git a/crates/wdc65816/Cargo.toml b/crates/wdc65816/Cargo.toml new file mode 100644 index 0000000..4caa1e6 --- /dev/null +++ b/crates/wdc65816/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "wdc65816" +description = "A WDC65816 emulator - The CPU used in the Super Nintendo (SNES)" +version = "0.1.0" +edition = "2021" +authors = ["Jonas Schievink "] +license = "Apache-2.0/MIT" + +[dependencies] diff --git a/crates/wdc65816/benches/cpu.rs b/crates/wdc65816/benches/cpu.rs new file mode 100644 index 0000000..6ad0053 --- /dev/null +++ b/crates/wdc65816/benches/cpu.rs @@ -0,0 +1,68 @@ +//! A simple CPU benchmark + +#![feature(test)] + +extern crate test; +extern crate wdc65816; + +use test::Bencher; +use wdc65816::Cpu; + +struct DummyMem(&'static [u8]); + +impl wdc65816::Mem for DummyMem { + fn load(&mut self, addr: u32) -> u8 { + // Get from array or return 0 + // This places all vectors at $0000 + *self.0.get(addr as usize).unwrap_or(&0) + } + + fn store(&mut self, _addr: u32, _value: u8) {} +} + +/// This is a bad benchmark for the WDC65816. It only ever runs in emulation mode with 8-bit acc and +/// index regs. +/// +/// However, this benchmark should be a rough estimation of "how fast is a naive interpreted CPU +/// emulator": In fact, it returns the speed of the emulated CPU by (ab)using Rust's benchmarks. A +/// speed of 200 MB/s means that the emulated CPU ran at 200 MHz. +#[bench] +fn cpu_simple(b: &mut Bencher) { + static CODE: &'static [u8] = &[ + 0xA9, 0x00, // lda #0 + 0xA2, 0x00, // ldx #0 + 0xA0, 0x00, // ldy #0 + 0x9A, // txs + // Let the PPU do some work + // Disable forced blank and set brightness to max + 0xA9, 0x0F, // lda #$0F + 0x8D, 0x00, 0x21, // sta $2100 + // Enable all layers on the main screen + 0xA9, 0x1F, // lda #$1F + 0x8D, 0x2C, 0x21, // sta $212C + // Repeat, just for fun + 0x4C, 0x00, 0x00, // jmp $0000 + ]; + + let mut cpu = Cpu::new(DummyMem(CODE)); + + // Runs the code until it loops + let mut run_once = || { + let mut cy = 0; + + loop { + cy += cpu.dispatch(); + + if cpu.pc == 0 { + break; + } + } + + cy + }; + + // We "return" the number of cycles elapsed + let cy = run_once(); + b.bytes = cy as u64; + b.iter(run_once); +} diff --git a/crates/wdc65816/src/addressing.rs b/crates/wdc65816/src/addressing.rs new file mode 100644 index 0000000..00e22ef --- /dev/null +++ b/crates/wdc65816/src/addressing.rs @@ -0,0 +1,323 @@ +//! Contains addressing mode definitions + +use std::fmt; + +use super::{Cpu, Mem}; + +/// As a safety measure, the load and store methods take the mode by value and consume it. Using +/// the same object twice requires an explicit `.clone()` (`Copy` isn't implemented). +#[derive(Clone)] +pub enum AddressingMode { + Immediate(u16), + Immediate8(u8), + + /// "Program Counter Relative-r" + /// Used for jumps + /// (PBR, PC + ) [PC+ wraps inside the bank] + Rel(i8), + /// "PC Relative Long-r" + /// Used for `PER` (Push Effective Relative Address) + /// (, PC + ) + RelLong(i16), + + /// "Direct-d" + /// + direct page register in bank 0 + /// (0, D + ) + Direct(u8), + /// "Direct Indexed with X-d,x" + /// (0, D + + X) + DirectIndexedX(u8), + /// "Direct Indexed with Y-d,y" + /// (0, D + + Y) + DirectIndexedY(u8), + + /// "Direct Indexed Indirect-(d,x)" - Indirect-X + /// addr := load2(0, D + + X) + /// (DBR, addr) + DirectIndexedIndirect(u8), + + /// "Direct Indirect-(d)" + /// addr := load2(0, D + ) + /// (DBR, addr) + DirectIndirect(u8), + + // "Direct Indirect Indexed-(d),y" - Indirect-Y + // addr := load2(D + ) + // (DBR, addr + Y) (NOTE: Wraps across data bank!) + DirectIndirectIndexed(u8), + + /// "Direct Indirect Long-[d]" + /// (bank, addr) := load3(0, D + ) + /// (bank, addr) + DirectIndirectLong(u8), + + /// "Direct Indirect Long Indexed-[d],y" (or "Direct Indirect Indexed Long") + /// (bank, addr) := load3(0, D + ) + /// (bank, addr + Y) + DirectIndirectLongIdx(u8), + + /// "Absolute-a" + /// Access absolute offset in the current data bank + /// (DBR, ) + Absolute(u16), + + /// "Absolute Indexed with X-a,x" + /// (DBR, + X) + AbsIndexedX(u16), + + /// "Absolute Indexed with Y-a,y" + /// (DBR, + Y) + AbsIndexedY(u16), + + /// "Absolute Indexed Indirect-(a,x)" + /// addr := load2(PBR, + X) + /// (PBR, addr) + AbsIndexedIndirect(u16), + + /// "Absolute Long Indexed With X-al,x" - Absolute Long + X + /// (, + X) + AbsLongIndexedX(u8, u16), + + /// "Absolute Long-al" + /// Access absolute offset in the specified data bank (DBR is not changed) + /// (, ) + AbsoluteLong(u8, u16), + + /// "Absolute Indirect-(a)" + /// Used only by `jmp`. + /// addr := load2(0, ) + /// (PBR, addr) + AbsoluteIndirect(u16), + + /// "Absolute Indirect Long-[a]" + /// Used only by `jml`. + /// (bank, addr) := load3(0, ) + /// (bank, addr) + AbsoluteIndirectLong(u16), + + /// "Stack Relative-d,s" + /// (0, SP + ) + StackRel(u8), +} + +impl AddressingMode { + /// Loads a byte from where this AM points to (or returns the immediate value) + pub fn loadb(self, cpu: &mut Cpu) -> u8 { + match self { + AddressingMode::Immediate(_) => panic!("loadb on 16-bit immediate"), + AddressingMode::Immediate8(val) => val, + _ => { + let (bank, addr) = self.address(cpu); + cpu.loadb(bank, addr) + } + } + } + + pub fn loadw(self, cpu: &mut Cpu) -> u16 { + match self { + AddressingMode::Immediate(val) => val, + AddressingMode::Immediate8(_) => panic!("loadw on 8-bit immediate"), + _ => { + let (bank, addr) = self.address(cpu); + cpu.loadw(bank, addr) + } + } + } + + pub fn storeb(self, cpu: &mut Cpu, value: u8) { + let (bank, addr) = self.address(cpu); + cpu.storeb(bank, addr, value); + } + + pub fn storew(self, cpu: &mut Cpu, value: u16) { + let (bank, addr) = self.address(cpu); + cpu.storew(bank, addr, value); + } + + /// Computes the effective address as a bank-address-tuple. Panics if the addressing mode is + /// immediate. For jumps, the effective address is the jump target. + pub fn address(&self, cpu: &mut Cpu) -> (u8, u16) { + use self::AddressingMode::*; + + // FIXME is something here dependant on register sizes? + // -> Yes, the cycle count. This causes bad timing, fix it! + // FIXME Use next bank on some address overflows + + match *self { + Absolute(addr) => (cpu.dbr, addr), + AbsoluteLong(bank, addr) => (bank, addr), + AbsLongIndexedX(bank, addr) => { + if !cpu.p.small_index() { + cpu.cy += 1 + } + let a = ((bank as u32) << 16) | addr as u32; + let mut eff_addr = a + cpu.x as u32; + //assert!(eff_addr & 0xff000000 == 0, "address overflow"); + eff_addr &= 0xFFFFFF; + let bank = eff_addr >> 16; + let addr = eff_addr as u16; + (bank as u8, addr) + } + AbsIndexedX(offset) => { + if !cpu.p.small_index() { + cpu.cy += 1 + } + (cpu.dbr, offset + cpu.x) + } + AbsIndexedY(offset) => { + if !cpu.p.small_index() { + cpu.cy += 1 + } + (cpu.dbr, offset + cpu.y) + } + AbsIndexedIndirect(addr_ptr) => { + let (x, pbr) = (cpu.x, cpu.pbr); + let addr = cpu.loadw(pbr, addr_ptr + x); + (pbr, addr) + } + AbsoluteIndirect(addr_ptr) => { + let addr = cpu.loadw(0, addr_ptr); + (cpu.pbr, addr) + } + AbsoluteIndirectLong(addr_ptr) => { + let addr = cpu.loadw(0, addr_ptr); + let bank = cpu.loadb(0, addr_ptr + 2); + (bank, addr) + } + Rel(rel) => (cpu.pbr, (cpu.pc as i16).wrapping_add(rel as i16) as u16), + RelLong(rel_long) => (cpu.pbr, (cpu.pc as i16).wrapping_add(rel_long) as u16), + Direct(offset) => { + if cpu.d & 0xff != 0 { + cpu.cy += 1 + } + (0, cpu.d.wrapping_add(offset as u16)) + } + DirectIndexedX(offset) => { + if cpu.d & 0xff != 0 { + cpu.cy += 1 + } + if !cpu.p.small_index() { + cpu.cy += 1 + } + (0, cpu.d.wrapping_add(offset as u16).wrapping_add(cpu.x)) + } + DirectIndexedY(offset) => { + if cpu.d & 0xff != 0 { + cpu.cy += 1 + } + if !cpu.p.small_index() { + cpu.cy += 1 + } + (0, cpu.d.wrapping_add(offset as u16).wrapping_add(cpu.y)) + } + DirectIndexedIndirect(offset) => { + if cpu.d & 0xff != 0 { + cpu.cy += 1 + } + let addr_ptr = cpu.d.wrapping_add(offset as u16).wrapping_add(cpu.x); + let lo = cpu.loadb(0, addr_ptr) as u16; + let hi = cpu.loadb(0, addr_ptr + 1) as u16; + (cpu.dbr, (hi << 8) | lo) + } + DirectIndirectIndexed(offset) => { + if cpu.d & 0xff != 0 { + cpu.cy += 1 + } + if !cpu.p.small_index() { + cpu.cy += 1 + } + + let addr_ptr = cpu.d.wrapping_add(offset as u16); + let lo = cpu.loadb(0, addr_ptr) as u32; + let hi = cpu.loadb(0, addr_ptr + 1) as u32; + let base_address = ((cpu.dbr as u32) << 16) | (hi << 8) | lo; + let eff_addr = base_address + cpu.y as u32; + assert!(eff_addr & 0xff000000 == 0, "address overflow"); + + let bank = (eff_addr >> 16) as u8; + let addr = eff_addr as u16; + (bank, addr) + } + DirectIndirect(offset) => { + if cpu.d & 0xff != 0 { + cpu.cy += 1 + } + let addr_ptr = cpu.d.wrapping_add(offset as u16); + let lo = cpu.loadb(0, addr_ptr) as u16; + let hi = cpu.loadb(0, addr_ptr + 1) as u16; + (cpu.dbr, (hi << 8) | lo) + } + DirectIndirectLong(offset) => { + if cpu.d & 0xff != 0 { + cpu.cy += 1 + } + let addr_ptr = cpu.d.wrapping_add(offset as u16); + let lo = cpu.loadb(0, addr_ptr) as u16; + let hi = cpu.loadb(0, addr_ptr + 1) as u16; + let bank = cpu.loadb(0, addr_ptr + 2); + (bank, (hi << 8) | lo) + } + DirectIndirectLongIdx(offset) => { + // "The 24-bit base address is pointed to by the sum of the second byte of the + // instruction and the Direct Register. The effective address is this 24-bit base + // address plus the Y Index Register." + if cpu.d & 0xff != 0 { + cpu.cy += 1 + } + if !cpu.p.small_index() { + cpu.cy += 1 + } + + let addr_ptr = cpu.d.wrapping_add(offset as u16); + let lo = cpu.loadb(0, addr_ptr) as u32; + let hi = cpu.loadb(0, addr_ptr + 1) as u32; + let bank = cpu.loadb(0, addr_ptr + 2) as u32; + let base_address = (bank << 16) | (hi << 8) | lo; + let eff_addr = base_address + cpu.y as u32; + assert!(eff_addr & 0xff000000 == 0, "address overflow"); + + let bank = (eff_addr >> 16) as u8; + let addr = eff_addr as u16; + (bank, addr) + } + StackRel(offset) => { + let addr = cpu.s + offset as u16; + (0, addr) + } + Immediate(_) | Immediate8(_) => panic!( + "attempted to take the address of an immediate value (attempted store to \ + immediate?)" + ), + } + } +} + +impl fmt::Display for AddressingMode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::AddressingMode::*; + + match *self { + Immediate(val) => write!(f, "#${:04X}", val), + Immediate8(val) => write!(f, "#${:02X}", val), + Absolute(addr) => write!(f, "${:04X}", addr), + AbsoluteLong(bank, addr) => write!(f, "${:02X}:{:04X}", bank, addr), + AbsLongIndexedX(bank, addr) => write!(f, "${:02X}:{:04X},x", bank, addr), + AbsIndexedX(offset) => write!(f, "${:04X},x", offset), + AbsIndexedY(offset) => write!(f, "${:04X},y", offset), + AbsIndexedIndirect(addr) => write!(f, "(${:04X},x)", addr), + AbsoluteIndirect(addr) => write!(f, "(${:04X})", addr), + AbsoluteIndirectLong(addr) => write!(f, "[${:04X}]", addr), + Rel(rel) => write!(f, "{:+}", rel), + RelLong(rel_long) => write!(f, "{:+}", rel_long), + Direct(offset) => write!(f, "${:02X}", offset), + DirectIndexedX(offset) => write!(f, "${:02X},x", offset), + DirectIndexedY(offset) => write!(f, "${:02X},y", offset), + DirectIndexedIndirect(offset) => write!(f, "(${:02X},x)", offset), + DirectIndirectIndexed(offset) => write!(f, "(${:02X}),y", offset), + DirectIndirect(offset) => write!(f, "(${:02X})", offset), + DirectIndirectLong(offset) => write!(f, "[${:02X}]", offset), + DirectIndirectLongIdx(offset) => write!(f, "[${:02X}],y", offset), + StackRel(offset) => write!(f, "${:02X},s", offset), + } + } +} diff --git a/crates/wdc65816/src/lib.rs b/crates/wdc65816/src/lib.rs new file mode 100644 index 0000000..bc3d366 --- /dev/null +++ b/crates/wdc65816/src/lib.rs @@ -0,0 +1,1807 @@ +//! 65816 emulator (work in progress) + +mod addressing; +mod statusreg; + +use addressing::AddressingMode; +use statusreg::StatusReg; + +/// Trait for devices attached to the 65816's address/data bus +pub trait Mem { + fn load(&mut self, addr: u32) -> u8; + fn store(&mut self, addr: u32, value: u8); +} + +// Emulation mode vectors +const IRQ_VEC8: u16 = 0xFFFE; +const RESET_VEC8: u16 = 0xFFFC; +const NMI_VEC8: u16 = 0xFFFA; +#[allow(dead_code)] +const ABORT_VEC8: u16 = 0xFFF8; +#[allow(dead_code)] +const COP_VEC8: u16 = 0xFFF4; + +// Native mode vectors +const IRQ_VEC16: u16 = 0xFFEE; +const NMI_VEC16: u16 = 0xFFEA; +#[allow(dead_code)] +const ABORT_VEC16: u16 = 0xFFE8; +#[allow(dead_code)] +const BRK_VEC16: u16 = 0xFFE6; +#[allow(dead_code)] +const COP_VEC16: u16 = 0xFFE4; + +#[derive(Debug, Clone)] +pub struct Cpu { + pub a: u16, + pub x: u16, + pub y: u16, + /// Stack pointer + pub s: u16, + /// Data bank register. Bank for all memory accesses. + pub dbr: u8, + /// Program bank register. Opcodes are fetched from this bank. + pub pbr: u8, + /// Direct (page) register. Address offset for all instruction using "direct addressing" mode. + pub d: u16, + /// Program counter. Note that PBR is not changed on pc overflow, so code can not span + /// multiple banks (without `jml` or `jsr`). + pub pc: u16, + p: StatusReg, + pub emulation: bool, + /// Set to true when executing a WAI instruction. Stops the processor from dispatching further + /// instructions until an interrupt is triggered. + wai: bool, + + /// CPU clock cycle counter for the current instruction. + cy: u16, + + /// Signals that an illegal instruction was executed. + pub ill: bool, + + pub trace: bool, + pub mem: M, +} + +impl Cpu { + /// Creates a new CPU and executes a reset. This will fetch the RESET vector from memory and + /// put the CPU in emulation mode. + pub fn new(mut mem: M) -> Cpu { + let pcl = mem.load(RESET_VEC8 as u32) as u16; + let pch = mem.load(RESET_VEC8 as u32 + 1) as u16; + let pc = (pch << 8) | pcl; + + Cpu { + // Undefined according to datasheet + a: 0, + x: 0, + y: 0, + // High byte set to 1 since we're now in emulation mode + s: 0x0100, + // Initialized to 0 + dbr: 0, + d: 0, + pbr: 0, + // Read from RESET vector above + pc, + // Acc and index regs start in 8-bit mode, IRQs disabled, CPU in emulation mode + p: StatusReg::new(), + emulation: true, + wai: false, + cy: 0, + trace: false, + ill: false, + mem, + } + } + + /// Load a byte from memory. + fn loadb(&mut self, bank: u8, addr: u16) -> u8 { + // FIXME Remove? + self.mem.load((bank as u32) << 16 | addr as u32) + } + + fn loadw(&mut self, bank: u8, addr: u16) -> u16 { + assert!(addr < 0xffff, "loadw on bank boundary"); + // ^ if this should be supported, make sure to fix the potential overflow below + + let lo = self.loadb(bank, addr) as u16; + let hi = self.loadb(bank, addr + 1) as u16; + (hi << 8) | lo + } + + fn storeb(&mut self, bank: u8, addr: u16, value: u8) { + // FIXME Remove? + self.mem.store((bank as u32) << 16 | addr as u32, value) + } + + fn storew(&mut self, bank: u8, addr: u16, value: u16) { + self.storeb(bank, addr, value as u8); + if addr == 0xffff { + self.storeb(bank + 1, 0, (value >> 8) as u8); + } else { + self.storeb(bank, addr + 1, (value >> 8) as u8); + } + } + + /// Fetches the byte PC points at, then increments PC + fn fetchb(&mut self) -> u8 { + let (pbr, pc) = (self.pbr, self.pc); + let b = self.loadb(pbr, pc); + self.pc += 1; + b + } + + /// Fetches a 16-bit word (little-endian) located at PC, by fetching 2 individual bytes + fn fetchw(&mut self) -> u16 { + let low = self.fetchb() as u16; + let high = self.fetchb() as u16; + (high << 8) | low + } + + /// Pushes a byte onto the stack and decrements the stack pointer + fn pushb(&mut self, value: u8) { + let s = self.s; + self.storeb(0, s, value); + if self.emulation { + // stack must stay in 0x01xx + assert_eq!(self.s & 0xff00, 0x0100); + let s = self.s as u8 - 1; + self.s = (self.s & 0xff00) | s as u16; + } else { + self.s -= 1; + } + } + + fn pushw(&mut self, value: u16) { + let hi = (value >> 8) as u8; + let lo = value as u8; + self.pushb(hi); + self.pushb(lo); + } + + fn popb(&mut self) -> u8 { + if self.emulation { + // stack must stay in 0x01xx + assert_eq!(self.s & 0xff00, 0x0100); + let s = self.s as u8 + 1; + self.s = (self.s & 0xff00) | s as u16; + } else { + self.s += 1; + } + + let s = self.s; + self.loadb(0, s) + } + + fn popw(&mut self) -> u16 { + let lo = self.popb() as u16; + let hi = self.popb() as u16; + (hi << 8) | lo + } + + /// Enters/exits emulation mode + fn set_emulation(&mut self, value: bool) { + // FIXME Should this set the DBR/PBR? + if !self.emulation && value { + // Enter emulation mode + + // Set high byte of stack ptr to 0x01 and set M/X bits to make A,X and Y 8-bit + self.s = 0x0100 | (self.s & 0xff); + self.p.set_small_acc(true); + self.p.set_small_index(true); + // "If the Index Select Bit (X) equals one, both registers will be 8 bits wide, and the + // high byte is forced to zero" + self.x &= 0xff; + self.y &= 0xff; + } + + self.emulation = value; + } + + fn trace_op(&self, pc: u16, raw: u8, op: &str, am: Option<&AddressingMode>) { + //use log::LogLevel::Trace; + //if !log_enabled!(Trace) || !self.trace { return } + if !self.trace { + return; + } + + let opstr = match am { + Some(am) => format!("{} {}", op, am), + None => op.to_string(), + }; + println!( + "${:02X}:{:04X} {:02X} {:14} a:{:04X} x:{:04X} y:{:04X} s:{:04X} d:{:04X} dbr:{:02X} emu:{} {}", + self.pbr, pc, raw, opstr, self.a, self.x, self.y, self.s, self.d, self.dbr, self.emulation as u8, self.p, + ); + } + + /// Executes a single opcode and returns the number of CPU clock cycles used. + /// + /// Note that in case a WAI instruction was executed, this will *not* execute anything and + /// return 0. An interrupt has to be caused to resume work. + pub fn dispatch(&mut self) -> u16 { + // CPU cycles each opcode takes (at the minimum). + // This table assumes that fetching a byte takes 1 CPU cycle. The `Mem` implementor can add + // additional wait state cycles externally. + // (FIXME: Is the above correct? Critical for timing!) + static CYCLE_TABLE: [u8; 256] = [ + 7, 6, 7, 4, 5, 3, 5, 6, 3, 2, 2, 4, 6, 4, 6, 5, // $00 - $0f + 2, 5, 5, 7, 5, 4, 6, 6, 2, 4, 2, 2, 6, 4, 7, 5, // $10 - $1f + 6, 6, 8, 4, 3, 3, 5, 6, 4, 2, 2, 5, 4, 4, 6, 5, // $20 - $2f + 2, 5, 5, 7, 4, 4, 6, 6, 2, 4, 2, 2, 4, 4, 7, 5, // $30 - $3f + 7, 6, 2, 4, 7, 3, 5, 6, 3, 2, 2, 3, 3, 4, 6, 5, // $40 - $4f + 2, 5, 5, 7, 7, 4, 6, 6, 2, 4, 3, 2, 4, 4, 7, 5, // $50 - $5f + 7, 6, 6, 4, 3, 3, 5, 6, 4, 2, 2, 6, 5, 4, 6, 5, // $60 - $6f + 2, 5, 5, 7, 4, 4, 6, 6, 2, 4, 4, 2, 6, 2, 7, 5, // $70 - $7f + 2, 6, 3, 4, 3, 3, 3, 2, 2, 2, 2, 3, 4, 4, 4, 5, // $80 - $8f + 2, 6, 5, 7, 4, 4, 4, 6, 2, 5, 2, 2, 3, 5, 5, 5, // $90 - $9f + 2, 6, 2, 4, 3, 3, 3, 6, 2, 2, 2, 4, 4, 4, 4, 5, // $a0 - $af + 2, 5, 5, 7, 4, 4, 4, 6, 2, 4, 2, 2, 4, 4, 4, 5, // $b0 - $bf + 2, 6, 3, 4, 3, 3, 5, 6, 2, 2, 2, 3, 4, 4, 6, 5, // $c0 - $cf + 2, 5, 5, 7, 6, 4, 6, 6, 2, 4, 3, 3, 6, 4, 7, 5, // $d0 - $df + 2, 6, 3, 4, 3, 3, 5, 6, 2, 2, 2, 3, 4, 4, 6, 5, // $e0 - $ef + 2, 5, 5, 7, 5, 4, 6, 6, 2, 4, 4, 2, 6, 4, 7, 5, // $f0 - $ff + ]; + + // Still waiting for interrupt? Don't do any work. + if self.wai { + return 0; + } + + let pc = self.pc; + self.cy = 0; + let op = self.fetchb(); + self.cy += CYCLE_TABLE[op as usize] as u16; + + macro_rules! instr { + ( $name:ident ) => {{ + self.trace_op(pc, op, stringify!($name), None); + self.$name() + }}; + ( $name:ident $am:ident ) => {{ + let am = self.$am(); + self.trace_op(pc, op, stringify!($name), Some(&am)); + self.$name(am) + }}; + } + + match op { + // Stack operations + 0x4b => instr!(phk), + 0x0b => instr!(phd), + 0x2b => instr!(pld), + 0x8b => instr!(phb), + 0xab => instr!(plb), + 0x08 => instr!(php), + 0x28 => instr!(plp), + 0x48 => instr!(pha), + 0x68 => instr!(pla), + 0xda => instr!(phx), + 0xfa => instr!(plx), + 0x5a => instr!(phy), + 0x7a => instr!(ply), + 0xf4 => instr!(pea absolute), + 0x62 => instr!(per relative_long), + + // Processor status + 0x18 => instr!(clc), + 0x38 => instr!(sec), + 0x58 => instr!(cli), + 0x78 => instr!(sei), + 0xcb => instr!(wai), + 0xd8 => instr!(cld), + 0xf8 => instr!(sed), + 0xfb => instr!(xce), + 0xc2 => instr!(rep immediate8), + 0xe2 => instr!(sep immediate8), + + // Arithmetic + 0x0a => instr!(asl_a), + 0x06 => instr!(asl direct), + 0x16 => instr!(asl direct_indexed_x), + 0x0e => instr!(asl absolute), + 0x1e => instr!(asl absolute_indexed_x), + 0x2a => instr!(rol_a), + 0x26 => instr!(rol direct), + 0x2e => instr!(rol absolute), + 0x3e => instr!(rol absolute_indexed_x), + 0x36 => instr!(rol direct_indexed_x), + 0x4a => instr!(lsr_a), + 0x46 => instr!(lsr direct), + 0x4e => instr!(lsr absolute), + 0x56 => instr!(lsr direct_indexed_x), + 0x5e => instr!(lsr absolute_indexed_x), + 0x66 => instr!(ror direct), + 0x6a => instr!(ror_a), + 0x6e => instr!(ror absolute), + 0x76 => instr!(ror direct_indexed_x), + 0x7e => instr!(ror absolute_indexed_x), + 0x23 => instr!(and stack_rel), + 0x25 => instr!(and direct), + 0x27 => instr!(and direct_indirect_long), + 0x37 => instr!(and direct_indirect_long_idx), + 0x21 => instr!(and direct_indexed_indirect), + 0x29 => instr!(and immediate_acc), + 0x2d => instr!(and absolute), + 0x3d => instr!(and absolute_indexed_x), + 0x39 => instr!(and absolute_indexed_y), + 0x2f => instr!(and absolute_long), + 0x3f => instr!(and absolute_long_indexed_x), + 0x03 => instr!(ora stack_rel), + 0x05 => instr!(ora direct), + 0x15 => instr!(ora direct_indexed_x), + 0x09 => instr!(ora immediate_acc), + 0x12 => instr!(ora direct_indirect), + 0x07 => instr!(ora direct_indirect_long), + 0x17 => instr!(ora direct_indirect_long_idx), + 0x0d => instr!(ora absolute), + 0x1d => instr!(ora absolute_indexed_x), + 0x19 => instr!(ora absolute_indexed_y), + 0x0f => instr!(ora absolute_long), + 0x1f => instr!(ora absolute_long_indexed_x), + 0x45 => instr!(eor direct), + 0x55 => instr!(eor direct_indexed_x), + 0x49 => instr!(eor immediate_acc), + 0x4d => instr!(eor absolute), + 0x5d => instr!(eor absolute_indexed_x), + 0x59 => instr!(eor absolute_indexed_y), + 0x4f => instr!(eor absolute_long), + 0x5f => instr!(eor absolute_long_indexed_x), + 0x65 => instr!(adc direct), + 0x75 => instr!(adc direct_indexed_x), + 0x72 => instr!(adc direct_indirect), + 0x71 => instr!(adc direct_indirect_indexed), + 0x77 => instr!(adc direct_indirect_long_idx), + 0x67 => instr!(adc direct_indirect_long), + 0x69 => instr!(adc immediate_acc), + 0x6d => instr!(adc absolute), + 0x7d => instr!(adc absolute_indexed_x), + 0x79 => instr!(adc absolute_indexed_y), + 0x6f => instr!(adc absolute_long), + 0x7f => instr!(adc absolute_long_indexed_x), + 0xe5 => instr!(sbc direct), + 0xf5 => instr!(sbc direct_indexed_x), + 0xe9 => instr!(sbc immediate_acc), + 0xed => instr!(sbc absolute), + 0xf9 => instr!(sbc absolute_indexed_y), + 0xfd => instr!(sbc absolute_indexed_x), + 0xef => instr!(sbc absolute_long), + 0xff => instr!(sbc absolute_long_indexed_x), + 0xe6 => instr!(inc direct), + 0xf6 => instr!(inc direct_indexed_x), + 0xfe => instr!(inc absolute_indexed_x), + 0xee => instr!(inc absolute), + 0x1a => instr!(ina), + 0xe8 => instr!(inx), + 0xc8 => instr!(iny), + 0x3a => instr!(dea), + 0xc6 => instr!(dec direct), + 0xd6 => instr!(dec direct_indexed_x), + 0xce => instr!(dec absolute), + 0xde => instr!(dec absolute_indexed_x), + 0xca => instr!(dex), + 0x88 => instr!(dey), + + // Register and memory transfers + 0x5b => instr!(tcd), + 0x7b => instr!(tdc), + 0x1b => instr!(tcs), + 0x3b => instr!(tsc), + 0xba => instr!(tsx), + 0xaa => instr!(tax), + 0xa8 => instr!(tay), + 0x8a => instr!(txa), + 0x9a => instr!(txs), + 0x9b => instr!(txy), + 0x98 => instr!(tya), + 0xbb => instr!(tyx), + 0xeb => instr!(xba), + 0x83 => instr!(sta stack_rel), + 0x85 => instr!(sta direct), + 0x95 => instr!(sta direct_indexed_x), + 0x92 => instr!(sta direct_indirect), + 0x87 => instr!(sta direct_indirect_long), + 0x97 => instr!(sta direct_indirect_long_idx), + 0x8d => instr!(sta absolute), + 0x8f => instr!(sta absolute_long), + 0x9d => instr!(sta absolute_indexed_x), + 0x99 => instr!(sta absolute_indexed_y), + 0x9f => instr!(sta absolute_long_indexed_x), + 0x86 => instr!(stx direct), + 0x96 => instr!(stx direct_indexed_y), + 0x8e => instr!(stx absolute), + 0x84 => instr!(sty direct), + 0x94 => instr!(sty direct_indexed_y), + 0x8c => instr!(sty absolute), + 0x64 => instr!(stz direct), + 0x9c => instr!(stz absolute), + 0x74 => instr!(stz direct_indexed_x), + 0x9e => instr!(stz absolute_indexed_x), + 0xa3 => instr!(lda stack_rel), + 0xa5 => instr!(lda direct), + 0xb5 => instr!(lda direct_indexed_x), + 0xb1 => instr!(lda direct_indirect_indexed), + 0xa9 => instr!(lda immediate_acc), + 0xb2 => instr!(lda direct_indirect), + 0xa7 => instr!(lda direct_indirect_long), + 0xb7 => instr!(lda direct_indirect_long_idx), + 0xad => instr!(lda absolute), + 0xbd => instr!(lda absolute_indexed_x), + 0xb9 => instr!(lda absolute_indexed_y), + 0xaf => instr!(lda absolute_long), + 0xbf => instr!(lda absolute_long_indexed_x), + 0xa6 => instr!(ldx direct), + 0xb6 => instr!(ldx direct_indexed_y), + 0xa2 => instr!(ldx immediate_index), + 0xae => instr!(ldx absolute), + 0xbe => instr!(ldx absolute_indexed_y), + 0xa4 => instr!(ldy direct), + 0xb4 => instr!(ldy direct_indexed_x), + 0xa0 => instr!(ldy immediate_index), + 0xac => instr!(ldy absolute), + 0xbc => instr!(ldy absolute_indexed_x), + 0x54 => instr!(mvn), // FIXME These look bad in the trace, print src/dest banks! + 0x44 => instr!(mvp), + + // Bit operations + 0x24 => instr!(bit direct), + 0x2c => instr!(bit absolute), + 0x34 => instr!(bit direct_indexed_x), + 0x3c => instr!(bit absolute_indexed_x), + 0x89 => instr!(bit immediate_acc), + 0x04 => instr!(tsb direct), + 0x0c => instr!(tsb absolute), + 0x14 => instr!(trb direct), + 0x1c => instr!(trb absolute), + + // Comparisons + 0xc9 => instr!(cmp immediate_acc), + 0xc5 => instr!(cmp direct), + 0xd5 => instr!(cmp direct_indexed_x), + 0xcd => instr!(cmp absolute), + 0xdd => instr!(cmp absolute_indexed_x), + 0xd9 => instr!(cmp absolute_indexed_y), + 0xcf => instr!(cmp absolute_long), + 0xdf => instr!(cmp absolute_long_indexed_x), + 0xd2 => instr!(cmp direct_indirect), + 0xd1 => instr!(cmp direct_indirect_indexed), + 0xd7 => instr!(cmp direct_indirect_long_idx), + 0xe0 => instr!(cpx immediate_index), + 0xe4 => instr!(cpx direct), + 0xec => instr!(cpx absolute), + 0xc0 => instr!(cpy immediate_index), + 0xc4 => instr!(cpy direct), + 0xcc => instr!(cpy absolute), + + // Branches + 0x80 => instr!(bra rel), + 0x82 => instr!(bra relative_long), // BRL + 0xf0 => instr!(beq rel), + 0xd0 => instr!(bne rel), + 0x10 => instr!(bpl rel), + 0x30 => instr!(bmi rel), + 0x50 => instr!(bvc rel), + 0x70 => instr!(bvs rel), + 0x90 => instr!(bcc rel), + 0xb0 => instr!(bcs rel), + + // Jumps, calls and returns + 0x4c => instr!(jmp absolute), // DBR is ignored + 0x5c => instr!(jml absolute_long), + 0x6c => instr!(jmp absolute_indirect), + 0x7c => instr!(jmp absolute_indexed_indirect), + 0xdc => instr!(jml absolute_indirect_long), + 0x20 => instr!(jsr absolute), + 0x22 => instr!(jsl absolute_long), + 0xfc => instr!(jsr absolute_indexed_indirect), + 0x40 => instr!(rti), + 0x60 => instr!(rts), + 0x6b => instr!(rtl), + + 0xea => instr!(nop), + _ => { + instr!(ill); + self.ill = true; + //panic!("illegal CPU opcode: ${:02X}", op); + } + } + + self.cy + } + + /// Invokes the NMI handler. + pub fn trigger_nmi(&mut self) { + if self.emulation { + self.interrupt(NMI_VEC8); + } else { + self.interrupt(NMI_VEC16); + } + } + + /// Invokes the IRQ handler if interrupts are enabled. Returns whether the interrupt was + /// generated. + pub fn trigger_irq(&mut self) -> bool { + if !self.p.irq_disable() { + false + } else { + if self.emulation { + self.interrupt(IRQ_VEC8); + } else { + self.interrupt(IRQ_VEC16); + } + true + } + } + + /// Execute an IRQ sequence. This pushes PBR, PC and the processor status register P on the + /// stack, sets the PBR to 0, loads the handler address from the given vector, and jumps to the + /// handler. + fn interrupt(&mut self, vector: u16) { + self.wai = false; + + if !self.emulation { + let pbr = self.pbr; + self.pushb(pbr); + self.pbr = 0; + } + + let pc = self.pc; + self.pushw(pc); + let p = self.p.0; + self.pushb(p); + + // Interrupts clear the decimal flag (http://www.6502.org/tutorials/decimal_mode.html) + // ...but only in native mode + if !self.emulation { + self.p.set_decimal(false); + } + + let handler = self.loadw(0, vector); + self.pc = handler; + } + + fn return_from_interrupt(&mut self) { + let p = self.popb(); + self.p.0 = p; + let pc = self.popw(); + self.pc = pc; + + if !self.emulation { + let pbr = self.popb(); + self.pbr = pbr; + } + } + + /// Common method for all comparison opcodes. Compares `a` to `b` by effectively computing + /// `a-b`. This method only works correctly for 16-bit values. + /// + /// The Z flag is set if both numbers are equal. + /// The C flag will be set to `a >= b`. + /// The N flag is set to the most significant bit of `a-b`. + fn compare(&mut self, a: u16, b: u16) { + self.p.set_zero(a == b); + self.p.set_carry(a >= b); + self.p.set_negative(a.wrapping_sub(b) & 0x8000 != 0); + } + + /// Does the exact same thing as `compare`, but for 8-bit operands + fn compare8(&mut self, a: u8, b: u8) { + self.p.set_zero(a == b); + self.p.set_carry(a >= b); + self.p.set_negative(a.wrapping_sub(b) & 0x80 != 0); + } + + /// Branch to an absolute address. This will overwrite the current program bank. + fn branch(&mut self, target: (u8, u16)) { + self.pbr = target.0; + self.pc = target.1; + } + + /// Changes the status register. + fn set_p(&mut self, new: u8) { + let small_idx = self.p.small_index(); + self.p.0 = new; + if !small_idx && self.p.small_index() { + // "If the Index Select Bit (X) equals one, both registers will be 8 bits wide, and the + // high byte is forced to zero" + self.x &= 0xff; + self.y &= 0xff; + } + } +} + +/// Opcode implementations +impl Cpu { + /// Move Next (incrementing address). Copies C+1 (16-bit A) bytes from the address in X to the + /// address in Y. + fn mvn(&mut self) { + let destbank = self.fetchb(); + let srcbank = self.fetchb(); + + while self.a != 0xffff { + let (x, y) = (self.x, self.y); + let val = self.loadb(srcbank, x); + self.storeb(destbank, y, val); + + self.x = self.x.wrapping_add(1); + self.y = self.y.wrapping_add(1); + self.a = self.a.wrapping_sub(1); + } + } + + /// Move Previous (decrementing address) + fn mvp(&mut self) { + let destbank = self.fetchb(); + let srcbank = self.fetchb(); + + while self.a != 0xffff { + let (x, y) = (self.x, self.y); + let val = self.loadb(srcbank, x); + self.storeb(destbank, y, val); + + self.x = self.x.wrapping_sub(1); + self.y = self.y.wrapping_sub(1); + self.a = self.a.wrapping_sub(1); + } + } + + /// Push Program Bank Register + fn phk(&mut self) { + let pbr = self.pbr; + self.pushb(pbr); + } + + /// Push Direct Page Register + fn phd(&mut self) { + let d = self.d; + self.pushw(d); + } + + /// Pull Direct Page Register + fn pld(&mut self) { + let d = self.popw(); + self.d = d; + } + + /// Push Data Bank Register + fn phb(&mut self) { + let dbr = self.dbr; + self.pushb(dbr); + } + + /// Pop Data Bank Register + fn plb(&mut self) { + let dbr = self.popb(); + self.dbr = dbr; + } + + /// Push Processor Status Register + fn php(&mut self) { + // Changes no flags + let p = self.p.0; + self.pushb(p); + } + + /// Pull Processor Status Register + fn plp(&mut self) { + let p = self.popb(); + self.set_p(p); + } + + /// Push A on the stack + fn pha(&mut self) { + // No flags modified + if self.p.small_acc() { + let a = self.a as u8; + self.pushb(a); + } else { + let a = self.a; + self.pushw(a); + self.cy += 1; + } + } + + /// Pull Accumulator from stack + fn pla(&mut self) { + // Changes N and Z + if self.p.small_acc() { + let a = self.popb(); + self.a = (self.a & 0xff00) | self.p.set_nz_8(a) as u16; + } else { + let a = self.popw(); + self.a = self.p.set_nz(a); + self.cy += 1; + } + } + + /// Push Index Register X + fn phx(&mut self) { + if self.p.small_index() { + let val = self.x as u8; + self.pushb(val); + } else { + let val = self.x; + self.pushw(val); + self.cy += 1; + } + } + + /// Pop Index Register X + fn plx(&mut self) { + // Changes N and Z + if self.p.small_index() { + let val = self.popb(); + self.x = self.p.set_nz_8(val) as u16; + } else { + let val = self.popw(); + self.x = self.p.set_nz(val); + self.cy += 1; + } + } + + /// Push Index Register Y + fn phy(&mut self) { + if self.p.small_index() { + let val = self.y as u8; + self.pushb(val); + } else { + let val = self.y; + self.pushw(val); + self.cy += 1; + } + } + + /// Pop Index Register Y + fn ply(&mut self) { + // Changes N and Z + if self.p.small_index() { + let val = self.popb(); + self.y = self.p.set_nz_8(val) as u16; + } else { + let val = self.popw(); + self.y = self.p.set_nz(val); + self.cy += 1; + } + } + + fn push_effective(&mut self, am: AddressingMode) { + let (_, addr) = am.address(self); + self.pushw(addr); + } + + /// Push Effective Absolute Address + fn pea(&mut self, am: AddressingMode) { + // Pushes the address (16-bit, no bank) onto the stack. This is equivalent of pushing the + // 2 bytes following the opcode onto the stack. + self.push_effective(am) + } + + /// Push Effective PC-Relative Address + fn per(&mut self, am: AddressingMode) { + self.push_effective(am) + } + + /// AND Accumulator with Memory (or immediate) + fn and(&mut self, am: AddressingMode) { + // Sets N and Z + if self.p.small_acc() { + let val = am.loadb(self); + let res = self.a as u8 & val; + self.p.set_nz_8(res); + self.a = (self.a & 0xff00) | res as u16; + } else { + let val = am.loadw(self); + let res = self.a & val; + self.a = self.p.set_nz(res); + self.cy += 1; + } + } + + /// OR Accumulator with Memory + fn ora(&mut self, am: AddressingMode) { + // Sets N and Z + if self.p.small_acc() { + let val = am.loadb(self); + let res = self.a as u8 | val; + self.p.set_nz_8(res); + self.a = (self.a & 0xff00) | res as u16; + } else { + let val = am.loadw(self); + let res = self.a | val; + self.a = self.p.set_nz(res); + self.cy += 1; + } + } + + /// Exclusive Or Accumulator with Memory + fn eor(&mut self, am: AddressingMode) { + // Sets N and Z + if self.p.small_acc() { + let val = am.loadb(self); + let res = self.a as u8 ^ val; + self.p.set_nz_8(res); + self.a = (self.a & 0xff00) | res as u16; + } else { + let val = am.loadw(self); + let res = self.a ^ val; + self.a = self.p.set_nz(res); + self.cy += 1; + } + } + + /// Add With Carry + fn adc(&mut self, am: AddressingMode) { + // Sets N, V, C and Z + let c: u16 = if self.p.carry() { 1 } else { 0 }; + + if self.p.small_acc() { + let a = self.a & 0xff; + let val = am.loadb(self) as u16; + let mut res = if self.p.decimal() { + let mut low = (a & 0xf) + (val & 0xf) + c; + if low > 9 { + low += 6; + } + + (a & 0xf0) + (val & 0xf0) + (low & 0x0f) + if low > 0x0f { 0x10 } else { 0 } + } else { + a + val + c + }; + self.p.set_overflow((a as u8 ^ val as u8) & 0x80 == 0 && (a as u8 ^ res as u8) & 0x80 == 0x80); + if self.p.decimal() && res > 0x9f { + res += 0x60; + } + self.p.set_carry(res > 255); + + self.a = (self.a & 0xff00) | self.p.set_nz_8(res as u8) as u16; + } else { + let val = am.loadw(self); + let mut res: u32 = if self.p.decimal() { + let mut res0 = (self.a & 0x000f) + (val & 0x000f) + c; + if res0 > 0x0009 { + res0 += 0x0006; + } + + let mut res1 = + (self.a & 0x00f0) + (val & 0x00f0) + (res0 & 0x000f) + if res0 > 0x000f { 0x0010 } else { 0x0000 }; + if res1 > 0x009f { + res1 += 0x0060; + } + + let mut res2 = + (self.a & 0x0f00) + (val & 0x0f00) + (res1 & 0x00ff) + if res1 > 0x00ff { 0x0100 } else { 0x0000 }; + if res2 > 0x09ff { + res2 += 0x0600; + } + + (self.a as u32 & 0xf000) + + (val as u32 & 0xf000) + + (res2 as u32 & 0x0fff) + + if res2 > 0x0fff { 0x1000 } else { 0x0000 } + } else { + self.a as u32 + val as u32 + c as u32 + }; + self.p.set_overflow((self.a ^ val) & 0x8000 == 0 && (self.a ^ res as u16) & 0x8000 == 0x8000); + if self.p.decimal() && res > 0x9fff { + res += 0x6000; + } + self.p.set_carry(res > 65535); + + self.a = self.p.set_nz(res as u16); + self.cy += 1; + } + } + + /// Subtract with Borrow from Accumulator + fn sbc(&mut self, am: AddressingMode) { + // Sets N, Z, C and V + let c: i16 = if self.p.carry() { 1 } else { 0 }; + + if self.p.small_acc() { + let a = self.a as i16 & 0xff; + let v = am.loadb(self) as i16 ^ 0xff; + let mut res: i16 = if self.p.decimal() { + let mut low: i16 = (a & 0x0f) + (v & 0x0f) + c; + if low < 0x10 { + low -= 6; + } + + (a & 0xf0) + (v & 0xf0) + (low & 0x0f) + if low > 0x0f { 0x10 } else { 0x00 } + } else { + a + v + c + }; + self.p.set_overflow((a & 0x80) == (v & 0x80) && (a & 0x80) != (res & 0x80)); + if self.p.decimal() && res < 0x100 { + res -= 0x60; + } + self.p.set_carry(res > 255); + + self.a = (self.a & 0xff00) | self.p.set_nz_8(res as u8) as u16; + } else { + let a = self.a as i32; + let v = am.loadw(self) as i32 ^ 0xffff; + let mut res: i32 = if self.p.decimal() { + let mut res0 = (a & 0x000f) + (v & 0x000f) + c as i32; + if res0 < 0x0010 { + res0 -= 0x0006; + } + + let mut res1 = (a & 0x00f0) + (v & 0x00f0) + (res0 & 0x000f) + if res0 > 0x000f { 0x10 } else { 0x00 }; + if res1 < 0x0100 { + res1 -= 0x0060; + } + + let mut res2 = + (a & 0x0f00) + (v & 0x0f00) + (res1 & 0x00ff) + if res1 > 0x00ff { 0x100 } else { 0x000 }; + if res2 < 0x1000 { + res2 -= 0x0600; + } + + (a & 0xf000) + (v & 0xf000) + (res2 & 0x0fff) + if res2 > 0x0fff { 0x1000 } else { 0x0000 } + } else { + self.a as i32 + v + c as i32 + }; + self.p.set_overflow((self.a ^ res as u16) & 0x8000 != 0 && (self.a ^ v as u16) & 0x8000 == 0); + if self.p.decimal() && res < 0x10000 { + res -= 0x6000; + } + self.p.set_carry(res > 65535); + + self.a = self.p.set_nz(res as u16); + self.cy += 1; + } + } + + /// Shift accumulator left by 1 bit + fn asl_a(&mut self) { + // Sets N, Z and C. The rightmost bit is filled with 0. + if self.p.small_acc() { + let a = self.a as u8; + self.p.set_carry(self.a & 0x80 != 0); + self.a = (self.a & 0xff00) | self.p.set_nz_8(a << 1) as u16; + } else { + self.p.set_carry(self.a & 0x8000 != 0); + self.a = self.p.set_nz(self.a << 1); + } + } + + /// Arithmetic left-shift: Shift a memory location left by 1 bit (Read-Modify-Write) + fn asl(&mut self, am: AddressingMode) { + // Sets N, Z and C. The rightmost bit is filled with 0. + let (bank, addr) = am.address(self); + if self.p.small_acc() { + let val = self.loadb(bank, addr); + self.p.set_carry(val & 0x80 != 0); + let res = self.p.set_nz_8(val << 1); + self.storeb(bank, addr, res); + } else { + let val = self.loadw(bank, addr); + self.p.set_carry(val & 0x8000 != 0); + let res = self.p.set_nz(val << 1); + self.storew(bank, addr, res); + self.cy += 2; + } + } + + /// Rotate Accumulator Left + fn rol_a(&mut self) { + // Sets N, Z, and C. C is used to fill the rightmost bit. + let c: u8 = if self.p.carry() { 1 } else { 0 }; + if self.p.small_acc() { + let a = self.a as u8; + self.p.set_carry(self.a & 0x80 != 0); + let res = (a << 1) | c; + self.a = (self.a & 0xff00) | self.p.set_nz_8(res) as u16; + } else { + self.p.set_carry(self.a & 0x8000 != 0); + let res = (self.a << 1) | c as u16; + self.a = self.p.set_nz(res); + self.cy += 1; + } + } + + /// Rotate Memory Left + fn rol(&mut self, am: AddressingMode) { + // Sets N, Z, and C. C is used to fill the rightmost bit. + let c: u8 = if self.p.carry() { 1 } else { 0 }; + if self.p.small_acc() { + let a = am.clone().loadb(self); + self.p.set_carry(a & 0x80 != 0); + let res = self.p.set_nz_8((a << 1) | c); + am.storeb(self, res); + } else { + let a = am.clone().loadw(self); + self.p.set_carry(a & 0x8000 != 0); + let res = self.p.set_nz((a << 1) | c as u16); + am.storew(self, res); + self.cy += 1; // FIXME times 2? + } + } + + /// Logical Shift Accumulator Right + fn lsr_a(&mut self) { + // Sets N (always cleared), Z and C. The leftmost bit is filled with 0. + if self.p.small_acc() { + let a = self.a as u8; + self.p.set_carry(self.a & 0x01 != 0); + self.a = (self.a & 0xff00) | self.p.set_nz_8(a >> 1) as u16; + } else { + self.p.set_carry(self.a & 0x0001 != 0); + self.a = self.p.set_nz(self.a >> 1); + } + } + + /// Logical Shift Right + fn lsr(&mut self, am: AddressingMode) { + // Sets N (always cleared), Z and C. The leftmost bit is filled with 0. + if self.p.small_acc() { + let a = am.clone().loadb(self); + self.p.set_carry(a & 0x01 != 0); + let res = self.p.set_nz_8(a >> 1); + am.storeb(self, res); + } else { + let a = am.clone().loadw(self); + self.p.set_carry(a & 0x0001 != 0); + let res = self.p.set_nz(a >> 1); + am.storew(self, res); + } + } + + /// Rotate accumulator right + fn ror_a(&mut self) { + // Sets N, Z, and C. C is used to fill the leftmost bit. + let c: u8 = if self.p.carry() { 1 } else { 0 }; + if self.p.small_acc() { + let val = self.a as u8; + self.p.set_carry(val & 0x01 != 0); + let res = self.p.set_nz_8((val >> 1) | (c << 7)); + self.a = (self.a & 0xff00) | res as u16; + } else { + let val = self.a; + self.p.set_carry(val & 0x0001 != 0); + self.a = self.p.set_nz((val >> 1) | ((c as u16) << 15)); + self.cy += 2; + } + } + + /// Rotate Memory Right + fn ror(&mut self, am: AddressingMode) { + // Sets N, Z, and C. C is used to fill the leftmost bit. + // The `AddressingMode` is used for both loading and storing the value (Read-Modify-Write + // instruction) + let c: u8 = if self.p.carry() { 1 } else { 0 }; + let (bank, addr) = am.address(self); + if self.p.small_acc() { + let val = self.loadb(bank, addr); + self.p.set_carry(val & 0x01 != 0); + let res = self.p.set_nz_8((val >> 1) | (c << 7)); + self.storeb(bank, addr, res); + } else { + let val = self.loadw(bank, addr); + self.p.set_carry(val & 0x0001 != 0); + let res = self.p.set_nz((val >> 1) | ((c as u16) << 15)); + self.storew(bank, addr, res); + self.cy += 2; + } + } + + /// Exchange B with A (B is the MSB of the accumulator, A is the LSB) + fn xba(&mut self) { + // Changes N and Z: "The flags are changed based on the new value of the low byte, the A + // accumulator (that is, on the former value of the high byte, the B accumulator), even in + // sixteen-bit accumulator mode." + let lo = self.a & 0xff; + let hi = self.a >> 8; + self.a = (lo << 8) | self.p.set_nz_8(hi as u8) as u16; + } + + /// Transfer Accumulator to Index Register X + fn tax(&mut self) { + // Changes N and Z + if self.p.small_index() { + self.x = (self.x & 0xff00) | self.p.set_nz_8(self.a as u8) as u16; + } else { + self.x = self.p.set_nz(self.a); + } + } + + /// Transfer Accumulator to Index register Y + fn tay(&mut self) { + // Changes N and Z + if self.p.small_index() { + self.y = (self.y & 0xff00) | self.p.set_nz_8(self.a as u8) as u16; + } else { + self.y = self.p.set_nz(self.a); + } + } + + /// Transfer X to A + fn txa(&mut self) { + // Changes N and Z + if self.p.small_acc() { + self.a = (self.a & 0xff00) | self.p.set_nz_8(self.x as u8) as u16; + } else { + self.a = self.p.set_nz(self.x); + } + } + + /// Transfer X to S + fn txs(&mut self) { + // High Byte of X is 0 if X is 8-bit, we can just copy the whole X + // Changes no flags + if self.emulation { + // "When in the Emulation mode, a 01 is forced into SH." + self.s = 0x0100 | (self.x & 0xff); + } else { + self.s = self.x; + } + } + + /// Transfer X to Y + fn txy(&mut self) { + // Changes N and Z + if self.p.small_index() { + self.y = self.p.set_nz_8(self.x as u8) as u16; + } else { + self.y = self.p.set_nz(self.x); + } + } + + /// Transfer Index Register Y to Accumulator + fn tya(&mut self) { + // Changes N and Z + if self.p.small_acc() { + self.a = (self.a & 0xff00) | self.p.set_nz_8(self.y as u8) as u16; + } else { + self.a = self.p.set_nz(self.y); + } + } + + /// Transfer Y to X + fn tyx(&mut self) { + // Changes N and Z + if self.p.small_index() { + self.x = self.p.set_nz_8(self.y as u8) as u16; + } else { + self.x = self.p.set_nz(self.y); + } + } + + /// Increment memory location + fn inc(&mut self, am: AddressingMode) { + let (bank, addr) = am.address(self); + if self.p.small_acc() { + let res = self.loadb(bank, addr).wrapping_add(1); + self.p.set_nz_8(res); + self.storeb(bank, addr, res); + } else { + let res = self.loadw(bank, addr).wrapping_add(1); + self.p.set_nz(res); + self.storew(bank, addr, res); + } + } + + /// Increment accumulator + fn ina(&mut self) { + // Changes N and Z. Timing does not depend on accumulator size. + if self.p.small_acc() { + let res = self.p.set_nz_8((self.a as u8).wrapping_add(1)); + self.a = (self.a & 0xff00) | res as u16; + } else { + self.a = self.p.set_nz(self.a.wrapping_add(1)); + } + } + + /// Increment Index Register X + fn inx(&mut self) { + // Changes N and Z. Timing does not depend on index register size. + if self.p.small_index() { + let res = self.p.set_nz_8((self.x as u8).wrapping_add(1)); + self.x = (self.x & 0xff00) | res as u16; + } else { + self.x = self.p.set_nz(self.x.wrapping_add(1)); + } + } + + /// Increment Index Register Y + fn iny(&mut self) { + // Changes N and Z. Timing does not depend on index register size. + if self.p.small_index() { + let res = self.p.set_nz_8((self.y as u8).wrapping_add(1)); + self.y = (self.y & 0xff00) | res as u16; + } else { + self.y = self.p.set_nz(self.y.wrapping_add(1)); + } + } + + /// Decrement Accumulator + fn dea(&mut self) { + // Changes N and Z. Timing does not depend on accumulator size. + if self.p.small_acc() { + let res = self.p.set_nz_8((self.a as u8).wrapping_sub(1)); + self.a = (self.a & 0xff00) | res as u16; + } else { + self.a = self.p.set_nz(self.a.wrapping_sub(1)); + } + } + + /// Decrement memory location + fn dec(&mut self, am: AddressingMode) { + let (bank, addr) = am.address(self); + if self.p.small_acc() { + let res = self.loadb(bank, addr).wrapping_sub(1); + self.p.set_nz_8(res); + self.storeb(bank, addr, res); + } else { + let res = self.loadw(bank, addr).wrapping_sub(1); + self.p.set_nz(res); + self.storew(bank, addr, res); + } + } + + /// Decrement X + fn dex(&mut self) { + // Changes N and Z. Timing does not depend on index register size. + // NB According to the datasheet, this writes the result to A, not X! But since this + // doesn't make sense when looking at the way it's used, I'm going to ignore the datasheet + if self.p.small_index() { + let res = self.p.set_nz_8((self.x as u8).wrapping_sub(1)); + self.x = (self.x & 0xff00) | res as u16; + } else { + self.x = self.p.set_nz(self.x.wrapping_sub(1)); + } + } + + /// Decrement Y + fn dey(&mut self) { + // Changes N and Z. Timing does not depend on index register size. + if self.p.small_index() { + let res = self.p.set_nz_8((self.y as u8).wrapping_sub(1)); + self.y = (self.y & 0xff00) | res as u16; + } else { + self.y = self.p.set_nz(self.y.wrapping_sub(1)); + } + } + + /// Jump long. Changes the PBR. + fn jml(&mut self, am: AddressingMode) { + let a = am.address(self); + self.branch(a); + } + + /// Jump inside current program bank + fn jmp(&mut self, am: AddressingMode) { + let (_, addr) = am.address(self); + self.pc = addr; + } + + /// Branch always (inside current program bank, but this isn't checked) + fn bra(&mut self, am: AddressingMode) { + let a = am.address(self); + self.branch(a); + } + + /// Branch if Plus (N = 0) + fn bpl(&mut self, am: AddressingMode) { + let a = am.address(self); + if !self.p.negative() { + self.branch(a); + self.cy += 1; + } + } + + /// Branch if Minus/Negative (N = 1) + fn bmi(&mut self, am: AddressingMode) { + let a = am.address(self); + if self.p.negative() { + self.branch(a); + self.cy += 1; + } + } + + /// Branch if Overflow Clear + fn bvc(&mut self, am: AddressingMode) { + let a = am.address(self); + if !self.p.overflow() { + self.branch(a); + self.cy += 1; + } + } + + /// Branch if Overflow Set + fn bvs(&mut self, am: AddressingMode) { + let a = am.address(self); + if self.p.overflow() { + self.branch(a); + self.cy += 1; + } + } + + /// Branch if carry clear + fn bcc(&mut self, am: AddressingMode) { + let a = am.address(self); + if !self.p.carry() { + self.branch(a); + self.cy += 1; + } + } + + /// Branch if carry set + fn bcs(&mut self, am: AddressingMode) { + let a = am.address(self); + if self.p.carry() { + self.branch(a); + self.cy += 1; + } + } + + /// Branch if Equal + fn beq(&mut self, am: AddressingMode) { + let a = am.address(self); + if self.p.zero() { + self.branch(a); + self.cy += 1; + } + } + + /// Branch if Not Equal (Branch if Z = 0) + fn bne(&mut self, am: AddressingMode) { + let a = am.address(self); + if !self.p.zero() { + self.branch(a); + self.cy += 1; + } + } + + /// Test memory bits against accumulator + fn bit(&mut self, am: AddressingMode) { + if self.p.small_acc() { + let val = am.clone().loadb(self); + self.p.set_zero(val & self.a as u8 == 0); + match am { + AddressingMode::Immediate(_) | AddressingMode::Immediate8(_) => {} + _ => { + self.p.set_negative(val & 0x80 != 0); + self.p.set_overflow(val & 0x40 != 0); + } + } + } else { + let val = am.clone().loadw(self); + self.p.set_zero(val & self.a == 0); + match am { + AddressingMode::Immediate(_) | AddressingMode::Immediate8(_) => {} + _ => { + self.p.set_negative(val & 0x8000 != 0); + self.p.set_overflow(val & 0x4000 != 0); + } + } + self.cy += 1; + } + } + + /// Test and set memory bits against accumulator + fn tsb(&mut self, am: AddressingMode) { + // Sets Z + // FIXME Is this correct? + if self.p.small_index() { + let val = am.clone().loadb(self); + self.p.set_zero(val & self.a as u8 == 0); + let res = val | self.a as u8; + am.storeb(self, res); + } else { + let val = am.clone().loadw(self); + self.p.set_zero(val & self.a == 0); + let res = val | self.a; + am.storew(self, res); + + self.cy += 2; + } + } + + /// Test and reset memory bits against accumulator + fn trb(&mut self, am: AddressingMode) { + // Sets Z + // FIXME Is this correct? + if self.p.small_index() { + let val = am.clone().loadb(self); + self.p.set_zero(val & self.a as u8 == 0); + let res = val & !(self.a as u8); + am.storeb(self, res); + } else { + let val = am.clone().loadw(self); + self.p.set_zero(val & self.a == 0); + let res = val & !self.a; + am.storew(self, res); + + self.cy += 2; + } + } + + /// Compare Accumulator with Memory + fn cmp(&mut self, am: AddressingMode) { + if self.p.small_acc() { + let a = self.a as u8; + let b = am.loadb(self); + self.compare8(a, b); + } else { + let a = self.a; + let b = am.loadw(self); + self.compare(a, b); + self.cy += 1; + } + } + + /// Compare Index Register X with Memory + fn cpx(&mut self, am: AddressingMode) { + if self.p.small_index() { + let val = am.loadb(self); + let x = self.x as u8; + self.compare8(x, val); + } else { + let val = am.loadw(self); + let x = self.x; + self.compare(x, val); + self.cy += 1; + } + } + + /// Compare Index Register Y with Memory + fn cpy(&mut self, am: AddressingMode) { + if self.p.small_index() { + let val = am.loadb(self); + let y = self.y as u8; + self.compare8(y, val); + } else { + let val = am.loadw(self); + let y = self.y; + self.compare(y, val); + self.cy += 1; + } + } + + /// Jump to Subroutine (with short address). Doesn't change PBR. + /// + /// "The address saved is the address of the last byte of the JSR instruction (the address of + /// the last byte of the operand), not the address of the next instruction as is the case with + /// some other processors. The address is pushed onto the stack in standard 65x order – the + /// low byte in the lower address, the high byte in the higher address – and done in standard + /// 65x fashion – the first byte is stored at the location pointed to by the stack pointer, the + /// stack pointer is decremented, the second byte is stored, and the stack pointer is + /// decremented again." + fn jsr(&mut self, am: AddressingMode) { + // Changes no flags + let pc = self.pc - 1; + self.pushb((pc >> 8) as u8); + self.pushb(pc as u8); + + self.pc = am.address(self).1; + } + + /// Long jump to subroutine. Additionally saves PBR on the stack and sets it to the bank + /// returned by `am.address()`. + fn jsl(&mut self, am: AddressingMode) { + // Changes no flags + let pbr = self.pbr; + self.pushb(pbr); + let pc = self.pc - 1; + self.pushb((pc >> 8) as u8); + self.pushb(pc as u8); + + let (pbr, pc) = am.address(self); + self.pbr = pbr; + self.pc = pc; + } + + /// Return from Interrupt + fn rti(&mut self) { + self.return_from_interrupt() + } + + /// Return from Subroutine (Short - Like JSR) + fn rts(&mut self) { + let pcl = self.popb() as u16; + let pch = self.popb() as u16; + let pc = (pch << 8) | pcl; + self.pc = pc + 1; // +1 since the last byte of the JSR was saved + } + + /// Return from Subroutine called with `jsl`. + /// + /// This also restores the PBR. + fn rtl(&mut self) { + let pcl = self.popb() as u16; + let pch = self.popb() as u16; + let pbr = self.popb(); + let pc = (pch << 8) | pcl; + self.pbr = pbr; + self.pc = pc + 1; // +1 since the last byte of the JSR was saved + } + + fn cli(&mut self) { + self.p.set_irq_disable(false) + } + + fn sei(&mut self) { + self.p.set_irq_disable(true) + } + + fn cld(&mut self) { + self.p.set_decimal(false) + } + + fn sed(&mut self) { + self.p.set_decimal(true) + } + + fn clc(&mut self) { + self.p.set_carry(false) + } + + fn sec(&mut self) { + self.p.set_carry(true) + } + + fn wai(&mut self) { + self.wai = true; + } + + /// Store 0 to memory + fn stz(&mut self, am: AddressingMode) { + if self.p.small_acc() { + am.storeb(self, 0); + } else { + am.storew(self, 0); + self.cy += 1; + } + } + + /// Load accumulator from memory + fn lda(&mut self, am: AddressingMode) { + // Changes N and Z + if self.p.small_acc() { + let val = am.loadb(self); + self.a = (self.a & 0xff00) | self.p.set_nz_8(val) as u16; + } else { + let val = am.loadw(self); + self.a = self.p.set_nz(val); + self.cy += 1; + } + } + + /// Load X register from memory + fn ldx(&mut self, am: AddressingMode) { + // Changes N and Z + if self.p.small_index() { + let val = am.loadb(self); + self.x = (self.x & 0xff00) | self.p.set_nz_8(val) as u16; + } else { + let val = am.loadw(self); + self.x = self.p.set_nz(val); + self.cy += 1; + } + } + + /// Load Y register from memory + fn ldy(&mut self, am: AddressingMode) { + // Changes N and Z + if self.p.small_index() { + let val = am.loadb(self); + self.y = (self.y & 0xff00) | self.p.set_nz_8(val) as u16; + } else { + let val = am.loadw(self); + self.y = self.p.set_nz(val); + self.cy += 1; + } + } + + /// Store accumulator to memory + fn sta(&mut self, am: AddressingMode) { + // Changes no flags + if self.p.small_acc() { + let b = self.a as u8; + am.storeb(self, b); + } else { + let w = self.a; + am.storew(self, w); + self.cy += 1; + } + } + + fn stx(&mut self, am: AddressingMode) { + // Changes no flags + if self.p.small_index() { + let b = self.x as u8; + am.storeb(self, b); + } else { + let w = self.x; + am.storew(self, w); + self.cy += 1; + } + } + + fn sty(&mut self, am: AddressingMode) { + // Changes no flags + if self.p.small_index() { + let b = self.y as u8; + am.storeb(self, b); + } else { + let w = self.y; + am.storew(self, w); + self.cy += 1; + } + } + + /// Exchange carry and emulation flags + fn xce(&mut self) { + let carry = self.p.carry(); + let e = self.emulation; + self.p.set_carry(e); + self.set_emulation(carry); + } + + /// Reset status bits + /// + /// Clears the bits in the status register that are 1 in the argument (argument is interpreted + /// as 8-bit) + fn rep(&mut self, am: AddressingMode) { + let p = self.p.0 & !am.loadb(self); + self.set_p(p); + } + + /// Set Processor Status Bits + fn sep(&mut self, am: AddressingMode) { + let p = self.p.0 | am.loadb(self); + self.set_p(p); + } + + /// Transfer 16-bit Accumulator to Direct Page Register + fn tcd(&mut self) { + self.d = self.p.set_nz(self.a); + } + + /// Transfer Direct Page Register to 16-bit Accumulator + fn tdc(&mut self) { + self.a = self.p.set_nz(self.d); + } + + /// Transfer 16-bit Accumulator to Stack Pointer + fn tcs(&mut self) { + if self.emulation { + // "When in the Emulation mode, a 01 is forced into SH. In this case, the B Accumulator + // will not be loaded into SH during a TCS instruction." + // S = 16-bit A; B = High byte of S + self.s = 0x0100 | (self.a & 0xff); + } else { + self.s = self.a; + } + } + + /// Transfer Stack Pointer to 16-bit Accumulator + fn tsc(&mut self) { + // Sets N and Z + self.a = self.p.set_nz(self.s); + } + + /// Transfer Stack Pointer to X Register + fn tsx(&mut self) { + // Sets N and Z + if self.p.small_index() { + self.x = self.p.set_nz_8(self.s as u8) as u16; + } else { + self.x = self.p.set_nz(self.s); + } + } + + fn nop(&mut self) {} + + fn ill(&mut self) {} +} + +/// Addressing mode construction +impl Cpu { + fn direct_indirect(&mut self) -> AddressingMode { + AddressingMode::DirectIndirect(self.fetchb()) + } + + fn direct_indirect_long(&mut self) -> AddressingMode { + AddressingMode::DirectIndirectLong(self.fetchb()) + } + + fn direct_indirect_long_idx(&mut self) -> AddressingMode { + AddressingMode::DirectIndirectLongIdx(self.fetchb()) + } + + fn absolute(&mut self) -> AddressingMode { + AddressingMode::Absolute(self.fetchw()) + } + + fn absolute_indexed_x(&mut self) -> AddressingMode { + AddressingMode::AbsIndexedX(self.fetchw()) + } + + fn absolute_indexed_y(&mut self) -> AddressingMode { + AddressingMode::AbsIndexedY(self.fetchw()) + } + + fn absolute_indexed_indirect(&mut self) -> AddressingMode { + AddressingMode::AbsIndexedIndirect(self.fetchw()) + } + + fn absolute_long(&mut self) -> AddressingMode { + let addr = self.fetchw(); + let bank = self.fetchb(); + AddressingMode::AbsoluteLong(bank, addr) + } + + fn absolute_long_indexed_x(&mut self) -> AddressingMode { + let addr = self.fetchw(); + let bank = self.fetchb(); + AddressingMode::AbsLongIndexedX(bank, addr) + } + + fn absolute_indirect(&mut self) -> AddressingMode { + AddressingMode::AbsoluteIndirect(self.fetchw()) + } + + fn absolute_indirect_long(&mut self) -> AddressingMode { + AddressingMode::AbsoluteIndirectLong(self.fetchw()) + } + + fn rel(&mut self) -> AddressingMode { + AddressingMode::Rel(self.fetchb() as i8) + } + + fn relative_long(&mut self) -> AddressingMode { + AddressingMode::RelLong(self.fetchw() as i16) + } + + fn stack_rel(&mut self) -> AddressingMode { + AddressingMode::StackRel(self.fetchb()) + } + + fn direct(&mut self) -> AddressingMode { + AddressingMode::Direct(self.fetchb()) + } + + fn direct_indexed_x(&mut self) -> AddressingMode { + AddressingMode::DirectIndexedX(self.fetchb()) + } + + fn direct_indexed_y(&mut self) -> AddressingMode { + AddressingMode::DirectIndexedY(self.fetchb()) + } + + fn direct_indexed_indirect(&mut self) -> AddressingMode { + AddressingMode::DirectIndexedIndirect(self.fetchb()) + } + + fn direct_indirect_indexed(&mut self) -> AddressingMode { + AddressingMode::DirectIndirectIndexed(self.fetchb()) + } + + /// Immediate value with accumulator size + fn immediate_acc(&mut self) -> AddressingMode { + if self.p.small_acc() { + AddressingMode::Immediate8(self.fetchb()) + } else { + self.cy += 1; + AddressingMode::Immediate(self.fetchw()) + } + } + + /// Immediate value with index register size + fn immediate_index(&mut self) -> AddressingMode { + if self.p.small_index() { + AddressingMode::Immediate8(self.fetchb()) + } else { + self.cy += 1; + AddressingMode::Immediate(self.fetchw()) + } + } + + /// Immediate value, one byte + fn immediate8(&mut self) -> AddressingMode { + AddressingMode::Immediate8(self.fetchb()) + } +} diff --git a/crates/wdc65816/src/statusreg.rs b/crates/wdc65816/src/statusreg.rs new file mode 100644 index 0000000..585ae66 --- /dev/null +++ b/crates/wdc65816/src/statusreg.rs @@ -0,0 +1,127 @@ +//! The CPU's status register + +use std::fmt; + +const NEG_FLAG: u8 = 0x80; +const OVERFLOW_FLAG: u8 = 0x40; +/// 1 = Accumulator is 8-bit (native mode only) +const SMALL_ACC_FLAG: u8 = 0x20; +/// 1 = Index registers X/Y are 8-bit (native mode only) +const SMALL_INDEX_FLAG: u8 = 0x10; +/// Emulation mode only (same bit as `SMALL_INDEX_FLAG`) +#[allow(dead_code)] // FIXME Implement or scrap this +const BREAK_FLAG: u8 = 0x10; +const DEC_FLAG: u8 = 0x08; +/// 1 = IRQs disabled +const IRQ_FLAG: u8 = 0x04; +const ZERO_FLAG: u8 = 0x02; +const CARRY_FLAG: u8 = 0x01; + +#[derive(Debug, Clone)] +pub struct StatusReg(pub u8); + +impl StatusReg { + pub fn new() -> StatusReg { + // Acc and index regs start in 8-bit mode, IRQs disabled + StatusReg(SMALL_ACC_FLAG | SMALL_INDEX_FLAG | IRQ_FLAG) + } + + pub fn negative(&self) -> bool { + self.0 & NEG_FLAG != 0 + } + + pub fn overflow(&self) -> bool { + self.0 & OVERFLOW_FLAG != 0 + } + + pub fn small_acc(&self) -> bool { + self.0 & SMALL_ACC_FLAG != 0 + } + + pub fn small_index(&self) -> bool { + self.0 & SMALL_INDEX_FLAG != 0 + } + + pub fn decimal(&self) -> bool { + self.0 & DEC_FLAG != 0 + } + + pub fn irq_disable(&self) -> bool { + self.0 & IRQ_FLAG != 0 + } + + pub fn zero(&self) -> bool { + self.0 & ZERO_FLAG != 0 + } + + pub fn carry(&self) -> bool { + self.0 & CARRY_FLAG != 0 + } + + fn set(&mut self, flag: u8, value: bool) { + if value { + self.0 |= flag; + } else { + self.0 &= !flag; + } + } + + pub fn set_negative(&mut self, value: bool) { + self.set(NEG_FLAG, value) + } + + pub fn set_overflow(&mut self, value: bool) { + self.set(OVERFLOW_FLAG, value) + } + + pub fn set_small_acc(&mut self, value: bool) { + self.set(SMALL_ACC_FLAG, value) + } + + pub fn set_small_index(&mut self, value: bool) { + self.set(SMALL_INDEX_FLAG, value) + } + + pub fn set_decimal(&mut self, value: bool) { + self.set(DEC_FLAG, value) + } + + pub fn set_irq_disable(&mut self, value: bool) { + self.set(IRQ_FLAG, value) + } + + pub fn set_zero(&mut self, value: bool) { + self.set(ZERO_FLAG, value) + } + + pub fn set_carry(&mut self, value: bool) { + self.set(CARRY_FLAG, value) + } + + pub fn set_nz(&mut self, val: u16) -> u16 { + self.set_zero(val == 0); + self.set_negative(val & 0x8000 != 0); + val + } + + pub fn set_nz_8(&mut self, val: u8) -> u8 { + self.set_zero(val == 0); + self.set_negative(val & 0x80 != 0); + val + } +} + +impl fmt::Display for StatusReg { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(if self.negative() { "N" } else { "-" })?; + f.write_str(if self.overflow() { "V" } else { "-" })?; + f.write_str(if self.small_acc() { "M" } else { "-" })?; + f.write_str(if self.small_index() { "X" } else { "-" })?; + f.write_str(if self.decimal() { "D" } else { "-" })?; + f.write_str(if self.irq_disable() { "I" } else { "-" })?; + f.write_str(if self.zero() { "Z" } else { "-" })?; + f.write_str(if self.carry() { "C" } else { "-" })?; + + Ok(()) + } +} diff --git a/smwe-project/src/lib.rs b/smwe-project/src/lib.rs deleted file mode 100644 index 0b1ed76..0000000 --- a/smwe-project/src/lib.rs +++ /dev/null @@ -1,10 +0,0 @@ -use std::{cell::RefCell, sync::Arc}; - -use smwe_rom::SmwRom; - -pub struct Project { - pub title: String, - pub rom_data: SmwRom, -} - -pub type ProjectRef = Arc>; diff --git a/smwe-rom/src/objects/mod.rs b/smwe-rom/src/objects/mod.rs deleted file mode 100644 index 115be98..0000000 --- a/smwe-rom/src/objects/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod animated_tile_data; -pub mod map16; -pub mod object_gfx_list; -pub mod tilesets; diff --git a/smwe-widgets/src/lib.rs b/smwe-widgets/src/lib.rs deleted file mode 100644 index c5c97cb..0000000 --- a/smwe-widgets/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod flipbook; diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..728c7bc --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,3 @@ +pub mod project; +pub mod ui; +pub mod undo; diff --git a/src/main.rs b/src/main.rs index 056f596..06bb85d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,21 +1,22 @@ -mod ui; +use std::{cell::RefCell, env, rc::Rc}; -use std::{cell::RefCell, env, sync::Arc}; +use eframe::{NativeOptions, Renderer}; +use egui::{vec2, ViewportBuilder}; +use smw_editor::{ + project::{Project, ProjectRef}, + ui::UiMainWindow, +}; -use smwe_project::{Project, ProjectRef}; -use smwe_rom::SmwRom; - -use crate::ui::UiMainWindow; - -fn main() { +fn main() -> eframe::Result<()> { log4rs::init_file("log4rs.yaml", Default::default()).expect("Failed to initialize log4rs"); let project = dev_open_rom(); - let options = eframe::NativeOptions::default(); - if let Err(e) = eframe::run_native("SMW Editor v0.1.0", options, Box::new(|_| Box::new(UiMainWindow::new(project)))) - { - log::error!("Application error: {e}"); + let native_options = NativeOptions { + renderer: Renderer::Glow, + viewport: ViewportBuilder::default().with_min_inner_size(vec2(1280., 720.)), + ..NativeOptions::default() }; + eframe::run_native("SMW Editor v0.1.0", native_options, Box::new(|cc| Box::new(UiMainWindow::new(project, cc)))) } fn dev_open_rom() -> Option { @@ -25,14 +26,12 @@ fn dev_open_rom() -> Option { }; log::info!("Opening ROM from path defined in ROM_PATH"); - let project = Project { - title: String::from("Test Project"), - rom_data: SmwRom::from_file(rom_path) - .map_err(|e| { - log::error!("Couldn't load ROM: {e}"); - e - }) - .ok()?, - }; - Some(Arc::new(RefCell::new(project))) + let project = Project::new(rom_path) + .map_err(|e| { + log::info!("Cannot create project: {e}"); + e + }) + .ok()?; + + Some(Rc::new(RefCell::new(project))) } diff --git a/src/project.rs b/src/project.rs new file mode 100644 index 0000000..cb35365 --- /dev/null +++ b/src/project.rs @@ -0,0 +1,30 @@ +use std::{cell::RefCell, path::Path, rc::Rc, sync::Arc}; + +use egui::Id; +use smwe_emu::rom::Rom; + +#[derive(Debug)] +pub struct Project { + pub title: String, + pub rom: Arc, +} + +pub type ProjectRef = Rc>; + +impl Project { + pub fn new(rom_path: impl AsRef) -> anyhow::Result { + // TODO: check smc/sfc + let mut rom = Rom::new(std::fs::read(&rom_path)?[0x200..].to_vec()); + rom.load_symbols(include_str!("../symbols/SMW_U.sym")); + + Ok(Self { title: String::from("Test Project"), rom: Arc::new(rom) }) + } + + pub fn rom_id() -> Id { + Id::new("rom") + } + + pub fn project_title_id() -> Id { + Id::new("project_title") + } +} diff --git a/src/ui/color.rs b/src/ui/color.rs deleted file mode 100644 index 74785c5..0000000 --- a/src/ui/color.rs +++ /dev/null @@ -1,3 +0,0 @@ -use eframe::egui::Color32; - -pub const TEXT_ERROR: Color32 = Color32::RED; diff --git a/src/ui/dev_utils/address_converter.rs b/src/ui/dev_utils/address_converter.rs index c229901..c6cc7ae 100644 --- a/src/ui/dev_utils/address_converter.rs +++ b/src/ui/dev_utils/address_converter.rs @@ -1,15 +1,14 @@ use std::fmt::Write; use egui::{TextEdit, Ui, WidgetText}; -use smwe_project::ProjectRef; use smwe_rom::snes_utils::addr::{AddrPc, AddrSnes}; use crate::ui::{ - color, dev_utils::address_converter::{ helpers::adjust_to_header, modes::{ConvDir, ConversionMode}, }, + style::{EditorStyle, ErrorStyle}, tool::DockableEditorTool, }; @@ -36,7 +35,7 @@ impl Default for UiAddressConverter { } impl DockableEditorTool for UiAddressConverter { - fn update(&mut self, ui: &mut Ui, _project_ref: &mut Option) { + fn update(&mut self, ui: &mut Ui) { self.mode_selection(ui); self.conversions(ui); } @@ -69,7 +68,7 @@ impl UiAddressConverter { self.address_input(ui, ConvDir::PcToSnes); self.address_input(ui, ConvDir::SnesToPc); if !self.text_error.is_empty() { - ui.colored_label(color::TEXT_ERROR, &self.text_error); + ui.colored_label(ErrorStyle::get_from_egui(ui.ctx(), |style| style.text_color), &self.text_error); } } diff --git a/src/ui/dev_utils/disassembler.rs b/src/ui/dev_utils/disassembler.rs deleted file mode 100644 index 06754d1..0000000 --- a/src/ui/dev_utils/disassembler.rs +++ /dev/null @@ -1,328 +0,0 @@ -use std::{cell::RefCell, collections::BTreeMap, fmt::Write, ops::Deref}; - -use egui::*; -use egui_extras::{Column, TableBuilder}; -use inline_tweak::tweak; -use itertools::Itertools; -use smwe_project::ProjectRef; -use smwe_rom::{ - disassembler::{binary_block::BinaryBlock, instruction::Instruction}, - snes_utils::addr::{Addr, AddrPc, AddrSnes}, -}; - -use crate::ui::tool::DockableEditorTool; - -#[derive(Debug)] -pub struct UiDisassembler { - current_address_scroll: u32, - address_y_map: BTreeMap, - // opt_draw_debug_info: bool, - branch_arrows: Vec, -} - -#[derive(Clone, Debug, Default)] -struct BranchArrow { - source: AddrSnes, - target: AddrSnes, -} - -impl Default for UiDisassembler { - fn default() -> Self { - Self { - current_address_scroll: AddrSnes::MIN.0, - address_y_map: BTreeMap::new(), - // opt_draw_debug_info: false, - branch_arrows: Vec::with_capacity(30), - } - } -} - -impl DockableEditorTool for UiDisassembler { - fn update(&mut self, ui: &mut Ui, project_ref: &mut Option) { - SidePanel::left("disasm_switches_panel").resizable(false).show_inside(ui, |ui| self.switches(ui, project_ref)); - let avail_area = ui.available_rect_before_wrap(); - self.code(ui, project_ref); - self.branch_arrows(ui, project_ref, avail_area); - } - - fn title(&self) -> WidgetText { - "Disassembler".into() - } -} - -impl UiDisassembler { - fn switches(&mut self, ui: &mut Ui, project_ref: &mut Option) { - let project = project_ref.as_ref().unwrap().borrow(); - let disasm = &project.rom_data.disassembly; - - ui.horizontal(|ui| { - ui.add( - DragValue::new(&mut self.current_address_scroll) - .clamp_range({ - let min = AddrSnes::MIN; - let max = AddrSnes::try_from_lorom(AddrPc(disasm.rom_bytes().len() as _)).unwrap(); - min.0..=max.0 - 1 - }) - .prefix("$") - .hexadecimal(6, false, true), - ); - ui.label("Address"); - }); - - // ui.checkbox(&mut self.opt_draw_debug_info, "Draw debug info"); - } - - fn code(&mut self, ui: &mut Ui, project_ref: &mut Option) { - const COLOR_ADDRESS: Color32 = Color32::from_rgba_premultiplied(0xaa, 0xaa, 0xaa, 0xff); - const COLOR_DATA: Color32 = Color32::from_rgba_premultiplied(0xdd, 0xdd, 0xee, 0xff); - const COLOR_CODE: Color32 = Color32::from_rgba_premultiplied(0xee, 0xdd, 0xdd, 0xff); - // const COLOR_BRANCH_TARGET: Color32 = Color32::from_rgba_premultiplied(0xbb, 0xaa, 0xaa, 0xff); - const COLOR_CODE_HEX: Color32 = Color32::from_rgba_premultiplied(0xdd, 0xcc, 0xcc, 0xff); - // const COLOR_DEBUG_NOTE: Color32 = Color32::from_rgba_premultiplied(0xee, 0xee, 0x55, 0xff); - - self.address_y_map.clear(); - - let project = project_ref.as_ref().unwrap().borrow(); - let disasm = &project.rom_data.disassembly; - - let str_buf = RefCell::new(String::with_capacity(256)); - - let write_hex = |bytes: &mut dyn Iterator| { - let mut str_buf = str_buf.borrow_mut(); - str_buf.clear(); - let mut num_bytes = 0; - for byte in bytes { - write!(str_buf, "{byte:02X} ").unwrap(); - num_bytes += 1; - } - (str_buf, num_bytes) - }; - - let curr_pc_addr_scroll = AddrPc::try_from_lorom(AddrSnes(self.current_address_scroll)).unwrap().0; - let first_block_idx = disasm.chunks.partition_point(|(a, _)| a.0 < curr_pc_addr_scroll).max(1) - 1; - let mut current_address = curr_pc_addr_scroll; - - let row_height = tweak!(17.0); - let header_height = tweak!(30.0); - let spacing = ui.spacing().item_spacing; - let total_rows = ((ui.available_height() - header_height) / (row_height + spacing.y)) as _; - - let mut curr_y = ui.cursor().top() + header_height + (0.5 * row_height + spacing.y); - - let min_scroll_height = ui.available_height(); - TableBuilder::new(ui) - .min_scrolled_height(min_scroll_height) - .striped(true) - .cell_layout(Layout::left_to_right(Align::Min)) - .column(Column::exact(tweak!(90.0))) - .column(Column::exact(tweak!(170.0))) - .column(Column::exact(tweak!(250.0))) - .column(Column::exact(tweak!(50.0))) - .column(Column::exact(tweak!(70.0))) - .header(header_height, |mut th| { - th.col(|ui| { - ui.heading("Label"); - }); - th.col(|ui| { - ui.heading("Bytes"); - }); - th.col(|ui| { - ui.heading("Code"); - }); - th.col(|ui| { - ui.heading("A Size"); - }); - th.col(|ui| { - ui.heading("X&Y Size"); - }); - }) - .body(|mut tb| { - let mut lines_drawn_so_far = 0; - 'draw_lines: for (chunk_idx, (chunk_pc, chunk)) in - disasm.chunks.iter().enumerate().skip(first_block_idx) - { - if lines_drawn_so_far >= total_rows { - break 'draw_lines; - } - - let chunk_pc = *chunk_pc; - let next_chunk_pc = disasm - .chunks - .get(chunk_idx + 1) - .map(|c| c.0) - .unwrap_or_else(|| AddrPc(disasm.rom_bytes().len() as _)); - let chunk_bytes = &disasm.rom_bytes()[chunk_pc.as_index()..next_chunk_pc.as_index()]; - - match chunk { - BinaryBlock::EndOfRom => break 'draw_lines, - BinaryBlock::Unknown | BinaryBlock::Data(_) => { - let stride = 8; - let skip_lines = (current_address - chunk_pc.0) / stride; - let chunks = chunk_bytes.iter().copied().chunks(stride as usize); - for (line_number, mut byte_line) in chunks.into_iter().enumerate().skip(skip_lines as usize) - { - let line_addr_str = { - let pc = AddrPc(chunk_pc.0 + line_number as u32 * stride); - let snes = AddrSnes::try_from_lorom(pc).unwrap(); - self.address_y_map.insert(snes, curr_y); - format!("DATA_{:06X}:", snes.0) - }; - - let (bytes_str, num_bytes) = write_hex(&mut byte_line); - current_address += num_bytes; - - let data_str = { - let mut s = String::with_capacity(40); - write!(s, ".db ").unwrap(); - for byte in bytes_str.split(' ').filter(|s| !s.is_empty()) { - write!(s, "${byte},").unwrap(); - } - s.pop().unwrap(); - s - }; - - tb.row(row_height, |mut tr| { - tr.col(|ui| { - ui.monospace(RichText::new(line_addr_str).color(COLOR_ADDRESS)); - }); - tr.col(|ui| { - ui.monospace(RichText::new(bytes_str.deref()).color(COLOR_CODE_HEX)); - }); - tr.col(|ui| { - ui.monospace(RichText::new(data_str).color(COLOR_DATA)); - }); - tr.col(|_ui| {}); - tr.col(|_ui| {}); - }); - - curr_y += row_height + spacing.y; - lines_drawn_so_far += 1; - if lines_drawn_so_far >= total_rows { - break 'draw_lines; - } - } - } - BinaryBlock::Code(code) => { - let first_instruction = code.instructions.partition_point(|i| i.offset.0 < current_address); - - for ins in code.instructions.iter().copied().skip(first_instruction) { - let Instruction { offset: addr, x_flag, m_flag, .. } = ins; - - let line_addr_str = { - let snes = AddrSnes::try_from_lorom(addr).unwrap(); - self.address_y_map.insert(snes, curr_y); - format!("CODE_{:06X}:", snes.0) - }; - - let (code_bytes_str, num_bytes) = { - let mut b_it = disasm - .rom_bytes() - .iter() - .copied() - .skip(addr.as_index()) - .take(ins.opcode.instruction_size()); - write_hex(&mut b_it) - }; - current_address += num_bytes; - - let code_str = format!("{}", ins.display()); - - tb.row(row_height, |mut tr| { - tr.col(|ui| { - ui.monospace(RichText::new(line_addr_str).color(COLOR_ADDRESS)); - }); - tr.col(|ui| { - ui.monospace(RichText::new(code_bytes_str.deref()).color(COLOR_CODE_HEX)); - }); - tr.col(|ui| { - ui.monospace(RichText::new(code_str).color(COLOR_CODE)); - }); - tr.col(|ui| { - ui.monospace( - RichText::new(format!("{}", 8 * (m_flag as u32 + 1))).color(COLOR_CODE), - ); - }); - tr.col(|ui| { - ui.monospace( - RichText::new(format!("{}", 8 * (x_flag as u32 + 1))).color(COLOR_CODE), - ); - }); - }); - - if ins.is_branch_or_jump() { - let source = addr.try_into().unwrap(); - for &target in code.exits.iter() { - self.branch_arrows.push(BranchArrow { source, target }); - } - } - - curr_y += row_height + spacing.y; - lines_drawn_so_far += 1; - if lines_drawn_so_far >= total_rows { - break 'draw_lines; - } - } - } - } - current_address = next_chunk_pc.0; - } - }); - } - - fn branch_arrows(&mut self, ui: &mut Ui, _project_ref: &mut Option, avail_area: Rect) { - const ARROW_SZ: f32 = 4.0f32; - const ARROW_SEP: f32 = 3.0f32; - - let first_visible_addr = self.address_y_map.iter().next().map(|(&e, _)| e).unwrap_or_default(); - let branch_colors: [Color32; 6] = [ - Color32::from_rgba_premultiplied(0xaa, 0xaa, 0xee, 0xff), - Color32::from_rgba_premultiplied(0xaa, 0xee, 0xaa, 0xff), - Color32::from_rgba_premultiplied(0xee, 0xaa, 0xaa, 0xff), - Color32::from_rgba_premultiplied(0xee, 0xee, 0xaa, 0xff), - Color32::from_rgba_premultiplied(0xee, 0xaa, 0xee, 0xff), - Color32::from_rgba_premultiplied(0xaa, 0xee, 0xee, 0xff), - ]; - let mut branch_color_it = branch_colors.iter().copied().cycle(); - let mut arrx = avail_area.left() - ARROW_SZ; - let mut arrows_at_addr: BTreeMap = BTreeMap::new(); - - for arrow in self.branch_arrows.iter() { - arrx = (arrx - ARROW_SEP).max(ARROW_SZ); - let start_arrows = arrows_at_addr.entry(arrow.source).or_insert(0); - let arrow_ystart = self.address_y_map.get(&arrow.source).copied().unwrap() + (*start_arrows as f32); - *start_arrows += 1; - let end_arrows = arrows_at_addr.entry(arrow.target).or_insert(0); - let target_y = self.address_y_map.get(&arrow.target).copied().map(|v| v + (*end_arrows as f32)); - *end_arrows += 1; - let arrow_yend = - target_y.unwrap_or(if arrow.target < first_visible_addr { 0.0f32 } else { avail_area.bottom() }); - let color = branch_color_it.next().unwrap(); - let stroke = Stroke::new(1.0, color); - ui.painter() - .line_segment([Pos2::new(arrx, arrow_ystart), Pos2::new(avail_area.left(), arrow_ystart)], stroke); - ui.painter().line_segment([Pos2::new(arrx, arrow_ystart), Pos2::new(arrx, arrow_yend)], stroke); - - if target_y.is_some() { - let xoff = avail_area.left() - (*end_arrows - 1) as f32 * ARROW_SEP; - - // - insn - ui.painter() - .line_segment([Pos2::new(arrx, arrow_yend), Pos2::new(avail_area.left(), arrow_yend)], stroke); - - // \ insn - ui.painter().line_segment( - [Pos2::new(xoff - ARROW_SZ, arrow_yend - ARROW_SZ), Pos2::new(xoff, arrow_yend)], - stroke, - ); - - // / insn - ui.painter().line_segment( - [Pos2::new(xoff - ARROW_SZ, arrow_yend + ARROW_SZ), Pos2::new(xoff, arrow_yend)], - stroke, - ); - } - } - - self.branch_arrows.clear(); - } -} diff --git a/src/ui/dev_utils/gfx_viewer.rs b/src/ui/dev_utils/gfx_viewer.rs deleted file mode 100644 index e4f627d..0000000 --- a/src/ui/dev_utils/gfx_viewer.rs +++ /dev/null @@ -1,167 +0,0 @@ -use constants::*; -use egui::*; -use smwe_project::ProjectRef; -use smwe_rom::graphics::{gfx_file::TileFormat, palette::ColorPalette}; - -use crate::ui::tool::DockableEditorTool; - -#[allow(dead_code)] -#[rustfmt::skip] -mod constants { - pub const N_TILES_IN_ROW: usize = 16; - - pub const I_PAL_LEVEL_WTF: usize = 0; - pub const I_PAL_LEVEL_PLAYERS: usize = 1; - pub const I_PAL_LEVEL_LAYER3: usize = 2; - pub const I_PAL_LEVEL_BERRY: usize = 3; - pub const I_PAL_LEVEL_BACKGROUND: usize = 4; - pub const I_PAL_LEVEL_FOREGROUND: usize = 5; - pub const I_PAL_LEVEL_SPRITE: usize = 6; - pub const PALETTE_CATEGORIES: [&str; 7] = [ - "Level: Wtf", - "Level: Players", - "Level: Layer3", - "Level: Berry", - "Level: Background", - "Level: Foreground", - "Level: Sprite", - ]; -} - -pub struct UiGfxViewer { - image_handle: Option, - curr_image_size: (usize, usize), - curr_image_format: TileFormat, - curr_gfx_file_num: i32, - curr_palette_row_idx: i32, - curr_bg_palette_num: i32, - curr_fg_palette_num: i32, - curr_sp_palette_num: i32, - curr_pl_palette_num: i32, -} - -impl Default for UiGfxViewer { - fn default() -> Self { - UiGfxViewer { - image_handle: None, - curr_image_size: (0, 0), - curr_image_format: TileFormat::Tile2bpp, - curr_gfx_file_num: 0, - curr_palette_row_idx: 0, - curr_bg_palette_num: 0, - curr_fg_palette_num: 0, - curr_sp_palette_num: 0, - curr_pl_palette_num: 0, - } - } -} - -impl DockableEditorTool for UiGfxViewer { - fn update(&mut self, ui: &mut Ui, project_ref: &mut Option) { - if self.image_handle.is_none() { - self.update_image(project_ref, ui.ctx()); - } - SidePanel::left("switches_panel").resizable(false).show_inside(ui, |ui| self.switches(ui, project_ref)); - self.gfx_image(ui); - } - - fn title(&self) -> WidgetText { - "GFX viewer".into() - } -} - -impl UiGfxViewer { - fn switches(&mut self, ui: &mut Ui, project_ref: &mut Option) { - let mut changed_any = false; - if let Some(project) = project_ref.as_ref() { - let project = project.borrow(); - let rom = &project.rom_data; - - let file_count = rom.gfx.files.len() as i32; - let palette_row_count = 16i32; - let bg_pals_count = rom.gfx.color_palettes.lv_specific_set.bg_palettes.len() as i32; - let fg_pals_count = rom.gfx.color_palettes.lv_specific_set.fg_palettes.len() as i32; - let sp_pals_count = rom.gfx.color_palettes.lv_specific_set.sprite_palettes.len() as i32; - let pl_pals_count = rom.gfx.color_palettes.players.len() as i32 / 10; - - let inputs = [ - ("GFX file number", &mut self.curr_gfx_file_num, file_count), - ("Palette row index", &mut self.curr_palette_row_idx, palette_row_count), - ("Background palette index", &mut self.curr_bg_palette_num, bg_pals_count), - ("Foreground palette index", &mut self.curr_fg_palette_num, fg_pals_count), - ("Sprite palette index", &mut self.curr_sp_palette_num, sp_pals_count), - ("Player palette index", &mut self.curr_pl_palette_num, pl_pals_count), - ]; - - ui.vertical(|ui| { - for (label, var, max) in inputs { - ui.horizontal(|ui| { - let dv = DragValue::new(var).hexadecimal(2, false, true).clamp_range(0..=max - 1); - changed_any |= ui.add(dv).changed(); - ui.label(label); - }); - } - ui.label(format!("{} x {} px", self.curr_image_size.0, self.curr_image_size.1)); - ui.label(match self.curr_image_format { - TileFormat::Tile2bpp => "2BPP", - TileFormat::Tile3bpp => "3BPP", - TileFormat::Tile4bpp => "4BPP", - TileFormat::Tile8bpp => "8BPP", - TileFormat::Tile3bppMode7 => "Mode7", - }); - }); - } - - if changed_any { - self.update_image(project_ref, ui.ctx()); - } - } - - fn gfx_image(&mut self, ui: &mut Ui) { - if let Some(handle) = &self.image_handle { - let (img_w, img_h) = self.curr_image_size; - ScrollArea::vertical().min_scrolled_height(ui.available_height()).show(ui, |ui| { - ui.image(handle, [(img_w * 3) as f32, (img_h * 3) as f32]); - }); - } - } - - fn update_image(&mut self, project_ref: &mut Option, ctx: &Context) { - let project = project_ref.as_ref().unwrap().borrow(); - let rom = &project.rom_data; - let gfx_file = &rom.gfx.files[self.curr_gfx_file_num as usize]; - - let img_w = (gfx_file.tiles.len() * 8).clamp(8, N_TILES_IN_ROW * 8); - let img_h = ((1 + (gfx_file.tiles.len() / N_TILES_IN_ROW)) * 8).max(8); - self.curr_image_size = (img_w, img_h); - self.curr_image_format = gfx_file.tile_format; - - let palette = &rom - .gfx - .color_palettes - .lv_specific_set - .palette_from_indices( - 0, - self.curr_bg_palette_num as usize, - self.curr_fg_palette_num as usize, - self.curr_sp_palette_num as usize, - &rom.gfx.color_palettes, - ) - .unwrap(); - - let mut new_image = ColorImage::new([img_w, img_h], Color32::TRANSPARENT); - let palette = palette.get_row(self.curr_palette_row_idx as usize); - for (tile_idx, tile) in gfx_file.tiles.iter().enumerate() { - let (row, column) = (tile_idx / N_TILES_IN_ROW, tile_idx % N_TILES_IN_ROW); - let (tile_x, tile_y) = (column * 8, row * 8); - let rgba_tile = tile.to_rgba(&palette); - for (pixel_idx, &color) in rgba_tile.iter().enumerate() { - let (pixel_x, pixel_y) = (tile_x + (pixel_idx % 8), tile_y + (pixel_idx / 8)); - new_image[(pixel_x, pixel_y)] = Color32::from(color); - } - } - self.image_handle = Some(ctx.load_texture("gfx-file-image", new_image, TextureOptions::NEAREST)); - - log::info!("Successfully created a GFX file image (w = {img_w}, h = {img_h})."); - } -} diff --git a/src/ui/dev_utils/mod.rs b/src/ui/dev_utils/mod.rs index 36b3827..57c4480 100644 --- a/src/ui/dev_utils/mod.rs +++ b/src/ui/dev_utils/mod.rs @@ -1,6 +1 @@ pub mod address_converter; -pub mod disassembler; -pub mod gfx_viewer; -pub mod palette_viewer; -pub mod rom_info; -pub mod tiles16x16; diff --git a/src/ui/dev_utils/palette_viewer.rs b/src/ui/dev_utils/palette_viewer.rs deleted file mode 100644 index ea93449..0000000 --- a/src/ui/dev_utils/palette_viewer.rs +++ /dev/null @@ -1,216 +0,0 @@ -use egui::*; -use inline_tweak::tweak; -use itertools::Itertools; -use num_enum::TryFromPrimitive; -use smwe_project::ProjectRef; -use smwe_rom::graphics::palette::{ColorPalette, OverworldState}; -use smwe_widgets::flipbook::{AnimationState, Flipbook}; - -use crate::ui::tool::DockableEditorTool; - -#[repr(usize)] -#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, TryFromPrimitive)] -pub enum PaletteContext { - Level = 0, - Overworld = 1, -} - -enum PaletteImage { - Static(TextureHandle), - Animated(AnimationState), -} - -pub struct UiPaletteViewer { - palette_context: PaletteContext, - palette_image: Option, - // Level viewer - level_num: i32, - // Overworld viewer - submap_num: i32, - special_completed: bool, -} - -impl Default for UiPaletteViewer { - fn default() -> Self { - UiPaletteViewer { - palette_context: PaletteContext::Level, - palette_image: None, - level_num: 0, - submap_num: 0, - special_completed: false, - } - } -} - -impl DockableEditorTool for UiPaletteViewer { - fn update(&mut self, ui: &mut Ui, project_ref: &mut Option) { - if self.palette_image.is_none() { - self.update_palette_image(ui, project_ref); - } - - TopBottomPanel::top("palette_selectors_panel").show_inside(ui, |ui| { - self.context_selector(ui, project_ref); - match self.palette_context { - PaletteContext::Level => self.selectors_level(ui, project_ref), - PaletteContext::Overworld => self.selectors_overworld(ui, project_ref), - } - }); - ui.centered_and_justified(|ui| self.display_palette(ui)); - } - - fn title(&self) -> WidgetText { - "Color palettes".into() - } -} - -impl UiPaletteViewer { - fn context_selector(&mut self, ui: &mut Ui, project_ref: &mut Option) { - let mut context_changed = false; - let mut context_raw = self.palette_context as usize; - let context_names = ["Level", "Overworld"]; - - ComboBox::from_label("Context").selected_text(context_names[context_raw]).show_ui(ui, |ui| { - for (context_idx, &context_name) in context_names.iter().enumerate() { - if ui.selectable_value(&mut context_raw, context_idx, context_name).changed() { - context_changed = true; - } - } - }); - - self.palette_context = PaletteContext::try_from(context_raw).unwrap_or(PaletteContext::Level); - - if context_changed { - self.update_palette_image(ui, project_ref); - } - } - - fn selectors_level(&mut self, ui: &mut Ui, project_ref: &mut Option) { - let level_count = { - let project = project_ref.as_ref().unwrap().borrow(); - project.rom_data.levels.len() as i32 - }; - - ui.horizontal(|ui| { - let drag_level_num = - DragValue::new(&mut self.level_num).clamp_range(0..=level_count - 1).hexadecimal(3, false, true); - if ui.add(drag_level_num).changed() { - log::info!("Showing color palette for level {:X}", self.level_num); - self.update_palette_image(ui, project_ref); - } - ui.label("Level number"); - }); - } - - fn selectors_overworld(&mut self, ui: &mut Ui, project_ref: &mut Option) { - if ui.checkbox(&mut self.special_completed, "Special world completed").changed() { - log::info!( - "Showing color palette for {}", - if self.special_completed { "post-special world" } else { "pre-special world" } - ); - self.update_palette_image(ui, project_ref); - } - - let submap_count = { - let project = project_ref.as_ref().unwrap().borrow(); - project.rom_data.gfx.color_palettes.ow_specific_set.layer2_indices.len() as i32 - }; - - ui.horizontal(|ui| { - let drag_submap_num = - DragValue::new(&mut self.submap_num).clamp_range(0..=submap_count - 1).hexadecimal(1, false, true); - if ui.add(drag_submap_num).changed() { - log::info!("Showing color palette for submap {:X}", self.submap_num); - self.update_palette_image(ui, project_ref); - } - ui.label("Submap number"); - }); - } - - fn update_palette_image(&mut self, ui: &mut Ui, project_ref: &mut Option) { - let project = project_ref.as_ref().unwrap().borrow(); - let rom = &project.rom_data; - self.palette_image = Some(match self.palette_context { - PaletteContext::Level => { - let header = &rom.levels[self.level_num as usize].primary_header; - let palette = &rom.gfx.color_palettes.get_level_palette(header).unwrap(); - let frames = palette - .animated - .iter() - .map(|&animated_color| { - let mut image = ColorImage::new([16, 16], Color32::BLACK); - for y in 0..=0xF { - for x in 0..=0xF { - if y == 6 && x == 4 { - image[(x, y)] = Color32::from(animated_color); - } else { - let color = palette.get_color_at(y, x).unwrap(); - image[(x, y)] = Color32::from(color); - } - } - } - image - }) - .collect_vec(); - PaletteImage::Animated( - AnimationState::from_frames(frames, "palette-image", ui.ctx()) - .expect("Cannot assemble animation for palette image"), - ) - } - PaletteContext::Overworld => { - let ow_state = - if self.special_completed { OverworldState::PostSpecial } else { OverworldState::PreSpecial }; - let palette = &rom.gfx.color_palettes.get_submap_palette(self.submap_num as usize, ow_state).unwrap(); - let mut image = ColorImage::new([16, 16], Color32::BLACK); - for y in 0..=0xF { - for x in 0..=0xF { - let color = palette.get_color_at(y, x).unwrap(); - image[(x, y)] = Color32::from(color); - } - } - - PaletteImage::Static(ui.ctx().load_texture("palette-image", image, TextureOptions::NEAREST)) - } - }); - } - - fn display_palette(&mut self, ui: &mut Ui) { - const CELL_SIZE: f32 = 20.0; - let label_size = tweak!(18.0); - let (mut rect, _) = - ui.allocate_exact_size(Vec2::splat(16.0 * CELL_SIZE + label_size), Sense::focusable_noninteractive()); - - // Row and column labels - for col in 0..=0xF { - let mut cpos = rect.min; - let mut rpos = rect.min; - cpos.x += (1 + col) as f32 * CELL_SIZE + tweak!(3.0); - rpos.y += (1 + col) as f32 * CELL_SIZE; - ui.painter().text( - cpos, - Align2::LEFT_TOP, - format!("{col:X}"), - FontId::proportional(label_size), - ui.visuals().text_color(), - ); - ui.painter().text( - rpos, - Align2::LEFT_TOP, - format!("{col:X}"), - FontId::proportional(label_size), - ui.visuals().text_color(), - ); - } - - // Palette image - rect.min += Vec2::splat(label_size); - let image = match self.palette_image.as_mut().unwrap() { - PaletteImage::Static(handle) => { - Shape::image(handle.id(), rect, Rect::from_x_y_ranges(0.0..=1.0, 0.0..=1.0), Color32::WHITE) - } - PaletteImage::Animated(animation) => { - Flipbook::new(animation, rect.size()).fps(tweak!(15.0)).looped(true).to_shape(ui, rect) - } - }; - ui.painter().add(image); - } -} diff --git a/src/ui/dev_utils/rom_info.rs b/src/ui/dev_utils/rom_info.rs deleted file mode 100644 index a139a5f..0000000 --- a/src/ui/dev_utils/rom_info.rs +++ /dev/null @@ -1,54 +0,0 @@ -use eframe::egui::{RichText, Ui}; -use egui::WidgetText; -use egui_extras::{Column, TableBuilder}; -use smwe_project::ProjectRef; -use smwe_rom::internal_header::RomInternalHeader; - -use crate::ui::tool::DockableEditorTool; - -#[derive(Debug)] -pub struct UiRomInfo { - display_data: Vec<(String, String)>, -} - -impl DockableEditorTool for UiRomInfo { - fn update(&mut self, ui: &mut Ui, _project_ref: &mut Option) { - let min_scroll_height = ui.available_height(); - TableBuilder::new(ui) // - .striped(true) - .min_scrolled_height(min_scroll_height) - .columns(Column::exact(130.0), 2) - .body(|body| { - body.rows(15.0, self.display_data.len(), |i, mut row| { - let (name, data) = &self.display_data[i]; - row.col(|ui| { - ui.label(name); - }); - row.col(|ui| { - ui.label(RichText::new(data).monospace()); - }); - }); - }); - } - - fn title(&self) -> WidgetText { - "Internal ROM Header".into() - } -} - -impl UiRomInfo { - pub fn new(header: &RomInternalHeader) -> Self { - Self { - display_data: vec![ - (String::from("Internal ROM name:"), header.internal_rom_name.clone()), - (String::from("Map mode:"), format!("{}", header.map_mode)), - (String::from("ROM type:"), format!("{}", header.rom_type)), - (String::from("ROM size:"), format!("{} kB", header.rom_size_in_kb())), - (String::from("SRAM size:"), format!("{} kB", header.sram_size_in_kb())), - (String::from("Region:"), format!("{}", header.region_code)), - (String::from("Developer ID:"), format!("0x{:x}", header.developer_id)), - (String::from("Version:"), format!("1.{}", header.version_number)), - ], - } - } -} diff --git a/src/ui/dev_utils/tiles16x16.rs b/src/ui/dev_utils/tiles16x16.rs deleted file mode 100644 index e2840f6..0000000 --- a/src/ui/dev_utils/tiles16x16.rs +++ /dev/null @@ -1,242 +0,0 @@ -use std::{borrow::Borrow, cell::RefCell}; - -use egui::{panel::Side, *}; -use egui_extras::{Column, TableBuilder}; -use inline_tweak::tweak; -use itertools::Itertools; -use smwe_project::{Project, ProjectRef}; -use smwe_rom::{ - graphics::{palette::ColorPalette, BlockGfx}, - objects::map16::Tile8x8, -}; -use smwe_widgets::flipbook::{AnimationState, Flipbook}; - -use crate::ui::tool::DockableEditorTool; - -enum TileImage { - Static(TextureHandle), - Animated(AnimationState, f32), -} - -pub struct UiTiles16x16 { - tile_images: Vec, - selected_tileset: u32, - level_number: u32, - blue_pswitch: bool, - silver_pswitch: bool, - on_off_switch: bool, - vram_offset: u16, -} - -impl Default for UiTiles16x16 { - fn default() -> Self { - Self { - tile_images: Vec::new(), - selected_tileset: 1, - level_number: 0, - blue_pswitch: false, - silver_pswitch: false, - on_off_switch: false, - vram_offset: 0, - } - } -} - -impl DockableEditorTool for UiTiles16x16 { - fn update(&mut self, ui: &mut Ui, project_ref: &mut Option) { - if self.tile_images.is_empty() { - self.load_images(project_ref, ui.ctx()); - } - - SidePanel::new(Side::Left, ui.id().with("sp")).resizable(false).show_inside(ui, |ui| { - ComboBox::new(ui.id().with("cb Tileset"), "Tileset") - .selected_text(format!("Tileset {}", self.selected_tileset)) - .show_ui(ui, |ui| { - for i in 1..=5 { - let response = ui.selectable_value(&mut self.selected_tileset, i, format!("Tileset {i}")); - if response.clicked() { - self.load_images(project_ref, ui.ctx()); - } - } - }); - - ui.horizontal(|ui| { - let response = - ui.add(DragValue::new(&mut self.level_number).clamp_range(0..=0x1FF).hexadecimal(3, false, true)); - ui.label("Level number"); - - if response.changed() { - self.load_images(project_ref, ui.ctx()); - } - }); - - if ui.checkbox(&mut self.blue_pswitch, "Blue P-Switch").changed() { - self.load_images(project_ref, ui.ctx()); - } - - if ui.checkbox(&mut self.silver_pswitch, "Silver P-Switch").changed() { - self.load_images(project_ref, ui.ctx()); - } - - if ui.checkbox(&mut self.on_off_switch, "ON/OFF Switch").changed() { - self.load_images(project_ref, ui.ctx()); - } - - ui.horizontal(|ui| { - if ui.add_enabled(self.vram_offset > 0, Button::new("-")).clicked() { - self.vram_offset = self.vram_offset.saturating_sub(0x20); - self.load_images(project_ref, ui.ctx()); - } - if ui - .add(DragValue::new(&mut self.vram_offset).clamp_range(0..=0xFFFF).hexadecimal(4, false, true)) - .changed() - { - self.load_images(project_ref, ui.ctx()); - } - if ui.add_enabled(self.vram_offset < 0xFFFF, Button::new("+")).clicked() { - self.vram_offset = self.vram_offset.saturating_add(0x20); - self.load_images(project_ref, ui.ctx()); - } - ui.label("Level number"); - }); - }); - - let block_size = Vec2::splat(tweak!(32.0)); - let cell_padding = vec2(tweak!(5.0), tweak!(19.0)); - ScrollArea::both().show(ui, |ui| { - Frame::none().show(ui, |ui| { - let available_rect = ui.available_rect_before_wrap(); - ui.expand_to_include_rect(available_rect); - TableBuilder::new(ui).vscroll(false).columns(Column::exact(block_size.x + cell_padding.x), 16).body( - |tb| { - tb.rows(block_size.y + cell_padding.y, self.tile_images.len() / 16, |row, mut tr| { - for tile in (row * 16)..((row * 16) + 16).min(0x200) { - tr.col(|ui| { - ui.label(format!("{tile:X}")); - match &mut self.tile_images[tile] { - TileImage::Static(texture_id) => { - ui.image(texture_id, block_size); - } - TileImage::Animated(animation, fps) => { - ui.add(Flipbook::new(animation, block_size).looped(true).fps(*fps)); - } - } - }); - } - }); - }, - ); - }) - }); - } - - fn title(&self) -> WidgetText { - "Tiles 16x16".into() - } -} - -impl UiTiles16x16 { - fn load_images(&mut self, project_ref: &mut Option, ctx: &Context) { - self.tile_images.clear(); - - let project: &RefCell = project_ref.as_ref().unwrap(); - let rom = &project.borrow().rom_data; - - let level = &rom.levels[self.level_number as usize]; - let palette = rom.gfx.color_palettes.get_level_palette(&level.primary_header).unwrap(); - - for map16_id in 0..=0x1FF { - let map16_tile = rom.map16_tilesets.get_map16_tile(map16_id, self.selected_tileset as usize - 1).unwrap(); - let tiles_8x8 = - [map16_tile.upper_left, map16_tile.lower_left, map16_tile.upper_right, map16_tile.lower_right]; - - match rom.gfx.tiles_from_block( - &map16_tile, - self.selected_tileset as usize - 1, - self.blue_pswitch, - self.silver_pswitch, - self.on_off_switch, - self.vram_offset, - ) { - BlockGfx::Animated(frames) => { - let frames = frames - .into_iter() - .map(|tiles| { - let make_tile_iter = || tiles.into_iter().zip(tiles_8x8); - let map16_gfx = make_tile_iter() - .map(|(gfx, m16)| { - let palette_row = palette.get_row(m16.palette() as usize); - gfx.to_rgba(&palette_row) - }) - .collect_vec(); - assert_eq!(map16_gfx.len(), 4); - Self::make_image(&tiles_8x8, &map16_gfx) - }) - .collect_vec(); - let animation = AnimationState::from_frames(frames, Id::new(format!("map16_{map16_id}")), ctx) - .unwrap_or_else(|e| panic!("Cannot assemble animation for tile {map16_id}: {e}")); - self.tile_images.push(TileImage::Animated(animation, 12.)); - } - BlockGfx::Static(tiles) => { - let make_tile_iter = || tiles.into_iter().zip(tiles_8x8); - - let has_animated_color = make_tile_iter() - .any(|(gfx, m16)| m16.palette() == 6 && gfx.color_indices.iter().any(|&idx| idx == 4)); - - if has_animated_color { - let mut frames = Vec::with_capacity(palette.animated.len()); - for &animated_color in palette.animated.iter() { - let map16_gfx = make_tile_iter() - .map(|(gfx, m16)| { - let palette_row = palette.get_row(m16.palette() as usize); - if m16.palette() == 6 { - gfx.to_rgba_with_substitute_at(&palette_row, animated_color, 4) - } else { - gfx.to_rgba(&palette_row) - } - }) - .collect_vec(); - assert_eq!(map16_gfx.len(), 4); - let frame_image = Self::make_image(&tiles_8x8, &map16_gfx); - frames.push(frame_image); - } - let animation = AnimationState::from_frames(frames, Id::new(format!("map16_{map16_id}")), ctx) - .unwrap_or_else(|e| panic!("Cannot assemble animation for tile {map16_id}: {e}")); - self.tile_images.push(TileImage::Animated(animation, 16.)); - } else { - let map16_gfx = make_tile_iter() - .map(|(gfx, m16)| { - let palette_row = palette.get_row(m16.palette() as usize); - gfx.to_rgba(&palette_row) - }) - .collect_vec(); - assert_eq!(map16_gfx.len(), 4); - let image = ctx.load_texture( - format!("map16 {map16_id}"), - Self::make_image(&tiles_8x8, &map16_gfx), - TextureOptions::NEAREST, - ); - self.tile_images.push(TileImage::Static(image)); - } - } - } - } - } - - fn make_image(tiles_8x8: &[Tile8x8; 4], map16_gfx: &[Box<[Rgba]>]) -> ColorImage { - let mut image = ColorImage::new([16, 16], Color32::TRANSPARENT); - for y in 0..image.size[1] { - for x in 0..image.size[0] { - let ti = (y / 8) + (2 * (x / 8)); - let tile_8x8 = tiles_8x8[ti]; - let tile_gfx: &[Rgba] = map16_gfx[ti].borrow(); - let [ty, tx] = [ - if tile_8x8.flip_y() { 7 - (y % 8) } else { y % 8 }, - if tile_8x8.flip_x() { 7 - (x % 8) } else { x % 8 }, - ]; - image[(x, y)] = Color32::from(tile_gfx[(8 * ty) + tx]); - } - } - image - } -} diff --git a/src/ui/editing_mode.rs b/src/ui/editing_mode.rs new file mode 100644 index 0000000..e581d62 --- /dev/null +++ b/src/ui/editing_mode.rs @@ -0,0 +1,127 @@ +#![allow(dead_code)] + +use egui::{PointerButton, Pos2, Rect, Response, Vec2}; +use smwe_math::coordinates::OnScreen; + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum EditingMode { + Draw, + Erase, + Move(Option), + Probe, + Select, + FlipHorizontally, + FlipVertically, +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum Selection { + Click(Option>), + Drag(Option>), +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct Drag { + pub from: OnScreen, + pub to: OnScreen, +} + +#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)] +pub struct SnapToGrid { + pub cell_origin: OnScreen, +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum FlipDirection { + Horizontal, + Vertical, +} + +impl Drag { + #[inline] + pub fn delta(self) -> OnScreen { + OnScreen(self.to.0 - self.from.0) + } +} + +impl EditingMode { + pub fn inserted(self, response: &Response) -> bool { + match self { + Self::Move(_) => response.clicked_by(PointerButton::Secondary), + Self::Draw => response.clicked_by(PointerButton::Primary) || response.dragged_by(PointerButton::Primary), + _ => false, + } + } + + pub fn moving(&mut self, response: &Response) -> Option { + match self { + Self::Move(drag) => response + .dragged_by(PointerButton::Primary) + .then(|| { + *drag = response.ctx.input(|i| { + i.pointer + .press_origin() + .map(OnScreen) + .zip(response.interact_pointer_pos().map(OnScreen)) + .map(|(from, to)| Drag { from, to }) + }); + *drag + }) + .flatten(), + _ => None, + } + } + + /// If the mode is `Move`, its inner value is cleared. + /// Must be called after [`Self::moving`] to get correct data in the return value. + pub fn dropped(&mut self, response: &Response) -> Option { + match self { + Self::Move(drag) => response.drag_stopped_by(PointerButton::Primary).then_some(drag.take()).flatten(), + _ => None, + } + } + + pub fn selected(self, response: &Response) -> Option { + match self { + Self::Move(_) => response + .clicked_by(PointerButton::Primary) + .then(|| Selection::Click(response.interact_pointer_pos().map(OnScreen))), + Self::Select => response.dragged_by(PointerButton::Primary).then(|| { + let rect = response.ctx.input(|i| { + i.pointer.press_origin().and_then(|origin| { + response.interact_pointer_pos().map(|current| Rect::from_two_pos(origin, current)).map(OnScreen) + }) + }); + Selection::Drag(rect) + }), + _ => None, + } + } + + pub fn erased(self, response: &Response) -> bool { + match self { + Self::Erase => response.clicked_by(PointerButton::Primary) || response.dragged_by(PointerButton::Primary), + _ => false, + } + } + + pub fn probed(self, response: &Response) -> bool { + match self { + Self::Probe => response.clicked_by(PointerButton::Primary), + _ => false, + } + } + + pub fn flipped(self, response: &Response) -> Option { + if response.clicked_by(PointerButton::Primary) { + let invert = response.ctx.input(|input| input.modifiers.command_only()); + match (self, invert) { + (Self::FlipHorizontally, false) | (Self::FlipVertically, true) => Some(FlipDirection::Horizontal), + (Self::FlipVertically, false) | (Self::FlipHorizontally, true) => Some(FlipDirection::Vertical), + _ => None, + } + } else { + None + } + } +} diff --git a/src/ui/editor_prototypes/block_editor.rs b/src/ui/editor_prototypes/block_editor.rs index b495eca..15a30ed 100644 --- a/src/ui/editor_prototypes/block_editor.rs +++ b/src/ui/editor_prototypes/block_editor.rs @@ -1,7 +1,6 @@ use egui::*; use egui_extras::{Column, TableBuilder}; use inline_tweak::tweak; -use smwe_project::ProjectRef; use crate::ui::tool::DockableEditorTool; @@ -45,7 +44,7 @@ impl Default for UiBlockEditor { } impl DockableEditorTool for UiBlockEditor { - fn update(&mut self, ui: &mut Ui, project_ref: &mut Option) { + fn update(&mut self, ui: &mut Ui) { ui.horizontal(|ui| { for (i, mode) in self.editing_modes.iter().enumerate() { if ui.add_enabled(self.editing_mode_idx != i, Button::new(mode)).clicked() { @@ -53,15 +52,11 @@ impl DockableEditorTool for UiBlockEditor { } } }); - SidePanel::left(ui.id().with("block-editor-left")) - .resizable(false) - .show_inside(ui, |ui| self.mappings(ui, project_ref)); - SidePanel::right(ui.id().with("block-editor-right")) - .resizable(false) - .show_inside(ui, |ui| self.vram(ui, project_ref)); + SidePanel::left(ui.id().with("block-editor-left")).resizable(false).show_inside(ui, |ui| self.mappings(ui)); + SidePanel::right(ui.id().with("block-editor-right")).resizable(false).show_inside(ui, |ui| self.vram(ui)); CentralPanel::default().show_inside(ui, |ui| { - self.appearance(ui, project_ref); - self.behaviour(ui, project_ref); + self.appearance(ui); + self.behaviour(ui); }); } @@ -71,15 +66,15 @@ impl DockableEditorTool for UiBlockEditor { } impl UiBlockEditor { - fn mappings(&mut self, ui: &mut Ui, _project_ref: &mut Option) { + fn mappings(&mut self, ui: &mut Ui) { ui.heading("Mappings"); } - fn vram(&mut self, ui: &mut Ui, _project_ref: &mut Option) { + fn vram(&mut self, ui: &mut Ui) { ui.heading("VRAM"); } - fn appearance(&mut self, ui: &mut Ui, _project_ref: &mut Option) { + fn appearance(&mut self, ui: &mut Ui) { ui.heading("Appearance"); TableBuilder::new(ui) @@ -107,7 +102,8 @@ impl UiBlockEditor { }); }) .body(|tb| { - tb.rows(tweak!(15.0), 4, |i, mut tr| { + let mut i = 0; + tb.rows(tweak!(15.0), 4, |mut tr| { tr.col(|ui| { ui.label(i.to_string()); }); @@ -126,11 +122,12 @@ impl UiBlockEditor { tr.col(|ui| { ui.checkbox(&mut self.tile_priorities[i], ""); }); + i += 1; }) }); } - fn behaviour(&mut self, ui: &mut Ui, _project_ref: &mut Option) { + fn behaviour(&mut self, ui: &mut Ui) { ui.heading("Behaviour"); ComboBox::from_label("Collision type") diff --git a/src/ui/editor_prototypes/code_editor.rs b/src/ui/editor_prototypes/code_editor.rs deleted file mode 100644 index 2cb13d7..0000000 --- a/src/ui/editor_prototypes/code_editor.rs +++ /dev/null @@ -1,67 +0,0 @@ -use egui::{TextEdit, Ui, WidgetText}; -use egui_extras::{Column, TableBuilder}; -use smwe_project::ProjectRef; -use smwe_rom::disassembler::serialization::LineKind; - -use crate::ui::tool::DockableEditorTool; - -#[derive(Default)] -pub struct UiCodeEditor {} - -impl DockableEditorTool for UiCodeEditor { - fn update(&mut self, ui: &mut Ui, project_ref: &mut Option) { - let min_scroll_height = ui.available_height(); - TableBuilder::new(ui) - .min_scrolled_height(min_scroll_height) - .columns(Column::remainder().at_least(300.), 2) - .body(|tb| { - let mut project = project_ref.as_mut().unwrap().borrow_mut(); - tb.rows(15., project.rom_data.disassembly.code_lines.len(), |i, mut tr| { - match &mut project.rom_data.disassembly.code_lines[i] { - LineKind::Label { label, comment } => { - tr.col(|ui| { - ui.horizontal(|ui| { - ui.add( - TextEdit::singleline(label).code_editor().clip_text(false).desired_width(0.), - ); - ui.monospace(":"); - }); - }); - tr.col(|ui| { - ui.horizontal(|ui| { - ui.monospace(if comment.is_empty() { " " } else { ";" }); - ui.add( - TextEdit::singleline(comment).code_editor().clip_text(false).desired_width(0.), - ); - }); - }); - } - LineKind::Op { op, comment } => { - tr.col(|ui| { - ui.indent("line-indent", |ui| { - ui.add(TextEdit::singleline(op).code_editor().clip_text(false).desired_width(0.)); - }); - }); - tr.col(|ui| { - ui.horizontal(|ui| { - ui.monospace(if comment.is_empty() { " " } else { ";" }); - ui.add( - TextEdit::singleline(comment).code_editor().clip_text(false).desired_width(0.), - ); - }); - }); - } - LineKind::Empty {} => { - tr.col(|_| {}); - tr.col(|_| {}); - } - _ => {} - } - }) - }); - } - - fn title(&self) -> WidgetText { - "Code editor".into() - } -} diff --git a/src/ui/editor_prototypes/level_editor/central_panel.rs b/src/ui/editor_prototypes/level_editor/central_panel.rs new file mode 100644 index 0000000..935766c --- /dev/null +++ b/src/ui/editor_prototypes/level_editor/central_panel.rs @@ -0,0 +1,71 @@ +use std::sync::Arc; + +use egui::{vec2, Color32, PaintCallback, PointerButton, Rect, Rounding, Sense, Stroke, Ui, Vec2}; +use egui_glow::CallbackFn; +use inline_tweak::tweak; +use smwe_math::coordinates::{OnCanvas, OnGrid, OnScreen}; +use smwe_render::color::Abgr1555; + +use super::UiLevelEditor; + +impl UiLevelEditor { + fn editable_area_rect(&self, view_rect: OnScreen) -> OnScreen { + let (level_width, level_height) = self.level_properties.level_dimensions_in_tiles(); + let editable_area_size = OnGrid::::new(level_width as f32, level_height as f32).to_screen( + self.pixels_per_point, + self.zoom, + self.tile_size_px, + ); + let editable_area_min = OnCanvas(self.offset).to_screen(self.pixels_per_point, self.zoom).to_pos2() + + OnScreen(view_rect.min).to_vec2(); + OnScreen::::from_min_size(editable_area_min, editable_area_size).intersect(view_rect) + } + + pub(super) fn central_panel(&mut self, ui: &mut Ui) { + let level_renderer = Arc::clone(&self.level_renderer); + let (view_rect, response) = + ui.allocate_exact_size(vec2(ui.available_width(), ui.available_height()), Sense::click_and_drag()); + let screen_size_px = view_rect.size() * ui.ctx().pixels_per_point(); + + let zoom = self.zoom; + if response.dragged_by(PointerButton::Middle) { + let mut level_renderer = level_renderer.lock().unwrap(); + let delta = response.drag_delta(); + self.offset += delta / zoom; + level_renderer.set_offset(self.offset); + } + + // Background. + let bg_color = self.cpu.mem.load_u16(0x7E0701); + let bg_color = Color32::from(Abgr1555(bg_color)); + ui.painter().rect_filled(self.editable_area_rect(OnScreen(view_rect)).0, Rounding::ZERO, bg_color); + + // Level. + ui.painter().add(PaintCallback { + rect: view_rect, + callback: Arc::new(CallbackFn::new(move |_info, painter| { + level_renderer.lock().expect("Cannot lock mutex on level_renderer").paint( + painter.gl(), + screen_size_px, + zoom, + ); + })), + }); + + // Grid. + if self.always_show_grid || ui.input(|i| i.modifiers.shift_only()) { + let spacing = self.zoom * self.tile_size_px / self.pixels_per_point; + let stroke = Stroke::new(1., Color32::from_white_alpha(tweak!(70))); + for col in 0..=(view_rect.width() / spacing) as u32 { + let x_offset = (self.offset.x * self.zoom / self.pixels_per_point).rem_euclid(spacing); + let x_coord = col as f32 * spacing + x_offset; + ui.painter().vline(view_rect.min.x + x_coord, view_rect.min.y..=view_rect.max.y, stroke); + } + for row in 0..=(view_rect.height() / spacing) as u32 { + let y_offset = (self.offset.y * self.zoom / self.pixels_per_point).rem_euclid(spacing); + let y_coord = row as f32 * spacing + y_offset; + ui.painter().hline(view_rect.min.x..=view_rect.max.x, view_rect.min.y + y_coord, stroke); + } + } + } +} diff --git a/src/ui/editor_prototypes/level_editor/left_panel.rs b/src/ui/editor_prototypes/level_editor/left_panel.rs new file mode 100644 index 0000000..dfd3cd6 --- /dev/null +++ b/src/ui/editor_prototypes/level_editor/left_panel.rs @@ -0,0 +1,61 @@ +use egui::{vec2, DragValue, Slider, Ui}; +use smwe_widgets::value_switcher::{ValueSwitcher, ValueSwitcherButtons}; + +use super::UiLevelEditor; + +impl UiLevelEditor { + pub(super) fn left_panel(&mut self, ui: &mut Ui) { + if cfg!(debug_assertions) { + ui.add_space(ui.spacing().item_spacing.y); + ui.group(|ui| { + ui.allocate_space(vec2(ui.available_width(), 0.)); + self.debug_panel(ui); + }); + } + } + + fn debug_panel(&mut self, ui: &mut Ui) { + let mut need_update_level = false; + let mut need_update = false; + need_update_level |= { + let switcher = ValueSwitcher::new(&mut self.level_num, "Level", ValueSwitcherButtons::MinusPlus) + .range(0..=0x1FF) + .hexadecimal(3, false, true); + ui.add(switcher).changed() + }; + need_update_level |= { + let switcher = ValueSwitcher::new(&mut self.sprite_id, "Sprite ID", ValueSwitcherButtons::MinusPlus) + .range(0..=0xFF) + .hexadecimal(2, false, true); + ui.add(switcher).changed() + }; + ui.horizontal(|ui| { + need_update |= ui + .add(DragValue::new(&mut self.palette_line).clamp_range(0x0..=0xF).hexadecimal(1, false, true)) + .changed(); + ui.label("Palette"); + }); + need_update |= ui.checkbox(&mut self.blue_pswitch, "Blue P-Switch").changed(); + need_update |= ui.checkbox(&mut self.silver_pswitch, "Silver P-Switch").changed(); + need_update |= ui.checkbox(&mut self.on_off_switch, "ON/OFF Switch").changed(); + ui.checkbox(&mut self.run_sprites, "Run sprites"); + if ui.button("»").clicked() { + self.update_cpu_sprite_id(); + // self.draw_sprites(state, ui.ctx()); + } + + ui.add(Slider::new(&mut self.zoom, 1.0..=3.0).step_by(0.25)); + + ui.checkbox(&mut self.always_show_grid, "Always show grid"); + + if need_update_level { + self.update_cpu(); + self.update_level_properties(); + self.update_layer1(); + self.update_cpu_sprite_id(); + } + if need_update || need_update_level { + self.update_renderer(); + } + } +} diff --git a/src/ui/editor_prototypes/level_editor/level_renderer.rs b/src/ui/editor_prototypes/level_editor/level_renderer.rs new file mode 100644 index 0000000..4a97744 --- /dev/null +++ b/src/ui/editor_prototypes/level_editor/level_renderer.rs @@ -0,0 +1,193 @@ +use egui::Vec2; +use glow::*; +use smwe_emu::Cpu; +use smwe_render::{ + gfx_buffers::GfxBuffers, + tile_renderer::{Tile, TileRenderer, TileUniforms}, +}; + +#[derive(Debug)] +pub(super) struct LevelRenderer { + layer1: TileRenderer, + layer2: TileRenderer, + sprites: TileRenderer, + gfx_bufs: GfxBuffers, + + offset: Vec2, + destroyed: bool, +} + +impl LevelRenderer { + pub(super) fn new(gl: &Context) -> Self { + let layer1 = TileRenderer::new(gl); + let layer2 = TileRenderer::new(gl); + let sprites = TileRenderer::new(gl); + let gfx_bufs = GfxBuffers::new(gl); + Self { layer1, layer2, sprites, gfx_bufs, offset: Vec2::splat(0.), destroyed: false } + } + + pub(super) fn destroy(&mut self, gl: &Context) { + self.gfx_bufs.destroy(gl); + self.layer1.destroy(gl); + self.layer2.destroy(gl); + self.destroyed = true; + } + + pub(super) fn paint(&self, gl: &Context, screen_size: Vec2, zoom: f32) { + if self.destroyed { + return; + } + let uniforms = TileUniforms { gfx_bufs: self.gfx_bufs, screen_size, offset: self.offset, zoom }; + self.layer2.paint(gl, &uniforms); + self.layer1.paint(gl, &uniforms); + self.sprites.paint(gl, &uniforms); + } + + pub(super) fn upload_gfx(&self, gl: &Context, data: &[u8]) { + if self.destroyed { + return; + } + self.gfx_bufs.upload_vram(gl, data); + } + + pub(super) fn upload_palette(&self, gl: &Context, data: &[u8]) { + if self.destroyed { + return; + } + self.gfx_bufs.upload_palette(gl, data); + } + + pub(super) fn upload_level(&mut self, gl: &Context, cpu: &mut Cpu) { + if self.destroyed { + return; + } + self.load_layer(gl, cpu, false); + self.load_layer(gl, cpu, true); + } + + pub(super) fn upload_sprites(&mut self, gl: &Context, cpu: &mut Cpu) { + if self.destroyed { + return; + } + + let mut tiles = Vec::new(); + for spr in (0..64).rev() { + let mut x = cpu.mem.load_u8(0x300 + spr * 4) as u32; + let mut y = cpu.mem.load_u8(0x301 + spr * 4) as u32; + if y >= 0xE0 { + continue; + } + x += cpu.mem.load_u16(0x1A) as u32; + y += cpu.mem.load_u16(0x1C) as u32; + let tile = cpu.mem.load_u16(0x302 + spr * 4); + let size = cpu.mem.load_u8(0x460 + spr); + if size & 0x01 != 0 { + if x > 0x80 { + x = x.wrapping_sub(256); + } else { + x = x.wrapping_add(256); + } + } + if size & 0x02 != 0 { + let (xn, xf) = if tile & 0x4000 == 0 { (0, 8) } else { (8, 0) }; + let (yn, yf) = if tile & 0x8000 == 0 { (0, 8) } else { (8, 0) }; + tiles.push(sp_tile(x.wrapping_add(xn), y.wrapping_add(yn), tile)); + tiles.push(sp_tile(x.wrapping_add(xf), y.wrapping_add(yn), tile + 1)); + tiles.push(sp_tile(x.wrapping_add(xn), y.wrapping_add(yf), tile + 16)); + tiles.push(sp_tile(x.wrapping_add(xf), y.wrapping_add(yf), tile + 17)); + //Self::draw_tile_sp(cpu, &mut new_image, tile + 1, x + xf, y + yn); + //Self::draw_tile_sp(cpu, &mut new_image, tile + 16, x + xn, y + yf); + //Self::draw_tile_sp(cpu, &mut new_image, tile + 17, x + xf, y + yf); + } else { + tiles.push(sp_tile(x, y, tile)); + } + } + self.sprites.set_tiles(gl, tiles); + } + + pub(super) fn set_offset(&mut self, offset: Vec2) { + if self.destroyed { + return; + } + self.offset = offset; + } + + fn load_layer(&mut self, gl: &Context, cpu: &mut Cpu, bg: bool) { + let mut tiles = Vec::new(); + let map16_bank = cpu.mem.cart.resolve("Map16Common").expect("Cannot resolve Map16Common") & 0xFF0000; + let map16_bg = cpu.mem.cart.resolve("Map16BGTiles").expect("Cannot resolve Map16BGTiles"); + let vertical = cpu.mem.load_u8(0x5B) & if bg { 2 } else { 1 } != 0; + let has_layer2 = { + let mode = cpu.mem.load_u8(0x1925); + let renderer_table = cpu.mem.cart.resolve("CODE_058955").unwrap() + 9; + let renderer = cpu.mem.load_u24(renderer_table + (mode as u32) * 3); + let l2_renderers = [cpu.mem.cart.resolve("CODE_058B8D"), cpu.mem.cart.resolve("CODE_058C71")]; + l2_renderers.contains(&Some(renderer)) + }; + let scr_len = match (vertical, has_layer2) { + (false, false) => 0x20, + (true, false) => 0x1C, + (false, true) => 0x10, + (true, true) => 0x0E, + }; + let scr_size = if vertical { 16 * 32 } else { 16 * 27 }; + let (blocks_lo_addr, blocks_hi_addr) = match (bg, has_layer2) { + (true, true) => { + let offset = scr_len * scr_size; + (0x7EC800 + offset, 0x7FC800 + offset) + } + (true, false) => (0x7EB900, 0x7EBD00), + (false, _) => (0x7EC800, 0x7FC800), + }; + let len = if has_layer2 { 256 * 27 } else { 512 * 27 }; + for idx in 0..len { + let (block_x, block_y) = if vertical { + let (screen, sidx) = (idx / (16 * 16), idx % (16 * 16)); + let (row, column) = (sidx / 16, sidx % 16); + let (sub_y, sub_x) = (screen / 2, screen % 2); + (column * 16 + sub_x * 256, row * 16 + sub_y * 256) + } else { + let (screen, sidx) = (idx / (16 * 27), idx % (16 * 27)); + let (row, column) = (sidx / 16, sidx % 16); + (column * 16 + screen * 256, row * 16) + }; + let idx = if bg && !has_layer2 { idx % (16 * 27 * 2) } else { idx }; + let block_id = + cpu.mem.load_u8(blocks_lo_addr + idx) as u16 | ((cpu.mem.load_u8(blocks_hi_addr + idx) as u16) << 8); + let block_ptr = if bg && !has_layer2 { + block_id as u32 * 8 + map16_bg + } else { + cpu.mem.load_u16(0x0FBE + block_id as u32 * 2) as u32 + map16_bank + }; + for (tile_id, (off_x, off_y)) in (0..4).zip([(0, 0), (0, 8), (8, 0), (8, 8)]) { + let tile_id = cpu.mem.load_u16(block_ptr + tile_id * 2); + tiles.push(bg_tile(block_x + off_x, block_y + off_y, tile_id)); + } + } + + // todo: non-background layer2 + if bg { + self.layer2.set_tiles(gl, tiles); + } else { + self.layer1.set_tiles(gl, tiles); + } + } +} + +fn bg_tile(x: u32, y: u32, t: u16) -> Tile { + let t = t as u32; + let tile = t & 0x3FF; + let scale = 8; + let pal = (t >> 10) & 0x7; + let params = scale | (pal << 8) | (t & 0xC000); + Tile([x, y, tile, params]) +} + +fn sp_tile(x: u32, y: u32, t: u16) -> Tile { + let t = t as u32; + let tile = (t & 0x1FF) + 0x600; + let scale = 8; + let pal = ((t >> 9) & 0x7) + 8; + let params = scale | (pal << 8) | (t & 0xC000); + Tile([x, y, tile, params]) +} diff --git a/src/ui/editor_prototypes/level_editor/mod.rs b/src/ui/editor_prototypes/level_editor/mod.rs new file mode 100644 index 0000000..fffb612 --- /dev/null +++ b/src/ui/editor_prototypes/level_editor/mod.rs @@ -0,0 +1,167 @@ +mod central_panel; +mod left_panel; +mod level_renderer; +mod object_layer; +mod properties; + +use std::sync::{Arc, Mutex}; + +use egui::{CentralPanel, SidePanel, Ui, WidgetText, *}; +use smwe_emu::{emu::CheckedMem, rom::Rom, Cpu}; + +use self::{level_renderer::LevelRenderer, object_layer::EditableObjectLayer, properties::LevelProperties}; +use crate::ui::tool::DockableEditorTool; + +pub struct UiLevelEditor { + gl: Arc, + cpu: Cpu, + level_renderer: Arc>, + + level_num: u16, + blue_pswitch: bool, + silver_pswitch: bool, + on_off_switch: bool, + run_sprites: bool, + palette_line: u8, + sprite_id: u8, + timestamp: std::time::Instant, + + offset: Vec2, + zoom: f32, + tile_size_px: f32, + pixels_per_point: f32, + always_show_grid: bool, + + level_properties: LevelProperties, + layer1: EditableObjectLayer, +} + +impl UiLevelEditor { + pub fn new(gl: Arc, rom: Arc) -> Self { + let level_renderer = Arc::new(Mutex::new(LevelRenderer::new(&gl))); + let mut editor = Self { + gl, + cpu: Cpu::new(CheckedMem::new(rom)), + level_renderer, + level_num: 0x105, + blue_pswitch: false, + silver_pswitch: false, + on_off_switch: false, + run_sprites: false, + palette_line: 0, + sprite_id: 0, + timestamp: std::time::Instant::now(), + offset: Vec2::ZERO, + zoom: 2., + tile_size_px: 16., + pixels_per_point: 1., + always_show_grid: false, + level_properties: LevelProperties::default(), + layer1: EditableObjectLayer::default(), + }; + editor.init_cpu(); + editor.update_cpu_sprite(); + editor.update_renderer(); + editor + } +} + +// UI +impl DockableEditorTool for UiLevelEditor { + fn update(&mut self, ui: &mut Ui) { + self.pixels_per_point = ui.ctx().pixels_per_point(); + + SidePanel::left("level_editor.left_panel").resizable(false).show_inside(ui, |ui| self.left_panel(ui)); + + CentralPanel::default() + .frame(Frame::none().inner_margin(0.).fill(Color32::GRAY)) + .show_inside(ui, |ui| self.central_panel(ui)); + + // Auto-play animations + let ft = std::time::Duration::from_secs_f32(1. / 60.); + let now = std::time::Instant::now(); + while now - self.timestamp > ft { + self.timestamp += ft; + self.update_timers(); + self.update_anim_frame(); + if self.run_sprites { + self.update_cpu_sprite(); + //self.level_renderer.lock().unwrap().upload_level(&self.gl, &mut self.cpu); + } + } + ui.ctx().request_repaint(); + } + + fn title(&self) -> WidgetText { + "Level Editor".into() + } + + fn on_closed(&mut self) { + self.level_renderer.lock().unwrap().destroy(&self.gl); + } +} + +// Internals +impl UiLevelEditor { + fn init_cpu(&mut self) { + smwe_emu::emu::decompress_sublevel(&mut self.cpu, self.level_num); + println!("Updated CPU"); + self.level_renderer.lock().unwrap().upload_level(&self.gl, &mut self.cpu); + self.update_level_properties(); + self.update_layer1(); + } + + fn update_cpu(&mut self) { + smwe_emu::emu::decompress_extram(&mut self.cpu, self.level_num); + println!("Updated CPU"); + self.level_renderer.lock().unwrap().upload_level(&self.gl, &mut self.cpu); + } + + fn update_level_properties(&mut self) { + self.level_properties = LevelProperties::parse_from_ram(&mut self.cpu); + } + + fn update_layer1(&mut self) { + self.layer1 = EditableObjectLayer::parse_from_ram(&mut self.cpu, self.level_properties.is_vertical) + .expect("Failed to parse objects from ExtRAM"); + } + + fn update_timers(&mut self) { + let m = self.cpu.mem.load_u8(0x13); + self.cpu.mem.store_u8(0x13, m.wrapping_add(1)); + let m = self.cpu.mem.load_u8(0x14); + self.cpu.mem.store_u8(0x14, m.wrapping_add(1)); + } + + fn update_cpu_sprite(&mut self) { + self.cpu.mem.wram[0x300..0x400].fill(0xE0); + smwe_emu::emu::exec_sprites(&mut self.cpu); + self.level_renderer.lock().unwrap().upload_sprites(&self.gl, &mut self.cpu); + } + + fn update_cpu_sprite_id(&mut self) { + let mut cpu = self.cpu.clone(); + cpu.mem.wram[0x300..0x400].fill(0xE0); + smwe_emu::emu::exec_sprite_id(&mut cpu, self.sprite_id); + self.level_renderer.lock().unwrap().upload_sprites(&self.gl, &mut cpu); + } + + fn update_anim_frame(&mut self) { + self.cpu.mem.store_u8(0x14AD, self.blue_pswitch as u8); + self.cpu.mem.store_u8(0x14AE, self.silver_pswitch as u8); + self.cpu.mem.store_u8(0x14AF, self.on_off_switch as u8); + smwe_emu::emu::fetch_anim_frame(&mut self.cpu); + self.level_renderer + .lock() + .expect("Cannot lock mutex on level_renderer") + .upload_gfx(&self.gl, &self.cpu.mem.vram); + } + + fn update_renderer(&mut self) { + self.update_anim_frame(); + + let level_renderer = self.level_renderer.lock().expect("Cannot lock mutex on level_renderer"); + level_renderer.upload_palette(&self.gl, &self.cpu.mem.cgram); + level_renderer.upload_gfx(&self.gl, &self.cpu.mem.vram); + } +} diff --git a/src/ui/editor_prototypes/level_editor/object_layer.rs b/src/ui/editor_prototypes/level_editor/object_layer.rs new file mode 100644 index 0000000..c9029e2 --- /dev/null +++ b/src/ui/editor_prototypes/level_editor/object_layer.rs @@ -0,0 +1,142 @@ +#![allow(clippy::identity_op)] +#![allow(dead_code)] + +use smwe_emu::Cpu; +use smwe_rom::objects::Object; + +const SCREEN_WIDTH: u32 = 16; + +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] +pub(super) struct EditableObject { + pub x: u32, + pub y: u32, + pub id: u8, + pub settings: u8, +} + +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] +pub(super) struct EditableExit { + pub screen: u8, + pub midway: bool, + pub secondary: bool, + pub id: u16, +} + +#[derive(Clone, Debug, Default)] +pub(super) struct EditableObjectLayer { + pub objects: Vec, + pub exits: Vec, +} + +impl EditableObject { + pub fn from_raw(object: Object, current_screen: u32, vertical_level: bool) -> Option { + (!object.is_exit() && !object.is_screen_jump()).then(|| EditableObject { + x: object.x() as u32 + if vertical_level { 0 } else { current_screen * SCREEN_WIDTH }, + y: object.y() as u32 + if vertical_level { current_screen * SCREEN_WIDTH } else { 0 }, + id: object.standard_object_number(), + settings: object.settings(), + }) + } + + pub fn to_raw(self, new_screen: bool) -> Object { + Object( + ((new_screen as u32) << 31) + | ((self.id as u32 & 0x30) << 30) + | ((self.id as u32 & 0x0F) << 20) + | ((self.y & 0x1F) << 24) + | ((self.x & 0x0F) << 16) + | (self.settings as u32), + ) + } +} + +impl EditableExit { + pub fn from_raw(object: Object) -> Option { + object.is_exit().then(|| EditableExit { + screen: object.screen_number(), + midway: object.is_midway(), + secondary: object.is_secondary_exit(), + id: object.exit_id(), + }) + } + + pub fn to_raw(self) -> Object { + Object( + ((self.screen as u32 & 0x1F) << 24) + | ((self.midway as u32) << 19) + | ((self.secondary as u32) << 17) + | (self.id as u32 & 0x01FF), + ) + } +} + +impl EditableObjectLayer { + pub fn parse_from_ram(cpu: &mut Cpu, is_vertical_level: bool) -> Option { + let raw_objects = Object::parse_from_ram(&cpu.mem.extram)?; + let mut layer = Self::default(); + let mut current_screen = 0; + + for raw_object in raw_objects.into_iter() { + if raw_object.is_exit() { + layer.exits.push(EditableExit::from_raw(raw_object)?); + } else if raw_object.is_screen_jump() { + current_screen = raw_object.screen_number(); + } else { + if raw_object.is_new_screen() { + current_screen += 1; + } + let object = EditableObject::from_raw(raw_object, current_screen as u32, is_vertical_level)?; + layer.objects.push(object); + } + } + + Some(layer) + } + + pub fn write_to_extram(&mut self, cpu: &mut Cpu, is_vertical_level: bool) { + let mut current_offset = 5; // Start after primary header + + // Exits + for exit in self.exits.iter() { + Self::store_object_at(cpu, exit.to_raw(), &mut current_offset, 4); + } + + if is_vertical_level { + self.objects.sort_by(|a, b| a.y.cmp(&b.y)); + } else { + self.objects.sort_by(|a, b| a.x.cmp(&b.x)); + } + + let mut current_screen = 0; + for object in self.objects.iter() { + let screen_number = if is_vertical_level { object.y } else { object.x } / SCREEN_WIDTH; + debug_assert!(screen_number >= current_screen, "screen numbers must be in increasing order"); + debug_assert!(screen_number < 0x20, "exceeded maximum number of screens"); + + let screen_difference = screen_number - current_screen; + current_screen = screen_number; + + let new_screen = match (screen_difference > 0, screen_difference > 1) { + (true, true) => { + // Screen jump + let jump = Object(((screen_number & 0x1F) << 16) | 0x100); + Self::store_object_at(cpu, jump, &mut current_offset, 3); + false + } + (new_screen, _) => new_screen, + }; + + // Standard/extended object + Self::store_object_at(cpu, object.to_raw(new_screen), &mut current_offset, 3); + } + } + + fn store_object_at(cpu: &mut Cpu, object: Object, current_offset: &mut usize, length: usize) { + debug_assert!(length > 0); + debug_assert!(length <= 4); + for byte_index in 0..length { + cpu.mem.extram[*current_offset] = (object.0 >> ((3 - byte_index) * 8)) as u8; + *current_offset += 1; + } + } +} diff --git a/src/ui/editor_prototypes/level_editor/properties.rs b/src/ui/editor_prototypes/level_editor/properties.rs new file mode 100644 index 0000000..ba43026 --- /dev/null +++ b/src/ui/editor_prototypes/level_editor/properties.rs @@ -0,0 +1,99 @@ +#![allow(dead_code)] + +use smwe_emu::Cpu; +use smwe_rom::level::PrimaryHeader; + +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] +pub(super) struct LevelProperties { + // Primary header + pub palette_bg: u8, + pub level_length: u8, + pub back_area_color: u8, + pub level_mode: u8, + pub layer3_priority: bool, + pub music: u8, + pub sprite_gfx: u8, + pub timer: u8, + pub palette_sprite: u8, + pub palette_fg: u8, + pub item_memory: u8, + pub vertical_scroll: u8, + pub fg_bg_gfx: u8, + + // Other + pub is_vertical: bool, + pub has_layer2: bool, +} + +impl LevelProperties { + pub fn parse_from_ram(cpu: &mut Cpu) -> Self { + let raw_header = PrimaryHeader::new(&cpu.mem.extram[..5]); + let is_vertical = cpu.mem.load_u8(0x5B) & 1 != 0; + let has_layer2 = { + let mode = cpu.mem.load_u8(0x1925); + let renderer_table = cpu.mem.cart.resolve("CODE_058955").unwrap() + 9; + let renderer = cpu.mem.load_u24(renderer_table + (mode as u32) * 3); + let l2_renderers = [cpu.mem.cart.resolve("CODE_058B8D"), cpu.mem.cart.resolve("CODE_058C71")]; + l2_renderers.contains(&Some(renderer)) + }; + Self { + palette_bg: raw_header.palette_bg(), + level_length: raw_header.level_length(), + back_area_color: raw_header.back_area_color(), + level_mode: raw_header.level_mode(), + layer3_priority: raw_header.layer3_priority(), + music: raw_header.music(), + sprite_gfx: raw_header.sprite_gfx(), + timer: raw_header.timer(), + palette_sprite: raw_header.palette_sprite(), + palette_fg: raw_header.palette_fg(), + item_memory: raw_header.item_memory(), + vertical_scroll: raw_header.vertical_scroll(), + fg_bg_gfx: raw_header.fg_bg_gfx(), + is_vertical, + has_layer2, + } + } + + pub fn write_to_ram(&self, cpu: &mut Cpu) { + // Primary header + cpu.mem.extram[0] = ((self.palette_bg & 0x07) << 5) | (self.level_length & 0x1F); + cpu.mem.extram[1] = ((self.back_area_color & 0x07) << 5) | (self.level_mode & 0x1F); + cpu.mem.extram[2] = ((self.layer3_priority as u8) << 7) | ((self.music & 0x07) << 4) | (self.sprite_gfx & 0x0F); + cpu.mem.extram[3] = ((self.timer & 0x02) << 6) | ((self.palette_sprite & 0x07) << 3) | (self.palette_fg & 0x07); + cpu.mem.extram[4] = + ((self.item_memory & 0x02) << 6) | ((self.vertical_scroll & 0x02) << 4) | (self.fg_bg_gfx & 0x0F); + + // Other + let b = cpu.mem.load_u8(0x5B); + cpu.mem.store_u8(0x5B, if self.is_vertical { b | 1 } else { b & !1 }); + } + + /// (width, height) + pub fn level_dimensions_in_tiles(&self) -> (u32, u32) { + let (screen_width, screen_height) = self.screen_dimensions_in_tiles(); + if self.is_vertical { + (screen_width, screen_height * self.num_screens()) + } else { + (screen_width * self.num_screens(), screen_height) + } + } + + /// (width, height) + pub fn screen_dimensions_in_tiles(&self) -> (u32, u32) { + if self.is_vertical { + (32, 16) + } else { + (16, 27) + } + } + + pub fn num_screens(&self) -> u32 { + match (self.is_vertical, self.has_layer2) { + (false, false) => 0x20, + (true, false) => 0x1C, + (false, true) => 0x10, + (true, true) => 0x0E, + } + } +} diff --git a/src/ui/editor_prototypes/mod.rs b/src/ui/editor_prototypes/mod.rs index 4c84aad..c365ca3 100644 --- a/src/ui/editor_prototypes/mod.rs +++ b/src/ui/editor_prototypes/mod.rs @@ -1,2 +1,3 @@ pub mod block_editor; -pub mod code_editor; +pub mod level_editor; +pub mod sprite_map_editor; diff --git a/src/ui/editor_prototypes/sprite_map_editor/central_panel.rs b/src/ui/editor_prototypes/sprite_map_editor/central_panel.rs new file mode 100644 index 0000000..6077d80 --- /dev/null +++ b/src/ui/editor_prototypes/sprite_map_editor/central_panel.rs @@ -0,0 +1,251 @@ +use std::sync::Arc; + +use duplicate::duplicate; +use egui::*; +use egui_glow::CallbackFn; +use egui_phosphor::regular as icons; +use inline_tweak::tweak; +use smwe_math::coordinates::*; +use smwe_render::tile_renderer::TileUniforms; +use smwe_widgets::value_switcher::{ValueSwitcher, ValueSwitcherButtons}; + +use super::{keyboard_shortcuts::*, UiSpriteMapEditor}; +use crate::ui::editing_mode::*; + +impl UiSpriteMapEditor { + pub(super) fn central_panel(&mut self, ui: &mut Ui) { + Frame::menu(ui.style()).show(ui, |ui| { + ui.horizontal(|ui| { + self.editor_toolbar_menu(ui); + }); + }); + ui.add_space(ui.spacing().item_spacing.y); + self.editing_area(ui); + } + + pub(super) fn editor_toolbar_menu(&mut self, ui: &mut Ui) { + let level_switcher = ValueSwitcher::new(&mut self.level_num, "Level", ValueSwitcherButtons::LeftRight) + .range(0..=0x1FF) + .hexadecimal(3, false, true); + if ui.add(level_switcher).changed() { + self.update_cpu(); + self.update_renderers(); + } + ui.separator(); + + self.editing_mode_selector(ui); + ui.separator(); + + ui.horizontal(|ui| { + let zoom_slider = Slider::new(&mut self.zoom, Self::MIN_ZOOM..=Self::MAX_ZOOM).step_by(0.25).suffix("x"); + ui.label(icons::MAGNIFYING_GLASS_PLUS); + ui.add(zoom_slider); + }); + + ui.add_space(ui.available_width()); + } + + pub(super) fn editing_mode_selector(&mut self, ui: &mut Ui) { + ui.horizontal(|ui| { + duplicate! { + [ + icon mode_name shortcut mode_desc mode_pattern mode_value; + + [icons::CURSOR] + ["Insertion tool"] + [SHORTCUT_MODE_INSERT] + ["Right-click to insert tile, single click to select, drag to move."] + [EditingMode::Move(_)] + [EditingMode::Move(None)]; + + [icons::RECTANGLE] + ["Rectangular selection"] + [SHORTCUT_MODE_SELECT] + ["Left-click and drag to select tiles."] + [EditingMode::Select] + [EditingMode::Select]; + + [icons::ERASER] + ["Eraser"] + [SHORTCUT_MODE_ERASE] + ["Delete tiles while left mouse button is pressed."] + [EditingMode::Erase] + [EditingMode::Erase]; + + [icons::EYEDROPPER] + ["Probe"] + [SHORTCUT_MODE_PROBE] + ["Pick a tile from the canvas on left-click."] + [EditingMode::Probe] + [EditingMode::Probe]; + + [icons::ARROWS_OUT_LINE_HORIZONTAL] + ["Horizontal flip"] + [SHORTCUT_MODE_FLIP_HORIZONTALLY] + ["Horizontally mirror selection or individual tile on left-click. Hold Ctrl to temporarily switch to vertical flip."] + [EditingMode::FlipHorizontally] + [EditingMode::FlipHorizontally]; + + [icons::ARROWS_OUT_LINE_VERTICAL] + ["Vertical flip"] + [SHORTCUT_MODE_FLIP_VERTICALLY] + ["Vertical mirror selection or individual tile on left-click. Hold Ctrl to temporarily switch to horizontal flip."] + [EditingMode::FlipVertically] + [EditingMode::FlipVertically]; + ] + { + let button = if matches!(self.editing_mode, mode_pattern) { + Button::new(icon).fill(Color32::from_rgb(tweak!(200), tweak!(30), tweak!(70))) + } else { + Button::new(icon) + }; + + let tooltip = |ui: &mut Ui| { + ui.horizontal(|ui| { + ui.strong(mode_name); + ui.weak(ui.ctx().format_shortcut(&shortcut)); + }); + ui.label(mode_desc); + }; + + if ui.add(button).on_hover_ui_at_pointer(tooltip).clicked() { + self.editing_mode = mode_value; + } + } + } + }); + } + + pub(super) fn editing_area(&mut self, ui: &mut Ui) { + ScrollArea::both() + .drag_to_scroll(false) + .min_scrolled_height(ui.available_height()) + .min_scrolled_width(ui.available_width()) + .show(ui, |ui| { + let editing_area_rect = ui.available_rect_before_wrap(); + let canvas_rect = Rect::from_center_size(editing_area_rect.center(), self.canvas_size().0); + let max_rect = editing_area_rect.union(canvas_rect); + let margin = Margin { + left: canvas_rect.min.x - max_rect.min.x, + right: max_rect.max.x - canvas_rect.max.x, + top: canvas_rect.min.y - max_rect.min.y, + bottom: max_rect.max.y - canvas_rect.max.y, + }; + Frame::canvas(ui.style()).inner_margin(Margin::same(0.)).outer_margin(margin).show(ui, |ui| { + self.canvas(ui); + }); + }); + } + + pub(super) fn canvas(&mut self, ui: &mut Ui) { + let (canvas_rect, response) = ui.allocate_exact_size(self.canvas_size().0, Sense::click_and_drag()); + + // Tiles + ui.painter().add(PaintCallback { + rect: canvas_rect, + callback: { + let sprite_renderer = Arc::clone(&self.sprite_renderer); + let gfx_bufs = self.gfx_bufs; + let screen_size = canvas_rect.size() * self.pixels_per_point; + let zoom = self.zoom; + Arc::new(CallbackFn::new(move |_info, painter| { + sprite_renderer + .lock() + .expect("Cannot lock mutex on sprite renderer") + .paint(painter.gl(), &TileUniforms { gfx_bufs, screen_size, offset: Vec2::ZERO, zoom }); + })) + }, + }); + + // Grid + if self.always_show_grid || ui.input(|i| i.modifiers.shift_only()) { + let spacing = self.zoom * self.tile_size_px / self.pixels_per_point; + let stroke = Stroke::new(1., Color32::from_white_alpha(tweak!(70))); + for cell in 0..33 { + let position = cell as f32 * spacing; + ui.painter().hline(canvas_rect.min.x..=canvas_rect.max.x, canvas_rect.min.y + position, stroke); + ui.painter().vline(canvas_rect.min.x + position, canvas_rect.min.y..=canvas_rect.max.y, stroke); + } + } + + // DEBUG: show selection bounds + #[cfg(debug_assertions)] + if self.debug_selection_bounds { + if let Some(mut bounds) = self.selection_bounds { + bounds.0.max += Vec2::splat(self.tile_size_px); + ui.painter().rect_stroke( + bounds.to_screen(self.pixels_per_point, self.zoom).0.translate(canvas_rect.left_top().to_vec2()), + Rounding::ZERO, + Stroke::new(2., Color32::BLUE), + ); + } + } + + // Interaction + if let Some(hover_pos) = response.hover_pos() { + let canvas_top_left_pos = OnScreen(canvas_rect.left_top()); + + let relative_pointer_offset = OnScreen(hover_pos - canvas_rect.left_top()); + let relative_pointer_pos = relative_pointer_offset.to_pos2(); + + let grid_cell_pos = relative_pointer_offset + .to_grid(self.pixels_per_point, self.zoom, self.tile_size_px) + .clamp(OnGrid::::ZERO, self.grid_size) + .to_canvas(self.tile_size_px) + .to_pos2(); + + let (holding_shift_only, holding_ctrl_only) = + ui.input(|input| (input.modifiers.shift_only(), input.modifiers.command_only())); + + let mut should_highlight_hovered = true; + + // Keyboard shortcuts + ui.input(|input| self.kb_shortcut_paste(input, canvas_top_left_pos)); + + // Editing tools + if self.editing_mode.inserted(&response) { + self.handle_edition_insert(grid_cell_pos); + } + + if let Some(selection) = self.editing_mode.selected(&response) { + if let Selection::Drag(Some(selection_rect)) = selection { + ui.painter().rect_stroke( + selection_rect.0, + Rounding::ZERO, + Stroke::new(1., ui.visuals().selection.bg_fill), + ); + } + self.handle_selection_plot(selection, !holding_ctrl_only, canvas_top_left_pos); + } + + if let Some(drag_data) = self.editing_mode.dropped(&response) { + self.handle_edition_drop(drag_data, holding_shift_only, canvas_top_left_pos); + } + + if let Some(drag_data) = self.editing_mode.moving(&response) { + self.handle_edition_dragging(drag_data, holding_shift_only, canvas_top_left_pos); + should_highlight_hovered = false; + } + + if self.editing_mode.erased(&response) { + self.handle_edition_erase(relative_pointer_pos); + } + + if self.editing_mode.probed(&response) { + self.handle_edition_probe(relative_pointer_pos); + } + + if let Some(flip_direction) = self.editing_mode.flipped(&response) { + self.handle_edition_flip(relative_pointer_pos, flip_direction); + } + + // Highlighting + if should_highlight_hovered { + self.higlight_hovered_tiles(ui, relative_pointer_pos, OnScreen(canvas_rect.left_top())); + } + } + + self.highlight_selected_tiles(ui, OnScreen(canvas_rect.left_top())); + self.hovering_selected_tile = false; + } +} diff --git a/src/ui/editor_prototypes/sprite_map_editor/highlighting.rs b/src/ui/editor_prototypes/sprite_map_editor/highlighting.rs new file mode 100644 index 0000000..6478551 --- /dev/null +++ b/src/ui/editor_prototypes/sprite_map_editor/highlighting.rs @@ -0,0 +1,85 @@ +use egui::*; +use smwe_math::coordinates::*; +use smwe_widgets::vram_view::VramSelectionMode; + +use super::UiSpriteMapEditor; +use crate::ui::{ + editing_mode::*, + style::{CellSelectorStyle, EditorStyle}, +}; + +impl UiSpriteMapEditor { + pub(super) fn higlight_hovered_tiles( + &mut self, ui: &Ui, relative_pointer_pos: OnScreen, canvas_left_top: OnScreen, + ) { + let pointer_pos_canvas = relative_pointer_pos.to_canvas(self.pixels_per_point, self.zoom); + match self.editing_mode { + EditingMode::Move(_) | EditingMode::FlipHorizontally | EditingMode::FlipVertically => { + if self.any_selected_tile_contains_point(pointer_pos_canvas) { + self.hovering_selected_tile = true; + } else if let Some((_, hovered_tile)) = self.find_tile_containing_point(pointer_pos_canvas) { + let tile_pos_in_canvas = hovered_tile.pos().to_screen(self.pixels_per_point, self.zoom); + let exact_tile_pos = canvas_left_top + tile_pos_in_canvas.to_vec2(); + self.highlight_tile_at( + ui, + exact_tile_pos, + CellSelectorStyle::get_from_egui(ui.ctx(), |style| style.hovered_tile_highlight_color), + 1., + ); + } else if matches!(self.editing_mode, EditingMode::Move(_)) { + let (selection_scale, max_selected_tile) = match self.vram_selection_mode { + VramSelectionMode::SingleTile => (1., self.grid_size), + VramSelectionMode::TwoByTwoTiles => (2., self.grid_size - OnGrid::splat(1.)), + }; + let tile_pos_in_canvas = relative_pointer_pos + .to_grid(self.pixels_per_point, self.zoom, self.tile_size_px) + .clamp(OnGrid::::ZERO, max_selected_tile.to_pos2()) + .to_screen(self.pixels_per_point, self.zoom, self.tile_size_px); + let exact_tile_pos = canvas_left_top + tile_pos_in_canvas.to_vec2(); + self.highlight_tile_at( + ui, + exact_tile_pos, + CellSelectorStyle::get_from_egui(ui.ctx(), |style| style.hovered_void_highlight_color), + selection_scale, + ); + } + } + EditingMode::Erase => { + if let Some((_, hovered_tile)) = self.find_tile_containing_point(pointer_pos_canvas) { + let tile_pos_in_canvas = hovered_tile.pos().to_screen(self.pixels_per_point, self.zoom); + let exact_tile_pos = canvas_left_top + tile_pos_in_canvas.to_vec2(); + self.highlight_tile_at( + ui, + exact_tile_pos, + CellSelectorStyle::get_from_egui(ui.ctx(), |style| style.delete_highlight_color), + 1., + ); + } + } + _ => {} + } + } + + pub(super) fn highlight_tile_at(&self, ui: &Ui, point: OnScreen, color: impl Into, scale: f32) { + let size = OnCanvas::splat(self.tile_size_px * scale).to_screen(self.pixels_per_point, self.zoom); + ui.painter().rect_filled(OnScreen::from_min_size(point, size).0, Rounding::ZERO, color); + } + + pub(super) fn highlight_selected_tiles(&mut self, ui: &Ui, canvas_pos: OnScreen) { + let selection_offset = self.selection_offset.take().unwrap_or_default(); + for tile in self.selected_sprite_tile_indices.iter().map(|&idx| self.sprite_tiles.read(|tiles| tiles[idx])) { + self.highlight_tile_at( + ui, + canvas_pos + selection_offset + tile.pos().to_screen(self.pixels_per_point, self.zoom).to_vec2(), + CellSelectorStyle::get_from_egui(ui.ctx(), |style| { + if self.hovering_selected_tile { + style.hovered_tile_highlight_color + } else { + style.selection_highlight_color + } + }), + 1., + ); + } + } +} diff --git a/src/ui/editor_prototypes/sprite_map_editor/internals/canvas.rs b/src/ui/editor_prototypes/sprite_map_editor/internals/canvas.rs new file mode 100644 index 0000000..9c72b4e --- /dev/null +++ b/src/ui/editor_prototypes/sprite_map_editor/internals/canvas.rs @@ -0,0 +1,178 @@ +use egui::{PlatformOutput, Pos2, Rangef, Rect, Vec2}; +use itertools::Itertools; +use num::Integer; +use smwe_math::coordinates::{OnCanvas, OnGrid, OnScreen}; +use smwe_render::tile_renderer::{Tile, TileJson}; + +use super::super::UiSpriteMapEditor; +use crate::ui::editing_mode::{FlipDirection, SnapToGrid}; + +impl UiSpriteMapEditor { + pub(in super::super) fn canvas_size(&self) -> OnScreen { + OnGrid::splat(32.).to_screen(self.pixels_per_point, self.zoom, self.tile_size_px) + } + + pub(in super::super) fn any_selected_tile_contains_point(&self, point: OnCanvas) -> bool { + self.sprite_tiles + .read(|tiles| self.selected_sprite_tile_indices.iter().copied().any(|i| tiles[i].contains_point(point))) + } + + pub(in super::super) fn find_tile_containing_point(&self, point: OnCanvas) -> Option<(usize, Tile)> { + self.sprite_tiles.read(|tiles| tiles.iter().copied().enumerate().find(|(_, tile)| tile.contains_point(point))) + } + + pub(in super::super) fn select_tile_at(&mut self, pos: OnScreen, clear_previous_selection: bool) { + if clear_previous_selection { + self.unselect_all_tiles(); + } + + if let Some((idx, _)) = self.find_tile_containing_point(pos.to_canvas(self.pixels_per_point, self.zoom)) { + self.selected_sprite_tile_indices.insert(idx); + } + + self.compute_selection_bounds(); + } + + pub(in super::super) fn select_tiles_inside(&mut self, rect: OnScreen, clear_previous_selection: bool) { + if clear_previous_selection { + self.unselect_all_tiles(); + } + + let indices = self.sprite_tiles.read(|tiles| { + tiles + .iter() + .enumerate() + .filter_map(|(index, &tile)| { + tile.intersects_rect(rect.to_canvas(self.pixels_per_point, self.zoom)).then_some(index) + }) + .collect_vec() + }); + self.mark_tiles_as_selected(indices); + } + + pub(in super::super) fn move_selected_tiles_by( + &mut self, move_offset: OnCanvas, snap_to_grid: Option, + ) { + if self.selected_sprite_tile_indices.is_empty() { + return; + } + + let bounds = self.selection_bounds.expect("unset even though some tiles are selected"); + let move_offset = move_offset.clamp( + -bounds.left_top().to_vec2(), + OnCanvas::splat(31. * self.tile_size_px) - bounds.right_bottom().to_vec2(), + ); + + self.sprite_tiles.write(|tiles| { + for &idx in self.selected_sprite_tile_indices.iter() { + tiles[idx].move_by(move_offset); + if let Some(snap_to_grid) = snap_to_grid { + tiles[idx].snap_to_grid(self.tile_size_px as u32, snap_to_grid.cell_origin); + } + } + }); + + self.compute_selection_bounds(); + self.upload_tiles(); + } + + pub(in super::super) fn add_selected_tile_at(&mut self, pos: OnCanvas) { + let tile_idx = (self.selected_vram_tile.0 + self.selected_vram_tile.1 * 16) as usize; + let tile = self.tile_palette[tile_idx + (32 * 16)]; + self.add_tile_at(tile, pos); + } + + pub(in super::super) fn add_tile_at(&mut self, mut tile: Tile, pos: OnCanvas) { + tile.move_to(pos.floor()); + self.add_tiles([tile]); + } + + pub(in super::super) fn add_tiles(&mut self, new_tiles: impl IntoIterator) { + self.sprite_tiles.write(|tiles| { + self.selected_sprite_tile_indices.insert(tiles.len()); + tiles.extend(new_tiles); + }); + } + + pub(in super::super) fn copy_selected_tiles(&self, platform_output: &mut PlatformOutput) { + let selected_tiles = self + .selected_sprite_tile_indices + .iter() + .map(|&i| self.sprite_tiles.read(|tiles| tiles[i])) + .map(|mut t| { + let bounds = self.selection_bounds.expect("No selection bounds even though tiles are selected"); + t.move_by(-bounds.left_top().to_vec2()); + t + }) + .map(TileJson::from) + .collect_vec(); + platform_output.copied_text = + serde_json::to_string(&selected_tiles).expect("Failed to serialize selected tiles"); + } + + pub(in super::super) fn paste_tiles_to(&mut self, tiles: Vec, paste_offset: OnCanvas) { + self.unselect_all_tiles(); + + let tiles = tiles + .into_iter() + .map(Tile::from) + .map(|mut tile| { + tile.move_by(paste_offset); + tile + }) + .collect_vec(); + self.add_tiles(tiles); + + self.compute_selection_bounds(); + self.upload_tiles(); + } + + pub(in super::super) fn delete_selected_tiles(&mut self) { + self.sprite_tiles.write(|tiles| { + for idx in self.selected_sprite_tile_indices.drain().sorted().rev() { + tiles.remove(idx); + } + }); + self.selection_bounds = None; + self.upload_tiles(); + } + + pub(in super::super) fn delete_tiles_at(&mut self, pos: OnScreen) { + self.sprite_tiles + .write(|tiles| tiles.retain(|&tile| !tile.contains_point(pos.to_canvas(self.pixels_per_point, self.zoom)))); + self.upload_tiles(); + } + + pub(in super::super) fn probe_tile_at(&mut self, pos: OnScreen) { + self.sprite_tiles.read(|tiles| { + if let Some(tile) = + tiles.iter().rev().find(|&&tile| tile.contains_point(pos.to_canvas(self.pixels_per_point, self.zoom))) + { + let (y, x) = tile.tile_num().div_rem(&16); + self.selected_vram_tile = (x, y - 96); + } + }); + } + + pub(in super::super) fn flip_selected_tiles(&mut self, flip_direction: FlipDirection) { + let selection_bounds = self.selection_bounds.expect("unset even though some tiles are selected"); + let Rangef { min: x_min, max: x_max } = selection_bounds.x_range(); + let Rangef { min: y_min, max: y_max } = selection_bounds.y_range(); + self.sprite_tiles.write(|tiles| { + for &i in self.selected_sprite_tile_indices.iter() { + let tile = &mut tiles[i]; + match flip_direction { + FlipDirection::Horizontal => { + tile.toggle_flip_x(); + tile[0] = (x_min + (x_max - tile[0] as f32)) as u32; + } + FlipDirection::Vertical => { + tile.toggle_flip_y(); + tile[1] = (y_min + (y_max - tile[1] as f32)) as u32; + } + } + } + }); + self.upload_tiles(); + } +} diff --git a/src/ui/editor_prototypes/sprite_map_editor/internals/editing.rs b/src/ui/editor_prototypes/sprite_map_editor/internals/editing.rs new file mode 100644 index 0000000..1d4760e --- /dev/null +++ b/src/ui/editor_prototypes/sprite_map_editor/internals/editing.rs @@ -0,0 +1,167 @@ +use egui::{PlatformOutput, Pos2, Vec2}; +use smwe_math::coordinates::*; +use smwe_widgets::vram_view::VramSelectionMode; + +use super::super::UiSpriteMapEditor; +use crate::ui::editing_mode::{Drag, FlipDirection, Selection, SnapToGrid}; + +impl UiSpriteMapEditor { + pub(in super::super) fn handle_undo(&mut self) { + self.sprite_tiles.undo(); + self.selected_sprite_tile_indices.clear(); + self.compute_selection_bounds(); + self.upload_tiles(); + } + + pub(in super::super) fn handle_redo(&mut self) { + self.sprite_tiles.redo(); + self.selected_sprite_tile_indices.clear(); + self.compute_selection_bounds(); + self.upload_tiles(); + } + + pub(in super::super) fn handle_copy(&mut self, output: &mut PlatformOutput) { + self.copy_selected_tiles(output); + } + + pub(in super::super) fn handle_cut(&mut self, output: &mut PlatformOutput) { + self.handle_copy(output); + self.delete_selected_tiles(); + } + + pub(in super::super) fn handle_edition_insert(&mut self, grid_cell_pos: OnCanvas) { + self.unselect_all_tiles(); + match self.vram_selection_mode { + VramSelectionMode::SingleTile => self.add_selected_tile_at(grid_cell_pos), + VramSelectionMode::TwoByTwoTiles => { + let current_selection = self.selected_vram_tile; + for offset in [(0, 0), (0, 1), (1, 0), (1, 1)] { + self.selected_vram_tile.0 = current_selection.0 + offset.0; + self.selected_vram_tile.1 = current_selection.1 + offset.1; + let offset = OnGrid::::new(offset.0 as f32, offset.1 as f32).to_canvas(self.tile_size_px); + let pos = OnCanvas(grid_cell_pos.0 + offset.0); + self.add_selected_tile_at(pos); + } + self.selected_vram_tile = current_selection; + } + } + self.compute_selection_bounds(); + self.upload_tiles(); + } + + pub(in super::super) fn handle_selection_plot( + &mut self, selection: Selection, clear_previous_selection: bool, canvas_top_left_pos: OnScreen, + ) { + match selection { + Selection::Click(Some(origin)) => { + let pos = origin.0 - canvas_top_left_pos.0; + self.select_tile_at(OnScreen(pos.to_pos2()), clear_previous_selection); + } + Selection::Drag(Some(selection_rect)) => { + self.select_tiles_inside( + OnScreen(selection_rect.0.translate(-canvas_top_left_pos.0.to_vec2())), + clear_previous_selection, + ); + } + _ => {} + } + } + + pub(in super::super) fn handle_edition_drop( + &mut self, drag_data: Drag, snap_to_grid: bool, canvas_top_left_pos: OnScreen, + ) { + let pointer_pos = drag_data.from.relative_to(canvas_top_left_pos).to_canvas(self.pixels_per_point, self.zoom); + if !self.any_selected_tile_contains_point(pointer_pos) { + return; + } + + self.move_selected_tiles_by( + drag_data.delta().to_canvas(self.pixels_per_point, self.zoom), + snap_to_grid.then(|| { + let pointer_in_canvas = drag_data.from.relative_to(canvas_top_left_pos); + let hovered_tile_exact_offset = pointer_in_canvas + .to_grid(self.pixels_per_point, self.zoom, self.tile_size_px) + .clamp(OnGrid::::ZERO, self.grid_size.to_pos2()) + .to_screen(self.pixels_per_point, self.zoom, self.tile_size_px); + let cell_origin = pointer_in_canvas.relative_to(hovered_tile_exact_offset).to_vec2() / self.zoom; + SnapToGrid { cell_origin } + }), + ); + } + + pub(in super::super) fn handle_edition_dragging( + &mut self, mut drag_data: Drag, snap_to_grid: bool, canvas_top_left_pos: OnScreen, + ) { + let pointer_pos = drag_data.from.relative_to(canvas_top_left_pos).to_canvas(self.pixels_per_point, self.zoom); + if !self.any_selected_tile_contains_point(pointer_pos) { + return; + } + + if snap_to_grid { + let sel_bounds = self.selection_bounds.expect("unset even though some tiles are selected"); + + let bounds_min_grid = sel_bounds.left_top().to_grid(self.tile_size_px); + let started_tile = drag_data.from.relative_to(canvas_top_left_pos).to_grid( + self.pixels_per_point, + self.zoom, + self.tile_size_px, + ); + let hovered_tile = drag_data.to.relative_to(canvas_top_left_pos).to_grid( + self.pixels_per_point, + self.zoom, + self.tile_size_px, + ); + + let bounds_at_grid_exact_offset = + bounds_min_grid.to_screen(self.pixels_per_point, self.zoom, self.tile_size_px).to_vec2(); + let started_tile_exact_offset = + started_tile.to_screen(self.pixels_per_point, self.zoom, self.tile_size_px).to_vec2(); + let hovered_tile_exact_offset = + hovered_tile.to_screen(self.pixels_per_point, self.zoom, self.tile_size_px).to_vec2(); + + let bounds_screen = sel_bounds.left_top().to_screen(self.pixels_per_point, self.zoom); + let bounds_offset = bounds_screen.to_vec2() - bounds_at_grid_exact_offset; + drag_data.from = canvas_top_left_pos + started_tile_exact_offset + bounds_offset; + drag_data.to = canvas_top_left_pos + hovered_tile_exact_offset; + } + + // todo restrict moving selection display to canvas + // move_offset.x = move_offset.x.clamp(-bounds.min.x, (31. * self.scale) - bounds.max.x); + // move_offset.y = move_offset.y.clamp(-bounds.min.y, (31. * self.scale) - bounds.max.y); + + self.selection_offset = Some(drag_data.delta()); + } + + pub(in super::super) fn handle_edition_erase(&mut self, relative_pointer_pos: OnScreen) { + self.delete_tiles_at(relative_pointer_pos); + self.unselect_all_tiles(); + } + + pub(in super::super) fn handle_edition_probe(&mut self, relative_pointer_pos: OnScreen) { + self.probe_tile_at(relative_pointer_pos); + self.unselect_all_tiles(); + } + + pub(in super::super) fn handle_edition_flip( + &mut self, relative_pointer_pos: OnScreen, flip_direction: FlipDirection, + ) { + let pointer_on_canvas = relative_pointer_pos.to_canvas(self.pixels_per_point, self.zoom); + if self.any_selected_tile_contains_point(pointer_on_canvas) { + self.flip_selected_tiles(flip_direction); + } else { + let flipped = self.sprite_tiles.write(|tiles| { + tiles + .iter_mut() + .find(|tile| tile.contains_point(pointer_on_canvas)) + .map(|tile| match flip_direction { + FlipDirection::Horizontal => tile.toggle_flip_x(), + FlipDirection::Vertical => tile.toggle_flip_y(), + }) + .is_some() + }); + if flipped { + self.upload_tiles(); + } + } + } +} diff --git a/src/ui/editor_prototypes/sprite_map_editor/internals/file.rs b/src/ui/editor_prototypes/sprite_map_editor/internals/file.rs new file mode 100644 index 0000000..8ab7348 --- /dev/null +++ b/src/ui/editor_prototypes/sprite_map_editor/internals/file.rs @@ -0,0 +1,81 @@ +use std::path::PathBuf; + +use itertools::Itertools; +use rfd::{MessageButtons, MessageDialog, MessageLevel}; +use smwe_render::tile_renderer::{Tile, TileJson}; + +use super::super::{SpriteTiles, UiSpriteMapEditor}; + +impl UiSpriteMapEditor { + pub(in super::super) fn create_new_map(&mut self) { + self.sprite_tiles.write(|tiles| tiles.clear()); + self.upload_tiles(); + } + + pub(in super::super) fn open_map_dialog(&mut self) { + if let Some(path) = rfd::FileDialog::new().pick_file() { + self.open_map(path); + } + } + + pub(in super::super) fn save_map_dialog(&mut self) { + if let Some(path) = rfd::FileDialog::new().save_file() { + self.save_map(path); + } + } + + pub(in super::super) fn open_map(&mut self, path: PathBuf) { + match std::fs::read_to_string(path) { + Err(e) => { + MessageDialog::new() + .set_title("Failed to open selected file.") + .set_description(format!("{e:?}")) + .set_level(MessageLevel::Error) + .set_buttons(MessageButtons::Ok) + .show(); + } + Ok(s) => match serde_json::from_str::>(&s) { + Err(e) => { + MessageDialog::new() + .set_title("Failed to deserialize sprite tile map from JSON.") + .set_description(format!("{e:?}")) + .set_level(MessageLevel::Error) + .set_buttons(MessageButtons::Ok) + .show(); + } + Ok(loaded_tiles) => { + self.sprite_tiles.write(move |tiles| { + *tiles = SpriteTiles(loaded_tiles.into_iter().map(Tile::from).collect_vec()); + }); + self.sprite_tiles.clear_stack(); + self.selected_sprite_tile_indices.clear(); + self.upload_tiles(); + } + }, + } + } + + pub(in super::super) fn save_map(&mut self, path: PathBuf) { + let tiles = self.sprite_tiles.read(|tiles| tiles.iter().map(|&t| TileJson::from(t)).collect_vec()); + match serde_json::to_string_pretty(&tiles) { + Err(e) => { + MessageDialog::new() + .set_title("Failed to serialize sprite tile map into JSON.") + .set_description(format!("{e:?}")) + .set_level(MessageLevel::Error) + .set_buttons(MessageButtons::Ok) + .show(); + } + Ok(s) => { + if let Err(e) = std::fs::write(path, s) { + MessageDialog::new() + .set_title("Save sprite tile map to selected file.") + .set_description(format!("{e:?}")) + .set_level(MessageLevel::Error) + .set_buttons(MessageButtons::Ok) + .show(); + } + } + } + } +} diff --git a/src/ui/editor_prototypes/sprite_map_editor/internals/mod.rs b/src/ui/editor_prototypes/sprite_map_editor/internals/mod.rs new file mode 100644 index 0000000..3b7c226 --- /dev/null +++ b/src/ui/editor_prototypes/sprite_map_editor/internals/mod.rs @@ -0,0 +1,5 @@ +mod canvas; +mod editing; +mod file; +mod selection; +mod state; diff --git a/src/ui/editor_prototypes/sprite_map_editor/internals/selection.rs b/src/ui/editor_prototypes/sprite_map_editor/internals/selection.rs new file mode 100644 index 0000000..d6b7206 --- /dev/null +++ b/src/ui/editor_prototypes/sprite_map_editor/internals/selection.rs @@ -0,0 +1,48 @@ +use std::ops::Not; + +use duplicate::duplicate; +use egui::{pos2, Rect}; +use itertools::Itertools; +use paste::paste; +use smwe_math::coordinates::OnCanvas; + +use super::super::UiSpriteMapEditor; + +impl UiSpriteMapEditor { + pub(in super::super) fn select_all_tiles(&mut self) { + self.mark_tiles_as_selected(0..self.sprite_tiles.read(|tiles| tiles.len())); + } + + pub(in super::super) fn unselect_all_tiles(&mut self) { + self.selected_sprite_tile_indices.clear(); + self.selection_bounds = None; + } + + pub(in super::super) fn mark_tiles_as_selected(&mut self, indices: impl IntoIterator) { + for index in indices { + self.selected_sprite_tile_indices.insert(index); + } + self.compute_selection_bounds(); + } + + pub(in super::super) fn compute_selection_bounds(&mut self) { + self.selection_bounds = self.selected_sprite_tile_indices.is_empty().not().then(|| { + duplicate! { + [dimension; [x]; [y]] + paste! { + let ([], []) = self.sprite_tiles.read(|tiles| { + self + .selected_sprite_tile_indices + .iter() + .map(|&i| tiles[i].pos()) + .minmax_by(|a, b| a.dimension.total_cmp(&b.dimension)) + .into_option() + .map(|(min, max)| (min.dimension, max.dimension)) + .unwrap() + }); + } + } + OnCanvas(Rect::from_min_max(pos2(min_tile_x, min_tile_y), pos2(max_tile_x, max_tile_y))) + }); + } +} diff --git a/src/ui/editor_prototypes/sprite_map_editor/internals/state.rs b/src/ui/editor_prototypes/sprite_map_editor/internals/state.rs new file mode 100644 index 0000000..39931dc --- /dev/null +++ b/src/ui/editor_prototypes/sprite_map_editor/internals/state.rs @@ -0,0 +1,42 @@ +use egui::Context; + +use super::super::UiSpriteMapEditor; + +impl UiSpriteMapEditor { + pub(in super::super) fn reset_state(&mut self, ctx: &Context) { + if self.state_needs_reset { + self.update_cpu(); + self.update_renderers(); + self.pixels_per_point = ctx.pixels_per_point(); + self.state_needs_reset = false; + } + } + + pub(in super::super) fn update_cpu(&mut self) { + smwe_emu::emu::decompress_sublevel(&mut self.cpu, self.level_num); + println!("Updated CPU"); + } + + pub(in super::super) fn update_renderers(&mut self) { + self.gfx_bufs.upload_palette(&self.gl, &self.cpu.mem.cgram); + self.gfx_bufs.upload_vram(&self.gl, &self.cpu.mem.vram); + } + + pub(in super::super) fn upload_tiles(&self) { + self.sprite_renderer + .lock() + .expect("Cannot lock mutex on sprite renderer") + .set_tiles(&self.gl, self.sprite_tiles.read(|tiles| tiles.0.clone())); + } + + pub(in super::super) fn update_tile_palette(&mut self) { + for tile in self.tile_palette.iter_mut() { + tile[3] &= 0xC0FF; + tile[3] |= (self.selected_palette + 8) << 8; + } + self.vram_renderer + .lock() + .expect("Cannot lock mutex on VRAM renderer") + .set_tiles(&self.gl, self.tile_palette.clone()); + } +} diff --git a/src/ui/editor_prototypes/sprite_map_editor/keyboard_shortcuts/data.rs b/src/ui/editor_prototypes/sprite_map_editor/keyboard_shortcuts/data.rs new file mode 100644 index 0000000..5ea81e3 --- /dev/null +++ b/src/ui/editor_prototypes/sprite_map_editor/keyboard_shortcuts/data.rs @@ -0,0 +1,25 @@ +use egui::{Key::*, KeyboardShortcut as Shortcut, Modifiers}; + +pub(in super::super) const SHORTCUT_NEW: Shortcut = Shortcut::new(Modifiers::COMMAND, N); +pub(in super::super) const SHORTCUT_SAVE: Shortcut = Shortcut::new(Modifiers::COMMAND, S); +pub(in super::super) const SHORTCUT_OPEN: Shortcut = Shortcut::new(Modifiers::COMMAND, O); + +pub(in super::super) const SHORTCUT_UNDO: Shortcut = Shortcut::new(Modifiers::COMMAND, Z); +pub(in super::super) const SHORTCUT_REDO: Shortcut = Shortcut::new(Modifiers::COMMAND, Y); +pub(in super::super) const SHORTCUT_COPY: Shortcut = Shortcut::new(Modifiers::COMMAND, C); +pub(in super::super) const SHORTCUT_CUT: Shortcut = Shortcut::new(Modifiers::COMMAND, X); + +pub(in super::super) const SHORTCUT_SELECT_ALL: Shortcut = Shortcut::new(Modifiers::COMMAND, A); +pub(in super::super) const SHORTCUT_UNSELECT_ALL: Shortcut = Shortcut::new(Modifiers::NONE, Escape); + +pub(in super::super) const SHORTCUT_DELETE_SELECTED: Shortcut = Shortcut::new(Modifiers::NONE, Delete); + +pub(in super::super) const SHORTCUT_ZOOM_IN: Shortcut = Shortcut::new(Modifiers::COMMAND, Plus); +pub(in super::super) const SHORTCUT_ZOOM_OUT: Shortcut = Shortcut::new(Modifiers::COMMAND, Minus); + +pub(in super::super) const SHORTCUT_MODE_INSERT: Shortcut = Shortcut::new(Modifiers::NONE, Num1); +pub(in super::super) const SHORTCUT_MODE_SELECT: Shortcut = Shortcut::new(Modifiers::NONE, Num2); +pub(in super::super) const SHORTCUT_MODE_ERASE: Shortcut = Shortcut::new(Modifiers::NONE, Num3); +pub(in super::super) const SHORTCUT_MODE_PROBE: Shortcut = Shortcut::new(Modifiers::NONE, Num4); +pub(in super::super) const SHORTCUT_MODE_FLIP_HORIZONTALLY: Shortcut = Shortcut::new(Modifiers::NONE, Num5); +pub(in super::super) const SHORTCUT_MODE_FLIP_VERTICALLY: Shortcut = Shortcut::new(Modifiers::NONE, Num6); diff --git a/src/ui/editor_prototypes/sprite_map_editor/keyboard_shortcuts/mod.rs b/src/ui/editor_prototypes/sprite_map_editor/keyboard_shortcuts/mod.rs new file mode 100644 index 0000000..48e0078 --- /dev/null +++ b/src/ui/editor_prototypes/sprite_map_editor/keyboard_shortcuts/mod.rs @@ -0,0 +1,107 @@ +mod data; + +pub(super) use data::*; +use egui::{vec2, Event, InputState, Key, Pos2, Ui}; +use smwe_math::coordinates::{OnCanvas, OnScreen}; +use smwe_render::tile_renderer::TileJson; + +use super::UiSpriteMapEditor; +use crate::ui::editing_mode::{EditingMode, SnapToGrid}; + +impl UiSpriteMapEditor { + pub(super) fn handle_input(&mut self, ui: &Ui) { + ui.input_mut(|input| { + if input.consume_shortcut(&SHORTCUT_NEW) { + self.create_new_map(); + } + if input.consume_shortcut(&SHORTCUT_SAVE) { + self.save_map_dialog(); + } + if input.consume_shortcut(&SHORTCUT_OPEN) { + self.open_map_dialog(); + } + if input.consume_shortcut(&SHORTCUT_UNDO) { + self.handle_undo(); + } + if input.consume_shortcut(&SHORTCUT_REDO) { + self.handle_redo(); + } + if input.consume_shortcut(&SHORTCUT_SELECT_ALL) { + self.select_all_tiles(); + } + if input.consume_shortcut(&SHORTCUT_UNSELECT_ALL) { + self.unselect_all_tiles(); + } + if input.consume_shortcut(&SHORTCUT_DELETE_SELECTED) { + self.delete_selected_tiles(); + } + self.kb_shortcut_move_selection(input); + self.kb_shortcuts_tools(input); + self.handle_zoom(input); + }); + + if ui.input(|input| input.events.contains(&Event::Copy)) { + ui.output_mut(|output| self.handle_copy(output)); + } + if ui.input(|input| input.events.contains(&Event::Cut)) { + ui.output_mut(|output| self.handle_cut(output)); + } + } + + pub(super) fn kb_shortcut_paste(&mut self, input: &InputState, canvas_top_left: OnScreen) { + for event in input.events.iter() { + if let Event::Paste(pasted_text) = event { + if let Ok(pasted_tiles) = serde_json::from_str::>(pasted_text) { + let hover_pos = OnScreen(input.pointer.hover_pos().expect("Failed to get hover position")); + let paste_offset = + hover_pos.relative_to(canvas_top_left).to_vec2().to_canvas(self.pixels_per_point, self.zoom); + self.paste_tiles_to(pasted_tiles, paste_offset); + } + break; + } + } + } + + fn kb_shortcut_move_selection(&mut self, input: &InputState) { + let move_distance = if input.modifiers.shift_only() { self.tile_size_px } else { 1. }; + let moves = [ + (Key::ArrowUp, vec2(0., -move_distance)), + (Key::ArrowDown, vec2(0., move_distance)), + (Key::ArrowLeft, vec2(-move_distance, 0.)), + (Key::ArrowRight, vec2(move_distance, 0.)), + ]; + for (key, offset) in moves { + if input.key_pressed(key) { + self.move_selected_tiles_by( + OnCanvas(offset), + input.modifiers.shift_only().then_some(SnapToGrid::default()), + ); + } + } + } + + fn kb_shortcuts_tools(&mut self, input: &mut InputState) { + let modes = [ + (&SHORTCUT_MODE_INSERT, EditingMode::Move(None)), + (&SHORTCUT_MODE_SELECT, EditingMode::Select), + (&SHORTCUT_MODE_ERASE, EditingMode::Erase), + (&SHORTCUT_MODE_PROBE, EditingMode::Probe), + (&SHORTCUT_MODE_FLIP_HORIZONTALLY, EditingMode::FlipHorizontally), + (&SHORTCUT_MODE_FLIP_VERTICALLY, EditingMode::FlipVertically), + ]; + for (shortcut, mode) in modes { + if input.consume_shortcut(shortcut) { + self.editing_mode = mode; + break; + } + } + } + + fn handle_zoom(&mut self, input: &mut InputState) { + if input.zoom_delta() > 1. || input.consume_shortcut(&SHORTCUT_ZOOM_IN) { + self.zoom = Self::MAX_ZOOM.min(self.zoom + 0.25); + } else if input.zoom_delta() < 1. || input.consume_shortcut(&SHORTCUT_ZOOM_OUT) { + self.zoom = Self::MIN_ZOOM.max(self.zoom - 0.25); + } + } +} diff --git a/src/ui/editor_prototypes/sprite_map_editor/left_panel.rs b/src/ui/editor_prototypes/sprite_map_editor/left_panel.rs new file mode 100644 index 0000000..c581916 --- /dev/null +++ b/src/ui/editor_prototypes/sprite_map_editor/left_panel.rs @@ -0,0 +1,107 @@ +use std::sync::Arc; + +use egui::*; +use egui_glow::CallbackFn; +use inline_tweak::tweak; +use smwe_math::coordinates::OnCanvas; +use smwe_render::tile_renderer::TileUniforms; +use smwe_widgets::{ + palette_view::{PaletteView, SelectionType, ViewedPalettes}, + vram_view::{ViewedVramTiles, VramSelectionMode, VramView}, +}; + +use super::UiSpriteMapEditor; + +impl UiSpriteMapEditor { + pub(super) fn left_panel(&mut self, ui: &mut Ui) { + ScrollArea::vertical().min_scrolled_height(ui.available_height()).show(ui, |ui| { + ui.add_space(ui.spacing().item_spacing.y); + ui.group(|ui| { + ui.allocate_space(vec2(ui.available_width(), 0.)); + self.tile_selector(ui); + ui.add_space(ui.spacing().item_spacing.y); + self.tile_selection_preview(ui); + }); + + ui.add_space(ui.spacing().item_spacing.y); + ui.group(|ui| { + ui.allocate_space(vec2(ui.available_width(), 0.)); + self.palette_row_selector(ui); + }); + + if cfg!(debug_assertions) { + ui.add_space(ui.spacing().item_spacing.y); + ui.group(|ui| { + ui.allocate_space(vec2(ui.available_width(), 0.)); + self.debug_toggles(ui); + }); + } + }); + } + + fn tile_selector(&mut self, ui: &mut Ui) { + ui.horizontal(|ui| { + ui.strong("VRAM"); + ui.radio_value(&mut self.vram_selection_mode, VramSelectionMode::SingleTile, "8x8"); + ui.radio_value(&mut self.vram_selection_mode, VramSelectionMode::TwoByTwoTiles, "16x16"); + }); + Frame::canvas(ui.style()).show(ui, |ui| { + let vram_renderer = Arc::clone(&self.vram_renderer); + let gfx_bufs = self.gfx_bufs; + ui.add( + VramView::new(vram_renderer, gfx_bufs) + .viewed_tiles(ViewedVramTiles::SpritesOnly) + .selection(self.vram_selection_mode, &mut self.selected_vram_tile) + .zoom(2.), + ); + }); + } + + fn tile_selection_preview(&mut self, ui: &mut Ui) { + let vram_renderer = Arc::clone(&self.vram_renderer); + let gfx_bufs = self.gfx_bufs; + + ui.strong("Selection preview"); + let px = self.pixels_per_point; + let preview_size = tweak!(8.); + let zoom = tweak!(8.); + let (rect, _response) = + ui.allocate_exact_size(OnCanvas::splat(preview_size).to_screen(px, zoom).0, Sense::hover()); + + let screen_size = match self.vram_selection_mode { + VramSelectionMode::SingleTile => rect.size() * px, + VramSelectionMode::TwoByTwoTiles => rect.size() * px * 2., + }; + let offset = vec2(-(self.selected_vram_tile.0 as f32), -32. - self.selected_vram_tile.1 as f32) * zoom; + + ui.painter().add(PaintCallback { + rect, + callback: Arc::new(CallbackFn::new(move |_info, painter| { + vram_renderer + .lock() + .expect("Cannot lock mutex on selected tile view's tile renderer") + .paint(painter.gl(), &TileUniforms { gfx_bufs, screen_size, offset, zoom }); + })), + }); + } + + fn palette_row_selector(&mut self, ui: &mut Ui) { + ui.strong("Palette"); + Frame::canvas(ui.style()).show(ui, |ui| { + let size = vec2(tweak!(230.), tweak!(115.)); + let palette_view = PaletteView::new(Arc::clone(&self.palette_renderer), self.gfx_bufs.palette_buf, size) + .viewed_rows(ViewedPalettes::SpritesOnly) + .selection(SelectionType::Row(&mut self.selected_palette)); + if ui.add(palette_view).changed() { + self.update_tile_palette(); + } + }); + } + + #[cfg(debug_assertions)] + fn debug_toggles(&mut self, ui: &mut Ui) { + ui.collapsing("Debug", |ui| { + ui.checkbox(&mut self.debug_selection_bounds, "Show selection bounds"); + }); + } +} diff --git a/src/ui/editor_prototypes/sprite_map_editor/menu_bar.rs b/src/ui/editor_prototypes/sprite_map_editor/menu_bar.rs new file mode 100644 index 0000000..dbb2590 --- /dev/null +++ b/src/ui/editor_prototypes/sprite_map_editor/menu_bar.rs @@ -0,0 +1,97 @@ +use egui::*; + +use super::{keyboard_shortcuts::*, UiSpriteMapEditor}; + +impl UiSpriteMapEditor { + pub(super) fn menu_bar(&mut self, ui: &mut Ui) { + ui.menu_button("File", |ui| self.menu_file(ui)); + ui.menu_button("Edit", |ui| self.menu_edit(ui)); + ui.menu_button("View", |ui| self.menu_view(ui)); + } + + fn menu_file(&mut self, ui: &mut Ui) { + if ui.add(Button::new("New").shortcut_text(ui.ctx().format_shortcut(&SHORTCUT_NEW))).clicked() { + self.create_new_map(); + ui.close_menu(); + } + if ui.add(Button::new("Save").shortcut_text(ui.ctx().format_shortcut(&SHORTCUT_SAVE))).clicked() { + self.save_map_dialog(); + ui.close_menu(); + } + if ui.add(Button::new("Open").shortcut_text(ui.ctx().format_shortcut(&SHORTCUT_OPEN))).clicked() { + self.open_map_dialog(); + ui.close_menu(); + } + } + + fn menu_edit(&mut self, ui: &mut Ui) { + if ui + .add_enabled( + self.sprite_tiles.can_undo(), + Button::new("Undo").shortcut_text(ui.ctx().format_shortcut(&SHORTCUT_UNDO)), + ) + .clicked() + { + self.handle_undo(); + ui.close_menu(); + } + if ui + .add_enabled( + self.sprite_tiles.can_redo(), + Button::new("Redo").shortcut_text(ui.ctx().format_shortcut(&SHORTCUT_REDO)), + ) + .clicked() + { + self.handle_redo(); + ui.close_menu(); + } + if ui.add(Button::new("Copy").shortcut_text(ui.ctx().format_shortcut(&SHORTCUT_COPY))).clicked() { + ui.output_mut(|output| self.handle_copy(output)); + ui.close_menu(); + } + if ui.add(Button::new("Cut").shortcut_text(ui.ctx().format_shortcut(&SHORTCUT_CUT))).clicked() { + ui.output_mut(|output| self.handle_cut(output)); + ui.close_menu(); + } + if ui.add(Button::new("Select all").shortcut_text(ui.ctx().format_shortcut(&SHORTCUT_SELECT_ALL))).clicked() { + self.select_all_tiles(); + ui.close_menu(); + } + if ui + .add_enabled( + !self.selected_sprite_tile_indices.is_empty(), + Button::new("Unselect all").shortcut_text(ui.ctx().format_shortcut(&SHORTCUT_UNSELECT_ALL)), + ) + .clicked() + { + self.unselect_all_tiles(); + ui.close_menu(); + } + } + + fn menu_view(&mut self, ui: &mut Ui) { + if ui + .add_enabled( + self.zoom < Self::MAX_ZOOM, + Button::new("Zoom +25%").shortcut_text(ui.ctx().format_shortcut(&SHORTCUT_ZOOM_IN)), + ) + .clicked() + { + self.zoom += 0.25; + ui.close_menu(); + } + if ui + .add_enabled( + self.zoom > Self::MIN_ZOOM, + Button::new("Zoom -25%").shortcut_text(ui.ctx().format_shortcut(&SHORTCUT_ZOOM_OUT)), + ) + .clicked() + { + self.zoom -= 0.25; + ui.close_menu(); + } + if ui.checkbox(&mut self.always_show_grid, "Always show grid").clicked() { + ui.close_menu(); + } + } +} diff --git a/src/ui/editor_prototypes/sprite_map_editor/mod.rs b/src/ui/editor_prototypes/sprite_map_editor/mod.rs new file mode 100644 index 0000000..c7a9a04 --- /dev/null +++ b/src/ui/editor_prototypes/sprite_map_editor/mod.rs @@ -0,0 +1,136 @@ +mod central_panel; +mod highlighting; +mod internals; +mod keyboard_shortcuts; +mod left_panel; +mod menu_bar; +mod sprite_tiles; + +use std::{ + collections::HashSet, + sync::{Arc, Mutex}, +}; + +use egui::*; +use glow::Context; +use smwe_emu::{emu::CheckedMem, rom::Rom, Cpu}; +use smwe_math::coordinates::*; +use smwe_render::{ + gfx_buffers::GfxBuffers, + palette_renderer::PaletteRenderer, + tile_renderer::{Tile, TileRenderer}, +}; +use smwe_widgets::vram_view::{VramSelectionMode, VramView}; +use sprite_tiles::SpriteTiles; + +use crate::{ + ui::{editing_mode::EditingMode, tool::DockableEditorTool}, + undo::UndoableData, +}; + +pub struct UiSpriteMapEditor { + gl: Arc, + cpu: Cpu, + tile_palette: Vec, + vram_renderer: Arc>, + sprite_renderer: Arc>, + palette_renderer: Arc>, + gfx_bufs: GfxBuffers, + state_needs_reset: bool, + + level_num: u16, + vram_selection_mode: VramSelectionMode, + editing_mode: EditingMode, + always_show_grid: bool, + + #[cfg(debug_assertions)] + debug_selection_bounds: bool, + + tile_size_px: f32, + zoom: f32, + pixels_per_point: f32, + grid_size: OnGrid, + hovering_selected_tile: bool, + selection_bounds: Option>, + selection_offset: Option>, + + selected_vram_tile: (u32, u32), + selected_palette: u32, + sprite_tiles: UndoableData, + selected_sprite_tile_indices: HashSet, +} + +impl UiSpriteMapEditor { + pub fn new(gl: Arc, rom: Arc) -> Self { + let (vram_renderer, tile_palette) = VramView::new_renderer(&gl); + let sprite_renderer = TileRenderer::new(&gl); + let palette_renderer = PaletteRenderer::new(&gl); + let gfx_bufs = GfxBuffers::new(&gl); + Self { + gl, + cpu: Cpu::new(CheckedMem::new(rom)), + tile_palette, + vram_renderer: Arc::new(Mutex::new(vram_renderer)), + sprite_renderer: Arc::new(Mutex::new(sprite_renderer)), + palette_renderer: Arc::new(Mutex::new(palette_renderer)), + gfx_bufs, + state_needs_reset: true, + + level_num: 0, + vram_selection_mode: VramSelectionMode::SingleTile, + editing_mode: EditingMode::Move(None), + always_show_grid: false, + + #[cfg(debug_assertions)] + debug_selection_bounds: false, + + tile_size_px: 8., + zoom: 3., + pixels_per_point: 0., + grid_size: OnGrid::splat(31.), + hovering_selected_tile: false, + selection_bounds: None, + selection_offset: None, + + selected_vram_tile: (0, 0), + selected_palette: 0, + sprite_tiles: UndoableData::new(SpriteTiles(Vec::new())), + selected_sprite_tile_indices: HashSet::new(), + } + } + + fn destroy(&self) { + self.vram_renderer.lock().expect("Cannot lock mutex on VRAM renderer").destroy(&self.gl); + self.sprite_renderer.lock().expect("Cannot lock mutex on sprite renderer").destroy(&self.gl); + self.palette_renderer.lock().expect("Cannot lock mutex on palette renderer").destroy(&self.gl); + } +} + +impl DockableEditorTool for UiSpriteMapEditor { + fn update(&mut self, ui: &mut Ui) { + self.reset_state(ui.ctx()); + self.handle_input(ui); + self.layout(ui); + } + + fn title(&self) -> WidgetText { + "Sprite Tile Editor".into() + } + + fn on_closed(&mut self) { + self.destroy(); + } +} + +impl UiSpriteMapEditor { + const MAX_ZOOM: f32 = 5.0; + const MIN_ZOOM: f32 = 1.0; + + pub(super) fn layout(&mut self, ui: &mut Ui) { + TopBottomPanel::top("sprite_map_editor.top_panel").show_inside(ui, |ui| { + menu::bar(ui, |ui| self.menu_bar(ui)); + }); + SidePanel::left("sprite_map_editor.left_panel").resizable(false).show_inside(ui, |ui| self.left_panel(ui)); + CentralPanel::default().show_inside(ui, |ui| self.central_panel(ui)); + } +} diff --git a/src/ui/editor_prototypes/sprite_map_editor/sprite_tiles.rs b/src/ui/editor_prototypes/sprite_map_editor/sprite_tiles.rs new file mode 100644 index 0000000..f58d20d --- /dev/null +++ b/src/ui/editor_prototypes/sprite_map_editor/sprite_tiles.rs @@ -0,0 +1,27 @@ +use shrinkwraprs::Shrinkwrap; +use smwe_render::tile_renderer::Tile; + +use crate::undo::Undo; + +#[derive(Clone, Debug, Shrinkwrap)] +#[shrinkwrap(mutable)] +pub(super) struct SpriteTiles(pub Vec); + +impl Undo for SpriteTiles { + fn from_bytes(bytes: Vec) -> Self { + let tiles = bytes + .chunks(16) + .map(|chunk| chunk.try_into().expect("Length of bytes list not divisible by 16")) + .map(Tile::from_le_bytes) + .collect(); + Self(tiles) + } + + fn to_bytes(&self) -> Vec { + self.0.iter().flat_map(|tile| tile.0.into_iter().flat_map(|x| x.to_le_bytes())).collect() + } + + fn size_bytes(&self) -> usize { + self.0.len() * std::mem::size_of::() + } +} diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 4cde1d4..763d306 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -1,56 +1,79 @@ -mod color; mod dev_utils; +mod editing_mode; mod editor_prototypes; mod project_creator; +mod style; mod tab_viewer; mod tool; use std::sync::Arc; -use eframe::Frame; +use eframe::{CreationContext, Frame}; use egui::*; -use egui_dock::{DockArea, StyleBuilder, Tree}; -use rfd::FileDialog; -use smwe_project::ProjectRef; - -use crate::ui::{ - dev_utils::{ - address_converter::UiAddressConverter, - disassembler::UiDisassembler, - gfx_viewer::UiGfxViewer, - palette_viewer::UiPaletteViewer, - rom_info::UiRomInfo, - tiles16x16::UiTiles16x16, +use egui_dock::{DockArea, DockState, Style as DockStyle}; +use egui_phosphor::Variant; +use smwe_emu::rom::Rom; + +use crate::{ + project::{Project, ProjectRef}, + ui::{ + dev_utils::address_converter::UiAddressConverter, + editor_prototypes::{ + block_editor::UiBlockEditor, + level_editor::UiLevelEditor, + sprite_map_editor::UiSpriteMapEditor, + }, + project_creator::UiProjectCreator, + tab_viewer::EditorToolTabViewer, + tool::DockableEditorTool, }, - editor_prototypes::{block_editor::UiBlockEditor, code_editor::UiCodeEditor}, - project_creator::UiProjectCreator, - tab_viewer::EditorToolTabViewer, - tool::{DockableEditorTool, DockableEditorToolEnum}, }; pub struct UiMainWindow { - project: Option, - style: Arc