├── tinlib ├── src │ ├── machine │ │ ├── input.rs │ │ ├── code.rs │ │ ├── memory.rs │ │ ├── ram.rs │ │ ├── vram.rs │ │ ├── mod.rs │ │ └── screen.rs │ ├── common │ │ ├── mod.rs │ │ ├── size.rs │ │ ├── error.rs │ │ └── coord.rs │ ├── graphic │ │ ├── mod.rs │ │ ├── color.rs │ │ ├── palette.rs │ │ ├── font.rs │ │ └── glyph.rs │ ├── lib.rs │ ├── cartridge │ │ ├── error.rs │ │ ├── chunk.rs │ │ └── mod.rs │ └── map │ │ └── mod.rs ├── README.md ├── Cargo.toml └── examples │ └── cartridge.rs ├── devkit ├── src │ └── main.rs ├── README.md └── Cargo.toml ├── player ├── README.md ├── Cargo.toml └── src │ └── main.rs ├── Cargo.toml ├── images └── logo-128x128.png ├── .gitignore ├── CHANGELOG.md ├── .editorconfig ├── Makefile ├── .pre-commit-config.yaml ├── LICENSE ├── cliff.toml ├── .github └── workflows │ └── ci.yml ├── README.md └── Cargo.lock /tinlib/src/machine/input.rs: -------------------------------------------------------------------------------- 1 | #[derive(Default)] 2 | pub struct Input; 3 | -------------------------------------------------------------------------------- /devkit/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /player/README.md: -------------------------------------------------------------------------------- 1 | # SN-50 Player 2 | 3 | The SN-50 Fantasy Computer player. 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["tinlib", "devkit", "player"] 3 | resolver = "2" 4 | -------------------------------------------------------------------------------- /devkit/README.md: -------------------------------------------------------------------------------- 1 | # SN-50 DevKit 2 | 3 | The SN-50 Fantasy Computer with DevKit tools. 4 | -------------------------------------------------------------------------------- /tinlib/README.md: -------------------------------------------------------------------------------- 1 | # tinlib 2 | 3 | Components for SN-50 Fantasy Computer implementations. 4 | -------------------------------------------------------------------------------- /images/logo-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TinTeam/SN-50/HEAD/images/logo-128x128.png -------------------------------------------------------------------------------- /tinlib/src/machine/code.rs: -------------------------------------------------------------------------------- 1 | pub struct Code { 2 | #[allow(dead_code)] 3 | chars: [char; 1], 4 | } 5 | 6 | impl Default for Code { 7 | fn default() -> Self { 8 | Self { chars: [' '; 1] } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # These are backup files generated by rustfmt 7 | **/*.rs.bk 8 | 9 | # MSVC Windows builds of rustc generate these, which store debugging information 10 | *.pdb 11 | -------------------------------------------------------------------------------- /tinlib/src/common/mod.rs: -------------------------------------------------------------------------------- 1 | //! Common utilities. 2 | mod coord; 3 | mod error; 4 | mod size; 5 | 6 | pub use crate::common::coord::{Coord, CoordEnumerate, CoordEnumerateMut, CoordIter}; 7 | pub use crate::common::error::{CommonError, Result}; 8 | pub use crate::common::size::Size; 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | --- 4 | ## [unreleased] 5 | 6 | ### Miscellaneous Chores 7 | 8 | - refactor ci config - ([891c54e](https://github.com/cocogitto/cocogitto/commit/891c54e76239912bd140872e4c3f1df7f169e2e2)) - Luiz F. A. de Prá 9 | 10 | 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | charset = utf-8 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | 9 | [*.rs] 10 | max_line_length = 120 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | indent_style = space 15 | indent_size = 2 16 | 17 | [*.{json,yml,yaml}] 18 | indent_style = space 19 | indent_size = 2 20 | 21 | [Makefile] 22 | indent_style = tab 23 | -------------------------------------------------------------------------------- /tinlib/src/graphic/mod.rs: -------------------------------------------------------------------------------- 1 | //! Graphic utilities. 2 | mod color; 3 | mod font; 4 | mod glyph; 5 | mod palette; 6 | 7 | pub use crate::graphic::color::Color; 8 | pub use crate::graphic::font::{Font, FontGlyphIter, FontGlyphIterMut}; 9 | pub use crate::graphic::glyph::{ 10 | Glyph, GlyphPixel, GlyphPixelEnumerate, GlyphPixelEnumerateMut, GlyphPixelIter, 11 | GlyphPixelIterMut, 12 | }; 13 | pub use crate::graphic::palette::{Palette, PaletteColorIter, PaletteColorIterMut}; 14 | -------------------------------------------------------------------------------- /devkit/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sn-50" 3 | version = "0.1.0" 4 | authors = ["Luiz F. A. de Prá "] 5 | description = "The SN-50 Fantasy Computer with DevKit tools." 6 | homepage = "https://github.com/TinTeam/SN-50/" 7 | repository = "https://github.com/TinTeam/SN-50/" 8 | documentation = "https://docs.rs/sn-50/" 9 | keywords = ["sn-50", "fantasy", "console", "computer"] 10 | categories = ["games", "game-development", "game-engines"] 11 | license = "MIT" 12 | readme = "README.md" 13 | edition = "2024" 14 | rust-version = "1.85.0" 15 | 16 | [dependencies] 17 | tinlib = { version = "0.1.0", path = "../tinlib" } 18 | -------------------------------------------------------------------------------- /tinlib/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::result::Result as StdResult; 2 | 3 | pub mod cartridge; 4 | pub mod common; 5 | pub mod graphic; 6 | pub mod machine; 7 | pub mod map; 8 | 9 | use thiserror::Error; 10 | 11 | use crate::cartridge::CartridgeError; 12 | use crate::common::CommonError; 13 | 14 | /// Internal errors. 15 | #[derive(Error, Debug)] 16 | pub enum Error { 17 | /// Error to wrap internal Cartridge errors. 18 | #[error(transparent)] 19 | Cartridge(#[from] CartridgeError), 20 | /// Error to wrap internal Common errors. 21 | #[error(transparent)] 22 | Common(#[from] CommonError), 23 | } 24 | 25 | /// Internal result. 26 | pub type Result = StdResult; 27 | -------------------------------------------------------------------------------- /tinlib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tinlib" 3 | version = "0.1.0" 4 | authors = ["Luiz F. A. de Prá "] 5 | description = "Components for SN-50 Fantasy Computer implementations." 6 | homepage = "https://github.com/TinTeam/SN-50/" 7 | repository = "https://github.com/TinTeam/SN-50/" 8 | documentation = "https://docs.rs/tinlib/" 9 | keywords = ["sn-50", "fantasy", "console", "computer"] 10 | categories = ["games", "game-development", "game-engines"] 11 | license = "MIT" 12 | readme = "README.md" 13 | edition = "2024" 14 | rust-version = "1.85.0" 15 | 16 | [dependencies] 17 | byteorder = "^1.5" 18 | log = "^0.4" 19 | thiserror = "^2.0" 20 | 21 | [dev-dependencies] 22 | assert_matches = "^1.5" 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all install pre-commit check check-fmt check-clippy fix fix-fmt fix-clippy test test-cov build 2 | 3 | all: install fix test build 4 | 5 | install: 6 | cargo install git-cliff --locked 7 | cargo install cargo-llvm-cov --locked 8 | pre-commit install 9 | 10 | pre-commit: 11 | pre-commit run --all --verbose 12 | 13 | check: check-fmt check-clippy 14 | 15 | check-fmt: 16 | cargo fmt --all --check 17 | 18 | check-clippy: 19 | cargo clippy --all-targets --all-features --locked -- -D warnings 20 | 21 | fix: fix-fmt fix-clippy 22 | 23 | fix-fmt: 24 | cargo fmt --all 25 | 26 | fix-clippy: 27 | cargo clippy --all-targets --all-features --fix --locked 28 | 29 | test: 30 | cargo test --all-targets --locked 31 | 32 | test-cov: 33 | cargo llvm-cov --all --locked 34 | 35 | build: 36 | cargo build --all-targets --locked 37 | -------------------------------------------------------------------------------- /player/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sn-50-player" 3 | version = "0.1.0" 4 | authors = ["Luiz F. A. de Prá "] 5 | description = "The SN-50 Fantasy Computer player." 6 | homepage = "https://github.com/TinTeam/SN-50/" 7 | repository = "https://github.com/TinTeam/SN-50/" 8 | documentation = "https://docs.rs/sn-50/" 9 | keywords = ["sn-50", "fantasy", "console", "computer"] 10 | categories = ["games", "game-development", "game-engines"] 11 | license = "MIT" 12 | readme = "README.md" 13 | edition = "2024" 14 | rust-version = "1.85.0" 15 | 16 | [dependencies] 17 | tinlib = { version = "0.1.0", path = "../tinlib" } 18 | winit = { version = "0.30", default-features = false, features = [ 19 | "rwh_06", 20 | "x11", 21 | "wayland", 22 | "wayland-dlopen", 23 | "wayland-csd-adwaita", 24 | ] } 25 | env_logger = "0.11" 26 | error-iter = "0.4" 27 | anyhow = "1.0" 28 | log = "0.4" 29 | pixels = "0.15" 30 | -------------------------------------------------------------------------------- /tinlib/src/machine/memory.rs: -------------------------------------------------------------------------------- 1 | //! Memory implementation and manipulation. 2 | use crate::machine::ram::RAM; 3 | use crate::machine::vram::VRAM; 4 | 5 | /// The machine Memory representation. 6 | pub struct Memory<'ram> { 7 | ram: RAM<'ram>, 8 | vram: VRAM, 9 | } 10 | 11 | impl<'ram> Memory<'ram> { 12 | /// Returns a ram reference. 13 | pub fn ram(&self) -> &RAM { 14 | &self.ram 15 | } 16 | 17 | /// Returns a mutable ram reference. 18 | pub fn ram_mut(&mut self) -> &mut RAM<'ram> { 19 | &mut self.ram 20 | } 21 | 22 | /// Returns a vram reference. 23 | pub fn vram(&self) -> &VRAM { 24 | &self.vram 25 | } 26 | 27 | /// Returns a mutable ram reference. 28 | pub fn vram_mut(&mut self) -> &mut VRAM { 29 | &mut self.vram 30 | } 31 | } 32 | 33 | impl Default for Memory<'_> { 34 | /// Creates a new Memory. 35 | fn default() -> Self { 36 | Self { 37 | ram: RAM::default(), 38 | vram: VRAM::default(), 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v4.5.0 4 | hooks: 5 | - id: check-executables-have-shebangs 6 | - id: check-json 7 | - id: check-shebang-scripts-are-executable 8 | - id: check-merge-conflict 9 | - id: check-symlinks 10 | - id: check-toml 11 | - id: check-yaml 12 | - id: detect-private-key 13 | - id: end-of-file-fixer 14 | - id: forbid-submodules 15 | - id: mixed-line-ending 16 | - id: mixed-line-ending 17 | - id: no-commit-to-branch 18 | - id: trailing-whitespace 19 | 20 | - repo: https://github.com/compilerla/conventional-pre-commit 21 | rev: v3.1.0 22 | hooks: 23 | - id: conventional-pre-commit 24 | stages: [commit-msg] 25 | 26 | - repo: https://github.com/doublify/pre-commit-rust 27 | rev: v1.0 28 | hooks: 29 | - id: fmt 30 | - id: cargo-check 31 | args: ['--all-targets', '--locked'] 32 | - id: clippy 33 | args: ['--all-targets', '--all-features', '--locked', '--', '-D', 'warnings'] 34 | -------------------------------------------------------------------------------- /tinlib/src/machine/ram.rs: -------------------------------------------------------------------------------- 1 | //! RAM implementation and manipulation. 2 | use crate::machine::code::Code; 3 | use crate::machine::input::Input; 4 | use crate::map::Map; 5 | 6 | /// The machine RAM representation. 7 | #[derive(Default)] 8 | pub struct RAM<'map> { 9 | code: Code, 10 | map: Map<'map>, 11 | input: Input, 12 | } 13 | 14 | impl<'map> RAM<'map> { 15 | /// Returns a code reference. 16 | pub fn code(&self) -> &Code { 17 | &self.code 18 | } 19 | 20 | /// Returns a mutable code reference. 21 | pub fn code_mut(&mut self) -> &mut Code { 22 | &mut self.code 23 | } 24 | 25 | /// Returns a map reference. 26 | pub fn map(&self) -> &Map { 27 | &self.map 28 | } 29 | 30 | /// Returns a mutable map reference. 31 | pub fn map_mut(&mut self) -> &mut Map<'map> { 32 | &mut self.map 33 | } 34 | 35 | /// Returns an input reference. 36 | pub fn input(&self) -> &Input { 37 | &self.input 38 | } 39 | 40 | /// Returns a mutable input reference. 41 | pub fn input_mut(&mut self) -> &mut Input { 42 | &mut self.input 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Luiz F. A. de Prá 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tinlib/src/machine/vram.rs: -------------------------------------------------------------------------------- 1 | //! VRAM implementation and manipulation. 2 | use crate::graphic::{Font, Palette}; 3 | use crate::machine::screen::Screen; 4 | 5 | /// The machine VRAM representation. 6 | pub struct VRAM { 7 | screen: Screen, 8 | palette: Palette, 9 | font: Font, 10 | } 11 | 12 | impl VRAM { 13 | /// Returns a screen reference. 14 | pub fn screen(&self) -> &Screen { 15 | &self.screen 16 | } 17 | 18 | /// Returns a mutable screen reference. 19 | pub fn screen_mut(&mut self) -> &mut Screen { 20 | &mut self.screen 21 | } 22 | 23 | /// Returns a palette reference. 24 | pub fn palette(&self) -> &Palette { 25 | &self.palette 26 | } 27 | 28 | /// Returns a mutable palette reference. 29 | pub fn palette_mut(&mut self) -> &mut Palette { 30 | &mut self.palette 31 | } 32 | 33 | /// Returns a font reference. 34 | pub fn font(&self) -> &Font { 35 | &self.font 36 | } 37 | 38 | /// Returns a mutable font reference. 39 | pub fn font_mut(&mut self) -> &mut Font { 40 | &mut self.font 41 | } 42 | } 43 | 44 | impl Default for VRAM { 45 | /// Creates a new VRAM. 46 | fn default() -> Self { 47 | Self { 48 | screen: Screen::default(), 49 | palette: Palette::default(), 50 | font: Font::default(), 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tinlib/examples/cartridge.rs: -------------------------------------------------------------------------------- 1 | use std::io::Cursor; 2 | 3 | use tinlib::cartridge::Cartridge; 4 | 5 | fn main() { 6 | let cart = Cartridge { 7 | // An incomplete game cart with empty fonts, map and cover. 8 | version: 17, 9 | name: "Dungeons of the Dungeon".to_string(), 10 | desc: "A cool game about dungeons inside dungeons.".to_string(), 11 | author: "Luiz de Prá".to_string(), 12 | palette: vec![ 13 | 0x2d, 0x1b, 0x000, // dark 14 | 0x1e, 0x60, 0x6e, // dark greenish 15 | 0x5a, 0xb9, 0xa8, // greenish 16 | 0xc4, 0xf0, 0xc2, // light greenish 17 | ], 18 | code: "def main:\n pass".to_string(), 19 | ..Default::default() 20 | }; 21 | 22 | println!("Pre-save Cart: {:?}\n\n", &cart); 23 | 24 | // Saving the cart data into a cursor (file or anything that implements Write). 25 | let mut cursor = Cursor::new(vec![]); 26 | cart.save(&mut cursor).expect("failed to save cart"); 27 | 28 | println!("File data: {:?}\n\n", &cursor); 29 | 30 | // Loading the cart data from a cursor (file, or anything that implements Read). 31 | cursor.set_position(0); 32 | let new_cart = Cartridge::from_reader(&mut cursor).expect("failed to load cart"); 33 | 34 | println!("Post-load Cart: {:?}\n\n", &new_cart); 35 | 36 | println!("They has the same data? {}\n\n", cart == new_cart); 37 | } 38 | -------------------------------------------------------------------------------- /tinlib/src/machine/mod.rs: -------------------------------------------------------------------------------- 1 | //! Machine utilities. 2 | mod code; 3 | mod input; 4 | mod memory; 5 | mod ram; 6 | mod screen; 7 | mod vram; 8 | 9 | pub use crate::machine::code::Code; 10 | pub use crate::machine::input::Input; 11 | pub use crate::machine::memory::Memory; 12 | pub use crate::machine::ram::RAM; 13 | pub use crate::machine::screen::{ 14 | Screen, ScreenPixel, ScreenPixelEnumerate, ScreenPixelEnumerateMut, ScreenPixelIter, 15 | ScreenPixelIterMut, 16 | }; 17 | pub use crate::machine::vram::VRAM; 18 | 19 | /// Machine states. 20 | #[derive(Debug, Clone, Copy, PartialEq)] 21 | pub enum MachineState { 22 | /// When the machine was just created and no cart was loaded yet. 23 | Created, 24 | /// When a cart was loaded but the machine is not running it yet. 25 | Loaded, 26 | /// When the machine is running the loaded cart. 27 | Started, 28 | /// When the cart execution is paused. 29 | Paused, 30 | } 31 | 32 | /// The machine representation. 33 | pub struct Machine<'mem> { 34 | state: MachineState, 35 | #[allow(dead_code)] 36 | memory: Memory<'mem>, 37 | } 38 | 39 | impl Machine<'_> { 40 | /// Returns the current state. 41 | pub fn state(&self) -> MachineState { 42 | self.state 43 | } 44 | 45 | pub fn load_cartridge(&mut self) {} 46 | 47 | pub fn start(&mut self) {} 48 | 49 | pub fn pause(&mut self) {} 50 | 51 | pub fn stop(&mut self) {} 52 | } 53 | 54 | impl Default for Machine<'_> { 55 | /// Creates a new Machine in the `Created` state. 56 | fn default() -> Self { 57 | Self { 58 | state: MachineState::Created, 59 | memory: Memory::default(), 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /tinlib/src/common/size.rs: -------------------------------------------------------------------------------- 1 | //! Size implementation and manipulation. 2 | 3 | /// A Size implementation with `usize` dimensions. 4 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Default, PartialOrd, Ord)] 5 | pub struct Size { 6 | width: usize, 7 | height: usize, 8 | } 9 | 10 | impl Size { 11 | /// Creates a new Size. 12 | pub fn new(width: usize, height: usize) -> Self { 13 | Self { width, height } 14 | } 15 | 16 | /// Returns the width. 17 | pub fn width(&self) -> usize { 18 | self.width 19 | } 20 | 21 | /// Returns the height. 22 | pub fn height(&self) -> usize { 23 | self.height 24 | } 25 | } 26 | 27 | impl From<(usize, usize)> for Size { 28 | fn from((x, y): (usize, usize)) -> Self { 29 | Self::new(x, y) 30 | } 31 | } 32 | 33 | impl From<[usize; 2]> for Size { 34 | fn from(array: [usize; 2]) -> Self { 35 | Self::new(array[0], array[1]) 36 | } 37 | } 38 | 39 | #[cfg(test)] 40 | mod test { 41 | use super::*; 42 | 43 | #[test] 44 | fn test_size_new() { 45 | let size = Size::new(80, 48); 46 | 47 | assert_eq!(size.width, 80); 48 | assert_eq!(size.height, 48); 49 | } 50 | 51 | #[test] 52 | fn test_size_width_and_height() { 53 | let size = Size::new(80, 48); 54 | 55 | assert_eq!(size.width(), 80); 56 | assert_eq!(size.height(), 48); 57 | } 58 | 59 | #[test] 60 | fn test_size_from_tuple() { 61 | let tuple = (80usize, 48usize); 62 | let size = Size::from(tuple); 63 | 64 | assert_eq!(size, Size::new(80, 48)); 65 | } 66 | 67 | #[test] 68 | fn test_size_from_array() { 69 | let array = [80usize, 48usize]; 70 | let size = Size::from(array); 71 | 72 | assert_eq!(size, Size::new(80, 48)); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /tinlib/src/common/error.rs: -------------------------------------------------------------------------------- 1 | //! CommonError implementation and manipulation. 2 | use std::result::Result as StdResult; 3 | 4 | use thiserror::Error; 5 | 6 | use crate::common::coord::Coord; 7 | use crate::common::size::Size; 8 | 9 | /// Common errors. 10 | #[derive(Error, Debug)] 11 | pub enum CommonError { 12 | /// Error to represent invalid coords. 13 | #[error("invalid coord ({coord:?}) for size ({size:?})")] 14 | InvalidCoord { coord: Coord, size: Size }, 15 | /// Error to reprense invalid indexes. 16 | #[error("invalid index {index} for lenght {lenght}")] 17 | InvalidIndex { index: usize, lenght: usize }, 18 | } 19 | 20 | impl CommonError { 21 | /// Creates a `InvalidCoord` error. 22 | pub fn new_invalid_coord(coord: Coord, size: Size) -> Self { 23 | Self::InvalidCoord { coord, size } 24 | } 25 | 26 | /// Creates a `InvalidIndex` error. 27 | pub fn new_invalid_index(index: usize, lenght: usize) -> Self { 28 | Self::InvalidIndex { index, lenght } 29 | } 30 | } 31 | 32 | pub type Result = StdResult; 33 | 34 | #[cfg(test)] 35 | mod test_super { 36 | use assert_matches::assert_matches; 37 | 38 | use super::*; 39 | 40 | #[test] 41 | fn test_commonerror_new_invalid_index() { 42 | let index = 2usize; 43 | let lenght = 1usize; 44 | 45 | let error = CommonError::new_invalid_index(index, lenght); 46 | 47 | assert_matches!( 48 | error, 49 | CommonError::InvalidIndex { index: i, lenght: l } if i == index && l == lenght 50 | ); 51 | } 52 | 53 | #[test] 54 | fn test_commonerror_new_invalid_coord() { 55 | let coord = Coord::new(2, 2); 56 | let size: Size = Size::new(1, 1); 57 | 58 | let error = CommonError::new_invalid_coord(coord, size); 59 | 60 | assert_matches!( 61 | error, 62 | CommonError::InvalidCoord { coord: c, size: s } if c == coord && s == size 63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tinlib/src/graphic/color.rs: -------------------------------------------------------------------------------- 1 | //! Color implementation and manipulation. 2 | 3 | /// A color representation with red, green and blue values. 4 | #[derive(Debug, Default, Clone, Copy, PartialEq)] 5 | pub struct Color { 6 | pub red: u8, 7 | pub green: u8, 8 | pub blue: u8, 9 | } 10 | 11 | impl Color { 12 | /// Creates a Color with red, green and blue values. 13 | pub fn new(red: u8, green: u8, blue: u8) -> Self { 14 | Self { red, green, blue } 15 | } 16 | } 17 | 18 | impl From for Color { 19 | fn from(value: u32) -> Self { 20 | Self { 21 | red: ((value & 0x00ff_0000) >> 16) as u8, 22 | green: ((value & 0x0000_ff00) >> 8) as u8, 23 | blue: (value & 0x0000_00ff) as u8, 24 | } 25 | } 26 | } 27 | 28 | impl From<(u8, u8, u8)> for Color { 29 | fn from(value: (u8, u8, u8)) -> Self { 30 | Self::new(value.0, value.1, value.2) 31 | } 32 | } 33 | 34 | impl From<[u8; 3]> for Color { 35 | fn from(array: [u8; 3]) -> Self { 36 | Self::new(array[0], array[1], array[2]) 37 | } 38 | } 39 | 40 | #[cfg(test)] 41 | mod tests { 42 | use super::*; 43 | 44 | #[test] 45 | fn test_color_new() { 46 | let color = Color::new(1, 2, 3); 47 | 48 | assert_eq!(color.red, 1); 49 | assert_eq!(color.green, 2); 50 | assert_eq!(color.blue, 3); 51 | } 52 | 53 | #[test] 54 | fn test_color_new_from_hex() { 55 | let color = Color::from(0x7bc950); 56 | 57 | assert_eq!(color.red, 123); 58 | assert_eq!(color.green, 201); 59 | assert_eq!(color.blue, 80); 60 | } 61 | 62 | #[test] 63 | fn test_color_red_green_blue() { 64 | let color = Color::new(1, 2, 3); 65 | 66 | assert_eq!(color.red, 1); 67 | assert_eq!(color.green, 2); 68 | assert_eq!(color.blue, 3); 69 | } 70 | 71 | #[test] 72 | fn test_color_from_tuple() { 73 | let tuple = (1u8, 2u8, 3u8); 74 | let color = Color::from(tuple); 75 | 76 | assert_eq!(color.red, tuple.0); 77 | assert_eq!(color.green, tuple.1); 78 | assert_eq!(color.blue, tuple.2); 79 | } 80 | 81 | #[test] 82 | fn test_color_from_array() { 83 | let array = [1u8, 2u8, 3u8]; 84 | let color = Color::from(array); 85 | 86 | assert_eq!(color.red, array[0]); 87 | assert_eq!(color.green, array[1]); 88 | assert_eq!(color.blue, array[2]); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /cliff.toml: -------------------------------------------------------------------------------- 1 | [changelog] 2 | header = """ 3 | # Changelog\n 4 | """ 5 | body = """ 6 | --- 7 | {% if version %}\ 8 | {% if previous.version %}\ 9 | ## [{{ version | trim_start_matches(pat="v") }}]($REPO/compare/{{ previous.version }}..{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }} 10 | {% else %}\ 11 | ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} 12 | {% endif %}\ 13 | {% else %}\ 14 | ## [unreleased] 15 | {% endif %}\ 16 | {% for group, commits in commits | group_by(attribute="group") %} 17 | ### {{ group | striptags | trim | upper_first }} 18 | {% for commit in commits 19 | | filter(attribute="scope") 20 | | sort(attribute="scope") %} 21 | - **({{commit.scope}})**{% if commit.breaking %} [**breaking**]{% endif %} \ 22 | {{ commit.message }} - ([{{ commit.id | truncate(length=7, end="") }}]($REPO/commit/{{ commit.id }})) - {{ commit.author.name }} 23 | {%- endfor -%} 24 | {% raw %}\n{% endraw %}\ 25 | {%- for commit in commits %} 26 | {%- if commit.scope -%} 27 | {% else -%} 28 | - {% if commit.breaking %} [**breaking**]{% endif %}\ 29 | {{ commit.message }} - ([{{ commit.id | truncate(length=7, end="") }}]($REPO/commit/{{ commit.id }})) - {{ commit.author.name }} 30 | {% endif -%} 31 | {% endfor -%} 32 | {% endfor %}\n 33 | """ 34 | footer = """ 35 | 36 | """ 37 | trim = true 38 | postprocessors = [ 39 | { pattern = '\$REPO', replace = "https://github.com/cocogitto/cocogitto" }, # replace repository URL 40 | ] 41 | 42 | [git] 43 | conventional_commits = true 44 | filter_unconventional = false 45 | split_commits = false 46 | commit_preprocessors = [] 47 | commit_parsers = [ 48 | { message = "^feat", group = "Features" }, 49 | { message = "^fix", group = "Bug Fixes" }, 50 | { message = "^doc", group = "Documentation" }, 51 | { message = "^perf", group = "Performance" }, 52 | { message = "^refactor", group = "Refactoring" }, 53 | { message = "^style", group = "Style" }, 54 | { message = "^revert", group = "Revert" }, 55 | { message = "^test", group = "Tests" }, 56 | { message = "^chore\\(version\\):", skip = true }, 57 | { message = "^chore", group = "Miscellaneous Chores" }, 58 | { body = ".*security", group = "Security" }, 59 | ] 60 | protect_breaking_commits = false 61 | filter_commits = false 62 | tag_pattern = "v[0-9].*" 63 | skip_tags = "v0.1.0-beta.1" 64 | ignore_tags = "" 65 | topo_order = false 66 | sort_commits = "oldest" 67 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | fmt: 11 | name: Fmt 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Checkout sources 16 | uses: actions/checkout@v4 17 | 18 | - name: Install toolchain 19 | uses: dtolnay/rust-toolchain@stable 20 | with: 21 | toolchain: stable 22 | components: rustfmt 23 | 24 | - name: Restore cache 25 | uses: Swatinem/rust-cache@v2 26 | 27 | - name: Run fmt 28 | run: cargo fmt --all --check 29 | 30 | clippy: 31 | name: Clippy 32 | runs-on: ubuntu-latest 33 | 34 | steps: 35 | - name: Checkout sources 36 | uses: actions/checkout@v4 37 | 38 | - name: Install toolchain 39 | uses: dtolnay/rust-toolchain@stable 40 | with: 41 | toolchain: stable 42 | components: clippy 43 | 44 | - name: Restore cache 45 | uses: Swatinem/rust-cache@v1 46 | 47 | - name: Run clippy 48 | run: cargo clippy --all-targets --all-features --locked -- -D warnings 49 | 50 | check: 51 | name: Check 52 | 53 | strategy: 54 | fail-fast: false 55 | matrix: 56 | rust: 57 | - stable 58 | - beta 59 | - nightly 60 | os: 61 | - ubuntu-latest 62 | # - macos-latest 63 | # - windows-latest 64 | 65 | runs-on: ${{ matrix.os }} 66 | 67 | steps: 68 | - name: Checkout sources 69 | uses: actions/checkout@v4 70 | 71 | - name: Install toolchain 72 | uses: dtolnay/rust-toolchain@stable 73 | with: 74 | toolchain: ${{ matrix.rust }} 75 | 76 | - name: Run check 77 | run: cargo check --all-targets --locked 78 | 79 | test: 80 | name: Test 81 | 82 | strategy: 83 | fail-fast: false 84 | matrix: 85 | rust: 86 | - stable 87 | - beta 88 | - nightly 89 | os: 90 | - ubuntu-latest 91 | # - macos-latest 92 | # - windows-latest 93 | 94 | runs-on: ${{ matrix.os }} 95 | 96 | steps: 97 | - name: Checkout sources 98 | uses: actions/checkout@v4 99 | 100 | - name: Install toolchain 101 | uses: dtolnay/rust-toolchain@stable 102 | with: 103 | toolchain: ${{ matrix.rust }} 104 | 105 | - name: Run test 106 | run: cargo test --all-targets --locked 107 | 108 | coverage: 109 | name: Coverage 110 | 111 | runs-on: ubuntu-latest 112 | 113 | steps: 114 | - name: Checkout sources 115 | uses: actions/checkout@v4 116 | 117 | - name: Install toolchain 118 | uses: dtolnay/rust-toolchain@nightly 119 | with: 120 | components: llvm-tools 121 | 122 | - name: Install llvm-cov 123 | uses: taiki-e/install-action@v2 124 | with: 125 | tool: cargo-llvm-cov 126 | - run: cargo llvm-cov --all-features --lcov --output-path lcov.info 127 | 128 | - name: Upload coverage to coveralls 129 | uses: coverallsapp/github-action@v2 130 | with: 131 | github-token: ${{ secrets.GITHUB_TOKEN }} 132 | path-to-lcov: lcov.info 133 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |

