From c2fb2ae08dfb991aa957ddcdca3ace94d538b35f Mon Sep 17 00:00:00 2001 From: vE5li Date: Wed, 8 May 2024 20:43:20 +0200 Subject: [PATCH] Create ragnarok_formats crate with byte convertable structs --- Cargo.lock | 24 +- Cargo.toml | 1 + korangar/Cargo.toml | 2 +- korangar/src/graphics/cameras/debug.rs | 2 +- korangar/src/graphics/cameras/mod.rs | 3 +- korangar/src/graphics/cameras/player.rs | 2 +- korangar/src/graphics/cameras/shadow.rs | 2 +- korangar/src/graphics/cameras/start.rs | 2 +- korangar/src/graphics/color.rs | 13 + korangar/src/graphics/mod.rs | 2 - .../graphics/renderers/deferred/box/mod.rs | 1 + .../src/graphics/renderers/deferred/mod.rs | 2 + korangar/src/graphics/transform.rs | 21 - korangar/src/loaders/action/mod.rs | 90 +---- .../src/loaders/archive/native/assettable.rs | 15 - .../src/loaders/archive/native/builder.rs | 12 +- .../loaders/archive/native/filetablerow.rs | 12 - korangar/src/loaders/archive/native/header.rs | 27 -- korangar/src/loaders/archive/native/mod.rs | 30 +- korangar/src/loaders/color.rs | 23 -- korangar/src/loaders/effect/mod.rs | 182 ++++----- korangar/src/loaders/error.rs | 10 + korangar/src/loaders/gamefile/mod.rs | 7 +- korangar/src/loaders/map/data.rs | 170 -------- korangar/src/loaders/map/mod.rs | 80 ++-- korangar/src/loaders/map/resource.rs | 147 ------- korangar/src/loaders/map/vertices.rs | 7 +- korangar/src/loaders/mod.rs | 8 +- korangar/src/loaders/model/mod.rs | 143 +------ korangar/src/loaders/sprite/mod.rs | 145 +------ korangar/src/loaders/texture/mod.rs | 9 +- korangar/src/world/effect/mod.rs | 35 +- korangar/src/world/entity/mod.rs | 19 +- korangar/src/world/light/mod.rs | 41 +- korangar/src/world/map/mod.rs | 16 +- korangar/src/world/map/tile.rs | 60 +-- korangar/src/world/model/mod.rs | 7 +- korangar/src/world/model/node.rs | 5 +- korangar/src/world/object/mod.rs | 1 + korangar/src/world/sound/mod.rs | 37 +- ragnarok_formats/Cargo.toml | 13 + ragnarok_formats/README.md | 3 + ragnarok_formats/src/action.rs | 84 ++++ ragnarok_formats/src/archive.rs | 55 +++ ragnarok_formats/src/color.rs | 18 + ragnarok_formats/src/effect.rs | 58 +++ ragnarok_formats/src/lib.rs | 16 + ragnarok_formats/src/map.rs | 369 ++++++++++++++++++ ragnarok_formats/src/model.rs | 133 +++++++ ragnarok_formats/src/signature.rs | 38 ++ ragnarok_formats/src/sprite.rs | 115 ++++++ ragnarok_formats/src/transform.rs | 66 ++++ .../src}/version.rs | 2 + ragnarok_packets/src/lib.rs | 1 - ragnarok_procedural/src/helper.rs | 4 +- ragnarok_procedural/src/lib.rs | 3 - 56 files changed, 1267 insertions(+), 1126 deletions(-) delete mode 100644 korangar/src/loaders/archive/native/assettable.rs delete mode 100644 korangar/src/loaders/archive/native/filetablerow.rs delete mode 100644 korangar/src/loaders/archive/native/header.rs delete mode 100644 korangar/src/loaders/color.rs create mode 100644 korangar/src/loaders/error.rs delete mode 100644 korangar/src/loaders/map/data.rs delete mode 100644 korangar/src/loaders/map/resource.rs create mode 100644 ragnarok_formats/Cargo.toml create mode 100644 ragnarok_formats/README.md create mode 100644 ragnarok_formats/src/action.rs create mode 100644 ragnarok_formats/src/archive.rs create mode 100644 ragnarok_formats/src/color.rs create mode 100644 ragnarok_formats/src/effect.rs create mode 100644 ragnarok_formats/src/lib.rs create mode 100644 ragnarok_formats/src/map.rs create mode 100644 ragnarok_formats/src/model.rs create mode 100644 ragnarok_formats/src/signature.rs create mode 100644 ragnarok_formats/src/sprite.rs create mode 100644 ragnarok_formats/src/transform.rs rename {korangar/src/loaders => ragnarok_formats/src}/version.rs (99%) diff --git a/Cargo.lock b/Cargo.lock index 8378afe82..62830a808 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1006,6 +1006,7 @@ dependencies = [ "option-ext", "pathfinding", "ragnarok_bytes", + "ragnarok_formats", "ragnarok_packets", "rand 0.8.5", "random_color", @@ -1017,7 +1018,6 @@ dependencies = [ "tokio", "vulkano", "vulkano-shaders", - "vulkano-win", "walkdir", "winit", "xml-rs", @@ -1708,6 +1708,16 @@ dependencies = [ "ragnarok_procedural", ] +[[package]] +name = "ragnarok_formats" +version = "0.1.0" +dependencies = [ + "bitflags 2.5.0", + "cgmath", + "korangar_interface", + "ragnarok_bytes", +] + [[package]] name = "ragnarok_packets" version = "0.1.0" @@ -2640,18 +2650,6 @@ dependencies = [ "vulkano", ] -[[package]] -name = "vulkano-win" -version = "0.33.0" -source = "git+https://github.com/vulkano-rs/vulkano.git?rev=db3df4e55f80c137ea6187250957eb92c2291627#db3df4e55f80c137ea6187250957eb92c2291627" -dependencies = [ - "core-graphics-types", - "objc", - "raw-window-handle", - "vulkano", - "winit", -] - [[package]] name = "walkdir" version = "2.5.0" diff --git a/Cargo.toml b/Cargo.toml index b751646d5..7629bc2b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ korangar_interface = { path = "korangar_interface" } korangar_networking = { path = "korangar_networking" } num = "0.4.1" ragnarok_bytes = { path = "ragnarok_bytes" } +ragnarok_formats = { path = "ragnarok_formats" } ragnarok_packets = { path = "ragnarok_packets" } ragnarok_procedural = { path = "ragnarok_procedural" } serde = "1.0.137" diff --git a/korangar/Cargo.toml b/korangar/Cargo.toml index 55c1097ec..d3937affb 100644 --- a/korangar/Cargo.toml +++ b/korangar/Cargo.toml @@ -21,6 +21,7 @@ option-ext = "0.2.0" pathfinding = "2.2.2" korangar_debug = { workspace = true, optional = true } ragnarok_bytes = { workspace = true, features = ["derive", "cgmath"] } +ragnarok_formats = { workspace = true, features = ["interface"] } ragnarok_packets = { workspace = true, features = ["derive", "interface", "packet-to-prototype-element"] } rand = "0.8.5" random_color = { version = "0.6.1", optional = true } @@ -32,7 +33,6 @@ serde-xml-rs = "0.6.0" tokio = { version = "1.37.0", features = ["full"] } vulkano = { git = "https://github.com/vulkano-rs/vulkano.git", rev = "db3df4e55f80c137ea6187250957eb92c2291627" } vulkano-shaders = { git = "https://github.com/vulkano-rs/vulkano.git", rev = "db3df4e55f80c137ea6187250957eb92c2291627" } -vulkano-win = { git = "https://github.com/vulkano-rs/vulkano.git", rev = "db3df4e55f80c137ea6187250957eb92c2291627" } walkdir = "2" winit = "0.28.7" xml-rs = "0.8.0" diff --git a/korangar/src/graphics/cameras/debug.rs b/korangar/src/graphics/cameras/debug.rs index adaeb5365..9f0c66fcc 100644 --- a/korangar/src/graphics/cameras/debug.rs +++ b/korangar/src/graphics/cameras/debug.rs @@ -1,9 +1,9 @@ use std::f32::consts::FRAC_PI_4; use cgmath::{Array, EuclideanSpace, InnerSpace, Matrix4, MetricSpace, Point3, Rad, SquareMatrix, Vector2, Vector3, Vector4}; +use ragnarok_formats::transform::Transform; use super::Camera; -use crate::graphics::Transform; use crate::interface::layout::{ScreenPosition, ScreenSize}; const LOOK_AROUND_SPEED: f32 = 0.005; diff --git a/korangar/src/graphics/cameras/mod.rs b/korangar/src/graphics/cameras/mod.rs index 25833b4cb..887619d1a 100644 --- a/korangar/src/graphics/cameras/mod.rs +++ b/korangar/src/graphics/cameras/mod.rs @@ -5,13 +5,14 @@ mod shadow; mod start; use cgmath::{InnerSpace, Matrix4, Vector2, Vector3, Vector4}; +use ragnarok_formats::transform::Transform; #[cfg(feature = "debug")] pub use self::debug::DebugCamera; pub use self::player::PlayerCamera; pub use self::shadow::ShadowCamera; pub use self::start::StartCamera; -use crate::graphics::{SmoothedValue, Transform}; +use crate::graphics::SmoothedValue; use crate::interface::layout::{ScreenPosition, ScreenSize}; fn direction(vector: Vector2) -> usize { diff --git a/korangar/src/graphics/cameras/player.rs b/korangar/src/graphics/cameras/player.rs index d3ac26c85..1826bcacf 100644 --- a/korangar/src/graphics/cameras/player.rs +++ b/korangar/src/graphics/cameras/player.rs @@ -1,9 +1,9 @@ use std::f32::consts::FRAC_PI_2; use cgmath::{Array, EuclideanSpace, InnerSpace, Matrix4, MetricSpace, Point3, Rad, SquareMatrix, Vector2, Vector3, Vector4}; +use ragnarok_formats::transform::Transform; use super::{Camera, SmoothedValue}; -use crate::graphics::Transform; use crate::interface::layout::{ScreenPosition, ScreenSize}; const ZOOM_SPEED: f32 = 2.0; diff --git a/korangar/src/graphics/cameras/shadow.rs b/korangar/src/graphics/cameras/shadow.rs index 1a31e5a42..88499be01 100644 --- a/korangar/src/graphics/cameras/shadow.rs +++ b/korangar/src/graphics/cameras/shadow.rs @@ -1,7 +1,7 @@ use cgmath::{Array, EuclideanSpace, InnerSpace, Matrix4, MetricSpace, Point3, SquareMatrix, Vector2, Vector3, Vector4}; +use ragnarok_formats::transform::Transform; use super::Camera; -use crate::graphics::Transform; use crate::interface::layout::{ScreenPosition, ScreenSize}; pub struct ShadowCamera { diff --git a/korangar/src/graphics/cameras/start.rs b/korangar/src/graphics/cameras/start.rs index a30df064b..456c9fece 100644 --- a/korangar/src/graphics/cameras/start.rs +++ b/korangar/src/graphics/cameras/start.rs @@ -1,9 +1,9 @@ use std::f32::consts::{FRAC_PI_2, FRAC_PI_4}; use cgmath::{Array, EuclideanSpace, InnerSpace, Matrix4, MetricSpace, Point3, Rad, SquareMatrix, Vector2, Vector3, Vector4}; +use ragnarok_formats::transform::Transform; use super::Camera; -use crate::graphics::Transform; use crate::interface::layout::{ScreenPosition, ScreenSize}; const DEFAULT_ZOOM: f32 = 150.0; diff --git a/korangar/src/graphics/color.rs b/korangar/src/graphics/color.rs index 97f4656e2..c0bc9dce5 100644 --- a/korangar/src/graphics/color.rs +++ b/korangar/src/graphics/color.rs @@ -1,3 +1,4 @@ +use ragnarok_formats::color::ColorRGB; use serde::{Deserialize, Serialize}; #[derive(Copy, Clone, Debug, Default, PartialEq, Serialize, Deserialize)] @@ -116,3 +117,15 @@ impl From for [f32; 4] { [val.red, val.green, val.blue, val.alpha] } } + +impl From for Color { + fn from(value: ColorRGB) -> Self { + let ColorRGB { red, blue, green } = value; + Color { + red, + green, + blue, + alpha: 1.0, + } + } +} diff --git a/korangar/src/graphics/mod.rs b/korangar/src/graphics/mod.rs index 5f12bdd66..2c6bd0b51 100644 --- a/korangar/src/graphics/mod.rs +++ b/korangar/src/graphics/mod.rs @@ -5,7 +5,6 @@ mod particles; mod renderers; mod settings; mod smoothed; -mod transform; mod vertices; use std::sync::Arc; @@ -22,7 +21,6 @@ pub use self::particles::*; pub use self::renderers::*; pub use self::settings::GraphicsSettings; pub use self::smoothed::SmoothedValue; -pub use self::transform::Transform; pub use self::vertices::*; pub type CommandBuilder = AutoCommandBufferBuilder, MemoryAllocator>; diff --git a/korangar/src/graphics/renderers/deferred/box/mod.rs b/korangar/src/graphics/renderers/deferred/box/mod.rs index 8a03b8834..570a71e56 100644 --- a/korangar/src/graphics/renderers/deferred/box/mod.rs +++ b/korangar/src/graphics/renderers/deferred/box/mod.rs @@ -4,6 +4,7 @@ fragment_shader!("src/graphics/renderers/deferred/box/fragment_shader.glsl"); use std::sync::Arc; use cgmath::Vector3; +use ragnarok_formats::transform::Transform; use vulkano::descriptor_set::WriteDescriptorSet; use vulkano::device::{Device, DeviceOwned}; use vulkano::pipeline::graphics::input_assembly::PrimitiveTopology; diff --git a/korangar/src/graphics/renderers/deferred/mod.rs b/korangar/src/graphics/renderers/deferred/mod.rs index 80602189f..a5856cdff 100644 --- a/korangar/src/graphics/renderers/deferred/mod.rs +++ b/korangar/src/graphics/renderers/deferred/mod.rs @@ -21,6 +21,8 @@ use std::sync::Arc; use cgmath::SquareMatrix; use cgmath::{Matrix4, Vector2, Vector3}; use korangar_interface::application::FontSizeTrait; +#[cfg(feature = "debug")] +use ragnarok_formats::transform::Transform; use ragnarok_packets::EntityId; use vulkano::device::{DeviceOwned, Queue}; use vulkano::format::Format; diff --git a/korangar/src/graphics/transform.rs b/korangar/src/graphics/transform.rs index 7e5898fe5..8a8f05a53 100644 --- a/korangar/src/graphics/transform.rs +++ b/korangar/src/graphics/transform.rs @@ -4,27 +4,6 @@ use cgmath::{Deg, Rad, Vector3}; use korangar_interface::elements::PrototypeElement; use ragnarok_bytes::{ByteStream, ConversionResult, ConversionResultExt, FromBytes}; -#[derive(Copy, Clone, Debug, PrototypeElement)] -pub struct Transform { - pub position: Vector3, - #[hidden_element] // TODO: unhide - pub rotation: Vector3>, - pub scale: Vector3, -} - -impl FromBytes for Transform { - fn from_bytes(byte_stream: &mut ByteStream) -> ConversionResult { - let mut position = >::from_bytes(byte_stream).trace::()?; - let rotation = >::from_bytes(byte_stream).trace::()?; - let scale = >::from_bytes(byte_stream).trace::()?; - - // TODO: make this nicer - position.y = -position.y; - - Ok(Transform::from(position, rotation.map(Deg), scale)) - } -} - impl Transform { pub fn from(position: Vector3, rotation: Vector3>, scale: Vector3) -> Self { let rotation = rotation.map(|degrees| degrees.into()); diff --git a/korangar/src/loaders/action/mod.rs b/korangar/src/loaders/action/mod.rs index debf30f62..6c9463437 100644 --- a/korangar/src/loaders/action/mod.rs +++ b/korangar/src/loaders/action/mod.rs @@ -7,16 +7,18 @@ use derive_new::new; #[cfg(feature = "debug")] use korangar_debug::logging::{print_debug, Colorize, Timer}; use korangar_interface::elements::PrototypeElement; -use ragnarok_bytes::{ByteConvertable, ByteStream, FromBytes}; +use ragnarok_bytes::{ByteStream, FromBytes}; +use ragnarok_formats::action::{Action, ActionsData}; +use ragnarok_formats::version::InternalVersion; use ragnarok_packets::ClientTick; use vulkano::image::view::ImageView; -use super::version::InternalVersion; +use super::error::LoadError; use super::Sprite; use crate::graphics::{Color, Renderer, SpriteRenderer}; use crate::interface::application::InterfaceSettings; use crate::interface::layout::{ScreenClip, ScreenPosition, ScreenSize}; -use crate::loaders::{GameFileLoader, MinorFirst, Version, FALLBACK_ACTIONS_FILE}; +use crate::loaders::{GameFileLoader, FALLBACK_ACTIONS_FILE}; #[derive(Clone, Debug, new)] pub struct AnimationState { @@ -195,95 +197,19 @@ impl Actions { } } -#[derive(Debug, Clone, ByteConvertable, PrototypeElement)] -struct SpriteClip { - pub position: Vector2, - pub sprite_number: u32, - pub mirror_on: u32, - #[version_equals_or_above(2, 0)] - pub color: Option, - #[version_smaller(2, 4)] - pub zoom: Option, - #[version_equals_or_above(2, 4)] - pub zoom2: Option>, - #[version_equals_or_above(2, 0)] - pub angle: Option, - #[version_equals_or_above(2, 0)] - pub sprite_type: Option, - #[version_equals_or_above(2, 5)] - pub size: Option>, -} - -#[derive(Debug, Clone, ByteConvertable, PrototypeElement)] -struct AttachPoint { - pub ignored: u32, - pub position: Vector2, - pub attribute: u32, -} - -#[derive(Debug, Clone, ByteConvertable, PrototypeElement)] -struct Motion { - pub range1: [i32; 4], // maybe just skip this? - pub range2: [i32; 4], // maybe just skip this? - pub sprite_clip_count: u32, - #[repeating(self.sprite_clip_count)] - pub sprite_clips: Vec, - #[version_equals_or_above(2, 0)] - pub event_id: Option, // if version == 2.0 this maybe needs to be set to None ? - // (after it is parsed) - #[version_equals_or_above(2, 3)] - pub attach_point_count: Option, - #[repeating(self.attach_point_count.unwrap_or_default())] - pub attach_points: Vec, -} - -#[derive(Debug, Clone, ByteConvertable, PrototypeElement)] -struct Action { - pub motion_count: u32, - #[repeating(self.motion_count)] - pub motions: Vec, -} - -#[derive(Debug, Clone, FromBytes, PrototypeElement)] -struct Event { - #[length_hint(40)] - pub name: String, -} - -#[derive(Debug, Clone, FromBytes, PrototypeElement)] -struct ActionsData { - #[version] - pub version: Version, - pub action_count: u16, - pub reserved: [u8; 10], - #[repeating(self.action_count)] - pub actions: Vec, - #[version_equals_or_above(2, 1)] - pub event_count: Option, - #[repeating(self.event_count.unwrap_or_default())] - pub events: Vec, - #[version_equals_or_above(2, 2)] - #[repeating(self.action_count)] - pub delays: Option>, -} - #[derive(Default)] pub struct ActionLoader { cache: HashMap>, } impl ActionLoader { - fn load(&mut self, path: &str, game_file_loader: &mut GameFileLoader) -> Result, String> { + fn load(&mut self, path: &str, game_file_loader: &mut GameFileLoader) -> Result, LoadError> { #[cfg(feature = "debug")] let timer = Timer::new_dynamic(format!("load actions from {}", path.magenta())); - let bytes = game_file_loader.get(&format!("data\\sprite\\{path}"))?; + let bytes = game_file_loader.get(&format!("data\\sprite\\{path}")).map_err(LoadError::File)?; let mut byte_stream: ByteStream> = ByteStream::without_metadata(&bytes); - if <[u8; 2]>::from_bytes(&mut byte_stream).unwrap() != [b'A', b'C'] { - return Err(format!("failed to read magic number from {path}")); - } - let actions_data = match ActionsData::from_bytes(&mut byte_stream) { Ok(actions_data) => actions_data, Err(_error) => { @@ -319,7 +245,7 @@ impl ActionLoader { Ok(sprite) } - pub fn get(&mut self, path: &str, game_file_loader: &mut GameFileLoader) -> Result, String> { + pub fn get(&mut self, path: &str, game_file_loader: &mut GameFileLoader) -> Result, LoadError> { match self.cache.get(path) { Some(sprite) => Ok(sprite.clone()), None => self.load(path, game_file_loader), diff --git a/korangar/src/loaders/archive/native/assettable.rs b/korangar/src/loaders/archive/native/assettable.rs deleted file mode 100644 index 01cc2bc3f..000000000 --- a/korangar/src/loaders/archive/native/assettable.rs +++ /dev/null @@ -1,15 +0,0 @@ -use derive_new::new; -use ragnarok_bytes::{ByteConvertable, FixedByteSize}; - -/// Stores the table of files the parent GRF is holding. -#[derive(Clone, ByteConvertable, FixedByteSize, new)] -pub(super) struct AssetTable { - compressed_size: u32, - uncompressed_size: u32, -} - -impl AssetTable { - pub(super) fn get_compressed_size(&self) -> usize { - self.compressed_size as usize - } -} diff --git a/korangar/src/loaders/archive/native/builder.rs b/korangar/src/loaders/archive/native/builder.rs index 4ea7c666d..bc6d8304b 100644 --- a/korangar/src/loaders/archive/native/builder.rs +++ b/korangar/src/loaders/archive/native/builder.rs @@ -5,12 +5,10 @@ use std::path::{Path, PathBuf}; use ragnarok_bytes::ToBytes; +use ragnarok_formats::archive::{AssetTable, FileTableRow, Header}; use yazi::{compress, CompressionLevel, Format}; -use super::assettable::AssetTable; -use super::filetablerow::FileTableRow; -use super::header::Header; -use super::{FileTable, MAGIC_BYTES}; +use super::FileTable; use crate::loaders::archive::Writable; pub struct NativeArchiveBuilder { @@ -61,7 +59,6 @@ impl Writable for NativeArchiveBuilder { let mut bytes = Vec::new(); - bytes.extend_from_slice(MAGIC_BYTES); bytes.extend_from_slice(&file_header.to_bytes().unwrap()); bytes.extend_from_slice(&self.data); @@ -72,7 +69,10 @@ impl Writable for NativeArchiveBuilder { } let compressed_file_information_data = compress(&file_table_data, Format::Zlib, CompressionLevel::Default).unwrap(); - let file_table = AssetTable::new(compressed_file_information_data.len() as u32, file_table_data.len() as u32); + let file_table = AssetTable { + compressed_size: compressed_file_information_data.len() as u32, + uncompressed_size: file_table_data.len() as u32, + }; bytes.extend_from_slice(&file_table.to_bytes().unwrap()); bytes.extend_from_slice(&compressed_file_information_data); diff --git a/korangar/src/loaders/archive/native/filetablerow.rs b/korangar/src/loaders/archive/native/filetablerow.rs deleted file mode 100644 index ba7d9b503..000000000 --- a/korangar/src/loaders/archive/native/filetablerow.rs +++ /dev/null @@ -1,12 +0,0 @@ -use ragnarok_bytes::ByteConvertable; - -/// Represents file information about each of the files stored in the GRF. -#[derive(Clone, Debug, ByteConvertable)] -pub(super) struct FileTableRow { - pub file_name: String, - pub compressed_size: u32, - pub compressed_size_aligned: u32, - pub uncompressed_size: u32, - pub flags: u8, - pub offset: u32, -} diff --git a/korangar/src/loaders/archive/native/header.rs b/korangar/src/loaders/archive/native/header.rs deleted file mode 100644 index dffe7078c..000000000 --- a/korangar/src/loaders/archive/native/header.rs +++ /dev/null @@ -1,27 +0,0 @@ -use derive_new::new; -use ragnarok_bytes::{ByteConvertable, FixedByteSize}; - -/// Represents the Header of the GRF file. -#[derive(Clone, ByteConvertable, FixedByteSize, new)] -pub(super) struct Header { - #[new(default)] - encryption: [u8; 14], - file_table_offset: u32, - reserved_files: u32, - file_count: u32, - version: u32, -} - -impl Header { - pub fn validate_version(&self) { - assert_eq!(self.version, 0x200, "invalid grf version"); - } - - pub fn get_file_table_offset(&self) -> usize { - self.file_table_offset as usize - } - - pub fn get_file_count(&self) -> usize { - (self.file_count - self.reserved_files) as usize - 7 - } -} diff --git a/korangar/src/loaders/archive/native/mod.rs b/korangar/src/loaders/archive/native/mod.rs index 73bb0f102..23c8f2b66 100644 --- a/korangar/src/loaders/archive/native/mod.rs +++ b/korangar/src/loaders/archive/native/mod.rs @@ -1,8 +1,8 @@ //! A GRF file containing game assets. -mod assettable; +// mod assettable; mod builder; -mod filetablerow; -mod header; +// mod filetablerow; +// mod header; use std::collections::HashMap; use std::fs::File; @@ -12,12 +12,10 @@ use std::path::Path; #[cfg(feature = "debug")] use korangar_debug::logging::{Colorize, Timer}; use ragnarok_bytes::{ByteStream, FixedByteSize, FromBytes}; +use ragnarok_formats::archive::{AssetTable, FileTableRow, Header}; use yazi::{decompress, Format}; -use self::assettable::AssetTable; pub use self::builder::NativeArchiveBuilder; -use self::filetablerow::FileTableRow; -use self::header::Header; use crate::loaders::archive::Archive; /// Represents a GRF file. GRF Files are an archive to store game assets. @@ -30,32 +28,26 @@ pub struct NativeArchive { os_file_handler: File, } -const MAGIC_BYTES: &[u8] = b"Master of Magic\0"; -const UNPACKED_SIZE_OF_MAGIC_STRING: usize = MAGIC_BYTES.len(); - impl Archive for NativeArchive { fn from_path(path: &Path) -> Self { #[cfg(feature = "debug")] let timer = Timer::new_dynamic(format!("load game data from {}", path.display().magenta())); let mut file = File::open(path).unwrap(); - let mut magic_number_buffer = [0u8; UNPACKED_SIZE_OF_MAGIC_STRING]; - file.read_exact(&mut magic_number_buffer).unwrap(); - - // Keeping the convenience of using [`loaders::stream::ByteStream`] - // while being able to read without buffering the entire file. - let mut file_header_buffer = vec![0; Header::size_in_bytes()]; + let mut file_header_buffer = vec![0u8; Header::size_in_bytes()]; file.read_exact(&mut file_header_buffer).unwrap(); let file_header = Header::from_bytes(&mut ByteStream::<()>::without_metadata(&file_header_buffer)).unwrap(); - file_header.validate_version(); - let _ = file.seek(SeekFrom::Current(file_header.get_file_table_offset() as i64)).unwrap(); + // FIX: Add back + // file_header.validate_version(); + + let _ = file.seek(SeekFrom::Current(file_header.file_table_offset as i64)).unwrap(); let mut file_table_buffer = vec![0; AssetTable::size_in_bytes()]; file.read_exact(&mut file_table_buffer).unwrap(); let file_table = AssetTable::from_bytes(&mut ByteStream::<()>::without_metadata(&file_table_buffer)).unwrap(); - let mut compressed_file_table_buffer = vec![0u8; file_table.get_compressed_size()]; + let mut compressed_file_table_buffer = vec![0u8; file_table.compressed_size as usize]; file.read_exact(&mut compressed_file_table_buffer).unwrap(); let (decompressed, _checksum) = decompress(&compressed_file_table_buffer, Format::Zlib).unwrap(); @@ -92,7 +84,7 @@ impl Archive for NativeArchive { return None; } - let position = file_information.offset as u64 + UNPACKED_SIZE_OF_MAGIC_STRING as u64 + Header::size_in_bytes() as u64; + let position = file_information.offset as u64 + Header::size_in_bytes() as u64; self.os_file_handler.seek(SeekFrom::Start(position)).unwrap(); self.os_file_handler.read_exact(&mut compressed_file_buffer).unwrap(); diff --git a/korangar/src/loaders/color.rs b/korangar/src/loaders/color.rs deleted file mode 100644 index b0f987e63..000000000 --- a/korangar/src/loaders/color.rs +++ /dev/null @@ -1,23 +0,0 @@ -use korangar_interface::elements::PrototypeElement; -use ragnarok_bytes::ByteConvertable; - -use crate::graphics::Color; - -#[derive(Clone, Debug, ByteConvertable, PrototypeElement)] -pub struct ColorRGB { - pub red: f32, - pub green: f32, - pub blue: f32, -} - -impl From for Color { - fn from(value: ColorRGB) -> Self { - let ColorRGB { red, blue, green } = value; - Color { - red, - green, - blue, - alpha: 1.0, - } - } -} diff --git a/korangar/src/loaders/effect/mod.rs b/korangar/src/loaders/effect/mod.rs index d117c43df..eba0b00bf 100644 --- a/korangar/src/loaders/effect/mod.rs +++ b/korangar/src/loaders/effect/mod.rs @@ -5,121 +5,73 @@ use cgmath::{Vector2, Vector3}; use derive_new::new; #[cfg(feature = "debug")] use korangar_debug::logging::{Colorize, Timer}; -use korangar_interface::elements::PrototypeElement; use ragnarok_bytes::{ByteStream, FromBytes}; +use ragnarok_formats::effect::{EffectData, Frame}; +use ragnarok_formats::version::InternalVersion; use ragnarok_packets::EntityId; use vulkano::image::view::ImageView; -use super::version::InternalVersion; -use super::{MajorFirst, TextureLoader}; +use super::error::LoadError; +use super::TextureLoader; use crate::graphics::{Camera, Color, DeferredRenderer, Renderer}; -use crate::loaders::{GameFileLoader, Version}; - -#[derive(Debug, FromBytes, PrototypeElement)] -struct TextureName { - #[length_hint(128)] - pub name: String, -} - -#[derive(Debug, Clone, FromBytes, PrototypeElement)] -pub struct Frame { - pub frame_index: i32, - pub frame_type: i32, - pub offset: Vector2, - pub uv: [f32; 8], - pub xy: [f32; 8], - pub texture_index: f32, - pub animation_type: i32, - pub delay: f32, - pub angle: f32, - pub color: [f32; 4], - // Needs to actually set the attachment blend mode of the source alpha - pub source_alpha: i32, - // Needs to actually set the attachment blend mode of the destination alpha - pub destination_alpha: i32, - pub mt_present: i32, -} - -impl Frame { - fn ease_interpolate(start_value: f32, end_value: f32, time: f32, bias: f32, sub_multiplier: f32) -> f32 { - if bias > 0.0 { - (end_value - start_value) * time.powf(1.0 + bias / 5.0) + start_value * sub_multiplier - } else if bias < 0.0 { - (end_value - start_value) * (1.0 - (1.0 - time).powf(-bias / 5.0 + 1.0)) + start_value * sub_multiplier - } else { - (end_value - start_value) * time + start_value * sub_multiplier - } +use crate::loaders::GameFileLoader; + +fn ease_interpolate(start_value: f32, end_value: f32, time: f32, bias: f32, sub_multiplier: f32) -> f32 { + if bias > 0.0 { + (end_value - start_value) * time.powf(1.0 + bias / 5.0) + start_value * sub_multiplier + } else if bias < 0.0 { + (end_value - start_value) * (1.0 - (1.0 - time).powf(-bias / 5.0 + 1.0)) + start_value * sub_multiplier + } else { + (end_value - start_value) * time + start_value * sub_multiplier } - - pub fn interpolate(&self, other: &Frame, frame_index: usize) -> Frame { - let time = 1.0 / (other.frame_index as f32 - self.frame_index as f32) * (frame_index as f32 - self.frame_index as f32); - let sub_mult = 1.0; - - // TODO: angle bias - let angle = Self::ease_interpolate(self.angle, other.angle, time, 0.0, sub_mult); - let color = [ - (other.color[0] - self.color[0]) * time + self.color[0] * sub_mult, - (other.color[1] - self.color[1]) * time + self.color[1] * sub_mult, - (other.color[2] - self.color[2]) * time + self.color[2] * sub_mult, - (other.color[3] - self.color[3]) * time + self.color[3] * sub_mult, - ]; - - let uv = (0..8) - .map(|index| (other.uv[index] - self.uv[index]) * time + self.uv[index] * sub_mult) - .next_chunk() - .unwrap(); - - // TODO: scale bias - let xy = (0..8) - .map(|index| Self::ease_interpolate(self.xy[index], other.xy[index], time, 0.0, sub_mult)) - .next_chunk() - .unwrap(); - - // TODO: additional logic for animation type 2 and 3 - let texture_index = self.texture_index; - - // TODO: bezier curves - let offset_x = (other.offset.x - self.offset.x) * time + self.offset.x * sub_mult; - let offset_y = (other.offset.y - self.offset.y) * time + self.offset.y * sub_mult; - - Frame { - frame_index: frame_index as i32, - frame_type: self.frame_type, - offset: Vector2::new(offset_x, offset_y), - uv, - xy, - texture_index, - animation_type: self.animation_type, - delay: self.delay, - angle, - color, - source_alpha: self.source_alpha, - destination_alpha: self.destination_alpha, - mt_present: self.mt_present, - } - } -} - -#[derive(Debug, FromBytes, PrototypeElement)] -struct LayerData { - pub texture_count: i32, - #[repeating(self.texture_count)] - pub texture_names: Vec, - pub frame_count: i32, - #[repeating(self.frame_count)] - pub frames: Vec, } -#[derive(Debug, FromBytes, PrototypeElement)] -struct EffectData { - pub version: Version, - pub _skip0: [u8; 2], - pub frames_per_second: u32, - pub max_key: u32, - pub layer_count: u32, - pub _skip1: [u8; 16], - #[repeating(self.layer_count)] - pub layers: Vec, +pub fn interpolate(first: &Frame, second: &Frame, frame_index: usize) -> Frame { + let time = 1.0 / (second.frame_index as f32 - first.frame_index as f32) * (frame_index as f32 - first.frame_index as f32); + let sub_mult = 1.0; + + // TODO: angle bias + let angle = ease_interpolate(first.angle, second.angle, time, 0.0, sub_mult); + let color = [ + (second.color[0] - first.color[0]) * time + first.color[0] * sub_mult, + (second.color[1] - first.color[1]) * time + first.color[1] * sub_mult, + (second.color[2] - first.color[2]) * time + first.color[2] * sub_mult, + (second.color[3] - first.color[3]) * time + first.color[3] * sub_mult, + ]; + + let uv = (0..8) + .map(|index| (second.uv[index] - first.uv[index]) * time + first.uv[index] * sub_mult) + .next_chunk() + .unwrap(); + + // TODO: scale bias + let xy = (0..8) + .map(|index| ease_interpolate(first.xy[index], second.xy[index], time, 0.0, sub_mult)) + .next_chunk() + .unwrap(); + + // TODO: additional logic for animation type 2 and 3 + let texture_index = first.texture_index; + + // TODO: bezier curves + let offset_x = (second.offset.x - first.offset.x) * time + first.offset.x * sub_mult; + let offset_y = (second.offset.y - first.offset.y) * time + first.offset.y * sub_mult; + + Frame { + frame_index: frame_index as i32, + frame_type: first.frame_type, + offset: Vector2::new(offset_x, offset_y), + uv, + xy, + texture_index, + animation_type: first.animation_type, + delay: first.delay, + angle, + color, + source_alpha: first.source_alpha, + destination_alpha: first.destination_alpha, + mt_present: first.mt_present, + } } #[derive(Default)] @@ -137,7 +89,7 @@ impl Layer { fn interpolate(&self, frame_timer: &FrameTimer) -> Option { if let Some(frame_index) = self.indices[frame_timer.current_frame] { if let Some(next_frame) = self.frames.get(frame_index + 2) { - return Some(self.frames[frame_index].interpolate(next_frame, frame_timer.current_frame)); + return Some(interpolate(&self.frames[frame_index], next_frame, frame_timer.current_frame)); } else { return Some(self.frames[frame_index].clone()); } @@ -247,19 +199,17 @@ impl EffectLoader { path: &str, game_file_loader: &mut GameFileLoader, texture_loader: &mut TextureLoader, - ) -> Result, String> { + ) -> Result, LoadError> { #[cfg(feature = "debug")] let timer = Timer::new_dynamic(format!("load effect from {}", path.magenta())); - let bytes = game_file_loader.get(&format!("data\\texture\\effect\\{path}"))?; + let bytes = game_file_loader + .get(&format!("data\\texture\\effect\\{path}")) + .map_err(LoadError::File)?; let mut byte_stream: ByteStream> = ByteStream::without_metadata(&bytes); - if <[u8; 4]>::from_bytes(&mut byte_stream).unwrap() != [b'S', b'T', b'R', b'M'] { - return Err(format!("failed to read magic number from {path}")); - } - // TODO: Add fallback - let effect_data = EffectData::from_bytes(&mut byte_stream).unwrap(); + let effect_data = EffectData::from_bytes(&mut byte_stream).map_err(LoadError::Conversion)?; let prefix = match path.chars().rev().position(|character| character == '\\') { Some(offset) => path.split_at(path.len() - offset).0, @@ -332,7 +282,7 @@ impl EffectLoader { path: &str, game_file_loader: &mut GameFileLoader, texture_loader: &mut TextureLoader, - ) -> Result, String> { + ) -> Result, LoadError> { match self.cache.get(path) { Some(effect) => Ok(effect.clone()), None => self.load(path, game_file_loader, texture_loader), diff --git a/korangar/src/loaders/error.rs b/korangar/src/loaders/error.rs new file mode 100644 index 000000000..d80ed184e --- /dev/null +++ b/korangar/src/loaders/error.rs @@ -0,0 +1,10 @@ +use ragnarok_bytes::ConversionError; + +use super::FileNotFoundError; + +#[derive(Debug)] +pub enum LoadError { + File(FileNotFoundError), + Conversion(Box), + UnsupportedFormat(String), +} diff --git a/korangar/src/loaders/gamefile/mod.rs b/korangar/src/loaders/gamefile/mod.rs index 5f6cf7071..1e06716c2 100644 --- a/korangar/src/loaders/gamefile/mod.rs +++ b/korangar/src/loaders/gamefile/mod.rs @@ -27,6 +27,9 @@ pub const FALLBACK_MODEL_FILE: &str = "data\\model\\missing.rsm"; pub const FALLBACK_SPRITE_FILE: &str = "data\\sprite\\npc\\missing.spr"; pub const FALLBACK_ACTIONS_FILE: &str = "data\\sprite\\npc\\missing.act"; +#[derive(Debug)] +pub struct FileNotFoundError(String); + /// Type implementing the game files loader. /// /// Currently, there are two types implementing @@ -154,13 +157,13 @@ impl GameFileLoader { lua_archive.save(); } - pub fn get(&mut self, path: &str) -> Result, String> { + pub fn get(&mut self, path: &str) -> Result, FileNotFoundError> { let lowercase_path = path.to_lowercase(); let result = self .archives .iter_mut() .find_map(|archive| archive.get_file_by_path(&lowercase_path)) - .ok_or(format!("failed to find file {path}")); + .ok_or(FileNotFoundError(path.to_owned())); // TODO: should this be removed in the future or left in for resilience? if result.is_err() { diff --git a/korangar/src/loaders/map/data.rs b/korangar/src/loaders/map/data.rs deleted file mode 100644 index 1e5a128f6..000000000 --- a/korangar/src/loaders/map/data.rs +++ /dev/null @@ -1,170 +0,0 @@ -use korangar_interface::elements::PrototypeElement; -use korangar_interface::windows::PrototypeWindow; -use ragnarok_bytes::{ByteConvertable, ByteStream, ConversionError, ConversionResult, ConversionResultExt, FromBytes}; - -pub use super::resource::MapResources; -use crate::loaders::map::resource::{LightSettings, WaterSettings}; -use crate::loaders::version::InternalVersion; -use crate::loaders::{MajorFirst, Version}; -use crate::world::Tile; - -#[derive(Clone, Debug, ByteConvertable, PrototypeElement)] -pub struct ColorBGRA { - pub blue: u8, - pub green: u8, - pub red: u8, - pub alpha: u8, -} - -#[derive(Clone, FromBytes, PrototypeElement, PrototypeWindow)] -#[window_title("Map Viewer")] -#[window_class("map_viewer")] -pub struct MapData { - #[version] - pub version: Version, - #[version_equals_or_above(2, 5)] - pub build_number: Option, - #[version_equals_or_above(2, 2)] - pub _unknown: Option, - #[length_hint(40)] - pub _ini_file: String, - #[length_hint(40)] - pub ground_file: String, - #[length_hint(40)] - pub gat_file: String, - #[version_equals_or_above(1, 4)] - #[length_hint(40)] - pub _source_file: Option, - #[version_smaller(2, 6)] - pub water_settings: Option, - pub light_settings: LightSettings, - #[version_equals_or_above(1, 6)] - pub ground_top: Option, - #[version_equals_or_above(1, 6)] - pub ground_bottom: Option, - #[version_equals_or_above(1, 6)] - pub ground_left: Option, - #[version_equals_or_above(1, 6)] - pub ground_right: Option, - // TODO: verify version - //`#[version_equals_or_above(2, 6)] - //pub quad_tree: QuadTree, - pub resources: MapResources, -} - -#[derive(FromBytes)] -pub struct GatData { - #[version] - pub version: Version, - pub map_width: i32, - pub map_height: i32, - #[repeating(self.map_width * self.map_height)] - pub tiles: Vec, -} - -#[derive(FromBytes)] -pub struct GroundData { - #[version] - pub version: Version, - pub width: i32, - pub height: i32, - pub zoom: f32, - pub texture_count: i32, - pub texture_name_length: i32, - #[repeating(self.texture_count)] - #[length_hint(self.texture_name_length)] - pub textures: Vec, - pub light_map_count: i32, - pub light_map_width: i32, - pub light_map_height: i32, - pub light_map_cells_per_grid: i32, - #[version_equals_or_above(1, 7)] - #[length_hint(self.light_map_count * self.light_map_width * self.light_map_height * 4)] - pub _skip: Option>, - #[version_smaller(1, 7)] - #[length_hint(self.light_map_count * 16)] - pub _skip2: Option>, - pub surface_count: i32, - #[repeating(self.surface_count)] - pub surfaces: Vec, - #[repeating(self.width as usize * self.height as usize)] - pub ground_tiles: Vec, -} - -pub struct GroundTile { - pub upper_left_height: f32, - pub upper_right_height: f32, - pub lower_left_height: f32, - pub lower_right_height: f32, - pub top_surface_index: i32, - pub front_surface_index: i32, - pub right_surface_index: i32, -} - -impl GroundTile { - pub fn get_lowest_point(&self) -> f32 { - [ - self.lower_right_height, - self.lower_left_height, - self.upper_left_height, - self.lower_right_height, - ] - .into_iter() - .reduce(f32::max) - .unwrap() - } -} - -impl FromBytes for GroundTile { - fn from_bytes(byte_stream: &mut ByteStream) -> ConversionResult { - let upper_left_height = f32::from_bytes(byte_stream).trace::()?; - let upper_right_height = f32::from_bytes(byte_stream).trace::()?; - let lower_left_height = f32::from_bytes(byte_stream).trace::()?; - let lower_right_height = f32::from_bytes(byte_stream).trace::()?; - - let version = byte_stream - .get_metadata::>()? - .ok_or(ConversionError::from_message("version not set"))?; - - let top_surface_index = match version.equals_or_above(1, 7) { - true => i32::from_bytes(byte_stream).trace::()?, - false => i16::from_bytes(byte_stream).trace::()? as i32, - }; - - let front_surface_index = match version.equals_or_above(1, 7) { - true => i32::from_bytes(byte_stream).trace::()?, - false => i16::from_bytes(byte_stream).trace::()? as i32, - }; - - let right_surface_index = match version.equals_or_above(1, 7) { - true => i32::from_bytes(byte_stream).trace::()?, - false => i16::from_bytes(byte_stream).trace::()? as i32, - }; - - Ok(Self { - upper_left_height, - upper_right_height, - lower_left_height, - lower_right_height, - top_surface_index, - front_surface_index, - right_surface_index, - }) - } -} - -#[derive(Copy, Clone, Debug)] -pub enum SurfaceType { - Front, - Right, - Top, -} - -#[derive(FromBytes)] -pub struct Surface { - pub u: [f32; 4], - pub v: [f32; 4], - pub texture_index: i16, - pub light_map_index: i16, - pub color: ColorBGRA, -} diff --git a/korangar/src/loaders/map/mod.rs b/korangar/src/loaders/map/mod.rs index 2c5dddcae..d7a9d9fa9 100644 --- a/korangar/src/loaders/map/mod.rs +++ b/korangar/src/loaders/map/mod.rs @@ -1,5 +1,3 @@ -mod data; -mod resource; mod vertices; use std::collections::HashMap; @@ -10,13 +8,11 @@ use derive_new::new; #[cfg(feature = "debug")] use korangar_debug::logging::Timer; use ragnarok_bytes::{ByteStream, FromBytes}; +use ragnarok_formats::map::{GatData, GroundData, GroundTile, MapData, MapResources}; +use ragnarok_formats::version::InternalVersion; -#[cfg(feature = "debug")] -pub use self::data::MapData; -use self::data::*; -pub use self::resource::{LightSettings, WaterSettings}; use self::vertices::{generate_tile_vertices, ground_water_vertices, load_textures}; -use super::version::InternalVersion; +use super::error::LoadError; use crate::graphics::{BufferAllocator, NativeModelVertex}; use crate::loaders::{GameFileLoader, ModelLoader, TextureLoader}; use crate::world::*; @@ -50,7 +46,7 @@ impl MapLoader { buffer_allocator: &mut BufferAllocator, model_loader: &mut ModelLoader, texture_loader: &mut TextureLoader, - ) -> Result, String> { + ) -> Result, LoadError> { match self.cache.get(&resource_file) { Some(map) => Ok(map.clone()), None => self.load(resource_file, game_file_loader, buffer_allocator, model_loader, texture_loader), @@ -64,18 +60,22 @@ impl MapLoader { buffer_allocator: &mut BufferAllocator, model_loader: &mut ModelLoader, texture_loader: &mut TextureLoader, - ) -> Result, String> { + ) -> Result, LoadError> { #[cfg(feature = "debug")] let timer = Timer::new_dynamic(format!("load map from {}", &resource_file)); - let mut map_data = parse_map_data(&resource_file, game_file_loader)?; + let map_file = format!("data\\{}.rsw", resource_file); + let mut map_data: MapData = parse_generic_data(&map_file, game_file_loader)?; + + let ground_file = format!("data\\{}", map_data.ground_file); + let ground_data: GroundData = parse_generic_data(&ground_file, game_file_loader)?; + + let gat_file = format!("data\\{}", map_data.gat_file); + let mut gat_data: GatData = parse_generic_data(&gat_file, game_file_loader)?; #[cfg(feature = "debug")] let map_data_clone = map_data.clone(); - let ground_data = parse_ground_data(map_data.ground_file.as_str(), game_file_loader)?; - let mut gat_data = parse_gat_data(map_data.gat_file.as_str(), game_file_loader)?; - let (tile_vertices, tile_picker_vertices) = generate_tile_vertices(&mut gat_data); let water_level = -map_data .water_settings @@ -154,7 +154,7 @@ fn apply_map_offset(ground_data: &GroundData, resources: &mut MapResources) { ground_data.height as f32 * MAP_OFFSET, ); - resources.objects.iter_mut().for_each(|object| object.offset(offset)); + resources.objects.iter_mut().for_each(|object| object.transform.position += offset); resources .sound_sources .iter_mut() @@ -169,50 +169,32 @@ fn apply_map_offset(ground_data: &GroundData, resources: &mut MapResources) { .for_each(|effect_source| effect_source.offset(offset)); } -fn parse_map_data(resource_file: &str, game_file_loader: &mut GameFileLoader) -> Result { - let bytes = game_file_loader.get(&format!("data\\{}.rsw", &resource_file))?; +fn parse_generic_data(resource_file: &str, game_file_loader: &mut GameFileLoader) -> Result { + let bytes = game_file_loader.get(resource_file).map_err(LoadError::File)?; let mut byte_stream: ByteStream> = ByteStream::without_metadata(&bytes); - if <[u8; 4]>::from_bytes(&mut byte_stream).unwrap() != [b'G', b'R', b'S', b'W'] { - return Err(format!("failed to read magic number from {}.rsw", &resource_file)); - } - - let map_data = MapData::from_bytes(&mut byte_stream).unwrap(); + let data = Data::from_bytes(&mut byte_stream).map_err(LoadError::Conversion)?; #[cfg(feature = "debug")] assert_byte_stream_empty(byte_stream, resource_file); - Ok(map_data) + Ok(data) } -fn parse_ground_data(ground_file: &str, game_file_loader: &mut GameFileLoader) -> Result { - let bytes = game_file_loader.get(&format!("data\\{}", &ground_file))?; - let mut byte_stream: ByteStream> = ByteStream::without_metadata(&bytes); - - if <[u8; 4]>::from_bytes(&mut byte_stream).unwrap() != [b'G', b'R', b'G', b'N'] { - return Err(format!("failed to read magic number from {}", &ground_file)); - } - - let ground_data = GroundData::from_bytes(&mut byte_stream).unwrap(); - - #[cfg(feature = "debug")] - assert_byte_stream_empty(byte_stream, ground_file); - - Ok(ground_data) +pub trait GroundTileExt { + fn get_lowest_point(&self) -> f32; } -fn parse_gat_data(gat_file: &str, game_file_loader: &mut GameFileLoader) -> Result { - let bytes = game_file_loader.get(&format!("data\\{}", &gat_file))?; - let mut byte_stream: ByteStream> = ByteStream::without_metadata(&bytes); - - if <[u8; 4]>::from_bytes(&mut byte_stream).unwrap() != [b'G', b'R', b'A', b'T'] { - return Err(format!("failed to read magic number from {}", &gat_file)); +impl GroundTileExt for GroundTile { + fn get_lowest_point(&self) -> f32 { + [ + self.lower_right_height, + self.lower_left_height, + self.upper_left_height, + self.lower_right_height, + ] + .into_iter() + .reduce(f32::max) + .unwrap() } - - let gat_data = GatData::from_bytes(&mut byte_stream).unwrap(); - - #[cfg(feature = "debug")] - assert_byte_stream_empty(byte_stream, gat_file); - - Ok(gat_data) } diff --git a/korangar/src/loaders/map/resource.rs b/korangar/src/loaders/map/resource.rs deleted file mode 100644 index 4f9ad0dea..000000000 --- a/korangar/src/loaders/map/resource.rs +++ /dev/null @@ -1,147 +0,0 @@ -use cgmath::Vector3; -use korangar_interface::elements::PrototypeElement; -use ragnarok_bytes::{ByteStream, ConversionError, ConversionResult, ConversionResultExt, FromBytes}; - -use crate::graphics::Transform; -use crate::loaders::color::ColorRGB; -use crate::world::{EffectSource, LightSource, SoundSource}; - -#[derive(Copy, Clone, Debug)] -pub enum ResourceType { - Object, - LightSource, - SoundSource, - EffectSource, -} - -impl FromBytes for ResourceType { - fn from_bytes(byte_stream: &mut ByteStream) -> ConversionResult { - let index = i32::from_bytes(byte_stream).trace::()?; - match index { - 1 => Ok(ResourceType::Object), - 2 => Ok(ResourceType::LightSource), - 3 => Ok(ResourceType::SoundSource), - 4 => Ok(ResourceType::EffectSource), - _ => Err(ConversionError::from_message(format!("invalid object type {index}"))), - } - } -} - -#[derive(Clone, FromBytes, PrototypeElement)] -pub struct ObjectData { - #[length_hint(40)] - #[version_equals_or_above(1, 3)] - pub name: Option, - #[version_equals_or_above(1, 3)] - pub _animation_type: Option, - #[version_equals_or_above(1, 3)] - pub _animation_speed: Option, - #[version_equals_or_above(1, 3)] - pub _block_type: Option, - // FIX: only if build_version >= 186 - #[version_equals_or_above(2, 6)] - pub _unknown: Option, - #[length_hint(80)] - pub model_name: String, - #[length_hint(80)] - pub _node_name: String, - pub transform: Transform, -} - -impl ObjectData { - pub fn offset(&mut self, offset: Vector3) { - self.transform.position += offset; - } -} - -#[allow(dead_code)] -#[derive(Clone, PrototypeElement)] -pub struct MapResources { - resources_amount: usize, - pub objects: Vec, - pub light_sources: Vec, - pub sound_sources: Vec, - pub effect_sources: Vec, -} - -impl FromBytes for MapResources { - fn from_bytes(byte_stream: &mut ByteStream) -> ConversionResult { - let resources_amount = i32::from_bytes(byte_stream).trace::()? as usize; - - let mut objects = Vec::new(); - let mut light_sources = Vec::new(); - let mut sound_sources = Vec::new(); - let mut effect_sources = Vec::new(); - - for index in 0..resources_amount { - let resource_type = ResourceType::from_bytes(byte_stream).trace::()?; - - match resource_type { - ResourceType::Object => { - let mut object = ObjectData::from_bytes(byte_stream).trace::()?; - // offset the objects slightly to avoid depth buffer fighting - object.transform.position += Vector3::new(0.0, 0.0005, 0.0) * index as f32; - objects.push(object); - } - ResourceType::LightSource => { - let mut light_source = LightSource::from_bytes(byte_stream).trace::()?; - light_source.position.y = -light_source.position.y; - light_sources.push(light_source); - } - ResourceType::SoundSource => { - let mut sound_source = SoundSource::from_bytes(byte_stream).trace::()?; - sound_source.position.y = -sound_source.position.y; - - if sound_source.cycle.is_none() { - sound_source.cycle = Some(4.0); - } - - sound_sources.push(sound_source); - } - ResourceType::EffectSource => { - let mut effect_source = EffectSource::from_bytes(byte_stream).trace::()?; - effect_source.position.y = -effect_source.position.y; - effect_sources.push(effect_source); - } - } - } - - Ok(Self { - resources_amount, - objects, - light_sources, - sound_sources, - effect_sources, - }) - } -} - -#[derive(Clone, Debug, FromBytes, PrototypeElement)] -pub struct WaterSettings { - #[version_equals_or_above(1, 3)] - pub water_level: Option, - #[version_equals_or_above(1, 8)] - pub water_type: Option, - #[version_equals_or_above(1, 8)] - pub wave_height: Option, - #[version_equals_or_above(1, 8)] - pub wave_speed: Option, - #[version_equals_or_above(1, 8)] - pub wave_pitch: Option, - #[version_equals_or_above(1, 9)] - pub water_animation_speed: Option, -} - -#[derive(Clone, Debug, FromBytes, PrototypeElement)] -pub struct LightSettings { - #[version_equals_or_above(1, 5)] - pub light_longitude: Option, - #[version_equals_or_above(1, 5)] - pub light_latitude: Option, - #[version_equals_or_above(1, 5)] - pub diffuse_color: Option, - #[version_equals_or_above(1, 5)] - pub ambient_color: Option, - #[version_equals_or_above(1, 7)] - pub light_intensity: Option, -} diff --git a/korangar/src/loaders/map/vertices.rs b/korangar/src/loaders/map/vertices.rs index 409c4b7a9..7404d29cf 100644 --- a/korangar/src/loaders/map/vertices.rs +++ b/korangar/src/loaders/map/vertices.rs @@ -1,9 +1,10 @@ use std::sync::Arc; use cgmath::{Vector2, Vector3}; +use ragnarok_formats::map::{GatData, GroundData, GroundTile, SurfaceType}; use vulkano::image::view::ImageView; -use super::data::{GatData, GroundData, GroundTile, SurfaceType}; +use super::GroundTileExt; use crate::graphics::{ModelVertex, NativeModelVertex, PickerTarget, TileVertex, WaterVertex}; use crate::loaders::{GameFileLoader, TextureLoader}; @@ -174,7 +175,7 @@ pub fn generate_tile_vertices(gat_data: &mut GatData) -> (Vec, Vec< tile.lower_right_height = -tile.lower_right_height; count += 1; - if tile.tile_type.is_none() { + if tile.flags.is_empty() { continue; } @@ -193,7 +194,7 @@ pub fn generate_tile_vertices(gat_data: &mut GatData) -> (Vec, Vec< let third_texture_coordinates = Vector2::new(1.0, 1.0); let fourth_texture_coordinates = Vector2::new(1.0, 0.0); - let tile_type_index = tile.tile_type.0 as i32; + let tile_type_index = tile.flags.bits() as i32; tile_vertices.push(ModelVertex::new( first_position, diff --git a/korangar/src/loaders/mod.rs b/korangar/src/loaders/mod.rs index 7dcd28318..9cd21e4e5 100644 --- a/korangar/src/loaders/mod.rs +++ b/korangar/src/loaders/mod.rs @@ -1,8 +1,8 @@ mod action; mod archive; pub mod client; -pub mod color; mod effect; +pub mod error; mod font; mod gamefile; mod map; @@ -11,18 +11,14 @@ mod script; mod server; mod sprite; mod texture; -mod version; pub use self::action::*; pub use self::effect::{EffectHolder, EffectLoader, *}; pub use self::font::{FontLoader, FontSize, Scaling}; pub use self::gamefile::*; -#[cfg(feature = "debug")] -pub use self::map::MapData; -pub use self::map::{LightSettings, MapLoader, WaterSettings}; +pub use self::map::MapLoader; pub use self::model::*; pub use self::script::ScriptLoader; pub use self::server::{load_client_info, ClientInfo, ServiceId}; pub use self::sprite::*; pub use self::texture::TextureLoader; -pub use self::version::{InternalVersion, MajorFirst, MinorFirst, Version}; diff --git a/korangar/src/loaders/model/mod.rs b/korangar/src/loaders/model/mod.rs index 85e572e17..7c7de1e0d 100644 --- a/korangar/src/loaders/model/mod.rs +++ b/korangar/src/loaders/model/mod.rs @@ -1,141 +1,22 @@ use std::collections::HashMap; use std::sync::Arc; -use cgmath::{Matrix3, Matrix4, Quaternion, Rad, SquareMatrix, Vector2, Vector3}; +use cgmath::{Matrix4, Rad, SquareMatrix, Vector2, Vector3}; use derive_new::new; #[cfg(feature = "debug")] use korangar_debug::logging::{print_debug, Colorize, Timer}; -use korangar_interface::application::Application; -use korangar_interface::elements::{ElementCell, PrototypeElement}; -use ragnarok_bytes::{ByteStream, ConversionError, ConversionResult, ConversionResultExt, FromBytes, FromBytesExt}; +use ragnarok_bytes::{ByteStream, FromBytes}; +use ragnarok_formats::model::{ModelData, ModelString, NodeData}; +use ragnarok_formats::version::InternalVersion; use vulkano::image::view::ImageView; -use super::version::InternalVersion; +use super::error::LoadError; use super::FALLBACK_MODEL_FILE; use crate::graphics::{BufferAllocator, NativeModelVertex}; -use crate::loaders::{GameFileLoader, MajorFirst, TextureLoader, Version}; +use crate::loaders::{GameFileLoader, TextureLoader}; use crate::system::multiply_matrix4_and_vector3; use crate::world::{BoundingBox, Model, Node}; -#[derive(Debug, FromBytes, PrototypeElement)] -pub struct PositionKeyframeData { - pub frame: u32, - pub position: Vector3, -} - -#[derive(Clone, Debug, FromBytes, PrototypeElement)] -pub struct RotationKeyframeData { - pub frame: u32, - pub quaternions: Quaternion, -} - -#[allow(dead_code)] -#[derive(Debug, FromBytes, PrototypeElement)] -pub struct FaceData { - pub vertex_position_indices: [u16; 3], - pub texture_coordinate_indices: [u16; 3], - pub texture_index: u16, - pub padding: u16, - pub two_sided: i32, - pub smooth_group: i32, -} - -#[derive(Debug, FromBytes, PrototypeElement)] -pub struct TextureCoordinateData { - #[version_equals_or_above(1, 2)] - pub color: Option, - pub coordinates: Vector2, // possibly wrong if version < 1.2 -} - -#[derive(Debug, FromBytes, PrototypeElement)] -pub struct NodeData { - pub node_name: ModelString<40>, - pub parent_node_name: ModelString<40>, // This is where 2.2 starts failing - pub texture_count: u32, - #[repeating(self.texture_count)] - pub texture_indices: Vec, - #[hidden_element] - pub offset_matrix: Matrix3, - pub translation1: Vector3, - pub translation2: Vector3, - pub rotation_angle: f32, - pub rotation_axis: Vector3, - pub scale: Vector3, - pub vertex_position_count: u32, - #[repeating(self.vertex_position_count)] - pub vertex_positions: Vec>, - pub texture_coordinate_count: u32, - #[repeating(self.texture_coordinate_count)] - pub texture_coordinates: Vec, - pub face_count: u32, - #[repeating(self.face_count)] - pub faces: Vec, - #[version_equals_or_above(2, 5)] // unsure what vesion this is supposed to be (must be > 1.5) - pub position_keyframe_count: Option, - #[repeating(self.position_keyframe_count.unwrap_or_default())] - pub position_keyframes: Vec, - pub rotation_keyframe_count: u32, - #[repeating(self.rotation_keyframe_count)] - pub rotation_keyframes: Vec, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct ModelString { - pub inner: String, -} - -impl FromBytes for ModelString { - fn from_bytes(byte_stream: &mut ByteStream) -> ConversionResult { - let inner = if byte_stream - .get_metadata::>()? - .ok_or(ConversionError::from_message("version not set"))? - .equals_or_above(2, 2) - { - let length = u32::from_bytes(byte_stream).trace::()? as usize; - let mut inner = String::from_n_bytes(byte_stream, length).trace::()?; - // need to remove the last character for some reason - inner.pop(); - inner - } else { - String::from_n_bytes(byte_stream, LENGTH).trace::()? - }; - - Ok(Self { inner }) - } -} - -impl PrototypeElement for ModelString -where - App: Application, -{ - fn to_element(&self, display: String) -> ElementCell { - self.inner.to_element(display) - } -} - -#[derive(Debug, FromBytes, PrototypeElement)] -pub struct ModelData { - #[version] - pub version: Version, - pub animation_length: u32, - pub shade_type: u32, - #[version_equals_or_above(1, 4)] - pub alpha: Option, - #[version_smaller(2, 2)] - pub reserved0: Option<[u8; 16]>, - #[version_equals_or_above(2, 2)] - pub reserved1: Option<[u8; 4]>, - pub texture_count: u32, - #[repeating(self.texture_count)] - pub texture_names: Vec>, - #[version_equals_or_above(2, 2)] - pub skip: Option, - pub root_node_name: ModelString<40>, - pub node_count: u32, - #[repeating(self.node_count)] - pub nodes: Vec, -} - #[derive(new)] pub struct ModelLoader { #[new(default)] @@ -323,17 +204,15 @@ impl ModelLoader { texture_loader: &mut TextureLoader, model_file: &str, reverse_order: bool, - ) -> Result, String> { + ) -> Result, LoadError> { #[cfg(feature = "debug")] let timer = Timer::new_dynamic(format!("load rsm model from {}", model_file.magenta())); - let bytes = game_file_loader.get(&format!("data\\model\\{model_file}"))?; + let bytes = game_file_loader + .get(&format!("data\\model\\{model_file}")) + .map_err(LoadError::File)?; let mut byte_stream: ByteStream> = ByteStream::without_metadata(&bytes); - if <[u8; 4]>::from_bytes(&mut byte_stream).unwrap() != [b'G', b'R', b'S', b'M'] { - return Err(format!("failed to read magic number from {model_file}")); - } - let model_data = match ModelData::from_bytes(&mut byte_stream) { Ok(model_data) => model_data, Err(_error) => { @@ -400,7 +279,7 @@ impl ModelLoader { texture_loader: &mut TextureLoader, model_file: &str, reverse_order: bool, - ) -> Result, String> { + ) -> Result, LoadError> { match self.cache.get(&(model_file.to_string(), reverse_order)) { // kinda dirty Some(model) => Ok(model.clone()), diff --git a/korangar/src/loaders/sprite/mod.rs b/korangar/src/loaders/sprite/mod.rs index 3aedd3b6a..28c1bb34b 100644 --- a/korangar/src/loaders/sprite/mod.rs +++ b/korangar/src/loaders/sprite/mod.rs @@ -5,7 +5,9 @@ use derive_new::new; #[cfg(feature = "debug")] use korangar_debug::logging::{print_debug, Colorize, Timer}; use korangar_interface::elements::PrototypeElement; -use ragnarok_bytes::{ByteStream, ConversionError, ConversionResult, ConversionResultExt, FromBytes, FromBytesExt}; +use ragnarok_bytes::{ByteStream, FromBytes}; +use ragnarok_formats::sprite::{PaletteColor, RgbaImageData, SpriteData}; +use ragnarok_formats::version::InternalVersion; use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage}; use vulkano::command_buffer::{ AutoCommandBufferBuilder, CommandBufferUsage, CopyBufferToImageInfo, PrimaryAutoCommandBuffer, PrimaryCommandBufferAbstract, @@ -18,10 +20,10 @@ use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter}; use vulkano::sync::future::FenceSignalFuture; use vulkano::sync::GpuFuture; -use super::version::InternalVersion; use super::FALLBACK_SPRITE_FILE; use crate::graphics::MemoryAllocator; -use crate::loaders::{GameFileLoader, MinorFirst, Version}; +use crate::loaders::error::LoadError; +use crate::loaders::GameFileLoader; #[derive(Clone, Debug, PrototypeElement)] pub struct Sprite { @@ -31,121 +33,6 @@ pub struct Sprite { sprite_data: SpriteData, } -#[derive(Clone, Debug, PrototypeElement)] -struct PaletteImageData { - pub width: u16, - pub height: u16, - pub data: EncodedData, -} - -#[derive(Clone, Debug, PrototypeElement)] -struct EncodedData(pub Vec); - -impl FromBytes for PaletteImageData { - fn from_bytes(byte_stream: &mut ByteStream) -> ConversionResult - where - Self: Sized, - { - let width = u16::from_bytes(byte_stream).trace::()?; - let height = u16::from_bytes(byte_stream).trace::()?; - - let data = match width as usize * height as usize { - 0 => Vec::new(), - image_size - if byte_stream - .get_metadata::>()? - .ok_or(ConversionError::from_message("version not set"))? - .smaller(2, 1) => - { - Vec::from_n_bytes(byte_stream, image_size).trace::()? - } - image_size => { - let mut data = vec![0; image_size]; - let mut encoded = u16::from_bytes(byte_stream).trace::()?; - let mut next = 0; - - while next < image_size && encoded > 0 { - let byte = byte_stream.byte::()?; - encoded -= 1; - - if byte == 0 { - let length = usize::max(byte_stream.byte::()? as usize, 1); - encoded -= 1; - - if next + length > image_size { - return Err(ConversionError::from_message("too much data encoded in palette image")); - } - - next += length; - } else { - data[next] = byte; - next += 1; - } - } - - if next != image_size || encoded > 0 { - return Err(ConversionError::from_message("badly encoded palette image")); - } - - data - } - }; - - Ok(Self { - width, - height, - data: EncodedData(data), - }) - } -} - -#[derive(Clone, Debug, FromBytes, PrototypeElement)] -struct RgbaImageData { - pub width: u16, - pub height: u16, - #[length_hint(self.width as usize * self.height as usize * 4)] - pub data: Vec, -} - -#[derive(Copy, Clone, Debug, Default, FromBytes, PrototypeElement)] -struct PaletteColor { - pub red: u8, - pub green: u8, - pub blue: u8, - pub reserved: u8, -} - -impl PaletteColor { - pub fn color_bytes(&self, index: u8) -> [u8; 4] { - let alpha = match index { - 0 => 0, - _ => 255, - }; - - [self.red, self.green, self.blue, alpha] - } -} - -#[derive(Clone, Debug, FromBytes, PrototypeElement)] -struct Palette { - pub colors: [PaletteColor; 256], -} - -#[derive(Clone, Debug, FromBytes, PrototypeElement)] -struct SpriteData { - #[version] - pub version: Version, - pub palette_image_count: u16, - #[version_equals_or_above(1, 2)] - pub rgba_image_count: Option, - #[repeating(self.palette_image_count)] - pub palette_image_data: Vec, - #[repeating(self.rgba_image_count.unwrap_or_default())] - pub rgba_image_data: Vec, - #[version_equals_or_above(1, 1)] - pub palette: Option, -} - #[derive(new)] pub struct SpriteLoader { memory_allocator: Arc, @@ -157,17 +44,13 @@ pub struct SpriteLoader { } impl SpriteLoader { - fn load(&mut self, path: &str, game_file_loader: &mut GameFileLoader) -> Result, String> { + fn load(&mut self, path: &str, game_file_loader: &mut GameFileLoader) -> Result, LoadError> { #[cfg(feature = "debug")] let timer = Timer::new_dynamic(format!("load sprite from {}", path.magenta())); - let bytes = game_file_loader.get(&format!("data\\sprite\\{path}"))?; + let bytes = game_file_loader.get(&format!("data\\sprite\\{path}")).map_err(LoadError::File)?; let mut byte_stream: ByteStream> = ByteStream::without_metadata(&bytes); - if <[u8; 2]>::from_bytes(&mut byte_stream).unwrap() != [b'S', b'P'] { - return Err(format!("failed to read magic number from {path}")); - } - let sprite_data = match SpriteData::from_bytes(&mut byte_stream) { Ok(sprite_data) => sprite_data, Err(_error) => { @@ -191,13 +74,23 @@ impl SpriteLoader { .rgba_image_data .into_iter(); + // TODO: Move this to an extension trait in `korangar_loaders`. + pub fn color_bytes(palette: &PaletteColor, index: u8) -> [u8; 4] { + let alpha = match index { + 0 => 0, + _ => 255, + }; + + [palette.red, palette.green, palette.blue, alpha] + } + let palette_images = sprite_data.palette_image_data.into_iter().map(|image_data| { // decode palette image data if necessary let data: Vec = image_data .data .0 .iter() - .flat_map(|palette_index| palette.colors[*palette_index as usize].color_bytes(*palette_index)) + .flat_map(|palette_index| color_bytes(&palette.colors[*palette_index as usize], *palette_index)) .collect(); RgbaImageData { @@ -267,7 +160,7 @@ impl SpriteLoader { Ok(sprite) } - pub fn get(&mut self, path: &str, game_file_loader: &mut GameFileLoader) -> Result, String> { + pub fn get(&mut self, path: &str, game_file_loader: &mut GameFileLoader) -> Result, LoadError> { match self.cache.get(path) { Some(sprite) => Ok(sprite.clone()), None => self.load(path, game_file_loader), diff --git a/korangar/src/loaders/texture/mod.rs b/korangar/src/loaders/texture/mod.rs index 8e0aab77c..87a642472 100644 --- a/korangar/src/loaders/texture/mod.rs +++ b/korangar/src/loaders/texture/mod.rs @@ -19,6 +19,7 @@ use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter}; use vulkano::sync::future::FenceSignalFuture; use vulkano::sync::GpuFuture; +use super::error::LoadError; use super::{FALLBACK_BMP_FILE, FALLBACK_PNG_FILE, FALLBACK_TGA_FILE}; use crate::graphics::MemoryAllocator; use crate::loaders::GameFileLoader; @@ -34,7 +35,7 @@ pub struct TextureLoader { } impl TextureLoader { - fn load(&mut self, path: &str, game_file_loader: &mut GameFileLoader) -> Result, String> { + fn load(&mut self, path: &str, game_file_loader: &mut GameFileLoader) -> Result, LoadError> { #[cfg(feature = "debug")] let timer = Timer::new_dynamic(format!("load texture from {}", path.magenta())); @@ -42,10 +43,10 @@ impl TextureLoader { ".png" => ImageFormat::Png, ".bmp" | ".BMP" => ImageFormat::Bmp, ".tga" | ".TGA" => ImageFormat::Tga, - extension => return Err(format!("unsupported file format {extension}")), + extension => return Err(LoadError::UnsupportedFormat(extension.to_owned())), }; - let file_data = game_file_loader.get(&format!("data\\texture\\{path}"))?; + let file_data = game_file_loader.get(&format!("data\\texture\\{path}")).map_err(LoadError::File)?; let reader = ImageReader::with_format(Cursor::new(file_data), image_format); let mut image_buffer = match reader.decode() { @@ -124,7 +125,7 @@ impl TextureLoader { Ok(texture) } - pub fn get(&mut self, path: &str, game_file_loader: &mut GameFileLoader) -> Result, String> { + pub fn get(&mut self, path: &str, game_file_loader: &mut GameFileLoader) -> Result, LoadError> { match self.cache.get(path) { Some(texture) => Ok(texture.clone()), None => self.load(path, game_file_loader), diff --git a/korangar/src/world/effect/mod.rs b/korangar/src/world/effect/mod.rs index fe808a7f1..da4f712a6 100644 --- a/korangar/src/world/effect/mod.rs +++ b/korangar/src/world/effect/mod.rs @@ -1,36 +1,35 @@ mod lookup; use cgmath::Vector3; -use korangar_interface::elements::PrototypeElement; -use korangar_interface::windows::PrototypeWindow; -use ragnarok_bytes::ByteConvertable; +use ragnarok_formats::map::EffectSource; #[cfg(feature = "debug")] use crate::graphics::{Camera, MarkerRenderer, Renderer}; #[cfg(feature = "debug")] use crate::world::MarkerIdentifier; -#[derive(Clone, ByteConvertable, PrototypeElement, PrototypeWindow)] -#[window_title("Effect Source")] -pub struct EffectSource { - #[length_hint(80)] - pub name: String, - pub position: Vector3, - pub effect_type: u32, // TODO: fix this - pub emit_speed: f32, - pub _param0: f32, - pub _param1: f32, - pub _param2: f32, - pub _param3: f32, +pub trait EffectSourceExt { + fn offset(&mut self, offset: Vector3); + + #[cfg(feature = "debug")] + fn render_marker( + &self, + render_target: &mut T::Target, + renderer: &T, + camera: &dyn Camera, + marker_identifier: MarkerIdentifier, + hovered: bool, + ) where + T: Renderer + MarkerRenderer; } -impl EffectSource { - pub fn offset(&mut self, offset: Vector3) { +impl EffectSourceExt for EffectSource { + fn offset(&mut self, offset: Vector3) { self.position += offset; } #[cfg(feature = "debug")] - pub fn render_marker( + fn render_marker( &self, render_target: &mut T::Target, renderer: &T, diff --git a/korangar/src/world/entity/mod.rs b/korangar/src/world/entity/mod.rs index 4431dbb76..3389c8e9f 100644 --- a/korangar/src/world/entity/mod.rs +++ b/korangar/src/world/entity/mod.rs @@ -5,6 +5,7 @@ use derive_new::new; use korangar_interface::elements::PrototypeElement; use korangar_interface::windows::{PrototypeWindow, Window}; use korangar_networking::EntityData; +use ragnarok_formats::map::TileFlags; use ragnarok_packets::{AccountId, CharacterInformation, ClientTick, EntityId, Sex, StatusType, WorldPosition}; use vulkano::buffer::Subbuffer; @@ -436,39 +437,39 @@ impl Common { if map.x_in_bounds(x + 1) && map.y_in_bounds(y + 1) - && map.get_tile(Vector2::new(x + 1, y)).is_walkable() - && map.get_tile(Vector2::new(x, y + 1)).is_walkable() + && map.get_tile(Vector2::new(x + 1, y)).flags.contains(TileFlags::WALKABLE) + && map.get_tile(Vector2::new(x, y + 1)).flags.contains(TileFlags::WALKABLE) { successors.push(Pos(x + 1, y + 1)); } if x > 0 && map.y_in_bounds(y + 1) - && map.get_tile(Vector2::new(x - 1, y)).is_walkable() - && map.get_tile(Vector2::new(x, y + 1)).is_walkable() + && map.get_tile(Vector2::new(x - 1, y)).flags.contains(TileFlags::WALKABLE) + && map.get_tile(Vector2::new(x, y + 1)).flags.contains(TileFlags::WALKABLE) { successors.push(Pos(x - 1, y + 1)); } if map.x_in_bounds(x + 1) && y > 0 - && map.get_tile(Vector2::new(x + 1, y)).is_walkable() - && map.get_tile(Vector2::new(x, y - 1)).is_walkable() + && map.get_tile(Vector2::new(x + 1, y)).flags.contains(TileFlags::WALKABLE) + && map.get_tile(Vector2::new(x, y - 1)).flags.contains(TileFlags::WALKABLE) { successors.push(Pos(x + 1, y - 1)); } if x > 0 && y > 0 - && map.get_tile(Vector2::new(x - 1, y)).is_walkable() - && map.get_tile(Vector2::new(x, y - 1)).is_walkable() + && map.get_tile(Vector2::new(x - 1, y)).flags.contains(TileFlags::WALKABLE) + && map.get_tile(Vector2::new(x, y - 1)).flags.contains(TileFlags::WALKABLE) { successors.push(Pos(x - 1, y - 1)); } let successors = successors .drain(..) - .filter(|Pos(x, y)| map.get_tile(Vector2::new(*x, *y)).is_walkable()) + .filter(|Pos(x, y)| map.get_tile(Vector2::new(*x, *y)).flags.contains(TileFlags::WALKABLE)) .collect::>(); successors diff --git a/korangar/src/world/light/mod.rs b/korangar/src/world/light/mod.rs index 083786e65..a68d7ef67 100644 --- a/korangar/src/world/light/mod.rs +++ b/korangar/src/world/light/mod.rs @@ -1,39 +1,38 @@ use cgmath::Vector3; -use korangar_interface::elements::PrototypeElement; -use korangar_interface::windows::PrototypeWindow; -use ragnarok_bytes::ByteConvertable; +use ragnarok_formats::map::LightSource; use crate::graphics::*; -use crate::loaders::color::ColorRGB; #[cfg(feature = "debug")] use crate::world::MarkerIdentifier; -#[derive(Clone, ByteConvertable, PrototypeElement, PrototypeWindow)] -#[window_title("Light Source")] -pub struct LightSource { - #[length_hint(80)] - pub name: String, - pub position: Vector3, - pub color: ColorRGB, - pub range: f32, +pub trait LightSourceExt { + fn offset(&mut self, offset: Vector3); + + fn render_light(&self, render_target: &mut ::Target, renderer: &DeferredRenderer, camera: &dyn Camera); + + #[cfg(feature = "debug")] + fn render_marker( + &self, + render_target: &mut T::Target, + renderer: &T, + camera: &dyn Camera, + marker_identifier: MarkerIdentifier, + hovered: bool, + ) where + T: Renderer + MarkerRenderer; } -impl LightSource { - pub fn offset(&mut self, offset: Vector3) { +impl LightSourceExt for LightSource { + fn offset(&mut self, offset: Vector3) { self.position += offset; } - pub fn render_light( - &self, - render_target: &mut ::Target, - renderer: &DeferredRenderer, - camera: &dyn Camera, - ) { + fn render_light(&self, render_target: &mut ::Target, renderer: &DeferredRenderer, camera: &dyn Camera) { renderer.point_light(render_target, camera, self.position, self.color.to_owned().into(), self.range); } #[cfg(feature = "debug")] - pub fn render_marker( + fn render_marker( &self, render_target: &mut T::Target, renderer: &T, diff --git a/korangar/src/world/map/mod.rs b/korangar/src/world/map/mod.rs index 11d186d14..cdf34f11d 100644 --- a/korangar/src/world/map/mod.rs +++ b/korangar/src/world/map/mod.rs @@ -1,4 +1,3 @@ -mod tile; use std::sync::Arc; use cgmath::{Array, EuclideanSpace, Matrix4, Point3, SquareMatrix, Vector2, Vector3}; @@ -9,18 +8,21 @@ use korangar_debug::profiling::Profiler; use korangar_interface::windows::PrototypeWindow; #[cfg(feature = "debug")] use option_ext::OptionExt; +use ragnarok_formats::map::{EffectSource, LightSettings, LightSource, MapData, SoundSource, Tile, TileFlags, WaterSettings}; +#[cfg(feature = "debug")] +use ragnarok_formats::transform::Transform; use ragnarok_packets::ClientTick; use vulkano::buffer::Subbuffer; use vulkano::image::view::ImageView; -pub use self::tile::Tile; use crate::graphics::*; use crate::interface::application::InterfaceSettings; -#[cfg(feature = "debug")] -use crate::loaders::MapData; -use crate::loaders::{LightSettings, WaterSettings}; use crate::world::*; +fn average_tile_height(tile: &Tile) -> f32 { + (tile.upper_left_height + tile.upper_right_height + tile.lower_left_height + tile.lower_right_height) / 4.0 +} + // MOVE fn get_value(day_timer: f32, offset: f32, p: f32) -> f32 { let sin = (day_timer + offset).sin(); @@ -122,7 +124,7 @@ impl Map { } pub fn get_world_position(&self, position: Vector2) -> Vector3 { - let height = self.get_tile(position).average_height(); + let height = average_tile_height(self.get_tile(position)); Vector3::new(position.x as f32 * 5.0 + 2.5, height, position.y as f32 * 5.0 + 2.5) } @@ -264,7 +266,7 @@ impl Map { let tile = self.get_tile(position); - if tile.is_walkable() { + if tile.flags.contains(TileFlags::WALKABLE) { let base_x = position.x as f32 * 5.0; let base_y = position.y as f32 * 5.0; diff --git a/korangar/src/world/map/tile.rs b/korangar/src/world/map/tile.rs index dd8165b34..43e0d452f 100644 --- a/korangar/src/world/map/tile.rs +++ b/korangar/src/world/map/tile.rs @@ -1,61 +1,3 @@ -use ragnarok_bytes::{ByteStream, ConversionResult, FromBytes}; -const NONE: u8 = 0b00000000; -const WALKABLE: u8 = 0b00000001; -const WATER: u8 = 0b00000010; -const SNIPABLE: u8 = 0b00000100; -const CLIFF: u8 = 0b00001000; +pub -#[allow(dead_code)] -#[derive(Debug)] -pub struct TileType(pub u8); - -impl FromBytes for TileType { - fn from_bytes(byte_stream: &mut ByteStream) -> ConversionResult { - byte_stream.byte::().map(Self::new) - } -} - -impl TileType { - pub fn new(type_index: u8) -> Self { - match type_index { - 0 => Self(WALKABLE), - 1 => Self(NONE), - 2 => Self(WATER), - 3 => Self(WATER | WALKABLE), - 4 => Self(WATER | SNIPABLE), - 5 => Self(CLIFF | SNIPABLE), - 6 => Self(CLIFF), - invalid => panic!("invalid tile type {invalid}"), - } - } - - pub fn is_none(&self) -> bool { - self.0 == 0 - } - - pub fn is_walkable(&self) -> bool { - self.0 & WALKABLE != 0 - } -} - -#[allow(dead_code)] -#[derive(FromBytes, Debug)] -pub struct Tile { - pub upper_left_height: f32, - pub upper_right_height: f32, - pub lower_left_height: f32, - pub lower_right_height: f32, - pub tile_type: TileType, - _skip: [u8; 3], -} - -impl Tile { - pub fn is_walkable(&self) -> bool { - self.tile_type.is_walkable() - } - - pub fn average_height(&self) -> f32 { - (self.upper_left_height + self.upper_right_height + self.lower_left_height + self.lower_right_height) / 4.0 - } -} diff --git a/korangar/src/world/model/mod.rs b/korangar/src/world/model/mod.rs index a551335e1..5508642ec 100644 --- a/korangar/src/world/model/mod.rs +++ b/korangar/src/world/model/mod.rs @@ -5,14 +5,15 @@ use std::ops::Mul; use cgmath::{Matrix4, Vector3}; use derive_new::new; use korangar_interface::elements::PrototypeElement; +#[cfg(feature = "debug")] +use ragnarok_formats::model::ModelData; +use ragnarok_formats::transform::Transform; use ragnarok_packets::ClientTick; pub use self::node::{BoundingBox, Node, OrientedBox}; -use crate::graphics::{Camera, GeometryRenderer, Renderer, Transform}; +use crate::graphics::{Camera, GeometryRenderer, Renderer}; #[cfg(feature = "debug")] use crate::graphics::{Color, DeferredRenderer}; -#[cfg(feature = "debug")] -use crate::loaders::ModelData; #[derive(PrototypeElement, new)] pub struct Model { diff --git a/korangar/src/world/model/node.rs b/korangar/src/world/model/node.rs index 7bd9e6638..292f87cd3 100644 --- a/korangar/src/world/model/node.rs +++ b/korangar/src/world/model/node.rs @@ -3,12 +3,13 @@ use std::sync::Arc; use cgmath::{Array, Matrix4, SquareMatrix, Vector3, Vector4}; use derive_new::new; use korangar_interface::elements::PrototypeElement; +use ragnarok_formats::model::RotationKeyframeData; +use ragnarok_formats::transform::Transform; use ragnarok_packets::ClientTick; use vulkano::buffer::Subbuffer; use vulkano::image::view::ImageView; -use crate::graphics::{Camera, GeometryRenderer, ModelVertex, Renderer, Transform}; -use crate::loaders::RotationKeyframeData; +use crate::graphics::{Camera, GeometryRenderer, ModelVertex, Renderer}; use crate::system::multiply_matrix4_and_vector3; #[derive(Copy, Clone, Debug, PrototypeElement)] diff --git a/korangar/src/world/object/mod.rs b/korangar/src/world/object/mod.rs index cb2e943f2..e4df49617 100644 --- a/korangar/src/world/object/mod.rs +++ b/korangar/src/world/object/mod.rs @@ -4,6 +4,7 @@ use cgmath::Matrix4; use derive_new::new; use korangar_interface::elements::PrototypeElement; use korangar_interface::windows::PrototypeWindow; +use ragnarok_formats::transform::Transform; use ragnarok_packets::ClientTick; use crate::graphics::*; diff --git a/korangar/src/world/sound/mod.rs b/korangar/src/world/sound/mod.rs index 7a9f304ba..1099e8b20 100644 --- a/korangar/src/world/sound/mod.rs +++ b/korangar/src/world/sound/mod.rs @@ -1,36 +1,33 @@ use cgmath::Vector3; -use korangar_interface::elements::PrototypeElement; -use korangar_interface::windows::PrototypeWindow; -use ragnarok_bytes::ByteConvertable; +use ragnarok_formats::map::SoundSource; #[cfg(feature = "debug")] use crate::graphics::{Camera, MarkerRenderer, Renderer}; #[cfg(feature = "debug")] use crate::world::MarkerIdentifier; -#[derive(Clone, PrototypeElement, PrototypeWindow, ByteConvertable)] -#[window_title("Sound Source")] -pub struct SoundSource { - #[length_hint(80)] - pub name: String, - #[length_hint(80)] - pub sound_file: String, - pub position: Vector3, - pub volume: f32, - pub width: u32, - pub height: u32, - pub range: f32, - #[version_equals_or_above(2, 0)] - pub cycle: Option, +pub trait SoundSourceExt { + fn offset(&mut self, offset: Vector3); + + #[cfg(feature = "debug")] + fn render_marker( + &self, + render_target: &mut T::Target, + renderer: &T, + camera: &dyn Camera, + marker_identifier: MarkerIdentifier, + hovered: bool, + ) where + T: Renderer + MarkerRenderer; } -impl SoundSource { - pub fn offset(&mut self, offset: Vector3) { +impl SoundSourceExt for SoundSource { + fn offset(&mut self, offset: Vector3) { self.position += offset; } #[cfg(feature = "debug")] - pub fn render_marker( + fn render_marker( &self, render_target: &mut T::Target, renderer: &T, diff --git a/ragnarok_formats/Cargo.toml b/ragnarok_formats/Cargo.toml new file mode 100644 index 000000000..24c8c291e --- /dev/null +++ b/ragnarok_formats/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "ragnarok_formats" +version = "0.1.0" +edition = "2021" + +[dependencies] +cgmath = { workspace = true } +ragnarok_bytes = { workspace = true, features = ["derive", "cgmath"] } +korangar_interface = { workspace = true, optional = true } +bitflags = { workspace = true } + +[features] +interface = ["korangar_interface"] diff --git a/ragnarok_formats/README.md b/ragnarok_formats/README.md new file mode 100644 index 000000000..24f6baaa5 --- /dev/null +++ b/ragnarok_formats/README.md @@ -0,0 +1,3 @@ +# Ragnarok Formats + + diff --git a/ragnarok_formats/src/action.rs b/ragnarok_formats/src/action.rs new file mode 100644 index 000000000..838450797 --- /dev/null +++ b/ragnarok_formats/src/action.rs @@ -0,0 +1,84 @@ +use cgmath::Vector2; +use ragnarok_bytes::{ByteConvertable, FromBytes}; + +use crate::signature::Signature; +use crate::version::{MinorFirst, Version}; + +#[derive(Debug, Clone, ByteConvertable)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct SpriteClip { + pub position: Vector2, + pub sprite_number: u32, + pub mirror_on: u32, + #[version_equals_or_above(2, 0)] + pub color: Option, + #[version_smaller(2, 4)] + pub zoom: Option, + #[version_equals_or_above(2, 4)] + pub zoom2: Option>, + #[version_equals_or_above(2, 0)] + pub angle: Option, + #[version_equals_or_above(2, 0)] + pub sprite_type: Option, + #[version_equals_or_above(2, 5)] + pub size: Option>, +} + +#[derive(Debug, Clone, ByteConvertable)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct AttachPoint { + pub ignored: u32, + pub position: Vector2, + pub attribute: u32, +} + +#[derive(Debug, Clone, ByteConvertable)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct Motion { + pub range1: [i32; 4], // maybe just skip this? + pub range2: [i32; 4], // maybe just skip this? + pub sprite_clip_count: u32, + #[repeating(self.sprite_clip_count)] + pub sprite_clips: Vec, + #[version_equals_or_above(2, 0)] + pub event_id: Option, // if version == 2.0 this maybe needs to be set to None ? + // (after it is parsed) + #[version_equals_or_above(2, 3)] + pub attach_point_count: Option, + #[repeating(self.attach_point_count.unwrap_or_default())] + pub attach_points: Vec, +} + +#[derive(Debug, Clone, ByteConvertable)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct Action { + pub motion_count: u32, + #[repeating(self.motion_count)] + pub motions: Vec, +} + +#[derive(Debug, Clone, FromBytes)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct Event { + #[length_hint(40)] + pub name: String, +} + +#[derive(Debug, Clone, FromBytes)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct ActionsData { + pub signature: Signature, + #[version] + pub version: Version, + pub action_count: u16, + pub reserved: [u8; 10], + #[repeating(self.action_count)] + pub actions: Vec, + #[version_equals_or_above(2, 1)] + pub event_count: Option, + #[repeating(self.event_count.unwrap_or_default())] + pub events: Vec, + #[version_equals_or_above(2, 2)] + #[repeating(self.action_count)] + pub delays: Option>, +} diff --git a/ragnarok_formats/src/archive.rs b/ragnarok_formats/src/archive.rs new file mode 100644 index 000000000..6db735050 --- /dev/null +++ b/ragnarok_formats/src/archive.rs @@ -0,0 +1,55 @@ +use ragnarok_bytes::{ByteConvertable, FixedByteSize}; + +use crate::signature::Signature; + +/// Represents the Header of the GRF file. +#[derive(Clone, ByteConvertable, FixedByteSize)] +pub struct Header { + pub signature: Signature, + pub encryption: [u8; 14], + pub file_table_offset: u32, + pub reserved_files: u32, + pub file_count: u32, + pub version: u32, +} + +impl Header { + // TODO: This is temporary and can be removed after improving the + // ByteConvertable trait. + pub fn new(file_table_offset: u32, reserved_files: u32, file_count: u32, version: u32) -> Self { + Self { + signature: Signature, + encryption: Default::default(), + file_table_offset, + reserved_files, + file_count, + version, + } + } +} + +impl Header { + pub const FILE_OFFSET: usize = 7; + + pub fn get_file_count(&self) -> usize { + (self.file_count - self.reserved_files) as usize - Self::FILE_OFFSET + } +} + +/// Represents file information about each of the files stored in the GRF. +#[derive(Clone, Debug, ByteConvertable)] +pub struct FileTableRow { + pub file_name: String, + pub compressed_size: u32, + pub compressed_size_aligned: u32, + pub uncompressed_size: u32, + pub flags: u8, + pub offset: u32, +} + +/// Stores the table of files the parent GRF is holding. +#[derive(Clone, ByteConvertable, FixedByteSize)] +pub struct AssetTable { + pub compressed_size: u32, + pub uncompressed_size: u32, +} diff --git a/ragnarok_formats/src/color.rs b/ragnarok_formats/src/color.rs new file mode 100644 index 000000000..859a3cf15 --- /dev/null +++ b/ragnarok_formats/src/color.rs @@ -0,0 +1,18 @@ +use ragnarok_bytes::ByteConvertable; + +#[derive(Clone, Debug, ByteConvertable)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct ColorRGB { + pub red: f32, + pub green: f32, + pub blue: f32, +} + +#[derive(Clone, Debug, ByteConvertable)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct ColorBGRA { + pub blue: u8, + pub green: u8, + pub red: u8, + pub alpha: u8, +} diff --git a/ragnarok_formats/src/effect.rs b/ragnarok_formats/src/effect.rs new file mode 100644 index 000000000..8afb28eff --- /dev/null +++ b/ragnarok_formats/src/effect.rs @@ -0,0 +1,58 @@ +use cgmath::Vector2; +use ragnarok_bytes::FromBytes; + +use crate::signature::Signature; +use crate::version::{MajorFirst, Version}; + +#[derive(Debug, FromBytes)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct TextureName { + #[length_hint(128)] + pub name: String, +} + +#[derive(Debug, Clone, FromBytes)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct Frame { + pub frame_index: i32, + pub frame_type: i32, + pub offset: Vector2, + pub uv: [f32; 8], + pub xy: [f32; 8], + pub texture_index: f32, + pub animation_type: i32, + pub delay: f32, + pub angle: f32, + pub color: [f32; 4], + // Needs to actually set the attachment blend mode of the source alpha + pub source_alpha: i32, + // Needs to actually set the attachment blend mode of the destination alpha + pub destination_alpha: i32, + pub mt_present: i32, +} + +#[derive(Debug, FromBytes)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct LayerData { + pub texture_count: i32, + #[repeating(self.texture_count)] + pub texture_names: Vec, + pub frame_count: i32, + #[repeating(self.frame_count)] + pub frames: Vec, +} + +#[derive(Debug, FromBytes)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct EffectData { + pub signature: Signature, + #[version] + pub version: Version, + pub _skip0: [u8; 2], + pub frames_per_second: u32, + pub max_key: u32, + pub layer_count: u32, + pub _skip1: [u8; 16], + #[repeating(self.layer_count)] + pub layers: Vec, +} diff --git a/ragnarok_formats/src/lib.rs b/ragnarok_formats/src/lib.rs new file mode 100644 index 000000000..1fc2d1394 --- /dev/null +++ b/ragnarok_formats/src/lib.rs @@ -0,0 +1,16 @@ +#![feature(adt_const_params)] +#![allow(incomplete_features)] + +pub mod action; +pub mod archive; +pub mod color; +pub mod effect; +pub mod map; +pub mod model; +pub mod signature; +pub mod sprite; +pub mod transform; +pub mod version; + +// To make proc macros work in ragnarok_formats. +extern crate self as ragnarok_formats; diff --git a/ragnarok_formats/src/map.rs b/ragnarok_formats/src/map.rs new file mode 100644 index 000000000..c5023f019 --- /dev/null +++ b/ragnarok_formats/src/map.rs @@ -0,0 +1,369 @@ +use cgmath::Vector3; +use ragnarok_bytes::{ByteConvertable, ByteStream, ConversionError, ConversionResult, ConversionResultExt, FromBytes}; + +use crate::color::{ColorBGRA, ColorRGB}; +use crate::signature::Signature; +use crate::transform::Transform; +use crate::version::{InternalVersion, MajorFirst, Version}; + +#[derive(Clone, FromBytes)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +#[cfg_attr(feature = "interface", derive(korangar_interface::windows::PrototypeWindow))] +#[window_title("Map Viewer")] +#[window_class("map_viewer")] +pub struct MapData { + pub signature: Signature, + #[version] + pub version: Version, + #[version_equals_or_above(2, 5)] + pub build_number: Option, + #[version_equals_or_above(2, 2)] + pub _unknown: Option, + #[length_hint(40)] + pub _ini_file: String, + #[length_hint(40)] + pub ground_file: String, + #[length_hint(40)] + pub gat_file: String, + #[version_equals_or_above(1, 4)] + #[length_hint(40)] + pub _source_file: Option, + #[version_smaller(2, 6)] + pub water_settings: Option, + pub light_settings: LightSettings, + #[version_equals_or_above(1, 6)] + pub ground_top: Option, + #[version_equals_or_above(1, 6)] + pub ground_bottom: Option, + #[version_equals_or_above(1, 6)] + pub ground_left: Option, + #[version_equals_or_above(1, 6)] + pub ground_right: Option, + // TODO: verify version + //`#[version_equals_or_above(2, 6)] + //pub quad_tree: QuadTree, + pub resources: MapResources, +} + +bitflags::bitflags! { + #[derive(Debug)] + pub struct TileFlags: u8 { + const WALKABLE = 0b00000001; + const WATER = 0b00000010; + const SNIPABLE = 0b00000100; + const CLIFF = 0b00001000; + } +} + +impl FromBytes for TileFlags { + fn from_bytes(byte_stream: &mut ByteStream) -> ConversionResult { + match byte_stream.byte::()? { + 0 => Ok(Self::WALKABLE), + 1 => Ok(Self::empty()), + 2 => Ok(Self::WATER), + 3 => Ok(Self::WATER | Self::WALKABLE), + 4 => Ok(Self::WATER | Self::SNIPABLE), + 5 => Ok(Self::CLIFF | Self::SNIPABLE), + 6 => Ok(Self::CLIFF), + invalid => Err(ConversionError::from_message(format!("invalid tile type {invalid}"))), + } + } +} + +#[derive(Debug, FromBytes)] +pub struct Tile { + pub upper_left_height: f32, + pub upper_right_height: f32, + pub lower_left_height: f32, + pub lower_right_height: f32, + pub flags: TileFlags, + pub unused: [u8; 3], +} + +#[derive(FromBytes)] +pub struct GatData { + pub signature: Signature, + #[version] + pub version: Version, + pub map_width: i32, + pub map_height: i32, + #[repeating(self.map_width * self.map_height)] + pub tiles: Vec, +} + +#[derive(FromBytes)] +pub struct GroundData { + pub signature: Signature, + #[version] + pub version: Version, + pub width: i32, + pub height: i32, + pub zoom: f32, + pub texture_count: i32, + pub texture_name_length: i32, + #[repeating(self.texture_count)] + #[length_hint(self.texture_name_length)] + pub textures: Vec, + pub light_map_count: i32, + pub light_map_width: i32, + pub light_map_height: i32, + pub light_map_cells_per_grid: i32, + #[version_equals_or_above(1, 7)] + #[length_hint(self.light_map_count * self.light_map_width * self.light_map_height * 4)] + pub _skip: Option>, + #[version_smaller(1, 7)] + #[length_hint(self.light_map_count * 16)] + pub _skip2: Option>, + pub surface_count: i32, + #[repeating(self.surface_count)] + pub surfaces: Vec, + #[repeating(self.width as usize * self.height as usize)] + pub ground_tiles: Vec, +} + +pub struct GroundTile { + pub upper_left_height: f32, + pub upper_right_height: f32, + pub lower_left_height: f32, + pub lower_right_height: f32, + pub top_surface_index: i32, + pub front_surface_index: i32, + pub right_surface_index: i32, +} + +impl FromBytes for GroundTile { + fn from_bytes(byte_stream: &mut ByteStream) -> ConversionResult { + let upper_left_height = f32::from_bytes(byte_stream).trace::()?; + let upper_right_height = f32::from_bytes(byte_stream).trace::()?; + let lower_left_height = f32::from_bytes(byte_stream).trace::()?; + let lower_right_height = f32::from_bytes(byte_stream).trace::()?; + + let version = byte_stream + .get_metadata::>()? + .ok_or(ConversionError::from_message("version not set"))?; + + let top_surface_index = match version.equals_or_above(1, 7) { + true => i32::from_bytes(byte_stream).trace::()?, + false => i16::from_bytes(byte_stream).trace::()? as i32, + }; + + let front_surface_index = match version.equals_or_above(1, 7) { + true => i32::from_bytes(byte_stream).trace::()?, + false => i16::from_bytes(byte_stream).trace::()? as i32, + }; + + let right_surface_index = match version.equals_or_above(1, 7) { + true => i32::from_bytes(byte_stream).trace::()?, + false => i16::from_bytes(byte_stream).trace::()? as i32, + }; + + Ok(Self { + upper_left_height, + upper_right_height, + lower_left_height, + lower_right_height, + top_surface_index, + front_surface_index, + right_surface_index, + }) + } +} + +#[derive(Copy, Clone, Debug)] +pub enum SurfaceType { + Front, + Right, + Top, +} + +#[derive(FromBytes)] +pub struct Surface { + pub u: [f32; 4], + pub v: [f32; 4], + pub texture_index: i16, + pub light_map_index: i16, + pub color: ColorBGRA, +} + +#[derive(Copy, Clone, Debug)] +pub enum ResourceType { + Object, + LightSource, + SoundSource, + EffectSource, +} + +impl FromBytes for ResourceType { + fn from_bytes(byte_stream: &mut ByteStream) -> ConversionResult { + let index = i32::from_bytes(byte_stream).trace::()?; + match index { + 1 => Ok(ResourceType::Object), + 2 => Ok(ResourceType::LightSource), + 3 => Ok(ResourceType::SoundSource), + 4 => Ok(ResourceType::EffectSource), + _ => Err(ConversionError::from_message(format!("invalid object type {index}"))), + } + } +} + +#[derive(Clone, FromBytes)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct ObjectData { + #[length_hint(40)] + #[version_equals_or_above(1, 3)] + pub name: Option, + #[version_equals_or_above(1, 3)] + pub _animation_type: Option, + #[version_equals_or_above(1, 3)] + pub _animation_speed: Option, + #[version_equals_or_above(1, 3)] + pub _block_type: Option, + // FIX: only if build_version >= 186 + #[version_equals_or_above(2, 6)] + pub _unknown: Option, + #[length_hint(80)] + pub model_name: String, + #[length_hint(80)] + pub _node_name: String, + pub transform: Transform, +} + +#[derive(Clone)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct MapResources { + resources_amount: usize, + pub objects: Vec, + pub light_sources: Vec, + pub sound_sources: Vec, + pub effect_sources: Vec, +} + +impl FromBytes for MapResources { + fn from_bytes(byte_stream: &mut ByteStream) -> ConversionResult { + let resources_amount = i32::from_bytes(byte_stream).trace::()? as usize; + + let mut objects = Vec::new(); + let mut light_sources = Vec::new(); + let mut sound_sources = Vec::new(); + let mut effect_sources = Vec::new(); + + for index in 0..resources_amount { + let resource_type = ResourceType::from_bytes(byte_stream).trace::()?; + + match resource_type { + ResourceType::Object => { + let mut object = ObjectData::from_bytes(byte_stream).trace::()?; + // offset the objects slightly to avoid depth buffer fighting + object.transform.position += Vector3::new(0.0, 0.0005, 0.0) * index as f32; + objects.push(object); + } + ResourceType::LightSource => { + let mut light_source = LightSource::from_bytes(byte_stream).trace::()?; + light_source.position.y = -light_source.position.y; + light_sources.push(light_source); + } + ResourceType::SoundSource => { + let mut sound_source = SoundSource::from_bytes(byte_stream).trace::()?; + sound_source.position.y = -sound_source.position.y; + + if sound_source.cycle.is_none() { + sound_source.cycle = Some(4.0); + } + + sound_sources.push(sound_source); + } + ResourceType::EffectSource => { + let mut effect_source = EffectSource::from_bytes(byte_stream).trace::()?; + effect_source.position.y = -effect_source.position.y; + effect_sources.push(effect_source); + } + } + } + + Ok(Self { + resources_amount, + objects, + light_sources, + sound_sources, + effect_sources, + }) + } +} + +#[derive(Clone, Debug, FromBytes)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct WaterSettings { + #[version_equals_or_above(1, 3)] + pub water_level: Option, + #[version_equals_or_above(1, 8)] + pub water_type: Option, + #[version_equals_or_above(1, 8)] + pub wave_height: Option, + #[version_equals_or_above(1, 8)] + pub wave_speed: Option, + #[version_equals_or_above(1, 8)] + pub wave_pitch: Option, + #[version_equals_or_above(1, 9)] + pub water_animation_speed: Option, +} + +#[derive(Clone, Debug, FromBytes)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct LightSettings { + #[version_equals_or_above(1, 5)] + pub light_longitude: Option, + #[version_equals_or_above(1, 5)] + pub light_latitude: Option, + #[version_equals_or_above(1, 5)] + pub diffuse_color: Option, + #[version_equals_or_above(1, 5)] + pub ambient_color: Option, + #[version_equals_or_above(1, 7)] + pub light_intensity: Option, +} + +#[derive(Clone, ByteConvertable)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +#[cfg_attr(feature = "interface", derive(korangar_interface::windows::PrototypeWindow))] +#[window_title("Light Source")] +pub struct LightSource { + #[length_hint(80)] + pub name: String, + pub position: Vector3, + pub color: ColorRGB, + pub range: f32, +} + +#[derive(Clone, ByteConvertable)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +#[cfg_attr(feature = "interface", derive(korangar_interface::windows::PrototypeWindow))] +#[window_title("Effect Source")] +pub struct EffectSource { + #[length_hint(80)] + pub name: String, + pub position: Vector3, + pub effect_type: u32, // TODO: fix this + pub emit_speed: f32, + pub _param0: f32, + pub _param1: f32, + pub _param2: f32, + pub _param3: f32, +} + +#[derive(Clone, ByteConvertable)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +#[cfg_attr(feature = "interface", derive(korangar_interface::windows::PrototypeWindow))] +#[window_title("Sound Source")] +pub struct SoundSource { + #[length_hint(80)] + pub name: String, + #[length_hint(80)] + pub sound_file: String, + pub position: Vector3, + pub volume: f32, + pub width: u32, + pub height: u32, + pub range: f32, + #[version_equals_or_above(2, 0)] + pub cycle: Option, +} diff --git a/ragnarok_formats/src/model.rs b/ragnarok_formats/src/model.rs new file mode 100644 index 000000000..636fc2788 --- /dev/null +++ b/ragnarok_formats/src/model.rs @@ -0,0 +1,133 @@ +use cgmath::{Matrix3, Quaternion, Vector2, Vector3}; +use ragnarok_bytes::{ByteStream, ConversionError, ConversionResult, ConversionResultExt, FromBytes, FromBytesExt}; + +use crate::signature::Signature; +use crate::version::{InternalVersion, MajorFirst, Version}; + +/// A string that can either have a fixed lenght or be length prefixed, based on +/// the file format version. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ModelString { + pub inner: String, +} + +impl FromBytes for ModelString { + fn from_bytes(byte_stream: &mut ByteStream) -> ConversionResult { + let inner = if byte_stream + .get_metadata::>()? + .ok_or(ConversionError::from_message("version not set"))? + .equals_or_above(2, 2) + { + let length = u32::from_bytes(byte_stream).trace::()? as usize; + let mut inner = String::from_n_bytes(byte_stream, length).trace::()?; + // need to remove the last character for some reason + inner.pop(); + inner + } else { + String::from_n_bytes(byte_stream, LENGTH).trace::()? + }; + + Ok(Self { inner }) + } +} + +#[cfg(feature = "interface")] +impl korangar_interface::elements::PrototypeElement for ModelString +where + App: korangar_interface::application::Application, +{ + fn to_element(&self, display: String) -> korangar_interface::elements::ElementCell { + self.inner.to_element(display) + } +} + +#[derive(Debug, FromBytes)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct PositionKeyframeData { + pub frame: u32, + pub position: Vector3, +} + +#[derive(Clone, Debug, FromBytes)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct RotationKeyframeData { + pub frame: u32, + pub quaternions: Quaternion, +} + +#[derive(Debug, FromBytes)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct FaceData { + pub vertex_position_indices: [u16; 3], + pub texture_coordinate_indices: [u16; 3], + pub texture_index: u16, + pub padding: u16, + pub two_sided: i32, + pub smooth_group: i32, +} + +#[derive(Debug, FromBytes)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct TextureCoordinateData { + #[version_equals_or_above(1, 2)] + pub color: Option, + pub coordinates: Vector2, // possibly wrong if version < 1.2 +} + +#[derive(Debug, FromBytes)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct NodeData { + pub node_name: ModelString<40>, + pub parent_node_name: ModelString<40>, // This is where 2.2 starts failing + pub texture_count: u32, + #[repeating(self.texture_count)] + pub texture_indices: Vec, + #[hidden_element] + pub offset_matrix: Matrix3, + pub translation1: Vector3, + pub translation2: Vector3, + pub rotation_angle: f32, + pub rotation_axis: Vector3, + pub scale: Vector3, + pub vertex_position_count: u32, + #[repeating(self.vertex_position_count)] + pub vertex_positions: Vec>, + pub texture_coordinate_count: u32, + #[repeating(self.texture_coordinate_count)] + pub texture_coordinates: Vec, + pub face_count: u32, + #[repeating(self.face_count)] + pub faces: Vec, + #[version_equals_or_above(2, 5)] // unsure what vesion this is supposed to be (must be > 1.5) + pub position_keyframe_count: Option, + #[repeating(self.position_keyframe_count.unwrap_or_default())] + pub position_keyframes: Vec, + pub rotation_keyframe_count: u32, + #[repeating(self.rotation_keyframe_count)] + pub rotation_keyframes: Vec, +} + +#[derive(Debug, FromBytes)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct ModelData { + pub signature: Signature, + #[version] + pub version: Version, + pub animation_length: u32, + pub shade_type: u32, + #[version_equals_or_above(1, 4)] + pub alpha: Option, + #[version_smaller(2, 2)] + pub reserved0: Option<[u8; 16]>, + #[version_equals_or_above(2, 2)] + pub reserved1: Option<[u8; 4]>, + pub texture_count: u32, + #[repeating(self.texture_count)] + pub texture_names: Vec>, + #[version_equals_or_above(2, 2)] + pub skip: Option, + pub root_node_name: ModelString<40>, + pub node_count: u32, + #[repeating(self.node_count)] + pub nodes: Vec, +} diff --git a/ragnarok_formats/src/signature.rs b/ragnarok_formats/src/signature.rs new file mode 100644 index 000000000..de2655fac --- /dev/null +++ b/ragnarok_formats/src/signature.rs @@ -0,0 +1,38 @@ +use ragnarok_bytes::{ByteStream, ConversionError, ConversionResult, FixedByteSize, FromBytes, ToBytes}; + +#[derive(Debug, Clone)] +pub struct Signature; + +impl FixedByteSize for Signature { + fn size_in_bytes() -> usize { + MAGIC.len() + } +} + +impl FromBytes for Signature { + fn from_bytes(byte_stream: &mut ByteStream) -> ConversionResult + where + Self: Sized, + { + let bytes = byte_stream.slice::(MAGIC.len())?; + match bytes == MAGIC { + true => Ok(Self), + false => Err(ConversionError::from_message("invalid magic number")), + } + } +} + +impl ToBytes for Signature { + fn to_bytes(&self) -> ConversionResult> { + Ok(MAGIC.to_vec()) + } +} + +#[cfg(feature = "interface")] +impl korangar_interface::elements::PrototypeElement + for Signature +{ + fn to_element(&self, display: String) -> korangar_interface::elements::ElementCell { + std::str::from_utf8(MAGIC).unwrap().to_element(display) + } +} diff --git a/ragnarok_formats/src/sprite.rs b/ragnarok_formats/src/sprite.rs new file mode 100644 index 000000000..73505b181 --- /dev/null +++ b/ragnarok_formats/src/sprite.rs @@ -0,0 +1,115 @@ +use ragnarok_bytes::{ByteStream, ConversionError, ConversionResult, ConversionResultExt, FromBytes, FromBytesExt}; + +use crate::signature::Signature; +use crate::version::{InternalVersion, MinorFirst, Version}; + +#[derive(Clone, Debug)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct PaletteImageData { + pub width: u16, + pub height: u16, + pub data: EncodedData, +} + +#[derive(Clone, Debug)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct EncodedData(pub Vec); + +impl FromBytes for PaletteImageData { + fn from_bytes(byte_stream: &mut ByteStream) -> ConversionResult + where + Self: Sized, + { + let width = u16::from_bytes(byte_stream).trace::()?; + let height = u16::from_bytes(byte_stream).trace::()?; + + let data = match width as usize * height as usize { + 0 => Vec::new(), + image_size + if byte_stream + .get_metadata::>()? + .ok_or(ConversionError::from_message("version not set"))? + .smaller(2, 1) => + { + Vec::from_n_bytes(byte_stream, image_size).trace::()? + } + image_size => { + let mut data = vec![0; image_size]; + let mut encoded = u16::from_bytes(byte_stream).trace::()?; + let mut next = 0; + + while next < image_size && encoded > 0 { + let byte = byte_stream.byte::()?; + encoded -= 1; + + if byte == 0 { + let length = usize::max(byte_stream.byte::()? as usize, 1); + encoded -= 1; + + if next + length > image_size { + return Err(ConversionError::from_message("too much data encoded in palette image")); + } + + next += length; + } else { + data[next] = byte; + next += 1; + } + } + + if next != image_size || encoded > 0 { + return Err(ConversionError::from_message("badly encoded palette image")); + } + + data + } + }; + + Ok(Self { + width, + height, + data: EncodedData(data), + }) + } +} + +#[derive(Clone, Debug, FromBytes)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct RgbaImageData { + pub width: u16, + pub height: u16, + #[length_hint(self.width as usize * self.height as usize * 4)] + pub data: Vec, +} + +#[derive(Copy, Clone, Debug, Default, FromBytes)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct PaletteColor { + pub red: u8, + pub green: u8, + pub blue: u8, + pub reserved: u8, +} + +#[derive(Clone, Debug, FromBytes)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct Palette { + pub colors: [PaletteColor; 256], +} + +#[derive(Clone, Debug, FromBytes)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct SpriteData { + pub signature: Signature, + #[version] + pub version: Version, + pub palette_image_count: u16, + #[version_equals_or_above(1, 2)] + pub rgba_image_count: Option, + #[repeating(self.palette_image_count)] + pub palette_image_data: Vec, + #[repeating(self.rgba_image_count.unwrap_or_default())] + pub rgba_image_data: Vec, + #[version_equals_or_above(1, 1)] + pub palette: Option, +} diff --git a/ragnarok_formats/src/transform.rs b/ragnarok_formats/src/transform.rs new file mode 100644 index 000000000..138420b65 --- /dev/null +++ b/ragnarok_formats/src/transform.rs @@ -0,0 +1,66 @@ +use std::ops::Add; + +use cgmath::{Deg, Rad, Vector3}; +use ragnarok_bytes::{ByteStream, ConversionResult, ConversionResultExt, FromBytes}; + +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub struct Transform { + pub position: Vector3, + #[hidden_element] // TODO: unhide + pub rotation: Vector3>, + pub scale: Vector3, +} + +impl FromBytes for Transform { + fn from_bytes(byte_stream: &mut ByteStream) -> ConversionResult { + let mut position = >::from_bytes(byte_stream).trace::()?; + let rotation = >::from_bytes(byte_stream).trace::()?; + let scale = >::from_bytes(byte_stream).trace::()?; + + // Convert from a standard Rust float (which is in degrees) to a stronger cgmath + // type that also represents degrees. We can then easily convert it to + // radiants. + let rotation = rotation.map(|degrees| Deg(degrees).into()); + + // TODO: make this nicer + position.y = -position.y; + + Ok(Transform { position, rotation, scale }) + } +} + +impl Transform { + pub fn from(position: Vector3, rotation: Vector3>, scale: Vector3) -> Self { + let rotation = rotation.map(|degrees| degrees.into()); + Self { position, rotation, scale } + } + + pub fn position(position: Vector3) -> Self { + Self { + position, + rotation: Vector3::new(Rad(0.0), Rad(0.0), Rad(0.0)), + scale: Vector3::new(1.0, 1.0, 1.0), + } + } +} + +impl Add for Transform { + type Output = Self; + + fn add(self, other: Self) -> Self { + Self { + position: self.position + other.position, + rotation: Vector3::new( + self.rotation.x + other.rotation.x, + self.rotation.y + other.rotation.y, + self.rotation.z + other.rotation.z, + ), + scale: Vector3::new( + self.scale.x * other.scale.x, + self.scale.y * other.scale.y, + self.scale.z * other.scale.z, + ), + } + } +} diff --git a/korangar/src/loaders/version.rs b/ragnarok_formats/src/version.rs similarity index 99% rename from korangar/src/loaders/version.rs rename to ragnarok_formats/src/version.rs index 1131d4012..9efb4f9d2 100644 --- a/korangar/src/loaders/version.rs +++ b/ragnarok_formats/src/version.rs @@ -5,8 +5,10 @@ use ragnarok_bytes::{ByteStream, ConversionResult, FromBytes}; #[derive(Copy, Clone, Debug)] pub struct MajorFirst; + #[derive(Copy, Clone, Debug)] pub struct MinorFirst; + #[derive(Copy, Clone, Debug)] pub struct Version { pub major: u8, diff --git a/ragnarok_packets/src/lib.rs b/ragnarok_packets/src/lib.rs index 46238b76e..ec9ddfc47 100644 --- a/ragnarok_packets/src/lib.rs +++ b/ragnarok_packets/src/lib.rs @@ -1797,7 +1797,6 @@ pub struct QuestDetails { #[derive(Debug, Clone, ByteConvertable)] #[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] pub struct Quest { - #[packet_length] pub quest_id: u32, pub active: u8, pub remaining_time: u32, // TODO: double check these diff --git a/ragnarok_procedural/src/helper.rs b/ragnarok_procedural/src/helper.rs index 64a68f77d..10a682702 100644 --- a/ragnarok_procedural/src/helper.rs +++ b/ragnarok_procedural/src/helper.rs @@ -145,7 +145,7 @@ pub fn byte_convertable_helper(data_struct: DataStruct) -> (Vec, Ve Some(function) => { quote! { let #field_variable = match byte_stream - .get_metadata::>()? + .get_metadata::>()? .ok_or(ragnarok_bytes::ConversionError::from_message("version not set"))? .#function { true => Some(#from_implementation), @@ -173,7 +173,7 @@ pub fn byte_convertable_helper(data_struct: DataStruct) -> (Vec, Ve if is_version { from_bytes_implementations.push( - quote!(*byte_stream.get_metadata_mut::>()? = Some(InternalVersion::from(#field_variable));), + quote!(*byte_stream.get_metadata_mut::>()? = Some(ragnarok_formats::version::InternalVersion::from(#field_variable));), ); } } diff --git a/ragnarok_procedural/src/lib.rs b/ragnarok_procedural/src/lib.rs index 8aef802a3..7796c1677 100644 --- a/ragnarok_procedural/src/lib.rs +++ b/ragnarok_procedural/src/lib.rs @@ -35,7 +35,6 @@ pub fn derive_fixed_byte_size(token_stream: InterfaceTokenStream) -> InterfaceTo #[proc_macro_derive( ByteConvertable, attributes( - packet_length, length_hint, repeating, repeating_remaining, @@ -65,7 +64,6 @@ pub fn derive_byte_convertable(token_stream: InterfaceTokenStream) -> InterfaceT #[proc_macro_derive( FromBytes, attributes( - packet_length, length_hint, repeating, repeating_remaining, @@ -95,7 +93,6 @@ pub fn derive_from_bytes(token_stream: InterfaceTokenStream) -> InterfaceTokenSt #[proc_macro_derive( ToBytes, attributes( - packet_length, length_hint, repeating, repeating_remaining,