From 54d370a8b6a476547b327574bb8a2757becf6c90 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Sat, 5 Mar 2022 20:51:58 +0000 Subject: [PATCH 01/51] Remove rand usage in agb-macros --- agb-macros/src/lib.rs | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/agb-macros/src/lib.rs b/agb-macros/src/lib.rs index 486e8e9a..46e491ff 100644 --- a/agb-macros/src/lib.rs +++ b/agb-macros/src/lib.rs @@ -3,9 +3,11 @@ use proc_macro::TokenStream; use proc_macro2::Span; use quote::{quote, ToTokens}; -use rand::Rng; use syn::{FnArg, Ident, ItemFn, Pat, ReturnType, Token, Type, Visibility}; +use std::collections::hash_map::DefaultHasher; +use std::hash::{Hash, Hasher}; + #[proc_macro_attribute] pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { let f: ItemFn = syn::parse(input).expect("#[agb::entry] must be applied to a function"); @@ -57,7 +59,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { "Must pass no args to #[agb::entry] macro" ); - let fn_name = random_ident(); + let fn_name = hashed_ident(&f); let attrs = f.attrs; let stmts = f.block.stmts; @@ -98,18 +100,13 @@ pub fn num(input: TokenStream) -> TokenStream { quote!((#integer, #fractional)).into() } -fn random_ident() -> Ident { - let mut rng = rand::thread_rng(); - Ident::new( - &(0..16) - .map(|i| { - if i == 0 || rng.gen() { - (b'a' + rng.gen::() % 25) as char - } else { - (b'0' + rng.gen::() % 10) as char - } - }) - .collect::(), - Span::call_site(), - ) +fn hashed_ident(f: &T) -> Ident { + let hash = calculate_hash(f); + Ident::new(&format!("_agb_main_func_{}", hash), Span::call_site()) +} + +fn calculate_hash(t: &T) -> u64 { + let mut s = DefaultHasher::new(); + t.hash(&mut s); + s.finish() } From a82289008bf102363762e14e05ff3c0df29221e6 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Sat, 5 Mar 2022 20:52:20 +0000 Subject: [PATCH 02/51] Remove rand dependency --- agb-macros/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/agb-macros/Cargo.toml b/agb-macros/Cargo.toml index 4c8c218a..0e52d5d4 100644 --- a/agb-macros/Cargo.toml +++ b/agb-macros/Cargo.toml @@ -13,4 +13,3 @@ proc-macro = true syn = { version = "1.0.86", features = ["full", "extra-traits"] } proc-macro2 = "1.0.36" quote = "1.0.15" -rand = "0.8.5" \ No newline at end of file From 39a8b5a64fbf44c092ae8c42dc32eb9da2521f89 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Sat, 5 Mar 2022 20:52:26 +0000 Subject: [PATCH 03/51] Update lockfiles --- agb-fixnum/Cargo.lock | 66 ------------------ agb-image-converter/Cargo.lock | 8 +-- agb-macros/Cargo.lock | 66 ------------------ agb/Cargo.lock | 68 ++----------------- book/games/pong/Cargo.lock | 68 ++----------------- .../the-hat-chooses-the-wizard/Cargo.lock | 68 ++----------------- examples/the-purple-night/Cargo.lock | 68 ++----------------- mgba-test-runner/Cargo.lock | 12 ++-- 8 files changed, 26 insertions(+), 398 deletions(-) diff --git a/agb-fixnum/Cargo.lock b/agb-fixnum/Cargo.lock index e6e394cd..e8c0c4ce 100644 --- a/agb-fixnum/Cargo.lock +++ b/agb-fixnum/Cargo.lock @@ -15,39 +15,9 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "rand", "syn", ] -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "getrandom" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "libc" -version = "0.2.119" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" - -[[package]] -name = "ppv-lite86" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" - [[package]] name = "proc-macro2" version = "1.0.36" @@ -66,36 +36,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom", -] - [[package]] name = "syn" version = "1.0.86" @@ -112,9 +52,3 @@ name = "unicode-xid" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - -[[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" diff --git a/agb-image-converter/Cargo.lock b/agb-image-converter/Cargo.lock index cfdd885c..f0089b29 100644 --- a/agb-image-converter/Cargo.lock +++ b/agb-image-converter/Cargo.lock @@ -40,9 +40,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bytemuck" -version = "1.7.3" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439989e6b8c38d1b6570a384ef1e49c8848128f5a97f3914baef02920842712f" +checksum = "0e851ca7c24871e7336801608a4797d7376545b6928a10d32d75685687141ead" [[package]] name = "byteorder" @@ -147,9 +147,9 @@ dependencies = [ [[package]] name = "png" -version = "0.17.4" +version = "0.17.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02cd7d51cea7e2fa6bbcb8af5fbcad15b871451bfc2d20ed72dff2f4ae072a84" +checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba" dependencies = [ "bitflags", "crc32fast", diff --git a/agb-macros/Cargo.lock b/agb-macros/Cargo.lock index 6bfc3ccb..866dacdc 100644 --- a/agb-macros/Cargo.lock +++ b/agb-macros/Cargo.lock @@ -8,39 +8,9 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "rand", "syn", ] -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "getrandom" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "libc" -version = "0.2.119" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" - -[[package]] -name = "ppv-lite86" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" - [[package]] name = "proc-macro2" version = "1.0.36" @@ -59,36 +29,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom", -] - [[package]] name = "syn" version = "1.0.86" @@ -105,9 +45,3 @@ name = "unicode-xid" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - -[[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" diff --git a/agb/Cargo.lock b/agb/Cargo.lock index 16115991..fa448ec0 100644 --- a/agb/Cargo.lock +++ b/agb/Cargo.lock @@ -51,7 +51,6 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "rand", "syn", ] @@ -85,9 +84,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bytemuck" -version = "1.7.3" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439989e6b8c38d1b6570a384ef1e49c8848128f5a97f3914baef02920842712f" +checksum = "0e851ca7c24871e7336801608a4797d7376545b6928a10d32d75685687141ead" [[package]] name = "byteorder" @@ -125,17 +124,6 @@ dependencies = [ "adler32", ] -[[package]] -name = "getrandom" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - [[package]] name = "hound" version = "3.4.0" @@ -157,12 +145,6 @@ dependencies = [ "png", ] -[[package]] -name = "libc" -version = "0.2.119" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" - [[package]] name = "miniz_oxide" version = "0.5.1" @@ -215,9 +197,9 @@ dependencies = [ [[package]] name = "png" -version = "0.17.4" +version = "0.17.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02cd7d51cea7e2fa6bbcb8af5fbcad15b871451bfc2d20ed72dff2f4ae072a84" +checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba" dependencies = [ "bitflags", "crc32fast", @@ -225,12 +207,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "ppv-lite86" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" - [[package]] name = "proc-macro2" version = "1.0.36" @@ -249,36 +225,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom", -] - [[package]] name = "serde" version = "1.0.136" @@ -324,9 +270,3 @@ name = "unicode-xid" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - -[[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" diff --git a/book/games/pong/Cargo.lock b/book/games/pong/Cargo.lock index ad7beaa5..dc5d523b 100644 --- a/book/games/pong/Cargo.lock +++ b/book/games/pong/Cargo.lock @@ -51,7 +51,6 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "rand", "syn", ] @@ -85,9 +84,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bytemuck" -version = "1.7.3" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439989e6b8c38d1b6570a384ef1e49c8848128f5a97f3914baef02920842712f" +checksum = "0e851ca7c24871e7336801608a4797d7376545b6928a10d32d75685687141ead" [[package]] name = "byteorder" @@ -125,17 +124,6 @@ dependencies = [ "adler32", ] -[[package]] -name = "getrandom" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - [[package]] name = "hound" version = "3.4.0" @@ -157,12 +145,6 @@ dependencies = [ "png", ] -[[package]] -name = "libc" -version = "0.2.119" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" - [[package]] name = "miniz_oxide" version = "0.5.1" @@ -215,9 +197,9 @@ dependencies = [ [[package]] name = "png" -version = "0.17.4" +version = "0.17.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02cd7d51cea7e2fa6bbcb8af5fbcad15b871451bfc2d20ed72dff2f4ae072a84" +checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba" dependencies = [ "bitflags", "crc32fast", @@ -232,12 +214,6 @@ dependencies = [ "agb", ] -[[package]] -name = "ppv-lite86" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" - [[package]] name = "proc-macro2" version = "1.0.36" @@ -256,36 +232,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom", -] - [[package]] name = "serde" version = "1.0.136" @@ -331,9 +277,3 @@ name = "unicode-xid" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - -[[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" diff --git a/examples/the-hat-chooses-the-wizard/Cargo.lock b/examples/the-hat-chooses-the-wizard/Cargo.lock index 72ded3c8..cd5fa42c 100644 --- a/examples/the-hat-chooses-the-wizard/Cargo.lock +++ b/examples/the-hat-chooses-the-wizard/Cargo.lock @@ -51,7 +51,6 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "rand", "syn", ] @@ -85,9 +84,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bytemuck" -version = "1.7.3" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439989e6b8c38d1b6570a384ef1e49c8848128f5a97f3914baef02920842712f" +checksum = "0e851ca7c24871e7336801608a4797d7376545b6928a10d32d75685687141ead" [[package]] name = "byteorder" @@ -125,17 +124,6 @@ dependencies = [ "adler32", ] -[[package]] -name = "getrandom" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - [[package]] name = "hound" version = "3.4.0" @@ -163,12 +151,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" -[[package]] -name = "libc" -version = "0.2.119" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" - [[package]] name = "miniz_oxide" version = "0.5.1" @@ -221,9 +203,9 @@ dependencies = [ [[package]] name = "png" -version = "0.17.4" +version = "0.17.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02cd7d51cea7e2fa6bbcb8af5fbcad15b871451bfc2d20ed72dff2f4ae072a84" +checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba" dependencies = [ "bitflags", "crc32fast", @@ -231,12 +213,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "ppv-lite86" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" - [[package]] name = "proc-macro2" version = "1.0.36" @@ -255,36 +231,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom", -] - [[package]] name = "ryu" version = "1.0.9" @@ -356,9 +302,3 @@ name = "unicode-xid" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - -[[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" diff --git a/examples/the-purple-night/Cargo.lock b/examples/the-purple-night/Cargo.lock index 7a6b1cdb..5566eb7e 100644 --- a/examples/the-purple-night/Cargo.lock +++ b/examples/the-purple-night/Cargo.lock @@ -51,7 +51,6 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "rand", "syn", ] @@ -94,9 +93,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bytemuck" -version = "1.7.3" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439989e6b8c38d1b6570a384ef1e49c8848128f5a97f3914baef02920842712f" +checksum = "0e851ca7c24871e7336801608a4797d7376545b6928a10d32d75685687141ead" [[package]] name = "byteorder" @@ -149,17 +148,6 @@ dependencies = [ "cfg-if 0.1.10", ] -[[package]] -name = "getrandom" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "wasi", -] - [[package]] name = "hound" version = "3.4.0" @@ -181,12 +169,6 @@ dependencies = [ "png", ] -[[package]] -name = "libc" -version = "0.2.119" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" - [[package]] name = "libflate" version = "0.1.27" @@ -251,9 +233,9 @@ dependencies = [ [[package]] name = "png" -version = "0.17.4" +version = "0.17.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02cd7d51cea7e2fa6bbcb8af5fbcad15b871451bfc2d20ed72dff2f4ae072a84" +checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba" dependencies = [ "bitflags", "crc32fast", @@ -261,12 +243,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "ppv-lite86" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" - [[package]] name = "proc-macro2" version = "1.0.36" @@ -285,36 +261,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom", -] - [[package]] name = "rle-decode-fast" version = "1.0.3" @@ -394,12 +340,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" -[[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" - [[package]] name = "xml-rs" version = "0.8.4" diff --git a/mgba-test-runner/Cargo.lock b/mgba-test-runner/Cargo.lock index 689ab620..ff033e0d 100644 --- a/mgba-test-runner/Cargo.lock +++ b/mgba-test-runner/Cargo.lock @@ -86,9 +86,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bytemuck" -version = "1.7.3" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439989e6b8c38d1b6570a384ef1e49c8848128f5a97f3914baef02920842712f" +checksum = "0e851ca7c24871e7336801608a4797d7376545b6928a10d32d75685687141ead" [[package]] name = "byteorder" @@ -363,9 +363,9 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "png" -version = "0.17.4" +version = "0.17.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02cd7d51cea7e2fa6bbcb8af5fbcad15b871451bfc2d20ed72dff2f4ae072a84" +checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba" dependencies = [ "bitflags", "crc32fast", @@ -428,9 +428,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "termcolor" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" dependencies = [ "winapi-util", ] From 97d043d505c6f2f342c46bc5f7874c06aa9eaf17 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Sat, 5 Mar 2022 20:58:15 +0000 Subject: [PATCH 04/51] Use a setup-mdbook action --- .github/workflows/build-and-test.yml | 4 +++ .github/workflows/build-book.yml | 38 ---------------------------- .github/workflows/publish-agb.yml | 6 +++-- 3 files changed, 8 insertions(+), 40 deletions(-) delete mode 100644 .github/workflows/build-book.yml diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 5ef0378e..3b9016e2 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -33,5 +33,9 @@ jobs: - name: Install gbafix run: cargo install gbafix - uses: extractions/setup-just@v1 + - name: Setup mdBook + uses: peaceiris/actions-mdbook@v1 + with: + mdbook-version: '0.4.13' - name: Build and test all crates run: just ci \ No newline at end of file diff --git a/.github/workflows/build-book.yml b/.github/workflows/build-book.yml deleted file mode 100644 index e533deab..00000000 --- a/.github/workflows/build-book.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: Build book - -on: - push: - branches: [ master ] - paths: - 'book/**' - pull_request: - branches: [ master ] - paths: - 'book/**' - -env: - CARGO_TERM_COLOR: always - -jobs: - build: - runs-on: ubuntu-20.04 - steps: - - name: Set CARGO_TARGET_DIR - run: echo "CARGO_TARGET_DIR=$HOME/target" >> $GITHUB_ENV - - name: Install build tools - run: sudo apt-get update && sudo apt-get install build-essential binutils-arm-none-eabi libelf-dev zip -y - - uses: actions/checkout@v3 - - name: Cache - uses: actions/cache@v2.1.7 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - ~/target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - - name: Install mdbook - run: cargo install mdbook - - name: Build the book - run: mdbook build - working-directory: book \ No newline at end of file diff --git a/.github/workflows/publish-agb.yml b/.github/workflows/publish-agb.yml index 2d7a6d29..681564e4 100644 --- a/.github/workflows/publish-agb.yml +++ b/.github/workflows/publish-agb.yml @@ -38,8 +38,10 @@ jobs: tag: ${{ github.ref }} overwrite: true - - name: Install mdbook - run: cargo install mdbook + - name: Setup mdBook + uses: peaceiris/actions-mdbook@v1 + with: + mdbook-version: '0.4.13' - name: Build the book run: just build-book - name: Deploy the book From cb0bcf7c6737f9db9e2f52a72adf24f3f65302ab Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Sat, 5 Mar 2022 20:58:25 +0000 Subject: [PATCH 05/51] Add build-book to just ci --- justfile | 1 + 1 file changed, 1 insertion(+) diff --git a/justfile b/justfile index 0229f453..82e36a06 100644 --- a/justfile +++ b/justfile @@ -27,6 +27,7 @@ ci: && build-roms just _all-crates _test-debug just _all-crates _test-release just _all-crates _clippy + just build-book build-roms: just _build-rom "examples/the-purple-night" "PURPLENIGHT" From 3c6c7efc79bf8a8ac4bfbca6989fd24284ea140b Mon Sep 17 00:00:00 2001 From: Corwin Date: Mon, 7 Feb 2022 21:19:56 +0000 Subject: [PATCH 06/51] allocator that can have a custom start point --- agb/src/agb_alloc/block_allocator.rs | 4 ++-- agb/src/agb_alloc/bump_allocator.rs | 36 ++++------------------------ agb/src/agb_alloc/mod.rs | 27 ++++++++++++++++++++- 3 files changed, 32 insertions(+), 35 deletions(-) diff --git a/agb/src/agb_alloc/block_allocator.rs b/agb/src/agb_alloc/block_allocator.rs index 6dfb154b..0866d697 100644 --- a/agb/src/agb_alloc/block_allocator.rs +++ b/agb/src/agb_alloc/block_allocator.rs @@ -49,9 +49,9 @@ pub(crate) struct BlockAllocator { } impl BlockAllocator { - pub(super) const unsafe fn new() -> Self { + pub(super) const unsafe fn new(start: fn() -> usize) -> Self { Self { - inner_allocator: BumpAllocator::new(), + inner_allocator: BumpAllocator::new(start), state: Mutex::new(RefCell::new(BlockAllocatorState { first_free_block: None, })), diff --git a/agb/src/agb_alloc/bump_allocator.rs b/agb/src/agb_alloc/bump_allocator.rs index d1ddac3a..701f29df 100644 --- a/agb/src/agb_alloc/bump_allocator.rs +++ b/agb/src/agb_alloc/bump_allocator.rs @@ -8,12 +8,14 @@ use bare_metal::{CriticalSection, Mutex}; pub(crate) struct BumpAllocator { current_ptr: Mutex>>>, + start: Mutex usize>, } impl BumpAllocator { - pub const fn new() -> Self { + pub const fn new(start: fn() -> usize) -> Self { Self { current_ptr: Mutex::new(RefCell::new(None)), + start: Mutex::new(start), } } } @@ -25,7 +27,7 @@ impl BumpAllocator { let ptr = if let Some(c) = *current_ptr { c.as_ptr() as usize } else { - get_data_end() + self.start.borrow(*cs)() }; let alignment_bitmask = layout.align() - 1; @@ -56,33 +58,3 @@ unsafe impl GlobalAlloc for BumpAllocator { unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} } - -fn get_data_end() -> usize { - extern "C" { - static __ewram_data_end: usize; - } - - // TODO: This seems completely wrong, but without the &, rust generates - // a double dereference :/. Maybe a bug in nightly? - (unsafe { &__ewram_data_end }) as *const _ as usize -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test_case] - fn should_return_data_end_somewhere_in_ewram(_gba: &mut crate::Gba) { - let data_end = get_data_end(); - - assert!( - 0x0200_0000 <= data_end, - "data end should be bigger than 0x0200_0000, got {}", - data_end - ); - assert!( - 0x0204_0000 > data_end, - "data end should be smaller than 0x0203_0000" - ); - } -} diff --git a/agb/src/agb_alloc/mod.rs b/agb/src/agb_alloc/mod.rs index 4084f3af..c3f9a875 100644 --- a/agb/src/agb_alloc/mod.rs +++ b/agb/src/agb_alloc/mod.rs @@ -33,7 +33,7 @@ impl DerefMut for SendNonNull { const EWRAM_END: usize = 0x0204_0000; #[global_allocator] -static GLOBAL_ALLOC: BlockAllocator = unsafe { BlockAllocator::new() }; +static GLOBAL_ALLOC: BlockAllocator = unsafe { BlockAllocator::new(get_data_end) }; #[cfg(test)] pub unsafe fn number_of_blocks() -> u32 { @@ -49,6 +49,16 @@ fn alloc_error(layout: Layout) -> ! { ); } +fn get_data_end() -> usize { + extern "C" { + static __ewram_data_end: usize; + } + + // TODO: This seems completely wrong, but without the &, rust generates + // a double dereference :/. Maybe a bug in nightly? + (unsafe { &__ewram_data_end }) as *const _ as usize +} + #[cfg(test)] mod test { const EWRAM_START: usize = 0x0200_0000; @@ -118,4 +128,19 @@ mod test { assert_eq!(v1[40], 137); assert_eq!(v2[78], 1075); } + + #[test_case] + fn should_return_data_end_somewhere_in_ewram(_gba: &mut crate::Gba) { + let data_end = get_data_end(); + + assert!( + 0x0200_0000 <= data_end, + "data end should be bigger than 0x0200_0000, got {}", + data_end + ); + assert!( + 0x0204_0000 > data_end, + "data end should be smaller than 0x0203_0000" + ); + } } From 475edadcb92841e7382a1a952536d2fd14adfa96 Mon Sep 17 00:00:00 2001 From: Corwin Date: Mon, 7 Feb 2022 21:43:17 +0000 Subject: [PATCH 07/51] extend to also supporting the end --- agb/src/agb_alloc/block_allocator.rs | 4 ++-- agb/src/agb_alloc/bump_allocator.rs | 17 ++++++++++++----- agb/src/agb_alloc/mod.rs | 9 ++++++++- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/agb/src/agb_alloc/block_allocator.rs b/agb/src/agb_alloc/block_allocator.rs index 0866d697..1d2221ae 100644 --- a/agb/src/agb_alloc/block_allocator.rs +++ b/agb/src/agb_alloc/block_allocator.rs @@ -12,7 +12,7 @@ use core::ptr::NonNull; use crate::interrupt::free; use bare_metal::{CriticalSection, Mutex}; -use super::bump_allocator::BumpAllocator; +use super::bump_allocator::{BumpAllocator, StartEnd}; use super::SendNonNull; struct Block { @@ -49,7 +49,7 @@ pub(crate) struct BlockAllocator { } impl BlockAllocator { - pub(super) const unsafe fn new(start: fn() -> usize) -> Self { + pub(super) const unsafe fn new(start: StartEnd) -> Self { Self { inner_allocator: BumpAllocator::new(start), state: Mutex::new(RefCell::new(BlockAllocatorState { diff --git a/agb/src/agb_alloc/bump_allocator.rs b/agb/src/agb_alloc/bump_allocator.rs index 701f29df..5b6ca15b 100644 --- a/agb/src/agb_alloc/bump_allocator.rs +++ b/agb/src/agb_alloc/bump_allocator.rs @@ -6,16 +6,23 @@ use super::SendNonNull; use crate::interrupt::free; use bare_metal::{CriticalSection, Mutex}; +pub(crate) struct AddrFn(pub fn() -> usize); + +pub(crate) struct StartEnd { + pub start: AddrFn, + pub end: AddrFn, +} + pub(crate) struct BumpAllocator { current_ptr: Mutex>>>, - start: Mutex usize>, + start_end: Mutex, } impl BumpAllocator { - pub const fn new(start: fn() -> usize) -> Self { + pub const fn new(start_end: StartEnd) -> Self { Self { current_ptr: Mutex::new(RefCell::new(None)), - start: Mutex::new(start), + start_end: Mutex::new(start_end), } } } @@ -27,7 +34,7 @@ impl BumpAllocator { let ptr = if let Some(c) = *current_ptr { c.as_ptr() as usize } else { - self.start.borrow(*cs)() + self.start_end.borrow(*cs).start.0() }; let alignment_bitmask = layout.align() - 1; @@ -38,7 +45,7 @@ impl BumpAllocator { let resulting_ptr = ptr + amount_to_add; let new_current_ptr = resulting_ptr + layout.size(); - if new_current_ptr as usize >= super::EWRAM_END { + if new_current_ptr as usize >= self.start_end.borrow(*cs).end.0() { return core::ptr::null_mut(); } diff --git a/agb/src/agb_alloc/mod.rs b/agb/src/agb_alloc/mod.rs index c3f9a875..97fc1734 100644 --- a/agb/src/agb_alloc/mod.rs +++ b/agb/src/agb_alloc/mod.rs @@ -7,6 +7,8 @@ mod bump_allocator; use block_allocator::BlockAllocator; +use self::bump_allocator::{AddrFn, StartEnd}; + struct SendNonNull(NonNull); unsafe impl Send for SendNonNull {} @@ -33,7 +35,12 @@ impl DerefMut for SendNonNull { const EWRAM_END: usize = 0x0204_0000; #[global_allocator] -static GLOBAL_ALLOC: BlockAllocator = unsafe { BlockAllocator::new(get_data_end) }; +static GLOBAL_ALLOC: BlockAllocator = unsafe { + BlockAllocator::new(StartEnd { + start: AddrFn(get_data_end), + end: AddrFn(|| EWRAM_END), + }) +}; #[cfg(test)] pub unsafe fn number_of_blocks() -> u32 { From beb9abbb7eb366ea26cf0fc224c428f59c318a12 Mon Sep 17 00:00:00 2001 From: Corwin Date: Mon, 7 Feb 2022 22:07:04 +0000 Subject: [PATCH 08/51] allocator methods pulled out of global alloc trait --- agb/src/agb_alloc/block_allocator.rs | 23 +++++++++++++++++------ agb/src/agb_alloc/bump_allocator.rs | 13 ++++++++----- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/agb/src/agb_alloc/block_allocator.rs b/agb/src/agb_alloc/block_allocator.rs index 1d2221ae..2c60114a 100644 --- a/agb/src/agb_alloc/block_allocator.rs +++ b/agb/src/agb_alloc/block_allocator.rs @@ -76,7 +76,7 @@ impl BlockAllocator { } /// Requests a brand new block from the inner bump allocator - fn new_block(&self, layout: Layout, cs: &CriticalSection) -> *mut u8 { + fn new_block(&self, layout: Layout, cs: &CriticalSection) -> Option> { let overall_layout = Block::either_layout(layout); self.inner_allocator.alloc_critical(overall_layout, cs) } @@ -111,10 +111,8 @@ impl BlockAllocator { } }); } -} -unsafe impl GlobalAlloc for BlockAllocator { - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + unsafe fn alloc(&self, layout: Layout) -> Option> { // find a block that this current request fits in let full_layout = Block::either_layout(layout); @@ -133,7 +131,7 @@ unsafe impl GlobalAlloc for BlockAllocator { let curr_block = curr.as_mut(); if curr_block.size == full_layout.size() { *list_ptr = curr_block.next; - return curr.as_ptr().cast(); + return Some(curr.cast()); } else if curr_block.size >= block_after_layout.size() { // can split block let split_block = Block { @@ -148,7 +146,7 @@ unsafe impl GlobalAlloc for BlockAllocator { *split_ptr = split_block; *list_ptr = NonNull::new(split_ptr).map(SendNonNull); - return curr.as_ptr().cast(); + return Some(curr.cast()); } current_block = curr_block.next; list_ptr = &mut curr_block.next; @@ -200,3 +198,16 @@ unsafe impl GlobalAlloc for BlockAllocator { self.normalise(); } } + +unsafe impl GlobalAlloc for BlockAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + match self.alloc(layout) { + None => core::ptr::null_mut(), + Some(p) => p.as_ptr(), + } + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + self.dealloc(ptr, layout); + } +} diff --git a/agb/src/agb_alloc/bump_allocator.rs b/agb/src/agb_alloc/bump_allocator.rs index 5b6ca15b..0be21bdd 100644 --- a/agb/src/agb_alloc/bump_allocator.rs +++ b/agb/src/agb_alloc/bump_allocator.rs @@ -28,7 +28,7 @@ impl BumpAllocator { } impl BumpAllocator { - pub fn alloc_critical(&self, layout: Layout, cs: &CriticalSection) -> *mut u8 { + pub fn alloc_critical(&self, layout: Layout, cs: &CriticalSection) -> Option> { let mut current_ptr = self.current_ptr.borrow(*cs).borrow_mut(); let ptr = if let Some(c) = *current_ptr { @@ -46,21 +46,24 @@ impl BumpAllocator { let new_current_ptr = resulting_ptr + layout.size(); if new_current_ptr as usize >= self.start_end.borrow(*cs).end.0() { - return core::ptr::null_mut(); + return None; } *current_ptr = NonNull::new(new_current_ptr as *mut _).map(SendNonNull); - resulting_ptr as *mut _ + NonNull::new(resulting_ptr as *mut _) } - pub fn alloc_safe(&self, layout: Layout) -> *mut u8 { + pub fn alloc_safe(&self, layout: Layout) -> Option> { free(|key| self.alloc_critical(layout, key)) } } unsafe impl GlobalAlloc for BumpAllocator { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - self.alloc_safe(layout) + match self.alloc_safe(layout) { + None => core::ptr::null_mut(), + Some(p) => p.as_ptr(), + } } unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} From 1849571344b44e76fd0d337efb8a073c611893bd Mon Sep 17 00:00:00 2001 From: Corwin Date: Mon, 7 Feb 2022 23:01:52 +0000 Subject: [PATCH 09/51] sprite allocator --- agb/Cargo.lock | 56 +++ agb/Cargo.toml | 1 + agb/src/agb_alloc/block_allocator.rs | 2 +- agb/src/agb_alloc/bump_allocator.rs | 10 +- agb/src/agb_alloc/mod.rs | 10 +- agb/src/display/object.rs | 564 ++++----------------------- 6 files changed, 138 insertions(+), 505 deletions(-) diff --git a/agb/Cargo.lock b/agb/Cargo.lock index fa448ec0..cf0c71a0 100644 --- a/agb/Cargo.lock +++ b/agb/Cargo.lock @@ -24,6 +24,7 @@ dependencies = [ "agb_sound_converter", "bare-metal", "bitflags", + "hashbrown", ] [[package]] @@ -64,6 +65,17 @@ dependencies = [ "syn", ] +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -124,6 +136,26 @@ dependencies = [ "adler32", ] +[[package]] +name = "getrandom" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c21d40587b92fa6a6c6e3c1bdbf87d75511db5672f9c93175574b3a00df1758" +dependencies = [ + "ahash", +] + [[package]] name = "hound" version = "3.4.0" @@ -145,6 +177,12 @@ dependencies = [ "png", ] +[[package]] +name = "libc" +version = "0.2.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" + [[package]] name = "miniz_oxide" version = "0.5.1" @@ -195,6 +233,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "once_cell" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" + [[package]] name = "png" version = "0.17.5" @@ -270,3 +314,15 @@ name = "unicode-xid" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" diff --git a/agb/Cargo.toml b/agb/Cargo.toml index dbd7d298..0dcfbade 100644 --- a/agb/Cargo.toml +++ b/agb/Cargo.toml @@ -26,6 +26,7 @@ agb_sound_converter = { version = "0.1.0", path = "../agb-sound-converter" } agb_macros = { version = "0.1.0", path = "../agb-macros" } agb_fixnum = { version = "0.1.0", path = "../agb-fixnum" } bare-metal = "1.0" +hashbrown = "0.12.0" [package.metadata.docs.rs] default-target = "thumbv6m-none-eabi" diff --git a/agb/src/agb_alloc/block_allocator.rs b/agb/src/agb_alloc/block_allocator.rs index 2c60114a..82cb8443 100644 --- a/agb/src/agb_alloc/block_allocator.rs +++ b/agb/src/agb_alloc/block_allocator.rs @@ -49,7 +49,7 @@ pub(crate) struct BlockAllocator { } impl BlockAllocator { - pub(super) const unsafe fn new(start: StartEnd) -> Self { + pub const unsafe fn new(start: StartEnd) -> Self { Self { inner_allocator: BumpAllocator::new(start), state: Mutex::new(RefCell::new(BlockAllocatorState { diff --git a/agb/src/agb_alloc/bump_allocator.rs b/agb/src/agb_alloc/bump_allocator.rs index 0be21bdd..daafb3c1 100644 --- a/agb/src/agb_alloc/bump_allocator.rs +++ b/agb/src/agb_alloc/bump_allocator.rs @@ -6,11 +6,9 @@ use super::SendNonNull; use crate::interrupt::free; use bare_metal::{CriticalSection, Mutex}; -pub(crate) struct AddrFn(pub fn() -> usize); - pub(crate) struct StartEnd { - pub start: AddrFn, - pub end: AddrFn, + pub start: fn() -> usize, + pub end: fn() -> usize, } pub(crate) struct BumpAllocator { @@ -34,7 +32,7 @@ impl BumpAllocator { let ptr = if let Some(c) = *current_ptr { c.as_ptr() as usize } else { - self.start_end.borrow(*cs).start.0() + (self.start_end.borrow(*cs).start)() }; let alignment_bitmask = layout.align() - 1; @@ -45,7 +43,7 @@ impl BumpAllocator { let resulting_ptr = ptr + amount_to_add; let new_current_ptr = resulting_ptr + layout.size(); - if new_current_ptr as usize >= self.start_end.borrow(*cs).end.0() { + if new_current_ptr as usize >= (self.start_end.borrow(*cs).end)() { return None; } diff --git a/agb/src/agb_alloc/mod.rs b/agb/src/agb_alloc/mod.rs index 97fc1734..9a2c6a94 100644 --- a/agb/src/agb_alloc/mod.rs +++ b/agb/src/agb_alloc/mod.rs @@ -2,12 +2,12 @@ use core::alloc::Layout; use core::ops::{Deref, DerefMut}; use core::ptr::NonNull; -mod block_allocator; -mod bump_allocator; +pub(crate) mod block_allocator; +pub(crate) mod bump_allocator; use block_allocator::BlockAllocator; -use self::bump_allocator::{AddrFn, StartEnd}; +use self::bump_allocator::StartEnd; struct SendNonNull(NonNull); unsafe impl Send for SendNonNull {} @@ -37,8 +37,8 @@ const EWRAM_END: usize = 0x0204_0000; #[global_allocator] static GLOBAL_ALLOC: BlockAllocator = unsafe { BlockAllocator::new(StartEnd { - start: AddrFn(get_data_end), - end: AddrFn(|| EWRAM_END), + start: get_data_end, + end: || EWRAM_END, }) }; diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 4dea6e6f..626acbba 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -1,12 +1,21 @@ use core::cell::RefCell; +use hashbrown::{hash_map::Entry, HashMap}; + +use super::palette16::Palette16; use super::{palette16, Priority, DISPLAY_CONTROL}; +use crate::agb_alloc::block_allocator::BlockAllocator; +use crate::agb_alloc::bump_allocator::StartEnd; use crate::bitarray::Bitarray; use crate::fixnum::Vector2D; use crate::memory_mapped::MemoryMapped1DArray; -type AffineLoan<'a> = crate::arena::Loan<'a, 32>; -type AffineArena = crate::arena::Arena<32>; +static SPRITE_ALLOCATOR: BlockAllocator = unsafe { + BlockAllocator::new(StartEnd { + start: || 0x06010000, + end: || 0x06010000 + 1024 * 8 * 4, + }) +}; const OBJECT_ATTRIBUTE_MEMORY: MemoryMapped1DArray = unsafe { MemoryMapped1DArray::new(0x0700_0000) }; @@ -15,523 +24,92 @@ const PALETTE_SPRITE: MemoryMapped1DArray = const TILE_SPRITE: MemoryMapped1DArray = unsafe { MemoryMapped1DArray::new(0x06010000) }; -/// Handles distributing objects and matrices along with operations that effect all objects. -/// You can create an instance of this using the Gba struct. -/// -/// This handles distribution of sprites, ensuring that object ids are not reused and are -/// returned to the pool once you're done handling them. -/// -/// # Examples -/// -/// ``` -/// # #![no_std] -/// # #![no_main] -/// # -/// # use agb::Gba; -/// # -/// # #[agb::entry] -/// # fn main() -> ! { -/// let mut gba = Gba::new(); -/// let mut object = gba.display.object.get(); -/// # -/// # loop {} -/// # } -/// ``` -pub struct ObjectControl { - objects: RefCell>, - affines: AffineArena, +pub struct Sprite { + palette: &'static Palette16, + data: &'static [u8], } -struct ObjectLoan<'a> { - index: u8, - objects: &'a RefCell>, +struct SpriteBorrow<'a> { + id: SpriteId, + controller: &'a RefCell, } -/// The standard object, without rotation. -/// -/// You should create this from an instance of ObjectControl created using the Gba struct. Note that -/// no changes made to this will be visible until `commit()` is called. You should call `commit()` during -/// vblank to ensure that you get no visual artifacts. -/// -/// This struct implements a sort of builder pattern, allowing you to chain settings together. -/// -/// # Examples -/// -/// ``` -/// # #![no_std] -/// # #![no_main] -/// # -/// # use agb::Gba; -/// use agb::display::object::Size; -/// -/// # #[agb::entry] -/// # fn main() -> ! { -/// # let mut gba = Gba::new(); -/// let mut object = gba.display.object.get(); -/// -/// let mut my_new_object = object.get_object_standard(); -/// my_new_object.set_x(50) -/// .set_y(50) -/// .set_sprite_Size(Size::S8x8) -/// .set_tile_id(7) -/// .show(); -/// -/// // some time later in vblank -/// my_new_object.commit(); -/// # loop {} -/// # } -/// ``` -pub struct ObjectStandard<'a> { - attributes: ObjectAttribute, - loan: ObjectLoan<'a>, +struct Storage { + location: u16, + count: u16, } -/// The affine object, with potential for using a transformation matrix to alter -/// how the sprite is rendered to screen. -pub struct ObjectAffine<'a> { - attributes: ObjectAttribute, - loan: ObjectLoan<'a>, - aff_loan: Option>, +pub struct Object<'a> { + sprite: SpriteBorrow<'a>, } -/// Refers to an affine matrix in the OAM. Includes both an index and the -/// components of the affine matrix. -pub struct AffineMatrix<'a> { - pub attributes: AffineMatrixAttributes, - loan: AffineLoan<'a>, +struct SpriteControllerInner { + palette: HashMap, + sprite: HashMap, } -/// The components of the affine matrix. The components are fixed point 8:8. -/// TODO is a type that can handle fixed point arithmetic. -pub struct AffineMatrixAttributes { - pub p_a: i16, - pub p_b: i16, - pub p_c: i16, - pub p_d: i16, +pub struct SpriteController { + inner: RefCell, } -#[allow(dead_code)] -enum Mode { - Normal = 0, - Affine = 1, - Hidden = 2, - AffineDouble = 3, -} +pub struct ObjectController {} -#[derive(Clone, Copy)] -pub enum Size { - // stored as attr0 attr1 - S8x8 = 0b00_00, - S16x16 = 0b00_01, - S32x32 = 0b00_10, - S64x64 = 0b00_11, +/// The Sprite Id is a thin wrapper around the pointer to the sprite in +/// rom and is therefore a unique identifier to a sprite +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +struct SpriteId(usize); - S16x8 = 0b01_00, - S32x8 = 0b01_01, - S32x16 = 0b01_10, - S64x32 = 0b01_11, +/// The palette id is a thin wrapper around the pointer to the palette in rom +/// and is therefore a unique reference to a palette +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +struct PaletteId(usize); - S8x16 = 0b10_00, - S8x32 = 0b10_01, - S16x32 = 0b10_10, - S32x64 = 0b10_11, -} - -impl ObjectStandard<'_> { - /// Commits the object to OAM such that the updated version is displayed on - /// screen. Recommend to do this during VBlank. - pub fn commit(&self) { - unsafe { self.attributes.commit(self.loan.index) } - } - - /// Sets the x coordinate of the sprite on screen. - pub fn set_x(&mut self, x: u16) -> &mut Self { - self.attributes.set_x(x); - - self - } - - /// Sets the y coordinate of the sprite on screen. - pub fn set_y(&mut self, y: u16) -> &mut Self { - self.attributes.set_y(y); - - self - } - - /// Sets the index of the tile to use as the sprite. Potentially a temporary function. - pub fn set_tile_id(&mut self, id: u16) -> &mut Self { - self.attributes.set_tile_id(id); - - self - } - - /// Sets whether the sprite is horizontally mirrored or not. - pub fn set_hflip(&mut self, hflip: bool) -> &mut Self { - self.attributes.set_hflip(hflip); - - self - } - - /// Sets the sprite size, will read tiles in x major order to construct this. - pub fn set_sprite_size(&mut self, size: Size) -> &mut Self { - self.attributes.set_size(size); - - self - } - - /// Show the object on screen. - pub fn show(&mut self) -> &mut Self { - self.attributes.set_mode(Mode::Normal); - - self - } - - /// Hide the object and do not render. - pub fn hide(&mut self) -> &mut Self { - self.attributes.set_mode(Mode::Hidden); - - self - } - - /// Sets the palette to use for this sprite - pub fn set_palette(&mut self, palette: u16) -> &mut Self { - self.attributes.set_palette(palette); - - self - } - - /// Sets the x and y position of the object, performing casts as nessesary - /// to fit within the bits allocated for this purpose. - pub fn set_position(&mut self, position: Vector2D) -> &mut Self { - let x = position.x as u16; - let y = position.y as u16; - self.attributes.set_x(x); - self.attributes.set_y(y); - - self - } - - /// Sets the priority (used for z ordering) of this sprite - pub fn set_priority(&mut self, p: Priority) -> &mut Self { - self.attributes.set_priority(p); - - self +impl Sprite { + fn get_id(&'static self) -> SpriteId { + SpriteId(self as *const _ as usize) } } -impl<'a> ObjectAffine<'a> { - /// Commits the object to OAM such that the updated version is displayed on - /// screen. Recommend to do this during VBlank. - pub fn commit(&self) { - unsafe { self.attributes.commit(self.loan.index) } - } - - /// Sets the x coordinate of the sprite on screen. - pub fn set_x(&mut self, x: u16) { - self.attributes.set_x(x) - } - /// Sets the y coordinate of the sprite on screen. - pub fn set_y(&mut self, y: u16) { - self.attributes.set_y(y) - } - /// Sets the index of the tile to use as the sprite. Potentially a temporary function. - pub fn set_tile_id(&mut self, id: u16) { - self.attributes.set_tile_id(id) - } - /// Sets the sprite size, will read tiles in x major order to construct this. - pub fn set_sprite_size(&mut self, size: Size) { - self.attributes.set_size(size); - } - - /// Show the object on screen. Panics if affine matrix has not been set. - pub fn show(&mut self) { - if self.aff_loan.is_none() { - panic!("affine matrix should be set") +impl SpriteController { + fn get_sprite(&self, sprite: &'static Sprite) -> Option { + let inner = self.inner.borrow_mut(); + let id = sprite.get_id(); + if let Some(storage) = inner.sprite.get_mut(&id) { + storage.count += 1; + Some(SpriteBorrow { + id, + controller: &self.inner, + }) + } else { + // allocate a new sprite + todo!(); } - self.attributes.set_mode(Mode::Affine) - } - /// Hide the object and do not render the sprite. - pub fn hide(&mut self) { - self.attributes.set_mode(Mode::Hidden) - } - - /// Sets the affine matrix to use. Changing the affine matrix will change - /// how the sprite is rendered. - pub fn set_affine_mat(&mut self, aff: &AffineMatrix<'a>) { - self.attributes.set_affine(aff.loan.my_index); - self.aff_loan = Some(aff.loan.clone()); - } - - /// Sets the x and y position of the object, performing casts as nessesary - /// to fit within the bits allocated for this purpose. - pub fn set_position(&mut self, position: Vector2D) { - let x = position.x as u16; - let y = position.y as u16; - self.attributes.set_x(x); - self.attributes.set_y(y); - } - - pub fn set_priority(&mut self, p: Priority) { - self.attributes.set_priority(p) } } -fn set_bits(current: u16, value: u16, length: u16, shift: u16) -> u16 { - let mask: u16 = (1 << length) - 1; - (current & !(mask << shift)) | ((value & mask) << shift) -} - -impl Drop for ObjectLoan<'_> { +impl<'a> Drop for SpriteBorrow<'a> { fn drop(&mut self) { - let attributes = ObjectAttribute::new(); - unsafe { - attributes.commit(self.index); - } - let mut objs = self.objects.borrow_mut(); - objs.set(self.index as usize, false); - } -} + let inner = self.controller.borrow_mut(); + let entry = inner + .sprite + .entry(self.id) + .and_replace_entry_with(|_, mut storage| { + storage.count -= 1; + if storage.count == 0 { + None + } else { + Some(storage) + } + }); -struct ObjectAttribute { - a0: u16, - a1: u16, - a2: u16, -} - -impl ObjectAttribute { - unsafe fn commit(&self, index: u8) { - OBJECT_ATTRIBUTE_MEMORY.set(index as usize * 4, self.a0); - OBJECT_ATTRIBUTE_MEMORY.set(index as usize * 4 + 1, self.a1); - OBJECT_ATTRIBUTE_MEMORY.set(index as usize * 4 + 2, self.a2); - } - - fn set_hflip(&mut self, hflip: bool) { - self.a1 = set_bits(self.a1, hflip as u16, 1, 0xC); - } - - fn set_size(&mut self, size: Size) { - let a1 = size as u16 & 0b11; - let a0 = (size as u16 >> 2) & 0b11; - - self.a0 = set_bits(self.a0, a0, 2, 0xE); - self.a1 = set_bits(self.a1, a1, 2, 0xE); - } - - fn set_palette(&mut self, palette: u16) { - self.a2 = set_bits(self.a2, palette, 4, 0xC); - } - - fn set_x(&mut self, x: u16) { - self.a1 = set_bits(self.a1, x, 9, 0); - } - - fn set_y(&mut self, y: u16) { - self.a0 = set_bits(self.a0, y, 8, 0) - } - - fn set_tile_id(&mut self, id: u16) { - self.a2 = set_bits(self.a2, id, 10, 0); - } - - fn set_mode(&mut self, mode: Mode) { - self.a0 = set_bits(self.a0, mode as u16, 2, 8); - } - - fn set_affine(&mut self, aff_id: u8) { - self.a1 = set_bits(self.a1, aff_id as u16, 5, 0x9); - } - - fn set_priority(&mut self, p: Priority) { - self.a2 = set_bits(self.a2, p as u16, 2, 0x0A); - } -} - -impl AffineMatrix<'_> { - /// Commits matrix to OAM, will cause any objects using this matrix to be updated. - pub fn commit(&self) { - unsafe { self.attributes.commit(self.loan.my_index) }; - } -} - -impl AffineMatrixAttributes { - #[allow(clippy::identity_op)] - unsafe fn commit(&self, index: u8) { - let index = index as usize * 4; - OBJECT_ATTRIBUTE_MEMORY.set((index + 0) * 4 + 3, self.p_a as u16); - OBJECT_ATTRIBUTE_MEMORY.set((index + 1) * 4 + 3, self.p_b as u16); - OBJECT_ATTRIBUTE_MEMORY.set((index + 2) * 4 + 3, self.p_c as u16); - OBJECT_ATTRIBUTE_MEMORY.set((index + 3) * 4 + 3, self.p_d as u16); - } -} - -impl ObjectAttribute { - fn new() -> Self { - let mut o = ObjectAttribute { - a0: 0, - a1: 0, - a2: 0, - }; - o.set_mode(Mode::Hidden); - o - } -} - -impl ObjectControl { - pub(crate) fn new() -> Self { - let o = ObjectAttribute::new(); - for index in 0..128 { - unsafe { o.commit(index) }; - } - ObjectControl { - objects: RefCell::new(Bitarray::new()), - affines: AffineArena::new(), - } - } - - fn set_sprite_tilemap_entry(&self, index: usize, data: u32) { - TILE_SPRITE.set(index, data); - } - - /// Copies raw palettes to the background palette without any checks. - pub fn set_sprite_palette_raw(&self, colour: &[u16]) { - for (index, &entry) in colour.iter().enumerate() { - self.set_sprite_palette_entry(index, entry) - } - } - fn set_sprite_palette_entry(&self, index: usize, colour: u16) { - PALETTE_SPRITE.set(index, colour) - } - - fn set_sprite_palette(&self, pal_index: u8, palette: &palette16::Palette16) { - for (colour_index, &colour) in palette.colours.iter().enumerate() { - PALETTE_SPRITE.set(pal_index as usize * 16 + colour_index, colour); - } - } - - pub fn set_sprite_palettes(&self, palettes: &[palette16::Palette16]) { - for (palette_index, entry) in palettes.iter().enumerate() { - self.set_sprite_palette(palette_index as u8, entry) - } - } - - /// Copies tiles to the sprite tilemap without any checks. - pub fn set_sprite_tilemap(&self, tiles: &[u32]) { - for (index, &tile) in tiles.iter().enumerate() { - self.set_sprite_tilemap_entry(index, tile) - } - } - - pub fn set_sprite_tilemap_at_idx(&self, idx: usize, tiles: &[u32]) { - for (index, &tile) in tiles.iter().enumerate() { - self.set_sprite_tilemap_entry(index + idx, tile) - } - } - - /// Enable objects on the GBA. - pub fn enable(&mut self) { - let disp = DISPLAY_CONTROL.get(); - let disp = disp | (1 << 0x0C); - DISPLAY_CONTROL.set(disp); - } - - /// Disable objects, objects won't be rendered. - pub fn disable(&mut self) { - let disp = DISPLAY_CONTROL.get(); - let disp = disp & !(1 << 0x0C); - DISPLAY_CONTROL.set(disp); - } - - fn get_unused_object_index(&self) -> u8 { - let mut objects = self.objects.borrow_mut(); - for index in 0..128 { - if !objects.get(index).unwrap() { - objects.set(index, true); - return index as u8; + match entry { + Entry::Vacant(_) => { + // free the underlying resource. + // palette might be unused too. } - } - panic!("object id must be less than 128"); - } - - /// Get an unused standard object. Panics if more than 128 objects are - /// obtained. - pub fn get_object_standard(&self) -> ObjectStandard { - let id = self.get_unused_object_index(); - ObjectStandard { - attributes: ObjectAttribute::new(), - loan: ObjectLoan { - objects: &self.objects, - index: id, - }, - } - } - - /// Get an unused affine object. Panics if more than 128 objects are - /// obtained. - pub fn get_object_affine(&self) -> ObjectAffine { - let id = self.get_unused_object_index(); - ObjectAffine { - attributes: ObjectAttribute::new(), - loan: ObjectLoan { - objects: &self.objects, - index: id, - }, - aff_loan: None, - } - } - - /// Get an unused affine matrix. Panics if more than 32 affine matricies are - /// obtained. - pub fn get_affine(&self) -> AffineMatrix { - AffineMatrix { - attributes: AffineMatrixAttributes { - p_a: 0, - p_b: 0, - p_c: 0, - p_d: 0, - }, - loan: self - .affines - .get_next_free() - .expect("there are no affines avaliable"), + _ => {} } } } -#[cfg(test)] -mod tests { - #[test_case] - fn get_and_release_object(gba: &mut crate::Gba) { - let objs = gba.display.object.get(); - - let _o1 = { - let o0 = objs.get_object_standard(); - let o1 = objs.get_object_standard(); - assert_eq!(o0.loan.index, 0); - assert_eq!(o1.loan.index, 1); - o1 - }; - - let o0 = objs.get_object_standard(); - assert_eq!(o0.loan.index, 0); - let o2 = objs.get_object_affine(); - assert_eq!(o2.loan.index, 2); - } - - #[test_case] - fn get_and_release_affine(gba: &mut crate::Gba) { - let objs = gba.display.object.get(); - - let _a1 = { - let a0 = objs.get_affine(); - let a1 = objs.get_affine(); - assert_eq!(a0.loan.my_index, 0); - assert_eq!(a1.loan.my_index, 1); - a1 - }; - - let a0 = objs.get_affine(); - assert_eq!(a0.loan.my_index, 0); - let a2 = objs.get_affine(); - assert_eq!(a2.loan.my_index, 2); - } -} +impl ObjectController {} From 93d994360e9596cfa7dffbba09406626511f4813 Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 8 Feb 2022 23:53:55 +0000 Subject: [PATCH 10/51] make alloc and dealloc public --- agb/src/agb_alloc/block_allocator.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agb/src/agb_alloc/block_allocator.rs b/agb/src/agb_alloc/block_allocator.rs index 82cb8443..c029522e 100644 --- a/agb/src/agb_alloc/block_allocator.rs +++ b/agb/src/agb_alloc/block_allocator.rs @@ -112,7 +112,7 @@ impl BlockAllocator { }); } - unsafe fn alloc(&self, layout: Layout) -> Option> { + pub unsafe fn alloc(&self, layout: Layout) -> Option> { // find a block that this current request fits in let full_layout = Block::either_layout(layout); @@ -156,7 +156,7 @@ impl BlockAllocator { }) } - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + pub unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { let new_layout = Block::either_layout(layout).pad_to_align(); free(|key| { let mut state = self.state.borrow(*key).borrow_mut(); From 6a2c9d88263a88ff50e322054b43869bc3d2d99c Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 8 Feb 2022 23:54:09 +0000 Subject: [PATCH 11/51] add some sprite allocation --- agb/src/display/object.rs | 76 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 626acbba..f85474ad 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -1,7 +1,11 @@ +use core::alloc::Layout; use core::cell::RefCell; +use core::ptr::NonNull; use hashbrown::{hash_map::Entry, HashMap}; +const BYTES_PER_TILE_4BPP: usize = 32; + use super::palette16::Palette16; use super::{palette16, Priority, DISPLAY_CONTROL}; use crate::agb_alloc::block_allocator::BlockAllocator; @@ -27,6 +31,45 @@ const TILE_SPRITE: MemoryMapped1DArray = pub struct Sprite { palette: &'static Palette16, data: &'static [u8], + size: Size, +} + +#[derive(Clone, Copy)] +pub enum Size { + // stored as attr0 attr1 + S8x8 = 0b00_00, + S16x16 = 0b00_01, + S32x32 = 0b00_10, + S64x64 = 0b00_11, + + S16x8 = 0b01_00, + S32x8 = 0b01_01, + S32x16 = 0b01_10, + S64x32 = 0b01_11, + + S8x16 = 0b10_00, + S8x32 = 0b10_01, + S16x32 = 0b10_10, + S32x64 = 0b10_11, +} + +impl Size { + fn number_of_tiles(self) -> usize { + match self { + S8x8 => 1, + S16x16 => 4, + S32x32 => 16, + S64x64 => 64, + S16x8 => 2, + S32x8 => 4, + S32x16 => 8, + S64x32 => 32, + S8x16 => 2, + S8x32 => 4, + S16x32 => 8, + S32x64 => 32, + } + } } struct SpriteBorrow<'a> { @@ -39,6 +82,15 @@ struct Storage { count: u16, } +impl Storage { + fn from_ptr(d: NonNull) -> Self { + Self { + location: (((d.as_ptr() as usize) - 0x06010000) / BYTES_PER_TILE_4BPP) as u16, + count: 1, + } + } +} + pub struct Object<'a> { sprite: SpriteBorrow<'a>, } @@ -59,6 +111,15 @@ pub struct ObjectController {} #[derive(Clone, Copy, PartialEq, Eq, Hash)] struct SpriteId(usize); +impl SpriteId { + fn get_sprite(self) -> &'static Sprite { + // # Safety + // This must be constructed using the get_id of a sprite, so + // they are always valid and always static + unsafe { (self.0 as *const Sprite).as_ref().unwrap_unchecked() } + } +} + /// The palette id is a thin wrapper around the pointer to the palette in rom /// and is therefore a unique reference to a palette #[derive(Copy, Clone, PartialEq, Eq, Hash)] @@ -68,6 +129,9 @@ impl Sprite { fn get_id(&'static self) -> SpriteId { SpriteId(self as *const _ as usize) } + const fn layout(&self) -> Layout { + Layout::from_size_align(self.size.number_of_tiles() * BYTES_PER_TILE_4BPP, 8).unwrap() + } } impl SpriteController { @@ -81,8 +145,18 @@ impl SpriteController { controller: &self.inner, }) } else { - // allocate a new sprite + // layout is non zero sized, so this is safe to call + + let dest = unsafe { SPRITE_ALLOCATOR.alloc(sprite.layout())? }; + inner.sprite.insert(id, Storage::from_ptr(dest)); + + // need to consider palette todo!(); + + Some(SpriteBorrow { + id, + controller: &self.inner, + }) } } } From d764ff9900f95f1f92bcbaa33624b4732d20b460 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 12 Feb 2022 14:40:31 +0000 Subject: [PATCH 12/51] allocate palettes too --- agb/src/display/object.rs | 97 +++++++++++++++++++++++++++++++----- agb/src/display/palette16.rs | 1 + 2 files changed, 85 insertions(+), 13 deletions(-) diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index f85474ad..848f12ea 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -21,6 +21,13 @@ static SPRITE_ALLOCATOR: BlockAllocator = unsafe { }) }; +static PALETTE_ALLOCATOR: BlockAllocator = unsafe { + BlockAllocator::new(StartEnd { + start: || 0x0500_0200, + end: || 0x0500_0400, + }) +}; + const OBJECT_ATTRIBUTE_MEMORY: MemoryMapped1DArray = unsafe { MemoryMapped1DArray::new(0x0700_0000) }; const PALETTE_SPRITE: MemoryMapped1DArray = @@ -74,6 +81,8 @@ impl Size { struct SpriteBorrow<'a> { id: SpriteId, + sprite_location: u16, + palette_location: u16, controller: &'a RefCell, } @@ -83,12 +92,21 @@ struct Storage { } impl Storage { - fn from_ptr(d: NonNull) -> Self { + fn from_sprite_ptr(d: NonNull) -> Self { Self { location: (((d.as_ptr() as usize) - 0x06010000) / BYTES_PER_TILE_4BPP) as u16, count: 1, } } + fn from_palette_ptr(d: NonNull) -> Self { + Self { + location: ((d.as_ptr() as usize - 0x0500_0200) / Palette16::layout().size()) as u16, + count: 1, + } + } + fn to_palette_ptr(&self) -> *mut u8 { + (self.location as usize * Palette16::layout().size() + 0x0500_0200) as *mut u8 + } } pub struct Object<'a> { @@ -125,6 +143,21 @@ impl SpriteId { #[derive(Copy, Clone, PartialEq, Eq, Hash)] struct PaletteId(usize); +impl PaletteId { + fn get_palette(self) -> &'static Palette16 { + unsafe { (self.0 as *const Palette16).as_ref().unwrap_unchecked() } + } +} + +impl Palette16 { + fn get_id(&'static self) -> PaletteId { + PaletteId(self as *const _ as usize) + } + const fn layout() -> Layout { + Layout::new::() + } +} + impl Sprite { fn get_id(&'static self) -> SpriteId { SpriteId(self as *const _ as usize) @@ -140,49 +173,87 @@ impl SpriteController { let id = sprite.get_id(); if let Some(storage) = inner.sprite.get_mut(&id) { storage.count += 1; + let palette_location = inner.get_palette(sprite.palette).unwrap(); Some(SpriteBorrow { id, + palette_location, + sprite_location: storage.location, controller: &self.inner, }) } else { // layout is non zero sized, so this is safe to call let dest = unsafe { SPRITE_ALLOCATOR.alloc(sprite.layout())? }; - inner.sprite.insert(id, Storage::from_ptr(dest)); + let palette_location = inner.get_palette(sprite.palette); + let palette_location = match palette_location { + Some(a) => a, + None => { + unsafe { SPRITE_ALLOCATOR.dealloc(dest.as_ptr(), sprite.layout()) } + return None; + } + }; - // need to consider palette - todo!(); + let storage = Storage::from_sprite_ptr(dest); + inner.sprite.insert(id, storage); Some(SpriteBorrow { id, controller: &self.inner, + palette_location, + sprite_location: storage.location, }) } } } +impl SpriteControllerInner { + fn get_palette(&mut self, palette: &'static Palette16) -> Option { + let id = palette.get_id(); + if let Some(storage) = self.palette.get_mut(&id) { + storage.count += 1; + Some(storage.location) + } else { + let dest = unsafe { PALETTE_ALLOCATOR.alloc(Palette16::layout())? }; + let storage = Storage::from_palette_ptr(dest); + self.palette.insert(id, storage); + + Some(storage.location) + } + } + + fn return_palette(&mut self, palette: &'static Palette16) { + let id = palette.get_id(); + self.palette + .entry(id) + .and_replace_entry_with(|_, mut storage| { + storage.count -= 1; + if storage.count == 0 { + unsafe { + PALETTE_ALLOCATOR.dealloc(storage.to_palette_ptr(), Palette16::layout()); + } + None + } else { + Some(storage) + } + }); + } +} + impl<'a> Drop for SpriteBorrow<'a> { fn drop(&mut self) { let inner = self.controller.borrow_mut(); - let entry = inner + inner .sprite .entry(self.id) .and_replace_entry_with(|_, mut storage| { storage.count -= 1; if storage.count == 0 { + inner.return_palette(self.id.get_sprite().palette); None } else { Some(storage) } }); - - match entry { - Entry::Vacant(_) => { - // free the underlying resource. - // palette might be unused too. - } - _ => {} - } } } diff --git a/agb/src/display/palette16.rs b/agb/src/display/palette16.rs index f8a4bab2..9dc79687 100644 --- a/agb/src/display/palette16.rs +++ b/agb/src/display/palette16.rs @@ -1,3 +1,4 @@ +#[repr(C)] #[derive(Clone)] pub struct Palette16 { pub(crate) colours: [u16; 16], From 836143e1f47c5bcaaff83b57d5f065cbf219360d Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 12 Feb 2022 14:44:18 +0000 Subject: [PATCH 13/51] copy the sprite and palette data --- agb/src/display/object.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 848f12ea..9890fcac 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -193,6 +193,11 @@ impl SpriteController { } }; + unsafe { + dest.as_ptr() + .copy_from(sprite.data.as_ptr(), sprite.data.len()) + } + let storage = Storage::from_sprite_ptr(dest); inner.sprite.insert(id, storage); @@ -214,6 +219,13 @@ impl SpriteControllerInner { Some(storage.location) } else { let dest = unsafe { PALETTE_ALLOCATOR.alloc(Palette16::layout())? }; + + unsafe { + dest.as_ptr() + .cast::() + .copy_from(palette.colours.as_ptr(), palette.colours.len()) + } + let storage = Storage::from_palette_ptr(dest); self.palette.insert(id, storage); From a1b67e107fc98f4b21e14925d8c60d2f75d7e4df Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 12 Feb 2022 21:20:53 +0000 Subject: [PATCH 14/51] object controller implementations --- agb/src/display/object.rs | 145 ++++++++++++++++++++++++++------------ 1 file changed, 101 insertions(+), 44 deletions(-) diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 9890fcac..98ce4cdf 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -1,3 +1,4 @@ +use alloc::vec::Vec; use core::alloc::Layout; use core::cell::RefCell; use core::ptr::NonNull; @@ -10,35 +11,30 @@ use super::palette16::Palette16; use super::{palette16, Priority, DISPLAY_CONTROL}; use crate::agb_alloc::block_allocator::BlockAllocator; use crate::agb_alloc::bump_allocator::StartEnd; -use crate::bitarray::Bitarray; use crate::fixnum::Vector2D; -use crate::memory_mapped::MemoryMapped1DArray; static SPRITE_ALLOCATOR: BlockAllocator = unsafe { BlockAllocator::new(StartEnd { - start: || 0x06010000, - end: || 0x06010000 + 1024 * 8 * 4, + start: || TILE_SPRITE, + end: || TILE_SPRITE + 1024 * 8 * 4, }) }; static PALETTE_ALLOCATOR: BlockAllocator = unsafe { BlockAllocator::new(StartEnd { - start: || 0x0500_0200, - end: || 0x0500_0400, + start: || PALETTE_SPRITE, + end: || PALETTE_SPRITE + 0x200, }) }; -const OBJECT_ATTRIBUTE_MEMORY: MemoryMapped1DArray = - unsafe { MemoryMapped1DArray::new(0x0700_0000) }; -const PALETTE_SPRITE: MemoryMapped1DArray = - unsafe { MemoryMapped1DArray::new(0x0500_0200) }; -const TILE_SPRITE: MemoryMapped1DArray = - unsafe { MemoryMapped1DArray::new(0x06010000) }; +const PALETTE_SPRITE: usize = 0x0500_0200; +const TILE_SPRITE: usize = 0x06010000; +const OBJECT_ATTRIBUTE_MEMORY: usize = 0x0700_0000; pub struct Sprite { - palette: &'static Palette16, - data: &'static [u8], - size: Size, + pub palette: &'static Palette16, + pub data: &'static [u8], + pub size: Size, } #[derive(Clone, Copy)] @@ -61,31 +57,32 @@ pub enum Size { } impl Size { - fn number_of_tiles(self) -> usize { + const fn number_of_tiles(self) -> usize { match self { - S8x8 => 1, - S16x16 => 4, - S32x32 => 16, - S64x64 => 64, - S16x8 => 2, - S32x8 => 4, - S32x16 => 8, - S64x32 => 32, - S8x16 => 2, - S8x32 => 4, - S16x32 => 8, - S32x64 => 32, + Size::S8x8 => 1, + Size::S16x16 => 4, + Size::S32x32 => 16, + Size::S64x64 => 64, + Size::S16x8 => 2, + Size::S32x8 => 4, + Size::S32x16 => 8, + Size::S64x32 => 32, + Size::S8x16 => 2, + Size::S8x32 => 4, + Size::S16x32 => 8, + Size::S32x64 => 32, } } } -struct SpriteBorrow<'a> { +pub struct SpriteBorrow<'a> { id: SpriteId, sprite_location: u16, palette_location: u16, controller: &'a RefCell, } +#[derive(Clone, Copy)] struct Storage { location: u16, count: u16, @@ -94,23 +91,24 @@ struct Storage { impl Storage { fn from_sprite_ptr(d: NonNull) -> Self { Self { - location: (((d.as_ptr() as usize) - 0x06010000) / BYTES_PER_TILE_4BPP) as u16, + location: (((d.as_ptr() as usize) - TILE_SPRITE) / BYTES_PER_TILE_4BPP) as u16, count: 1, } } fn from_palette_ptr(d: NonNull) -> Self { Self { - location: ((d.as_ptr() as usize - 0x0500_0200) / Palette16::layout().size()) as u16, + location: ((d.as_ptr() as usize - PALETTE_SPRITE) / Palette16::layout().size()) as u16, count: 1, } } - fn to_palette_ptr(&self) -> *mut u8 { - (self.location as usize * Palette16::layout().size() + 0x0500_0200) as *mut u8 + fn as_palette_ptr(&self) -> *mut u8 { + (self.location as usize * Palette16::layout().size() + PALETTE_SPRITE) as *mut u8 } } -pub struct Object<'a> { +pub struct Object<'a, 'b> { sprite: SpriteBorrow<'a>, + loan: Loan<'b>, } struct SpriteControllerInner { @@ -122,7 +120,52 @@ pub struct SpriteController { inner: RefCell, } -pub struct ObjectController {} +struct Loan<'a> { + index: u8, + free_list: &'a RefCell>, +} + +impl Drop for Loan<'_> { + fn drop(&mut self) { + let mut list = self.free_list.borrow_mut(); + list.push(self.index); + } +} + +pub struct ObjectController { + free_affine_matricies: RefCell>, + free_objects: RefCell>, + sprite_controller: SpriteController, +} + +impl ObjectController { + pub(crate) fn new() -> Self { + Self { + free_objects: RefCell::new((0..128).collect()), + free_affine_matricies: RefCell::new((0..32).collect()), + sprite_controller: SpriteController::new(), + } + } + + pub fn get_object<'a, 'b>(&'a self, sprite: SpriteBorrow<'b>) -> Option> { + let mut inner = self.free_objects.borrow_mut(); + let loan = Loan { + index: inner.pop()?, + free_list: &self.free_objects, + }; + Some(Object { sprite, loan }) + } + + pub fn get_sprite(&self, sprite: &'static Sprite) -> Option { + self.sprite_controller.get_sprite(sprite) + } +} + +impl<'a, 'b> Object<'a, 'b> { + pub fn set_sprite(&'a mut self, sprite: SpriteBorrow<'a>) { + self.sprite = sprite; + } +} /// The Sprite Id is a thin wrapper around the pointer to the sprite in /// rom and is therefore a unique identifier to a sprite @@ -162,22 +205,28 @@ impl Sprite { fn get_id(&'static self) -> SpriteId { SpriteId(self as *const _ as usize) } - const fn layout(&self) -> Layout { + fn layout(&self) -> Layout { Layout::from_size_align(self.size.number_of_tiles() * BYTES_PER_TILE_4BPP, 8).unwrap() } } impl SpriteController { + fn new() -> Self { + Self { + inner: RefCell::new(SpriteControllerInner::new()), + } + } fn get_sprite(&self, sprite: &'static Sprite) -> Option { - let inner = self.inner.borrow_mut(); + let mut inner = self.inner.borrow_mut(); let id = sprite.get_id(); if let Some(storage) = inner.sprite.get_mut(&id) { storage.count += 1; + let location = storage.location; let palette_location = inner.get_palette(sprite.palette).unwrap(); Some(SpriteBorrow { id, palette_location, - sprite_location: storage.location, + sprite_location: location, controller: &self.inner, }) } else { @@ -212,6 +261,12 @@ impl SpriteController { } impl SpriteControllerInner { + fn new() -> Self { + Self { + palette: HashMap::new(), + sprite: HashMap::new(), + } + } fn get_palette(&mut self, palette: &'static Palette16) -> Option { let id = palette.get_id(); if let Some(storage) = self.palette.get_mut(&id) { @@ -241,7 +296,7 @@ impl SpriteControllerInner { storage.count -= 1; if storage.count == 0 { unsafe { - PALETTE_ALLOCATOR.dealloc(storage.to_palette_ptr(), Palette16::layout()); + PALETTE_ALLOCATOR.dealloc(storage.as_palette_ptr(), Palette16::layout()); } None } else { @@ -253,20 +308,22 @@ impl SpriteControllerInner { impl<'a> Drop for SpriteBorrow<'a> { fn drop(&mut self) { - let inner = self.controller.borrow_mut(); - inner + let mut inner = self.controller.borrow_mut(); + let entry = inner .sprite .entry(self.id) .and_replace_entry_with(|_, mut storage| { storage.count -= 1; if storage.count == 0 { - inner.return_palette(self.id.get_sprite().palette); None } else { Some(storage) } }); + + match entry { + Entry::Occupied(_) => {} + Entry::Vacant(_) => inner.return_palette(self.id.get_sprite().palette), + } } } - -impl ObjectController {} From 2a7b350f57acd2b18c60cac398bd198e56da96e8 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 12 Feb 2022 23:57:10 +0000 Subject: [PATCH 15/51] objects now exist and work --- agb/Cargo.lock | 28 +++++ agb/Cargo.toml | 1 + agb/examples/chicken.rs | 117 +++++++++++++++----- agb/src/display/mod.rs | 8 +- agb/src/display/object.rs | 219 ++++++++++++++++++++++++++++++++++---- agb/src/syscall.rs | 92 ++++++++-------- 6 files changed, 370 insertions(+), 95 deletions(-) diff --git a/agb/Cargo.lock b/agb/Cargo.lock index cf0c71a0..bd85fdac 100644 --- a/agb/Cargo.lock +++ b/agb/Cargo.lock @@ -25,6 +25,7 @@ dependencies = [ "bare-metal", "bitflags", "hashbrown", + "modular-bitfield", ] [[package]] @@ -192,6 +193,27 @@ dependencies = [ "adler", ] +[[package]] +name = "modular-bitfield" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74" +dependencies = [ + "modular-bitfield-impl", + "static_assertions", +] + +[[package]] +name = "modular-bitfield-impl" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "num-integer" version = "0.1.44" @@ -289,6 +311,12 @@ dependencies = [ "syn", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "syn" version = "1.0.86" diff --git a/agb/Cargo.toml b/agb/Cargo.toml index 0dcfbade..ddbb7945 100644 --- a/agb/Cargo.toml +++ b/agb/Cargo.toml @@ -27,6 +27,7 @@ agb_macros = { version = "0.1.0", path = "../agb-macros" } agb_fixnum = { version = "0.1.0", path = "../agb-fixnum" } bare-metal = "1.0" hashbrown = "0.12.0" +modular-bitfield = "0.11.2" [package.metadata.docs.rs] default-target = "thumbv6m-none-eabi" diff --git a/agb/examples/chicken.rs b/agb/examples/chicken.rs index 359a4811..9113ea56 100644 --- a/agb/examples/chicken.rs +++ b/agb/examples/chicken.rs @@ -2,7 +2,12 @@ #![no_main] use agb::{ - display::{background::Map, object::ObjectStandard, HEIGHT, WIDTH}, + display::{ + background::Map, + object::{Object, ObjectController, Size, Sprite}, + palette16::Palette16, + HEIGHT, WIDTH, + }, input::Button, }; use core::convert::TryInto; @@ -15,7 +20,7 @@ enum State { } struct Character<'a> { - object: ObjectStandard<'a>, + object: Object<'a, 'a>, position: Vector2D, velocity: Vector2D, } @@ -30,8 +35,8 @@ fn tile_is_collidable(tile: u16) -> bool { masked == 0 || masked == 4 } -fn frame_ranger(count: u32, start: u32, end: u32, delay: u32) -> u16 { - (((count / delay) % (end + 1 - start)) + start) as u16 +fn frame_ranger(count: u32, start: u32, end: u32, delay: u32) -> usize { + (((count / delay) % (end + 1 - start)) + start) as usize } #[agb::entry] @@ -54,14 +59,11 @@ fn main(mut gba: agb::Gba) -> ! { background.show(); background.commit(); - let mut object = gba.display.object.get(); + let object = gba.display.object.get(); - object.set_sprite_palette_raw(&CHICKEN_PALETTE); - object.set_sprite_tilemap(&CHICKEN_TILES); - - object.enable(); + let sprite = object.get_sprite(&ChickenSprites[0]).unwrap(); let mut chicken = Character { - object: object.get_object_standard(), + object: object.get_object(sprite).unwrap(), position: Vector2D { x: (6 * 8) << 8, y: ((7 * 8) - 4) << 8, @@ -69,7 +71,6 @@ fn main(mut gba: agb::Gba) -> ! { velocity: Vector2D { x: 0, y: 0 }, }; - chicken.object.set_tile_id(0); chicken .object .set_x((chicken.position.x >> 8).try_into().unwrap()); @@ -120,14 +121,19 @@ fn main(mut gba: agb::Gba) -> ! { } restrict_to_screen(&mut chicken); - update_chicken_object(&mut chicken, state, frame_count); + update_chicken_object(&mut chicken, &object, state, frame_count); // Commit the chicken to vram chicken.object.commit(); } } -fn update_chicken_object(chicken: &mut Character, state: State, frame_count: u32) { +fn update_chicken_object<'a>( + chicken: &'_ mut Character<'a>, + object: &'a ObjectController, + state: State, + frame_count: u32, +) { if chicken.velocity.x > 1 { chicken.object.set_hflip(false); } else if chicken.velocity.x < -1 { @@ -136,18 +142,24 @@ fn update_chicken_object(chicken: &mut Character, state: State, frame_count: u32 match state { State::Ground => { if chicken.velocity.x.abs() > 1 << 4 { + chicken.object.set_sprite( + object + .get_sprite(&ChickenSprites[frame_ranger(frame_count, 1, 3, 10)]) + .unwrap(), + ); + } else { chicken .object - .set_tile_id(frame_ranger(frame_count, 1, 3, 10)); - } else { - chicken.object.set_tile_id(0); + .set_sprite(object.get_sprite(&ChickenSprites[0]).unwrap()); } } State::Upwards => {} State::Flapping => { - chicken - .object - .set_tile_id(frame_ranger(frame_count, 4, 5, 5)); + chicken.object.set_sprite( + object + .get_sprite(&ChickenSprites[frame_ranger(frame_count, 4, 5, 5)]) + .unwrap(), + ); } } @@ -232,16 +244,65 @@ fn handle_collision( // Below is the data for the sprites -static CHICKEN_TILES: [u32; 8 * 6] = [ - 0x01100000, 0x11100000, 0x01100010, 0x01111110, 0x01111110, 0x00001000, 0x00001000, 0x00011000, - 0x01100000, 0x11100000, 0x01100010, 0x01111110, 0x01111110, 0x00010100, 0x00100100, 0x00000010, - 0x01100000, 0x11100000, 0x01100010, 0x01111110, 0x01111110, 0x00011000, 0x00100110, 0x00100000, - 0x01100000, 0x11100000, 0x01100010, 0x01111110, 0x01111110, 0x00011000, 0x00011100, 0x00001000, - 0x01100000, 0x11111100, 0x01111010, 0x01111110, 0x01111110, 0x00011000, 0x00010000, 0x00000000, - 0x01100000, 0x11100000, 0x01111110, 0x01111110, 0x01111110, 0x00011000, 0x00010000, 0x00000000, -]; +static ChickenPalette: Palette16 = + Palette16::new([0x7C1E, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); -static CHICKEN_PALETTE: [u16; 1] = [0x7C1E]; +static ChickenSprites: &[Sprite] = &[ + Sprite { + palette: &ChickenPalette, + data: &[ + 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x00, 0x10, 0x01, 0x10, 0x11, + 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x10, 0x01, 0x00, + ], + size: Size::S8x8, + }, + Sprite { + palette: &ChickenPalette, + data: &[ + 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x00, 0x10, 0x01, 0x10, 0x11, + 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x10, 0x00, + 0x10, 0x00, 0x00, 0x00, + ], + size: Size::S8x8, + }, + Sprite { + palette: &ChickenPalette, + data: &[ + 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x00, 0x10, 0x01, 0x10, 0x11, + 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x01, 0x00, 0x10, 0x01, 0x10, 0x00, + 0x00, 0x00, 0x10, 0x00, + ], + size: Size::S8x8, + }, + Sprite { + palette: &ChickenPalette, + data: &[ + 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x00, 0x10, 0x01, 0x10, 0x11, + 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x01, 0x00, 0x00, 0x11, 0x01, 0x00, + 0x00, 0x10, 0x00, 0x00, + ], + size: Size::S8x8, + }, + Sprite { + palette: &ChickenPalette, + data: &[ + 0x00, 0x00, 0x10, 0x01, 0x00, 0x11, 0x11, 0x11, 0x10, 0x10, 0x11, 0x01, 0x10, 0x11, + 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, + ], + size: Size::S8x8, + }, + Sprite { + palette: &ChickenPalette, + data: &[ + 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x11, 0x11, 0x01, 0x10, 0x11, + 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, + ], + size: Size::S8x8, + }, +]; static MAP_TILES: [u32; 8 * 17] = [ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, diff --git a/agb/src/display/mod.rs b/agb/src/display/mod.rs index e4ce5206..fff057bb 100644 --- a/agb/src/display/mod.rs +++ b/agb/src/display/mod.rs @@ -1,9 +1,10 @@ use crate::memory_mapped::MemoryMapped; use bitflags::bitflags; +use modular_bitfield::BitfieldSpecifier; use video::Video; -use self::object::ObjectControl; +use self::object::ObjectController; /// Graphics mode 0. Four regular backgrounds. pub mod background; @@ -69,8 +70,8 @@ pub struct Display { pub struct ObjectDistribution {} impl ObjectDistribution { - pub fn get(&mut self) -> ObjectControl { - ObjectControl::new() + pub fn get(&mut self) -> ObjectController { + ObjectController::new() } } @@ -109,6 +110,7 @@ pub fn busy_wait_for_vblank() { while VCOUNT.get() < 160 {} } +#[derive(BitfieldSpecifier)] pub enum Priority { P0 = 0, P1 = 1, diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 98ce4cdf..9b4b1f78 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -2,6 +2,8 @@ use alloc::vec::Vec; use core::alloc::Layout; use core::cell::RefCell; use core::ptr::NonNull; +use modular_bitfield::prelude::{B10, B2, B3, B4, B5, B8, B9}; +use modular_bitfield::{bitfield, BitfieldSpecifier}; use hashbrown::{hash_map::Entry, HashMap}; @@ -13,6 +15,8 @@ use crate::agb_alloc::block_allocator::BlockAllocator; use crate::agb_alloc::bump_allocator::StartEnd; use crate::fixnum::Vector2D; +use attributes::*; + static SPRITE_ALLOCATOR: BlockAllocator = unsafe { BlockAllocator::new(StartEnd { start: || TILE_SPRITE, @@ -73,6 +77,9 @@ impl Size { Size::S32x64 => 32, } } + const fn shape_size(self) -> (u8, u8) { + (self as u8 >> 2, self as u8 & 0b11) + } } pub struct SpriteBorrow<'a> { @@ -104,11 +111,33 @@ impl Storage { fn as_palette_ptr(&self) -> *mut u8 { (self.location as usize * Palette16::layout().size() + PALETTE_SPRITE) as *mut u8 } + fn as_sprite_ptr(&self) -> *mut u8 { + (self.location as usize * BYTES_PER_TILE_4BPP + TILE_SPRITE) as *mut u8 + } +} + +struct Attributes { + a0: ObjectAttribute0, + a1s: ObjectAttribute1Standard, + a1a: ObjectAttribute1Affine, + a2: ObjectAttribute2, +} + +impl Attributes { + fn new() -> Self { + Self { + a0: ObjectAttribute0::new(), + a1s: ObjectAttribute1Standard::new(), + a1a: ObjectAttribute1Affine::new(), + a2: ObjectAttribute2::new(), + } + } } pub struct Object<'a, 'b> { sprite: SpriteBorrow<'a>, loan: Loan<'b>, + attrs: Attributes, } struct SpriteControllerInner { @@ -140,6 +169,17 @@ pub struct ObjectController { impl ObjectController { pub(crate) fn new() -> Self { + DISPLAY_CONTROL.set_bits(1, 1, 0x6); + DISPLAY_CONTROL.set_bits(1, 1, 0xC); + + for i in 0..128 { + unsafe { + (OBJECT_ATTRIBUTE_MEMORY as *mut u16) + .add(i * 4) + .write_volatile(0b10 << 8) + } + } + Self { free_objects: RefCell::new((0..128).collect()), free_affine_matricies: RefCell::new((0..32).collect()), @@ -153,7 +193,11 @@ impl ObjectController { index: inner.pop()?, free_list: &self.free_objects, }; - Some(Object { sprite, loan }) + Some(Object { + sprite, + loan, + attrs: Attributes::new(), + }) } pub fn get_sprite(&self, sprite: &'static Sprite) -> Option { @@ -161,10 +205,80 @@ impl ObjectController { } } +impl Drop for Object<'_, '_> { + fn drop(&mut self) { + self.attrs.a0.set_object_mode(ObjectMode::Disabled); + self.commit(); + } +} + impl<'a, 'b> Object<'a, 'b> { - pub fn set_sprite(&'a mut self, sprite: SpriteBorrow<'a>) { + pub fn set_sprite(&'_ mut self, sprite: SpriteBorrow<'a>) { + self.attrs.a2.set_tile_index(sprite.sprite_location); + let shape_size = sprite.id.get_sprite().size.shape_size(); + self.attrs.a0.set_shape(shape_size.0); + self.attrs.a1a.set_size(shape_size.1); + self.attrs.a1s.set_size(shape_size.1); self.sprite = sprite; } + + pub fn show(&mut self) -> &mut Self { + self.attrs.a0.set_object_mode(ObjectMode::Normal); + + self + } + + pub fn set_hflip(&mut self, flip: bool) -> &mut Self { + self.attrs.a1s.set_horizontal_flip(flip); + self + } + + pub fn set_vflip(&mut self, flip: bool) -> &mut Self { + self.attrs.a1s.set_vertical_flip(flip); + self + } + + pub fn set_x(&mut self, x: u16) -> &mut Self { + self.attrs.a1a.set_x(x as u16); + self.attrs.a1s.set_x(x as u16); + self + } + + pub fn set_y(&mut self, y: u16) -> &mut Self { + self.attrs.a0.set_y(y as u8); + + self + } + + pub fn set_position(&mut self, position: Vector2D) -> &mut Self { + self.attrs.a0.set_y(position.y as u8); + self.attrs.a1a.set_x(position.x as u16); + self.attrs.a1s.set_x(position.x as u16); + self + } + + pub fn commit(&self) { + let mode = self.attrs.a0.object_mode(); + let attrs: [[u8; 2]; 3] = match mode { + ObjectMode::Normal => [ + self.attrs.a0.into_bytes(), + self.attrs.a1s.into_bytes(), + self.attrs.a2.into_bytes(), + ], + _ => [ + self.attrs.a0.into_bytes(), + self.attrs.a1a.into_bytes(), + self.attrs.a2.into_bytes(), + ], + }; + + unsafe { + let attrs: [u8; 6] = core::mem::transmute(attrs); + (OBJECT_ATTRIBUTE_MEMORY as *mut u8) + .add(self.loan.index as usize * (4 * 2)) + .copy_from(attrs.as_ptr(), attrs.len()) + }; + } } /// The Sprite Id is a thin wrapper around the pointer to the sprite in @@ -288,6 +402,26 @@ impl SpriteControllerInner { } } + fn return_sprite(&mut self, sprite: &'static Sprite) { + let entry = self + .sprite + .entry(sprite.get_id()) + .and_replace_entry_with(|_, mut storage| { + storage.count -= 1; + if storage.count == 0 { + unsafe { SPRITE_ALLOCATOR.dealloc(storage.as_sprite_ptr(), sprite.layout()) } + None + } else { + Some(storage) + } + }); + + match entry { + Entry::Occupied(_) => {} + Entry::Vacant(_) => self.return_palette(sprite.palette), + } + } + fn return_palette(&mut self, palette: &'static Palette16) { let id = palette.get_id(); self.palette @@ -309,21 +443,70 @@ impl SpriteControllerInner { impl<'a> Drop for SpriteBorrow<'a> { fn drop(&mut self) { let mut inner = self.controller.borrow_mut(); - let entry = inner - .sprite - .entry(self.id) - .and_replace_entry_with(|_, mut storage| { - storage.count -= 1; - if storage.count == 0 { - None - } else { - Some(storage) - } - }); - - match entry { - Entry::Occupied(_) => {} - Entry::Vacant(_) => inner.return_palette(self.id.get_sprite().palette), - } + inner.return_sprite(self.id.get_sprite()) + } +} + +#[derive(BitfieldSpecifier, Clone, Copy)] +enum ObjectMode { + Normal, + Affine, + Disabled, + AffineDouble, +} + +#[derive(BitfieldSpecifier, Clone, Copy)] +#[bits = 2] +enum GraphicsMode { + Normal, + AlphaBlending, + Window, +} + +#[derive(BitfieldSpecifier, Clone, Copy)] +enum ColourMode { + Four, + Eight, +} + +#[allow(dead_code)] +mod attributes { + use super::*; + #[bitfield] + #[derive(Clone, Copy)] + pub(super) struct ObjectAttribute0 { + pub y: B8, + pub object_mode: ObjectMode, + pub graphics_mode: GraphicsMode, + pub mosaic: bool, + pub colour_mode: ColourMode, + pub shape: B2, + } + + #[bitfield] + #[derive(Clone, Copy)] + pub(super) struct ObjectAttribute1Standard { + pub x: B9, + #[skip] + __: B3, + pub horizontal_flip: bool, + pub vertical_flip: bool, + pub size: B2, + } + + #[bitfield] + #[derive(Clone, Copy)] + pub(super) struct ObjectAttribute1Affine { + pub x: B9, + pub affine_index: B5, + pub size: B2, + } + + #[bitfield] + #[derive(Clone, Copy)] + pub(super) struct ObjectAttribute2 { + pub tile_index: B10, + pub priority: Priority, + pub palete_bank: B4, } } diff --git a/agb/src/syscall.rs b/agb/src/syscall.rs index bf271981..805e2469 100644 --- a/agb/src/syscall.rs +++ b/agb/src/syscall.rs @@ -1,6 +1,6 @@ use core::arch::asm; -use crate::display::object::AffineMatrixAttributes; +// use crate::display::object::AffineMatrixAttributes; use crate::fixnum::Num; #[allow(non_snake_case)] @@ -113,55 +113,55 @@ pub fn arc_tan2(x: i16, y: i32) -> i16 { result } -pub fn affine_matrix( - x_scale: Num, - y_scale: Num, - rotation: u8, -) -> AffineMatrixAttributes { - let mut result = AffineMatrixAttributes { - p_a: 0, - p_b: 0, - p_c: 0, - p_d: 0, - }; +// pub fn affine_matrix( +// x_scale: Num, +// y_scale: Num, +// rotation: u8, +// ) -> AffineMatrixAttributes { +// let mut result = AffineMatrixAttributes { +// p_a: 0, +// p_b: 0, +// p_c: 0, +// p_d: 0, +// }; - #[allow(dead_code)] - #[repr(C, packed)] - struct Input { - x_scale: i16, - y_scale: i16, - rotation: u16, - } +// #[allow(dead_code)] +// #[repr(C, packed)] +// struct Input { +// x_scale: i16, +// y_scale: i16, +// rotation: u16, +// } - let input = Input { - y_scale: x_scale.to_raw(), - x_scale: y_scale.to_raw(), - rotation: rotation as u16, - }; +// let input = Input { +// y_scale: x_scale.to_raw(), +// x_scale: y_scale.to_raw(), +// rotation: rotation as u16, +// }; - unsafe { - asm!("swi 0x0F", - in("r0") &input as *const Input as usize, - in("r1") &mut result as *mut AffineMatrixAttributes as usize, - in("r2") 1, - in("r3") 2, - ) - } +// unsafe { +// asm!("swi 0x0F", +// in("r0") &input as *const Input as usize, +// in("r1") &mut result as *mut AffineMatrixAttributes as usize, +// in("r2") 1, +// in("r3") 2, +// ) +// } - result -} +// result +// } -#[cfg(test)] -mod tests { - use super::*; +// #[cfg(test)] +// mod tests { +// use super::*; - #[test_case] - fn affine(_gba: &mut crate::Gba) { - // expect identity matrix - let one: Num = 1.into(); +// #[test_case] +// fn affine(_gba: &mut crate::Gba) { +// // expect identity matrix +// let one: Num = 1.into(); - let aff = affine_matrix(one, one, 0); - assert_eq!(aff.p_a, one.to_raw()); - assert_eq!(aff.p_d, one.to_raw()); - } -} +// let aff = affine_matrix(one, one, 0); +// assert_eq!(aff.p_a, one.to_raw()); +// assert_eq!(aff.p_d, one.to_raw()); +// } +// } From ae681019bb8fc0eb65d20f34d8949ddbc3588da0 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sun, 13 Feb 2022 14:49:39 +0000 Subject: [PATCH 16/51] set palette location on sprite change --- agb/src/display/object.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 9b4b1f78..ea215817 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -216,6 +216,7 @@ impl<'a, 'b> Object<'a, 'b> { pub fn set_sprite(&'_ mut self, sprite: SpriteBorrow<'a>) { self.attrs.a2.set_tile_index(sprite.sprite_location); let shape_size = sprite.id.get_sprite().size.shape_size(); + self.attrs.a2.set_palete_bank(sprite.palette_location as u8); self.attrs.a0.set_shape(shape_size.0); self.attrs.a1a.set_size(shape_size.1); self.attrs.a1s.set_size(shape_size.1); From 8792146ddb8064305391f753eb8e2061054661a0 Mon Sep 17 00:00:00 2001 From: Corwin Date: Mon, 14 Feb 2022 20:57:11 +0000 Subject: [PATCH 17/51] use correct copys --- agb/src/display/object.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index ea215817..b8770b52 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -277,7 +277,7 @@ impl<'a, 'b> Object<'a, 'b> { let attrs: [u8; 6] = core::mem::transmute(attrs); (OBJECT_ATTRIBUTE_MEMORY as *mut u8) .add(self.loan.index as usize * (4 * 2)) - .copy_from(attrs.as_ptr(), attrs.len()) + .copy_from_nonoverlapping(attrs.as_ptr(), attrs.len()) }; } } @@ -359,7 +359,7 @@ impl SpriteController { unsafe { dest.as_ptr() - .copy_from(sprite.data.as_ptr(), sprite.data.len()) + .copy_from_nonoverlapping(sprite.data.as_ptr(), sprite.data.len()) } let storage = Storage::from_sprite_ptr(dest); @@ -393,7 +393,7 @@ impl SpriteControllerInner { unsafe { dest.as_ptr() .cast::() - .copy_from(palette.colours.as_ptr(), palette.colours.len()) + .copy_from_nonoverlapping(palette.colours.as_ptr(), palette.colours.len()) } let storage = Storage::from_palette_ptr(dest); From 9714b8a3ca97fd091692cee20f2438c37a082805 Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 15 Feb 2022 21:28:21 +0000 Subject: [PATCH 18/51] types required for aseprite import --- agb-image-converter/Cargo.toml | 3 +- agb-image-converter/src/aseprite.rs | 56 +++++++++++++++++++++++++++++ agb-image-converter/src/lib.rs | 1 + 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 agb-image-converter/src/aseprite.rs diff --git a/agb-image-converter/Cargo.toml b/agb-image-converter/Cargo.toml index f26d4630..52fdf639 100644 --- a/agb-image-converter/Cargo.toml +++ b/agb-image-converter/Cargo.toml @@ -15,4 +15,5 @@ toml = "0.5.8" serde = { version = "1.0", features = ["derive"] } syn = "1.0.86" proc-macro2 = "1.0.36" -quote = "1.0.15" \ No newline at end of file +quote = "1.0.15" +serde_json = "1.0" \ No newline at end of file diff --git a/agb-image-converter/src/aseprite.rs b/agb-image-converter/src/aseprite.rs new file mode 100644 index 00000000..5efa51e6 --- /dev/null +++ b/agb-image-converter/src/aseprite.rs @@ -0,0 +1,56 @@ +use serde::Deserialize; + +#[derive(Deserialize)] +pub struct Aseprite { + pub frames: Vec, + pub meta: Meta, +} + +#[derive(Deserialize)] +pub struct Meta { + pub app: String, + pub version: String, + pub image: String, + pub format: String, + pub size: Size, + pub scale: String, + #[serde(rename = "frameTags")] + pub frame_tags: Vec, +} + +#[derive(Deserialize)] +pub struct Size { + pub w: u32, + pub h: u32, +} + +#[derive(Deserialize, Clone, Copy)] +#[serde(rename_all = "lowercase")] +pub enum Direction { + Forward, + Backward, + Pingpong, +} + +#[derive(Deserialize)] + +pub struct FrameTag { + pub name: String, + pub from: u32, + pub to: u32, + pub direction: Direction, +} + +#[derive(Deserialize)] +pub struct Frame { + pub frame: Frame2, + pub trimmed: bool, +} + +#[derive(Deserialize)] +pub struct Frame2 { + pub x: u32, + pub y: u32, + pub w: u32, + pub h: u32, +} diff --git a/agb-image-converter/src/lib.rs b/agb-image-converter/src/lib.rs index 7d6df7e8..bedcf034 100644 --- a/agb-image-converter/src/lib.rs +++ b/agb-image-converter/src/lib.rs @@ -5,6 +5,7 @@ use std::path::Path; use quote::{format_ident, quote}; +mod aseprite; mod colour; mod config; mod image_loader; From e462d35febaff20610d580f26d9b412e3b5c6408 Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 15 Feb 2022 21:28:42 +0000 Subject: [PATCH 19/51] lock file update --- agb-image-converter/Cargo.lock | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/agb-image-converter/Cargo.lock b/agb-image-converter/Cargo.lock index f0089b29..71a2c9ee 100644 --- a/agb-image-converter/Cargo.lock +++ b/agb-image-converter/Cargo.lock @@ -22,6 +22,7 @@ dependencies = [ "proc-macro2", "quote", "serde", + "serde_json", "syn", "toml", ] @@ -95,6 +96,12 @@ dependencies = [ "png", ] +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + [[package]] name = "miniz_oxide" version = "0.5.1" @@ -175,6 +182,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + [[package]] name = "serde" version = "1.0.136" @@ -195,6 +208,17 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "syn" version = "1.0.86" From 3cc30a2bcd9a9ca5dca8f7186ea8c65bb167e85b Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 15 Feb 2022 21:29:03 +0000 Subject: [PATCH 20/51] satisfy linter --- agb-image-converter/src/palette16.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agb-image-converter/src/palette16.rs b/agb-image-converter/src/palette16.rs index 0c7e3451..18adc83c 100644 --- a/agb-image-converter/src/palette16.rs +++ b/agb-image-converter/src/palette16.rs @@ -151,12 +151,12 @@ impl Palette16Optimiser { let mut a_colour_is_used = false; for current_palette in unsatisfied_palettes { - if palette.union_length(¤t_palette) > MAX_COLOURS_PER_PALETTE { + if palette.union_length(current_palette) > MAX_COLOURS_PER_PALETTE { continue; } for colour in ¤t_palette.colours { - if palette.colours.contains(&colour) { + if palette.colours.contains(colour) { continue; } From 9095daebd8d7162e282aa3acb8149efee1a9f2b5 Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 15 Feb 2022 21:29:16 +0000 Subject: [PATCH 21/51] satisfy linter --- agb-image-converter/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb-image-converter/src/lib.rs b/agb-image-converter/src/lib.rs index bedcf034..ccf19f79 100644 --- a/agb-image-converter/src/lib.rs +++ b/agb-image-converter/src/lib.rs @@ -57,7 +57,7 @@ pub fn include_gfx(input: TokenStream) -> TokenStream { let images = config.images(); let image_code = images.iter().map(|(image_name, &image)| { - convert_image(image, parent, &image_name, &config.crate_prefix()) + convert_image(image, parent, image_name, &config.crate_prefix()) }); let module = quote! { From da9a15402ec8db1965f9cd370761c885ffb5d49e Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 15 Feb 2022 21:30:34 +0000 Subject: [PATCH 22/51] use more efficient hash function --- agb/Cargo.toml | 1 + agb/src/display/object.rs | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/agb/Cargo.toml b/agb/Cargo.toml index ddbb7945..ad0ece1a 100644 --- a/agb/Cargo.toml +++ b/agb/Cargo.toml @@ -28,6 +28,7 @@ agb_fixnum = { version = "0.1.0", path = "../agb-fixnum" } bare-metal = "1.0" hashbrown = "0.12.0" modular-bitfield = "0.11.2" +rustc-hash = { version = "1.0", default-features = false } [package.metadata.docs.rs] default-target = "thumbv6m-none-eabi" diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index b8770b52..5b66ec2f 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -1,9 +1,11 @@ use alloc::vec::Vec; use core::alloc::Layout; use core::cell::RefCell; +use core::hash::BuildHasherDefault; use core::ptr::NonNull; use modular_bitfield::prelude::{B10, B2, B3, B4, B5, B8, B9}; use modular_bitfield::{bitfield, BitfieldSpecifier}; +use rustc_hash::FxHasher; use hashbrown::{hash_map::Entry, HashMap}; @@ -141,8 +143,8 @@ pub struct Object<'a, 'b> { } struct SpriteControllerInner { - palette: HashMap, - sprite: HashMap, + palette: HashMap>, + sprite: HashMap>, } pub struct SpriteController { @@ -378,8 +380,8 @@ impl SpriteController { impl SpriteControllerInner { fn new() -> Self { Self { - palette: HashMap::new(), - sprite: HashMap::new(), + palette: HashMap::default(), + sprite: HashMap::default(), } } fn get_palette(&mut self, palette: &'static Palette16) -> Option { From 45823273ac040f87d68416a5fb18335534c348af Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 15 Feb 2022 21:31:23 +0000 Subject: [PATCH 23/51] satisfy linter --- agb/src/display/object.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 5b66ec2f..1adef5e4 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -12,7 +12,7 @@ use hashbrown::{hash_map::Entry, HashMap}; const BYTES_PER_TILE_4BPP: usize = 32; use super::palette16::Palette16; -use super::{palette16, Priority, DISPLAY_CONTROL}; +use super::{Priority, DISPLAY_CONTROL}; use crate::agb_alloc::block_allocator::BlockAllocator; use crate::agb_alloc::bump_allocator::StartEnd; use crate::fixnum::Vector2D; From c1901819af810f5fb628b3aeb79040a49bddb369 Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 15 Feb 2022 21:31:41 +0000 Subject: [PATCH 24/51] unblank screen on object init --- agb/src/display/object.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 1adef5e4..635bb127 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -173,6 +173,7 @@ impl ObjectController { pub(crate) fn new() -> Self { DISPLAY_CONTROL.set_bits(1, 1, 0x6); DISPLAY_CONTROL.set_bits(1, 1, 0xC); + DISPLAY_CONTROL.set_bits(0, 1, 0x7); for i in 0..128 { unsafe { From 80e53d47168b8b2f924ab4740ac2e0e48cbc450b Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 15 Feb 2022 21:31:59 +0000 Subject: [PATCH 25/51] volatile writes --- agb/src/display/object.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 635bb127..5be201cd 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -277,10 +277,12 @@ impl<'a, 'b> Object<'a, 'b> { }; unsafe { - let attrs: [u8; 6] = core::mem::transmute(attrs); - (OBJECT_ATTRIBUTE_MEMORY as *mut u8) - .add(self.loan.index as usize * (4 * 2)) - .copy_from_nonoverlapping(attrs.as_ptr(), attrs.len()) + let attrs: [u16; 3] = core::mem::transmute(attrs); + let ptr = (OBJECT_ATTRIBUTE_MEMORY as *mut u16).add(self.loan.index as usize * 4); + + ptr.add(0).write_volatile(attrs[0]); + ptr.add(1).write_volatile(attrs[1]); + ptr.add(2).write_volatile(attrs[2]); }; } } From 16ff9b8ec11cb81acbc623e960514a0d2e514221 Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 15 Feb 2022 21:32:51 +0000 Subject: [PATCH 26/51] types for nice sprite import and usage --- agb/Cargo.lock | 134 ++++++++++++++++++++++++++++++++++++++ agb/Cargo.toml | 1 + agb/src/display/object.rs | 98 ++++++++++++++++++++++++++++ 3 files changed, 233 insertions(+) diff --git a/agb/Cargo.lock b/agb/Cargo.lock index bd85fdac..3ba21947 100644 --- a/agb/Cargo.lock +++ b/agb/Cargo.lock @@ -26,6 +26,8 @@ dependencies = [ "bitflags", "hashbrown", "modular-bitfield", + "phf", + "rustc-hash", ] [[package]] @@ -43,6 +45,7 @@ dependencies = [ "proc-macro2", "quote", "serde", + "serde_json", "syn", "toml", ] @@ -178,6 +181,12 @@ dependencies = [ "png", ] +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + [[package]] name = "libc" version = "0.2.119" @@ -261,6 +270,50 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_macros", + "phf_shared", + "proc-macro-hack", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + [[package]] name = "png" version = "0.17.5" @@ -273,6 +326,18 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + [[package]] name = "proc-macro2" version = "1.0.36" @@ -291,6 +356,58 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + [[package]] name = "serde" version = "1.0.136" @@ -311,6 +428,23 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "siphasher" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a86232ab60fa71287d7f2ddae4a7073f6b7aac33631c3015abb556f08c6d0a3e" + [[package]] name = "static_assertions" version = "1.1.0" diff --git a/agb/Cargo.toml b/agb/Cargo.toml index ad0ece1a..8821c6e4 100644 --- a/agb/Cargo.toml +++ b/agb/Cargo.toml @@ -29,6 +29,7 @@ bare-metal = "1.0" hashbrown = "0.12.0" modular-bitfield = "0.11.2" rustc-hash = { version = "1.0", default-features = false } +phf = { version = "0.10", default-features = false, features = ["macros"] } [package.metadata.docs.rs] default-target = "thumbv6m-none-eabi" diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 5be201cd..35a1bce4 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -2,7 +2,9 @@ use alloc::vec::Vec; use core::alloc::Layout; use core::cell::RefCell; use core::hash::BuildHasherDefault; +use core::ops::Deref; use core::ptr::NonNull; +use core::slice; use modular_bitfield::prelude::{B10, B2, B3, B4, B5, B8, B9}; use modular_bitfield::{bitfield, BitfieldSpecifier}; use rustc_hash::FxHasher; @@ -62,6 +64,77 @@ pub enum Size { S32x64 = 0b10_11, } + +pub struct TagMap(phf::Map<&'static str, Tag>); + +impl TagMap { + pub const fn new(map: phf::Map<&'static str, Tag>) -> Self { + Self(map) + } +} + +impl Deref for TagMap { + type Target = phf::Map<&'static str, Tag>; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[derive(Clone, Copy)] +enum Direction { + Forward, + Backward, + Pingpong, +} + +impl Direction { + const fn from_usize(a: usize) -> Self { + match a { + 0 => Direction::Forward, + 1 => Direction::Backward, + 2 => Direction::Pingpong, + _ => panic!("Invalid direction, this is a bug in image converter or agb"), + } + } +} + +pub struct Tag { + sprites: *const Sprite, + len: usize, + direction: Direction, +} + +impl Tag { + pub fn get_sprites(&self) -> &'static [Sprite] { + unsafe { slice::from_raw_parts(self.sprites, self.len) } + } + + pub fn get_sprite(&self, idx: usize) -> &'static Sprite { + &self.get_sprites()[idx] + } + + pub fn get_animation_sprite(&self, idx: usize) -> &'static Sprite { + let len_sub_1 = self.len - 1; + match self.direction { + Direction::Forward => self.get_sprite(idx % self.len), + Direction::Backward => self.get_sprite(len_sub_1 - (idx % self.len)), + Direction::Pingpong => self.get_sprite( + (((idx + len_sub_1) % (len_sub_1 * 2)) as isize - len_sub_1 as isize).abs() + as usize, + ), + } + } + + pub const fn new(sprites: &'static [Sprite], from: usize, to: usize, direction: usize) -> Self { + assert!(from <= to); + Self { + sprites: &sprites[from] as *const Sprite, + len: to - from + 1, + direction: Direction::from_usize(direction), + } + } +} + impl Size { const fn number_of_tiles(self) -> usize { match self { @@ -82,6 +155,24 @@ impl Size { const fn shape_size(self) -> (u8, u8) { (self as u8 >> 2, self as u8 & 0b11) } + + pub const fn from_width_height(width: usize, height: usize) -> Self { + match (width, height) { + (8, 8) => Size::S8x8, + (16, 16) => Size::S16x16, + (32, 32) => Size::S32x32, + (64, 64) => Size::S64x64, + (16, 8) => Size::S16x8, + (32, 8) => Size::S32x8, + (32, 16) => Size::S32x16, + (64, 32) => Size::S64x32, + (8, 16) => Size::S8x16, + (8, 32) => Size::S8x32, + (16, 32) => Size::S16x32, + (32, 64) => Size::S32x64, + (_, _) => panic!("Bad width and height!"), + } + } } pub struct SpriteBorrow<'a> { @@ -328,6 +419,13 @@ impl Sprite { fn layout(&self) -> Layout { Layout::from_size_align(self.size.number_of_tiles() * BYTES_PER_TILE_4BPP, 8).unwrap() } + pub const fn new(palette: &'static Palette16, data: &'static [u8], size: Size) -> Self { + Self { + palette, + data, + size, + } + } } impl SpriteController { From 73a8d86f6ba912ef3ec4866a2e411ff96a5b8ad1 Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 15 Feb 2022 21:33:48 +0000 Subject: [PATCH 27/51] sprite import proc macro mess --- agb-image-converter/src/lib.rs | 186 ++++++++++++++++++++++++++++++++- 1 file changed, 185 insertions(+), 1 deletion(-) diff --git a/agb-image-converter/src/lib.rs b/agb-image-converter/src/lib.rs index ccf19f79..4e8dd2e7 100644 --- a/agb-image-converter/src/lib.rs +++ b/agb-image-converter/src/lib.rs @@ -1,7 +1,14 @@ +use palette16::Palette16OptimisationResults; use proc_macro::TokenStream; use syn::parse_macro_input; -use std::path::Path; +use std::{ + fs::File, + iter, + path::{Path, PathBuf}, + process::Command, + str, +}; use quote::{format_ident, quote}; @@ -71,6 +78,130 @@ pub fn include_gfx(input: TokenStream) -> TokenStream { TokenStream::from(module) } +#[proc_macro] +pub fn include_aseprite_inner(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as syn::LitStr); + let filename = input.value(); + + let root = std::env::var("CARGO_MANIFEST_DIR").expect("Failed to get cargo manifest dir"); + let path = Path::new(&root).join(&*filename); + + let out_dir = std::env::var("OUT_DIR").expect("Expected OUT_DIR"); + + let output_filename = Path::new(&out_dir).join(&*filename); + let image_output = output_filename.with_extension("png"); + let json_output = output_filename.with_extension("json"); + + let command = Command::new("aseprite") + .args([ + &PathBuf::from("-b"), + &path, + &"--sheet".into(), + &image_output, + &"--format".into(), + &"json-array".into(), + &"--data".into(), + &json_output, + &"--list-tags".into(), + ]) + .output() + .expect("Could not run aseprite"); + assert!( + command.status.success(), + "Aseprite did not complete successfully : {}", + str::from_utf8(&*command.stdout).unwrap_or("Output contains invalid string") + ); + + let json: aseprite::Aseprite = serde_json::from_reader( + File::open(&json_output).expect("The json output from aseprite could not be openned"), + ) + .expect("The output from aseprite could not be decoded"); + + // check that the size of the sprites are valid + + assert!( + json.frames[0].frame.w == json.frames[0].frame.h + && json.frames[0].frame.w.is_power_of_two() + && json.frames[0].frame.w <= 32 + ); + + let image = Image::load_from_file(image_output.as_path()); + + let optimised_results = + optimiser_for_image(&image, json.frames[0].frame.w as usize).optimise_palettes(None); + + let (palette_data, tile_data, assignments) = + palete_tile_data(&optimised_results, json.frames[0].frame.w as usize, &image); + + let palette_data = palette_data.iter().map(|colours| { + quote! { + Palette16::new([ + #(#colours),* + ]) + } + }); + + let mut pre = 0; + let sprites = json + .frames + .iter() + .zip(assignments.iter()) + .map(|(f, assignment)| { + let start: usize = pre; + let end: usize = pre + (f.frame.w as usize / 8) * (f.frame.h as usize / 8) * 32; + let data = &tile_data[start..end]; + pre = end; + let width = f.frame.w as usize; + let height = f.frame.h as usize; + quote! { + Sprite::new( + &PALETTES[#assignment], + &[ + #(#data),* + ], + Size::from_width_height(#width, #height) + ) + } + }); + + let tags = json.meta.frame_tags.iter().map(|tag| { + let start = tag.from as usize; + let end = tag.to as usize; + let direction = tag.direction as usize; + + let name = &tag.name; + assert!(start <= end, "Tag {} has start > end", name); + + quote! { + #name => Tag::new(SPRITES, #start, #end, #direction) + } + }); + + let include_path = path.to_string_lossy(); + + let module = quote! { + const _: &[u8] = include_bytes!(#include_path); + + + const PALETTES: &[Palette16] = &[ + #(#palette_data),* + ]; + + pub const SPRITES: &[Sprite] = &[ + #(#sprites),* + ]; + + const TAGS: &TagMap = &TagMap::new( + phf::phf_map! { + #(#tags),* + } + ); + + }; + + TokenStream::from(module) +} + fn convert_image( settings: &dyn config::Image, parent: &Path, @@ -122,3 +253,56 @@ fn optimiser_for_image(image: &Image, tile_size: usize) -> palette16::Palette16O palette_optimiser } + +fn palete_tile_data( + optimiser: &Palette16OptimisationResults, + tile_size: usize, + image: &Image, +) -> (Vec>, Vec, Vec) { + let palette_data: Vec> = optimiser + .optimised_palettes + .iter() + .map(|palette| { + palette + .clone() + .into_iter() + .map(|colour| colour.to_rgb15()) + .chain(iter::repeat(0)) + .take(16) + .map(|colour| colour as u16) + .collect() + }) + .collect(); + + let tiles_x = image.width / tile_size; + let tiles_y = image.height / tile_size; + + let mut tile_data = vec![]; + + for y in 0..tiles_y { + for x in 0..tiles_x { + let palette_index = optimiser.assignments[y * tiles_x + x]; + let palette = &optimiser.optimised_palettes[palette_index]; + + for inner_y in 0..tile_size / 8 { + for inner_x in 0..tile_size / 8 { + for j in inner_y * 8..inner_y * 8 + 8 { + for i in inner_x * 8..inner_x * 8 + 8 { + let colour = image.colour(x * tile_size + i, y * tile_size + j); + tile_data.push(palette.colour_index(colour)); + } + } + } + } + } + } + + let tile_data = tile_data + .chunks(2) + .map(|chunk| chunk[0] | (chunk[1] << 4)) + .collect(); + + let assignments = optimiser.assignments.clone(); + + (palette_data, tile_data, assignments) +} From efe67423c1a6d1fb26fb2b53f14366642139a1dd Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 15 Feb 2022 21:34:02 +0000 Subject: [PATCH 28/51] use the types --- agb/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/agb/src/lib.rs b/agb/src/lib.rs index db1e0c50..13977637 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -112,6 +112,9 @@ /// ``` pub use agb_image_converter::include_gfx; +pub use agb_image_converter::include_aseprite_inner; +pub use phf; + /// This macro declares the entry point to your game written using `agb`. /// /// It is already included in the template, but your `main` function must be annotated with `#[agb::entry]`, takes 1 argument and never returns. From 25da2a2711915412229f72aa2bfefb4e120568b9 Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 15 Feb 2022 21:34:18 +0000 Subject: [PATCH 29/51] expose the inner proc macro in a macro rules --- agb/src/display/object.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 35a1bce4..b43b0c11 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -64,6 +64,18 @@ pub enum Size { S32x64 = 0b10_11, } +#[macro_export] +macro_rules! include_aseprite { + ($aseprite_path: expr) => {{ + use $crate::display::object::{Size, Sprite, Tag, TagMap}; + use $crate::display::palette16::Palette16; + use $crate::phf; + + $crate::include_aseprite_inner!($aseprite_path); + + (SPRITES, TAGS) + }}; +} pub struct TagMap(phf::Map<&'static str, Tag>); From b3db55330a0df2dd97556de80c451f387b456c5c Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 15 Feb 2022 21:34:25 +0000 Subject: [PATCH 30/51] sprites example --- agb/examples/sprites.rs | 106 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 agb/examples/sprites.rs diff --git a/agb/examples/sprites.rs b/agb/examples/sprites.rs new file mode 100644 index 00000000..3600282e --- /dev/null +++ b/agb/examples/sprites.rs @@ -0,0 +1,106 @@ +#![no_std] +#![no_main] + +extern crate alloc; + +use agb::display::object::{ObjectController, Sprite, TagMap}; +use alloc::vec::Vec; + +const SPRITE_TAGS: (&[Sprite], &TagMap) = + agb::include_aseprite!("../examples/the-purple-night/gfx/objects.aseprite"); +const SPRITES: &[Sprite] = SPRITE_TAGS.0; +const TAG_MAP: &TagMap = SPRITE_TAGS.1; + +fn all_sprites(gfx: &ObjectController) { + let mut input = agb::input::ButtonController::new(); + let mut objs = Vec::new(); + + for y in 0..9 { + for x in 0..14 { + let mut obj = gfx + .get_object(gfx.get_sprite(&SPRITES[0]).unwrap()) + .unwrap(); + obj.show(); + obj.set_position((x * 16 + 8, y * 16 + 8).into()); + objs.push(obj); + } + } + + let mut count = 0; + let mut image = 0; + + let vblank = agb::interrupt::VBlank::get(); + + loop { + vblank.wait_for_vblank(); + input.update(); + + if input.is_just_pressed(agb::input::Button::A) { + break; + } + + count += 1; + + if count % 5 == 0 { + image += 1; + image %= SPRITES.len(); + let objs_len = objs.len(); + for (i, obj) in objs.iter_mut().enumerate() { + let this_image = (image + i * SPRITES.len() / objs_len) % SPRITES.len(); + obj.set_sprite(gfx.get_sprite(&SPRITES[this_image]).unwrap()); + obj.commit(); + } + } + } +} + +fn all_tags(gfx: &ObjectController) { + let mut input = agb::input::ButtonController::new(); + let mut objs = Vec::new(); + + for (i, v) in TAG_MAP.values().enumerate() { + let x = (i % 14) as i32; + let y = (i / 14) as i32; + let mut obj = gfx + .get_object(gfx.get_sprite(v.get_sprite(0)).unwrap()) + .unwrap(); + obj.show(); + obj.set_position((x * 16 + 8, y * 16 + 8).into()); + objs.push((obj, v)); + } + + let mut count = 0; + let mut image = 0; + + let vblank = agb::interrupt::VBlank::get(); + + loop { + vblank.wait_for_vblank(); + + input.update(); + + if input.is_just_pressed(agb::input::Button::A) { + break; + } + + count += 1; + + if count % 5 == 0 { + image += 1; + for (obj, tag) in objs.iter_mut() { + obj.set_sprite(gfx.get_sprite(tag.get_animation_sprite(image)).unwrap()); + obj.commit(); + } + } + } +} + +#[agb::entry] +fn main(mut gba: agb::Gba) -> ! { + let gfx = gba.display.object.get(); + + loop { + all_tags(&gfx); + all_sprites(&gfx); + } +} From 6e5cee1e3f584805a33398ac00ad6fd6e1be5a01 Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 22 Feb 2022 20:16:34 +0000 Subject: [PATCH 31/51] support loading multiple --- agb-image-converter/src/aseprite.rs | 53 ++++++- agb-image-converter/src/image_loader.rs | 4 + agb-image-converter/src/lib.rs | 192 +++++++++++++----------- 3 files changed, 158 insertions(+), 91 deletions(-) diff --git a/agb-image-converter/src/aseprite.rs b/agb-image-converter/src/aseprite.rs index 5efa51e6..cd2db333 100644 --- a/agb-image-converter/src/aseprite.rs +++ b/agb-image-converter/src/aseprite.rs @@ -1,3 +1,11 @@ +use std::{ + fs::File, + path::{Path, PathBuf}, + process::Command, + str, +}; + +use image::DynamicImage; use serde::Deserialize; #[derive(Deserialize)] @@ -32,8 +40,7 @@ pub enum Direction { Pingpong, } -#[derive(Deserialize)] - +#[derive(Deserialize, Clone)] pub struct FrameTag { pub name: String, pub from: u32, @@ -41,16 +48,54 @@ pub struct FrameTag { pub direction: Direction, } -#[derive(Deserialize)] +#[derive(Deserialize, Clone)] pub struct Frame { pub frame: Frame2, pub trimmed: bool, } -#[derive(Deserialize)] +#[derive(Deserialize, Clone)] pub struct Frame2 { pub x: u32, pub y: u32, pub w: u32, pub h: u32, } + +pub fn generate_from_file(filename: &str) -> (Aseprite, DynamicImage) { + let out_dir = std::env::var("OUT_DIR").expect("Expected OUT_DIR"); + + let output_filename = Path::new(&out_dir).join(&*filename); + let image_output = output_filename.with_extension("png"); + let json_output = output_filename.with_extension("json"); + + let command = Command::new("aseprite") + .args([ + &PathBuf::from("-b"), + &PathBuf::from(filename), + &"--sheet".into(), + &image_output, + &"--format".into(), + &"json-array".into(), + &"--data".into(), + &json_output, + &"--list-tags".into(), + ]) + .output() + .expect("Could not run aseprite"); + assert!( + command.status.success(), + "Aseprite did not complete successfully : {}", + str::from_utf8(&*command.stdout).unwrap_or("Output contains invalid string") + ); + + let json: Aseprite = serde_json::from_reader( + File::open(&json_output).expect("The json output from aseprite could not be openned"), + ) + .expect("The output from aseprite could not be decoded"); + + ( + json, + image::open(image_output).expect("Image should be readable"), + ) +} diff --git a/agb-image-converter/src/image_loader.rs b/agb-image-converter/src/image_loader.rs index aa3f5921..52a3b04f 100644 --- a/agb-image-converter/src/image_loader.rs +++ b/agb-image-converter/src/image_loader.rs @@ -13,6 +13,10 @@ pub(crate) struct Image { impl Image { pub fn load_from_file(image_path: &path::Path) -> Self { let img = image::open(image_path).expect("Expected image to exist"); + Self::load_from_dyn_image(img) + } + + pub fn load_from_dyn_image(img: image::DynamicImage) -> Self { let (width, height) = img.dimensions(); let width = width as usize; diff --git a/agb-image-converter/src/lib.rs b/agb-image-converter/src/lib.rs index 4e8dd2e7..35e658d8 100644 --- a/agb-image-converter/src/lib.rs +++ b/agb-image-converter/src/lib.rs @@ -1,16 +1,12 @@ use palette16::Palette16OptimisationResults; use proc_macro::TokenStream; -use syn::parse_macro_input; +use proc_macro2::Literal; +use syn::parse::Parser; +use syn::{parse_macro_input, punctuated::Punctuated, LitStr}; -use std::{ - fs::File, - iter, - path::{Path, PathBuf}, - process::Command, - str, -}; +use std::{iter, path::Path, str}; -use quote::{format_ident, quote}; +use quote::{format_ident, quote, ToTokens}; mod aseprite; mod colour; @@ -78,60 +74,65 @@ pub fn include_gfx(input: TokenStream) -> TokenStream { TokenStream::from(module) } +use quote::TokenStreamExt; +struct ByteString<'a>(&'a [u8]); +impl ToTokens for ByteString<'_> { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + tokens.append(Literal::byte_string(self.0)); + } +} + #[proc_macro] pub fn include_aseprite_inner(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as syn::LitStr); - let filename = input.value(); + let parser = Punctuated::::parse_separated_nonempty; + let parsed = match parser.parse(input) { + Ok(e) => e, + Err(e) => return e.to_compile_error().into(), + }; + + let mut optimiser = palette16::Palette16Optimiser::new(); + let mut images = Vec::new(); + let mut frames = Vec::new(); + let mut tags = Vec::new(); let root = std::env::var("CARGO_MANIFEST_DIR").expect("Failed to get cargo manifest dir"); - let path = Path::new(&root).join(&*filename); - let out_dir = std::env::var("OUT_DIR").expect("Expected OUT_DIR"); + let filenames: Vec = parsed + .iter() + .map(|s| s.value()) + .map(|s| { + Path::new(&root) + .join(&*s) + .as_path() + .to_string_lossy() + .into_owned() + }) + .collect(); - let output_filename = Path::new(&out_dir).join(&*filename); - let image_output = output_filename.with_extension("png"); - let json_output = output_filename.with_extension("json"); + for filename in filenames.iter() { + let (json, image) = aseprite::generate_from_file(filename); + let tile_size = json.frames[0].frame.w; - let command = Command::new("aseprite") - .args([ - &PathBuf::from("-b"), - &path, - &"--sheet".into(), - &image_output, - &"--format".into(), - &"json-array".into(), - &"--data".into(), - &json_output, - &"--list-tags".into(), - ]) - .output() - .expect("Could not run aseprite"); - assert!( - command.status.success(), - "Aseprite did not complete successfully : {}", - str::from_utf8(&*command.stdout).unwrap_or("Output contains invalid string") - ); + for frame in json.frames.iter() { + assert!(frame.frame.w == tile_size); + assert!( + frame.frame.w == frame.frame.h + && frame.frame.w.is_power_of_two() + && frame.frame.w <= 32 + ); + } - let json: aseprite::Aseprite = serde_json::from_reader( - File::open(&json_output).expect("The json output from aseprite could not be openned"), - ) - .expect("The output from aseprite could not be decoded"); + let image = Image::load_from_dyn_image(image); - // check that the size of the sprites are valid + add_to_optimiser(&mut optimiser, &image, tile_size as usize); + images.push(image); + frames.push(json.frames.clone()); + tags.push(json.meta.frame_tags.clone()); + } - assert!( - json.frames[0].frame.w == json.frames[0].frame.h - && json.frames[0].frame.w.is_power_of_two() - && json.frames[0].frame.w <= 32 - ); + let optimised_results = optimiser.optimise_palettes(None); - let image = Image::load_from_file(image_output.as_path()); - - let optimised_results = - optimiser_for_image(&image, json.frames[0].frame.w as usize).optimise_palettes(None); - - let (palette_data, tile_data, assignments) = - palete_tile_data(&optimised_results, json.frames[0].frame.w as usize, &image); + let (palette_data, tile_data, assignments) = palete_tile_data(&optimised_results, &images); let palette_data = palette_data.iter().map(|colours| { quote! { @@ -142,45 +143,54 @@ pub fn include_aseprite_inner(input: TokenStream) -> TokenStream { }); let mut pre = 0; - let sprites = json - .frames + let sprites = frames .iter() + .flatten() .zip(assignments.iter()) .map(|(f, assignment)| { let start: usize = pre; let end: usize = pre + (f.frame.w as usize / 8) * (f.frame.h as usize / 8) * 32; - let data = &tile_data[start..end]; + let data = ByteString(&tile_data[start..end]); pre = end; let width = f.frame.w as usize; let height = f.frame.h as usize; quote! { Sprite::new( &PALETTES[#assignment], - &[ - #(#data),* - ], + #data, Size::from_width_height(#width, #height) ) } }); - let tags = json.meta.frame_tags.iter().map(|tag| { - let start = tag.from as usize; - let end = tag.to as usize; - let direction = tag.direction as usize; + let tags = tags + .iter() + .enumerate() + .map(|(i, tag)| { + tag.iter().map(move |tag| (i, tag)).map(|(i, tag)| { + let offset: usize = frames[0..i].iter().map(|s| s.len()).sum(); + let start = tag.from as usize + offset; + let end = tag.to as usize + offset; + let direction = tag.direction as usize; - let name = &tag.name; - assert!(start <= end, "Tag {} has start > end", name); + let name = &tag.name; + assert!(start <= end, "Tag {} has start > end", name); + quote! { + #name => Tag::new(SPRITES, #start, #end, #direction) + } + }) + }) + .flatten(); + + let include_paths = filenames.iter().map(|s| { quote! { - #name => Tag::new(SPRITES, #start, #end, #direction) + const _: &[u8] = include_bytes!(#s); } }); - let include_path = path.to_string_lossy(); - let module = quote! { - const _: &[u8] = include_bytes!(#include_path); + #(#include_paths)* const PALETTES: &[Palette16] = &[ @@ -230,11 +240,19 @@ fn convert_image( } fn optimiser_for_image(image: &Image, tile_size: usize) -> palette16::Palette16Optimiser { + let mut palette_optimiser = palette16::Palette16Optimiser::new(); + add_to_optimiser(&mut palette_optimiser, image, tile_size); + palette_optimiser +} + +fn add_to_optimiser( + palette_optimiser: &mut palette16::Palette16Optimiser, + image: &Image, + tile_size: usize, +) { let tiles_x = image.width / tile_size; let tiles_y = image.height / tile_size; - let mut palette_optimiser = palette16::Palette16Optimiser::new(); - for y in 0..tiles_y { for x in 0..tiles_x { let mut palette = palette16::Palette16::new(); @@ -250,14 +268,11 @@ fn optimiser_for_image(image: &Image, tile_size: usize) -> palette16::Palette16O palette_optimiser.add_palette(palette); } } - - palette_optimiser } fn palete_tile_data( optimiser: &Palette16OptimisationResults, - tile_size: usize, - image: &Image, + images: &[Image], ) -> (Vec>, Vec, Vec) { let palette_data: Vec> = optimiser .optimised_palettes @@ -274,22 +289,25 @@ fn palete_tile_data( }) .collect(); - let tiles_x = image.width / tile_size; - let tiles_y = image.height / tile_size; + let mut tile_data = Vec::new(); - let mut tile_data = vec![]; + for image in images { + let tile_size = image.height; + let tiles_x = image.width / tile_size; + let tiles_y = image.height / tile_size; - for y in 0..tiles_y { - for x in 0..tiles_x { - let palette_index = optimiser.assignments[y * tiles_x + x]; - let palette = &optimiser.optimised_palettes[palette_index]; + for y in 0..tiles_y { + for x in 0..tiles_x { + let palette_index = optimiser.assignments[y * tiles_x + x]; + let palette = &optimiser.optimised_palettes[palette_index]; - for inner_y in 0..tile_size / 8 { - for inner_x in 0..tile_size / 8 { - for j in inner_y * 8..inner_y * 8 + 8 { - for i in inner_x * 8..inner_x * 8 + 8 { - let colour = image.colour(x * tile_size + i, y * tile_size + j); - tile_data.push(palette.colour_index(colour)); + for inner_y in 0..tile_size / 8 { + for inner_x in 0..tile_size / 8 { + for j in inner_y * 8..inner_y * 8 + 8 { + for i in inner_x * 8..inner_x * 8 + 8 { + let colour = image.colour(x * tile_size + i, y * tile_size + j); + tile_data.push(palette.colour_index(colour)); + } } } } From ecf31fa833bcd4f9d8448cd054cd1a3d34319318 Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 22 Feb 2022 20:16:41 +0000 Subject: [PATCH 32/51] load multiple --- agb/examples/sprites.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/agb/examples/sprites.rs b/agb/examples/sprites.rs index 3600282e..868472cf 100644 --- a/agb/examples/sprites.rs +++ b/agb/examples/sprites.rs @@ -6,8 +6,10 @@ extern crate alloc; use agb::display::object::{ObjectController, Sprite, TagMap}; use alloc::vec::Vec; -const SPRITE_TAGS: (&[Sprite], &TagMap) = - agb::include_aseprite!("../examples/the-purple-night/gfx/objects.aseprite"); +const SPRITE_TAGS: (&[Sprite], &TagMap) = agb::include_aseprite!( + "../examples/the-purple-night/gfx/objects.aseprite", + "../examples/the-purple-night/gfx/boss.aseprite" +); const SPRITES: &[Sprite] = SPRITE_TAGS.0; const TAG_MAP: &TagMap = SPRITE_TAGS.1; @@ -59,13 +61,14 @@ fn all_tags(gfx: &ObjectController) { let mut objs = Vec::new(); for (i, v) in TAG_MAP.values().enumerate() { - let x = (i % 14) as i32; - let y = (i / 14) as i32; - let mut obj = gfx - .get_object(gfx.get_sprite(v.get_sprite(0)).unwrap()) - .unwrap(); + let x = (i % 7) as i32; + let y = (i / 7) as i32; + let sprite = v.get_sprite(0); + let (size_x, size_y) = sprite.size().to_width_height(); + let (size_x, size_y) = (size_x as i32, size_y as i32); + let mut obj = gfx.get_object(gfx.get_sprite(sprite).unwrap()).unwrap(); obj.show(); - obj.set_position((x * 16 + 8, y * 16 + 8).into()); + obj.set_position((x * 32 + 16 - size_x / 2, y * 32 + 16 - size_y / 2).into()); objs.push((obj, v)); } From 6bafa3ebab300b3aae5360150f15387ca3e736cc Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 22 Feb 2022 20:16:51 +0000 Subject: [PATCH 33/51] use public api --- agb/examples/chicken.rs | 60 ++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/agb/examples/chicken.rs b/agb/examples/chicken.rs index 9113ea56..1e5882b4 100644 --- a/agb/examples/chicken.rs +++ b/agb/examples/chicken.rs @@ -248,60 +248,60 @@ static ChickenPalette: Palette16 = Palette16::new([0x7C1E, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); static ChickenSprites: &[Sprite] = &[ - Sprite { - palette: &ChickenPalette, - data: &[ + Sprite::new( + &ChickenPalette, + &[ 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x00, 0x10, 0x01, 0x10, 0x11, 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, ], - size: Size::S8x8, - }, - Sprite { - palette: &ChickenPalette, - data: &[ + Size::S8x8, + ), + Sprite::new( + &ChickenPalette, + &[ 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x00, 0x10, 0x01, 0x10, 0x11, 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, ], - size: Size::S8x8, - }, - Sprite { - palette: &ChickenPalette, - data: &[ + Size::S8x8, + ), + Sprite::new( + &ChickenPalette, + &[ 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x00, 0x10, 0x01, 0x10, 0x11, 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x01, 0x00, 0x10, 0x01, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, ], - size: Size::S8x8, - }, - Sprite { - palette: &ChickenPalette, - data: &[ + Size::S8x8, + ), + Sprite::new( + &ChickenPalette, + &[ 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x00, 0x10, 0x01, 0x10, 0x11, 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x01, 0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, ], - size: Size::S8x8, - }, - Sprite { - palette: &ChickenPalette, - data: &[ + Size::S8x8, + ), + Sprite::new( + &ChickenPalette, + &[ 0x00, 0x00, 0x10, 0x01, 0x00, 0x11, 0x11, 0x11, 0x10, 0x10, 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, ], - size: Size::S8x8, - }, - Sprite { - palette: &ChickenPalette, - data: &[ + Size::S8x8, + ), + Sprite::new( + &ChickenPalette, + &[ 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x11, 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, ], - size: Size::S8x8, - }, + Size::S8x8, + ), ]; static MAP_TILES: [u32; 8 * 17] = [ From 02e6194073b45cdc598985a590b397e227fe6cbf Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 22 Feb 2022 20:16:58 +0000 Subject: [PATCH 34/51] make fields private --- agb/src/display/object.rs | 40 +++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index b43b0c11..f65f1dfa 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -40,12 +40,12 @@ const TILE_SPRITE: usize = 0x06010000; const OBJECT_ATTRIBUTE_MEMORY: usize = 0x0700_0000; pub struct Sprite { - pub palette: &'static Palette16, - pub data: &'static [u8], - pub size: Size, + palette: &'static Palette16, + data: &'static [u8], + size: Size, } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq, Eq)] pub enum Size { // stored as attr0 attr1 S8x8 = 0b00_00, @@ -66,12 +66,12 @@ pub enum Size { #[macro_export] macro_rules! include_aseprite { - ($aseprite_path: expr) => {{ + ($($aseprite_path: expr),*) => {{ use $crate::display::object::{Size, Sprite, Tag, TagMap}; use $crate::display::palette16::Palette16; use $crate::phf; - $crate::include_aseprite_inner!($aseprite_path); + $crate::include_aseprite_inner!($($aseprite_path),*); (SPRITES, TAGS) }}; @@ -185,6 +185,23 @@ impl Size { (_, _) => panic!("Bad width and height!"), } } + + pub const fn to_width_height(self) -> (usize, usize) { + match self { + Size::S8x8 => (8, 8), + Size::S16x16 => (16, 16), + Size::S32x32 => (32, 32), + Size::S64x64 => (64, 64), + Size::S16x8 => (16, 8), + Size::S32x8 => (32, 8), + Size::S32x16 => (32, 16), + Size::S64x32 => (64, 32), + Size::S8x16 => (8, 16), + Size::S8x32 => (8, 32), + Size::S16x32 => (16, 32), + Size::S32x64 => (32, 64), + } + } } pub struct SpriteBorrow<'a> { @@ -438,6 +455,9 @@ impl Sprite { size, } } + pub const fn size(&self) -> Size { + self.size + } } impl SpriteController { @@ -519,8 +539,7 @@ impl SpriteControllerInner { } fn return_sprite(&mut self, sprite: &'static Sprite) { - let entry = self - .sprite + self.sprite .entry(sprite.get_id()) .and_replace_entry_with(|_, mut storage| { storage.count -= 1; @@ -532,10 +551,7 @@ impl SpriteControllerInner { } }); - match entry { - Entry::Occupied(_) => {} - Entry::Vacant(_) => self.return_palette(sprite.palette), - } + self.return_palette(sprite.palette) } fn return_palette(&mut self, palette: &'static Palette16) { From e67f1ff45f7586a142112f348298c4777e5eec42 Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 22 Feb 2022 20:24:32 +0000 Subject: [PATCH 35/51] output to the correct location --- agb-image-converter/src/aseprite.rs | 5 +++-- agb-image-converter/src/lib.rs | 12 ++++-------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/agb-image-converter/src/aseprite.rs b/agb-image-converter/src/aseprite.rs index cd2db333..72a6cbb4 100644 --- a/agb-image-converter/src/aseprite.rs +++ b/agb-image-converter/src/aseprite.rs @@ -62,10 +62,11 @@ pub struct Frame2 { pub h: u32, } -pub fn generate_from_file(filename: &str) -> (Aseprite, DynamicImage) { +pub fn generate_from_file(filename: &Path) -> (Aseprite, DynamicImage) { let out_dir = std::env::var("OUT_DIR").expect("Expected OUT_DIR"); - let output_filename = Path::new(&out_dir).join(&*filename); + let output_filename = Path::new(&out_dir).join(filename.file_name().unwrap()); + let image_output = output_filename.with_extension("png"); let json_output = output_filename.with_extension("json"); diff --git a/agb-image-converter/src/lib.rs b/agb-image-converter/src/lib.rs index 35e658d8..4c1a93ae 100644 --- a/agb-image-converter/src/lib.rs +++ b/agb-image-converter/src/lib.rs @@ -4,6 +4,7 @@ use proc_macro2::Literal; use syn::parse::Parser; use syn::{parse_macro_input, punctuated::Punctuated, LitStr}; +use std::path::PathBuf; use std::{iter, path::Path, str}; use quote::{format_ident, quote, ToTokens}; @@ -97,16 +98,10 @@ pub fn include_aseprite_inner(input: TokenStream) -> TokenStream { let root = std::env::var("CARGO_MANIFEST_DIR").expect("Failed to get cargo manifest dir"); - let filenames: Vec = parsed + let filenames: Vec = parsed .iter() .map(|s| s.value()) - .map(|s| { - Path::new(&root) - .join(&*s) - .as_path() - .to_string_lossy() - .into_owned() - }) + .map(|s| Path::new(&root).join(&*s)) .collect(); for filename in filenames.iter() { @@ -184,6 +179,7 @@ pub fn include_aseprite_inner(input: TokenStream) -> TokenStream { .flatten(); let include_paths = filenames.iter().map(|s| { + let s = s.as_os_str().to_string_lossy(); quote! { const _: &[u8] = include_bytes!(#s); } From 84e6af5d2f1ba72720ab0c8d5c0ba2987b8dcc00 Mon Sep 17 00:00:00 2001 From: Corwin Date: Wed, 23 Feb 2022 18:05:48 +0000 Subject: [PATCH 36/51] load aseprite files directly with asefile --- agb-image-converter/Cargo.lock | 78 +++++++++++++------ agb-image-converter/Cargo.toml | 2 +- agb-image-converter/src/aseprite.rs | 117 +++++----------------------- agb-image-converter/src/lib.rs | 51 +++++------- agb/Cargo.lock | 78 +++++++++++++------ 5 files changed, 150 insertions(+), 176 deletions(-) diff --git a/agb-image-converter/Cargo.lock b/agb-image-converter/Cargo.lock index 71a2c9ee..531ab8ed 100644 --- a/agb-image-converter/Cargo.lock +++ b/agb-image-converter/Cargo.lock @@ -18,15 +18,29 @@ checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" name = "agb_image_converter" version = "0.6.0" dependencies = [ + "asefile", "image", "proc-macro2", "quote", "serde", - "serde_json", "syn", "toml", ] +[[package]] +name = "asefile" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0d5f7de918fd4cb18249819fc4bd27f6a5dbfbc9dcb271727f27dacf17ce880" +dependencies = [ + "bitflags", + "byteorder", + "flate2", + "image", + "log", + "nohash", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -81,6 +95,18 @@ dependencies = [ "adler32", ] +[[package]] +name = "flate2" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" +dependencies = [ + "cfg-if", + "crc32fast", + "libc", + "miniz_oxide 0.4.4", +] + [[package]] name = "image" version = "0.24.1" @@ -97,10 +123,19 @@ dependencies = [ ] [[package]] -name = "itoa" -version = "1.0.1" +name = "libc" +version = "0.2.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] [[package]] name = "miniz_oxide" @@ -111,6 +146,22 @@ dependencies = [ "adler", ] +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + +[[package]] +name = "nohash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0f889fb66f7acdf83442c35775764b51fed3c606ab9cee51500dbde2cf528ca" + [[package]] name = "num-integer" version = "0.1.44" @@ -161,7 +212,7 @@ dependencies = [ "bitflags", "crc32fast", "deflate", - "miniz_oxide", + "miniz_oxide 0.3.7", ] [[package]] @@ -182,12 +233,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "ryu" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" - [[package]] name = "serde" version = "1.0.136" @@ -208,17 +253,6 @@ dependencies = [ "syn", ] -[[package]] -name = "serde_json" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" -dependencies = [ - "itoa", - "ryu", - "serde", -] - [[package]] name = "syn" version = "1.0.86" diff --git a/agb-image-converter/Cargo.toml b/agb-image-converter/Cargo.toml index 52fdf639..63f41d98 100644 --- a/agb-image-converter/Cargo.toml +++ b/agb-image-converter/Cargo.toml @@ -16,4 +16,4 @@ serde = { version = "1.0", features = ["derive"] } syn = "1.0.86" proc-macro2 = "1.0.36" quote = "1.0.15" -serde_json = "1.0" \ No newline at end of file +asefile = "0.3.2" diff --git a/agb-image-converter/src/aseprite.rs b/agb-image-converter/src/aseprite.rs index 72a6cbb4..694c0cfd 100644 --- a/agb-image-converter/src/aseprite.rs +++ b/agb-image-converter/src/aseprite.rs @@ -1,102 +1,23 @@ -use std::{ - fs::File, - path::{Path, PathBuf}, - process::Command, - str, -}; +use std::path::Path; +use asefile::{AsepriteFile, Tag}; use image::DynamicImage; -use serde::Deserialize; -#[derive(Deserialize)] -pub struct Aseprite { - pub frames: Vec, - pub meta: Meta, -} - -#[derive(Deserialize)] -pub struct Meta { - pub app: String, - pub version: String, - pub image: String, - pub format: String, - pub size: Size, - pub scale: String, - #[serde(rename = "frameTags")] - pub frame_tags: Vec, -} - -#[derive(Deserialize)] -pub struct Size { - pub w: u32, - pub h: u32, -} - -#[derive(Deserialize, Clone, Copy)] -#[serde(rename_all = "lowercase")] -pub enum Direction { - Forward, - Backward, - Pingpong, -} - -#[derive(Deserialize, Clone)] -pub struct FrameTag { - pub name: String, - pub from: u32, - pub to: u32, - pub direction: Direction, -} - -#[derive(Deserialize, Clone)] -pub struct Frame { - pub frame: Frame2, - pub trimmed: bool, -} - -#[derive(Deserialize, Clone)] -pub struct Frame2 { - pub x: u32, - pub y: u32, - pub w: u32, - pub h: u32, -} - -pub fn generate_from_file(filename: &Path) -> (Aseprite, DynamicImage) { - let out_dir = std::env::var("OUT_DIR").expect("Expected OUT_DIR"); - - let output_filename = Path::new(&out_dir).join(filename.file_name().unwrap()); - - let image_output = output_filename.with_extension("png"); - let json_output = output_filename.with_extension("json"); - - let command = Command::new("aseprite") - .args([ - &PathBuf::from("-b"), - &PathBuf::from(filename), - &"--sheet".into(), - &image_output, - &"--format".into(), - &"json-array".into(), - &"--data".into(), - &json_output, - &"--list-tags".into(), - ]) - .output() - .expect("Could not run aseprite"); - assert!( - command.status.success(), - "Aseprite did not complete successfully : {}", - str::from_utf8(&*command.stdout).unwrap_or("Output contains invalid string") - ); - - let json: Aseprite = serde_json::from_reader( - File::open(&json_output).expect("The json output from aseprite could not be openned"), - ) - .expect("The output from aseprite could not be decoded"); - - ( - json, - image::open(image_output).expect("Image should be readable"), - ) +pub fn generate_from_file(filename: &Path) -> (Vec, Vec) { + let ase = AsepriteFile::read_file(filename).expect("Aseprite file should exist"); + + let mut images = Vec::new(); + let mut tags = Vec::new(); + + for frame in 0..ase.num_frames() { + let image = ase.frame(frame).image(); + + images.push(DynamicImage::ImageRgba8(image)) + } + + for tag in 0..ase.num_tags() { + tags.push(ase.tag(tag).clone()) + } + + (images, tags) } diff --git a/agb-image-converter/src/lib.rs b/agb-image-converter/src/lib.rs index 4c1a93ae..3278147e 100644 --- a/agb-image-converter/src/lib.rs +++ b/agb-image-converter/src/lib.rs @@ -16,6 +16,7 @@ mod image_loader; mod palette16; mod rust_generator; +use image::GenericImageView; use image_loader::Image; use colour::Colour; @@ -93,7 +94,6 @@ pub fn include_aseprite_inner(input: TokenStream) -> TokenStream { let mut optimiser = palette16::Palette16Optimiser::new(); let mut images = Vec::new(); - let mut frames = Vec::new(); let mut tags = Vec::new(); let root = std::env::var("CARGO_MANIFEST_DIR").expect("Failed to get cargo manifest dir"); @@ -105,24 +105,18 @@ pub fn include_aseprite_inner(input: TokenStream) -> TokenStream { .collect(); for filename in filenames.iter() { - let (json, image) = aseprite::generate_from_file(filename); - let tile_size = json.frames[0].frame.w; + let (frames, tag) = aseprite::generate_from_file(filename); - for frame in json.frames.iter() { - assert!(frame.frame.w == tile_size); - assert!( - frame.frame.w == frame.frame.h - && frame.frame.w.is_power_of_two() - && frame.frame.w <= 32 - ); + tags.push((tag, images.len())); + + for frame in frames { + let width = frame.width(); + assert!(width == frame.height() && width.is_power_of_two() && width <= 32); + + let image = Image::load_from_dyn_image(frame); + add_to_optimiser(&mut optimiser, &image, width as usize); + images.push(image); } - - let image = Image::load_from_dyn_image(image); - - add_to_optimiser(&mut optimiser, &image, tile_size as usize); - images.push(image); - frames.push(json.frames.clone()); - tags.push(json.meta.frame_tags.clone()); } let optimised_results = optimiser.optimise_palettes(None); @@ -138,17 +132,16 @@ pub fn include_aseprite_inner(input: TokenStream) -> TokenStream { }); let mut pre = 0; - let sprites = frames + let sprites = images .iter() - .flatten() .zip(assignments.iter()) .map(|(f, assignment)| { let start: usize = pre; - let end: usize = pre + (f.frame.w as usize / 8) * (f.frame.h as usize / 8) * 32; + let end: usize = pre + (f.width / 8) * (f.height / 8) * 32; let data = ByteString(&tile_data[start..end]); pre = end; - let width = f.frame.w as usize; - let height = f.frame.h as usize; + let width = f.width; + let height = f.height; quote! { Sprite::new( &PALETTES[#assignment], @@ -160,15 +153,13 @@ pub fn include_aseprite_inner(input: TokenStream) -> TokenStream { let tags = tags .iter() - .enumerate() - .map(|(i, tag)| { - tag.iter().map(move |tag| (i, tag)).map(|(i, tag)| { - let offset: usize = frames[0..i].iter().map(|s| s.len()).sum(); - let start = tag.from as usize + offset; - let end = tag.to as usize + offset; - let direction = tag.direction as usize; + .map(|(tag, num_images)| { + tag.iter().map(move |tag| { + let start = tag.from_frame() as usize + num_images; + let end = tag.to_frame() as usize + num_images; + let direction = tag.animation_direction() as usize; - let name = &tag.name; + let name = tag.name(); assert!(start <= end, "Tag {} has start > end", name); quote! { diff --git a/agb/Cargo.lock b/agb/Cargo.lock index 3ba21947..017e679b 100644 --- a/agb/Cargo.lock +++ b/agb/Cargo.lock @@ -41,11 +41,11 @@ dependencies = [ name = "agb_image_converter" version = "0.6.0" dependencies = [ + "asefile", "image", "proc-macro2", "quote", "serde", - "serde_json", "syn", "toml", ] @@ -80,6 +80,20 @@ dependencies = [ "version_check", ] +[[package]] +name = "asefile" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0d5f7de918fd4cb18249819fc4bd27f6a5dbfbc9dcb271727f27dacf17ce880" +dependencies = [ + "bitflags", + "byteorder", + "flate2", + "image", + "log", + "nohash", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -140,6 +154,18 @@ dependencies = [ "adler32", ] +[[package]] +name = "flate2" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" +dependencies = [ + "cfg-if", + "crc32fast", + "libc", + "miniz_oxide 0.4.4", +] + [[package]] name = "getrandom" version = "0.2.5" @@ -181,18 +207,21 @@ dependencies = [ "png", ] -[[package]] -name = "itoa" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" - [[package]] name = "libc" version = "0.2.119" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + [[package]] name = "miniz_oxide" version = "0.5.1" @@ -202,6 +231,16 @@ dependencies = [ "adler", ] +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + [[package]] name = "modular-bitfield" version = "0.11.2" @@ -223,6 +262,12 @@ dependencies = [ "syn", ] +[[package]] +name = "nohash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0f889fb66f7acdf83442c35775764b51fed3c606ab9cee51500dbde2cf528ca" + [[package]] name = "num-integer" version = "0.1.44" @@ -323,7 +368,7 @@ dependencies = [ "bitflags", "crc32fast", "deflate", - "miniz_oxide", + "miniz_oxide 0.3.7", ] [[package]] @@ -402,12 +447,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -[[package]] -name = "ryu" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" - [[package]] name = "serde" version = "1.0.136" @@ -428,17 +467,6 @@ dependencies = [ "syn", ] -[[package]] -name = "serde_json" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" -dependencies = [ - "itoa", - "ryu", - "serde", -] - [[package]] name = "siphasher" version = "0.3.9" From 6cc29ebec0b7ffb6f9fda59660eae4cc71b1bc12 Mon Sep 17 00:00:00 2001 From: Corwin Date: Wed, 23 Feb 2022 18:14:42 +0000 Subject: [PATCH 37/51] animation direction test --- agb-image-converter/src/lib.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/agb-image-converter/src/lib.rs b/agb-image-converter/src/lib.rs index 3278147e..28076e1c 100644 --- a/agb-image-converter/src/lib.rs +++ b/agb-image-converter/src/lib.rs @@ -311,3 +311,18 @@ fn palete_tile_data( (palette_data, tile_data, assignments) } + +#[cfg(test)] +mod tests { + use asefile::AnimationDirection; + + #[test] + // These directions defined in agb and have these values. This is important + // when outputting code for agb. If more animation directions are added then + // we will have to support them there. + fn directions_to_agb() { + assert_eq!(AnimationDirection::Forward as usize, 0); + assert_eq!(AnimationDirection::Reverse as usize, 1); + assert_eq!(AnimationDirection::PingPong as usize, 2); + } +} From 592a0814f8035fb4000df254754d61931f2960ed Mon Sep 17 00:00:00 2001 From: Corwin Date: Wed, 23 Feb 2022 20:58:48 +0000 Subject: [PATCH 38/51] non optional feature alloc --- agb/Cargo.toml | 3 +-- agb/src/lib.rs | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/agb/Cargo.toml b/agb/Cargo.toml index 8821c6e4..be4f27a3 100644 --- a/agb/Cargo.toml +++ b/agb/Cargo.toml @@ -15,8 +15,7 @@ lto = true debug = true [features] -default = ["alloc"] -alloc = [] +default = [] freq18157 = ["agb_sound_converter/freq18157"] [dependencies] diff --git a/agb/src/lib.rs b/agb/src/lib.rs index 13977637..181d9c04 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -136,9 +136,7 @@ pub use agb_macros::entry; pub use agb_sound_converter::include_wav; -#[cfg(feature = "alloc")] extern crate alloc; -#[cfg(feature = "alloc")] mod agb_alloc; mod arena; From 4780b8525d9a11e39e43782efca92922c5d930ba Mon Sep 17 00:00:00 2001 From: Corwin Date: Wed, 23 Feb 2022 20:58:57 +0000 Subject: [PATCH 39/51] remove unused import --- agb/src/display/object.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index f65f1dfa..3d805511 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -9,7 +9,7 @@ use modular_bitfield::prelude::{B10, B2, B3, B4, B5, B8, B9}; use modular_bitfield::{bitfield, BitfieldSpecifier}; use rustc_hash::FxHasher; -use hashbrown::{hash_map::Entry, HashMap}; +use hashbrown::HashMap; const BYTES_PER_TILE_4BPP: usize = 32; From 8a6f341421765de11fc5a811d4d15e404a4b7849 Mon Sep 17 00:00:00 2001 From: Corwin Date: Thu, 24 Feb 2022 21:45:14 +0000 Subject: [PATCH 40/51] update hat chooses wizard --- agb-image-converter/Cargo.toml | 2 +- agb/src/display/object.rs | 38 ++- .../.vscode/settings.json | 7 +- .../the-hat-chooses-the-wizard/Cargo.lock | 263 +++++++++++++++++- .../gfx/object_sheet.png | Bin 1780 -> 0 bytes .../gfx/object_sheet.toml | 6 - .../gfx/sprites.aseprite | Bin 0 -> 12101 bytes .../the-hat-chooses-the-wizard/src/enemies.rs | 127 +++++---- .../the-hat-chooses-the-wizard/src/main.rs | 143 +++++----- 9 files changed, 442 insertions(+), 144 deletions(-) delete mode 100644 examples/the-hat-chooses-the-wizard/gfx/object_sheet.png delete mode 100644 examples/the-hat-chooses-the-wizard/gfx/object_sheet.toml create mode 100644 examples/the-hat-chooses-the-wizard/gfx/sprites.aseprite diff --git a/agb-image-converter/Cargo.toml b/agb-image-converter/Cargo.toml index 63f41d98..517b174c 100644 --- a/agb-image-converter/Cargo.toml +++ b/agb-image-converter/Cargo.toml @@ -10,7 +10,7 @@ description = "Library for converting graphics for use on the Game Boy Advance" proc-macro = true [dependencies] -image = { version = "0.24.1", default-features = false, features = [ "png", "bmp" ] } +image = { version = "0.23", default-features = false, features = [ "png", "bmp" ] } toml = "0.5.8" serde = { version = "1.0", features = ["derive"] } syn = "1.0.86" diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 3d805511..a1e1b13b 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -258,6 +258,7 @@ impl Attributes { pub struct Object<'a, 'b> { sprite: SpriteBorrow<'a>, + previous_sprite: SpriteBorrow<'a>, loan: Loan<'b>, attrs: Attributes, } @@ -316,8 +317,10 @@ impl ObjectController { index: inner.pop()?, free_list: &self.free_objects, }; + let p_sprite = sprite.clone(); Some(Object { sprite, + previous_sprite: p_sprite, loan, attrs: Attributes::new(), }) @@ -363,8 +366,18 @@ impl<'a, 'b> Object<'a, 'b> { } pub fn set_x(&mut self, x: u16) -> &mut Self { - self.attrs.a1a.set_x(x as u16); - self.attrs.a1s.set_x(x as u16); + self.attrs.a1a.set_x(x.rem_euclid(1 << 9) as u16); + self.attrs.a1s.set_x(x.rem_euclid(1 << 9) as u16); + self + } + + pub fn set_priority(&mut self, priority: Priority) -> &mut Self { + self.attrs.a2.set_priority(priority); + self + } + + pub fn hide(&mut self) -> &mut Self { + self.attrs.a0.set_object_mode(ObjectMode::Disabled); self } @@ -376,12 +389,12 @@ impl<'a, 'b> Object<'a, 'b> { pub fn set_position(&mut self, position: Vector2D) -> &mut Self { self.attrs.a0.set_y(position.y as u8); - self.attrs.a1a.set_x(position.x as u16); - self.attrs.a1s.set_x(position.x as u16); + self.attrs.a1a.set_x(position.x.rem_euclid(1 << 9) as u16); + self.attrs.a1s.set_x(position.x.rem_euclid(1 << 9) as u16); self } - pub fn commit(&self) { + pub fn commit(&mut self) { let mode = self.attrs.a0.object_mode(); let attrs: [[u8; 2]; 3] = match mode { ObjectMode::Normal => [ @@ -404,6 +417,7 @@ impl<'a, 'b> Object<'a, 'b> { ptr.add(1).write_volatile(attrs[1]); ptr.add(2).write_volatile(attrs[2]); }; + self.previous_sprite = self.sprite.clone(); } } @@ -579,6 +593,20 @@ impl<'a> Drop for SpriteBorrow<'a> { } } +impl<'a> Clone for SpriteBorrow<'a> { + fn clone(&self) -> Self { + let mut inner = self.controller.borrow_mut(); + inner.sprite.entry(self.id).and_modify(|a| a.count += 1); + let _ = inner.get_palette(self.id.get_sprite().palette).unwrap(); + Self { + id: self.id, + sprite_location: self.sprite_location, + palette_location: self.palette_location, + controller: self.controller, + } + } +} + #[derive(BitfieldSpecifier, Clone, Copy)] enum ObjectMode { Normal, diff --git a/examples/the-hat-chooses-the-wizard/.vscode/settings.json b/examples/the-hat-chooses-the-wizard/.vscode/settings.json index a3c76e3a..e58f9587 100644 --- a/examples/the-hat-chooses-the-wizard/.vscode/settings.json +++ b/examples/the-hat-chooses-the-wizard/.vscode/settings.json @@ -2,5 +2,10 @@ "files.associations": { "*.tsx": "xml", "*.tmx": "xml" - } + }, + "rust-analyzer.checkOnSave.allTargets": false, + "rust-analyzer.checkOnSave.extraArgs": [ + "--target", + "thumbv4t-none-eabi" + ] } \ No newline at end of file diff --git a/examples/the-hat-chooses-the-wizard/Cargo.lock b/examples/the-hat-chooses-the-wizard/Cargo.lock index cd5fa42c..0cb83886 100644 --- a/examples/the-hat-chooses-the-wizard/Cargo.lock +++ b/examples/the-hat-chooses-the-wizard/Cargo.lock @@ -24,6 +24,10 @@ dependencies = [ "agb_sound_converter", "bare-metal", "bitflags", + "hashbrown", + "modular-bitfield", + "phf", + "rustc-hash", ] [[package]] @@ -37,6 +41,7 @@ dependencies = [ name = "agb_image_converter" version = "0.6.0" dependencies = [ + "asefile", "image", "proc-macro2", "quote", @@ -64,6 +69,31 @@ dependencies = [ "syn", ] +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "asefile" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0d5f7de918fd4cb18249819fc4bd27f6a5dbfbc9dcb271727f27dacf17ce880" +dependencies = [ + "bitflags", + "byteorder", + "flate2", + "image", + "log", + "nohash", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -117,11 +147,44 @@ dependencies = [ [[package]] name = "deflate" -version = "1.0.0" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f" +checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" dependencies = [ "adler32", + "byteorder", +] + +[[package]] +name = "flate2" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" +dependencies = [ + "cfg-if", + "crc32fast", + "libc", + "miniz_oxide 0.4.4", +] + +[[package]] +name = "getrandom" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c21d40587b92fa6a6c6e3c1bdbf87d75511db5672f9c93175574b3a00df1758" +dependencies = [ + "ahash", ] [[package]] @@ -132,9 +195,9 @@ checksum = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549" [[package]] name = "image" -version = "0.24.1" +version = "0.23.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db207d030ae38f1eb6f240d5a1c1c88ff422aa005d10f8c6c6fc5e75286ab30e" +checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1" dependencies = [ "bytemuck", "byteorder", @@ -152,14 +215,66 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" [[package]] -name = "miniz_oxide" -version = "0.5.1" +name = "libc" +version = "0.2.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" +checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "miniz_oxide" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +dependencies = [ + "adler32", +] + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" dependencies = [ "adler", + "autocfg", ] +[[package]] +name = "modular-bitfield" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74" +dependencies = [ + "modular-bitfield-impl", + "static_assertions", +] + +[[package]] +name = "modular-bitfield-impl" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "nohash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0f889fb66f7acdf83442c35775764b51fed3c606ab9cee51500dbde2cf528ca" + [[package]] name = "num-integer" version = "0.1.44" @@ -183,9 +298,9 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" dependencies = [ "autocfg", "num-integer", @@ -202,17 +317,79 @@ dependencies = [ ] [[package]] -name = "png" -version = "0.17.5" +name = "once_cell" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_macros", + "phf_shared", + "proc-macro-hack", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "png" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" dependencies = [ "bitflags", "crc32fast", "deflate", - "miniz_oxide", + "miniz_oxide 0.3.7", ] +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + [[package]] name = "proc-macro2" version = "1.0.36" @@ -231,6 +408,42 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "ryu" version = "1.0.9" @@ -268,6 +481,18 @@ dependencies = [ "serde", ] +[[package]] +name = "siphasher" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a86232ab60fa71287d7f2ddae4a7073f6b7aac33631c3015abb556f08c6d0a3e" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "syn" version = "1.0.86" @@ -302,3 +527,15 @@ name = "unicode-xid" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" diff --git a/examples/the-hat-chooses-the-wizard/gfx/object_sheet.png b/examples/the-hat-chooses-the-wizard/gfx/object_sheet.png deleted file mode 100644 index 5ba186476d5015837ff7afd89a1fd291d28341a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1780 zcmVPx#Fi=cXMdYS(xNlH&KRG`rGM*?k4JwH!pfv>;P|Ns9i z==21H(S7{v1^@s65Oh*bQvm<}|NsC0|NsC0|NsC0|G-d-LjV8I7neG$ws07mRZ08)g&BTOFxOo{LTo7m6(9Pp6!G06HrKu7gqUR@svMm9GLh6`AySqTD4{IP~6 ze+e0qawzyN2(zJyZpn&QsNon9udkT<2f-L&pRgMtWG7kB_Knf~VscO2;{lEGU_hd| zVNh;C+mA+g8=xa(eHh3!72MKv*(8tMXmcX<{iw4|)%&L60g}fcdC+ialSc-U0j1ot zka|1WA0QS)A!bxLsDe>j>ok2d$zxYM@|?|-LhM*%|nfsvcz!v+#H;i z!?k%R!BmT<7KYOZJq2qX&@K<7guGTf?e-~wPq4WEXmx#2`wwP?tqpp+QLRArl62Ai z*h_?J&%Z3o%y^jPwk)@~Y~U9XLJSG(60RbwCju&Ke4RTgC(;%mofz_2=8DIhc;Nd4 zhzNCz5Bcq~`67_ANlz*}B-e3~ub@pv6E32^? zn=M7R>!RB!=CwsP&sw0(mI-f#$NCHHXLIXHZ?4JQviev4^*U3;Y!5WP4r=fx1W^v@ zwgl*q=^TZ*62u+)DFO1n2tnLof`HaiWXE!fLft5Z&`pU)$V7e8x|i4&1OO?(GyxZ2 zoQe_zlz`uYL0rI~q#O!iGp@w5byuR0dB>)Kf$V1p?PO_lwgO90r zjE{MYjq+eXqPb~OZbxlD8sTk>IzrY5jJc+QTW@}9lScuPyK<^(U3E58y$=?03hpE=2bwr_n*#bbNSV{4QL0}{3U2X-9BuL_ zKyp{Qs@7F!+p70%#bc8^oa8~nrA;0gNCuR0&qC_$WZ!MEAPO<7%0U&36GEOon&h!7 z9(hjXJT{h?cjv6*0=i|lT$_g)FJ+10_PM$3v>dL@LkYH8Jhk9XBlHxkd2H?Suu8~l z#nWz|68Hp*`;S)F7q$OjR_?C1C%dh4;Rp9)>K`$T!C!{5Z}BM)l2D+ZZ-!i)$pU_h zjWJ0GqW{ye*ZyyJEO>YHpI4J$%@#a|Z_h2gZMlF`vI#%14Ue@klbkw+Y}{OEM!MC% z?fEnw=>q zh9f*S;>h1RB^+b=#Fuc;=jN{97&)MiX z(BlQF@v>_kY(k0C$1Zq4o?zK^SNO0Qm!-}&D6ieYe#jo?Wjs!WE7aIe@_0gce~rD> z{@)AVU1M#zeTneJ8hdBz?-3r?*uOQZKO;LlDk{#K(g9P$gz5k)o)i@a`mqfzfD?yl z>3q5pWJLJ?I{-h`ir*jsYZOaCoJ!*dPA|uU+*Ul;Ep+;}w1Ee3novm; zsY{ESlqjhb)up_@^A2VD{oMZdPWk+o&;RssW}5S!=lP!Rd7kGy=e);44S@DTCO`!K zqW}Z|;0gc6iU>fkA35z~zy7#yy9fXb_K9$`ia@dA@Q+O6sN_A^liKADbcwp%kBFAaM!TNK-6!K$Dw4gwmCDYAQ9{%v3dIf<+ATuDG78Dxd?;FU> zhI~lUggy&_pLoD66_CMFH~-bXf!+i-oUaNQScL|xfh|O6ZeJBLSm@>trF-)Mv9B7S zGzicUcA5kxf89a_j{d#@Od7+}pE(&wd=27rBc@OiV6<=*{?VT6*7&=HNCRxxT!r#k zqvmV?9fZ4lX_}`Fw7DAq5CD#WZod9BivVV@H&Y#`f87l6aGEi&^MTIzjW$8+nYzIE z>qZEc)6<;k7UHu2(7$dK0QUZF;mlwfr+GGPB+^EnK)*~1=H6l1j~`FF3joDe2yx&% zg+RjaVC*R>5QIVh*XdKXR6nD7h0?)8?Rl!`C=` zK}}cuu}>jjjyy;(;0VOQ=_YdtBmzDt(8JZs)z#Gv5i2U9$EOo5qxdM-s8?vJ&;SG$ zgIp6%S3DrX&SO|e&@N%u_D&|4IhlQ(U+fXn^9ieDDs85^nC0u&Z`5A{1dj)*L}Z^0 zM*tBHW(XYX~mXv6-kqAj3g0Y?HxD)$6`i&X2^fKOJIL;|W(NX{6ggWpf- z;t8Ns1J0@dZs#z1(|KOVoGCNhb&=YrLnXJTtxj;#iQQ@qN?CfEBKo2?C%xt;D>nTX zl*VBeFA!x}2#v(kDDr3B_S9YGXA3KxlAon2oc&Ob7ZV1jFsCHauqolDl*qADix>!q zCK6fhf}(ACQPSoK9nCwuE?mB=py}X-%)W>0`k{>4@Wki{1k6ae2f)X1dglZL4nP1G z^cIb?l84sXIX-iLgpFo{7vGiZwT7MJCue`0(m+}!WKp%?b{A7TBJ+| zGbGsrC8bP&6NmoJe-31teKF0y>+f^e)z_=p*V)z8Yyec~2y2YYAK>x4k!j5t1x6;q z<)VBrISI>7PcY#GoxD%zftKg4UFlIXEa*Z>bxjYPl=5T?j6Cx<5poTu#`ABkMpOnt zn&G#&baTUkryC+#B%oa68kIh!7NM3#N{ZkFfS@ioI1(x_N5^sD_)ACkqjk7O#q1{6 zc06)(1wbi{o6JAI-`DMVvdnKSN6z&QLV!lW+5bB9P%q_t{6OErxxju~_2P4qR$D*n zpOIp`v?Z=vvhLbj(X&d%l}pJApZ*UmN7g*?X+LV5<1O#b^_Ztq%$lB6qWkvlv9E5| zFk@6Xu+}WoIxh8FQi#XhEXSj*l}~SNIk1d)^Bqul_-#66Vz=?Zg*+oa>k-2x3^O`QS)&p6Rz)UTv+&wj`GCB^4iU$SyIlC&^+s)H5;e@ z;HCZlZS%*bN=tdN%^%_GaZ1U~7lwwOgE`Ycg2k8UN#u%vWR8Vt*Fj@frM#JLw%L{2yed+u?Wy`*55L6ieX$UIsd{Bod(C za4+sWCczT|fwQTwjE5;!$XzE}EkR3Mzp43V_R4)kU#~~oRzAtkC$wD5NqW`|RP#XJ z1VBg_+1FS`q7mV#0i^QD#{peTt?U#3p{J<@_lhT@Zz%h(Eh4bib&$xa2?L0Gr$lTN~*?pgJ48 zVey1wWJ@$Y3Zukpf`CrJF-U_!;i36&I_fM}NPkzp!0%xh=y)p6_;hM<3r3<@c}X>Ai+G#Kqo6n~;^Az^iRM>0iVdJhoxu zK9!(yP9>Q2C8F5$n=BM7L8GQ?eN0$S0rVJiMuP=|xiPLhIAr--zNgABRbzzh_>hk8 z2?it>l_^|nlK}i*ShhBHEAZ6>~#0`3)>CkoOa zd;dH|aDlyuG|U*ew~7LsL`i-pW{d&|)8#cvEp~cc4u#)>I_qlyeqg3AF{#A6AvZyONq?l-pBTJAZEku%sJ(Aon7`#O>$}z@pyFO z-rV>j3DGKRGbE4fUVc@fd55zV|Ebu`sTEDrx?ZV$Kz^BoLlbxc?ED2Vw>t@O&QJn& zUg9L1EY*8x#A`p36^#a#c~eA|QEMkFi$qkZzVgiLTG&)|zNM&dS?0;A`{@I@_jDI- z0BsYH>x>51jg$d&!G25Zm|$j=i3 zfs^s+YJEVb+wHdLG;fZaMOQPgA-}k@JjF{~w%fv3-)c<-|MScwA(LMa$7r4=#FjH5 zNCAxY44{RSNO@QPc6AUtK8gPS0Qi>p!*?9CxC_o z4KeIAT{`$RI(_Nto-9SG`7F9&O2av=8^I+?=&I&FqPE~Kq|%-H?Zu&y%b! zYw!Avu9Pa+s?n@pZ?CQ2v&q}&a(8aQ7N7XNbqwKT@4amlSLe43^hqk%ZKO-QunxVRM~=0?^-Nq)C@#8nm?N5mT1Kdpa^L~u##<`Ov>-2uTueHxlwpM4j=A&NjY*XnL_WUqK zrFUV+H8sxN^kUXcKP#+u$7tGS6qyJblp#cAoiD-7*PI&elHjWw7U zwI?04ii(KtJ(i&*5WvEjosS9Ld)To7KlQO)ef?CwnKf$#{kQKfbuVsFoJWc%S5tEQ z?RRoo#({`-LA9esBzREC%YJXexJZRLn=59rgAg0O1!7pW4j}Wb%DGL5P`aCpdC%C14b+jn$Lw!D!D3>yWKQ4%JGm8VxIOMS(Ui z+0i`Mz*Q-$wJ6*vFFafyvr-}FQ09EHq>@8VN5GZKh4`JP*9_D@sDDQYxbf`e)j1hO z5ed7~BY=1lX-OGR>|${M+f8F-Z0pJmSip2Iy+6PAQ}M;TJG~y}JB1qyvIy*zNLILv zgSj-hsm@aR#9RCP@J-sr%T^DUwAh}i_xkBWV5aDArVs1un7aDEvkc@E7)EqfyQIgB zaKW{4pH?WJ7{*H-BDw!>#9R!S`sb$>;jq_#^8DF~O)x7aFf!Q42Xw=A>ggCU?HfW1 zr???0E9lM@$c%Y9^gw5E@ge1IvcmH|hvI>&-GbiU+qWh5<;CU;ncYvt%Y`>dg*qE2 z=-;V-pf;%?q>}8ev*3v}Y0*BO;Che3#qL&(nS~JNCXh?g=TM7-D{Wz!k#bCisCAJP zQlP|s_oy$ms&N-HdZTcfDpRwX|I?t|ivx-(qVBhFI_>%E9}MWppWgIr{S*|zJtD^b z$`iPiqu{V>8{FUo;Bll&f`J%}v8GwMgqIO*18E1BQ9I*+O6s|pY0`l~o3f1Ks}DUd zY0j-I8Wqh;&xU+0TC0Ss~AAN@tqBy?QvNqv%i9!|3f(28Yac>icea`8z@Qdkz!+ z{kT?$b6o3k9cD@JRiZ;$W!0zR`-?*rxA(afSw-H9>|MK_5ujCK-7~za$5BU7V#8oF z5^so?*vu0j3%|?}m1~X!;kBc%Y?TB(Dty;o6KDGab4ZA2EQk{fnXz)zJYHp^uCiB^mk;rqmVc1Y+go<@NOdF#<-sU5NPB)h8x)N<L4>%D^Q{uI4+aq>obh^`+4fD&e&f*LI@& b7?m9+xBF)Lh+M9|j?aagm-+2Kn8$wsL6bdA literal 0 HcmV?d00001 diff --git a/examples/the-hat-chooses-the-wizard/src/enemies.rs b/examples/the-hat-chooses-the-wizard/src/enemies.rs index 399474f3..c30c0ebe 100644 --- a/examples/the-hat-chooses-the-wizard/src/enemies.rs +++ b/examples/the-hat-chooses-the-wizard/src/enemies.rs @@ -1,6 +1,8 @@ -use super::{object_tiles, sfx::SfxPlayer, Entity, FixedNumberType, HatState, Level}; +use crate::TAG_MAP; + +use super::{sfx::SfxPlayer, Entity, FixedNumberType, HatState, Level}; use agb::{ - display::object::{ObjectControl, Size}, + display::object::{ObjectController, Size}, fixnum::Vector2D, }; @@ -28,11 +30,11 @@ pub enum EnemyUpdateState { } impl<'a> Enemy<'a> { - pub fn new_slime(object: &'a ObjectControl, start_pos: Vector2D) -> Self { + pub fn new_slime(object: &'a ObjectController, start_pos: Vector2D) -> Self { Enemy::Slime(Slime::new(object, start_pos + (0, 1).into())) } - pub fn new_snail(object: &'a ObjectControl, start_pos: Vector2D) -> Self { + pub fn new_snail(object: &'a ObjectController, start_pos: Vector2D) -> Self { Enemy::Snail(Snail::new(object, start_pos)) } @@ -45,6 +47,7 @@ impl<'a> Enemy<'a> { pub fn update( &mut self, + controller: &'a ObjectController, level: &Level, player_pos: Vector2D, hat_state: HatState, @@ -52,8 +55,12 @@ impl<'a> Enemy<'a> { sfx_player: &mut SfxPlayer, ) -> EnemyUpdateState { let update_state = match self { - Enemy::Slime(slime) => slime.update(level, player_pos, hat_state, timer, sfx_player), - Enemy::Snail(snail) => snail.update(level, player_pos, hat_state, timer, sfx_player), + Enemy::Slime(slime) => { + slime.update(controller, level, player_pos, hat_state, timer, sfx_player) + } + Enemy::Snail(snail) => { + snail.update(controller, level, player_pos, hat_state, timer, sfx_player) + } Enemy::Empty => UpdateState::Nothing, }; @@ -82,7 +89,7 @@ struct EnemyInfo<'a> { impl<'a> EnemyInfo<'a> { fn new( - object: &'a ObjectControl, + object: &'a ObjectController, start_pos: Vector2D, collision: Vector2D, ) -> Self { @@ -123,19 +130,18 @@ pub struct Slime<'a> { } impl<'a> Slime<'a> { - fn new(object: &'a ObjectControl, start_pos: Vector2D) -> Self { + fn new(object: &'a ObjectController, start_pos: Vector2D) -> Self { let mut slime = Slime { enemy_info: EnemyInfo::new(object, start_pos, (14u16, 14u16).into()), state: SlimeState::Idle, }; - slime.enemy_info.entity.sprite.set_sprite_size(Size::S16x16); - slime } fn update( &mut self, + controller: &'a ObjectController, level: &Level, player_pos: Vector2D, hat_state: HatState, @@ -147,11 +153,13 @@ impl<'a> Slime<'a> { match self.state { SlimeState::Idle => { - let offset = (timer / 16 % 2) * 4; - self.enemy_info - .entity - .sprite - .set_tile_id(object_tiles::SLIME_IDLE_START + offset as u16); + let offset = (timer / 16) as usize; + + let tag = TAG_MAP.get("Slime Idle").unwrap(); + let frame = tag.get_animation_sprite(offset); + let sprite = controller.get_sprite(frame).unwrap(); + + self.enemy_info.entity.sprite.set_sprite(sprite); if (self.enemy_info.entity.position - player_pos).magnitude_squared() < (64 * 64).into() @@ -178,7 +186,7 @@ impl<'a> Slime<'a> { } } SlimeState::Jumping(jumping_start_frame) => { - let offset = (timer - jumping_start_frame) / 4; + let offset = (timer - jumping_start_frame) as usize / 4; if timer == jumping_start_frame + 1 { sfx_player.slime_jump(); @@ -188,12 +196,11 @@ impl<'a> Slime<'a> { self.enemy_info.entity.velocity = (0, 0).into(); self.state = SlimeState::Idle; } else { - let sprite_offset = if offset >= 4 { 7 - offset } else { offset }; + let tag = TAG_MAP.get("Slime Jump").unwrap(); + let frame = tag.get_animation_sprite(offset); + let sprite = controller.get_sprite(frame).unwrap(); - self.enemy_info - .entity - .sprite - .set_tile_id(object_tiles::SLIME_JUMP_START + (sprite_offset * 4) as u16); + self.enemy_info.entity.sprite.set_sprite(sprite); } if player_has_collided { @@ -209,17 +216,18 @@ impl<'a> Slime<'a> { sfx_player.slime_death(); } - let offset = (timer - dying_start_frame) / 4; + let offset = (timer - dying_start_frame) as usize / 4; self.enemy_info.entity.velocity = (0, 0).into(); if offset >= 4 { return UpdateState::Remove; } - self.enemy_info - .entity - .sprite - .set_tile_id(object_tiles::SLIME_SPLAT_START + (offset * 4) as u16); + let tag = TAG_MAP.get("Slime splat").unwrap(); + let frame = tag.get_animation_sprite(offset); + let sprite = controller.get_sprite(frame).unwrap(); + + self.enemy_info.entity.sprite.set_sprite(sprite); } } @@ -247,14 +255,12 @@ pub struct Snail<'a> { } impl<'a> Snail<'a> { - fn new(object: &'a ObjectControl, start_pos: Vector2D) -> Self { + fn new(object: &'a ObjectController, start_pos: Vector2D) -> Self { let mut snail = Snail { enemy_info: EnemyInfo::new(object, start_pos, (16u16, 16u16).into()), state: SnailState::Idle(0), }; - snail.enemy_info.entity.sprite.set_sprite_size(Size::S16x16); - snail } @@ -264,6 +270,7 @@ impl<'a> Snail<'a> { fn update( &mut self, + controller: &'a ObjectController, level: &Level, player_pos: Vector2D, hat_state: HatState, @@ -288,10 +295,11 @@ impl<'a> Snail<'a> { } } - self.enemy_info - .entity - .sprite - .set_tile_id(object_tiles::SNAIL_IDLE_START); + let tag = TAG_MAP.get("Snail Idle").unwrap(); + let frame = tag.get_animation_sprite(0); + let sprite = controller.get_sprite(frame).unwrap(); + + self.enemy_info.entity.sprite.set_sprite(sprite); if player_has_collided { if hat_state != HatState::WizardTowards { return UpdateState::KillPlayer; @@ -301,17 +309,18 @@ impl<'a> Snail<'a> { } } SnailState::Emerging(time) => { - let offset = (timer - time) / 4; + let offset = (timer - time) as usize / 4; if offset >= 5 { self.state = SnailState::Moving(timer); } self.enemy_info.entity.velocity = (0, 0).into(); - self.enemy_info - .entity - .sprite - .set_tile_id(object_tiles::SNAIL_EMERGE_START + (offset * 4) as u16); + let tag = TAG_MAP.get("Snail Emerge").unwrap(); + let frame = tag.get_animation_sprite(offset); + let sprite = controller.get_sprite(frame).unwrap(); + + self.enemy_info.entity.sprite.set_sprite(sprite); if player_has_collided { if hat_state != HatState::WizardTowards { @@ -328,12 +337,13 @@ impl<'a> Snail<'a> { sfx_player.snail_retreat(); } - let offset = (timer - time) / 8 % 2; + let offset = (timer - time) as usize / 8; - self.enemy_info - .entity - .sprite - .set_tile_id(object_tiles::SNAIL_MOVE + (offset * 4) as u16); + let tag = TAG_MAP.get("Snail Move").unwrap(); + let frame = tag.get_animation_sprite(offset); + let sprite = controller.get_sprite(frame).unwrap(); + + self.enemy_info.entity.sprite.set_sprite(sprite); if timer % 32 == 0 { let x_vel: FixedNumberType = @@ -358,16 +368,17 @@ impl<'a> Snail<'a> { } } SnailState::Retreating(time) => { - let offset = 5 - (timer - time) / 4; + let offset = 5 - (timer - time) as usize / 4; if offset == 0 { self.state = SnailState::Idle(timer); } - self.enemy_info - .entity - .sprite - .set_tile_id(object_tiles::SNAIL_EMERGE_START + (offset * 4) as u16); + let tag = TAG_MAP.get("Snail Emerge").unwrap(); + let frame = tag.get_animation_sprite(offset); + let sprite = controller.get_sprite(frame).unwrap(); + + self.enemy_info.entity.sprite.set_sprite(sprite); self.enemy_info.entity.velocity = (0, 0).into(); if player_has_collided { @@ -383,18 +394,26 @@ impl<'a> Snail<'a> { sfx_player.snail_death(); } - let offset = (timer - time) / 4; - let tile_id = if offset < 5 { - object_tiles::SNAIL_EMERGE_START + ((5 - offset) * 4) as u16 + let offset = (timer - time) as usize / 4; + let frame = if offset < 5 { + TAG_MAP + .get("Snail Emerge") + .unwrap() + .get_animation_sprite(5 - offset) } else if offset == 5 { - object_tiles::SNAIL_IDLE_START + TAG_MAP.get("Snail Idle").unwrap().get_animation_sprite(0) } else if offset < 5 + 7 { - object_tiles::SNAIL_DEATH_START + ((offset - 5) * 4) as u16 + TAG_MAP + .get("Snail Death") + .unwrap() + .get_animation_sprite(offset - 5) } else { return UpdateState::Remove; }; - self.enemy_info.entity.sprite.set_tile_id(tile_id); + let sprite = controller.get_sprite(frame).unwrap(); + + self.enemy_info.entity.sprite.set_sprite(sprite); self.enemy_info.entity.velocity = (0, 0).into(); } } diff --git a/examples/the-hat-chooses-the-wizard/src/main.rs b/examples/the-hat-chooses-the-wizard/src/main.rs index 589db875..262154e2 100644 --- a/examples/the-hat-chooses-the-wizard/src/main.rs +++ b/examples/the-hat-chooses-the-wizard/src/main.rs @@ -18,27 +18,6 @@ pub struct Level { start_pos: (i32, i32), } -mod object_tiles { - pub const WIZARD_TILE_START: u16 = 0; - pub const WIZARD_JUMP: u16 = 4 * 4; - pub const WIZARD_FALL_START: u16 = 5 * 4; - - pub const HAT_TILE_START: u16 = 9 * 4; - pub const HAT_TILE_START_SECOND: u16 = 28 * 4; - pub const HAT_TILE_START_THIRD: u16 = 38 * 4; - - pub const SLIME_IDLE_START: u16 = 19 * 4; - pub const SLIME_JUMP_START: u16 = 20 * 4; - pub const SLIME_SPLAT_START: u16 = 24 * 4; - - pub const SNAIL_IDLE_START: u16 = 48 * 4; - pub const SNAIL_EMERGE_START: u16 = 49 * 4; - pub const SNAIL_MOVE: u16 = 54 * 4; - pub const SNAIL_DEATH_START: u16 = 56 * 4; -} - -agb::include_gfx!("gfx/object_sheet.toml"); - mod map_tiles { use super::Level; @@ -106,25 +85,32 @@ agb::include_gfx!("gfx/tile_sheet.toml"); use agb::{ display::{ background::BackgroundRegular, - object::{ObjectControl, ObjectStandard, Size}, + object::{Object, ObjectController, Sprite, TagMap}, Priority, HEIGHT, WIDTH, }, fixnum::{FixedNum, Vector2D}, input::{self, Button, ButtonController}, }; +const SPRITE_TAGS: (&[Sprite], &TagMap) = agb::include_aseprite!("gfx/sprites.aseprite"); +const SPRITES: &[Sprite] = SPRITE_TAGS.0; +const TAG_MAP: &TagMap = SPRITE_TAGS.1; + type FixedNumberType = FixedNum<10>; pub struct Entity<'a> { - sprite: ObjectStandard<'a>, + sprite: Object<'a, 'a>, position: Vector2D, velocity: Vector2D, collision_mask: Vector2D, } impl<'a> Entity<'a> { - pub fn new(object: &'a ObjectControl, collision_mask: Vector2D) -> Self { - let mut sprite = object.get_object_standard(); + pub fn new(object: &'a ObjectController, collision_mask: Vector2D) -> Self { + let dummy_sprite = object + .get_sprite(TAG_MAP.get("Walking").unwrap().get_sprite(0)) + .unwrap(); + let mut sprite = object.get_object(dummy_sprite).unwrap(); sprite.set_priority(Priority::P1); Entity { sprite, @@ -360,14 +346,21 @@ fn ping_pong(i: i32, n: i32) -> i32 { } impl<'a> Player<'a> { - fn new(controller: &'a ObjectControl, start_position: Vector2D) -> Self { - let mut hat = Entity::new(controller, (6_u16, 6_u16).into()); + fn new(controller: &'a ObjectController, start_position: Vector2D) -> Self { let mut wizard = Entity::new(controller, (6_u16, 14_u16).into()); + let mut hat = Entity::new(controller, (6_u16, 6_u16).into()); + + wizard.sprite.set_sprite( + controller + .get_sprite(TAG_MAP.get("Walking").unwrap().get_sprite(0)) + .unwrap(), + ); + hat.sprite.set_sprite( + controller + .get_sprite(TAG_MAP.get("HatSpin").unwrap().get_sprite(0)) + .unwrap(), + ); - wizard.sprite.set_tile_id(object_tiles::WIZARD_TILE_START); - hat.sprite.set_tile_id(object_tiles::HAT_TILE_START); - wizard.sprite.set_sprite_size(Size::S16x16); - hat.sprite.set_sprite_size(Size::S16x16); wizard.sprite.show(); hat.sprite.show(); @@ -393,6 +386,7 @@ impl<'a> Player<'a> { fn update_frame( &mut self, input: &ButtonController, + controller: &'a ObjectController, timer: i32, level: &Level, enemies: &[enemies::Enemy], @@ -467,23 +461,29 @@ impl<'a> Player<'a> { self.wizard.velocity = self.wizard.update_position(level); if self.wizard.velocity.x.abs() > 0.into() { - let offset = (ping_pong(timer / 16, 4)) as u16; + let offset = (ping_pong(timer / 16, 4)) as usize; self.wizard_frame = offset as u8; - self.wizard - .sprite - .set_tile_id(object_tiles::WIZARD_TILE_START + offset * 4); + let walk = TAG_MAP.get("Walking").unwrap(); + let frame = walk.get_animation_sprite(offset); + let sprite = controller.get_sprite(frame).unwrap(); + + self.wizard.sprite.set_sprite(sprite); } if self.wizard.velocity.y < -FixedNumberType::new(1) / 16 { // going up self.wizard_frame = 5; - self.wizard.sprite.set_tile_id(object_tiles::WIZARD_JUMP); + let walk = TAG_MAP.get("Jumping").unwrap(); + let frame = walk.get_animation_sprite(0); + let sprite = controller.get_sprite(frame).unwrap(); + + self.wizard.sprite.set_sprite(sprite); } else if self.wizard.velocity.y > FixedNumberType::new(1) / 16 { // going down let offset = if self.wizard.velocity.y * 2 > 3.into() { - ((timer / 4) % 4) as u16 + (timer / 4) as usize } else { // Don't flap beard unless going quickly 0 @@ -491,9 +491,11 @@ impl<'a> Player<'a> { self.wizard_frame = 0; - self.wizard - .sprite - .set_tile_id(object_tiles::WIZARD_FALL_START + offset * 4); + let walk = TAG_MAP.get("Falling").unwrap(); + let frame = walk.get_animation_sprite(offset); + let sprite = controller.get_sprite(frame).unwrap(); + + self.wizard.sprite.set_sprite(sprite); } if input.x_tri() != agb::input::Tri::Zero { @@ -502,19 +504,23 @@ impl<'a> Player<'a> { } let hat_base_tile = match self.num_recalls { - 0 => object_tiles::HAT_TILE_START, - 1 => object_tiles::HAT_TILE_START_SECOND, - _ => object_tiles::HAT_TILE_START_THIRD, + 0 => TAG_MAP.get("HatSpin").unwrap(), + 1 => TAG_MAP.get("HatSpin2").unwrap(), + _ => TAG_MAP.get("HatSpin3").unwrap(), }; match self.facing { agb::input::Tri::Negative => { self.wizard.sprite.set_hflip(true); - self.hat.sprite.set_tile_id(hat_base_tile + 4 * 5); + self.hat + .sprite + .set_sprite(controller.get_sprite(hat_base_tile.get_sprite(5)).unwrap()); } agb::input::Tri::Positive => { self.wizard.sprite.set_hflip(false); - self.hat.sprite.set_tile_id(hat_base_tile); + self.hat + .sprite + .set_sprite(controller.get_sprite(hat_base_tile.get_sprite(0)).unwrap()); } _ => {} } @@ -543,11 +549,13 @@ impl<'a> Player<'a> { _ => 4, }; - let hat_sprite_offset = timer / hat_sprite_divider % 10; + let hat_sprite_offset = (timer / hat_sprite_divider) as usize; - self.hat - .sprite - .set_tile_id(hat_base_tile + (hat_sprite_offset * 4) as u16); + self.hat.sprite.set_sprite( + controller + .get_sprite(hat_base_tile.get_animation_sprite(hat_sprite_offset)) + .unwrap(), + ); if self.hat_slow_counter < 30 && self.hat.velocity.magnitude() < 2.into() { self.hat.velocity = (0, 0).into(); @@ -578,9 +586,11 @@ impl<'a> Player<'a> { self.hat.position = self.wizard.position - hat_resting_position; } HatState::WizardTowards => { - self.hat - .sprite - .set_tile_id(hat_base_tile + 4 * (timer / 2 % 10) as u16); + self.hat.sprite.set_sprite( + controller + .get_sprite(hat_base_tile.get_animation_sprite(timer as usize / 2)) + .unwrap(), + ); let distance_vector = self.hat.position - self.wizard.position + hat_resting_position; let distance = distance_vector.magnitude(); @@ -617,7 +627,7 @@ enum UpdateState { impl<'a, 'b, 'c> PlayingLevel<'a, 'b> { fn open_level( level: &'a Level, - object_control: &'a ObjectControl, + object_control: &'a ObjectController, background: &'a mut BackgroundRegular<'b>, foreground: &'a mut BackgroundRegular<'b>, input: ButtonController, @@ -676,22 +686,27 @@ impl<'a, 'b, 'c> PlayingLevel<'a, 'b> { self.player.wizard.sprite.set_priority(Priority::P0); } - fn dead_update(&mut self) -> bool { + fn dead_update(&mut self, controller: &'a ObjectController) -> bool { self.timer += 1; + let tag = TAG_MAP.get("Player Death").unwrap(); + let frame = tag.get_animation_sprite(self.timer as usize / 8); + let sprite = controller.get_sprite(frame).unwrap(); + self.player.wizard.velocity += (0.into(), FixedNumberType::new(1) / 32).into(); self.player.wizard.position += self.player.wizard.velocity; - self.player - .wizard - .sprite - .set_tile_id((self.timer / 8 % 2 * 4 + 63 * 4) as u16); + self.player.wizard.sprite.set_sprite(sprite); self.player.wizard.commit_position(self.background.position); self.player.wizard.position.y - self.background.position.y < (HEIGHT + 8).into() } - fn update_frame(&mut self, sfx_player: &mut sfx::SfxPlayer) -> UpdateState { + fn update_frame( + &mut self, + controller: &'a ObjectController, + sfx_player: &mut sfx::SfxPlayer, + ) -> UpdateState { self.timer += 1; self.input.update(); @@ -699,6 +714,7 @@ impl<'a, 'b, 'c> PlayingLevel<'a, 'b> { self.player.update_frame( &self.input, + controller, self.timer, self.background.level, &self.enemies, @@ -707,6 +723,7 @@ impl<'a, 'b, 'c> PlayingLevel<'a, 'b> { for enemy in self.enemies.iter_mut() { match enemy.update( + controller, self.background.level, self.player.wizard.position, self.player.hat_state, @@ -784,8 +801,6 @@ fn main(mut agb: agb::Gba) -> ! { tiled.set_background_palettes(tile_sheet::background.palettes); tiled.set_background_tilemap(0, tile_sheet::background.tiles); - object.set_sprite_palettes(object_sheet::object_sheet.palettes); - object.set_sprite_tilemap(object_sheet::object_sheet.tiles); let mut world_display = tiled.get_raw_regular().unwrap(); world_display.clear(level_display::BLANK); @@ -793,7 +808,6 @@ fn main(mut agb: agb::Gba) -> ! { let mut background = tiled.get_regular().unwrap(); let mut foreground = tiled.get_regular().unwrap(); - object.enable(); mixer.enable(); let mut music_box = sfx::MusicBox::new(); @@ -856,11 +870,12 @@ fn main(mut agb: agb::Gba) -> ! { world_display.hide(); loop { - match level.update_frame(&mut sfx::SfxPlayer::new(&mut mixer, &music_box)) { + match level.update_frame(&object, &mut sfx::SfxPlayer::new(&mut mixer, &music_box)) + { UpdateState::Normal => {} UpdateState::Dead => { level.dead_start(); - while level.dead_update() { + while level.dead_update(&object) { music_box.before_frame(&mut mixer); mixer.frame(); vblank.wait_for_vblank(); From c55ff3f7143ffd69bcf0051107a82624a82b5356 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 5 Mar 2022 17:24:23 +0000 Subject: [PATCH 41/51] const fnify --- agb-image-converter/src/lib.rs | 29 +++--- agb/Cargo.lock | 57 ------------ agb/Cargo.toml | 1 - agb/src/display/object.rs | 50 +++++++--- agb/src/lib.rs | 1 - .../the-hat-chooses-the-wizard/Cargo.lock | 93 ------------------- 6 files changed, 50 insertions(+), 181 deletions(-) diff --git a/agb-image-converter/src/lib.rs b/agb-image-converter/src/lib.rs index 28076e1c..e4d8e954 100644 --- a/agb-image-converter/src/lib.rs +++ b/agb-image-converter/src/lib.rs @@ -151,23 +151,20 @@ pub fn include_aseprite_inner(input: TokenStream) -> TokenStream { } }); - let tags = tags - .iter() - .map(|(tag, num_images)| { - tag.iter().map(move |tag| { - let start = tag.from_frame() as usize + num_images; - let end = tag.to_frame() as usize + num_images; - let direction = tag.animation_direction() as usize; + let tags = tags.iter().flat_map(|(tag, num_images)| { + tag.iter().map(move |tag| { + let start = tag.from_frame() as usize + num_images; + let end = tag.to_frame() as usize + num_images; + let direction = tag.animation_direction() as usize; - let name = tag.name(); - assert!(start <= end, "Tag {} has start > end", name); + let name = tag.name(); + assert!(start <= end, "Tag {} has start > end", name); - quote! { - #name => Tag::new(SPRITES, #start, #end, #direction) - } - }) + quote! { + (#name, Tag::new(SPRITES, #start, #end, #direction)) + } }) - .flatten(); + }); let include_paths = filenames.iter().map(|s| { let s = s.as_os_str().to_string_lossy(); @@ -189,9 +186,9 @@ pub fn include_aseprite_inner(input: TokenStream) -> TokenStream { ]; const TAGS: &TagMap = &TagMap::new( - phf::phf_map! { + &[ #(#tags),* - } + ] ); }; diff --git a/agb/Cargo.lock b/agb/Cargo.lock index 017e679b..d910024c 100644 --- a/agb/Cargo.lock +++ b/agb/Cargo.lock @@ -26,7 +26,6 @@ dependencies = [ "bitflags", "hashbrown", "modular-bitfield", - "phf", "rustc-hash", ] @@ -315,50 +314,6 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" -[[package]] -name = "phf" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" -dependencies = [ - "phf_macros", - "phf_shared", - "proc-macro-hack", -] - -[[package]] -name = "phf_generator" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" -dependencies = [ - "phf_shared", - "rand", -] - -[[package]] -name = "phf_macros" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" -dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro-hack", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher", -] - [[package]] name = "png" version = "0.17.5" @@ -377,12 +332,6 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" -[[package]] -name = "proc-macro-hack" -version = "0.5.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" - [[package]] name = "proc-macro2" version = "1.0.36" @@ -467,12 +416,6 @@ dependencies = [ "syn", ] -[[package]] -name = "siphasher" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a86232ab60fa71287d7f2ddae4a7073f6b7aac33631c3015abb556f08c6d0a3e" - [[package]] name = "static_assertions" version = "1.1.0" diff --git a/agb/Cargo.toml b/agb/Cargo.toml index be4f27a3..b46ba012 100644 --- a/agb/Cargo.toml +++ b/agb/Cargo.toml @@ -28,7 +28,6 @@ bare-metal = "1.0" hashbrown = "0.12.0" modular-bitfield = "0.11.2" rustc-hash = { version = "1.0", default-features = false } -phf = { version = "0.10", default-features = false, features = ["macros"] } [package.metadata.docs.rs] default-target = "thumbv6m-none-eabi" diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index a1e1b13b..b4ca788e 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -69,7 +69,6 @@ macro_rules! include_aseprite { ($($aseprite_path: expr),*) => {{ use $crate::display::object::{Size, Sprite, Tag, TagMap}; use $crate::display::palette16::Palette16; - use $crate::phf; $crate::include_aseprite_inner!($($aseprite_path),*); @@ -77,18 +76,44 @@ macro_rules! include_aseprite { }}; } -pub struct TagMap(phf::Map<&'static str, Tag>); - -impl TagMap { - pub const fn new(map: phf::Map<&'static str, Tag>) -> Self { - Self(map) - } +pub struct TagMap { + tags: &'static [(&'static str, Tag)], } -impl Deref for TagMap { - type Target = phf::Map<&'static str, Tag>; - fn deref(&self) -> &Self::Target { - &self.0 +const fn const_byte_compare(a: &[u8], b: &[u8]) -> bool { + if a.len() != b.len() { + return false; + } + + let mut i = 0; + while i < a.len() { + if a[i] != b[i] { + return false; + } + i += 1; + } + true +} + +impl TagMap { + pub const fn new(tags: &'static [(&'static str, Tag)]) -> TagMap { + Self { tags } + } + pub const fn get(&'static self, tag: &str) -> Option<&'static Tag> { + let mut i = 0; + while i < self.tags.len() { + let s = self.tags[i].0; + if const_byte_compare(s.as_bytes(), tag.as_bytes()) { + return Some(&self.tags[i].1); + } + + i += 1; + } + + None + } + pub fn values(&self) -> impl Iterator { + self.tags.iter().map(|x| &x.1) } } @@ -317,10 +342,9 @@ impl ObjectController { index: inner.pop()?, free_list: &self.free_objects, }; - let p_sprite = sprite.clone(); Some(Object { + previous_sprite: sprite.clone(), sprite, - previous_sprite: p_sprite, loan, attrs: Attributes::new(), }) diff --git a/agb/src/lib.rs b/agb/src/lib.rs index 181d9c04..075b776a 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -113,7 +113,6 @@ pub use agb_image_converter::include_gfx; pub use agb_image_converter::include_aseprite_inner; -pub use phf; /// This macro declares the entry point to your game written using `agb`. /// diff --git a/examples/the-hat-chooses-the-wizard/Cargo.lock b/examples/the-hat-chooses-the-wizard/Cargo.lock index 0cb83886..c0f73bf9 100644 --- a/examples/the-hat-chooses-the-wizard/Cargo.lock +++ b/examples/the-hat-chooses-the-wizard/Cargo.lock @@ -26,7 +26,6 @@ dependencies = [ "bitflags", "hashbrown", "modular-bitfield", - "phf", "rustc-hash", ] @@ -322,50 +321,6 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" -[[package]] -name = "phf" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" -dependencies = [ - "phf_macros", - "phf_shared", - "proc-macro-hack", -] - -[[package]] -name = "phf_generator" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" -dependencies = [ - "phf_shared", - "rand", -] - -[[package]] -name = "phf_macros" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" -dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro-hack", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher", -] - [[package]] name = "png" version = "0.16.8" @@ -378,18 +333,6 @@ dependencies = [ "miniz_oxide 0.3.7", ] -[[package]] -name = "ppv-lite86" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" - -[[package]] -name = "proc-macro-hack" -version = "0.5.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" - [[package]] name = "proc-macro2" version = "1.0.36" @@ -408,36 +351,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom", -] - [[package]] name = "rustc-hash" version = "1.1.0" @@ -481,12 +394,6 @@ dependencies = [ "serde", ] -[[package]] -name = "siphasher" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a86232ab60fa71287d7f2ddae4a7073f6b7aac33631c3015abb556f08c6d0a3e" - [[package]] name = "static_assertions" version = "1.1.0" From 900cd007df62f1316330344ebfbdc1a9ad49868f Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 5 Mar 2022 17:50:37 +0000 Subject: [PATCH 42/51] pull out constants --- agb/src/display/object.rs | 9 +++- .../the-hat-chooses-the-wizard/src/enemies.rs | 44 ++++++++--------- .../the-hat-chooses-the-wizard/src/main.rs | 48 +++++++++---------- 3 files changed, 50 insertions(+), 51 deletions(-) diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index b4ca788e..7398cf59 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -99,7 +99,7 @@ impl TagMap { pub const fn new(tags: &'static [(&'static str, Tag)]) -> TagMap { Self { tags } } - pub const fn get(&'static self, tag: &str) -> Option<&'static Tag> { + pub const fn try_get(&'static self, tag: &str) -> Option<&'static Tag> { let mut i = 0; while i < self.tags.len() { let s = self.tags[i].0; @@ -112,6 +112,13 @@ impl TagMap { None } + pub const fn get(&'static self, tag: &str) -> &'static Tag { + let t = self.try_get(tag); + match t { + Some(t) => t, + None => panic!("The requested tag does not exist"), + } + } pub fn values(&self) -> impl Iterator { self.tags.iter().map(|x| &x.1) } diff --git a/examples/the-hat-chooses-the-wizard/src/enemies.rs b/examples/the-hat-chooses-the-wizard/src/enemies.rs index c30c0ebe..da9df438 100644 --- a/examples/the-hat-chooses-the-wizard/src/enemies.rs +++ b/examples/the-hat-chooses-the-wizard/src/enemies.rs @@ -2,10 +2,19 @@ use crate::TAG_MAP; use super::{sfx::SfxPlayer, Entity, FixedNumberType, HatState, Level}; use agb::{ - display::object::{ObjectController, Size}, + display::object::{ObjectController, Size, Tag}, fixnum::Vector2D, }; +const SLIME_IDLE: &Tag = TAG_MAP.get("Slime Idle"); +const SLIME_JUMP: &Tag = TAG_MAP.get("Slime Jump"); +const SLIME_SPLAT: &Tag = TAG_MAP.get("Slime splat"); + +const SNAIL_EMERGE: &Tag = TAG_MAP.get("Snail Emerge"); +const SNAIL_MOVE: &Tag = TAG_MAP.get("Snail Move"); +const SNAIL_DEATH: &Tag = TAG_MAP.get("Snail Death"); +const SNAIL_IDLE: &Tag = TAG_MAP.get("Snail Idle"); + enum UpdateState { Nothing, KillPlayer, @@ -155,8 +164,7 @@ impl<'a> Slime<'a> { SlimeState::Idle => { let offset = (timer / 16) as usize; - let tag = TAG_MAP.get("Slime Idle").unwrap(); - let frame = tag.get_animation_sprite(offset); + let frame = SLIME_IDLE.get_animation_sprite(offset); let sprite = controller.get_sprite(frame).unwrap(); self.enemy_info.entity.sprite.set_sprite(sprite); @@ -196,8 +204,7 @@ impl<'a> Slime<'a> { self.enemy_info.entity.velocity = (0, 0).into(); self.state = SlimeState::Idle; } else { - let tag = TAG_MAP.get("Slime Jump").unwrap(); - let frame = tag.get_animation_sprite(offset); + let frame = SLIME_JUMP.get_animation_sprite(offset); let sprite = controller.get_sprite(frame).unwrap(); self.enemy_info.entity.sprite.set_sprite(sprite); @@ -223,8 +230,7 @@ impl<'a> Slime<'a> { return UpdateState::Remove; } - let tag = TAG_MAP.get("Slime splat").unwrap(); - let frame = tag.get_animation_sprite(offset); + let frame = SLIME_SPLAT.get_animation_sprite(offset); let sprite = controller.get_sprite(frame).unwrap(); self.enemy_info.entity.sprite.set_sprite(sprite); @@ -295,8 +301,7 @@ impl<'a> Snail<'a> { } } - let tag = TAG_MAP.get("Snail Idle").unwrap(); - let frame = tag.get_animation_sprite(0); + let frame = SNAIL_IDLE.get_animation_sprite(0); let sprite = controller.get_sprite(frame).unwrap(); self.enemy_info.entity.sprite.set_sprite(sprite); @@ -316,8 +321,7 @@ impl<'a> Snail<'a> { } self.enemy_info.entity.velocity = (0, 0).into(); - let tag = TAG_MAP.get("Snail Emerge").unwrap(); - let frame = tag.get_animation_sprite(offset); + let frame = SNAIL_EMERGE.get_animation_sprite(offset); let sprite = controller.get_sprite(frame).unwrap(); self.enemy_info.entity.sprite.set_sprite(sprite); @@ -339,8 +343,7 @@ impl<'a> Snail<'a> { let offset = (timer - time) as usize / 8; - let tag = TAG_MAP.get("Snail Move").unwrap(); - let frame = tag.get_animation_sprite(offset); + let frame = SNAIL_MOVE.get_animation_sprite(offset); let sprite = controller.get_sprite(frame).unwrap(); self.enemy_info.entity.sprite.set_sprite(sprite); @@ -374,8 +377,7 @@ impl<'a> Snail<'a> { self.state = SnailState::Idle(timer); } - let tag = TAG_MAP.get("Snail Emerge").unwrap(); - let frame = tag.get_animation_sprite(offset); + let frame = SNAIL_EMERGE.get_animation_sprite(offset); let sprite = controller.get_sprite(frame).unwrap(); self.enemy_info.entity.sprite.set_sprite(sprite); @@ -396,17 +398,11 @@ impl<'a> Snail<'a> { let offset = (timer - time) as usize / 4; let frame = if offset < 5 { - TAG_MAP - .get("Snail Emerge") - .unwrap() - .get_animation_sprite(5 - offset) + SNAIL_EMERGE.get_animation_sprite(5 - offset) } else if offset == 5 { - TAG_MAP.get("Snail Idle").unwrap().get_animation_sprite(0) + SNAIL_IDLE.get_animation_sprite(0) } else if offset < 5 + 7 { - TAG_MAP - .get("Snail Death") - .unwrap() - .get_animation_sprite(offset - 5) + SNAIL_DEATH.get_animation_sprite(offset - 5) } else { return UpdateState::Remove; }; diff --git a/examples/the-hat-chooses-the-wizard/src/main.rs b/examples/the-hat-chooses-the-wizard/src/main.rs index 262154e2..ef347aea 100644 --- a/examples/the-hat-chooses-the-wizard/src/main.rs +++ b/examples/the-hat-chooses-the-wizard/src/main.rs @@ -85,7 +85,7 @@ agb::include_gfx!("gfx/tile_sheet.toml"); use agb::{ display::{ background::BackgroundRegular, - object::{Object, ObjectController, Sprite, TagMap}, + object::{Object, ObjectController, Sprite, Tag, TagMap}, Priority, HEIGHT, WIDTH, }, fixnum::{FixedNum, Vector2D}, @@ -93,9 +93,16 @@ use agb::{ }; const SPRITE_TAGS: (&[Sprite], &TagMap) = agb::include_aseprite!("gfx/sprites.aseprite"); -const SPRITES: &[Sprite] = SPRITE_TAGS.0; const TAG_MAP: &TagMap = SPRITE_TAGS.1; +const WALKING: &Tag = TAG_MAP.get("Walking"); +const JUMPING: &Tag = TAG_MAP.get("Jumping"); +const FALLING: &Tag = TAG_MAP.get("Falling"); +const PLAYER_DEATH: &Tag = TAG_MAP.get("Player Death"); +const HAT_SPIN_1: &Tag = TAG_MAP.get("HatSpin"); +const HAT_SPIN_2: &Tag = TAG_MAP.get("HatSpin2"); +const HAT_SPIN_3: &Tag = TAG_MAP.get("HatSpin3"); + type FixedNumberType = FixedNum<10>; pub struct Entity<'a> { @@ -107,9 +114,7 @@ pub struct Entity<'a> { impl<'a> Entity<'a> { pub fn new(object: &'a ObjectController, collision_mask: Vector2D) -> Self { - let dummy_sprite = object - .get_sprite(TAG_MAP.get("Walking").unwrap().get_sprite(0)) - .unwrap(); + let dummy_sprite = object.get_sprite(WALKING.get_sprite(0)).unwrap(); let mut sprite = object.get_object(dummy_sprite).unwrap(); sprite.set_priority(Priority::P1); Entity { @@ -350,16 +355,11 @@ impl<'a> Player<'a> { let mut wizard = Entity::new(controller, (6_u16, 14_u16).into()); let mut hat = Entity::new(controller, (6_u16, 6_u16).into()); - wizard.sprite.set_sprite( - controller - .get_sprite(TAG_MAP.get("Walking").unwrap().get_sprite(0)) - .unwrap(), - ); - hat.sprite.set_sprite( - controller - .get_sprite(TAG_MAP.get("HatSpin").unwrap().get_sprite(0)) - .unwrap(), - ); + wizard + .sprite + .set_sprite(controller.get_sprite(HAT_SPIN_1.get_sprite(0)).unwrap()); + hat.sprite + .set_sprite(controller.get_sprite(HAT_SPIN_1.get_sprite(0)).unwrap()); wizard.sprite.show(); hat.sprite.show(); @@ -464,8 +464,7 @@ impl<'a> Player<'a> { let offset = (ping_pong(timer / 16, 4)) as usize; self.wizard_frame = offset as u8; - let walk = TAG_MAP.get("Walking").unwrap(); - let frame = walk.get_animation_sprite(offset); + let frame = WALKING.get_animation_sprite(offset); let sprite = controller.get_sprite(frame).unwrap(); self.wizard.sprite.set_sprite(sprite); @@ -475,8 +474,7 @@ impl<'a> Player<'a> { // going up self.wizard_frame = 5; - let walk = TAG_MAP.get("Jumping").unwrap(); - let frame = walk.get_animation_sprite(0); + let frame = JUMPING.get_animation_sprite(0); let sprite = controller.get_sprite(frame).unwrap(); self.wizard.sprite.set_sprite(sprite); @@ -491,8 +489,7 @@ impl<'a> Player<'a> { self.wizard_frame = 0; - let walk = TAG_MAP.get("Falling").unwrap(); - let frame = walk.get_animation_sprite(offset); + let frame = FALLING.get_animation_sprite(offset); let sprite = controller.get_sprite(frame).unwrap(); self.wizard.sprite.set_sprite(sprite); @@ -504,9 +501,9 @@ impl<'a> Player<'a> { } let hat_base_tile = match self.num_recalls { - 0 => TAG_MAP.get("HatSpin").unwrap(), - 1 => TAG_MAP.get("HatSpin2").unwrap(), - _ => TAG_MAP.get("HatSpin3").unwrap(), + 0 => HAT_SPIN_1, + 1 => HAT_SPIN_2, + _ => HAT_SPIN_3, }; match self.facing { @@ -689,8 +686,7 @@ impl<'a, 'b, 'c> PlayingLevel<'a, 'b> { fn dead_update(&mut self, controller: &'a ObjectController) -> bool { self.timer += 1; - let tag = TAG_MAP.get("Player Death").unwrap(); - let frame = tag.get_animation_sprite(self.timer as usize / 8); + let frame = PLAYER_DEATH.get_animation_sprite(self.timer as usize / 8); let sprite = controller.get_sprite(frame).unwrap(); self.player.wizard.velocity += (0.into(), FixedNumberType::new(1) / 32).into(); From 4c57f85acda363bfc1901fff45873edf8b3981f4 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 5 Mar 2022 18:02:52 +0000 Subject: [PATCH 43/51] Inline the modulos --- agb/src/display/object.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 7398cf59..b0ffb3ec 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -157,6 +157,7 @@ impl Tag { &self.get_sprites()[idx] } + #[inline] pub fn get_animation_sprite(&self, idx: usize) -> &'static Sprite { let len_sub_1 = self.len - 1; match self.direction { From 9dca54c9bbb02eb4c0f5c49860d6c6411502a508 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sun, 6 Mar 2022 02:54:52 +0000 Subject: [PATCH 44/51] update to use new sprite system --- examples/the-purple-night/Cargo.lock | 170 ++++++- examples/the-purple-night/gfx/boss.aseprite | Bin 6026 -> 6027 bytes examples/the-purple-night/src/main.rs | 463 ++++++++++++-------- 3 files changed, 428 insertions(+), 205 deletions(-) diff --git a/examples/the-purple-night/Cargo.lock b/examples/the-purple-night/Cargo.lock index 5566eb7e..b0de5db9 100644 --- a/examples/the-purple-night/Cargo.lock +++ b/examples/the-purple-night/Cargo.lock @@ -24,6 +24,9 @@ dependencies = [ "agb_sound_converter", "bare-metal", "bitflags", + "hashbrown", + "modular-bitfield", + "rustc-hash", ] [[package]] @@ -37,6 +40,7 @@ dependencies = [ name = "agb_image_converter" version = "0.6.0" dependencies = [ + "asefile", "image", "proc-macro2", "quote", @@ -64,6 +68,31 @@ dependencies = [ "syn", ] +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "asefile" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0d5f7de918fd4cb18249819fc4bd27f6a5dbfbc9dcb271727f27dacf17ce880" +dependencies = [ + "bitflags", + "byteorder", + "flate2", + "image", + "log", + "nohash", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -132,11 +161,24 @@ dependencies = [ [[package]] name = "deflate" -version = "1.0.0" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f" +checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" dependencies = [ "adler32", + "byteorder", +] + +[[package]] +name = "flate2" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" +dependencies = [ + "cfg-if 1.0.0", + "crc32fast", + "libc", + "miniz_oxide 0.4.4", ] [[package]] @@ -148,6 +190,26 @@ dependencies = [ "cfg-if 0.1.10", ] +[[package]] +name = "getrandom" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c21d40587b92fa6a6c6e3c1bdbf87d75511db5672f9c93175574b3a00df1758" +dependencies = [ + "ahash", +] + [[package]] name = "hound" version = "3.4.0" @@ -156,9 +218,9 @@ checksum = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549" [[package]] name = "image" -version = "0.24.1" +version = "0.23.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db207d030ae38f1eb6f240d5a1c1c88ff422aa005d10f8c6c6fc5e75286ab30e" +checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1" dependencies = [ "bytemuck", "byteorder", @@ -169,6 +231,12 @@ dependencies = [ "png", ] +[[package]] +name = "libc" +version = "0.2.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" + [[package]] name = "libflate" version = "0.1.27" @@ -182,14 +250,60 @@ dependencies = [ ] [[package]] -name = "miniz_oxide" -version = "0.5.1" +name = "log" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "miniz_oxide" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +dependencies = [ + "adler32", +] + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" dependencies = [ "adler", + "autocfg", ] +[[package]] +name = "modular-bitfield" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74" +dependencies = [ + "modular-bitfield-impl", + "static_assertions", +] + +[[package]] +name = "modular-bitfield-impl" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "nohash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0f889fb66f7acdf83442c35775764b51fed3c606ab9cee51500dbde2cf528ca" + [[package]] name = "num-integer" version = "0.1.44" @@ -213,9 +327,9 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" dependencies = [ "autocfg", "num-integer", @@ -232,15 +346,21 @@ dependencies = [ ] [[package]] -name = "png" -version = "0.17.5" +name = "once_cell" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" + +[[package]] +name = "png" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" dependencies = [ "bitflags", "crc32fast", "deflate", - "miniz_oxide", + "miniz_oxide 0.3.7", ] [[package]] @@ -267,6 +387,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "serde" version = "1.0.136" @@ -287,6 +413,12 @@ dependencies = [ "syn", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "syn" version = "1.0.86" @@ -340,6 +472,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + [[package]] name = "xml-rs" version = "0.8.4" diff --git a/examples/the-purple-night/gfx/boss.aseprite b/examples/the-purple-night/gfx/boss.aseprite index 57487ec7f0aa6822324ddd497887bb3c79b5970c..935c9784319c97e561eb565aa1cd91d7b2e99f30 100644 GIT binary patch delta 33 pcmeCu@78DRp2%3wXt1%-osm&}@<~PoHWmh_{Nm!x?-=KZ0syM}3RM6A delta 32 ocmeCy@6u=Nn#fqssK2q%osm&(@<~PoR%V8f#PrSY8Rv)s0H9C_)&Kwi diff --git a/examples/the-purple-night/src/main.rs b/examples/the-purple-night/src/main.rs index ea00124f..3178947e 100644 --- a/examples/the-purple-night/src/main.rs +++ b/examples/the-purple-night/src/main.rs @@ -15,7 +15,7 @@ use rng::get_random; use agb::{ display::{ background::{BackgroundDistributor, BackgroundRegular}, - object::{ObjectControl, ObjectStandard}, + object::{Object, ObjectController, Sprite, Tag, TagMap}, Priority, HEIGHT, WIDTH, }, fixnum::{FixedNum, Rect, Vector2D}, @@ -23,7 +23,34 @@ use agb::{ }; use generational_arena::Arena; -agb::include_gfx!("gfx/objects.toml"); +const SPRITE_TAGS: (&[Sprite], &TagMap) = + agb::include_aseprite!("gfx/objects.aseprite", "gfx/boss.aseprite"); +const TAG_MAP: &TagMap = SPRITE_TAGS.1; + +const LONGSWORD_IDLE: &Tag = TAG_MAP.get("Idle - longsword"); +const LONGSWORD_WALK: &Tag = TAG_MAP.get("Walk - longsword"); +const LONGSWORD_JUMP: &Tag = TAG_MAP.get("Jump - longsword"); +const LONGSWORD_ATTACK: &Tag = TAG_MAP.get("Attack - longsword"); +const LONGSWORD_JUMP_ATTACK: &Tag = TAG_MAP.get("Jump attack - longsword"); + +const SHORTSWORD_IDLE: &Tag = TAG_MAP.get("Idle - shortsword"); +const SHORTSWORD_WALK: &Tag = TAG_MAP.get("Walk - shortsword"); +const SHORTSWORD_JUMP: &Tag = TAG_MAP.get("jump - shortsword"); +const SHORTSWORD_ATTACK: &Tag = TAG_MAP.get("attack - shortsword"); +const SHORTSWORD_JUMP_ATTACK: &Tag = TAG_MAP.get("jump attack - shortsword"); + +const KNIFE_IDLE: &Tag = TAG_MAP.get("idle - knife"); +const KNIFE_WALK: &Tag = TAG_MAP.get("walk - knife"); +const KNIFE_JUMP: &Tag = TAG_MAP.get("jump - knife"); +const KNIFE_ATTACK: &Tag = TAG_MAP.get("attack - knife"); +const KNIFE_JUMP_ATTACK: &Tag = TAG_MAP.get("jump attack - knife"); + +const SWORDLESS_IDLE: &Tag = TAG_MAP.get("idle swordless"); +const SWORDLESS_WALK: &Tag = TAG_MAP.get("walk swordless"); +const SWORDLESS_JUMP: &Tag = TAG_MAP.get("jump swordless"); +const SWORDLESS_ATTACK: &Tag = KNIFE_ATTACK; +const SWORDLESS_JUMP_ATTACK: &Tag = KNIFE_JUMP_ATTACK; + agb::include_gfx!("gfx/background.toml"); type Number = FixedNum<8>; @@ -127,7 +154,7 @@ impl Level { } struct Entity<'a> { - sprite: ObjectStandard<'a>, + sprite: Object<'a, 'a>, position: Vector2D, velocity: Vector2D, collision_mask: Rect, @@ -135,8 +162,11 @@ struct Entity<'a> { } impl<'a> Entity<'a> { - fn new(object_controller: &'a ObjectControl, collision_mask: Rect) -> Self { - let mut sprite = object_controller.get_object_standard(); + fn new(object_controller: &'a ObjectController, collision_mask: Rect) -> Self { + let s = object_controller + .get_sprite(LONGSWORD_IDLE.get_sprite(0)) + .unwrap(); + let mut sprite = object_controller.get_object(s).unwrap(); sprite.set_priority(Priority::P1); Entity { sprite, @@ -321,34 +351,30 @@ impl SwordState { SwordState::Swordless => Number::new(6) / 256, } } - fn idle_animation(self, counter: &mut u16) -> u16 { - if *counter >= 4 * 8 { - *counter = 0; - } + fn idle_animation(self, counter: u16) -> &'static Sprite { + let counter = counter as usize; match self { - SwordState::LongSword => (*counter / 8) * 4, - SwordState::ShortSword => (41 + *counter / 8) * 4, - SwordState::Dagger => (96 + *counter / 8) * 4, - SwordState::Swordless => (154 + *counter / 8) * 4, + SwordState::LongSword => LONGSWORD_IDLE.get_animation_sprite(counter / 8), + SwordState::ShortSword => SHORTSWORD_IDLE.get_animation_sprite(counter / 8), + SwordState::Dagger => KNIFE_IDLE.get_animation_sprite(counter / 8), + SwordState::Swordless => SWORDLESS_IDLE.get_animation_sprite(counter / 8), } } - fn jump_offset(self) -> u16 { + fn jump_tag(self) -> &'static Tag { match self { - SwordState::LongSword => 10, - SwordState::ShortSword => 51, - SwordState::Dagger => 106, - SwordState::Swordless => 164, + SwordState::LongSword => LONGSWORD_JUMP, + SwordState::ShortSword => SHORTSWORD_JUMP, + SwordState::Dagger => KNIFE_JUMP, + SwordState::Swordless => SWORDLESS_JUMP, } } - fn walk_animation(self, counter: &mut u16) -> u16 { - if *counter >= 6 * 4 { - *counter = 0; - } + fn walk_animation(self, counter: u16) -> &'static Sprite { + let counter = counter as usize; match self { - SwordState::LongSword => (4 + *counter / 4) * 4, - SwordState::ShortSword => (45 + *counter / 4) * 4, - SwordState::Dagger => (100 + *counter / 4) * 4, - SwordState::Swordless => (158 + *counter / 4) * 4, + SwordState::LongSword => LONGSWORD_WALK.get_animation_sprite(counter / 4), + SwordState::ShortSword => SHORTSWORD_WALK.get_animation_sprite(counter / 4), + SwordState::Dagger => KNIFE_WALK.get_animation_sprite(counter / 4), + SwordState::Swordless => SWORDLESS_WALK.get_animation_sprite(counter / 4), } } fn attack_duration(self) -> u16 { @@ -375,20 +401,20 @@ impl SwordState { SwordState::Swordless => (self.attack_duration() - timer) / 8, } } + fn jump_attack_tag(self) -> &'static Tag { + match self { + SwordState::LongSword => LONGSWORD_JUMP_ATTACK, + SwordState::ShortSword => SHORTSWORD_JUMP_ATTACK, + SwordState::Dagger => KNIFE_JUMP_ATTACK, + SwordState::Swordless => SWORDLESS_JUMP_ATTACK, + } + } fn jump_attack_frame(self, timer: u16) -> u16 { (self.jump_attack_duration() - timer) / 8 } fn hold_frame(self) -> u16 { 7 } - fn jump_attack_hold_frame(self) -> u16 { - match self { - SwordState::LongSword => 13, - SwordState::ShortSword => 54, - SwordState::Dagger => 109, - SwordState::Swordless => 0, - } - } fn cooldown_time(self) -> u16 { match self { @@ -398,26 +424,15 @@ impl SwordState { SwordState::Swordless => 0, } } - fn to_sprite_id(self, frame: u16) -> u16 { + fn attack_tag(self) -> &'static Tag { match self { - SwordState::LongSword => (16 + frame) * 4, - SwordState::ShortSword => (57 + frame) * 4, - SwordState::Dagger => (112 + frame) * 4, - SwordState::Swordless => 0, - } - } - fn to_jump_sprite_id(self, frame: u16) -> u16 { - if frame == self.jump_attack_hold_frame() { - frame * 4 - } else { - match self { - SwordState::LongSword => (24 + frame) * 4, - SwordState::ShortSword => (65 + frame) * 4, - SwordState::Dagger => (120 + frame) * 4, - SwordState::Swordless => 0, - } + SwordState::LongSword => LONGSWORD_ATTACK, + SwordState::ShortSword => SHORTSWORD_ATTACK, + SwordState::Dagger => KNIFE_ATTACK, + SwordState::Swordless => SWORDLESS_ATTACK, } } + fn fudge(self, frame: u16) -> i32 { match self { SwordState::LongSword => long_sword_fudge(frame), @@ -520,15 +535,15 @@ struct Player<'a> { } impl<'a> Player<'a> { - fn new(object_controller: &'a ObjectControl) -> Player { + fn new(object_controller: &'a ObjectController) -> Player { let mut entity = Entity::new( object_controller, Rect::new((0_u16, 0_u16).into(), (4_u16, 12_u16).into()), ); - entity - .sprite - .set_sprite_size(agb::display::object::Size::S16x16); - entity.sprite.set_tile_id(0); + let s = object_controller + .get_sprite(LONGSWORD_IDLE.get_sprite(0)) + .unwrap(); + entity.sprite.set_sprite(s); entity.sprite.show(); entity.position = (144, 0).into(); entity.sprite.commit(); @@ -549,6 +564,7 @@ impl<'a> Player<'a> { fn update( &mut self, + controller: &'a ObjectController, buttons: &ButtonController, level: &Level, sfx: &mut sfx::Sfx, @@ -580,13 +596,15 @@ impl<'a> Player<'a> { self.entity.sprite.set_hflip(self.facing == Tri::Negative); self.entity.velocity.x += self.sword.ground_walk_force() * x as i32; if self.entity.velocity.x.abs() > Number::new(1) / 10 { - self.entity - .sprite - .set_tile_id(self.sword.walk_animation(&mut self.sprite_offset)); + let sprite = controller + .get_sprite(self.sword.walk_animation(self.sprite_offset)) + .unwrap(); + self.entity.sprite.set_sprite(sprite); } else { - self.entity - .sprite - .set_tile_id(self.sword.idle_animation(&mut self.sprite_offset)); + let sprite = controller + .get_sprite(self.sword.idle_animation(self.sprite_offset)) + .unwrap(); + self.entity.sprite.set_sprite(sprite); } if b_press && self.sword != SwordState::Swordless { @@ -604,9 +622,11 @@ impl<'a> Player<'a> { *a -= 1; let frame = self.sword.attack_frame(*a); self.fudge_factor.x = self.sword.fudge(frame) * self.facing as i32; - self.entity - .sprite - .set_tile_id(self.sword.to_sprite_id(frame)); + let tag = self.sword.attack_tag(); + let sprite = controller + .get_sprite(tag.get_animation_sprite(frame as usize)) + .unwrap(); + self.entity.sprite.set_sprite(sprite); hurtbox = self.sword.ground_attack_hurtbox(frame); @@ -618,9 +638,11 @@ impl<'a> Player<'a> { *a -= 1; let frame = self.sword.hold_frame(); self.fudge_factor.x = self.sword.fudge(frame) * self.facing as i32; - self.entity - .sprite - .set_tile_id(self.sword.to_sprite_id(frame)); + let tag = self.sword.attack_tag(); + let sprite = controller + .get_sprite(tag.get_animation_sprite(frame as usize)) + .unwrap(); + self.entity.sprite.set_sprite(sprite); if *a == 0 { self.attack_timer = AttackTimer::Idle; } @@ -632,7 +654,7 @@ impl<'a> Player<'a> { match &mut self.attack_timer { AttackTimer::Idle => { - let sprite = if self.sprite_offset < 3 * 4 { + let frame = if self.sprite_offset < 3 * 4 { self.sprite_offset / 4 } else if self.entity.velocity.y.abs() < Number::new(1) / 5 { 3 @@ -643,9 +665,11 @@ impl<'a> Player<'a> { } else { 2 }; - self.entity - .sprite - .set_tile_id((sprite + self.sword.jump_offset()) * 4); + let tag = self.sword.jump_tag(); + let sprite = controller + .get_sprite(tag.get_animation_sprite(frame as usize)) + .unwrap(); + self.entity.sprite.set_sprite(sprite); if x != Tri::Zero { self.facing = x; @@ -665,9 +689,11 @@ impl<'a> Player<'a> { AttackTimer::Attack(a) => { *a -= 1; let frame = self.sword.jump_attack_frame(*a); - self.entity - .sprite - .set_tile_id(self.sword.to_jump_sprite_id(frame)); + let tag = self.sword.jump_attack_tag(); + let sprite = controller + .get_sprite(tag.get_animation_sprite(frame as usize)) + .unwrap(); + self.entity.sprite.set_sprite(sprite); hurtbox = self.sword.air_attack_hurtbox(frame); @@ -799,9 +825,10 @@ impl BatData { } } - fn update( + fn update<'a>( &mut self, - entity: &mut Entity, + controller: &'a ObjectController, + entity: &mut Entity<'a>, player: &Player, level: &Level, sfx: &mut sfx::Sfx, @@ -814,6 +841,8 @@ impl BatData { .unwrap_or(false); let should_damage = entity.collider().touches(player.entity.collider()); + const BAT_IDLE: &Tag = TAG_MAP.get("bat"); + match &mut self.bat_state { BatState::Idle => { self.sprite_offset += 1; @@ -825,7 +854,10 @@ impl BatData { sfx.bat_flap(); } - entity.sprite.set_tile_id((78 + self.sprite_offset / 8) * 4); + let sprite = BAT_IDLE.get_sprite(self.sprite_offset as usize / 8); + let sprite = controller.get_sprite(sprite).unwrap(); + + entity.sprite.set_sprite(sprite); if (entity.position - player.entity.position).manhattan_distance() < 50.into() { self.bat_state = BatState::Chasing(300); @@ -856,7 +888,11 @@ impl BatData { if self.sprite_offset >= 9 * 2 { self.sprite_offset = 0; } - entity.sprite.set_tile_id((78 + self.sprite_offset / 2) * 4); + + let sprite = BAT_IDLE.get_sprite(self.sprite_offset as usize / 2); + let sprite = controller.get_sprite(sprite).unwrap(); + + entity.sprite.set_sprite(sprite); if self.sprite_offset == 2 * 5 { sfx.bat_flap(); @@ -879,7 +915,12 @@ impl BatData { } } BatState::Dead => { - entity.sprite.set_tile_id(87 * 4); + const BAT_DEAD: &Tag = TAG_MAP.get("bat dead"); + let sprite = BAT_DEAD.get_sprite(0); + let sprite = controller.get_sprite(sprite).unwrap(); + + entity.sprite.set_sprite(sprite); + let gravity: Number = 1.into(); let gravity = gravity / 16; entity.velocity.x = 0.into(); @@ -917,9 +958,10 @@ impl SlimeData { } } - fn update( + fn update<'a>( &mut self, - entity: &mut Entity, + controller: &'a ObjectController, + entity: &mut Entity<'a>, player: &Player, level: &Level, sfx: &mut sfx::Sfx, @@ -940,9 +982,12 @@ impl SlimeData { self.sprite_offset = 0; } - entity - .sprite - .set_tile_id((29 + self.sprite_offset / 16) * 4); + const IDLE: &Tag = TAG_MAP.get("slime idle"); + + let sprite = IDLE.get_sprite(self.sprite_offset as usize / 16); + let sprite = controller.get_sprite(sprite).unwrap(); + + entity.sprite.set_sprite(sprite); if (player.entity.position - entity.position).manhattan_distance() < 40.into() { let direction = match player.entity.position.x.cmp(&entity.position.x) { @@ -977,7 +1022,12 @@ impl SlimeData { sfx.slime_boing(); } - entity.sprite.set_tile_id((frame + 31) * 4); + const CHASE: &Tag = TAG_MAP.get("Slime jump"); + + let sprite = CHASE.get_sprite(frame as usize); + let sprite = controller.get_sprite(sprite).unwrap(); + + entity.sprite.set_sprite(sprite); entity.velocity.x = match frame { 2 | 3 | 4 => (Number::new(1) / 5) * Number::new(*direction as i32), @@ -1003,7 +1053,11 @@ impl SlimeData { } SlimeState::Dead(count) => { if *count < 5 * 4 { - entity.sprite.set_tile_id((36 + *count / 4) * 4); + const DEATH: &Tag = TAG_MAP.get("Slime death"); + let sprite = DEATH.get_sprite(*count as usize / 4); + let sprite = controller.get_sprite(sprite).unwrap(); + + entity.sprite.set_sprite(sprite); *count += 1; } else { return UpdateInstruction::Remove; @@ -1033,9 +1087,10 @@ impl MiniFlameData { } } - fn update( + fn update<'a>( &mut self, - entity: &mut Entity, + controller: &'a ObjectController, + entity: &mut Entity<'a>, player: &Player, _level: &Level, sfx: &mut sfx::Sfx, @@ -1051,6 +1106,8 @@ impl MiniFlameData { self.sprite_offset += 1; + const ANGRY: &Tag = TAG_MAP.get("angry boss"); + match &mut self.state { MiniFlameState::Idle(frames) => { *frames -= 1; @@ -1065,13 +1122,9 @@ impl MiniFlameData { entity.velocity = resulting_direction.normalise() * Number::new(2); } } else { - if self.sprite_offset >= 12 * 8 { - self.sprite_offset = 0; - } - - entity - .sprite - .set_tile_id((137 + self.sprite_offset / 8) * 4); + let sprite = ANGRY.get_animation_sprite(self.sprite_offset as usize / 8); + let sprite = controller.get_sprite(sprite).unwrap(); + entity.sprite.set_sprite(sprite); entity.velocity = (0.into(), Number::new(-1) / Number::new(4)).into(); } @@ -1113,17 +1166,13 @@ impl MiniFlameData { instruction = UpdateInstruction::DamagePlayer; } - if self.sprite_offset >= 12 * 2 { - self.sprite_offset = 0; - } - if entity.velocity.manhattan_distance() < Number::new(1) / Number::new(4) { self.state = MiniFlameState::Idle(90); } - entity - .sprite - .set_tile_id((137 + self.sprite_offset / 2) * 4); + let sprite = ANGRY.get_animation_sprite(self.sprite_offset as usize / 2); + let sprite = controller.get_sprite(sprite).unwrap(); + entity.sprite.set_sprite(sprite); } MiniFlameState::Dead => { entity.velocity = (0, 0).into(); @@ -1131,9 +1180,11 @@ impl MiniFlameData { instruction = UpdateInstruction::Remove; } - entity - .sprite - .set_tile_id((148 + self.sprite_offset / 12) * 4); + const DEATH: &Tag = TAG_MAP.get("angry boss dead"); + + let sprite = DEATH.get_animation_sprite(self.sprite_offset as usize / 12); + let sprite = controller.get_sprite(sprite).unwrap(); + entity.sprite.set_sprite(sprite); self.sprite_offset += 1; } @@ -1165,9 +1216,10 @@ impl EmuData { } } - fn update( + fn update<'a>( &mut self, - entity: &mut Entity, + controller: &'a ObjectController, + entity: &mut Entity<'a>, player: &Player, level: &Level, sfx: &mut sfx::Sfx, @@ -1189,9 +1241,11 @@ impl EmuData { self.sprite_offset = 0; } - entity - .sprite - .set_tile_id((170 + self.sprite_offset / 16) * 4); + const IDLE: &Tag = TAG_MAP.get("emu - idle"); + + let sprite = IDLE.get_sprite(self.sprite_offset as usize / 16); + let sprite = controller.get_sprite(sprite).unwrap(); + entity.sprite.set_sprite(sprite); if (entity.position.y - player.entity.position.y).abs() < 10.into() { let velocity = Number::new(1) @@ -1234,9 +1288,11 @@ impl EmuData { sfx.emu_step(); } - entity - .sprite - .set_tile_id((173 + self.sprite_offset / 2) * 4); + const WALK: &Tag = TAG_MAP.get("emu-walk"); + + let sprite = WALK.get_sprite(self.sprite_offset as usize / 2); + let sprite = controller.get_sprite(sprite).unwrap(); + entity.sprite.set_sprite(sprite); let gravity: Number = 1.into(); let gravity = gravity / 16; @@ -1287,9 +1343,12 @@ impl EmuData { instruction = UpdateInstruction::Remove; } - entity - .sprite - .set_tile_id((177 + self.sprite_offset / 4) * 4); + const DEATH: &Tag = TAG_MAP.get("emu - die"); + + let sprite = DEATH.get_animation_sprite(self.sprite_offset as usize / 4); + let sprite = controller.get_sprite(sprite).unwrap(); + entity.sprite.set_sprite(sprite); + self.sprite_offset += 1; } } @@ -1317,27 +1376,32 @@ impl EnemyData { } } - fn tile_id(&self) -> u16 { + fn sprite(&self) -> &'static Sprite { + const SLIME: &Tag = TAG_MAP.get("slime idle"); + const BAT: &Tag = TAG_MAP.get("bat"); + const MINI_FLAME: &Tag = TAG_MAP.get("angry boss"); + const EMU: &Tag = TAG_MAP.get("emu - idle"); match self { - EnemyData::Slime(_) => 29, - EnemyData::Bat(_) => 78, - EnemyData::MiniFlame(_) => 137, - EnemyData::Emu(_) => 170, + EnemyData::Slime(_) => SLIME.get_sprite(0), + EnemyData::Bat(_) => BAT.get_sprite(0), + EnemyData::MiniFlame(_) => MINI_FLAME.get_sprite(0), + EnemyData::Emu(_) => EMU.get_sprite(0), } } - fn update( + fn update<'a>( &mut self, - entity: &mut Entity, + controller: &'a ObjectController, + entity: &mut Entity<'a>, player: &Player, level: &Level, sfx: &mut sfx::Sfx, ) -> UpdateInstruction { match self { - EnemyData::Slime(data) => data.update(entity, player, level, sfx), - EnemyData::Bat(data) => data.update(entity, player, level, sfx), - EnemyData::MiniFlame(data) => data.update(entity, player, level, sfx), - EnemyData::Emu(data) => data.update(entity, player, level, sfx), + EnemyData::Slime(data) => data.update(controller, entity, player, level, sfx), + EnemyData::Bat(data) => data.update(controller, entity, player, level, sfx), + EnemyData::MiniFlame(data) => data.update(controller, entity, player, level, sfx), + EnemyData::Emu(data) => data.update(controller, entity, player, level, sfx), } } } @@ -1348,13 +1412,13 @@ struct Enemy<'a> { } impl<'a> Enemy<'a> { - fn new(object_controller: &'a ObjectControl, enemy_data: EnemyData) -> Self { + fn new(object_controller: &'a ObjectController, enemy_data: EnemyData) -> Self { let mut entity = Entity::new(object_controller, enemy_data.collision_mask()); - entity - .sprite - .set_sprite_size(agb::display::object::Size::S16x16); - entity.sprite.set_tile_id(enemy_data.tile_id()); + let sprite = enemy_data.sprite(); + let sprite = object_controller.get_sprite(sprite).unwrap(); + + entity.sprite.set_sprite(sprite); entity.sprite.show(); entity.sprite.commit(); @@ -1362,8 +1426,15 @@ impl<'a> Enemy<'a> { Self { entity, enemy_data } } - fn update(&mut self, player: &Player, level: &Level, sfx: &mut sfx::Sfx) -> UpdateInstruction { - self.enemy_data.update(&mut self.entity, player, level, sfx) + fn update( + &mut self, + controller: &'a ObjectController, + player: &Player, + level: &Level, + sfx: &mut sfx::Sfx, + ) -> UpdateInstruction { + self.enemy_data + .update(controller, &mut self.entity, player, level, sfx) } } @@ -1394,9 +1465,10 @@ impl ParticleData { } } - fn update( + fn update<'a>( &mut self, - entity: &mut Entity, + controller: &'a ObjectController, + entity: &mut Entity<'a>, player: &Player, _level: &Level, ) -> UpdateInstruction { @@ -1406,7 +1478,11 @@ impl ParticleData { return UpdateInstruction::Remove; } - entity.sprite.set_tile_id((70 + *frame / 3) * 4); + const DUST: &Tag = TAG_MAP.get("dust"); + let sprite = DUST.get_sprite(*frame as usize / 3); + let sprite = controller.get_sprite(sprite).unwrap(); + + entity.sprite.set_sprite(sprite); *frame += 1; UpdateInstruction::None @@ -1416,7 +1492,11 @@ impl ParticleData { return UpdateInstruction::Remove; // have played the animation 6 times } - entity.sprite.set_tile_id((88 + (*frame / 3) % 8) * 4); + const HEALTH: &Tag = TAG_MAP.get("Heath"); + let sprite = HEALTH.get_animation_sprite(*frame as usize / 3); + let sprite = controller.get_sprite(sprite).unwrap(); + + entity.sprite.set_sprite(sprite); if *frame < 8 * 3 * 3 { entity.velocity.y = Number::new(-1) / 2; @@ -1438,7 +1518,11 @@ impl ParticleData { UpdateInstruction::None } ParticleData::BossHealer(frame, target) => { - entity.sprite.set_tile_id((88 + (*frame / 3) % 8) * 4); + const HEALTH: &Tag = TAG_MAP.get("Heath"); + let sprite = HEALTH.get_animation_sprite(*frame as usize / 3); + let sprite = controller.get_sprite(sprite).unwrap(); + + entity.sprite.set_sprite(sprite); if *frame < 8 * 3 * 3 { entity.velocity.y = Number::new(-1) / 2; @@ -1471,7 +1555,7 @@ struct Particle<'a> { impl<'a> Particle<'a> { fn new( - object_controller: &'a ObjectControl, + object_controller: &'a ObjectController, particle_data: ParticleData, position: Vector2D, ) -> Self { @@ -1480,11 +1564,6 @@ impl<'a> Particle<'a> { Rect::new((0u16, 0u16).into(), (0u16, 0u16).into()), ); - entity - .sprite - .set_sprite_size(agb::display::object::Size::S16x16); - entity.sprite.set_tile_id(particle_data.tile_id() * 4); - entity.sprite.show(); entity.position = position; Self { @@ -1493,8 +1572,15 @@ impl<'a> Particle<'a> { } } - fn update(&mut self, player: &Player, level: &Level) -> UpdateInstruction { - self.particle_data.update(&mut self.entity, player, level) + fn update( + &mut self, + controller: &'a ObjectController, + player: &Player, + level: &Level, + ) -> UpdateInstruction { + self.entity.sprite.show(); + self.particle_data + .update(controller, &mut self.entity, player, level) } } @@ -1515,14 +1601,14 @@ impl<'a> BossState<'a> { fn update( &mut self, enemies: &mut Arena>, - object_controller: &'a ObjectControl, + object_controller: &'a ObjectController, player: &Player, sfx: &mut sfx::Sfx, ) -> BossInstruction { match self { BossState::Active(boss) => boss.update(enemies, object_controller, player, sfx), BossState::Following(boss) => { - boss.update(player); + boss.update(object_controller, player); BossInstruction::None } BossState::NotSpawned => BossInstruction::None, @@ -1550,15 +1636,13 @@ struct FollowingBoss<'a> { } impl<'a> FollowingBoss<'a> { - fn new(object_controller: &'a ObjectControl, position: Vector2D) -> Self { + fn new(object_controller: &'a ObjectController, position: Vector2D) -> Self { let mut entity = Entity::new( object_controller, Rect::new((0_u16, 0_u16).into(), (0_u16, 0_u16).into()), ); entity.position = position; - entity - .sprite - .set_sprite_size(agb::display::object::Size::S16x16); + Self { entity, following: true, @@ -1567,11 +1651,11 @@ impl<'a> FollowingBoss<'a> { gone: false, } } - fn update(&mut self, player: &Player) { + fn update(&mut self, controller: &'a ObjectController, player: &Player) { let difference = player.entity.position - self.entity.position; self.timer += 1; - if self.to_hole { + let frame = if self.to_hole { let target: Vector2D = (17 * 8, -3 * 8).into(); let difference = target - self.entity.position; if difference.manhattan_distance() < 1.into() { @@ -1580,26 +1664,30 @@ impl<'a> FollowingBoss<'a> { self.entity.velocity = difference.normalise() * 2; } - let frame = (self.timer / 8) % 12; - self.entity.sprite.set_tile_id((125 + frame as u16) * 4); + self.timer / 8 } else if self.timer < 120 { - let frame = (self.timer / 20) % 12; - self.entity.sprite.set_tile_id((125 + frame as u16) * 4); + self.timer / 20 } else if self.following { self.entity.velocity = difference / 16; if difference.manhattan_distance() < 20.into() { self.following = false; } - let frame = (self.timer / 8) % 12; - self.entity.sprite.set_tile_id((125 + frame as u16) * 4); + self.timer / 8 } else { self.entity.velocity = (0, 0).into(); if difference.manhattan_distance() > 60.into() { self.following = true; } - let frame = (self.timer / 16) % 12; - self.entity.sprite.set_tile_id((125 + frame as u16) * 4); - } + self.timer / 16 + }; + + const BOSS: &Tag = TAG_MAP.get("happy boss"); + + let sprite = BOSS.get_animation_sprite(frame as usize); + let sprite = controller.get_sprite(sprite).unwrap(); + + self.entity.sprite.set_sprite(sprite); + self.entity.update_position_without_collision(); } @@ -1632,15 +1720,11 @@ enum BossInstruction { } impl<'a> Boss<'a> { - fn new(object_controller: &'a ObjectControl, screen_coords: Vector2D) -> Self { + fn new(object_controller: &'a ObjectController, screen_coords: Vector2D) -> Self { let mut entity = Entity::new( object_controller, Rect::new((0_u16, 0_u16).into(), (28_u16, 28_u16).into()), ); - entity - .sprite - .set_sprite_size(agb::display::object::Size::S32x32); - entity.sprite.set_palette(1); entity.position = screen_coords + (144, 136).into(); Self { entity, @@ -1655,7 +1739,7 @@ impl<'a> Boss<'a> { fn update( &mut self, enemies: &mut Arena>, - object_controller: &'a ObjectControl, + object_controller: &'a ObjectController, player: &Player, sfx: &mut sfx::Sfx, ) -> BossInstruction { @@ -1731,8 +1815,14 @@ impl<'a> Boss<'a> { BossActiveState::WaitUntilKilled => 3.into(), }; self.timer += 1; - let frame = (self.timer / animation_rate) % 12; - self.entity.sprite.set_tile_id(784 + (frame as u16) * 16); + let frame = self.timer / animation_rate; + + const BOSS: &Tag = TAG_MAP.get("Boss"); + + let sprite = BOSS.get_animation_sprite(frame as usize); + let sprite = object_controller.get_sprite(sprite).unwrap(); + + self.entity.sprite.set_sprite(sprite); self.entity.update_position_without_collision(); instruction @@ -1753,7 +1843,7 @@ impl<'a> Boss<'a> { self.entity .commit_with_size(offset + shake, (32, 32).into()); } - fn explode(&self, enemies: &mut Arena>, object_controller: &'a ObjectControl) { + fn explode(&self, enemies: &mut Arena>, object_controller: &'a ObjectController) { for _ in 0..(6 - self.health) { let x_offset: Number = Number::from_raw(get_random()).rem_euclid(2.into()) - 1; let y_offset: Number = Number::from_raw(get_random()).rem_euclid(2.into()) - 1; @@ -1825,7 +1915,7 @@ impl<'a> Game<'a> { fn advance_frame( &mut self, - object_controller: &'a ObjectControl, + object_controller: &'a ObjectController, sfx: &mut sfx::Sfx, ) -> GameStatus { let mut state = GameStatus::Continue; @@ -1921,7 +2011,7 @@ impl<'a> Game<'a> { self.input.update(); if let UpdateInstruction::CreateParticle(data, position) = - self.player.update(&self.input, &self.level, sfx) + self.player.update(object_controller, &self.input, &self.level, sfx) { let new_particle = Particle::new(object_controller, data, position); @@ -1935,7 +2025,7 @@ impl<'a> Game<'a> { continue; } - match enemy.update(&self.player, &self.level, sfx) { + match enemy.update(object_controller, &self.player, &self.level, sfx) { UpdateInstruction::Remove => { remove.push(idx); } @@ -1989,7 +2079,7 @@ impl<'a> Game<'a> { let mut remove = Vec::with_capacity(10); for (idx, particle) in self.particles.iter_mut() { - match particle.update(&self.player, &self.level) { + match particle.update(object_controller, &self.player, &self.level) { UpdateInstruction::Remove => remove.push(idx), UpdateInstruction::HealBossAndRemove => { sfx.sunrise(); @@ -2040,7 +2130,7 @@ impl<'a> Game<'a> { } } - fn load_enemies(&mut self, object_controller: &'a ObjectControl) { + fn load_enemies(&mut self, object_controller: &'a ObjectController) { if self.slime_load < self.level.slime_spawns.len() { for (idx, slime_spawn) in self .level @@ -2111,7 +2201,7 @@ impl<'a> Game<'a> { } fn new( - object: &'a ObjectControl, + object: &'a ObjectController, level: Level, background_distributor: &'a mut BackgroundDistributor, start_at_boss: bool, @@ -2146,16 +2236,6 @@ impl<'a> Game<'a> { } fn game_with_level(gba: &mut agb::Gba) { - { - let object = gba.display.object.get(); - object.set_sprite_palettes(&[ - objects::objects.palettes[0].clone(), - objects::boss.palettes[0].clone(), - ]); - object.set_sprite_tilemap(objects::objects.tiles); - object.set_sprite_tilemap_at_idx(8192 - objects::boss.tiles.len(), objects::boss.tiles); - } - let vblank = agb::interrupt::VBlank::get(); vblank.wait_for_vblank(); @@ -2172,8 +2252,7 @@ fn game_with_level(gba: &mut agb::Gba) { let mut background = gba.display.video.tiled0(); background.set_background_palettes(background::background.palettes); background.set_background_tilemap(0, background::background.tiles); - let mut object = gba.display.object.get(); - object.enable(); + let object = gba.display.object.get(); let mut game = Game::new( &object, From 6f064b0d993b7b5352ae797af314f2e5f7d7adc3 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sun, 6 Mar 2022 13:45:16 +0000 Subject: [PATCH 45/51] update lock files --- agb-image-converter/Cargo.lock | 23 ++++++------ agb/Cargo.lock | 69 ++++++---------------------------- 2 files changed, 24 insertions(+), 68 deletions(-) diff --git a/agb-image-converter/Cargo.lock b/agb-image-converter/Cargo.lock index 531ab8ed..aac7a0e5 100644 --- a/agb-image-converter/Cargo.lock +++ b/agb-image-converter/Cargo.lock @@ -88,11 +88,12 @@ dependencies = [ [[package]] name = "deflate" -version = "1.0.0" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f" +checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" dependencies = [ "adler32", + "byteorder", ] [[package]] @@ -109,9 +110,9 @@ dependencies = [ [[package]] name = "image" -version = "0.24.1" +version = "0.23.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db207d030ae38f1eb6f240d5a1c1c88ff422aa005d10f8c6c6fc5e75286ab30e" +checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1" dependencies = [ "bytemuck", "byteorder", @@ -139,11 +140,11 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.5.1" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" dependencies = [ - "adler", + "adler32", ] [[package]] @@ -185,9 +186,9 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" dependencies = [ "autocfg", "num-integer", @@ -205,9 +206,9 @@ dependencies = [ [[package]] name = "png" -version = "0.17.5" +version = "0.16.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba" +checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" dependencies = [ "bitflags", "crc32fast", diff --git a/agb/Cargo.lock b/agb/Cargo.lock index d910024c..3be13c4d 100644 --- a/agb/Cargo.lock +++ b/agb/Cargo.lock @@ -146,11 +146,12 @@ dependencies = [ [[package]] name = "deflate" -version = "1.0.0" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f" +checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" dependencies = [ "adler32", + "byteorder", ] [[package]] @@ -193,9 +194,9 @@ checksum = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549" [[package]] name = "image" -version = "0.24.1" +version = "0.23.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db207d030ae38f1eb6f240d5a1c1c88ff422aa005d10f8c6c6fc5e75286ab30e" +checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1" dependencies = [ "bytemuck", "byteorder", @@ -223,11 +224,11 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.5.1" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" dependencies = [ - "adler", + "adler32", ] [[package]] @@ -290,9 +291,9 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" dependencies = [ "autocfg", "num-integer", @@ -316,9 +317,9 @@ checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" [[package]] name = "png" -version = "0.17.5" +version = "0.16.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba" +checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" dependencies = [ "bitflags", "crc32fast", @@ -326,12 +327,6 @@ dependencies = [ "miniz_oxide 0.3.7", ] -[[package]] -name = "ppv-lite86" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" - [[package]] name = "proc-macro2" version = "1.0.36" @@ -350,46 +345,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", - "rand_hc", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_hc" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" -dependencies = [ - "rand_core", -] - [[package]] name = "rustc-hash" version = "1.1.0" From 8f7e7748e2225987663d5cba6ea98a3820032210 Mon Sep 17 00:00:00 2001 From: Corwin Date: Mon, 7 Mar 2022 20:38:06 +0000 Subject: [PATCH 46/51] Graphics --- agb/src/display/object.rs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index b0ffb3ec..1a74cc5d 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -67,15 +67,32 @@ pub enum Size { #[macro_export] macro_rules! include_aseprite { ($($aseprite_path: expr),*) => {{ - use $crate::display::object::{Size, Sprite, Tag, TagMap}; + use $crate::display::object::{Size, Sprite, Tag, TagMap, Graphics}; use $crate::display::palette16::Palette16; $crate::include_aseprite_inner!($($aseprite_path),*); - (SPRITES, TAGS) + &Graphics::new(SPRITES, TAGS) }}; } +pub struct Graphics { + sprites: &'static [Sprite], + tag_map: &'static TagMap, +} + +impl Graphics { + pub const fn new(sprites: &'static [Sprite], tag_map: &'static TagMap) -> Self { + Self { sprites, tag_map } + } + pub const fn tags(&self) -> &TagMap { + self.tag_map + } + pub const fn sprites(&self) -> &[Sprite] { + self.sprites + } +} + pub struct TagMap { tags: &'static [(&'static str, Tag)], } @@ -170,8 +187,10 @@ impl Tag { } } + #[doc(hidden)] pub const fn new(sprites: &'static [Sprite], from: usize, to: usize, direction: usize) -> Self { assert!(from <= to); + assert!(to < sprites.len()); Self { sprites: &sprites[from] as *const Sprite, len: to - from + 1, From 40c4a26cb13b129cedc71daeed867d8b2a055aa1 Mon Sep 17 00:00:00 2001 From: Corwin Date: Mon, 7 Mar 2022 20:38:13 +0000 Subject: [PATCH 47/51] update example --- agb/examples/sprites.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/agb/examples/sprites.rs b/agb/examples/sprites.rs index 868472cf..7342032b 100644 --- a/agb/examples/sprites.rs +++ b/agb/examples/sprites.rs @@ -3,15 +3,15 @@ extern crate alloc; -use agb::display::object::{ObjectController, Sprite, TagMap}; +use agb::display::object::{Graphics, ObjectController, Sprite, TagMap}; use alloc::vec::Vec; -const SPRITE_TAGS: (&[Sprite], &TagMap) = agb::include_aseprite!( +const GRAPHICS: &Graphics = agb::include_aseprite!( "../examples/the-purple-night/gfx/objects.aseprite", "../examples/the-purple-night/gfx/boss.aseprite" ); -const SPRITES: &[Sprite] = SPRITE_TAGS.0; -const TAG_MAP: &TagMap = SPRITE_TAGS.1; +const SPRITES: &[Sprite] = GRAPHICS.sprites(); +const TAG_MAP: &TagMap = GRAPHICS.tags(); fn all_sprites(gfx: &ObjectController) { let mut input = agb::input::ButtonController::new(); From 8c2931aca53ba95372679f522361518ee9d46428 Mon Sep 17 00:00:00 2001 From: Corwin Date: Mon, 7 Mar 2022 20:40:53 +0000 Subject: [PATCH 48/51] remove unused import --- agb/src/display/object.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 1a74cc5d..ad55fa29 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -2,7 +2,6 @@ use alloc::vec::Vec; use core::alloc::Layout; use core::cell::RefCell; use core::hash::BuildHasherDefault; -use core::ops::Deref; use core::ptr::NonNull; use core::slice; use modular_bitfield::prelude::{B10, B2, B3, B4, B5, B8, B9}; From 586f35265b265c347738b86fbb2063d014980138 Mon Sep 17 00:00:00 2001 From: Corwin Date: Mon, 7 Mar 2022 20:41:04 +0000 Subject: [PATCH 49/51] update example projects --- examples/the-hat-chooses-the-wizard/src/main.rs | 6 +++--- examples/the-purple-night/src/main.rs | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/the-hat-chooses-the-wizard/src/main.rs b/examples/the-hat-chooses-the-wizard/src/main.rs index ef347aea..211b8efa 100644 --- a/examples/the-hat-chooses-the-wizard/src/main.rs +++ b/examples/the-hat-chooses-the-wizard/src/main.rs @@ -85,15 +85,15 @@ agb::include_gfx!("gfx/tile_sheet.toml"); use agb::{ display::{ background::BackgroundRegular, - object::{Object, ObjectController, Sprite, Tag, TagMap}, + object::{Graphics, Object, ObjectController, Sprite, Tag, TagMap}, Priority, HEIGHT, WIDTH, }, fixnum::{FixedNum, Vector2D}, input::{self, Button, ButtonController}, }; -const SPRITE_TAGS: (&[Sprite], &TagMap) = agb::include_aseprite!("gfx/sprites.aseprite"); -const TAG_MAP: &TagMap = SPRITE_TAGS.1; +const GRAPHICS: &Graphics = agb::include_aseprite!("gfx/sprites.aseprite"); +const TAG_MAP: &TagMap = GRAPHICS.tags(); const WALKING: &Tag = TAG_MAP.get("Walking"); const JUMPING: &Tag = TAG_MAP.get("Jumping"); diff --git a/examples/the-purple-night/src/main.rs b/examples/the-purple-night/src/main.rs index 3178947e..3db69056 100644 --- a/examples/the-purple-night/src/main.rs +++ b/examples/the-purple-night/src/main.rs @@ -15,7 +15,7 @@ use rng::get_random; use agb::{ display::{ background::{BackgroundDistributor, BackgroundRegular}, - object::{Object, ObjectController, Sprite, Tag, TagMap}, + object::{Graphics, Object, ObjectController, Sprite, Tag, TagMap}, Priority, HEIGHT, WIDTH, }, fixnum::{FixedNum, Rect, Vector2D}, @@ -23,9 +23,8 @@ use agb::{ }; use generational_arena::Arena; -const SPRITE_TAGS: (&[Sprite], &TagMap) = - agb::include_aseprite!("gfx/objects.aseprite", "gfx/boss.aseprite"); -const TAG_MAP: &TagMap = SPRITE_TAGS.1; +const GRAPHICS: &Graphics = agb::include_aseprite!("gfx/objects.aseprite", "gfx/boss.aseprite"); +const TAG_MAP: &TagMap = GRAPHICS.tags(); const LONGSWORD_IDLE: &Tag = TAG_MAP.get("Idle - longsword"); const LONGSWORD_WALK: &Tag = TAG_MAP.get("Walk - longsword"); @@ -2011,7 +2010,8 @@ impl<'a> Game<'a> { self.input.update(); if let UpdateInstruction::CreateParticle(data, position) = - self.player.update(object_controller, &self.input, &self.level, sfx) + self.player + .update(object_controller, &self.input, &self.level, sfx) { let new_particle = Particle::new(object_controller, data, position); From eb6b2ac5a8658f479361598e6d1a26f7a1b796d3 Mon Sep 17 00:00:00 2001 From: Corwin Date: Mon, 7 Mar 2022 22:03:49 +0000 Subject: [PATCH 50/51] initialise to correct values --- agb/src/display/object.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index ad55fa29..a2b4c01c 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -368,11 +368,21 @@ impl ObjectController { index: inner.pop()?, free_list: &self.free_objects, }; + + let mut attrs = Attributes::new(); + + attrs.a2.set_tile_index(sprite.sprite_location); + let shape_size = sprite.id.get_sprite().size.shape_size(); + attrs.a2.set_palete_bank(sprite.palette_location as u8); + attrs.a0.set_shape(shape_size.0); + attrs.a1a.set_size(shape_size.1); + attrs.a1s.set_size(shape_size.1); + Some(Object { previous_sprite: sprite.clone(), sprite, loan, - attrs: Attributes::new(), + attrs, }) } From 8d20e02a4a79635ee850e7521d82eadd1757cf6d Mon Sep 17 00:00:00 2001 From: Corwin Date: Mon, 7 Mar 2022 22:04:02 +0000 Subject: [PATCH 51/51] fix pong example to use new system TODO: fix transparency --- book/games/pong/Cargo.lock | 170 +++++++++++++++++++++++++-- book/games/pong/gfx/sprites.aseprite | Bin 1012 -> 1588 bytes book/games/pong/gfx/sprites.png | Bin 438 -> 0 bytes book/games/pong/gfx/sprites.toml | 6 - book/games/pong/src/main.rs | 37 ++---- 5 files changed, 168 insertions(+), 45 deletions(-) delete mode 100644 book/games/pong/gfx/sprites.png delete mode 100644 book/games/pong/gfx/sprites.toml diff --git a/book/games/pong/Cargo.lock b/book/games/pong/Cargo.lock index dc5d523b..6f006849 100644 --- a/book/games/pong/Cargo.lock +++ b/book/games/pong/Cargo.lock @@ -24,6 +24,9 @@ dependencies = [ "agb_sound_converter", "bare-metal", "bitflags", + "hashbrown", + "modular-bitfield", + "rustc-hash", ] [[package]] @@ -37,6 +40,7 @@ dependencies = [ name = "agb_image_converter" version = "0.6.0" dependencies = [ + "asefile", "image", "proc-macro2", "quote", @@ -64,6 +68,31 @@ dependencies = [ "syn", ] +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "asefile" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0d5f7de918fd4cb18249819fc4bd27f6a5dbfbc9dcb271727f27dacf17ce880" +dependencies = [ + "bitflags", + "byteorder", + "flate2", + "image", + "log", + "nohash", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -117,11 +146,44 @@ dependencies = [ [[package]] name = "deflate" -version = "1.0.0" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f" +checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" dependencies = [ "adler32", + "byteorder", +] + +[[package]] +name = "flate2" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" +dependencies = [ + "cfg-if", + "crc32fast", + "libc", + "miniz_oxide 0.4.4", +] + +[[package]] +name = "getrandom" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c21d40587b92fa6a6c6e3c1bdbf87d75511db5672f9c93175574b3a00df1758" +dependencies = [ + "ahash", ] [[package]] @@ -132,9 +194,9 @@ checksum = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549" [[package]] name = "image" -version = "0.24.1" +version = "0.23.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db207d030ae38f1eb6f240d5a1c1c88ff422aa005d10f8c6c6fc5e75286ab30e" +checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1" dependencies = [ "bytemuck", "byteorder", @@ -146,14 +208,66 @@ dependencies = [ ] [[package]] -name = "miniz_oxide" -version = "0.5.1" +name = "libc" +version = "0.2.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" +checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "miniz_oxide" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +dependencies = [ + "adler32", +] + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" dependencies = [ "adler", + "autocfg", ] +[[package]] +name = "modular-bitfield" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74" +dependencies = [ + "modular-bitfield-impl", + "static_assertions", +] + +[[package]] +name = "modular-bitfield-impl" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "nohash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0f889fb66f7acdf83442c35775764b51fed3c606ab9cee51500dbde2cf528ca" + [[package]] name = "num-integer" version = "0.1.44" @@ -177,9 +291,9 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" dependencies = [ "autocfg", "num-integer", @@ -196,15 +310,21 @@ dependencies = [ ] [[package]] -name = "png" -version = "0.17.5" +name = "once_cell" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" + +[[package]] +name = "png" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" dependencies = [ "bitflags", "crc32fast", "deflate", - "miniz_oxide", + "miniz_oxide 0.3.7", ] [[package]] @@ -232,6 +352,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "serde" version = "1.0.136" @@ -252,6 +378,12 @@ dependencies = [ "syn", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "syn" version = "1.0.86" @@ -277,3 +409,15 @@ name = "unicode-xid" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" diff --git a/book/games/pong/gfx/sprites.aseprite b/book/games/pong/gfx/sprites.aseprite index 1c5bb9dc052f49f5e4a0a086f96099deee9a4125..5363cd3d01699fee39f23c72161da71e8d21a4e1 100644 GIT binary patch literal 1588 zcmb`Gdq`7J9LJBkO)<%3R=1$My<|a;>#{6zOcJx@g66B2W{KB?uWzqot^LZd+zTBO9**U zFD6p_XyQf)(cu{$SQ8c<_2Je3H|ugI#Afw!sCwbp@pwFGhop>iaz&^agI9$Ur{K71p!X`YzAl$(ke8ChP!H(dC z=ivetpbv2OuiTDTg(;0Yu zu0uqYYBMrCh^q|hURchiBXs&?5z_uNyd9;LsXARo@?2EJw6o0rI9X`T9km~BhJ1a# zpe)iVo3f80Z7s5ZTE}i@tNgmVQmRR=Al+0%J|j89ziL%!G(K!?r?kT1~)W_Ec^p=9c#MipEE}yZHQGGuP(n$8MAr{k9b|%__lqWa%y4w3gz6btSR1> zWiHvFxuz;G;n^4YOlI)>9g$JnJxg=Y zhgLV`Qic1fgQ(*t`7fbkjCY@@8gmmq9*eaLcd)E0a#P>1%}_4GuQm!K8_J38N$PvgHnyX+!*up zEEPtzNw{#o`9!yerggAR}dCw}Q-lpxPju>a(aODQ8ZYmIqV}$4! bU+X>9?i0n z5YRc823N(DfTVl?^d2*T42qC;^&oh$q5+H-7lVOeLeRsDUEgWVCXgZQbL!4?uu;H{ znL3>Vjy08n&x*v%2`p{Po;!toU=!E@^x+P1SVI}UkcBBU;Rr$4K@DDzVvQIEE)anQ z6p(KT4?`?65r{NIxv@zUj{9#xf!LE{NypPfnUd2P$^RN5lw_tXjNoGihgw*Gv0k`C z9skamYlcoMJNzf@ew`oNUU$2utAtUpe@bQ|ic5+pIrho1d|Yd?INgE7u+tS!BW?Dq zIj?i|cC6c6;(w_*)!zMZ(XtEo6K%%}sMNG1-(sn4P{+F$T$OpPH^-%;XQNSV_mC?R zJrspI2#4Hr12l0$9uu?et>AgFqH&AU`jr>_^qCQ|=Bk9j{Q1tP70nrw6C>ZhRTb=? zi@#UB{UBp9$gYdCd-Bz_f%?^RWBd3sub3*@5Gv=@wKtxtOfI)BYuc^Pu3cyKiTR+< zQ+|p2G1qdrP~VePdL>-{W?DNKsG6zj;;ZNzviHFu(~sz|&6;;|ty=d*ZXOn_zn6XI zz_i$NdAq_zD-F0ilQX6%{2VxB8t9f*5BKuqef}DkM5VSxSP`im&@mN0O+Ry;`^1h-Q$Vf2)p;II>GsR+DGuO+{0tfU_G?j9=Bt stGbu1UcIrhc>a#I^vclL`rz4#gxM!Py|Hte2j0((x91&3D+LX|0JVH@d;kCd diff --git a/book/games/pong/gfx/sprites.png b/book/games/pong/gfx/sprites.png deleted file mode 100644 index 9964bf0b6400be2a60e1778c6ee65dbb0f91467f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 438 zcmV;n0ZIOeP)Px$a7jc#R9J=Wm`_T>Kop0++Cq1NRTo(a*|@Qwt`yx^yn+`{=n(|3&=ZK@3A}?M zC4%!x&uJ)c;SYiN>C-J z5>yGQ1pg^PO`Z?Re>>kT#}($co&jiXw6uVr+t*0!2~_1?Yj(2dW*dOPNu-guKO;%| zx)}n{B+9(+kKp4v1d?tjlEmts2+rR|0CX1TG?KJA>ca0Vm_84O!%x8Flc*LdV?bHc zXj)O4dp&sB{{|8rkn``JAl?6w<}ir1W0Lk&CW*f;Ow$2>1o=8pFNi>rB$hoR_5^YY z<(HG3fA<7xk*5dVr!jRtkS5Oy#m$1LKKT2CVX6=QRase{KKLh?rVqvKL7qOiC-|#A g_}c?heXu6@0SQNyXlYPJ`Tzg`07*qoM6N<$g20Wxl>h($ diff --git a/book/games/pong/gfx/sprites.toml b/book/games/pong/gfx/sprites.toml deleted file mode 100644 index 39c68850..00000000 --- a/book/games/pong/gfx/sprites.toml +++ /dev/null @@ -1,6 +0,0 @@ -version = "1.0" - -[image.sprites] -filename = "sprites.png" -tile_size = "16x16" -transparent_colour = "ff0044" \ No newline at end of file diff --git a/book/games/pong/src/main.rs b/book/games/pong/src/main.rs index c5fa5e78..5ccb9dfa 100644 --- a/book/games/pong/src/main.rs +++ b/book/games/pong/src/main.rs @@ -10,42 +10,27 @@ // which won't be a particularly clear error message. #![no_main] -use agb::display::object::Size; +use agb::display::object::{Graphics, Tag}; use agb::Gba; -// Put all the graphics related code in the gfx module -mod gfx { - use agb::display::object::ObjectControl; - - // Import the sprites into this module. This will create a `sprites` module - // and within that will be a constant called `sprites` which houses all the - // palette and tile data. - agb::include_gfx!("gfx/sprites.toml"); - - // Loads the sprites tile data and palette data into VRAM - pub fn load_sprite_data(object: &mut ObjectControl) { - object.set_sprite_palettes(sprites::sprites.palettes); - object.set_sprite_tilemap(sprites::sprites.tiles); - } -} +const GRAPHICS: &Graphics = agb::include_aseprite!("gfx/sprites.aseprite"); // The main function must take 0 arguments and never return. The agb::entry decorator // ensures that everything is in order. `agb` will call this after setting up the stack // and interrupt handlers correctly. #[agb::entry] fn main(mut gba: Gba) -> ! { - let _tiled = gba.display.video.tiled0(); - let mut object = gba.display.object.get(); - gfx::load_sprite_data(&mut object); - object.enable(); + let object = gba.display.object.get(); - let mut ball = object.get_object_standard(); + const BALL: &Tag = GRAPHICS.tags().get("Ball"); + let ball_sprite = object + .get_sprite(BALL.get_sprite(0)) + .expect("We should be able to load a sprite"); + let mut ball = object + .get_object(ball_sprite) + .expect("We should have enoguh space to store an object"); - ball.set_x(50) - .set_y(50) - .set_sprite_size(Size::S16x16) - .set_tile_id(4 * 2) - .show(); + ball.set_x(50).set_y(50).show(); let mut ball_x = 50; let mut ball_y = 50;