3 | 4 | GitHub Workflow Status 5 | 6 | 7 | Coveralls Status 8 | 9 | 10 | docs.rs 11 | 12 | 13 | crates.io 14 | 15 | 16 | MIT License 17 | 18 |

19 | 20 |

21 | 22 | Logo 23 | 24 |

25 | 26 |

SN-50

27 | 28 |

29 | A fun Fantasy Computer 30 |

31 |

32 | 33 | ## Table of Contents 34 | 35 | - [About SN-50](#about-sn-50) 36 | - [Tailored Limitations](#tailored-limitations) 37 | - [Features](#features) 38 | - [Getting Started](#getting-started) 39 | - [Releases](#releases) 40 | - [Building](#building) 41 | - [Usage](#usage) 42 | - [Contributing](#contributing) 43 | - [License](#license) 44 | - [Acknowledgements](#acknowledgements) 45 | 46 | ## About SN-50 47 | 48 | SN-50 is a free and open source fantasy computer for building, playing and sharing resources-limited games. The game limitations were inspired in old computers and their text-based games. 49 | 50 | The project is basically a simple console and several tools for building games, as such: code, glyph, sound and music editors. Games are saved, packed and distributed in cartridge files. These files can be executed by the SN-50 or any player that implements the console specifications. 51 | 52 | ### Tailored Limitations 53 | 54 | TBD 55 | 56 | ### Features 57 | 58 | TBD 59 | 60 | ## Getting Started 61 | 62 | To start using SN-50 you can download a released binary or build it yourself. 63 | 64 | ### Releases 65 | 66 | You can download all the compiled version os SN-50 in the [release page][releases]. 67 | 68 | ### Building 69 | 70 | Follow these steps to build the project: 71 | 72 | **Important:** You must have the latest Rust version installed to build this project. 73 | 74 | 1. Clone the repo and move to the project's folder 75 | ```sh 76 | git clone https://github.com/TinTeam/SN-50.git && cd SN-50 77 | ``` 78 | 2. Build the project with `cargo` 79 | ```sh 80 | cargo build --release 81 | ``` 82 | 83 | ## Usage 84 | 85 | TBD 86 | 87 | ## Contributing 88 | 89 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 90 | 91 | 1. Fork the Project 92 | 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) 93 | 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) 94 | 4. Push to the Branch (`git push origin feature/AmazingFeature`) 95 | 5. Open a Pull Request 96 | 97 | ## License 98 | 99 | Distributed under the MIT License. See `LICENSE` for more information. 100 | 101 | ## Acknowledgements 102 | 103 | * [Elias "dlight/amiguxo"](https://github.com/dlight/), thank you for helping me solve tricky problems with lifetimes and giving advices. 104 | 105 | [releases]: https://github.com/TinTeam/SN-50/releases 106 | -------------------------------------------------------------------------------- /tinlib/src/cartridge/error.rs: -------------------------------------------------------------------------------- 1 | //! CartridgeError implementation and manipulation. 2 | use std::io; 3 | use std::result::Result as StdResult; 4 | use std::string::FromUtf8Error; 5 | 6 | use thiserror::Error; 7 | 8 | use crate::cartridge::chunk::ChunkType; 9 | 10 | /// Cartridge errors. 11 | #[derive(Error, Debug)] 12 | pub enum CartridgeError { 13 | /// Error to represent invalid chunk types. 14 | #[error("invalid chunk type {0}")] 15 | InvalidChunkType(u8), 16 | /// Error to represent invalid chunk sizes. 17 | #[error("invalid chunk size {1} for type {0:?}, expected: {2:?}")] 18 | InvalidChunkSize(ChunkType, usize, Vec), 19 | /// Error to represent invalid chunk max sizes. 20 | #[error("invalid chunk size {1} for type {0:?}, max expected: {2}")] 21 | InvalidChunkMaxSize(ChunkType, usize, usize), 22 | /// Error to represent mismatched chunk sizes. 23 | #[error("mismatched chunk header size {1} and data sizes {2} for type {0:?}")] 24 | MismatchedChunkSizes(ChunkType, usize, usize), 25 | /// Error to wrap an invalid conversion to UTF8. 26 | #[error("UFT8 conversion error")] 27 | FromUtf8(#[from] FromUtf8Error), 28 | /// Error to wrap `io::Error`s from loading process. 29 | #[error("IO operation error")] 30 | Io(#[from] io::Error), 31 | } 32 | 33 | impl CartridgeError { 34 | /// Creates a `InvalidChunkType` error. 35 | pub fn new_invalid_chunk_type(chunk_type: u8) -> Self { 36 | Self::InvalidChunkType(chunk_type) 37 | } 38 | 39 | /// Creates a `InvalidChunkSize` error. 40 | pub fn new_invalid_chunk_size( 41 | chunk_type: ChunkType, 42 | value: usize, 43 | expected: Vec, 44 | ) -> Self { 45 | Self::InvalidChunkSize(chunk_type, value, expected) 46 | } 47 | 48 | /// Creates a `InvalidChunkMaxSize` error. 49 | pub fn new_invalid_chunk_max_size( 50 | chunk_type: ChunkType, 51 | value: usize, 52 | expected: usize, 53 | ) -> Self { 54 | Self::InvalidChunkMaxSize(chunk_type, value, expected) 55 | } 56 | 57 | /// Creates a `MismatchedChunkSizes` error. 58 | pub fn new_mismatched_chunk_sizes( 59 | chunk_type: ChunkType, 60 | header_size: usize, 61 | data_size: usize, 62 | ) -> Self { 63 | Self::MismatchedChunkSizes(chunk_type, header_size, data_size) 64 | } 65 | } 66 | 67 | pub type Result = StdResult; 68 | 69 | #[cfg(test)] 70 | mod test_super { 71 | use assert_matches::assert_matches; 72 | 73 | use super::*; 74 | 75 | #[test] 76 | fn test_cartridgeerror_new_invalid_chunk_type() { 77 | let chunk_type = 99u8; 78 | 79 | let error = CartridgeError::new_invalid_chunk_type(chunk_type); 80 | 81 | assert_matches!( 82 | error, 83 | CartridgeError::InvalidChunkType(ct) if ct == chunk_type 84 | ); 85 | } 86 | 87 | #[test] 88 | fn test_cartridgeerror_new_invalid_chunk_size() { 89 | let chunk_type = ChunkType::End; 90 | let value = 1usize; 91 | let expected = vec![0usize]; 92 | 93 | let error = CartridgeError::new_invalid_chunk_size(chunk_type, value, expected.clone()); 94 | 95 | assert_matches!( 96 | error, 97 | CartridgeError::InvalidChunkSize(ct, v, e) if ct == chunk_type && v == value && e == expected 98 | ); 99 | } 100 | 101 | #[test] 102 | fn test_cartridgeerror_new_invalid_chunk_max_size() { 103 | let chunk_type = ChunkType::Code; 104 | let value = 140000usize; 105 | let expected = 131072usize; 106 | 107 | let error = CartridgeError::new_invalid_chunk_max_size(chunk_type, value, expected); 108 | 109 | assert_matches!( 110 | error, 111 | CartridgeError::InvalidChunkMaxSize(ct, v, e) if ct == chunk_type && v == value && e == expected 112 | ); 113 | } 114 | 115 | #[test] 116 | fn test_cartridgeerror_new_mismatched_chunk_sizes() { 117 | let chunk_type = ChunkType::Code; 118 | let header_size = 10usize; 119 | let data_size = 15usize; 120 | 121 | let error = CartridgeError::new_mismatched_chunk_sizes(chunk_type, header_size, data_size); 122 | 123 | assert_matches!( 124 | error, 125 | CartridgeError::MismatchedChunkSizes(ct, h, d) if ct == chunk_type && h == header_size && d == data_size 126 | ); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /tinlib/src/graphic/palette.rs: -------------------------------------------------------------------------------- 1 | //! Palette implementation and manipulation. 2 | use std::fmt; 3 | use std::slice; 4 | 5 | use crate::common::{CommonError, Result}; 6 | use crate::graphic::color::Color; 7 | 8 | /// Default number of colors in a Palette. 9 | const NUM_COLORS_IN_PALETTE: usize = 16; 10 | 11 | /// A iterator over all palette colors. 12 | pub type PaletteColorIter<'iter> = slice::Iter<'iter, Color>; 13 | /// A mutable iterator over all palette colors. 14 | pub type PaletteColorIterMut<'iter> = slice::IterMut<'iter, Color>; 15 | 16 | /// A Palette representation with N colors. 17 | #[derive(Clone)] 18 | pub struct Palette { 19 | /// Palette's colors. 20 | colors: Vec, 21 | } 22 | 23 | impl Palette { 24 | /// Creates a new Palette. 25 | pub fn new(num_colors: usize) -> Self { 26 | Self { 27 | colors: vec![Color::default(); num_colors], 28 | } 29 | } 30 | 31 | /// Returns the lenght. 32 | pub fn lenght(&self) -> usize { 33 | self.colors.len() 34 | } 35 | 36 | /// Returns a color. 37 | pub fn get_color(&self, index: usize) -> Result { 38 | if !self.is_index_valid(index) { 39 | return Err(CommonError::new_invalid_index(index, self.lenght())); 40 | } 41 | 42 | Ok(self.colors[index]) 43 | } 44 | 45 | /// Sets a color. 46 | pub fn set_color(&mut self, index: usize, color: Color) -> Result<()> { 47 | if !self.is_index_valid(index) { 48 | return Err(CommonError::new_invalid_index(index, self.lenght())); 49 | } 50 | 51 | self.colors[index] = color; 52 | 53 | Ok(()) 54 | } 55 | 56 | /// Returns an iterator over all palette pixels. 57 | pub fn iter(&self) -> PaletteColorIter { 58 | self.colors.iter() 59 | } 60 | 61 | /// Returns a mutable iterator over all palette pixels. 62 | pub fn iter_mut(&mut self) -> PaletteColorIterMut { 63 | self.colors.iter_mut() 64 | } 65 | 66 | fn is_index_valid(&self, index: usize) -> bool { 67 | index < self.lenght() 68 | } 69 | } 70 | 71 | impl Default for Palette { 72 | /// Creates a Palette with all colors set to black. 73 | fn default() -> Self { 74 | Self { 75 | colors: vec![Color::default(); NUM_COLORS_IN_PALETTE], 76 | } 77 | } 78 | } 79 | 80 | impl fmt::Debug for Palette { 81 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 82 | let data: Vec<&Color> = self.colors.iter().collect(); 83 | 84 | f.debug_struct("Palette").field("colors", &data).finish() 85 | } 86 | } 87 | 88 | impl From<&[Color]> for Palette { 89 | fn from(colors: &[Color]) -> Self { 90 | Self { 91 | colors: colors.to_vec(), 92 | } 93 | } 94 | } 95 | 96 | #[cfg(test)] 97 | mod tests { 98 | use assert_matches::assert_matches; 99 | 100 | use super::*; 101 | 102 | #[test] 103 | fn test_palette_default() { 104 | let palette = Palette::default(); 105 | assert_eq!(palette.colors.len(), NUM_COLORS_IN_PALETTE); 106 | } 107 | 108 | #[test] 109 | fn test_palette_len() { 110 | let palette = Palette::default(); 111 | assert_eq!(palette.lenght(), NUM_COLORS_IN_PALETTE); 112 | } 113 | 114 | #[test] 115 | fn test_palette_get_color() { 116 | let palette = Palette::default(); 117 | let color = Color::default(); 118 | 119 | let result = palette.get_color(0); 120 | assert!(result.is_ok()); 121 | assert_eq!(result.unwrap(), color); 122 | } 123 | 124 | #[test] 125 | fn test_palette_get_color_invalid_index() { 126 | let palette = Palette::default(); 127 | let index = 16usize; 128 | 129 | let result = palette.get_color(index); 130 | assert!(result.is_err()); 131 | assert_matches!( 132 | result.unwrap_err(), 133 | CommonError::InvalidIndex { index: i, lenght: l } if i == index && l == palette.lenght() 134 | ); 135 | } 136 | 137 | #[test] 138 | fn test_palette_set_color() { 139 | let mut palette = Palette::default(); 140 | let color = Color::new(255, 255, 255); 141 | 142 | let result = palette.set_color(0, color); 143 | assert!(result.is_ok()); 144 | 145 | let result = palette.get_color(0); 146 | assert_eq!(result.unwrap(), color); 147 | } 148 | 149 | #[test] 150 | fn test_palette_set_color_invalid_index() { 151 | let mut palette = Palette::default(); 152 | let color = Color::new(255, 255, 255); 153 | let index = 16usize; 154 | 155 | let result = palette.set_color(16, color); 156 | assert!(result.is_err()); 157 | assert_matches!( 158 | result.unwrap_err(), 159 | CommonError::InvalidIndex { index: i, lenght: l } if i == index && l == palette.lenght() 160 | ); 161 | } 162 | 163 | #[test] 164 | fn test_palette_iter() { 165 | let palette = Palette::default(); 166 | let default_color = Color::default(); 167 | 168 | for color in palette.iter() { 169 | assert_eq!(color, &default_color); 170 | } 171 | } 172 | 173 | #[test] 174 | fn test_palette_iter_mut() { 175 | let mut palette = Palette::default(); 176 | let new_color = Color::new(255, 255, 255); 177 | 178 | for color in palette.iter_mut() { 179 | *color = new_color; 180 | } 181 | 182 | for color in palette.iter() { 183 | assert_eq!(color, &new_color); 184 | } 185 | } 186 | 187 | #[test] 188 | fn test_palette_debug() { 189 | let palette = Palette::default(); 190 | let data: Vec<&Color> = palette.colors.iter().collect(); 191 | 192 | let expected = format!("Palette {{ colors: {:?} }}", data); 193 | let result = format!("{:?}", palette); 194 | 195 | assert_eq!(result, expected); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /player/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | array, 3 | sync::Arc, 4 | time::{Duration, Instant}, 5 | }; 6 | 7 | use anyhow::Result; 8 | use log::{error, info}; 9 | use pixels::{Pixels, SurfaceTexture}; 10 | use winit::{ 11 | application::ApplicationHandler, 12 | dpi::LogicalSize, 13 | event::{ElementState, KeyEvent, WindowEvent}, 14 | event_loop::{ActiveEventLoop, ControlFlow, EventLoop}, 15 | keyboard::{Key, NamedKey}, 16 | window::{Window, WindowId}, 17 | }; 18 | 19 | const WINDOW_WIDTH: u32 = 640; 20 | const WINDOW_HEIGHT: u32 = 360; 21 | 22 | const BUFFER_SIZE: usize = (WINDOW_WIDTH * WINDOW_HEIGHT * 4) as usize; 23 | const BLACK_COLOR: [u8; 4] = [0x00, 0x00, 0x00, 0xFF]; 24 | const COLOR_SIZE: usize = 4; 25 | 26 | const TARGET_FPS: f64 = 60.0; 27 | const TARGET_FRAME_TIME: f64 = 1.0 / TARGET_FPS; 28 | 29 | struct GamePlayer<'win> { 30 | pixels: Option>, 31 | window: Option>, 32 | buffer: [u8; BUFFER_SIZE], 33 | last_color: [u8; 4], 34 | should_exit: bool, 35 | is_paused: bool, 36 | previous_instant: Instant, 37 | current_instant: Instant, 38 | } 39 | 40 | impl GamePlayer<'_> { 41 | fn new() -> Self { 42 | let color = [0x00, 0x00, 0x00, 0xFF]; 43 | Self { 44 | pixels: None, 45 | window: None, 46 | buffer: array::from_fn(|i| color[i % color.len()]), 47 | last_color: color, 48 | should_exit: false, 49 | is_paused: false, 50 | previous_instant: Instant::now(), 51 | current_instant: Instant::now(), 52 | } 53 | } 54 | 55 | fn update(&mut self) { 56 | let last_value = self.last_color.len() - 1; 57 | for (i, v) in self.last_color.iter_mut().enumerate() { 58 | if i != last_value { 59 | *v = if *v == 0xFF { 0x00 } else { *v + 1 } 60 | } 61 | } 62 | 63 | for (i, pixel) in self.buffer.chunks_exact_mut(COLOR_SIZE).enumerate() { 64 | let x = i % WINDOW_WIDTH as usize; 65 | let y = i / WINDOW_WIDTH as usize; 66 | 67 | pixel.copy_from_slice(if (x / 16) % 2 == (y / 16) % 2 { 68 | &self.last_color 69 | } else { 70 | &BLACK_COLOR 71 | }); 72 | } 73 | } 74 | 75 | fn draw(&mut self) { 76 | let frame = self.pixels.as_mut().unwrap().frame_mut(); 77 | for (i, pixel) in frame.chunks_exact_mut(COLOR_SIZE).enumerate() { 78 | let color = &self.buffer[i * COLOR_SIZE..(i + 1) * COLOR_SIZE]; 79 | pixel.copy_from_slice(color); 80 | } 81 | } 82 | 83 | fn handle_input(&mut self, event: &KeyEvent) { 84 | if event.state == ElementState::Pressed && !event.repeat { 85 | match event.logical_key { 86 | Key::Named(NamedKey::Escape) => self.should_exit = true, 87 | Key::Named(NamedKey::Space) => self.is_paused = !self.is_paused, 88 | _ => {} 89 | } 90 | } 91 | } 92 | 93 | fn handle_update(&mut self) { 94 | if !self.is_paused { 95 | self.update(); 96 | } 97 | } 98 | 99 | fn handle_drawing(&mut self) { 100 | self.draw(); 101 | if let Err(err) = self.pixels.as_ref().unwrap().render() { 102 | error!("{err:?}"); 103 | self.should_exit = true; 104 | } 105 | } 106 | } 107 | 108 | impl ApplicationHandler for GamePlayer<'_> { 109 | fn resumed(&mut self, event_loop: &ActiveEventLoop) { 110 | let size = LogicalSize::new(WINDOW_WIDTH as f64, WINDOW_HEIGHT as f64); 111 | let window_attributes = Window::default_attributes() 112 | .with_title("SN-50 Player") 113 | .with_inner_size(size) 114 | .with_resizable(false); 115 | let window = Arc::new(event_loop.create_window(window_attributes).unwrap()); 116 | 117 | let window_size = window.inner_size(); 118 | let surface_texture = 119 | SurfaceTexture::new(window_size.width, window_size.height, window.clone()); 120 | let pixels = Pixels::new(WINDOW_WIDTH, WINDOW_HEIGHT, surface_texture).unwrap(); 121 | 122 | self.pixels = Some(pixels); 123 | self.window = Some(window); 124 | } 125 | 126 | fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { 127 | if self.should_exit { 128 | event_loop.exit(); 129 | return; 130 | } 131 | 132 | match event { 133 | WindowEvent::CloseRequested => { 134 | self.should_exit = true; 135 | } 136 | WindowEvent::KeyboardInput { 137 | event: key_event, .. 138 | } => { 139 | self.handle_input(&key_event); 140 | } 141 | WindowEvent::RedrawRequested => { 142 | self.current_instant = Instant::now(); 143 | let elapsed = self 144 | .current_instant 145 | .duration_since(self.previous_instant) 146 | .as_secs_f64(); 147 | self.previous_instant = self.current_instant; 148 | info!("Elapsed: {}", elapsed); 149 | 150 | self.handle_update(); 151 | self.handle_drawing(); 152 | 153 | let delay = TARGET_FRAME_TIME - elapsed; 154 | info!("Delay: {}", delay); 155 | if delay > 0.0 { 156 | std::thread::sleep(Duration::from_secs_f64(delay)); 157 | } 158 | 159 | info!("FPS: {}", 1.0 / (elapsed + delay)); 160 | 161 | self.window.as_ref().unwrap().request_redraw(); 162 | } 163 | _ => (), 164 | } 165 | } 166 | } 167 | 168 | fn main() -> Result<()> { 169 | env_logger::init(); 170 | 171 | let event_loop = EventLoop::new().unwrap(); 172 | event_loop.set_control_flow(ControlFlow::Poll); 173 | 174 | let mut player = GamePlayer::new(); 175 | event_loop.run_app(&mut player)?; 176 | 177 | Ok(()) 178 | } 179 | -------------------------------------------------------------------------------- /tinlib/src/common/coord.rs: -------------------------------------------------------------------------------- 1 | //! Coord implementation and manipulation. 2 | use std::slice; 3 | 4 | use crate::common::size::Size; 5 | 6 | /// A Coord representation. 7 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Default, PartialOrd, Ord)] 8 | pub struct Coord { 9 | pub x: usize, 10 | pub y: usize, 11 | } 12 | 13 | impl Coord { 14 | /// Creates a new Coord. 15 | pub fn new(x: usize, y: usize) -> Self { 16 | Self { x, y } 17 | } 18 | } 19 | 20 | impl From<(usize, usize)> for Coord { 21 | fn from((x, y): (usize, usize)) -> Self { 22 | Self::new(x, y) 23 | } 24 | } 25 | 26 | impl From<[usize; 2]> for Coord { 27 | fn from(array: [usize; 2]) -> Self { 28 | Self::new(array[0], array[1]) 29 | } 30 | } 31 | 32 | /// A iterator over all Coord limited by Size. 33 | pub struct CoordIter { 34 | size: Size, 35 | coord: Coord, 36 | } 37 | 38 | impl CoordIter { 39 | // Creates a new CoordIter from a Size. 40 | pub fn new(size: Size) -> Self { 41 | Self { 42 | size, 43 | coord: Coord::new(0, 0), 44 | } 45 | } 46 | } 47 | 48 | impl Iterator for CoordIter { 49 | type Item = Coord; 50 | 51 | fn next(&mut self) -> Option { 52 | if self.coord.x == self.size.height() { 53 | return None; 54 | } 55 | 56 | let result = self.coord; 57 | 58 | self.coord.y += 1; 59 | if self.coord.y == self.size.width() { 60 | self.coord.y = 0; 61 | self.coord.x += 1; 62 | } 63 | 64 | Some(result) 65 | } 66 | } 67 | 68 | /// A iterator over all Coord and their related itens, limited by Size. 69 | pub struct CoordEnumerate<'iter, T: 'iter> { 70 | coords: CoordIter, 71 | iter: slice::Iter<'iter, T>, 72 | } 73 | 74 | impl<'iter, T> CoordEnumerate<'iter, T> { 75 | /// Creates a CoordEnumerate from a CoordIter and item Iter. 76 | pub fn new(coords: CoordIter, iter: slice::Iter<'iter, T>) -> Self { 77 | Self { coords, iter } 78 | } 79 | } 80 | 81 | impl<'iter, T> Iterator for CoordEnumerate<'iter, T> { 82 | type Item = (Coord, &'iter T); 83 | 84 | fn next(&mut self) -> Option { 85 | self.coords 86 | .next() 87 | .and_then(|c| self.iter.next().map(|t| (c, t))) 88 | } 89 | } 90 | 91 | /// A mutable iterator over all Coord and their related itens, limited by Size. 92 | pub struct CoordEnumerateMut<'iter, T: 'iter> { 93 | coords: CoordIter, 94 | iter: slice::IterMut<'iter, T>, 95 | } 96 | 97 | impl<'iter, T> CoordEnumerateMut<'iter, T> { 98 | /// Creates a CoordEnumerateMut from a CoordIter and item Iter. 99 | pub fn new(coords: CoordIter, iter: slice::IterMut<'iter, T>) -> Self { 100 | Self { coords, iter } 101 | } 102 | } 103 | 104 | impl<'iter, T> Iterator for CoordEnumerateMut<'iter, T> { 105 | type Item = (Coord, &'iter mut T); 106 | fn next(&mut self) -> Option { 107 | self.coords 108 | .next() 109 | .and_then(|c| self.iter.next().map(|t| (c, t))) 110 | } 111 | } 112 | 113 | #[cfg(test)] 114 | mod test { 115 | use super::*; 116 | 117 | #[test] 118 | fn test_coord_new() { 119 | let coord = Coord::new(11, 27); 120 | 121 | assert_eq!(coord.x, 11); 122 | assert_eq!(coord.y, 27); 123 | } 124 | 125 | #[test] 126 | fn test_coord_from_tuple() { 127 | let tuple = (11usize, 27usize); 128 | let coord = Coord::from(tuple); 129 | 130 | assert_eq!(coord, Coord::new(11, 27)); 131 | } 132 | 133 | #[test] 134 | fn test_coord_from_array() { 135 | let array = [11usize, 27usize]; 136 | let coord = Coord::from(array); 137 | 138 | assert_eq!(coord, Coord::new(11, 27)); 139 | } 140 | 141 | #[test] 142 | fn test_coorditer_new() { 143 | let size = Size::new(3, 2); 144 | let coord = Coord::new(0, 0); 145 | let iter = CoordIter::new(size); 146 | 147 | assert_eq!(iter.size, size); 148 | assert_eq!(iter.coord, coord); 149 | } 150 | 151 | #[test] 152 | fn test_coorditer_next() { 153 | let size = Size::new(3, 2); 154 | let mut iter = CoordIter::new(size); 155 | 156 | assert_eq!(iter.next(), Some(Coord::new(0, 0))); 157 | assert_eq!(iter.next(), Some(Coord::new(0, 1))); 158 | assert_eq!(iter.next(), Some(Coord::new(0, 2))); 159 | assert_eq!(iter.next(), Some(Coord::new(1, 0))); 160 | assert_eq!(iter.next(), Some(Coord::new(1, 1))); 161 | assert_eq!(iter.next(), Some(Coord::new(1, 2))); 162 | assert_eq!(iter.next(), None); 163 | } 164 | 165 | #[test] 166 | fn test_coordenumerate_new_and_next() { 167 | let items = [1, 2, 3, 4, 5, 6]; 168 | let size = Size::new(3, 2); 169 | let coorditer = CoordIter::new(size); 170 | let itemiter = items.iter(); 171 | let mut enumerate = CoordEnumerate::new(coorditer, itemiter); 172 | 173 | assert_eq!(enumerate.next(), Some((Coord::new(0, 0), &1))); 174 | assert_eq!(enumerate.next(), Some((Coord::new(0, 1), &2))); 175 | assert_eq!(enumerate.next(), Some((Coord::new(0, 2), &3))); 176 | assert_eq!(enumerate.next(), Some((Coord::new(1, 0), &4))); 177 | assert_eq!(enumerate.next(), Some((Coord::new(1, 1), &5))); 178 | assert_eq!(enumerate.next(), Some((Coord::new(1, 2), &6))); 179 | assert_eq!(enumerate.next(), None); 180 | } 181 | 182 | #[test] 183 | fn test_coordenumeratemut_new_and_next() { 184 | let mut items = [1, 2, 3, 4, 5, 6]; 185 | let size = Size::new(3, 2); 186 | let coorditer = CoordIter::new(size); 187 | let itemiter = items.iter_mut(); 188 | let mut enumerate = CoordEnumerateMut::new(coorditer, itemiter); 189 | 190 | assert_eq!(enumerate.next(), Some((Coord::new(0, 0), &mut 1))); 191 | assert_eq!(enumerate.next(), Some((Coord::new(0, 1), &mut 2))); 192 | assert_eq!(enumerate.next(), Some((Coord::new(0, 2), &mut 3))); 193 | assert_eq!(enumerate.next(), Some((Coord::new(1, 0), &mut 4))); 194 | assert_eq!(enumerate.next(), Some((Coord::new(1, 1), &mut 5))); 195 | assert_eq!(enumerate.next(), Some((Coord::new(1, 2), &mut 6))); 196 | assert_eq!(enumerate.next(), None); 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /tinlib/src/graphic/font.rs: -------------------------------------------------------------------------------- 1 | //! Font implementation and manipulation. 2 | use std::fmt; 3 | use std::slice; 4 | 5 | use crate::common::Size; 6 | use crate::common::{CommonError, Result}; 7 | use crate::graphic::glyph::Glyph; 8 | 9 | /// Default number of Glyphs in a Font. 10 | const NUM_GLYPHS_IN_FONT: usize = 256; 11 | /// Default glyph width. 12 | const GLYPH_WIDTH: usize = 16; 13 | /// Default glyph height. 14 | const GLYPH_HEIGHT: usize = 16; 15 | 16 | /// A iterator over all font glyphs. 17 | pub type FontGlyphIter<'iter> = slice::Iter<'iter, Glyph>; 18 | /// A mutable iterator over all font glyphs. 19 | pub type FontGlyphIterMut<'iter> = slice::IterMut<'iter, Glyph>; 20 | 21 | /// A Font representation with N Glyphs. 22 | #[derive(Clone)] 23 | pub struct Font { 24 | /// Font's glyph size. 25 | glyph_size: Size, 26 | /// Font's glyphs. 27 | glyphs: Vec, 28 | } 29 | 30 | impl Font { 31 | /// Creates a new Font. 32 | pub fn new(glyph_size: Size, num_glyphs: usize) -> Self { 33 | Self { 34 | glyph_size, 35 | glyphs: vec![Glyph::default(); num_glyphs], 36 | } 37 | } 38 | 39 | /// Returns glyph's size. 40 | pub fn glyph_size(&self) -> Size { 41 | self.glyph_size 42 | } 43 | 44 | /// Returns the lenght. 45 | pub fn lenght(&self) -> usize { 46 | self.glyphs.len() 47 | } 48 | 49 | /// Returns a glyph. 50 | pub fn get_glyph(&self, index: usize) -> Result<&Glyph> { 51 | if !self.is_index_valid(index) { 52 | return Err(CommonError::new_invalid_index(index, self.lenght())); 53 | } 54 | 55 | Ok(&self.glyphs[index]) 56 | } 57 | 58 | /// Returns a mutable glyph. 59 | pub fn get_glyph_mut(&mut self, index: usize) -> Result<&mut Glyph> { 60 | if !self.is_index_valid(index) { 61 | return Err(CommonError::new_invalid_index(index, self.lenght())); 62 | } 63 | 64 | Ok(&mut self.glyphs[index]) 65 | } 66 | 67 | /// Sets a glyph. 68 | pub fn set_glyph(&mut self, index: usize, glyph: Glyph) -> Result<()> { 69 | if !self.is_index_valid(index) { 70 | return Err(CommonError::new_invalid_index(index, self.lenght())); 71 | } 72 | 73 | self.glyphs[index] = glyph; 74 | 75 | Ok(()) 76 | } 77 | 78 | /// Returns an iterator over all font glyphs. 79 | pub fn iter(&self) -> FontGlyphIter { 80 | self.glyphs.iter() 81 | } 82 | 83 | /// Returns a mutable iterator over all font glyphs. 84 | pub fn iter_mut(&mut self) -> FontGlyphIterMut { 85 | self.glyphs.iter_mut() 86 | } 87 | 88 | fn is_index_valid(&self, index: usize) -> bool { 89 | index < self.lenght() 90 | } 91 | } 92 | 93 | impl Default for Font { 94 | /// Creates a Font with default empty glyphs. 95 | fn default() -> Self { 96 | Self { 97 | glyph_size: Size::new(GLYPH_WIDTH, GLYPH_HEIGHT), 98 | glyphs: vec![Glyph::default(); NUM_GLYPHS_IN_FONT], 99 | } 100 | } 101 | } 102 | 103 | impl fmt::Debug for Font { 104 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 105 | let data: Vec<&Glyph> = self.glyphs.iter().collect(); 106 | 107 | f.debug_struct("Font").field("data", &data).finish() 108 | } 109 | } 110 | 111 | #[cfg(test)] 112 | mod tests { 113 | use assert_matches::assert_matches; 114 | 115 | use crate::common::Coord; 116 | use crate::graphic::glyph::GlyphPixel; 117 | 118 | use super::*; 119 | 120 | #[test] 121 | fn test_font_default() { 122 | let font = Font::default(); 123 | assert_eq!(font.glyphs.len(), NUM_GLYPHS_IN_FONT); 124 | } 125 | 126 | #[test] 127 | fn test_font_len() { 128 | let font = Font::default(); 129 | assert_eq!(font.lenght(), NUM_GLYPHS_IN_FONT); 130 | } 131 | 132 | #[test] 133 | fn test_font_get_glyph() { 134 | let font = Font::default(); 135 | let glyph = Glyph::default(); 136 | 137 | let result = font.get_glyph(0); 138 | assert!(result.is_ok()); 139 | assert_eq!(result.unwrap(), &glyph); 140 | } 141 | 142 | #[test] 143 | fn test_font_get_glyph_invalid_index() { 144 | let font = Font::default(); 145 | let index = 256usize; 146 | 147 | let result = font.get_glyph(index); 148 | assert!(result.is_err()); 149 | assert_matches!( 150 | result.unwrap_err(), 151 | CommonError::InvalidIndex { index: i, lenght: l } if i == index && l == font.lenght() 152 | ); 153 | } 154 | 155 | #[test] 156 | fn test_font_set_glyph() { 157 | let mut font = Font::default(); 158 | 159 | let coord = Coord::new(0, 0); 160 | let mut new_glyph = Glyph::default(); 161 | new_glyph.set_pixel(coord, GlyphPixel::Solid).unwrap(); 162 | 163 | let result = font.set_glyph(0, new_glyph.clone()); 164 | assert!(result.is_ok()); 165 | 166 | let result = font.get_glyph(0); 167 | assert_eq!(result.unwrap(), &new_glyph); 168 | } 169 | 170 | #[test] 171 | fn test_font_set_glyph_invalid_index() { 172 | let mut font = Font::default(); 173 | let glyph = Glyph::default(); 174 | let index = 256usize; 175 | 176 | let result = font.set_glyph(index, glyph); 177 | assert!(result.is_err()); 178 | assert_matches!( 179 | result.unwrap_err(), 180 | CommonError::InvalidIndex { index: i, lenght: l } if i == index && l == font.lenght() 181 | ); 182 | } 183 | 184 | #[test] 185 | fn test_font_iter() { 186 | let font = Font::default(); 187 | let default_glyph = Glyph::default(); 188 | 189 | for color in font.iter() { 190 | assert_eq!(color, &default_glyph); 191 | } 192 | } 193 | 194 | #[test] 195 | fn test_font_iter_mut() { 196 | let mut font = Font::default(); 197 | 198 | let coord = Coord::new(0, 0); 199 | let mut new_glyph = Glyph::default(); 200 | new_glyph.set_pixel(coord, GlyphPixel::Solid).unwrap(); 201 | 202 | for glyph in font.iter_mut() { 203 | *glyph = new_glyph.clone(); 204 | } 205 | 206 | for glyph in font.iter() { 207 | assert_eq!(glyph, &new_glyph); 208 | } 209 | } 210 | 211 | #[test] 212 | fn test_font_debug() { 213 | let font = Font::default(); 214 | let data: Vec<&Glyph> = font.glyphs.iter().collect(); 215 | 216 | let expected = format!("Font {{ data: {:?} }}", data); 217 | let result = format!("{:?}", font); 218 | 219 | assert_eq!(result, expected); 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /tinlib/src/machine/screen.rs: -------------------------------------------------------------------------------- 1 | //! Screen implementation and manipulation. 2 | use std::fmt; 3 | use std::slice; 4 | 5 | use crate::common::{ 6 | CommonError, Coord, CoordEnumerate, CoordEnumerateMut, CoordIter, Result, Size, 7 | }; 8 | use crate::graphic::Color; 9 | 10 | /// Screen width in pixels. 11 | const SCREEN_WIDTH: usize = 640; 12 | /// Screen width in pixels. 13 | const SCREEN_HEIGHT: usize = 384; 14 | 15 | /// A screen pixel or color. 16 | pub type ScreenPixel = Color; 17 | /// A iterator over all screen pixels. 18 | pub type ScreenPixelIter<'iter> = slice::Iter<'iter, ScreenPixel>; 19 | /// A mutable iterator over all screen pixels. 20 | pub type ScreenPixelIterMut<'iter> = slice::IterMut<'iter, ScreenPixel>; 21 | /// A enumeration iterator over all screen pixels and their coords. 22 | pub type ScreenPixelEnumerate<'iter> = CoordEnumerate<'iter, ScreenPixel>; 23 | /// A mutable enumeration iterator over all screen pixels and their coords. 24 | pub type ScreenPixelEnumerateMut<'iter> = CoordEnumerateMut<'iter, ScreenPixel>; 25 | 26 | /// A Screen representation with 640x384 tiles. 27 | pub struct Screen { 28 | pixels: [Color; SCREEN_WIDTH * SCREEN_HEIGHT], 29 | } 30 | 31 | impl Screen { 32 | /// Returns the width. 33 | pub fn width(&self) -> usize { 34 | SCREEN_WIDTH 35 | } 36 | 37 | /// Returns the height. 38 | pub fn height(&self) -> usize { 39 | SCREEN_HEIGHT 40 | } 41 | 42 | /// Returns the size. 43 | pub fn size(&self) -> Size { 44 | Size::new(self.width(), self.height()) 45 | } 46 | 47 | /// Returns a pixel. 48 | pub fn get_pixel(&self, coord: Coord) -> Result { 49 | if !self.is_coord_valid(coord) { 50 | return Err(CommonError::new_invalid_coord(coord, self.size())); 51 | } 52 | 53 | let index = self.get_index(coord); 54 | Ok(self.pixels[index]) 55 | } 56 | 57 | /// Sets a pixels. 58 | pub fn set_pixel(&mut self, coord: Coord, pixel: ScreenPixel) -> Result<()> { 59 | if !self.is_coord_valid(coord) { 60 | return Err(CommonError::new_invalid_coord(coord, self.size())); 61 | } 62 | 63 | let index = self.get_index(coord); 64 | self.pixels[index] = pixel; 65 | 66 | Ok(()) 67 | } 68 | 69 | /// Clears all pixels to black. 70 | pub fn clear(&mut self) { 71 | for pixel in self.pixels.iter_mut() { 72 | *pixel = ScreenPixel::default(); 73 | } 74 | } 75 | 76 | /// Returns an iterator over all screen coords. 77 | pub fn coords(&self) -> CoordIter { 78 | CoordIter::new(self.size()) 79 | } 80 | 81 | /// Returns an iterator over all screen pixels. 82 | pub fn iter(&self) -> ScreenPixelIter { 83 | self.pixels.iter() 84 | } 85 | 86 | /// Returns a mutable iterator over all screen pixels. 87 | pub fn iter_mut(&mut self) -> ScreenPixelIterMut { 88 | self.pixels.iter_mut() 89 | } 90 | 91 | /// Returns an enumerate iterator over all screen pixels and tiles. 92 | pub fn enumerate(&self) -> ScreenPixelEnumerate { 93 | ScreenPixelEnumerate::new(self.coords(), self.iter()) 94 | } 95 | 96 | /// Returns a mutable enumerate iterator over all screen pixels and tiles. 97 | pub fn enumerate_mut(&mut self) -> ScreenPixelEnumerateMut { 98 | ScreenPixelEnumerateMut::new(self.coords(), self.iter_mut()) 99 | } 100 | 101 | fn is_coord_valid(&self, coord: Coord) -> bool { 102 | coord.x < self.width() && coord.y < self.height() 103 | } 104 | 105 | fn get_index(&self, coord: Coord) -> usize { 106 | coord.x * self.width() + coord.y 107 | } 108 | } 109 | 110 | impl Default for Screen { 111 | /// Creates a new black Screen. 112 | fn default() -> Self { 113 | Self { 114 | pixels: [Color::default(); SCREEN_WIDTH * SCREEN_HEIGHT], 115 | } 116 | } 117 | } 118 | 119 | impl fmt::Debug for Screen { 120 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 121 | let pixels: Vec<&ScreenPixel> = self.pixels.iter().collect(); 122 | 123 | f.debug_struct("Screen").field("pixels", &pixels).finish() 124 | } 125 | } 126 | 127 | #[cfg(test)] 128 | mod tests { 129 | use assert_matches::assert_matches; 130 | 131 | use super::*; 132 | 133 | #[test] 134 | fn test_screen_default() { 135 | let screen = Screen::default(); 136 | let default_pixel = ScreenPixel::default(); 137 | 138 | assert_eq!(screen.pixels.len(), SCREEN_WIDTH * SCREEN_HEIGHT); 139 | assert!(screen.pixels.iter().all(|p| *p == default_pixel)); 140 | } 141 | 142 | #[test] 143 | fn test_screen_width_height_and_size() { 144 | let screen = Screen::default(); 145 | 146 | assert_eq!(screen.width(), SCREEN_WIDTH); 147 | assert_eq!(screen.height(), SCREEN_HEIGHT); 148 | assert_eq!(screen.size(), Size::new(SCREEN_WIDTH, SCREEN_HEIGHT)); 149 | } 150 | 151 | #[test] 152 | fn test_screen_get_pixel() { 153 | let screen = Screen::default(); 154 | let coord = Coord::new(1, 1); 155 | let color = Color::default(); 156 | 157 | let result = screen.get_pixel(coord); 158 | assert!(result.is_ok()); 159 | assert_eq!(result.unwrap(), color); 160 | } 161 | 162 | #[test] 163 | fn test_screen_get_pixel_invalid_coord() { 164 | let screen = Screen::default(); 165 | let coord = Coord::new(641, 1); 166 | 167 | let result = screen.get_pixel(coord); 168 | assert!(result.is_err()); 169 | assert_matches!( 170 | result.unwrap_err(), 171 | CommonError::InvalidCoord { coord: c, size: s } if c == coord && s == screen.size() 172 | ); 173 | } 174 | 175 | #[test] 176 | fn test_screen_set_pixel() { 177 | let mut screen = Screen::default(); 178 | let coord = Coord::new(1, 1); 179 | let pixel = ScreenPixel::new(255, 255, 255); 180 | 181 | let result = screen.set_pixel(coord, pixel); 182 | assert!(result.is_ok()); 183 | 184 | let result = screen.get_pixel(coord); 185 | assert!(result.is_ok()); 186 | assert_eq!(result.unwrap(), pixel); 187 | } 188 | 189 | #[test] 190 | fn test_screen_set_pixel_invalid_coord() { 191 | let mut screen = Screen::default(); 192 | let coord = Coord::new(641, 1); 193 | let pixel = ScreenPixel::new(255, 255, 255); 194 | 195 | let result = screen.set_pixel(coord, pixel); 196 | assert!(result.is_err()); 197 | assert_matches!( 198 | result.unwrap_err(), 199 | CommonError::InvalidCoord { coord: c, size: s } if c == coord && s == screen.size() 200 | ); 201 | } 202 | 203 | #[test] 204 | fn test_screen_coords() { 205 | let screen = Screen::default(); 206 | 207 | let mut x = 0usize; 208 | let mut y = 0usize; 209 | for coord in screen.coords() { 210 | assert_eq!(coord.x, x); 211 | assert_eq!(coord.y, y); 212 | 213 | y += 1; 214 | if y == screen.width() { 215 | y = 0; 216 | x += 1; 217 | } 218 | } 219 | } 220 | 221 | #[test] 222 | fn test_screen_iter() { 223 | let screen = Screen::default(); 224 | let default_pixel = ScreenPixel::default(); 225 | 226 | for pixel in screen.iter() { 227 | assert_eq!(pixel, &default_pixel); 228 | } 229 | } 230 | 231 | #[test] 232 | fn test_screen_iter_mut() { 233 | let mut screen = Screen::default(); 234 | let new_pixel = ScreenPixel::new(255, 255, 255); 235 | 236 | for pixel in screen.iter_mut() { 237 | *pixel = new_pixel; 238 | } 239 | 240 | for pixel in screen.iter() { 241 | assert_eq!(pixel, &new_pixel); 242 | } 243 | } 244 | 245 | #[test] 246 | fn test_screen_enumerate() { 247 | let screen = Screen::default(); 248 | let mut coorditer = screen.coords(); 249 | let mut pixeliter = screen.iter(); 250 | 251 | for (coord, pixel) in screen.enumerate() { 252 | assert_eq!(coord, coorditer.next().unwrap()); 253 | assert_eq!(pixel, pixeliter.next().unwrap()); 254 | } 255 | } 256 | 257 | #[test] 258 | fn test_screen_enumerate_mut() { 259 | let mut screen = Screen::default(); 260 | let mut coorditer = screen.coords(); 261 | let old_pixel = ScreenPixel::default(); 262 | let new_pixel = ScreenPixel::new(255, 255, 255); 263 | 264 | for (coord, pixel) in screen.enumerate_mut() { 265 | assert_eq!(coord, coorditer.next().unwrap()); 266 | assert_eq!(pixel, &old_pixel); 267 | 268 | *pixel = new_pixel; 269 | } 270 | 271 | for pixel in screen.iter() { 272 | assert_eq!(pixel, &new_pixel); 273 | } 274 | } 275 | 276 | #[test] 277 | fn test_screen_debug() { 278 | let screen = Screen::default(); 279 | let data: Vec<&ScreenPixel> = screen.pixels.iter().collect(); 280 | 281 | let expected = format!("Screen {{ pixels: {:?} }}", data); 282 | let result = format!("{:?}", screen); 283 | 284 | assert_eq!(result, expected); 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /tinlib/src/graphic/glyph.rs: -------------------------------------------------------------------------------- 1 | //! Glyph implementation and manipulation. 2 | use std::fmt; 3 | use std::slice; 4 | 5 | use crate::common::{ 6 | CommonError, Coord, CoordEnumerate, CoordEnumerateMut, CoordIter, Result, Size, 7 | }; 8 | 9 | /// The default Glyph width. 10 | pub const GLYPH_WIDTH: usize = 16; 11 | /// The default Glyph height. 12 | pub const GLYPH_HEIGHT: usize = 16; 13 | 14 | /// A Glyph pixel representation. 15 | #[derive(Debug, Clone, Copy, PartialEq)] 16 | pub enum GlyphPixel { 17 | /// An empty or transparent pixel. 18 | Empty, 19 | /// An solid pixel. 20 | Solid, 21 | } 22 | 23 | /// A iterator over all glyph pìxels. 24 | pub type GlyphPixelIter<'iter> = slice::Iter<'iter, GlyphPixel>; 25 | /// A mutable iterator over all glyph pìxels. 26 | pub type GlyphPixelIterMut<'iter> = slice::IterMut<'iter, GlyphPixel>; 27 | /// A enumeration iterator over all glyph pixels and their coords. 28 | pub type GlyphPixelEnumerate<'iter> = CoordEnumerate<'iter, GlyphPixel>; 29 | /// A mutable enumeration iterator over all glyph pixels and their coords. 30 | pub type GlyphPixelEnumerateMut<'iter> = CoordEnumerateMut<'iter, GlyphPixel>; 31 | 32 | /// A Glyph representation with NxM Pixels. 33 | #[derive(Clone)] 34 | pub struct Glyph { 35 | size: Size, 36 | data: Vec, 37 | } 38 | 39 | impl Glyph { 40 | /// Creates a new Glyph. 41 | pub fn new(size: Size) -> Self { 42 | Self { 43 | size, 44 | data: vec![GlyphPixel::Empty; size.width() * size.height()], 45 | } 46 | } 47 | 48 | /// Returns a Size. 49 | pub fn size(&self) -> Size { 50 | self.size 51 | } 52 | 53 | /// Returns a pixel. 54 | pub fn get_pixel(&self, coord: Coord) -> Result { 55 | if !self.is_coord_valid(coord) { 56 | return Err(CommonError::new_invalid_coord(coord, self.size)); 57 | } 58 | 59 | let index = self.get_index(coord); 60 | Ok(self.data[index]) 61 | } 62 | 63 | /// Sets a pixel. 64 | pub fn set_pixel(&mut self, coord: Coord, value: GlyphPixel) -> Result<()> { 65 | if !self.is_coord_valid(coord) { 66 | return Err(CommonError::new_invalid_coord(coord, self.size)); 67 | } 68 | 69 | let index = self.get_index(coord); 70 | self.data[index] = value; 71 | 72 | Ok(()) 73 | } 74 | 75 | /// Returns a iterator over the glyph's coords. 76 | pub fn coords(&self) -> CoordIter { 77 | CoordIter::new(self.size()) 78 | } 79 | 80 | /// Returns an iterator over all Glyph pixels. 81 | pub fn iter(&self) -> GlyphPixelIter { 82 | self.data.iter() 83 | } 84 | 85 | /// Returns a mutable iterator over all Glyph pixels. 86 | pub fn iter_mut(&mut self) -> GlyphPixelIterMut { 87 | self.data.iter_mut() 88 | } 89 | 90 | /// Returns an enumerate iterator over glyph's coords and pixels. 91 | pub fn enumerate(&self) -> GlyphPixelEnumerate { 92 | GlyphPixelEnumerate::new(self.coords(), self.iter()) 93 | } 94 | 95 | /// Returns a mutable enumerate iterator over glyph's coords and pixels. 96 | pub fn enumerate_mut(&mut self) -> GlyphPixelEnumerateMut { 97 | GlyphPixelEnumerateMut::new(self.coords(), self.iter_mut()) 98 | } 99 | 100 | fn is_coord_valid(&self, coord: Coord) -> bool { 101 | coord.x < self.size.width() && coord.y < self.size.height() 102 | } 103 | 104 | fn get_index(&self, coord: Coord) -> usize { 105 | coord.x * self.size.width() + coord.y 106 | } 107 | } 108 | 109 | impl Default for Glyph { 110 | /// Creates a Glyph with all pixels black. 111 | fn default() -> Self { 112 | Self { 113 | size: Size::new(GLYPH_WIDTH, GLYPH_HEIGHT), 114 | data: vec![GlyphPixel::Empty; GLYPH_WIDTH * GLYPH_HEIGHT], 115 | } 116 | } 117 | } 118 | 119 | impl PartialEq for Glyph { 120 | fn eq(&self, other: &Self) -> bool { 121 | self.data.iter().zip(other.data.iter()).all(|(a, b)| a == b) 122 | } 123 | } 124 | 125 | impl fmt::Debug for Glyph { 126 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 127 | let data: Vec<&GlyphPixel> = self.data.iter().collect(); 128 | 129 | f.debug_struct("Glyph").field("data", &data).finish() 130 | } 131 | } 132 | 133 | #[cfg(test)] 134 | mod tests { 135 | use assert_matches::assert_matches; 136 | 137 | use super::*; 138 | 139 | #[test] 140 | fn test_glyph_default() { 141 | let glyph = Glyph::default(); 142 | 143 | assert_eq!(glyph.data.len(), GLYPH_WIDTH * GLYPH_HEIGHT); 144 | for pixel in glyph.data.iter() { 145 | assert_eq!(*pixel, GlyphPixel::Empty); 146 | } 147 | } 148 | 149 | #[test] 150 | fn test_glyph_size() { 151 | let glyph = Glyph::default(); 152 | assert_eq!(glyph.size(), Size::new(GLYPH_WIDTH, GLYPH_HEIGHT)); 153 | } 154 | 155 | #[test] 156 | fn test_glyph_get_pixel() { 157 | let coord = Coord::new(1, 1); 158 | let glyph = Glyph::default(); 159 | 160 | let result = glyph.get_pixel(coord); 161 | assert!(result.is_ok()); 162 | assert_eq!(result.unwrap(), GlyphPixel::Empty); 163 | } 164 | 165 | #[test] 166 | fn test_glyph_get_pixel_invalid_coord() { 167 | let coord = Coord::new(17, 1); 168 | let glyph = Glyph::default(); 169 | 170 | let result = glyph.get_pixel(coord); 171 | assert!(result.is_err()); 172 | assert_matches!( 173 | result.unwrap_err(), 174 | CommonError::InvalidCoord { coord: c, size: s } if c == coord && s == glyph.size() 175 | ); 176 | } 177 | 178 | #[test] 179 | fn test_glyph_set_pixel() { 180 | let coord = Coord::new(1, 1); 181 | let mut glyph = Glyph::default(); 182 | 183 | let result = glyph.set_pixel(coord, GlyphPixel::Solid); 184 | assert!(result.is_ok()); 185 | 186 | let result = glyph.get_pixel(coord); 187 | assert!(result.is_ok()); 188 | assert_eq!(result.unwrap(), GlyphPixel::Solid); 189 | } 190 | 191 | #[test] 192 | fn test_glyph_set_pixel_invalid_coord() { 193 | let coord = Coord::new(17, 1); 194 | let mut glyph = Glyph::default(); 195 | 196 | let result = glyph.set_pixel(coord, GlyphPixel::Solid); 197 | assert!(result.is_err()); 198 | assert_matches!( 199 | result.unwrap_err(), 200 | CommonError::InvalidCoord { coord: c, size: s } if c == coord && s == glyph.size() 201 | ); 202 | } 203 | 204 | #[test] 205 | fn test_glyph_coords() { 206 | let glyph = Glyph::default(); 207 | 208 | let mut x = 0usize; 209 | let mut y = 0usize; 210 | for coord in glyph.coords() { 211 | assert_eq!(coord.x, x); 212 | assert_eq!(coord.y, y); 213 | 214 | y += 1; 215 | if y == glyph.size().width() { 216 | y = 0; 217 | x += 1; 218 | } 219 | } 220 | } 221 | 222 | #[test] 223 | fn test_glyph_iter() { 224 | let glyph = Glyph::default(); 225 | let default_pixel = GlyphPixel::Empty; 226 | 227 | for pixel in glyph.iter() { 228 | assert_eq!(pixel, &default_pixel); 229 | } 230 | } 231 | 232 | #[test] 233 | fn test_glyph_iter_mut() { 234 | let mut glyph = Glyph::default(); 235 | let new_pixel = GlyphPixel::Solid; 236 | 237 | for pixel in glyph.iter_mut() { 238 | *pixel = new_pixel; 239 | } 240 | 241 | for pixel in glyph.iter() { 242 | assert_eq!(pixel, &new_pixel); 243 | } 244 | } 245 | 246 | #[test] 247 | fn test_glyph_enumerate() { 248 | let glyph = Glyph::default(); 249 | let mut coorditer = glyph.coords(); 250 | let mut pixeliter = glyph.iter(); 251 | 252 | for (coord, pixel) in glyph.enumerate() { 253 | assert_eq!(coord, coorditer.next().unwrap()); 254 | assert_eq!(pixel, pixeliter.next().unwrap()); 255 | } 256 | } 257 | 258 | #[test] 259 | fn test_glyph_enumerate_mut() { 260 | let mut glyph = Glyph::default(); 261 | let mut coorditer = glyph.coords(); 262 | let old_pixel = GlyphPixel::Empty; 263 | let new_pixel = GlyphPixel::Solid; 264 | 265 | for (coord, pixel) in glyph.enumerate_mut() { 266 | assert_eq!(coord, coorditer.next().unwrap()); 267 | assert_eq!(pixel, &old_pixel); 268 | 269 | *pixel = new_pixel; 270 | } 271 | 272 | for pixel in glyph.iter() { 273 | assert_eq!(pixel, &new_pixel); 274 | } 275 | } 276 | 277 | #[test] 278 | fn test_glyph_partialeq() { 279 | let glyph_1 = Glyph::default(); 280 | let mut glyph_2 = Glyph::default(); 281 | 282 | assert_eq!(glyph_1, glyph_2); 283 | 284 | glyph_2.data[0] = GlyphPixel::Solid; 285 | assert_ne!(glyph_1, glyph_2); 286 | } 287 | 288 | #[test] 289 | fn test_glyph_debug() { 290 | let glyph = Glyph::default(); 291 | let data: Vec<&GlyphPixel> = glyph.data.iter().collect(); 292 | 293 | let expected = format!("Glyph {{ data: {:?} }}", data); 294 | let result = format!("{:?}", glyph); 295 | 296 | assert_eq!(result, expected); 297 | } 298 | } 299 | -------------------------------------------------------------------------------- /tinlib/src/map/mod.rs: -------------------------------------------------------------------------------- 1 | //! Map utilities. 2 | use std::fmt; 3 | use std::slice; 4 | 5 | use crate::common::{ 6 | CommonError, Coord, CoordEnumerate, CoordEnumerateMut, CoordIter, Result, Size, 7 | }; 8 | use crate::graphic::{Color, Glyph}; 9 | 10 | /// Map width in Glyphs. 11 | const MAP_WIDTH: usize = 320; 12 | /// Map height in Glyphs. 13 | const MAP_HEIGHT: usize = 192; 14 | 15 | /// A Tile representation with a glyph and a color. 16 | #[derive(Clone, Copy, PartialEq)] 17 | pub struct Tile<'refs> { 18 | /// A reference to a Glyph. 19 | pub glyph: &'refs Glyph, 20 | /// A reference to a Color. 21 | pub color: &'refs Color, 22 | } 23 | 24 | impl<'refs> Tile<'refs> { 25 | /// Creates a new Tile with references to a Glyph and a Color. 26 | pub fn new(glyph: &'refs Glyph, color: &'refs Color) -> Self { 27 | Self { glyph, color } 28 | } 29 | } 30 | 31 | impl fmt::Debug for Tile<'_> { 32 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 33 | f.debug_struct("Tile") 34 | .field("glyph", self.glyph) 35 | .field("color", self.color) 36 | .finish() 37 | } 38 | } 39 | 40 | /// A iterator over all map tiles. 41 | pub type MapTileIter<'iter, 'tile> = slice::Iter<'iter, Option>>; 42 | /// A mutable iterator over all map tiles. 43 | pub type MapTileIterMut<'iter, 'tile> = slice::IterMut<'iter, Option>>; 44 | /// A enumeration iterator over all map tiles and their coords. 45 | pub type MapTileEnumerate<'iter, 'tile> = CoordEnumerate<'iter, Option>>; 46 | /// A mutable enumeration iterator over all map tiles and their coords. 47 | pub type MapTileEnumerateMut<'iter, 'tile> = CoordEnumerateMut<'iter, Option>>; 48 | 49 | /// A Map representation with 320x192 tiles. 50 | pub struct Map<'tile> { 51 | /// Map's tiles. 52 | pub tiles: [Option>; MAP_WIDTH * MAP_HEIGHT], 53 | } 54 | 55 | impl<'tile> Map<'tile> { 56 | /// Returns the width. 57 | pub fn width(&self) -> usize { 58 | MAP_WIDTH 59 | } 60 | 61 | /// Returns the height. 62 | pub fn height(&self) -> usize { 63 | MAP_HEIGHT 64 | } 65 | 66 | /// Returns the size. 67 | pub fn size(&self) -> Size { 68 | Size::new(self.width(), self.height()) 69 | } 70 | 71 | /// Returns a tile. 72 | pub fn get_tile(&self, coord: Coord) -> Result>> { 73 | if !self.is_coord_valid(coord) { 74 | return Err(CommonError::new_invalid_coord(coord, self.size())); 75 | } 76 | 77 | let index = self.get_index(coord); 78 | Ok(self.tiles[index]) 79 | } 80 | 81 | /// Sets a tile. 82 | pub fn set_tile(&mut self, coord: Coord, value: Tile<'tile>) -> Result<()> { 83 | if !self.is_coord_valid(coord) { 84 | return Err(CommonError::new_invalid_coord(coord, self.size())); 85 | } 86 | 87 | let index = self.get_index(coord); 88 | self.tiles[index] = Some(value); 89 | 90 | Ok(()) 91 | } 92 | 93 | /// Returns an iterator over all map coords. 94 | pub fn coords(&self) -> CoordIter { 95 | CoordIter::new(self.size()) 96 | } 97 | 98 | /// Returns an iterator over all map tiles. 99 | pub fn iter(&self) -> MapTileIter { 100 | self.tiles.iter() 101 | } 102 | 103 | /// Returns a mutable iterator over all map tiles. 104 | pub fn iter_mut<'iter>(&'iter mut self) -> MapTileIterMut<'iter, 'tile> { 105 | self.tiles.iter_mut() 106 | } 107 | 108 | /// Returns an enumerate iterator over all map coords and tiles. 109 | pub fn enumerate(&self) -> MapTileEnumerate { 110 | MapTileEnumerate::new(self.coords(), self.iter()) 111 | } 112 | 113 | /// Returns a mutable enumerate iterator over all map coords and tiles. 114 | pub fn enumerate_mut<'iter>(&'iter mut self) -> MapTileEnumerateMut<'iter, 'tile> { 115 | MapTileEnumerateMut::new(self.coords(), self.iter_mut()) 116 | } 117 | 118 | fn is_coord_valid(&self, coord: Coord) -> bool { 119 | coord.x < self.width() && coord.y < self.height() 120 | } 121 | 122 | fn get_index(&self, coord: Coord) -> usize { 123 | coord.x * self.width() + coord.y 124 | } 125 | } 126 | 127 | impl Default for Map<'_> { 128 | /// Creates a new empty Map. 129 | fn default() -> Self { 130 | Self { 131 | tiles: [None; MAP_WIDTH * MAP_HEIGHT], 132 | } 133 | } 134 | } 135 | 136 | impl<'tile> fmt::Debug for Map<'tile> { 137 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 138 | let tiles: Vec<&Option>> = self.tiles.iter().collect(); 139 | 140 | f.debug_struct("Map").field("tiles", &tiles).finish() 141 | } 142 | } 143 | 144 | #[cfg(test)] 145 | mod tests { 146 | use assert_matches::assert_matches; 147 | 148 | use super::*; 149 | 150 | #[test] 151 | fn test_tile_default() { 152 | let glyph = Glyph::default(); 153 | let color = Color::default(); 154 | 155 | let tile = Tile::new(&glyph, &color); 156 | 157 | assert_eq!(tile.glyph, &glyph); 158 | assert_eq!(tile.color, &color); 159 | } 160 | 161 | #[test] 162 | fn test_tile_debug() { 163 | let glyph = Glyph::default(); 164 | let color = Color::default(); 165 | 166 | let tile = Tile::new(&glyph, &color); 167 | 168 | let expected = format!("Tile {{ glyph: {:?}, color: {:?} }}", &glyph, &color); 169 | let result = format!("{:?}", tile); 170 | 171 | assert_eq!(result, expected); 172 | } 173 | 174 | #[test] 175 | fn test_map_new() { 176 | let map = Map::default(); 177 | 178 | assert_eq!(map.tiles.len(), MAP_WIDTH * MAP_HEIGHT); 179 | } 180 | 181 | #[test] 182 | fn test_map_width_height_and_size() { 183 | let map = Map::default(); 184 | 185 | assert_eq!(map.width(), MAP_WIDTH); 186 | assert_eq!(map.height(), MAP_HEIGHT); 187 | assert_eq!(map.size(), Size::new(MAP_WIDTH, MAP_HEIGHT)); 188 | } 189 | 190 | #[test] 191 | fn test_map_get_tile() { 192 | let coord = Coord::new(1, 1); 193 | let map = Map::default(); 194 | 195 | let result = map.get_tile(coord); 196 | assert!(result.is_ok()); 197 | assert!(result.unwrap().is_none()); 198 | } 199 | 200 | #[test] 201 | fn test_map_get_tile_invalid_coord() { 202 | let coord = Coord::new(321, 1); 203 | let map = Map::default(); 204 | 205 | let result = map.get_tile(coord); 206 | assert!(result.is_err()); 207 | assert_matches!( 208 | result.unwrap_err(), 209 | CommonError::InvalidCoord { coord: c, size: s } if c == coord && s == map.size() 210 | ); 211 | } 212 | 213 | #[test] 214 | fn test_map_set_tile() { 215 | let glyph = Glyph::default(); 216 | let color = Color::default(); 217 | 218 | let coord = Coord::new(1, 1); 219 | let mut map = Map::default(); 220 | let tile = Tile::new(&glyph, &color); 221 | 222 | let result = map.set_tile(coord, tile); 223 | assert!(result.is_ok()); 224 | 225 | let result = map.get_tile(coord); 226 | assert!(result.is_ok()); 227 | let option = result.unwrap(); 228 | assert!(option.is_some()); 229 | assert_eq!(option.unwrap(), tile); 230 | } 231 | 232 | #[test] 233 | fn test_map_set_tile_invalid_coord() { 234 | let glyph = Glyph::default(); 235 | let color = Color::default(); 236 | 237 | let coord = Coord::new(321, 1); 238 | let mut map = Map::default(); 239 | let tile = Tile::new(&glyph, &color); 240 | 241 | let result = map.set_tile(coord, tile); 242 | assert!(result.is_err()); 243 | assert_matches!( 244 | result.unwrap_err(), 245 | CommonError::InvalidCoord { coord: c, size: s } if c == coord && s == map.size() 246 | ); 247 | } 248 | 249 | #[test] 250 | fn test_map_coords() { 251 | let map = Map::default(); 252 | 253 | let mut x = 0usize; 254 | let mut y = 0usize; 255 | for coord in map.coords() { 256 | assert_eq!(coord.x, x); 257 | assert_eq!(coord.y, y); 258 | 259 | y += 1; 260 | if y == map.width() { 261 | y = 0; 262 | x += 1; 263 | } 264 | } 265 | } 266 | 267 | #[test] 268 | fn test_map_iter() { 269 | let map = Map::default(); 270 | 271 | for tile in map.iter() { 272 | assert!(tile.is_none()); 273 | } 274 | } 275 | 276 | #[test] 277 | fn test_map_iter_mut() { 278 | let glyph = Glyph::default(); 279 | let color = Color::default(); 280 | 281 | let mut map = Map::default(); 282 | let new_tile = Tile::new(&glyph, &color); 283 | 284 | for tile in map.iter_mut() { 285 | *tile = Some(new_tile); 286 | } 287 | 288 | for tile in map.iter() { 289 | assert!(tile.is_some()); 290 | assert_eq!(tile.unwrap(), new_tile); 291 | } 292 | } 293 | 294 | #[test] 295 | fn test_map_enumerate() { 296 | let map = Map::default(); 297 | let mut coorditer = map.coords(); 298 | let mut pixeliter = map.iter(); 299 | 300 | for (coord, tile) in map.enumerate() { 301 | assert_eq!(coord, coorditer.next().unwrap()); 302 | assert_eq!(tile, pixeliter.next().unwrap()); 303 | } 304 | } 305 | 306 | #[test] 307 | fn test_map_enumerate_mut() { 308 | let glyph = Glyph::default(); 309 | let color = Color::default(); 310 | 311 | let mut map = Map::default(); 312 | let mut coorditer = map.coords(); 313 | let new_tile = Tile::new(&glyph, &color); 314 | 315 | for (coord, tile) in map.enumerate_mut() { 316 | assert_eq!(coord, coorditer.next().unwrap()); 317 | assert!(tile.is_none()); 318 | 319 | *tile = Some(new_tile); 320 | } 321 | 322 | for tile in map.iter() { 323 | assert!(tile.is_some()); 324 | assert_eq!(tile.unwrap(), new_tile); 325 | } 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /tinlib/src/cartridge/chunk.rs: -------------------------------------------------------------------------------- 1 | //! Chunk implementation and manipulation.\ 2 | use std::convert::TryFrom; 3 | use std::io::{Read, Write}; 4 | use std::result::Result as StdResult; 5 | 6 | use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; 7 | 8 | use crate::cartridge::error::{CartridgeError, Result}; 9 | 10 | // Valid chunk sizes. 11 | // TODO Use machine constants to give meaning to these guys. 12 | const END_CHUNK_VALID_SIZE: [usize; 1] = [0]; 13 | const COVER_CHUNK_VALID_SIZES: [usize; 2] = [0, 245760]; 14 | const FONT_CHUNK_VALID_SIZES: [usize; 2] = [0, 16384]; 15 | const PALETTE_CHUNK_VALID_SIZES: [usize; 4] = [0, 12, 24, 48]; 16 | const CODE_CHUNK_MAX_SIZE: usize = 131072; 17 | const MAP_CHUNK_MAX_SIZE: usize = 122880; 18 | 19 | /// The Chunk type. 20 | #[derive(Debug, Copy, Clone, PartialEq)] 21 | pub enum ChunkType { 22 | /// The End of all chunk data. 23 | End = 0, 24 | /// Cover data. 25 | Cover = 1, 26 | /// Code data. 27 | Code = 2, 28 | /// Dont data. 29 | Font = 3, 30 | /// Palette data. 31 | Palette = 4, 32 | /// Map data. 33 | Map = 5, 34 | } 35 | 36 | impl TryFrom for ChunkType { 37 | type Error = CartridgeError; 38 | 39 | fn try_from(value: u8) -> StdResult { 40 | match value { 41 | 0 => Ok(ChunkType::End), 42 | 1 => Ok(ChunkType::Cover), 43 | 2 => Ok(ChunkType::Code), 44 | 3 => Ok(ChunkType::Font), 45 | 4 => Ok(ChunkType::Palette), 46 | 5 => Ok(ChunkType::Map), 47 | _ => Err(CartridgeError::new_invalid_chunk_type(value)), 48 | } 49 | } 50 | } 51 | 52 | /// The chunk header. 53 | #[derive(Debug, Clone, PartialEq)] 54 | pub struct ChunkHeader { 55 | /// The chunk type value. 56 | chunk_type: ChunkType, 57 | /// The chunk size. 58 | size: u32, 59 | } 60 | 61 | impl ChunkHeader { 62 | /// Creates a ChunkHeader with the type and data provided. 63 | pub fn new(chunk_type: ChunkType, size: usize) -> Self { 64 | Self { 65 | chunk_type, 66 | size: size as u32, 67 | } 68 | } 69 | 70 | /// Creates a ChunkHeader from the data read from a Reader. 71 | pub fn from_reader(reader: &mut R) -> Result { 72 | let chunk_type = reader.read_u8()?; 73 | let chunk_type = ChunkType::try_from(chunk_type)?; 74 | 75 | let size = reader.read_u32::()?; 76 | 77 | Ok(ChunkHeader { chunk_type, size }) 78 | } 79 | 80 | // Saves the ChunkHeader data into a Writer. 81 | pub fn save(&self, writer: &mut W) -> Result<()> { 82 | writer.write_u8(self.chunk_type as u8)?; 83 | writer.write_u32::(self.size)?; 84 | 85 | Ok(()) 86 | } 87 | } 88 | 89 | impl Default for ChunkHeader { 90 | fn default() -> Self { 91 | Self { 92 | chunk_type: ChunkType::End, 93 | size: 0, 94 | } 95 | } 96 | } 97 | 98 | /// The data chunk. 99 | #[derive(Debug, Clone, PartialEq)] 100 | pub struct Chunk { 101 | header: ChunkHeader, 102 | data: Vec, 103 | } 104 | 105 | impl Chunk { 106 | /// Creates a Chunk with the type and data provided. 107 | pub fn new(chunk_type: ChunkType, data: Vec) -> Self { 108 | let header = ChunkHeader::new(chunk_type, data.len()); 109 | 110 | Self { header, data } 111 | } 112 | 113 | pub fn chunk_type(&self) -> ChunkType { 114 | self.header.chunk_type 115 | } 116 | 117 | pub fn data(&self) -> &Vec { 118 | &self.data 119 | } 120 | 121 | /// Creates a Chunk from the data read from a Reader. 122 | pub fn from_reader(reader: &mut R) -> Result { 123 | let header = ChunkHeader::from_reader(reader)?; 124 | 125 | let mut data = Vec::with_capacity(header.size as usize); 126 | for _ in 0..header.size { 127 | data.push(reader.read_u8()?); 128 | } 129 | 130 | let chunk = Chunk { header, data }; 131 | chunk.validate()?; 132 | 133 | Ok(chunk) 134 | } 135 | 136 | // Saves the Chunk data into a Writer. 137 | pub fn save(&self, writer: &mut W) -> Result<()> { 138 | self.validate()?; 139 | 140 | self.header.save(writer)?; 141 | 142 | for data in self.data.iter() { 143 | writer.write_u8(*data)?; 144 | } 145 | 146 | Ok(()) 147 | } 148 | 149 | fn validate(&self) -> Result<()> { 150 | if self.header.size != self.data.len() as u32 { 151 | return Err(CartridgeError::new_mismatched_chunk_sizes( 152 | self.header.chunk_type, 153 | self.header.size as usize, 154 | self.data.len(), 155 | )); 156 | } 157 | 158 | match self.chunk_type() { 159 | ChunkType::End => self.validate_end(), 160 | ChunkType::Cover => self.validate_cover(), 161 | ChunkType::Code => self.validate_code(), 162 | ChunkType::Font => self.validate_font(), 163 | ChunkType::Palette => self.validate_palette(), 164 | ChunkType::Map => self.validate_map(), 165 | } 166 | } 167 | 168 | fn validate_end(&self) -> Result<()> { 169 | if !END_CHUNK_VALID_SIZE.contains(&self.data.len()) { 170 | return Err(CartridgeError::new_invalid_chunk_size( 171 | self.header.chunk_type, 172 | self.data.len(), 173 | END_CHUNK_VALID_SIZE.to_vec(), 174 | )); 175 | } 176 | 177 | Ok(()) 178 | } 179 | 180 | fn validate_cover(&self) -> Result<()> { 181 | if !COVER_CHUNK_VALID_SIZES.contains(&self.data.len()) { 182 | return Err(CartridgeError::new_invalid_chunk_size( 183 | self.header.chunk_type, 184 | self.data.len(), 185 | COVER_CHUNK_VALID_SIZES.to_vec(), 186 | )); 187 | } 188 | 189 | Ok(()) 190 | } 191 | 192 | fn validate_code(&self) -> Result<()> { 193 | if self.data.len() > CODE_CHUNK_MAX_SIZE { 194 | return Err(CartridgeError::new_invalid_chunk_max_size( 195 | self.header.chunk_type, 196 | self.data.len(), 197 | CODE_CHUNK_MAX_SIZE, 198 | )); 199 | } 200 | 201 | Ok(()) 202 | } 203 | 204 | fn validate_font(&self) -> Result<()> { 205 | if !FONT_CHUNK_VALID_SIZES.contains(&self.data.len()) { 206 | return Err(CartridgeError::new_invalid_chunk_size( 207 | self.header.chunk_type, 208 | self.data.len(), 209 | FONT_CHUNK_VALID_SIZES.to_vec(), 210 | )); 211 | } 212 | 213 | Ok(()) 214 | } 215 | 216 | fn validate_palette(&self) -> Result<()> { 217 | if !PALETTE_CHUNK_VALID_SIZES.contains(&self.data.len()) { 218 | return Err(CartridgeError::new_invalid_chunk_size( 219 | self.header.chunk_type, 220 | self.data.len(), 221 | PALETTE_CHUNK_VALID_SIZES.to_vec(), 222 | )); 223 | } 224 | 225 | Ok(()) 226 | } 227 | 228 | fn validate_map(&self) -> Result<()> { 229 | if self.data.len() > MAP_CHUNK_MAX_SIZE { 230 | return Err(CartridgeError::new_invalid_chunk_max_size( 231 | self.header.chunk_type, 232 | self.data.len(), 233 | MAP_CHUNK_MAX_SIZE, 234 | )); 235 | } 236 | 237 | Ok(()) 238 | } 239 | } 240 | 241 | impl Default for Chunk { 242 | fn default() -> Self { 243 | let header = ChunkHeader::default(); 244 | let data = vec![]; 245 | 246 | Self { header, data } 247 | } 248 | } 249 | 250 | #[cfg(test)] 251 | mod test { 252 | use std::io::Cursor; 253 | 254 | use assert_matches::assert_matches; 255 | 256 | use super::*; 257 | 258 | #[test] 259 | fn test_chunktype_tryfrom() { 260 | let data = [ 261 | (0, ChunkType::End), 262 | (1, ChunkType::Cover), 263 | (2, ChunkType::Code), 264 | (3, ChunkType::Font), 265 | (4, ChunkType::Palette), 266 | (5, ChunkType::Map), 267 | ]; 268 | 269 | for (value, expected) in data.iter() { 270 | let result = ChunkType::try_from(*value); 271 | 272 | assert!(result.is_ok()); 273 | assert_eq!(result.unwrap(), *expected); 274 | } 275 | } 276 | 277 | #[test] 278 | fn test_chunktype_tryfrom_fail() { 279 | let value = 255u8; 280 | let result = ChunkType::try_from(value); 281 | 282 | assert!(result.is_err()); 283 | assert_matches!( 284 | result.unwrap_err(), 285 | CartridgeError::InvalidChunkType(v) if v == value 286 | ); 287 | } 288 | 289 | #[test] 290 | fn test_chunkheader_from_reader() { 291 | let mut reader = Cursor::new(vec![5, 0, 240, 0, 0]); 292 | let expected = ChunkHeader { 293 | chunk_type: ChunkType::Map, 294 | size: 61440, 295 | }; 296 | 297 | let result = ChunkHeader::from_reader(&mut reader); 298 | assert!(result.is_ok()); 299 | assert_eq!(result.unwrap(), expected); 300 | } 301 | 302 | #[test] 303 | fn test_chunkheader_from_reader_invalid_chunk_type() { 304 | let mut reader = Cursor::new(vec![6, 0, 240, 0, 0]); 305 | 306 | let result = ChunkHeader::from_reader(&mut reader); 307 | assert!(result.is_err()); 308 | assert_matches!( 309 | result.unwrap_err(), 310 | CartridgeError::InvalidChunkType(v) if v == 6 311 | ); 312 | } 313 | 314 | #[test] 315 | fn test_chunkheader_from_reader_invalid_data() { 316 | let mut reader = Cursor::new(vec![5, 0]); 317 | 318 | let result = ChunkHeader::from_reader(&mut reader); 319 | assert!(result.is_err()); 320 | assert_matches!(result.unwrap_err(), CartridgeError::Io(_)); 321 | } 322 | 323 | #[test] 324 | fn test_chunkheader_save() { 325 | let chunk_header = ChunkHeader { 326 | chunk_type: ChunkType::Map, 327 | size: 61440, 328 | }; 329 | let expected: Vec = vec![5, 0, 240, 0, 0]; 330 | 331 | let mut writer = Cursor::new(vec![0u8; 5]); 332 | let result = chunk_header.save(&mut writer); 333 | assert!(result.is_ok()); 334 | assert_eq!(writer.get_ref(), &expected); 335 | } 336 | 337 | #[test] 338 | fn test_chunkheader_save_error() { 339 | let chunk_header = ChunkHeader { 340 | chunk_type: ChunkType::Map, 341 | size: 61440, 342 | }; 343 | 344 | let mut buff = [0u8; 1]; 345 | let mut writer = Cursor::new(&mut buff[0..]); 346 | let result = chunk_header.save(&mut writer); 347 | assert!(result.is_err()); 348 | assert_matches!(result.unwrap_err(), CartridgeError::Io(_)); 349 | } 350 | 351 | #[test] 352 | fn test_chunkheader_default() { 353 | let chunk_header = ChunkHeader::default(); 354 | assert_eq!(chunk_header.chunk_type, ChunkType::End); 355 | assert_eq!(chunk_header.size, 0); 356 | } 357 | 358 | #[test] 359 | fn test_chunk_from_reader() { 360 | let mut reader = Cursor::new(vec![ 361 | // header 362 | 4, // type 363 | 12, 0, 0, 0, // size 364 | // data 365 | 0, 0, 0, 86, 86, 86, 172, 172, 172, 255, 255, 255, 366 | ]); 367 | let expected = Chunk { 368 | header: ChunkHeader { 369 | chunk_type: ChunkType::Palette, 370 | size: 12, 371 | }, 372 | data: vec![0, 0, 0, 86, 86, 86, 172, 172, 172, 255, 255, 255], 373 | }; 374 | 375 | let result = Chunk::from_reader(&mut reader); 376 | println!("{:?}", result); 377 | assert!(result.is_ok()); 378 | assert_eq!(result.unwrap(), expected); 379 | } 380 | 381 | #[test] 382 | fn test_header_from_reader_invalid_chunk_type() { 383 | let mut reader = Cursor::new(vec![ 384 | // header 385 | 6, // type 386 | 12, 0, 0, 0, // size 387 | // data 388 | 0, 0, 0, 86, 86, 86, 172, 172, 172, 255, 255, 255, 389 | ]); 390 | 391 | let result = Chunk::from_reader(&mut reader); 392 | assert!(result.is_err()); 393 | assert_matches!( 394 | result.unwrap_err(), 395 | CartridgeError::InvalidChunkType(v) if v == 6 396 | ); 397 | } 398 | 399 | #[test] 400 | fn test_header_from_reader_invalid_data() { 401 | let mut reader = Cursor::new(vec![ 402 | // header 403 | 4, // type 404 | 12, 0, 0, 0, // size 405 | // data 406 | 0, 407 | ]); 408 | 409 | let result = Chunk::from_reader(&mut reader); 410 | assert!(result.is_err()); 411 | assert_matches!(result.unwrap_err(), CartridgeError::Io(_)); 412 | } 413 | 414 | #[test] 415 | fn test_chunk_save() { 416 | let chunk = Chunk { 417 | header: ChunkHeader { 418 | chunk_type: ChunkType::Palette, 419 | size: 12, 420 | }, 421 | data: vec![0, 0, 0, 86, 86, 86, 172, 172, 172, 255, 255, 255], 422 | }; 423 | let expected: Vec = vec![ 424 | 4, 12, 0, 0, 0, 0, 0, 0, 86, 86, 86, 172, 172, 172, 255, 255, 255, 425 | ]; 426 | 427 | let mut writer = Cursor::new(vec![0u8; 17]); 428 | let result = chunk.save(&mut writer); 429 | assert!(result.is_ok()); 430 | assert_eq!(writer.get_ref(), &expected); 431 | } 432 | 433 | #[test] 434 | fn test_chunk_save_error() { 435 | let chunk = Chunk { 436 | header: ChunkHeader { 437 | chunk_type: ChunkType::Palette, 438 | size: 12, 439 | }, 440 | data: vec![0, 0, 0, 86, 86, 86, 172, 172, 172, 255, 255, 255], 441 | }; 442 | 443 | let mut buff = [0u8; 6]; 444 | let mut writer = Cursor::new(&mut buff[0..]); 445 | let result = chunk.save(&mut writer); 446 | assert!(result.is_err()); 447 | assert_matches!(result.unwrap_err(), CartridgeError::Io(_)); 448 | } 449 | 450 | #[test] 451 | fn test_chunk_default() { 452 | let chunk = Chunk::default(); 453 | assert_eq!(chunk.header.chunk_type, ChunkType::End); 454 | assert_eq!(chunk.header.size, 0); 455 | assert_eq!(chunk.data.len(), 0); 456 | } 457 | } 458 | -------------------------------------------------------------------------------- /tinlib/src/cartridge/mod.rs: -------------------------------------------------------------------------------- 1 | //! Cartridge utilities. 2 | mod chunk; 3 | mod error; 4 | 5 | pub use crate::cartridge::error::{CartridgeError, Result}; 6 | 7 | use std::io::{Read, Write}; 8 | 9 | use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; 10 | 11 | use crate::cartridge::chunk::{Chunk, ChunkType}; 12 | 13 | /// The default cartridge file version. 14 | const DEFAULT_CART_FILE_VERSION: u8 = 1; 15 | /// The default name size. 16 | const DEFAULT_NAME_SIZE: u8 = 64; 17 | /// The default description size. 18 | const DEFAULT_DESC_SIZE: u16 = 512; 19 | /// The default author name size. 20 | const DEFAULT_AUTHOR_SIZE: u8 = 64; 21 | /// The default game version. 22 | const DEFAULT_VERSION: u8 = 1; 23 | 24 | /// The cartridge header. 25 | #[derive(Debug, Clone, PartialEq)] 26 | struct CartridgeHeader { 27 | pub cart_version: u8, 28 | pub name_size: u8, 29 | pub desc_size: u16, 30 | pub author_size: u8, 31 | } 32 | 33 | impl CartridgeHeader { 34 | /// Creates a CartridgeHeader from the data read from a Reader. 35 | pub fn from_reader(reader: &mut R) -> Result { 36 | let cart_version = reader.read_u8()?; // TODO validate the version 37 | let name_size = reader.read_u8()?; 38 | let desc_size = reader.read_u16::()?; 39 | let author_size = reader.read_u8()?; 40 | 41 | Ok(CartridgeHeader { 42 | cart_version, 43 | name_size, 44 | desc_size, 45 | author_size, 46 | }) 47 | } 48 | 49 | /// Saves the CartridgeHeader data into a Writer. 50 | pub fn save(&self, writer: &mut W) -> Result<()> { 51 | writer.write_u8(self.cart_version)?; 52 | writer.write_u8(self.name_size)?; 53 | writer.write_u16::(self.desc_size)?; 54 | writer.write_u8(self.author_size)?; 55 | 56 | Ok(()) 57 | } 58 | } 59 | 60 | impl Default for CartridgeHeader { 61 | fn default() -> Self { 62 | Self { 63 | cart_version: DEFAULT_CART_FILE_VERSION, 64 | name_size: DEFAULT_NAME_SIZE, 65 | desc_size: DEFAULT_DESC_SIZE, 66 | author_size: DEFAULT_AUTHOR_SIZE, 67 | } 68 | } 69 | } 70 | 71 | /// The cartridge data. 72 | #[derive(Debug, Clone, PartialEq)] 73 | pub struct Cartridge { 74 | pub version: u8, 75 | pub name: String, 76 | pub desc: String, 77 | pub author: String, 78 | pub cover: Vec, 79 | pub font: Vec, 80 | pub palette: Vec, 81 | pub map: Vec, 82 | pub code: String, 83 | } 84 | 85 | impl Cartridge { 86 | pub fn from_reader(reader: &mut R) -> Result { 87 | let mut cart = Cartridge::default(); 88 | let header = CartridgeHeader::from_reader(reader)?; 89 | 90 | cart.version = reader.read_u8()?; 91 | 92 | let mut name = vec![0u8; header.name_size as usize]; 93 | reader.read_exact(&mut name)?; 94 | cart.name = String::from_utf8(name)?; 95 | 96 | let mut desc = vec![0u8; header.desc_size as usize]; 97 | reader.read_exact(&mut desc)?; 98 | cart.desc = String::from_utf8(desc)?; 99 | 100 | let mut author = vec![0u8; header.author_size as usize]; 101 | reader.read_exact(&mut author)?; 102 | cart.author = String::from_utf8(author)?; 103 | 104 | loop { 105 | let chunk = Chunk::from_reader(reader)?; 106 | 107 | match chunk.chunk_type() { 108 | ChunkType::End => { 109 | break; 110 | } 111 | ChunkType::Cover => { 112 | cart.cover.clone_from(chunk.data()); 113 | } 114 | ChunkType::Code => { 115 | cart.code = String::from_utf8(chunk.data().clone())?; 116 | } 117 | ChunkType::Font => { 118 | cart.font.clone_from(chunk.data()); 119 | } 120 | ChunkType::Palette => { 121 | cart.palette.clone_from(chunk.data()); 122 | } 123 | ChunkType::Map => { 124 | cart.map.clone_from(chunk.data()); 125 | } 126 | } 127 | } 128 | 129 | Ok(cart) 130 | } 131 | 132 | pub fn save(&self, writer: &mut W) -> Result<()> { 133 | let header = CartridgeHeader { 134 | name_size: self.name.len() as u8, 135 | desc_size: self.desc.len() as u16, 136 | author_size: self.author.len() as u8, 137 | ..Default::default() 138 | }; 139 | header.save(writer)?; 140 | 141 | writer.write_u8(self.version)?; 142 | writer.write_all(self.name.as_bytes())?; 143 | writer.write_all(self.desc.as_bytes())?; 144 | writer.write_all(self.author.as_bytes())?; 145 | 146 | let chunks = vec![ 147 | (self.cover.clone(), ChunkType::Cover), 148 | (self.code.as_bytes().to_vec(), ChunkType::Code), 149 | (self.font.clone(), ChunkType::Font), 150 | (self.palette.clone(), ChunkType::Palette), 151 | (self.map.clone(), ChunkType::Map), 152 | ]; 153 | 154 | for (data, chunk_type) in chunks.into_iter().filter(|(d, _)| !d.is_empty()) { 155 | let chunk = Chunk::new(chunk_type, data); 156 | chunk.save(writer)?; 157 | } 158 | 159 | let chunk = Chunk::default(); 160 | chunk.save(writer)?; 161 | 162 | Ok(()) 163 | } 164 | } 165 | 166 | impl Default for Cartridge { 167 | fn default() -> Self { 168 | Self { 169 | version: DEFAULT_VERSION, 170 | name: "".to_string(), 171 | desc: "".to_string(), 172 | author: "".to_string(), 173 | cover: vec![], 174 | font: vec![], 175 | palette: vec![], 176 | map: vec![], 177 | code: "".to_string(), 178 | } 179 | } 180 | } 181 | 182 | #[cfg(test)] 183 | mod test_super { 184 | use std::io::Cursor; 185 | 186 | use assert_matches::assert_matches; 187 | 188 | use super::*; 189 | 190 | #[test] 191 | fn test_cartridgeheader_from_reader() { 192 | let mut reader = Cursor::new(vec![5, 32, 0, 1, 32]); 193 | let expected = CartridgeHeader { 194 | cart_version: 5, 195 | name_size: 32, 196 | desc_size: 256, 197 | author_size: 32, 198 | }; 199 | 200 | let result = CartridgeHeader::from_reader(&mut reader); 201 | assert!(result.is_ok()); 202 | assert_eq!(result.unwrap(), expected); 203 | } 204 | 205 | #[test] 206 | fn test_cartridgeheader_from_reader_invalid_data() { 207 | let mut reader = Cursor::new(vec![5, 32, 0, 1]); 208 | 209 | let result = CartridgeHeader::from_reader(&mut reader); 210 | assert!(result.is_err()); 211 | assert_matches!(result.unwrap_err(), CartridgeError::Io(_)); 212 | } 213 | 214 | #[test] 215 | fn test_cartridgeheader_save() { 216 | let header = CartridgeHeader { 217 | cart_version: 1, 218 | name_size: 64, 219 | desc_size: 512, 220 | author_size: 64, 221 | }; 222 | let expected: Vec = vec![1, 64, 0, 2, 64]; 223 | 224 | let mut writer = Cursor::new(vec![0u8; 5]); 225 | let result = header.save(&mut writer); 226 | assert!(result.is_ok()); 227 | assert_eq!(writer.get_ref(), &expected); 228 | } 229 | 230 | #[test] 231 | fn test_cartridgeheader_save_error() { 232 | let header = CartridgeHeader { 233 | cart_version: 1, 234 | name_size: 64, 235 | desc_size: 512, 236 | author_size: 64, 237 | }; 238 | 239 | let mut buff = [0u8; 1]; 240 | let mut writer = Cursor::new(&mut buff[0..]); 241 | let result = header.save(&mut writer); 242 | assert!(result.is_err()); 243 | assert_matches!(result.unwrap_err(), CartridgeError::Io(_)); 244 | } 245 | 246 | #[test] 247 | fn test_cartridgeheader_default() { 248 | let header = CartridgeHeader::default(); 249 | assert_eq!(header.cart_version, DEFAULT_CART_FILE_VERSION); 250 | assert_eq!(header.name_size, DEFAULT_NAME_SIZE); 251 | assert_eq!(header.desc_size, DEFAULT_DESC_SIZE); 252 | assert_eq!(header.author_size, DEFAULT_AUTHOR_SIZE); 253 | } 254 | 255 | #[test] 256 | fn test_cartridge_from_reader() { 257 | let mut data = vec![ 258 | // cart header 259 | 1, // cart version 260 | 10, // name size 261 | 11, 0, // desc size 262 | 2, // author size 263 | ]; 264 | 265 | // cart data 266 | data.extend_from_slice(&[ 267 | 11, // cart version 268 | 116, 104, 105, 115, 105, 115, 110, 97, 109, 101, // name 269 | 100, 101, 115, 99, 114, 105, 195, 167, 195, 163, 111, // desc 270 | 109, 101, // author 271 | ]); 272 | 273 | // code chunk 274 | data.extend_from_slice(&[ 275 | 2, 6, 0, 0, 0, // header 276 | 109, 97, 105, 110, 40, 41, // data 277 | ]); 278 | 279 | // map chunk 280 | data.extend_from_slice(&[ 281 | 5, 0, 0, 0, 0, // header 282 | ]); 283 | 284 | // font chunk 285 | data.extend_from_slice(&[ 286 | 3, 0, 64, 0, 0, // header 287 | ]); 288 | data.extend_from_slice(&[0; 16384]); 289 | 290 | // cover chunk 291 | data.extend_from_slice(&[ 292 | 1, 0, 0, 0, 0, // header 293 | ]); 294 | 295 | // palette chunk 296 | data.extend_from_slice(&[ 297 | 4, 12, 0, 0, 0, // header 298 | 0, 0, 0, 255, 255, 255, 180, 180, 180, 90, 90, 90, // data 299 | ]); 300 | 301 | // end chunk 302 | data.extend_from_slice(&[ 303 | 0, 0, 0, 0, 0, // ignored 304 | 1, 0, 0, 0, 0, // junk data 305 | ]); 306 | 307 | let mut reader = Cursor::new(data); 308 | let expected = Cartridge { 309 | version: 11, 310 | name: "thisisname".to_string(), 311 | desc: "descrição".to_string(), 312 | author: "me".to_string(), 313 | cover: vec![], 314 | font: vec![0; 16384], 315 | palette: vec![0, 0, 0, 255, 255, 255, 180, 180, 180, 90, 90, 90], 316 | map: vec![], 317 | code: "main()".to_string(), 318 | }; 319 | 320 | let result = Cartridge::from_reader(&mut reader); 321 | assert!(result.is_ok()); 322 | assert_eq!(result.unwrap(), expected); 323 | } 324 | 325 | #[test] 326 | fn test_cartridge_from_reader_empty_data_and_chunks() { 327 | let mut reader = Cursor::new(vec![ 328 | // header 329 | 1, // cart version 330 | 0, // name size 331 | 0, 0, // desc size 332 | 0, // author size 333 | // cart 334 | 1, // version 335 | // end 336 | 0, 0, 0, 0, 0, 337 | ]); 338 | let expected = Cartridge { 339 | version: 1, 340 | name: "".to_string(), 341 | desc: "".to_string(), 342 | author: "".to_string(), 343 | cover: vec![], 344 | font: vec![], 345 | palette: vec![], 346 | map: vec![], 347 | code: "".to_string(), 348 | }; 349 | 350 | let result = Cartridge::from_reader(&mut reader); 351 | assert!(result.is_ok()); 352 | assert_eq!(result.unwrap(), expected); 353 | } 354 | 355 | #[test] 356 | fn test_cartridge_from_reader_missing_data() { 357 | let mut reader = Cursor::new(vec![ 358 | // header 359 | 1, // cart version 360 | 0, // name size 361 | 0, 0, // desc size 362 | 0, // author size 363 | // cart 364 | 1, // version 365 | ]); 366 | 367 | let result = Cartridge::from_reader(&mut reader); 368 | assert!(result.is_err()); 369 | assert_matches!(result.unwrap_err(), CartridgeError::Io(_)); 370 | } 371 | 372 | #[test] 373 | fn test_cartridge_from_reader_missing_end_chunk() { 374 | let mut reader = Cursor::new(vec![ 375 | // header 376 | 1, // cart version 377 | 0, // name size 378 | 0, 0, // desc size 379 | 0, // author size 380 | ]); 381 | 382 | let result = Cartridge::from_reader(&mut reader); 383 | assert!(result.is_err()); 384 | assert_matches!(result.unwrap_err(), CartridgeError::Io(_)); 385 | } 386 | 387 | #[test] 388 | fn test_cartridge_save() { 389 | let cart = Cartridge { 390 | version: 11, 391 | name: "thisisname".to_string(), 392 | desc: "descrição".to_string(), 393 | author: "me".to_string(), 394 | cover: vec![], 395 | font: vec![0; 16384], 396 | palette: vec![0, 0, 0, 255, 255, 255, 180, 180, 180, 90, 90, 90], 397 | map: vec![], 398 | code: "main()".to_string(), 399 | }; 400 | 401 | let mut expected = vec![ 402 | // cart header 403 | 1, // cart version 404 | 10, // name size 405 | 11, 0, // desc size 406 | 2, // author size 407 | ]; 408 | 409 | // cart data 410 | expected.extend_from_slice(&[ 411 | 11, // cart version 412 | 116, 104, 105, 115, 105, 115, 110, 97, 109, 101, // name 413 | 100, 101, 115, 99, 114, 105, 195, 167, 195, 163, 111, // desc 414 | 109, 101, // author 415 | ]); 416 | 417 | // code chunk 418 | expected.extend_from_slice(&[ 419 | 2, 6, 0, 0, 0, // header 420 | 109, 97, 105, 110, 40, 41, // data 421 | ]); 422 | 423 | // font chunk 424 | expected.extend_from_slice(&[ 425 | 3, 0, 64, 0, 0, // header 426 | ]); 427 | expected.extend_from_slice(&[0; 16384]); 428 | 429 | // palette chunk 430 | expected.extend_from_slice(&[ 431 | 4, 12, 0, 0, 0, // header 432 | 0, 0, 0, 255, 255, 255, 180, 180, 180, 90, 90, 90, // data 433 | ]); 434 | 435 | // end chunk 436 | expected.extend_from_slice(&[ 437 | 0, 0, 0, 0, 0, // ignored 438 | ]); 439 | 440 | let mut writer = Cursor::new(vec![0u8; expected.len()]); 441 | let result = cart.save(&mut writer); 442 | assert!(result.is_ok()); 443 | assert_eq!(writer.get_ref(), &expected); 444 | } 445 | 446 | #[test] 447 | fn test_cartridge_save_empty() { 448 | let cart = Cartridge::default(); 449 | let expected: Vec = vec![1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]; 450 | 451 | let mut writer = Cursor::new(vec![0u8; 5]); 452 | let result = cart.save(&mut writer); 453 | assert!(result.is_ok()); 454 | assert_eq!(writer.get_ref(), &expected); 455 | } 456 | 457 | #[test] 458 | fn test_cartridge_save_error() { 459 | let cart = Cartridge::default(); 460 | 461 | let mut buff = [0u8; 1]; 462 | let mut writer = Cursor::new(&mut buff[0..]); 463 | let result = cart.save(&mut writer); 464 | assert!(result.is_err()); 465 | assert_matches!(result.unwrap_err(), CartridgeError::Io(_)); 466 | } 467 | 468 | #[test] 469 | fn test_cartridge_default() { 470 | let cart = Cartridge::default(); 471 | assert_eq!(cart.version, DEFAULT_VERSION); 472 | assert_eq!(cart.name, "".to_string()); 473 | assert_eq!(cart.desc, "".to_string()); 474 | assert_eq!(cart.author, "".to_string()); 475 | assert_eq!(cart.cover, vec![]); 476 | assert_eq!(cart.font, vec![]); 477 | assert_eq!(cart.palette, vec![]); 478 | assert_eq!(cart.map, vec![]); 479 | assert_eq!(cart.code, "".to_string()); 480 | } 481 | } 482 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "ab_glyph" 7 | version = "0.2.29" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "ec3672c180e71eeaaac3a541fbbc5f5ad4def8b747c595ad30d674e43049f7b0" 10 | dependencies = [ 11 | "ab_glyph_rasterizer", 12 | "owned_ttf_parser", 13 | ] 14 | 15 | [[package]] 16 | name = "ab_glyph_rasterizer" 17 | version = "0.1.8" 18 | source = "registry+https://github.com/rust-lang/crates.io-index" 19 | checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" 20 | 21 | [[package]] 22 | name = "ahash" 23 | version = "0.8.11" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" 26 | dependencies = [ 27 | "cfg-if", 28 | "getrandom", 29 | "once_cell", 30 | "version_check", 31 | "zerocopy", 32 | ] 33 | 34 | [[package]] 35 | name = "aho-corasick" 36 | version = "1.1.3" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 39 | dependencies = [ 40 | "memchr", 41 | ] 42 | 43 | [[package]] 44 | name = "allocator-api2" 45 | version = "0.2.21" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" 48 | 49 | [[package]] 50 | name = "android-activity" 51 | version = "0.6.0" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" 54 | dependencies = [ 55 | "android-properties", 56 | "bitflags 2.9.0", 57 | "cc", 58 | "cesu8", 59 | "jni", 60 | "jni-sys", 61 | "libc", 62 | "log", 63 | "ndk", 64 | "ndk-context", 65 | "ndk-sys 0.6.0+11769913", 66 | "num_enum", 67 | "thiserror 1.0.69", 68 | ] 69 | 70 | [[package]] 71 | name = "android-properties" 72 | version = "0.2.2" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" 75 | 76 | [[package]] 77 | name = "android_system_properties" 78 | version = "0.1.5" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 81 | dependencies = [ 82 | "libc", 83 | ] 84 | 85 | [[package]] 86 | name = "anstream" 87 | version = "0.6.18" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" 90 | dependencies = [ 91 | "anstyle", 92 | "anstyle-parse", 93 | "anstyle-query", 94 | "anstyle-wincon", 95 | "colorchoice", 96 | "is_terminal_polyfill", 97 | "utf8parse", 98 | ] 99 | 100 | [[package]] 101 | name = "anstyle" 102 | version = "1.0.10" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" 105 | 106 | [[package]] 107 | name = "anstyle-parse" 108 | version = "0.2.6" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" 111 | dependencies = [ 112 | "utf8parse", 113 | ] 114 | 115 | [[package]] 116 | name = "anstyle-query" 117 | version = "1.1.2" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" 120 | dependencies = [ 121 | "windows-sys 0.59.0", 122 | ] 123 | 124 | [[package]] 125 | name = "anstyle-wincon" 126 | version = "3.0.7" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" 129 | dependencies = [ 130 | "anstyle", 131 | "once_cell", 132 | "windows-sys 0.59.0", 133 | ] 134 | 135 | [[package]] 136 | name = "anyhow" 137 | version = "1.0.97" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" 140 | 141 | [[package]] 142 | name = "arrayref" 143 | version = "0.3.9" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" 146 | 147 | [[package]] 148 | name = "arrayvec" 149 | version = "0.7.6" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" 152 | 153 | [[package]] 154 | name = "as-raw-xcb-connection" 155 | version = "1.0.1" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" 158 | 159 | [[package]] 160 | name = "ash" 161 | version = "0.37.3+1.3.251" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a" 164 | dependencies = [ 165 | "libloading 0.7.4", 166 | ] 167 | 168 | [[package]] 169 | name = "assert_matches" 170 | version = "1.5.0" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" 173 | 174 | [[package]] 175 | name = "atomic-waker" 176 | version = "1.1.2" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 179 | 180 | [[package]] 181 | name = "autocfg" 182 | version = "1.4.0" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 185 | 186 | [[package]] 187 | name = "bit-set" 188 | version = "0.5.3" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" 191 | dependencies = [ 192 | "bit-vec", 193 | ] 194 | 195 | [[package]] 196 | name = "bit-vec" 197 | version = "0.6.3" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" 200 | 201 | [[package]] 202 | name = "bitflags" 203 | version = "1.3.2" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 206 | 207 | [[package]] 208 | name = "bitflags" 209 | version = "2.9.0" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" 212 | 213 | [[package]] 214 | name = "block" 215 | version = "0.1.6" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" 218 | 219 | [[package]] 220 | name = "block2" 221 | version = "0.5.1" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" 224 | dependencies = [ 225 | "objc2", 226 | ] 227 | 228 | [[package]] 229 | name = "bumpalo" 230 | version = "3.17.0" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" 233 | 234 | [[package]] 235 | name = "bytemuck" 236 | version = "1.22.0" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" 239 | 240 | [[package]] 241 | name = "byteorder" 242 | version = "1.5.0" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 245 | 246 | [[package]] 247 | name = "bytes" 248 | version = "1.10.1" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" 251 | 252 | [[package]] 253 | name = "calloop" 254 | version = "0.13.0" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" 257 | dependencies = [ 258 | "bitflags 2.9.0", 259 | "log", 260 | "polling", 261 | "rustix", 262 | "slab", 263 | "thiserror 1.0.69", 264 | ] 265 | 266 | [[package]] 267 | name = "calloop-wayland-source" 268 | version = "0.3.0" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" 271 | dependencies = [ 272 | "calloop", 273 | "rustix", 274 | "wayland-backend", 275 | "wayland-client", 276 | ] 277 | 278 | [[package]] 279 | name = "cc" 280 | version = "1.2.17" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" 283 | dependencies = [ 284 | "jobserver", 285 | "libc", 286 | "shlex", 287 | ] 288 | 289 | [[package]] 290 | name = "cesu8" 291 | version = "1.1.0" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" 294 | 295 | [[package]] 296 | name = "cfg-if" 297 | version = "1.0.0" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 300 | 301 | [[package]] 302 | name = "cfg_aliases" 303 | version = "0.1.1" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" 306 | 307 | [[package]] 308 | name = "cfg_aliases" 309 | version = "0.2.1" 310 | source = "registry+https://github.com/rust-lang/crates.io-index" 311 | checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 312 | 313 | [[package]] 314 | name = "codespan-reporting" 315 | version = "0.11.1" 316 | source = "registry+https://github.com/rust-lang/crates.io-index" 317 | checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" 318 | dependencies = [ 319 | "termcolor", 320 | "unicode-width", 321 | ] 322 | 323 | [[package]] 324 | name = "colorchoice" 325 | version = "1.0.3" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" 328 | 329 | [[package]] 330 | name = "com" 331 | version = "0.6.0" 332 | source = "registry+https://github.com/rust-lang/crates.io-index" 333 | checksum = "7e17887fd17353b65b1b2ef1c526c83e26cd72e74f598a8dc1bee13a48f3d9f6" 334 | dependencies = [ 335 | "com_macros", 336 | ] 337 | 338 | [[package]] 339 | name = "com_macros" 340 | version = "0.6.0" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "d375883580a668c7481ea6631fc1a8863e33cc335bf56bfad8d7e6d4b04b13a5" 343 | dependencies = [ 344 | "com_macros_support", 345 | "proc-macro2", 346 | "syn 1.0.109", 347 | ] 348 | 349 | [[package]] 350 | name = "com_macros_support" 351 | version = "0.6.0" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "ad899a1087a9296d5644792d7cb72b8e34c1bec8e7d4fbc002230169a6e8710c" 354 | dependencies = [ 355 | "proc-macro2", 356 | "quote", 357 | "syn 1.0.109", 358 | ] 359 | 360 | [[package]] 361 | name = "combine" 362 | version = "4.6.7" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" 365 | dependencies = [ 366 | "bytes", 367 | "memchr", 368 | ] 369 | 370 | [[package]] 371 | name = "concurrent-queue" 372 | version = "2.5.0" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" 375 | dependencies = [ 376 | "crossbeam-utils", 377 | ] 378 | 379 | [[package]] 380 | name = "core-foundation" 381 | version = "0.9.4" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 384 | dependencies = [ 385 | "core-foundation-sys", 386 | "libc", 387 | ] 388 | 389 | [[package]] 390 | name = "core-foundation-sys" 391 | version = "0.8.7" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 394 | 395 | [[package]] 396 | name = "core-graphics" 397 | version = "0.23.2" 398 | source = "registry+https://github.com/rust-lang/crates.io-index" 399 | checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" 400 | dependencies = [ 401 | "bitflags 1.3.2", 402 | "core-foundation", 403 | "core-graphics-types", 404 | "foreign-types", 405 | "libc", 406 | ] 407 | 408 | [[package]] 409 | name = "core-graphics-types" 410 | version = "0.1.3" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" 413 | dependencies = [ 414 | "bitflags 1.3.2", 415 | "core-foundation", 416 | "libc", 417 | ] 418 | 419 | [[package]] 420 | name = "crossbeam-utils" 421 | version = "0.8.21" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" 424 | 425 | [[package]] 426 | name = "cursor-icon" 427 | version = "1.1.0" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" 430 | 431 | [[package]] 432 | name = "d3d12" 433 | version = "0.19.0" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | checksum = "3e3d747f100290a1ca24b752186f61f6637e1deffe3bf6320de6fcb29510a307" 436 | dependencies = [ 437 | "bitflags 2.9.0", 438 | "libloading 0.8.6", 439 | "winapi", 440 | ] 441 | 442 | [[package]] 443 | name = "dispatch" 444 | version = "0.2.0" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" 447 | 448 | [[package]] 449 | name = "dlib" 450 | version = "0.5.2" 451 | source = "registry+https://github.com/rust-lang/crates.io-index" 452 | checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" 453 | dependencies = [ 454 | "libloading 0.8.6", 455 | ] 456 | 457 | [[package]] 458 | name = "downcast-rs" 459 | version = "1.2.1" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" 462 | 463 | [[package]] 464 | name = "dpi" 465 | version = "0.1.1" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" 468 | 469 | [[package]] 470 | name = "env_filter" 471 | version = "0.1.3" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" 474 | dependencies = [ 475 | "log", 476 | "regex", 477 | ] 478 | 479 | [[package]] 480 | name = "env_logger" 481 | version = "0.11.7" 482 | source = "registry+https://github.com/rust-lang/crates.io-index" 483 | checksum = "c3716d7a920fb4fac5d84e9d4bce8ceb321e9414b4409da61b07b75c1e3d0697" 484 | dependencies = [ 485 | "anstream", 486 | "anstyle", 487 | "env_filter", 488 | "jiff", 489 | "log", 490 | ] 491 | 492 | [[package]] 493 | name = "equivalent" 494 | version = "1.0.2" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 497 | 498 | [[package]] 499 | name = "errno" 500 | version = "0.3.10" 501 | source = "registry+https://github.com/rust-lang/crates.io-index" 502 | checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" 503 | dependencies = [ 504 | "libc", 505 | "windows-sys 0.59.0", 506 | ] 507 | 508 | [[package]] 509 | name = "error-iter" 510 | version = "0.4.1" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "8070547d90d1b98debb6626421d742c897942bbb78f047694a5eb769495eccd6" 513 | 514 | [[package]] 515 | name = "foreign-types" 516 | version = "0.5.0" 517 | source = "registry+https://github.com/rust-lang/crates.io-index" 518 | checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" 519 | dependencies = [ 520 | "foreign-types-macros", 521 | "foreign-types-shared", 522 | ] 523 | 524 | [[package]] 525 | name = "foreign-types-macros" 526 | version = "0.2.3" 527 | source = "registry+https://github.com/rust-lang/crates.io-index" 528 | checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" 529 | dependencies = [ 530 | "proc-macro2", 531 | "quote", 532 | "syn 2.0.100", 533 | ] 534 | 535 | [[package]] 536 | name = "foreign-types-shared" 537 | version = "0.3.1" 538 | source = "registry+https://github.com/rust-lang/crates.io-index" 539 | checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" 540 | 541 | [[package]] 542 | name = "gethostname" 543 | version = "0.4.3" 544 | source = "registry+https://github.com/rust-lang/crates.io-index" 545 | checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" 546 | dependencies = [ 547 | "libc", 548 | "windows-targets 0.48.5", 549 | ] 550 | 551 | [[package]] 552 | name = "getrandom" 553 | version = "0.2.15" 554 | source = "registry+https://github.com/rust-lang/crates.io-index" 555 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 556 | dependencies = [ 557 | "cfg-if", 558 | "libc", 559 | "wasi", 560 | ] 561 | 562 | [[package]] 563 | name = "gl_generator" 564 | version = "0.14.0" 565 | source = "registry+https://github.com/rust-lang/crates.io-index" 566 | checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" 567 | dependencies = [ 568 | "khronos_api", 569 | "log", 570 | "xml-rs", 571 | ] 572 | 573 | [[package]] 574 | name = "glow" 575 | version = "0.13.1" 576 | source = "registry+https://github.com/rust-lang/crates.io-index" 577 | checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1" 578 | dependencies = [ 579 | "js-sys", 580 | "slotmap", 581 | "wasm-bindgen", 582 | "web-sys", 583 | ] 584 | 585 | [[package]] 586 | name = "glutin_wgl_sys" 587 | version = "0.5.0" 588 | source = "registry+https://github.com/rust-lang/crates.io-index" 589 | checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead" 590 | dependencies = [ 591 | "gl_generator", 592 | ] 593 | 594 | [[package]] 595 | name = "gpu-alloc" 596 | version = "0.6.0" 597 | source = "registry+https://github.com/rust-lang/crates.io-index" 598 | checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" 599 | dependencies = [ 600 | "bitflags 2.9.0", 601 | "gpu-alloc-types", 602 | ] 603 | 604 | [[package]] 605 | name = "gpu-alloc-types" 606 | version = "0.3.0" 607 | source = "registry+https://github.com/rust-lang/crates.io-index" 608 | checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" 609 | dependencies = [ 610 | "bitflags 2.9.0", 611 | ] 612 | 613 | [[package]] 614 | name = "gpu-allocator" 615 | version = "0.25.0" 616 | source = "registry+https://github.com/rust-lang/crates.io-index" 617 | checksum = "6f56f6318968d03c18e1bcf4857ff88c61157e9da8e47c5f29055d60e1228884" 618 | dependencies = [ 619 | "log", 620 | "presser", 621 | "thiserror 1.0.69", 622 | "winapi", 623 | "windows", 624 | ] 625 | 626 | [[package]] 627 | name = "gpu-descriptor" 628 | version = "0.2.4" 629 | source = "registry+https://github.com/rust-lang/crates.io-index" 630 | checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c" 631 | dependencies = [ 632 | "bitflags 2.9.0", 633 | "gpu-descriptor-types", 634 | "hashbrown 0.14.5", 635 | ] 636 | 637 | [[package]] 638 | name = "gpu-descriptor-types" 639 | version = "0.1.2" 640 | source = "registry+https://github.com/rust-lang/crates.io-index" 641 | checksum = "6bf0b36e6f090b7e1d8a4b49c0cb81c1f8376f72198c65dd3ad9ff3556b8b78c" 642 | dependencies = [ 643 | "bitflags 2.9.0", 644 | ] 645 | 646 | [[package]] 647 | name = "hashbrown" 648 | version = "0.14.5" 649 | source = "registry+https://github.com/rust-lang/crates.io-index" 650 | checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 651 | dependencies = [ 652 | "ahash", 653 | "allocator-api2", 654 | ] 655 | 656 | [[package]] 657 | name = "hashbrown" 658 | version = "0.15.2" 659 | source = "registry+https://github.com/rust-lang/crates.io-index" 660 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" 661 | 662 | [[package]] 663 | name = "hassle-rs" 664 | version = "0.11.0" 665 | source = "registry+https://github.com/rust-lang/crates.io-index" 666 | checksum = "af2a7e73e1f34c48da31fb668a907f250794837e08faa144fd24f0b8b741e890" 667 | dependencies = [ 668 | "bitflags 2.9.0", 669 | "com", 670 | "libc", 671 | "libloading 0.8.6", 672 | "thiserror 1.0.69", 673 | "widestring", 674 | "winapi", 675 | ] 676 | 677 | [[package]] 678 | name = "hermit-abi" 679 | version = "0.4.0" 680 | source = "registry+https://github.com/rust-lang/crates.io-index" 681 | checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" 682 | 683 | [[package]] 684 | name = "hexf-parse" 685 | version = "0.2.1" 686 | source = "registry+https://github.com/rust-lang/crates.io-index" 687 | checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" 688 | 689 | [[package]] 690 | name = "indexmap" 691 | version = "2.8.0" 692 | source = "registry+https://github.com/rust-lang/crates.io-index" 693 | checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" 694 | dependencies = [ 695 | "equivalent", 696 | "hashbrown 0.15.2", 697 | ] 698 | 699 | [[package]] 700 | name = "is_terminal_polyfill" 701 | version = "1.70.1" 702 | source = "registry+https://github.com/rust-lang/crates.io-index" 703 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 704 | 705 | [[package]] 706 | name = "jiff" 707 | version = "0.2.5" 708 | source = "registry+https://github.com/rust-lang/crates.io-index" 709 | checksum = "c102670231191d07d37a35af3eb77f1f0dbf7a71be51a962dcd57ea607be7260" 710 | dependencies = [ 711 | "jiff-static", 712 | "log", 713 | "portable-atomic", 714 | "portable-atomic-util", 715 | "serde", 716 | ] 717 | 718 | [[package]] 719 | name = "jiff-static" 720 | version = "0.2.5" 721 | source = "registry+https://github.com/rust-lang/crates.io-index" 722 | checksum = "4cdde31a9d349f1b1f51a0b3714a5940ac022976f4b49485fc04be052b183b4c" 723 | dependencies = [ 724 | "proc-macro2", 725 | "quote", 726 | "syn 2.0.100", 727 | ] 728 | 729 | [[package]] 730 | name = "jni" 731 | version = "0.21.1" 732 | source = "registry+https://github.com/rust-lang/crates.io-index" 733 | checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" 734 | dependencies = [ 735 | "cesu8", 736 | "cfg-if", 737 | "combine", 738 | "jni-sys", 739 | "log", 740 | "thiserror 1.0.69", 741 | "walkdir", 742 | "windows-sys 0.45.0", 743 | ] 744 | 745 | [[package]] 746 | name = "jni-sys" 747 | version = "0.3.0" 748 | source = "registry+https://github.com/rust-lang/crates.io-index" 749 | checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" 750 | 751 | [[package]] 752 | name = "jobserver" 753 | version = "0.1.32" 754 | source = "registry+https://github.com/rust-lang/crates.io-index" 755 | checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" 756 | dependencies = [ 757 | "libc", 758 | ] 759 | 760 | [[package]] 761 | name = "js-sys" 762 | version = "0.3.77" 763 | source = "registry+https://github.com/rust-lang/crates.io-index" 764 | checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 765 | dependencies = [ 766 | "once_cell", 767 | "wasm-bindgen", 768 | ] 769 | 770 | [[package]] 771 | name = "khronos-egl" 772 | version = "6.0.0" 773 | source = "registry+https://github.com/rust-lang/crates.io-index" 774 | checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" 775 | dependencies = [ 776 | "libc", 777 | "libloading 0.8.6", 778 | "pkg-config", 779 | ] 780 | 781 | [[package]] 782 | name = "khronos_api" 783 | version = "3.1.0" 784 | source = "registry+https://github.com/rust-lang/crates.io-index" 785 | checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" 786 | 787 | [[package]] 788 | name = "libc" 789 | version = "0.2.171" 790 | source = "registry+https://github.com/rust-lang/crates.io-index" 791 | checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" 792 | 793 | [[package]] 794 | name = "libloading" 795 | version = "0.7.4" 796 | source = "registry+https://github.com/rust-lang/crates.io-index" 797 | checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" 798 | dependencies = [ 799 | "cfg-if", 800 | "winapi", 801 | ] 802 | 803 | [[package]] 804 | name = "libloading" 805 | version = "0.8.6" 806 | source = "registry+https://github.com/rust-lang/crates.io-index" 807 | checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" 808 | dependencies = [ 809 | "cfg-if", 810 | "windows-targets 0.52.6", 811 | ] 812 | 813 | [[package]] 814 | name = "libredox" 815 | version = "0.1.3" 816 | source = "registry+https://github.com/rust-lang/crates.io-index" 817 | checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" 818 | dependencies = [ 819 | "bitflags 2.9.0", 820 | "libc", 821 | "redox_syscall 0.5.10", 822 | ] 823 | 824 | [[package]] 825 | name = "linux-raw-sys" 826 | version = "0.4.15" 827 | source = "registry+https://github.com/rust-lang/crates.io-index" 828 | checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" 829 | 830 | [[package]] 831 | name = "lock_api" 832 | version = "0.4.12" 833 | source = "registry+https://github.com/rust-lang/crates.io-index" 834 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 835 | dependencies = [ 836 | "autocfg", 837 | "scopeguard", 838 | ] 839 | 840 | [[package]] 841 | name = "log" 842 | version = "0.4.27" 843 | source = "registry+https://github.com/rust-lang/crates.io-index" 844 | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 845 | 846 | [[package]] 847 | name = "malloc_buf" 848 | version = "0.0.6" 849 | source = "registry+https://github.com/rust-lang/crates.io-index" 850 | checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" 851 | dependencies = [ 852 | "libc", 853 | ] 854 | 855 | [[package]] 856 | name = "memchr" 857 | version = "2.7.4" 858 | source = "registry+https://github.com/rust-lang/crates.io-index" 859 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 860 | 861 | [[package]] 862 | name = "memmap2" 863 | version = "0.9.5" 864 | source = "registry+https://github.com/rust-lang/crates.io-index" 865 | checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" 866 | dependencies = [ 867 | "libc", 868 | ] 869 | 870 | [[package]] 871 | name = "metal" 872 | version = "0.27.0" 873 | source = "registry+https://github.com/rust-lang/crates.io-index" 874 | checksum = "c43f73953f8cbe511f021b58f18c3ce1c3d1ae13fe953293e13345bf83217f25" 875 | dependencies = [ 876 | "bitflags 2.9.0", 877 | "block", 878 | "core-graphics-types", 879 | "foreign-types", 880 | "log", 881 | "objc", 882 | "paste", 883 | ] 884 | 885 | [[package]] 886 | name = "naga" 887 | version = "0.19.2" 888 | source = "registry+https://github.com/rust-lang/crates.io-index" 889 | checksum = "50e3524642f53d9af419ab5e8dd29d3ba155708267667c2f3f06c88c9e130843" 890 | dependencies = [ 891 | "bit-set", 892 | "bitflags 2.9.0", 893 | "codespan-reporting", 894 | "hexf-parse", 895 | "indexmap", 896 | "log", 897 | "num-traits", 898 | "rustc-hash", 899 | "spirv", 900 | "termcolor", 901 | "thiserror 1.0.69", 902 | "unicode-xid", 903 | ] 904 | 905 | [[package]] 906 | name = "ndk" 907 | version = "0.9.0" 908 | source = "registry+https://github.com/rust-lang/crates.io-index" 909 | checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" 910 | dependencies = [ 911 | "bitflags 2.9.0", 912 | "jni-sys", 913 | "log", 914 | "ndk-sys 0.6.0+11769913", 915 | "num_enum", 916 | "raw-window-handle", 917 | "thiserror 1.0.69", 918 | ] 919 | 920 | [[package]] 921 | name = "ndk-context" 922 | version = "0.1.1" 923 | source = "registry+https://github.com/rust-lang/crates.io-index" 924 | checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" 925 | 926 | [[package]] 927 | name = "ndk-sys" 928 | version = "0.5.0+25.2.9519653" 929 | source = "registry+https://github.com/rust-lang/crates.io-index" 930 | checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" 931 | dependencies = [ 932 | "jni-sys", 933 | ] 934 | 935 | [[package]] 936 | name = "ndk-sys" 937 | version = "0.6.0+11769913" 938 | source = "registry+https://github.com/rust-lang/crates.io-index" 939 | checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" 940 | dependencies = [ 941 | "jni-sys", 942 | ] 943 | 944 | [[package]] 945 | name = "num-traits" 946 | version = "0.2.19" 947 | source = "registry+https://github.com/rust-lang/crates.io-index" 948 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 949 | dependencies = [ 950 | "autocfg", 951 | ] 952 | 953 | [[package]] 954 | name = "num_enum" 955 | version = "0.7.3" 956 | source = "registry+https://github.com/rust-lang/crates.io-index" 957 | checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" 958 | dependencies = [ 959 | "num_enum_derive", 960 | ] 961 | 962 | [[package]] 963 | name = "num_enum_derive" 964 | version = "0.7.3" 965 | source = "registry+https://github.com/rust-lang/crates.io-index" 966 | checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" 967 | dependencies = [ 968 | "proc-macro-crate", 969 | "proc-macro2", 970 | "quote", 971 | "syn 2.0.100", 972 | ] 973 | 974 | [[package]] 975 | name = "objc" 976 | version = "0.2.7" 977 | source = "registry+https://github.com/rust-lang/crates.io-index" 978 | checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" 979 | dependencies = [ 980 | "malloc_buf", 981 | "objc_exception", 982 | ] 983 | 984 | [[package]] 985 | name = "objc-sys" 986 | version = "0.3.5" 987 | source = "registry+https://github.com/rust-lang/crates.io-index" 988 | checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" 989 | 990 | [[package]] 991 | name = "objc2" 992 | version = "0.5.2" 993 | source = "registry+https://github.com/rust-lang/crates.io-index" 994 | checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" 995 | dependencies = [ 996 | "objc-sys", 997 | "objc2-encode", 998 | ] 999 | 1000 | [[package]] 1001 | name = "objc2-app-kit" 1002 | version = "0.2.2" 1003 | source = "registry+https://github.com/rust-lang/crates.io-index" 1004 | checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" 1005 | dependencies = [ 1006 | "bitflags 2.9.0", 1007 | "block2", 1008 | "libc", 1009 | "objc2", 1010 | "objc2-core-data", 1011 | "objc2-core-image", 1012 | "objc2-foundation", 1013 | "objc2-quartz-core", 1014 | ] 1015 | 1016 | [[package]] 1017 | name = "objc2-cloud-kit" 1018 | version = "0.2.2" 1019 | source = "registry+https://github.com/rust-lang/crates.io-index" 1020 | checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" 1021 | dependencies = [ 1022 | "bitflags 2.9.0", 1023 | "block2", 1024 | "objc2", 1025 | "objc2-core-location", 1026 | "objc2-foundation", 1027 | ] 1028 | 1029 | [[package]] 1030 | name = "objc2-contacts" 1031 | version = "0.2.2" 1032 | source = "registry+https://github.com/rust-lang/crates.io-index" 1033 | checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" 1034 | dependencies = [ 1035 | "block2", 1036 | "objc2", 1037 | "objc2-foundation", 1038 | ] 1039 | 1040 | [[package]] 1041 | name = "objc2-core-data" 1042 | version = "0.2.2" 1043 | source = "registry+https://github.com/rust-lang/crates.io-index" 1044 | checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" 1045 | dependencies = [ 1046 | "bitflags 2.9.0", 1047 | "block2", 1048 | "objc2", 1049 | "objc2-foundation", 1050 | ] 1051 | 1052 | [[package]] 1053 | name = "objc2-core-image" 1054 | version = "0.2.2" 1055 | source = "registry+https://github.com/rust-lang/crates.io-index" 1056 | checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" 1057 | dependencies = [ 1058 | "block2", 1059 | "objc2", 1060 | "objc2-foundation", 1061 | "objc2-metal", 1062 | ] 1063 | 1064 | [[package]] 1065 | name = "objc2-core-location" 1066 | version = "0.2.2" 1067 | source = "registry+https://github.com/rust-lang/crates.io-index" 1068 | checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" 1069 | dependencies = [ 1070 | "block2", 1071 | "objc2", 1072 | "objc2-contacts", 1073 | "objc2-foundation", 1074 | ] 1075 | 1076 | [[package]] 1077 | name = "objc2-encode" 1078 | version = "4.1.0" 1079 | source = "registry+https://github.com/rust-lang/crates.io-index" 1080 | checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" 1081 | 1082 | [[package]] 1083 | name = "objc2-foundation" 1084 | version = "0.2.2" 1085 | source = "registry+https://github.com/rust-lang/crates.io-index" 1086 | checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" 1087 | dependencies = [ 1088 | "bitflags 2.9.0", 1089 | "block2", 1090 | "dispatch", 1091 | "libc", 1092 | "objc2", 1093 | ] 1094 | 1095 | [[package]] 1096 | name = "objc2-link-presentation" 1097 | version = "0.2.2" 1098 | source = "registry+https://github.com/rust-lang/crates.io-index" 1099 | checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" 1100 | dependencies = [ 1101 | "block2", 1102 | "objc2", 1103 | "objc2-app-kit", 1104 | "objc2-foundation", 1105 | ] 1106 | 1107 | [[package]] 1108 | name = "objc2-metal" 1109 | version = "0.2.2" 1110 | source = "registry+https://github.com/rust-lang/crates.io-index" 1111 | checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" 1112 | dependencies = [ 1113 | "bitflags 2.9.0", 1114 | "block2", 1115 | "objc2", 1116 | "objc2-foundation", 1117 | ] 1118 | 1119 | [[package]] 1120 | name = "objc2-quartz-core" 1121 | version = "0.2.2" 1122 | source = "registry+https://github.com/rust-lang/crates.io-index" 1123 | checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" 1124 | dependencies = [ 1125 | "bitflags 2.9.0", 1126 | "block2", 1127 | "objc2", 1128 | "objc2-foundation", 1129 | "objc2-metal", 1130 | ] 1131 | 1132 | [[package]] 1133 | name = "objc2-symbols" 1134 | version = "0.2.2" 1135 | source = "registry+https://github.com/rust-lang/crates.io-index" 1136 | checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" 1137 | dependencies = [ 1138 | "objc2", 1139 | "objc2-foundation", 1140 | ] 1141 | 1142 | [[package]] 1143 | name = "objc2-ui-kit" 1144 | version = "0.2.2" 1145 | source = "registry+https://github.com/rust-lang/crates.io-index" 1146 | checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" 1147 | dependencies = [ 1148 | "bitflags 2.9.0", 1149 | "block2", 1150 | "objc2", 1151 | "objc2-cloud-kit", 1152 | "objc2-core-data", 1153 | "objc2-core-image", 1154 | "objc2-core-location", 1155 | "objc2-foundation", 1156 | "objc2-link-presentation", 1157 | "objc2-quartz-core", 1158 | "objc2-symbols", 1159 | "objc2-uniform-type-identifiers", 1160 | "objc2-user-notifications", 1161 | ] 1162 | 1163 | [[package]] 1164 | name = "objc2-uniform-type-identifiers" 1165 | version = "0.2.2" 1166 | source = "registry+https://github.com/rust-lang/crates.io-index" 1167 | checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" 1168 | dependencies = [ 1169 | "block2", 1170 | "objc2", 1171 | "objc2-foundation", 1172 | ] 1173 | 1174 | [[package]] 1175 | name = "objc2-user-notifications" 1176 | version = "0.2.2" 1177 | source = "registry+https://github.com/rust-lang/crates.io-index" 1178 | checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" 1179 | dependencies = [ 1180 | "bitflags 2.9.0", 1181 | "block2", 1182 | "objc2", 1183 | "objc2-core-location", 1184 | "objc2-foundation", 1185 | ] 1186 | 1187 | [[package]] 1188 | name = "objc_exception" 1189 | version = "0.1.2" 1190 | source = "registry+https://github.com/rust-lang/crates.io-index" 1191 | checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" 1192 | dependencies = [ 1193 | "cc", 1194 | ] 1195 | 1196 | [[package]] 1197 | name = "once_cell" 1198 | version = "1.21.3" 1199 | source = "registry+https://github.com/rust-lang/crates.io-index" 1200 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 1201 | 1202 | [[package]] 1203 | name = "orbclient" 1204 | version = "0.3.48" 1205 | source = "registry+https://github.com/rust-lang/crates.io-index" 1206 | checksum = "ba0b26cec2e24f08ed8bb31519a9333140a6599b867dac464bb150bdb796fd43" 1207 | dependencies = [ 1208 | "libredox", 1209 | ] 1210 | 1211 | [[package]] 1212 | name = "owned_ttf_parser" 1213 | version = "0.25.0" 1214 | source = "registry+https://github.com/rust-lang/crates.io-index" 1215 | checksum = "22ec719bbf3b2a81c109a4e20b1f129b5566b7dce654bc3872f6a05abf82b2c4" 1216 | dependencies = [ 1217 | "ttf-parser", 1218 | ] 1219 | 1220 | [[package]] 1221 | name = "parking_lot" 1222 | version = "0.12.3" 1223 | source = "registry+https://github.com/rust-lang/crates.io-index" 1224 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 1225 | dependencies = [ 1226 | "lock_api", 1227 | "parking_lot_core", 1228 | ] 1229 | 1230 | [[package]] 1231 | name = "parking_lot_core" 1232 | version = "0.9.10" 1233 | source = "registry+https://github.com/rust-lang/crates.io-index" 1234 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 1235 | dependencies = [ 1236 | "cfg-if", 1237 | "libc", 1238 | "redox_syscall 0.5.10", 1239 | "smallvec", 1240 | "windows-targets 0.52.6", 1241 | ] 1242 | 1243 | [[package]] 1244 | name = "paste" 1245 | version = "1.0.15" 1246 | source = "registry+https://github.com/rust-lang/crates.io-index" 1247 | checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" 1248 | 1249 | [[package]] 1250 | name = "percent-encoding" 1251 | version = "2.3.1" 1252 | source = "registry+https://github.com/rust-lang/crates.io-index" 1253 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 1254 | 1255 | [[package]] 1256 | name = "pin-project" 1257 | version = "1.1.10" 1258 | source = "registry+https://github.com/rust-lang/crates.io-index" 1259 | checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" 1260 | dependencies = [ 1261 | "pin-project-internal", 1262 | ] 1263 | 1264 | [[package]] 1265 | name = "pin-project-internal" 1266 | version = "1.1.10" 1267 | source = "registry+https://github.com/rust-lang/crates.io-index" 1268 | checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" 1269 | dependencies = [ 1270 | "proc-macro2", 1271 | "quote", 1272 | "syn 2.0.100", 1273 | ] 1274 | 1275 | [[package]] 1276 | name = "pin-project-lite" 1277 | version = "0.2.16" 1278 | source = "registry+https://github.com/rust-lang/crates.io-index" 1279 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 1280 | 1281 | [[package]] 1282 | name = "pixels" 1283 | version = "0.15.0" 1284 | source = "registry+https://github.com/rust-lang/crates.io-index" 1285 | checksum = "518d43cd70c5381d4c7bd4bf47ee344beee99b58b0587adcb198cc713ff0dfb5" 1286 | dependencies = [ 1287 | "bytemuck", 1288 | "pollster", 1289 | "raw-window-handle", 1290 | "thiserror 1.0.69", 1291 | "ultraviolet", 1292 | "wgpu", 1293 | ] 1294 | 1295 | [[package]] 1296 | name = "pkg-config" 1297 | version = "0.3.32" 1298 | source = "registry+https://github.com/rust-lang/crates.io-index" 1299 | checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" 1300 | 1301 | [[package]] 1302 | name = "polling" 1303 | version = "3.7.4" 1304 | source = "registry+https://github.com/rust-lang/crates.io-index" 1305 | checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" 1306 | dependencies = [ 1307 | "cfg-if", 1308 | "concurrent-queue", 1309 | "hermit-abi", 1310 | "pin-project-lite", 1311 | "rustix", 1312 | "tracing", 1313 | "windows-sys 0.59.0", 1314 | ] 1315 | 1316 | [[package]] 1317 | name = "pollster" 1318 | version = "0.3.0" 1319 | source = "registry+https://github.com/rust-lang/crates.io-index" 1320 | checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" 1321 | 1322 | [[package]] 1323 | name = "portable-atomic" 1324 | version = "1.11.0" 1325 | source = "registry+https://github.com/rust-lang/crates.io-index" 1326 | checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" 1327 | 1328 | [[package]] 1329 | name = "portable-atomic-util" 1330 | version = "0.2.4" 1331 | source = "registry+https://github.com/rust-lang/crates.io-index" 1332 | checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" 1333 | dependencies = [ 1334 | "portable-atomic", 1335 | ] 1336 | 1337 | [[package]] 1338 | name = "presser" 1339 | version = "0.3.1" 1340 | source = "registry+https://github.com/rust-lang/crates.io-index" 1341 | checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" 1342 | 1343 | [[package]] 1344 | name = "proc-macro-crate" 1345 | version = "3.3.0" 1346 | source = "registry+https://github.com/rust-lang/crates.io-index" 1347 | checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" 1348 | dependencies = [ 1349 | "toml_edit", 1350 | ] 1351 | 1352 | [[package]] 1353 | name = "proc-macro2" 1354 | version = "1.0.94" 1355 | source = "registry+https://github.com/rust-lang/crates.io-index" 1356 | checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" 1357 | dependencies = [ 1358 | "unicode-ident", 1359 | ] 1360 | 1361 | [[package]] 1362 | name = "profiling" 1363 | version = "1.0.16" 1364 | source = "registry+https://github.com/rust-lang/crates.io-index" 1365 | checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" 1366 | 1367 | [[package]] 1368 | name = "quick-xml" 1369 | version = "0.37.3" 1370 | source = "registry+https://github.com/rust-lang/crates.io-index" 1371 | checksum = "bf763ab1c7a3aa408be466efc86efe35ed1bd3dd74173ed39d6b0d0a6f0ba148" 1372 | dependencies = [ 1373 | "memchr", 1374 | ] 1375 | 1376 | [[package]] 1377 | name = "quote" 1378 | version = "1.0.40" 1379 | source = "registry+https://github.com/rust-lang/crates.io-index" 1380 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 1381 | dependencies = [ 1382 | "proc-macro2", 1383 | ] 1384 | 1385 | [[package]] 1386 | name = "range-alloc" 1387 | version = "0.1.4" 1388 | source = "registry+https://github.com/rust-lang/crates.io-index" 1389 | checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde" 1390 | 1391 | [[package]] 1392 | name = "raw-window-handle" 1393 | version = "0.6.2" 1394 | source = "registry+https://github.com/rust-lang/crates.io-index" 1395 | checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" 1396 | 1397 | [[package]] 1398 | name = "redox_syscall" 1399 | version = "0.4.1" 1400 | source = "registry+https://github.com/rust-lang/crates.io-index" 1401 | checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" 1402 | dependencies = [ 1403 | "bitflags 1.3.2", 1404 | ] 1405 | 1406 | [[package]] 1407 | name = "redox_syscall" 1408 | version = "0.5.10" 1409 | source = "registry+https://github.com/rust-lang/crates.io-index" 1410 | checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" 1411 | dependencies = [ 1412 | "bitflags 2.9.0", 1413 | ] 1414 | 1415 | [[package]] 1416 | name = "regex" 1417 | version = "1.11.1" 1418 | source = "registry+https://github.com/rust-lang/crates.io-index" 1419 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 1420 | dependencies = [ 1421 | "aho-corasick", 1422 | "memchr", 1423 | "regex-automata", 1424 | "regex-syntax", 1425 | ] 1426 | 1427 | [[package]] 1428 | name = "regex-automata" 1429 | version = "0.4.9" 1430 | source = "registry+https://github.com/rust-lang/crates.io-index" 1431 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 1432 | dependencies = [ 1433 | "aho-corasick", 1434 | "memchr", 1435 | "regex-syntax", 1436 | ] 1437 | 1438 | [[package]] 1439 | name = "regex-syntax" 1440 | version = "0.8.5" 1441 | source = "registry+https://github.com/rust-lang/crates.io-index" 1442 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 1443 | 1444 | [[package]] 1445 | name = "renderdoc-sys" 1446 | version = "1.1.0" 1447 | source = "registry+https://github.com/rust-lang/crates.io-index" 1448 | checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" 1449 | 1450 | [[package]] 1451 | name = "rustc-hash" 1452 | version = "1.1.0" 1453 | source = "registry+https://github.com/rust-lang/crates.io-index" 1454 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 1455 | 1456 | [[package]] 1457 | name = "rustix" 1458 | version = "0.38.44" 1459 | source = "registry+https://github.com/rust-lang/crates.io-index" 1460 | checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" 1461 | dependencies = [ 1462 | "bitflags 2.9.0", 1463 | "errno", 1464 | "libc", 1465 | "linux-raw-sys", 1466 | "windows-sys 0.59.0", 1467 | ] 1468 | 1469 | [[package]] 1470 | name = "rustversion" 1471 | version = "1.0.20" 1472 | source = "registry+https://github.com/rust-lang/crates.io-index" 1473 | checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" 1474 | 1475 | [[package]] 1476 | name = "safe_arch" 1477 | version = "0.7.4" 1478 | source = "registry+https://github.com/rust-lang/crates.io-index" 1479 | checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" 1480 | dependencies = [ 1481 | "bytemuck", 1482 | ] 1483 | 1484 | [[package]] 1485 | name = "same-file" 1486 | version = "1.0.6" 1487 | source = "registry+https://github.com/rust-lang/crates.io-index" 1488 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 1489 | dependencies = [ 1490 | "winapi-util", 1491 | ] 1492 | 1493 | [[package]] 1494 | name = "scoped-tls" 1495 | version = "1.0.1" 1496 | source = "registry+https://github.com/rust-lang/crates.io-index" 1497 | checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" 1498 | 1499 | [[package]] 1500 | name = "scopeguard" 1501 | version = "1.2.0" 1502 | source = "registry+https://github.com/rust-lang/crates.io-index" 1503 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1504 | 1505 | [[package]] 1506 | name = "sctk-adwaita" 1507 | version = "0.10.1" 1508 | source = "registry+https://github.com/rust-lang/crates.io-index" 1509 | checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec" 1510 | dependencies = [ 1511 | "ab_glyph", 1512 | "log", 1513 | "memmap2", 1514 | "smithay-client-toolkit", 1515 | "tiny-skia", 1516 | ] 1517 | 1518 | [[package]] 1519 | name = "serde" 1520 | version = "1.0.219" 1521 | source = "registry+https://github.com/rust-lang/crates.io-index" 1522 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 1523 | dependencies = [ 1524 | "serde_derive", 1525 | ] 1526 | 1527 | [[package]] 1528 | name = "serde_derive" 1529 | version = "1.0.219" 1530 | source = "registry+https://github.com/rust-lang/crates.io-index" 1531 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 1532 | dependencies = [ 1533 | "proc-macro2", 1534 | "quote", 1535 | "syn 2.0.100", 1536 | ] 1537 | 1538 | [[package]] 1539 | name = "shlex" 1540 | version = "1.3.0" 1541 | source = "registry+https://github.com/rust-lang/crates.io-index" 1542 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 1543 | 1544 | [[package]] 1545 | name = "slab" 1546 | version = "0.4.9" 1547 | source = "registry+https://github.com/rust-lang/crates.io-index" 1548 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 1549 | dependencies = [ 1550 | "autocfg", 1551 | ] 1552 | 1553 | [[package]] 1554 | name = "slotmap" 1555 | version = "1.0.7" 1556 | source = "registry+https://github.com/rust-lang/crates.io-index" 1557 | checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" 1558 | dependencies = [ 1559 | "version_check", 1560 | ] 1561 | 1562 | [[package]] 1563 | name = "smallvec" 1564 | version = "1.14.0" 1565 | source = "registry+https://github.com/rust-lang/crates.io-index" 1566 | checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" 1567 | 1568 | [[package]] 1569 | name = "smithay-client-toolkit" 1570 | version = "0.19.2" 1571 | source = "registry+https://github.com/rust-lang/crates.io-index" 1572 | checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" 1573 | dependencies = [ 1574 | "bitflags 2.9.0", 1575 | "calloop", 1576 | "calloop-wayland-source", 1577 | "cursor-icon", 1578 | "libc", 1579 | "log", 1580 | "memmap2", 1581 | "rustix", 1582 | "thiserror 1.0.69", 1583 | "wayland-backend", 1584 | "wayland-client", 1585 | "wayland-csd-frame", 1586 | "wayland-cursor", 1587 | "wayland-protocols", 1588 | "wayland-protocols-wlr", 1589 | "wayland-scanner", 1590 | "xkeysym", 1591 | ] 1592 | 1593 | [[package]] 1594 | name = "smol_str" 1595 | version = "0.2.2" 1596 | source = "registry+https://github.com/rust-lang/crates.io-index" 1597 | checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" 1598 | dependencies = [ 1599 | "serde", 1600 | ] 1601 | 1602 | [[package]] 1603 | name = "sn-50" 1604 | version = "0.1.0" 1605 | dependencies = [ 1606 | "tinlib", 1607 | ] 1608 | 1609 | [[package]] 1610 | name = "sn-50-player" 1611 | version = "0.1.0" 1612 | dependencies = [ 1613 | "anyhow", 1614 | "env_logger", 1615 | "error-iter", 1616 | "log", 1617 | "pixels", 1618 | "tinlib", 1619 | "winit", 1620 | ] 1621 | 1622 | [[package]] 1623 | name = "spirv" 1624 | version = "0.3.0+sdk-1.3.268.0" 1625 | source = "registry+https://github.com/rust-lang/crates.io-index" 1626 | checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" 1627 | dependencies = [ 1628 | "bitflags 2.9.0", 1629 | ] 1630 | 1631 | [[package]] 1632 | name = "static_assertions" 1633 | version = "1.1.0" 1634 | source = "registry+https://github.com/rust-lang/crates.io-index" 1635 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 1636 | 1637 | [[package]] 1638 | name = "strict-num" 1639 | version = "0.1.1" 1640 | source = "registry+https://github.com/rust-lang/crates.io-index" 1641 | checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" 1642 | 1643 | [[package]] 1644 | name = "syn" 1645 | version = "1.0.109" 1646 | source = "registry+https://github.com/rust-lang/crates.io-index" 1647 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 1648 | dependencies = [ 1649 | "proc-macro2", 1650 | "quote", 1651 | "unicode-ident", 1652 | ] 1653 | 1654 | [[package]] 1655 | name = "syn" 1656 | version = "2.0.100" 1657 | source = "registry+https://github.com/rust-lang/crates.io-index" 1658 | checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" 1659 | dependencies = [ 1660 | "proc-macro2", 1661 | "quote", 1662 | "unicode-ident", 1663 | ] 1664 | 1665 | [[package]] 1666 | name = "termcolor" 1667 | version = "1.4.1" 1668 | source = "registry+https://github.com/rust-lang/crates.io-index" 1669 | checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" 1670 | dependencies = [ 1671 | "winapi-util", 1672 | ] 1673 | 1674 | [[package]] 1675 | name = "thiserror" 1676 | version = "1.0.69" 1677 | source = "registry+https://github.com/rust-lang/crates.io-index" 1678 | checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 1679 | dependencies = [ 1680 | "thiserror-impl 1.0.69", 1681 | ] 1682 | 1683 | [[package]] 1684 | name = "thiserror" 1685 | version = "2.0.12" 1686 | source = "registry+https://github.com/rust-lang/crates.io-index" 1687 | checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" 1688 | dependencies = [ 1689 | "thiserror-impl 2.0.12", 1690 | ] 1691 | 1692 | [[package]] 1693 | name = "thiserror-impl" 1694 | version = "1.0.69" 1695 | source = "registry+https://github.com/rust-lang/crates.io-index" 1696 | checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 1697 | dependencies = [ 1698 | "proc-macro2", 1699 | "quote", 1700 | "syn 2.0.100", 1701 | ] 1702 | 1703 | [[package]] 1704 | name = "thiserror-impl" 1705 | version = "2.0.12" 1706 | source = "registry+https://github.com/rust-lang/crates.io-index" 1707 | checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" 1708 | dependencies = [ 1709 | "proc-macro2", 1710 | "quote", 1711 | "syn 2.0.100", 1712 | ] 1713 | 1714 | [[package]] 1715 | name = "tinlib" 1716 | version = "0.1.0" 1717 | dependencies = [ 1718 | "assert_matches", 1719 | "byteorder", 1720 | "log", 1721 | "thiserror 2.0.12", 1722 | ] 1723 | 1724 | [[package]] 1725 | name = "tiny-skia" 1726 | version = "0.11.4" 1727 | source = "registry+https://github.com/rust-lang/crates.io-index" 1728 | checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" 1729 | dependencies = [ 1730 | "arrayref", 1731 | "arrayvec", 1732 | "bytemuck", 1733 | "cfg-if", 1734 | "log", 1735 | "tiny-skia-path", 1736 | ] 1737 | 1738 | [[package]] 1739 | name = "tiny-skia-path" 1740 | version = "0.11.4" 1741 | source = "registry+https://github.com/rust-lang/crates.io-index" 1742 | checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" 1743 | dependencies = [ 1744 | "arrayref", 1745 | "bytemuck", 1746 | "strict-num", 1747 | ] 1748 | 1749 | [[package]] 1750 | name = "toml_datetime" 1751 | version = "0.6.8" 1752 | source = "registry+https://github.com/rust-lang/crates.io-index" 1753 | checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" 1754 | 1755 | [[package]] 1756 | name = "toml_edit" 1757 | version = "0.22.24" 1758 | source = "registry+https://github.com/rust-lang/crates.io-index" 1759 | checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" 1760 | dependencies = [ 1761 | "indexmap", 1762 | "toml_datetime", 1763 | "winnow", 1764 | ] 1765 | 1766 | [[package]] 1767 | name = "tracing" 1768 | version = "0.1.41" 1769 | source = "registry+https://github.com/rust-lang/crates.io-index" 1770 | checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 1771 | dependencies = [ 1772 | "pin-project-lite", 1773 | "tracing-core", 1774 | ] 1775 | 1776 | [[package]] 1777 | name = "tracing-core" 1778 | version = "0.1.33" 1779 | source = "registry+https://github.com/rust-lang/crates.io-index" 1780 | checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" 1781 | 1782 | [[package]] 1783 | name = "ttf-parser" 1784 | version = "0.25.1" 1785 | source = "registry+https://github.com/rust-lang/crates.io-index" 1786 | checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" 1787 | 1788 | [[package]] 1789 | name = "ultraviolet" 1790 | version = "0.9.2" 1791 | source = "registry+https://github.com/rust-lang/crates.io-index" 1792 | checksum = "6a28554d13eb5daba527cc1b91b6c341372a0ae45ed277ffb2c6fbc04f319d7e" 1793 | dependencies = [ 1794 | "wide", 1795 | ] 1796 | 1797 | [[package]] 1798 | name = "unicode-ident" 1799 | version = "1.0.18" 1800 | source = "registry+https://github.com/rust-lang/crates.io-index" 1801 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 1802 | 1803 | [[package]] 1804 | name = "unicode-segmentation" 1805 | version = "1.12.0" 1806 | source = "registry+https://github.com/rust-lang/crates.io-index" 1807 | checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" 1808 | 1809 | [[package]] 1810 | name = "unicode-width" 1811 | version = "0.1.14" 1812 | source = "registry+https://github.com/rust-lang/crates.io-index" 1813 | checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" 1814 | 1815 | [[package]] 1816 | name = "unicode-xid" 1817 | version = "0.2.6" 1818 | source = "registry+https://github.com/rust-lang/crates.io-index" 1819 | checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" 1820 | 1821 | [[package]] 1822 | name = "utf8parse" 1823 | version = "0.2.2" 1824 | source = "registry+https://github.com/rust-lang/crates.io-index" 1825 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 1826 | 1827 | [[package]] 1828 | name = "version_check" 1829 | version = "0.9.5" 1830 | source = "registry+https://github.com/rust-lang/crates.io-index" 1831 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 1832 | 1833 | [[package]] 1834 | name = "walkdir" 1835 | version = "2.5.0" 1836 | source = "registry+https://github.com/rust-lang/crates.io-index" 1837 | checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 1838 | dependencies = [ 1839 | "same-file", 1840 | "winapi-util", 1841 | ] 1842 | 1843 | [[package]] 1844 | name = "wasi" 1845 | version = "0.11.0+wasi-snapshot-preview1" 1846 | source = "registry+https://github.com/rust-lang/crates.io-index" 1847 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1848 | 1849 | [[package]] 1850 | name = "wasm-bindgen" 1851 | version = "0.2.100" 1852 | source = "registry+https://github.com/rust-lang/crates.io-index" 1853 | checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 1854 | dependencies = [ 1855 | "cfg-if", 1856 | "once_cell", 1857 | "rustversion", 1858 | "wasm-bindgen-macro", 1859 | ] 1860 | 1861 | [[package]] 1862 | name = "wasm-bindgen-backend" 1863 | version = "0.2.100" 1864 | source = "registry+https://github.com/rust-lang/crates.io-index" 1865 | checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 1866 | dependencies = [ 1867 | "bumpalo", 1868 | "log", 1869 | "proc-macro2", 1870 | "quote", 1871 | "syn 2.0.100", 1872 | "wasm-bindgen-shared", 1873 | ] 1874 | 1875 | [[package]] 1876 | name = "wasm-bindgen-futures" 1877 | version = "0.4.50" 1878 | source = "registry+https://github.com/rust-lang/crates.io-index" 1879 | checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" 1880 | dependencies = [ 1881 | "cfg-if", 1882 | "js-sys", 1883 | "once_cell", 1884 | "wasm-bindgen", 1885 | "web-sys", 1886 | ] 1887 | 1888 | [[package]] 1889 | name = "wasm-bindgen-macro" 1890 | version = "0.2.100" 1891 | source = "registry+https://github.com/rust-lang/crates.io-index" 1892 | checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 1893 | dependencies = [ 1894 | "quote", 1895 | "wasm-bindgen-macro-support", 1896 | ] 1897 | 1898 | [[package]] 1899 | name = "wasm-bindgen-macro-support" 1900 | version = "0.2.100" 1901 | source = "registry+https://github.com/rust-lang/crates.io-index" 1902 | checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 1903 | dependencies = [ 1904 | "proc-macro2", 1905 | "quote", 1906 | "syn 2.0.100", 1907 | "wasm-bindgen-backend", 1908 | "wasm-bindgen-shared", 1909 | ] 1910 | 1911 | [[package]] 1912 | name = "wasm-bindgen-shared" 1913 | version = "0.2.100" 1914 | source = "registry+https://github.com/rust-lang/crates.io-index" 1915 | checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 1916 | dependencies = [ 1917 | "unicode-ident", 1918 | ] 1919 | 1920 | [[package]] 1921 | name = "wayland-backend" 1922 | version = "0.3.8" 1923 | source = "registry+https://github.com/rust-lang/crates.io-index" 1924 | checksum = "b7208998eaa3870dad37ec8836979581506e0c5c64c20c9e79e9d2a10d6f47bf" 1925 | dependencies = [ 1926 | "cc", 1927 | "downcast-rs", 1928 | "rustix", 1929 | "scoped-tls", 1930 | "smallvec", 1931 | "wayland-sys", 1932 | ] 1933 | 1934 | [[package]] 1935 | name = "wayland-client" 1936 | version = "0.31.8" 1937 | source = "registry+https://github.com/rust-lang/crates.io-index" 1938 | checksum = "c2120de3d33638aaef5b9f4472bff75f07c56379cf76ea320bd3a3d65ecaf73f" 1939 | dependencies = [ 1940 | "bitflags 2.9.0", 1941 | "rustix", 1942 | "wayland-backend", 1943 | "wayland-scanner", 1944 | ] 1945 | 1946 | [[package]] 1947 | name = "wayland-csd-frame" 1948 | version = "0.3.0" 1949 | source = "registry+https://github.com/rust-lang/crates.io-index" 1950 | checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" 1951 | dependencies = [ 1952 | "bitflags 2.9.0", 1953 | "cursor-icon", 1954 | "wayland-backend", 1955 | ] 1956 | 1957 | [[package]] 1958 | name = "wayland-cursor" 1959 | version = "0.31.8" 1960 | source = "registry+https://github.com/rust-lang/crates.io-index" 1961 | checksum = "a93029cbb6650748881a00e4922b076092a6a08c11e7fbdb923f064b23968c5d" 1962 | dependencies = [ 1963 | "rustix", 1964 | "wayland-client", 1965 | "xcursor", 1966 | ] 1967 | 1968 | [[package]] 1969 | name = "wayland-protocols" 1970 | version = "0.32.6" 1971 | source = "registry+https://github.com/rust-lang/crates.io-index" 1972 | checksum = "0781cf46869b37e36928f7b432273c0995aa8aed9552c556fb18754420541efc" 1973 | dependencies = [ 1974 | "bitflags 2.9.0", 1975 | "wayland-backend", 1976 | "wayland-client", 1977 | "wayland-scanner", 1978 | ] 1979 | 1980 | [[package]] 1981 | name = "wayland-protocols-plasma" 1982 | version = "0.3.6" 1983 | source = "registry+https://github.com/rust-lang/crates.io-index" 1984 | checksum = "7ccaacc76703fefd6763022ac565b590fcade92202492381c95b2edfdf7d46b3" 1985 | dependencies = [ 1986 | "bitflags 2.9.0", 1987 | "wayland-backend", 1988 | "wayland-client", 1989 | "wayland-protocols", 1990 | "wayland-scanner", 1991 | ] 1992 | 1993 | [[package]] 1994 | name = "wayland-protocols-wlr" 1995 | version = "0.3.6" 1996 | source = "registry+https://github.com/rust-lang/crates.io-index" 1997 | checksum = "248a02e6f595aad796561fa82d25601bd2c8c3b145b1c7453fc8f94c1a58f8b2" 1998 | dependencies = [ 1999 | "bitflags 2.9.0", 2000 | "wayland-backend", 2001 | "wayland-client", 2002 | "wayland-protocols", 2003 | "wayland-scanner", 2004 | ] 2005 | 2006 | [[package]] 2007 | name = "wayland-scanner" 2008 | version = "0.31.6" 2009 | source = "registry+https://github.com/rust-lang/crates.io-index" 2010 | checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" 2011 | dependencies = [ 2012 | "proc-macro2", 2013 | "quick-xml", 2014 | "quote", 2015 | ] 2016 | 2017 | [[package]] 2018 | name = "wayland-sys" 2019 | version = "0.31.6" 2020 | source = "registry+https://github.com/rust-lang/crates.io-index" 2021 | checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615" 2022 | dependencies = [ 2023 | "dlib", 2024 | "log", 2025 | "once_cell", 2026 | "pkg-config", 2027 | ] 2028 | 2029 | [[package]] 2030 | name = "web-sys" 2031 | version = "0.3.77" 2032 | source = "registry+https://github.com/rust-lang/crates.io-index" 2033 | checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" 2034 | dependencies = [ 2035 | "js-sys", 2036 | "wasm-bindgen", 2037 | ] 2038 | 2039 | [[package]] 2040 | name = "web-time" 2041 | version = "1.1.0" 2042 | source = "registry+https://github.com/rust-lang/crates.io-index" 2043 | checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" 2044 | dependencies = [ 2045 | "js-sys", 2046 | "wasm-bindgen", 2047 | ] 2048 | 2049 | [[package]] 2050 | name = "wgpu" 2051 | version = "0.19.4" 2052 | source = "registry+https://github.com/rust-lang/crates.io-index" 2053 | checksum = "cbd7311dbd2abcfebaabf1841a2824ed7c8be443a0f29166e5d3c6a53a762c01" 2054 | dependencies = [ 2055 | "arrayvec", 2056 | "cfg-if", 2057 | "cfg_aliases 0.1.1", 2058 | "js-sys", 2059 | "log", 2060 | "naga", 2061 | "parking_lot", 2062 | "profiling", 2063 | "raw-window-handle", 2064 | "smallvec", 2065 | "static_assertions", 2066 | "wasm-bindgen", 2067 | "wasm-bindgen-futures", 2068 | "web-sys", 2069 | "wgpu-core", 2070 | "wgpu-hal", 2071 | "wgpu-types", 2072 | ] 2073 | 2074 | [[package]] 2075 | name = "wgpu-core" 2076 | version = "0.19.4" 2077 | source = "registry+https://github.com/rust-lang/crates.io-index" 2078 | checksum = "28b94525fc99ba9e5c9a9e24764f2bc29bad0911a7446c12f446a8277369bf3a" 2079 | dependencies = [ 2080 | "arrayvec", 2081 | "bit-vec", 2082 | "bitflags 2.9.0", 2083 | "cfg_aliases 0.1.1", 2084 | "codespan-reporting", 2085 | "indexmap", 2086 | "log", 2087 | "naga", 2088 | "once_cell", 2089 | "parking_lot", 2090 | "profiling", 2091 | "raw-window-handle", 2092 | "rustc-hash", 2093 | "smallvec", 2094 | "thiserror 1.0.69", 2095 | "web-sys", 2096 | "wgpu-hal", 2097 | "wgpu-types", 2098 | ] 2099 | 2100 | [[package]] 2101 | name = "wgpu-hal" 2102 | version = "0.19.5" 2103 | source = "registry+https://github.com/rust-lang/crates.io-index" 2104 | checksum = "bfabcfc55fd86611a855816326b2d54c3b2fd7972c27ce414291562650552703" 2105 | dependencies = [ 2106 | "android_system_properties", 2107 | "arrayvec", 2108 | "ash", 2109 | "bit-set", 2110 | "bitflags 2.9.0", 2111 | "block", 2112 | "cfg_aliases 0.1.1", 2113 | "core-graphics-types", 2114 | "d3d12", 2115 | "glow", 2116 | "glutin_wgl_sys", 2117 | "gpu-alloc", 2118 | "gpu-allocator", 2119 | "gpu-descriptor", 2120 | "hassle-rs", 2121 | "js-sys", 2122 | "khronos-egl", 2123 | "libc", 2124 | "libloading 0.8.6", 2125 | "log", 2126 | "metal", 2127 | "naga", 2128 | "ndk-sys 0.5.0+25.2.9519653", 2129 | "objc", 2130 | "once_cell", 2131 | "parking_lot", 2132 | "profiling", 2133 | "range-alloc", 2134 | "raw-window-handle", 2135 | "renderdoc-sys", 2136 | "rustc-hash", 2137 | "smallvec", 2138 | "thiserror 1.0.69", 2139 | "wasm-bindgen", 2140 | "web-sys", 2141 | "wgpu-types", 2142 | "winapi", 2143 | ] 2144 | 2145 | [[package]] 2146 | name = "wgpu-types" 2147 | version = "0.19.2" 2148 | source = "registry+https://github.com/rust-lang/crates.io-index" 2149 | checksum = "b671ff9fb03f78b46ff176494ee1ebe7d603393f42664be55b64dc8d53969805" 2150 | dependencies = [ 2151 | "bitflags 2.9.0", 2152 | "js-sys", 2153 | "web-sys", 2154 | ] 2155 | 2156 | [[package]] 2157 | name = "wide" 2158 | version = "0.7.32" 2159 | source = "registry+https://github.com/rust-lang/crates.io-index" 2160 | checksum = "41b5576b9a81633f3e8df296ce0063042a73507636cbe956c61133dd7034ab22" 2161 | dependencies = [ 2162 | "bytemuck", 2163 | "safe_arch", 2164 | ] 2165 | 2166 | [[package]] 2167 | name = "widestring" 2168 | version = "1.2.0" 2169 | source = "registry+https://github.com/rust-lang/crates.io-index" 2170 | checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" 2171 | 2172 | [[package]] 2173 | name = "winapi" 2174 | version = "0.3.9" 2175 | source = "registry+https://github.com/rust-lang/crates.io-index" 2176 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 2177 | dependencies = [ 2178 | "winapi-i686-pc-windows-gnu", 2179 | "winapi-x86_64-pc-windows-gnu", 2180 | ] 2181 | 2182 | [[package]] 2183 | name = "winapi-i686-pc-windows-gnu" 2184 | version = "0.4.0" 2185 | source = "registry+https://github.com/rust-lang/crates.io-index" 2186 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 2187 | 2188 | [[package]] 2189 | name = "winapi-util" 2190 | version = "0.1.9" 2191 | source = "registry+https://github.com/rust-lang/crates.io-index" 2192 | checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 2193 | dependencies = [ 2194 | "windows-sys 0.59.0", 2195 | ] 2196 | 2197 | [[package]] 2198 | name = "winapi-x86_64-pc-windows-gnu" 2199 | version = "0.4.0" 2200 | source = "registry+https://github.com/rust-lang/crates.io-index" 2201 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2202 | 2203 | [[package]] 2204 | name = "windows" 2205 | version = "0.52.0" 2206 | source = "registry+https://github.com/rust-lang/crates.io-index" 2207 | checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" 2208 | dependencies = [ 2209 | "windows-core", 2210 | "windows-targets 0.52.6", 2211 | ] 2212 | 2213 | [[package]] 2214 | name = "windows-core" 2215 | version = "0.52.0" 2216 | source = "registry+https://github.com/rust-lang/crates.io-index" 2217 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 2218 | dependencies = [ 2219 | "windows-targets 0.52.6", 2220 | ] 2221 | 2222 | [[package]] 2223 | name = "windows-sys" 2224 | version = "0.45.0" 2225 | source = "registry+https://github.com/rust-lang/crates.io-index" 2226 | checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" 2227 | dependencies = [ 2228 | "windows-targets 0.42.2", 2229 | ] 2230 | 2231 | [[package]] 2232 | name = "windows-sys" 2233 | version = "0.52.0" 2234 | source = "registry+https://github.com/rust-lang/crates.io-index" 2235 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 2236 | dependencies = [ 2237 | "windows-targets 0.52.6", 2238 | ] 2239 | 2240 | [[package]] 2241 | name = "windows-sys" 2242 | version = "0.59.0" 2243 | source = "registry+https://github.com/rust-lang/crates.io-index" 2244 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 2245 | dependencies = [ 2246 | "windows-targets 0.52.6", 2247 | ] 2248 | 2249 | [[package]] 2250 | name = "windows-targets" 2251 | version = "0.42.2" 2252 | source = "registry+https://github.com/rust-lang/crates.io-index" 2253 | checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" 2254 | dependencies = [ 2255 | "windows_aarch64_gnullvm 0.42.2", 2256 | "windows_aarch64_msvc 0.42.2", 2257 | "windows_i686_gnu 0.42.2", 2258 | "windows_i686_msvc 0.42.2", 2259 | "windows_x86_64_gnu 0.42.2", 2260 | "windows_x86_64_gnullvm 0.42.2", 2261 | "windows_x86_64_msvc 0.42.2", 2262 | ] 2263 | 2264 | [[package]] 2265 | name = "windows-targets" 2266 | version = "0.48.5" 2267 | source = "registry+https://github.com/rust-lang/crates.io-index" 2268 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 2269 | dependencies = [ 2270 | "windows_aarch64_gnullvm 0.48.5", 2271 | "windows_aarch64_msvc 0.48.5", 2272 | "windows_i686_gnu 0.48.5", 2273 | "windows_i686_msvc 0.48.5", 2274 | "windows_x86_64_gnu 0.48.5", 2275 | "windows_x86_64_gnullvm 0.48.5", 2276 | "windows_x86_64_msvc 0.48.5", 2277 | ] 2278 | 2279 | [[package]] 2280 | name = "windows-targets" 2281 | version = "0.52.6" 2282 | source = "registry+https://github.com/rust-lang/crates.io-index" 2283 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 2284 | dependencies = [ 2285 | "windows_aarch64_gnullvm 0.52.6", 2286 | "windows_aarch64_msvc 0.52.6", 2287 | "windows_i686_gnu 0.52.6", 2288 | "windows_i686_gnullvm", 2289 | "windows_i686_msvc 0.52.6", 2290 | "windows_x86_64_gnu 0.52.6", 2291 | "windows_x86_64_gnullvm 0.52.6", 2292 | "windows_x86_64_msvc 0.52.6", 2293 | ] 2294 | 2295 | [[package]] 2296 | name = "windows_aarch64_gnullvm" 2297 | version = "0.42.2" 2298 | source = "registry+https://github.com/rust-lang/crates.io-index" 2299 | checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" 2300 | 2301 | [[package]] 2302 | name = "windows_aarch64_gnullvm" 2303 | version = "0.48.5" 2304 | source = "registry+https://github.com/rust-lang/crates.io-index" 2305 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 2306 | 2307 | [[package]] 2308 | name = "windows_aarch64_gnullvm" 2309 | version = "0.52.6" 2310 | source = "registry+https://github.com/rust-lang/crates.io-index" 2311 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 2312 | 2313 | [[package]] 2314 | name = "windows_aarch64_msvc" 2315 | version = "0.42.2" 2316 | source = "registry+https://github.com/rust-lang/crates.io-index" 2317 | checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" 2318 | 2319 | [[package]] 2320 | name = "windows_aarch64_msvc" 2321 | version = "0.48.5" 2322 | source = "registry+https://github.com/rust-lang/crates.io-index" 2323 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 2324 | 2325 | [[package]] 2326 | name = "windows_aarch64_msvc" 2327 | version = "0.52.6" 2328 | source = "registry+https://github.com/rust-lang/crates.io-index" 2329 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 2330 | 2331 | [[package]] 2332 | name = "windows_i686_gnu" 2333 | version = "0.42.2" 2334 | source = "registry+https://github.com/rust-lang/crates.io-index" 2335 | checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" 2336 | 2337 | [[package]] 2338 | name = "windows_i686_gnu" 2339 | version = "0.48.5" 2340 | source = "registry+https://github.com/rust-lang/crates.io-index" 2341 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 2342 | 2343 | [[package]] 2344 | name = "windows_i686_gnu" 2345 | version = "0.52.6" 2346 | source = "registry+https://github.com/rust-lang/crates.io-index" 2347 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 2348 | 2349 | [[package]] 2350 | name = "windows_i686_gnullvm" 2351 | version = "0.52.6" 2352 | source = "registry+https://github.com/rust-lang/crates.io-index" 2353 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 2354 | 2355 | [[package]] 2356 | name = "windows_i686_msvc" 2357 | version = "0.42.2" 2358 | source = "registry+https://github.com/rust-lang/crates.io-index" 2359 | checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" 2360 | 2361 | [[package]] 2362 | name = "windows_i686_msvc" 2363 | version = "0.48.5" 2364 | source = "registry+https://github.com/rust-lang/crates.io-index" 2365 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 2366 | 2367 | [[package]] 2368 | name = "windows_i686_msvc" 2369 | version = "0.52.6" 2370 | source = "registry+https://github.com/rust-lang/crates.io-index" 2371 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 2372 | 2373 | [[package]] 2374 | name = "windows_x86_64_gnu" 2375 | version = "0.42.2" 2376 | source = "registry+https://github.com/rust-lang/crates.io-index" 2377 | checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" 2378 | 2379 | [[package]] 2380 | name = "windows_x86_64_gnu" 2381 | version = "0.48.5" 2382 | source = "registry+https://github.com/rust-lang/crates.io-index" 2383 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 2384 | 2385 | [[package]] 2386 | name = "windows_x86_64_gnu" 2387 | version = "0.52.6" 2388 | source = "registry+https://github.com/rust-lang/crates.io-index" 2389 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 2390 | 2391 | [[package]] 2392 | name = "windows_x86_64_gnullvm" 2393 | version = "0.42.2" 2394 | source = "registry+https://github.com/rust-lang/crates.io-index" 2395 | checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" 2396 | 2397 | [[package]] 2398 | name = "windows_x86_64_gnullvm" 2399 | version = "0.48.5" 2400 | source = "registry+https://github.com/rust-lang/crates.io-index" 2401 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 2402 | 2403 | [[package]] 2404 | name = "windows_x86_64_gnullvm" 2405 | version = "0.52.6" 2406 | source = "registry+https://github.com/rust-lang/crates.io-index" 2407 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 2408 | 2409 | [[package]] 2410 | name = "windows_x86_64_msvc" 2411 | version = "0.42.2" 2412 | source = "registry+https://github.com/rust-lang/crates.io-index" 2413 | checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" 2414 | 2415 | [[package]] 2416 | name = "windows_x86_64_msvc" 2417 | version = "0.48.5" 2418 | source = "registry+https://github.com/rust-lang/crates.io-index" 2419 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 2420 | 2421 | [[package]] 2422 | name = "windows_x86_64_msvc" 2423 | version = "0.52.6" 2424 | source = "registry+https://github.com/rust-lang/crates.io-index" 2425 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 2426 | 2427 | [[package]] 2428 | name = "winit" 2429 | version = "0.30.9" 2430 | source = "registry+https://github.com/rust-lang/crates.io-index" 2431 | checksum = "a809eacf18c8eca8b6635091543f02a5a06ddf3dad846398795460e6e0ae3cc0" 2432 | dependencies = [ 2433 | "ahash", 2434 | "android-activity", 2435 | "atomic-waker", 2436 | "bitflags 2.9.0", 2437 | "block2", 2438 | "bytemuck", 2439 | "calloop", 2440 | "cfg_aliases 0.2.1", 2441 | "concurrent-queue", 2442 | "core-foundation", 2443 | "core-graphics", 2444 | "cursor-icon", 2445 | "dpi", 2446 | "js-sys", 2447 | "libc", 2448 | "memmap2", 2449 | "ndk", 2450 | "objc2", 2451 | "objc2-app-kit", 2452 | "objc2-foundation", 2453 | "objc2-ui-kit", 2454 | "orbclient", 2455 | "percent-encoding", 2456 | "pin-project", 2457 | "raw-window-handle", 2458 | "redox_syscall 0.4.1", 2459 | "rustix", 2460 | "sctk-adwaita", 2461 | "smithay-client-toolkit", 2462 | "smol_str", 2463 | "tracing", 2464 | "unicode-segmentation", 2465 | "wasm-bindgen", 2466 | "wasm-bindgen-futures", 2467 | "wayland-backend", 2468 | "wayland-client", 2469 | "wayland-protocols", 2470 | "wayland-protocols-plasma", 2471 | "web-sys", 2472 | "web-time", 2473 | "windows-sys 0.52.0", 2474 | "x11-dl", 2475 | "x11rb", 2476 | "xkbcommon-dl", 2477 | ] 2478 | 2479 | [[package]] 2480 | name = "winnow" 2481 | version = "0.7.4" 2482 | source = "registry+https://github.com/rust-lang/crates.io-index" 2483 | checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" 2484 | dependencies = [ 2485 | "memchr", 2486 | ] 2487 | 2488 | [[package]] 2489 | name = "x11-dl" 2490 | version = "2.21.0" 2491 | source = "registry+https://github.com/rust-lang/crates.io-index" 2492 | checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" 2493 | dependencies = [ 2494 | "libc", 2495 | "once_cell", 2496 | "pkg-config", 2497 | ] 2498 | 2499 | [[package]] 2500 | name = "x11rb" 2501 | version = "0.13.1" 2502 | source = "registry+https://github.com/rust-lang/crates.io-index" 2503 | checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" 2504 | dependencies = [ 2505 | "as-raw-xcb-connection", 2506 | "gethostname", 2507 | "libc", 2508 | "libloading 0.8.6", 2509 | "once_cell", 2510 | "rustix", 2511 | "x11rb-protocol", 2512 | ] 2513 | 2514 | [[package]] 2515 | name = "x11rb-protocol" 2516 | version = "0.13.1" 2517 | source = "registry+https://github.com/rust-lang/crates.io-index" 2518 | checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" 2519 | 2520 | [[package]] 2521 | name = "xcursor" 2522 | version = "0.3.8" 2523 | source = "registry+https://github.com/rust-lang/crates.io-index" 2524 | checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61" 2525 | 2526 | [[package]] 2527 | name = "xkbcommon-dl" 2528 | version = "0.4.2" 2529 | source = "registry+https://github.com/rust-lang/crates.io-index" 2530 | checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" 2531 | dependencies = [ 2532 | "bitflags 2.9.0", 2533 | "dlib", 2534 | "log", 2535 | "once_cell", 2536 | "xkeysym", 2537 | ] 2538 | 2539 | [[package]] 2540 | name = "xkeysym" 2541 | version = "0.2.1" 2542 | source = "registry+https://github.com/rust-lang/crates.io-index" 2543 | checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" 2544 | 2545 | [[package]] 2546 | name = "xml-rs" 2547 | version = "0.8.25" 2548 | source = "registry+https://github.com/rust-lang/crates.io-index" 2549 | checksum = "c5b940ebc25896e71dd073bad2dbaa2abfe97b0a391415e22ad1326d9c54e3c4" 2550 | 2551 | [[package]] 2552 | name = "zerocopy" 2553 | version = "0.7.35" 2554 | source = "registry+https://github.com/rust-lang/crates.io-index" 2555 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 2556 | dependencies = [ 2557 | "zerocopy-derive", 2558 | ] 2559 | 2560 | [[package]] 2561 | name = "zerocopy-derive" 2562 | version = "0.7.35" 2563 | source = "registry+https://github.com/rust-lang/crates.io-index" 2564 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 2565 | dependencies = [ 2566 | "proc-macro2", 2567 | "quote", 2568 | "syn 2.0.100", 2569 | ] 2570 | --------------------------------------------------------------------------------