diff --git a/examples/the-purple-night/.cargo/config.toml b/examples/the-purple-night/.cargo/config.toml new file mode 100644 index 00000000..76de9890 --- /dev/null +++ b/examples/the-purple-night/.cargo/config.toml @@ -0,0 +1,10 @@ +[unstable] +build-std = ["core", "alloc"] +build-std-features = ["compiler-builtins-mem"] + +[build] +target = "thumbv4t-none-eabi" + +[target.thumbv4t-none-eabi] +rustflags = ["-Clink-arg=-Tgba.ld"] +runner = "mgba-qt" diff --git a/examples/the-purple-night/.gitignore b/examples/the-purple-night/.gitignore new file mode 100644 index 00000000..eb5a316c --- /dev/null +++ b/examples/the-purple-night/.gitignore @@ -0,0 +1 @@ +target diff --git a/examples/the-purple-night/Cargo.lock b/examples/the-purple-night/Cargo.lock new file mode 100644 index 00000000..c8b458ee --- /dev/null +++ b/examples/the-purple-night/Cargo.lock @@ -0,0 +1,475 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + +[[package]] +name = "agb" +version = "0.8.0" +dependencies = [ + "agb_image_converter", + "agb_macros", + "agb_sound_converter", + "bitflags", +] + +[[package]] +name = "agb_image_converter" +version = "0.6.0" +dependencies = [ + "image", + "proc-macro2", + "quote", + "serde", + "syn", + "toml", +] + +[[package]] +name = "agb_macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "rand", + "syn", +] + +[[package]] +name = "agb_sound_converter" +version = "0.1.0" +dependencies = [ + "hound", + "proc-macro2", + "quote", + "siphasher", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "base64" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" +dependencies = [ + "byteorder", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "bytemuck" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9966d2ab714d0f785dbac0a0396251a35280aeb42413281617d0209ab4898435" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cc" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "crc32fast" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "deflate" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" +dependencies = [ + "adler32", + "byteorder", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "generational-arena" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d3b771574f62d0548cee0ad9057857e9fc25d7a3335f140c84f6acd0bf601" +dependencies = [ + "cfg-if 0.1.10", +] + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "hound" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549" + +[[package]] +name = "image" +version = "0.23.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "num-iter", + "num-rational", + "num-traits", + "png", +] + +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + +[[package]] +name = "jobserver" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +dependencies = [ + "libc", +] + +[[package]] +name = "libc" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2f96d100e1cf1929e7719b7edb3b90ab5298072638fccd77be9ce942ecdfce" + +[[package]] +name = "libflate" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9135df43b1f5d0e333385cb6e7897ecd1a43d7d11b91ac003f4d2c2d2401fdd" +dependencies = [ + "adler32", + "crc32fast", + "rle-decode-fast", + "take_mut", +] + +[[package]] +name = "minijam92" +version = "0.1.0" +dependencies = [ + "agb", + "generational-arena", + "quote", + "tiled", +] + +[[package]] +name = "miniz_oxide" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +dependencies = [ + "adler32", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[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", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ca011bd0129ff4ae15cd04c4eef202cadf6c51c21e47aba319b4e0501db741" + +[[package]] +name = "proc-macro2" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +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 = "rle-decode-fast" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac" + +[[package]] +name = "serde" +version = "1.0.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f03b9878abf6d14e6779d3f24f07b2cfa90352cfec4acc5aab8f1ac7f146fae8" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a024926d3432516606328597e0f224a51355a493b49fdd67e9209187cbe55ecc" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "siphasher" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b" + +[[package]] +name = "syn" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "take_mut" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" + +[[package]] +name = "tiled" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9b76189425fed476c9cb0e2a97adb0c6c9d370bf69a57cb951814e77a5fe63" +dependencies = [ + "base64", + "libflate", + "xml-rs", + "zstd", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +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" + +[[package]] +name = "xml-rs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" + +[[package]] +name = "zstd" +version = "0.5.4+zstd.1.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69996ebdb1ba8b1517f61387a883857818a66c8a295f487b1ffd8fd9d2c82910" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "2.0.6+zstd.1.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98aa931fb69ecee256d44589d19754e61851ae4769bf963b385119b1cc37a49e" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "1.4.18+zstd.1.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e6e8778706838f43f771d80d37787cb2fe06dafe89dd3aebaf6721b9eaec81" +dependencies = [ + "cc", + "glob", + "itertools", + "libc", +] diff --git a/examples/the-purple-night/Cargo.toml b/examples/the-purple-night/Cargo.toml new file mode 100644 index 00000000..7af1923a --- /dev/null +++ b/examples/the-purple-night/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "minijam92" +version = "0.1.0" +authors = [""] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +agb = { path = "../../agb", version = "0.8.0", features = ["freq18157"]} +generational-arena = { version = "0.2", default-features = false } + +[build-dependencies] +quote = "1.0.10" +tiled = "0.9.5" + +[profile.dev] +opt-level = 2 + +[profile.release] +panic = "abort" +lto = true \ No newline at end of file diff --git a/examples/the-purple-night/build.rs b/examples/the-purple-night/build.rs new file mode 100644 index 00000000..d76fae49 --- /dev/null +++ b/examples/the-purple-night/build.rs @@ -0,0 +1,107 @@ +use std::collections::HashMap; +use std::env; +use std::fs::File; +use std::io::{BufWriter, Write}; +use std::path::Path; + +use quote::quote; + +fn main() { + let out_dir = env::var("OUT_DIR").expect("OUT_DIR environment variable must be specified"); + + let playground_filename = "map/map.tmx"; + println!("cargo:rerun-if-changed={}", playground_filename); + + let map = tiled::parse_file(Path::new(playground_filename)).unwrap(); + + let width = map.width; + let height = map.height; + + let cloud_layer = &map.layers[0]; + let cloud_tiles = extract_tiles(&cloud_layer.tiles); + + let background_layer = &map.layers[1]; + let background_tiles = extract_tiles(&background_layer.tiles); + + let foreground_layer = &map.layers[2]; + let foreground_tiles = extract_tiles(&foreground_layer.tiles); + + let (slimes_x, slimes_y) = get_spawn_locations(&map.object_groups[0], "Slime Spawn"); + let (bats_x, bats_y) = get_spawn_locations(&map.object_groups[0], "Bat Spawn"); + let (emus_x, emus_y) = get_spawn_locations(&map.object_groups[0], "Emu Spawn"); + + let mut tile_types = HashMap::new(); + + for tile in map.tilesets[0].tiles.iter() { + if let Some("Collision") = tile.tile_type.as_deref() { + tile_types.insert(tile.id, 1u8); + } + } + + let tile_types = + (0..map.tilesets[0].tilecount.unwrap()).map(|id| tile_types.get(&(id + 1)).unwrap_or(&0)); + + let output = quote! { + pub const CLOUD_MAP: &[u16] = &[#(#cloud_tiles),*]; + pub const BACKGROUND_MAP: &[u16] = &[#(#background_tiles),*]; + pub const FOREGROUND_MAP: &[u16] = &[#(#foreground_tiles),*]; + pub const WIDTH: u32 = #width; + pub const HEIGHT: u32 = #height; + + pub const SLIME_SPAWNS_X: &[u16] = &[#(#slimes_x),*]; + pub const SLIME_SPAWNS_Y: &[u16] = &[#(#slimes_y),*]; + + pub const BAT_SPAWNS_X: &[u16] = &[#(#bats_x),*]; + pub const BAT_SPAWNS_Y: &[u16] = &[#(#bats_y),*]; + + pub const EMU_SPAWNS_X: &[u16] = &[#(#emus_x),*]; + pub const EMU_SPAWNS_Y: &[u16] = &[#(#emus_y),*]; + + pub const TILE_TYPES: &[u8] = &[#(#tile_types),*]; + }; + + let output_file = File::create(format!("{}/tilemap.rs", out_dir)) + .expect("failed to open tilemap.rs file for writing"); + let mut writer = BufWriter::new(output_file); + + write!(&mut writer, "{}", output).unwrap(); +} + +fn extract_tiles<'a>(layer: &'a tiled::LayerData) -> impl Iterator + 'a { + match layer { + tiled::LayerData::Finite(tiles) => { + tiles.iter().flat_map(|row| row.iter().map(|tile| tile.gid)) + } + _ => unimplemented!("cannot use infinite layer"), + } + .map(|tileid| get_map_id(tileid)) +} + +fn get_map_id(tileid: u32) -> u16 { + match tileid { + 0 => 0, + i => i as u16 - 1, + } +} + +fn get_spawn_locations<'a>( + object_group: &'a tiled::ObjectGroup, + enemy_type: &str, +) -> ( + impl Iterator + 'a, + impl Iterator + 'a, +) { + let mut spawns = object_group + .objects + .iter() + .filter(|object| &object.obj_type == enemy_type) + .map(|object| (object.x as u16, object.y as u16)) + .collect::>(); + + spawns.sort_by(|a, b| a.0.cmp(&b.0)); + + let xs = spawns.iter().map(|pos| pos.0).collect::>(); + let ys = spawns.iter().map(|pos| pos.1).collect::>(); + + return (xs.into_iter(), ys.into_iter()); +} diff --git a/examples/the-purple-night/gba.ld b/examples/the-purple-night/gba.ld new file mode 100644 index 00000000..ba54bc5a --- /dev/null +++ b/examples/the-purple-night/gba.ld @@ -0,0 +1,109 @@ +OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") +OUTPUT_ARCH(arm) + +ENTRY(__start) +EXTERN(__RUST_INTERRUPT_HANDLER) + +MEMORY { + ewram (w!x) : ORIGIN = 0x02000000, LENGTH = 256K + iwram (w!x) : ORIGIN = 0x03000000, LENGTH = 32K + rom (rx) : ORIGIN = 0x08000000, LENGTH = 32M +} + +__text_start = ORIGIN(rom); + +SECTIONS { + . = __text_start; + + .crt0 : { + KEEP (crt0.o(.text)); + . = ALIGN(4); + } > rom + + .text : { + *(.text .text*); + . = ALIGN(4); + } > rom + __text_end = .; + + .rodata : { + *(.rodata .rodata.*); + . = ALIGN(4); + } > rom + + __iwram_rom_start = .; + .iwram : { + __iwram_data_start = ABSOLUTE(.); + + *(.iwram .iwram.*); + . = ALIGN(4); + + *(.text_iwram .text_iwram.*); + . = ALIGN(4); + + __iwram_data_end = ABSOLUTE(.); + } > iwram AT>rom + + . = __iwram_rom_start + (__iwram_data_end - __iwram_data_start); + + __ewram_rom_start = .; + .ewram : { + __ewram_data_start = ABSOLUTE(.); + + *(.ewram .ewram.*); + . = ALIGN(4); + + *(.data .data.*); + . = ALIGN(4); + + __ewram_data_end = ABSOLUTE(.); + } > ewram AT>rom + + .bss : { + *(.bss .bss.*); + . = ALIGN(4); + } > iwram + + __iwram_rom_length_bytes = __iwram_data_end - __iwram_data_start; + __iwram_rom_length_halfwords = (__iwram_rom_length_bytes + 1) / 2; + + __ewram_rom_length_bytes = __ewram_data_end - __ewram_data_start; + __ewram_rom_length_halfwords = (__ewram_rom_length_bytes + 1) / 2; + + /* debugging sections */ + /* Stabs */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + + .debug_ranges 0 : { *(.debug_ranges) } + + /* discard anything not already mentioned */ + /DISCARD/ : { *(*) } +} \ No newline at end of file diff --git a/examples/the-purple-night/gba_mb.ld b/examples/the-purple-night/gba_mb.ld new file mode 100644 index 00000000..9fdd23c1 --- /dev/null +++ b/examples/the-purple-night/gba_mb.ld @@ -0,0 +1,108 @@ +OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") +OUTPUT_ARCH(arm) + +ENTRY(__start) +EXTERN(__RUST_INTERRUPT_HANDLER) + +MEMORY { + ewram (w!x) : ORIGIN = 0x02000000, LENGTH = 256K + iwram (w!x) : ORIGIN = 0x03000000, LENGTH = 32K +} + +__text_start = ORIGIN(ewram); + +SECTIONS { + . = __text_start; + + .crt0 : { + KEEP (crt0.o(.text)); + . = ALIGN(4); + } > ewram + + .text : { + *(.text .text*); + . = ALIGN(4); + } > ewram + __text_end = .; + + .rodata : { + *(.rodata .rodata.*); + . = ALIGN(4); + } > ewram + + __iwram_rom_start = .; + .iwram : { + __iwram_data_start = ABSOLUTE(.); + + *(.iwram .iwram.*); + . = ALIGN(4); + + *(.text_iwram .text_iwram.*); + . = ALIGN(4); + + __iwram_data_end = ABSOLUTE(.); + } > iwram AT>ewram + + . = __iwram_rom_start + (__iwram_data_end - __iwram_data_start); + + __ewram_rom_start = .; + .ewram : { + __ewram_data_start = ABSOLUTE(.); + + *(.ewram .ewram.*); + . = ALIGN(4); + + *(.data .data.*); + . = ALIGN(4); + + __ewram_data_end = ABSOLUTE(.); + } > ewram AT>ewram + + .bss : { + *(.bss .bss.*); + . = ALIGN(4); + } > iwram + + __iwram_rom_length_bytes = __iwram_data_end - __iwram_data_start; + __iwram_rom_length_halfwords = (__iwram_rom_length_bytes + 1) / 2; + + __ewram_rom_length_bytes = __ewram_data_end - __ewram_data_start; + __ewram_rom_length_halfwords = (__ewram_rom_length_bytes + 1) / 2; + + /* debugging sections */ + /* Stabs */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + + .debug_ranges 0 : { *(.debug_ranges) } + + /* discard anything not already mentioned */ + /DISCARD/ : { *(*) } +} \ No newline at end of file diff --git a/examples/the-purple-night/gfx/background.aseprite b/examples/the-purple-night/gfx/background.aseprite new file mode 100644 index 00000000..7fa5ecc1 Binary files /dev/null and b/examples/the-purple-night/gfx/background.aseprite differ diff --git a/examples/the-purple-night/gfx/background.png b/examples/the-purple-night/gfx/background.png new file mode 100644 index 00000000..32786957 Binary files /dev/null and b/examples/the-purple-night/gfx/background.png differ diff --git a/examples/the-purple-night/gfx/background.toml b/examples/the-purple-night/gfx/background.toml new file mode 100644 index 00000000..0cb20336 --- /dev/null +++ b/examples/the-purple-night/gfx/background.toml @@ -0,0 +1,6 @@ +version = "1.0" + +[image.background] +filename = "background.png" +tile_size = "8x8" +transparent_colour = "53269a" diff --git a/examples/the-purple-night/gfx/boss.aseprite b/examples/the-purple-night/gfx/boss.aseprite new file mode 100644 index 00000000..57487ec7 Binary files /dev/null and b/examples/the-purple-night/gfx/boss.aseprite differ diff --git a/examples/the-purple-night/gfx/boss.png b/examples/the-purple-night/gfx/boss.png new file mode 100644 index 00000000..b17c3a39 Binary files /dev/null and b/examples/the-purple-night/gfx/boss.png differ diff --git a/examples/the-purple-night/gfx/objects.aseprite b/examples/the-purple-night/gfx/objects.aseprite new file mode 100644 index 00000000..c961c131 Binary files /dev/null and b/examples/the-purple-night/gfx/objects.aseprite differ diff --git a/examples/the-purple-night/gfx/objects.png b/examples/the-purple-night/gfx/objects.png new file mode 100644 index 00000000..167e3a2f Binary files /dev/null and b/examples/the-purple-night/gfx/objects.png differ diff --git a/examples/the-purple-night/gfx/objects.toml b/examples/the-purple-night/gfx/objects.toml new file mode 100644 index 00000000..4f2626b5 --- /dev/null +++ b/examples/the-purple-night/gfx/objects.toml @@ -0,0 +1,11 @@ +version = "1.0" + +[image.objects] +filename = "objects.png" +tile_size = "16x16" +transparent_colour = "53269a" + +[image.boss] +filename = "boss.png" +tile_size = "32x32" +transparent_colour = "53269a" diff --git a/examples/the-purple-night/html/LICENSE b/examples/the-purple-night/html/LICENSE new file mode 100644 index 00000000..d159169d --- /dev/null +++ b/examples/the-purple-night/html/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/examples/the-purple-night/html/app.js b/examples/the-purple-night/html/app.js new file mode 100644 index 00000000..afa73517 --- /dev/null +++ b/examples/the-purple-night/html/app.js @@ -0,0 +1,2 @@ +!function(e){var o={};function i(a){if(o[a])return o[a].exports;var r=o[a]={i:a,l:!1,exports:{}};return e[a].call(r.exports,r,r.exports,i),r.l=!0,r.exports}i.m=e,i.c=o,i.d=function(e,o,a){i.o(e,o)||Object.defineProperty(e,o,{configurable:!1,enumerable:!0,get:a})},i.r=function(e){Object.defineProperty(e,"__esModule",{value:!0})},i.n=function(e){var o=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(o,"a",o),o},i.o=function(e,o){return Object.prototype.hasOwnProperty.call(e,o)},i.p="",i(i.s=15)}([function(e,o,i){!function(){"use strict";var o=i(8);e.exports=function(e){return o[e]?escapeHtml(o[e][0]):"Unknown Game"}}()},,,,,,function(e,o,i){!function(){"use strict";function o(e){this.el=e,this.currentlyBinding=!1,this.initialHTML=e.innerHTML,this.el.addEventListener("keydown",this.onKeyDown.bind(this)),this.paused=!1}o.prototype=Object.create(Object.prototype),o.prototype.constructor=o,o.prototype.setPausedState=function(e){this.paused=e,this.el.querySelector(".load-rom-section").style.display=e?"none":"",this.el.querySelector(".paused-section").style.display=e?"":"none"},o.prototype.reset=function(){var e;this.el.innerHTML=this.initialHTML,this.currentlyBinding=!1;var o=this.el.querySelector(".saves-list"),a="",r=window.vbaSaves.listSaves();for(e=0;e";r.length||(a+=""),window.isShittyLocalstorage&&(a+=""),a+="
["+r[e].romCode+"] "+i(0)(r[e].romCode)+"ExportDelete
None
Saving will not be possible because the 'LocalStorage'
feature of your browser is disabled.
",o.innerHTML=a;var s=this.el.querySelector(".keyboard-bindings"),n="",t=window.vbaInput.listBindings();function m(e){return e.join(", ").replace(/Key/im,"Key ").replace(/Arrow/im,"Arrow ").replace(/Digit/im,"Digit ").replace(/Numpad/im,"Numpad ").replace(/Left/im," Left").replace(/Right/im," Right")}for(e=0;e","PAUSE"===t[e].name&&(this.el.querySelector(".unpause-key-prompt").innerText=m(t[e].codes));n+="
"+t[e].friendlyName+""+m(t[e].codes)+"Rebind
",s.innerHTML=n,this.setPausedState(this.paused)},o.prototype.export=function(){vbaSaves.exportSave()},o.prototype.onKeyDown=function(e){if(this.currentlyBinding){var o=vbaInput.bindings[this.currentlyBinding].codes.join();vbaInput.setBinding(this.currentlyBinding,e.code,e.keyCode);var i=vbaInput.bindings[this.currentlyBinding].codes.join();gtag("event","rebind_key_1",{event_label:"Change "+this.currentlyBinding+" from "+o+" to "+i}),this.reset()}},o.prototype.startRebinding=function(e,o){this.currentlyBinding=o,this.el.querySelectorAll(".rebind-key-button").forEach(function(e){e.innerText="Rebind"}),e.innerText="Rebinding..."},o.prototype.resetBindings=function(){gtag("event","reset_bindings_1",{}),vbaInput.resetBindings(),this.reset()},o.prototype.exportSave=function(e){vbaSaves.exportSave(e),this.reset(),gtag("event","export_save_1",{event_label:e+" "+i(0)(e)})},o.prototype.deleteSave=function(e){var o=modal("Are you sure you want to delete your save for ["+e+"] "+i(0)(e)+"?",{title:"Confirm Deletion",leftButtonText:"Delete",leftButtonFn:function(){vbaSaves.deleteSave(e),this.reset(),gtag("event","delete_save_1",{event_label:e+" "+i(0)(e)})}.bind(this),rightButtonText:"Cancel",rightButtonFn:function(){o.hideModal()}})},e.exports=o}()},function(e,o){!function(){"use strict";var o={};function i(){this.downCodes={},this.downKeyCodes={},window.addEventListener("keydown",function(e){var o=this.isKeyDown(this.bindings.PERF_STATS),i=this.isKeyDown(this.bindings.PAUSE);this.downCodes[e.code]=1,this.downKeyCodes[e.keyCode]=1;var a=this.isKeyDown(this.bindings.PERF_STATS);!o&&a&&window.doPerfCalc();var r=this.isKeyDown(this.bindings.PAUSE);return!i&&r&&window.togglePause(),!1}.bind(this)),window.addEventListener("keyup",function(e){var o=this.isKeyDown(this.bindings.PERF_STATS);this.downCodes[e.code]=0,this.downKeyCodes[e.keyCode]=0;var i=this.isKeyDown(this.bindings.PERF_STATS);return o&&!i&&window.doPerfCalc(),!1}.bind(this)),this.bindings=null,this.loadBindings(),null===this.bindings&&this.resetBindings()}o.KEY_BUTTON_A={friendlyName:"A",codes:["KeyZ"],keyCodes:[90]},o.KEY_BUTTON_B={friendlyName:"B",codes:["KeyX"],keyCodes:[88]},o.KEY_BUTTON_SELECT={friendlyName:"Select",codes:["Backspace"],keyCodes:[8]},o.KEY_BUTTON_START={friendlyName:"Start",codes:["Enter"],keyCodes:[13]},o.KEY_RIGHT={friendlyName:"Right",codes:["ArrowRight"],keyCodes:[39]},o.KEY_LEFT={friendlyName:"Left",codes:["ArrowLeft"],keyCodes:[37]},o.KEY_UP={friendlyName:"Up",codes:["ArrowUp"],keyCodes:[38]},o.KEY_DOWN={friendlyName:"Down",codes:["ArrowDown"],keyCodes:[40]},o.KEY_BUTTON_R={friendlyName:"R",codes:["Control"],keyCodes:[17]},o.KEY_BUTTON_L={friendlyName:"L",codes:["Shift"],keyCodes:[16]},o.PERF_STATS={friendlyName:"Performance Stats",codes:["Backquote"],keyCodes:[192]},o.PAUSE={friendlyName:"Pause",codes:["Escape"],keyCodes:[27]},i.prototype=Object.create(Object.prototype),i.prototype.constructor=i,i.prototype.listBindings=function(){return Object.keys(this.bindings).map(function(e){return{name:e,friendlyName:this.bindings[e].friendlyName,codes:this.bindings[e].codes}}.bind(this))},i.prototype.setBinding=function(e,o,i){this.bindings[e].codes=[o],this.bindings[e].keyCodes=[i],this.saveBindings()},i.prototype.loadBindings=function(e){if(this.bindings=JSON.parse(localStorage.VBABindings||"null")||o,Object.keys(this.bindings).sort().join()!==Object.keys(o).sort().join()){if(e)return;this.resetBindings(!0)}},i.prototype.saveBindings=function(){localStorage.VBABindings=JSON.stringify(this.bindings)},i.prototype.resetBindings=function(e){this.bindings=o,this.saveBindings(),this.loadBindings(e)},i.prototype.isKeyDown=function(e){var o;for(o=0;o