first pass
parent
d41dbd36a0
commit
a0fb4d60bd
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
**swp
|
|
@ -0,0 +1,61 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "gmflib"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"gmfmacros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gmfmacros"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gmftool"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"gmflib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "704df27628939572cd88d33f171cd6f896f4eaca85252c6e0a72d8d8287ee86f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
|
@ -0,0 +1,7 @@
|
|||
[workspace]
|
||||
|
||||
members = [
|
||||
"gmflib",
|
||||
"gmftool",
|
||||
"gmfmacros",
|
||||
]
|
|
@ -0,0 +1,79 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "binread"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16598dfc8e6578e9b597d9910ba2e73618385dc9f4b1d43dd92c349d6be6418f"
|
||||
dependencies = [
|
||||
"binread_derive",
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "binread_derive"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d9672209df1714ee804b1f4d4f68c8eb2a90b1f7a07acf472f88ce198ef1fed"
|
||||
dependencies = [
|
||||
"either",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||
|
||||
[[package]]
|
||||
name = "gmftools"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"binread",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "704df27628939572cd88d33f171cd6f896f4eaca85252c6e0a72d8d8287ee86f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "gmflib"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
gmfmacros = { path = "../gmfmacros" }
|
|
@ -0,0 +1,4 @@
|
|||
mod machinery;
|
||||
pub mod types;
|
||||
|
||||
pub use types::*;
|
|
@ -0,0 +1,195 @@
|
|||
use std::fmt;
|
||||
|
||||
pub struct ReadError {
|
||||
pub msg: String,
|
||||
pub stack: Vec<ParseFrame>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for ReadError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let stack = self.stack.iter().map(|pf| format!("{}@{:x}", pf.name, pf.loc)).collect::<Vec<String>>().join("->");
|
||||
f.debug_struct("ReadError")
|
||||
.field("msg", &self.msg)
|
||||
.field("stack", &stack)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ParseFrame {
|
||||
pub name: String,
|
||||
pub loc: usize,
|
||||
}
|
||||
|
||||
pub type ReadResult<T> = Result<T, ReadError>;
|
||||
|
||||
pub struct GMFElementDescriptor {
|
||||
fields: Vec<GMFElementField>,
|
||||
}
|
||||
|
||||
pub struct GMFElementField {
|
||||
rust_name: &'static str,
|
||||
gma_repr: GMARepr,
|
||||
}
|
||||
|
||||
pub enum GMARepr {
|
||||
Named(&'static str),
|
||||
}
|
||||
|
||||
pub trait GMFElement: Sized {
|
||||
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self>;
|
||||
//fn descriptor() -> GMFElementDescriptor;
|
||||
}
|
||||
|
||||
impl<T: GMFElement> GMFElement for Vec<T> {
|
||||
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
|
||||
let count: u32 = r.read("count")?;
|
||||
(0..count).map(|_| r.read("data")).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: GMFElement, const N: usize> GMFElement for [T; N] {
|
||||
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
|
||||
(0..N).map(|_| r.read("data")).collect::<ReadResult<Vec<T>>>()?.try_into().map_err(|_| r.error("eof"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<T: GMFElement> GMFElement for Option<T> {
|
||||
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
|
||||
let count: u32 = r.read("count")?;
|
||||
if count != 0 {
|
||||
return Ok(Some(r.read("data")?));
|
||||
}
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
|
||||
impl GMFElement for u8 {
|
||||
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
|
||||
Ok(r.bytes(1)?[0])
|
||||
}
|
||||
}
|
||||
|
||||
impl GMFElement for u32 {
|
||||
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
|
||||
let buf: [u8; 4] = r.bytes(4)?.try_into().unwrap();
|
||||
Ok(u32::from_le_bytes(buf))
|
||||
}
|
||||
}
|
||||
|
||||
impl GMFElement for f32 {
|
||||
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
|
||||
let buf: [u8; 4] = r.bytes(4)?.try_into().unwrap();
|
||||
Ok(f32::from_le_bytes(buf))
|
||||
}
|
||||
}
|
||||
|
||||
impl GMFElement for String {
|
||||
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
|
||||
let len: u32 = r.read("len")?;
|
||||
r.push("bytes".to_string());
|
||||
let bytes = r.bytes(len as usize)?;
|
||||
let bytes: Vec<u8> = bytes.into_iter().filter(|&b| b != 0).collect();
|
||||
let res = std::str::from_utf8(&bytes).map_err(|_| r.error("invalid string")).map(String::from);
|
||||
r.pop();
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ReadStream<R: std::io::Read> {
|
||||
backing: R,
|
||||
stack: Vec<ParseFrame>,
|
||||
pos: usize,
|
||||
}
|
||||
|
||||
impl<R: std::io::Read> ReadStream<R> {
|
||||
pub fn new(r: R) -> Self {
|
||||
Self {
|
||||
backing: r,
|
||||
pos: 0,
|
||||
stack: vec![
|
||||
ParseFrame{name: "root".into(), loc: 0},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
//fn context(&mut self, s: String) {
|
||||
// self.stack.last_mut().unwrap().name = s;
|
||||
// self.stack.last_mut().unwrap().loc = self.pos;
|
||||
//}
|
||||
|
||||
fn push(&mut self, s: String) {
|
||||
self.stack.push(ParseFrame {
|
||||
name: s,
|
||||
loc: self.pos,
|
||||
});
|
||||
}
|
||||
|
||||
fn pop(&mut self) {
|
||||
self.stack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
impl <R: std::io::Read> ReadStream<R> {
|
||||
pub fn bytes(&mut self, n: usize) -> ReadResult<Vec<u8>> {
|
||||
let mut buf: Vec<u8> = vec![0; n];
|
||||
self.pos += n;
|
||||
match self.backing.read(&mut buf) {
|
||||
Ok(_) => return Ok(buf),
|
||||
Err(_) => return Err(self.error("eof")),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tlv(&mut self) -> ReadResult<TLV> {
|
||||
let start_pos = self.pos;
|
||||
let tag: u32 = self.read("tag")?;
|
||||
let flags: u32 = self.read("flags")?;
|
||||
let length: u32 = self.read("length")?;
|
||||
Ok(TLV {
|
||||
tag, flags, length, start_pos,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn read<T: GMFElement>(&mut self, ctx: &str) -> ReadResult<T> {
|
||||
self.push(ctx.to_string());
|
||||
let res = T::read(self);
|
||||
self.pop();
|
||||
res
|
||||
}
|
||||
|
||||
pub fn error<S: std::string::ToString>(&self, msg: S) -> ReadError {
|
||||
ReadError {
|
||||
msg: msg.to_string(),
|
||||
stack: self.stack.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TLV {
|
||||
pub tag: u32,
|
||||
pub flags: u32,
|
||||
pub length: u32,
|
||||
|
||||
start_pos: usize,
|
||||
}
|
||||
|
||||
impl TLV {
|
||||
pub fn error(&self, msg: &str) -> ReadError {
|
||||
ReadError {
|
||||
msg: msg.to_string(),
|
||||
stack: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check<R: std::io::Read>(&self, r: &mut ReadStream<R>) -> ReadResult<()> {
|
||||
let expected = self.start_pos + self.length as usize + 12usize;
|
||||
if expected != r.pos {
|
||||
return Err(ReadError {
|
||||
msg: format!("tlv length mismatch: expected end at {}, got at {}", expected, r.pos),
|
||||
stack: vec![],
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,435 @@
|
|||
use gmfmacros::GMFElement;
|
||||
|
||||
use crate::machinery::{ReadResult, ReadStream, GMFElement};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GMI {
|
||||
pub version: u32,
|
||||
pub model_type: ModelType,
|
||||
pub unk1: u32,
|
||||
pub unk2: u32,
|
||||
|
||||
pub scene: Scene,
|
||||
pub materials: MaterialList,
|
||||
pub objects: ObjectList,
|
||||
}
|
||||
|
||||
impl GMI {
|
||||
pub fn parse<R: std::io::Read>(r: R) -> ReadResult<Self> {
|
||||
let mut r = ReadStream::new(r);
|
||||
r.read("gmi")
|
||||
}
|
||||
}
|
||||
|
||||
impl GMFElement for GMI {
|
||||
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
|
||||
if r.bytes(3)? != b"GMI".to_vec() {
|
||||
return Err(r.error("invaid magic"));
|
||||
}
|
||||
let version: u32 = r.read("version")?;
|
||||
let model_type = r.read("model_type")?;
|
||||
let unk1: u32 = r.read("unk1")?;
|
||||
if unk1 != 0 {
|
||||
return Err(r.error("invalid unk1"));
|
||||
}
|
||||
let unk2: u32 = r.read("unk2")?;
|
||||
if unk2 != 15 {
|
||||
return Err(r.error("invalid unk2"));
|
||||
}
|
||||
Ok(Self {
|
||||
version, model_type, unk1, unk2,
|
||||
scene: r.read("scene")?,
|
||||
materials: r.read("material_list")?,
|
||||
objects: r.read("object_list")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug,GMFElement)]
|
||||
pub struct Color {
|
||||
pub r: u8,
|
||||
pub g: u8,
|
||||
pub b: u8,
|
||||
pub a: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug,GMFElement)]
|
||||
pub enum MapKind {
|
||||
Diffuse = 1,
|
||||
SelfIllum = 5,
|
||||
Opacity = 6,
|
||||
}
|
||||
|
||||
#[derive(Debug,GMFElement)]
|
||||
pub enum MapType {
|
||||
Screen = 4,
|
||||
}
|
||||
|
||||
#[derive(Debug,GMFElement)]
|
||||
pub enum BitmapFilter {
|
||||
Pyramidal = 0,
|
||||
SAT = 1,
|
||||
}
|
||||
|
||||
#[derive(Debug, GMFElement)]
|
||||
pub enum ModelType {
|
||||
BasicModel = 1,
|
||||
}
|
||||
|
||||
#[derive(Debug,GMFElement)]
|
||||
#[gmf_tagged(1,2)]
|
||||
pub struct Scene {
|
||||
pub filename: String,
|
||||
pub first_frame: u32,
|
||||
pub last_frame: u32,
|
||||
pub frame_speed: u32,
|
||||
pub ticks_per_frame: u32,
|
||||
pub background: Color,
|
||||
pub ambient: Color,
|
||||
}
|
||||
|
||||
#[derive(Debug,GMFElement)]
|
||||
#[gmf_tagged(7, 2)]
|
||||
pub struct MaterialList {
|
||||
pub materials: Vec<Material>,
|
||||
}
|
||||
|
||||
#[derive(Debug,GMFElement)]
|
||||
#[gmf_tagged(8, 2)]
|
||||
pub struct Material {
|
||||
pub ref_no: u32,
|
||||
pub name: String,
|
||||
pub class: String,
|
||||
pub ambient: Color,
|
||||
pub diffuse: Color,
|
||||
pub specular: Color,
|
||||
pub shine: f32,
|
||||
pub shine_strength: f32,
|
||||
pub wiresize: f32,
|
||||
pub transparency: f32,
|
||||
pub shading: Shading,
|
||||
pub xp_falloff: f32,
|
||||
pub selfillum: f32,
|
||||
pub falloff: Falloff,
|
||||
pub xp_type: XPType,
|
||||
pub textures: Option<TextureList>,
|
||||
pub sub: Option<MaterialList>,
|
||||
}
|
||||
|
||||
#[derive(Debug,GMFElement)]
|
||||
pub enum Shading {
|
||||
Other = 0,
|
||||
Blinn = 0xc,
|
||||
}
|
||||
|
||||
#[derive(Debug, GMFElement)]
|
||||
pub enum Falloff {
|
||||
In = 0,
|
||||
}
|
||||
|
||||
#[derive(Debug,GMFElement)]
|
||||
pub enum XPType {
|
||||
Other = 0,
|
||||
Filter = 1,
|
||||
}
|
||||
|
||||
#[derive(Debug,GMFElement)]
|
||||
#[gmf_tagged(14, 2)]
|
||||
pub struct TextureList {
|
||||
pub textures: Vec<Texture>,
|
||||
}
|
||||
|
||||
#[derive(Debug,GMFElement)]
|
||||
#[gmf_tagged(15, 4)]
|
||||
pub struct Texture {
|
||||
pub name: String,
|
||||
pub class: String,
|
||||
pub bitmap: String,
|
||||
pub amount: f32,
|
||||
pub kind: MapKind,
|
||||
pub map_type: MapType,
|
||||
pub u_offset: f32,
|
||||
pub v_offset: f32,
|
||||
pub u_tiling: f32,
|
||||
pub v_tiling: f32,
|
||||
pub angle: f32,
|
||||
pub blur: f32,
|
||||
pub blur_offset: f32,
|
||||
pub noise_amt: f32,
|
||||
pub noise_size: f32,
|
||||
pub noise_level: u32,
|
||||
pub noise_phase: f32,
|
||||
pub invert: u32,
|
||||
pub unknown: u32,
|
||||
pub filter: BitmapFilter,
|
||||
pub channel: u32,
|
||||
pub sub: Option<TextureList>,
|
||||
}
|
||||
|
||||
#[derive(Debug,GMFElement)]
|
||||
#[gmf_tagged(18,2)]
|
||||
pub struct ObjectList {
|
||||
pub objects: Vec<Object>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Object {
|
||||
Geometry(GeometryObject),
|
||||
Light(LightObject),
|
||||
AttachmentPoint(AttachmentPointObject),
|
||||
ConstraintSolver(ConstraintSolverObject),
|
||||
Simulation(SimulationObject),
|
||||
RBCollection(RBCollectionObject),
|
||||
}
|
||||
|
||||
impl GMFElement for Object {
|
||||
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
|
||||
let tlv = r.tlv()?;
|
||||
let (res, check) = match (tlv.tag, tlv.flags) {
|
||||
(2, 4) => (Object::Geometry(r.read("geometry")?), false),
|
||||
(5, 3) => (Object::Light(r.read("light")?), false),
|
||||
(21, 2) => (Object::AttachmentPoint(r.read("attachmentpoint")?), true),
|
||||
(42, 3) => (Object::ConstraintSolver(r.read("constraintsolver")?), false),
|
||||
(30, 2) => (Object::Simulation(r.read("simulation")?), false),
|
||||
_ => return Err(r.error(format!("unknown object type ({}, {})", tlv.tag, tlv.flags))),
|
||||
};
|
||||
println!("{:#?}", res);
|
||||
if check {
|
||||
tlv.check(r)?;
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug,GMFElement)]
|
||||
pub struct GeometryObject {
|
||||
pub name: String,
|
||||
pub parent: String,
|
||||
pub shade_verts: u8,
|
||||
pub tm: TransformMatrix,
|
||||
pub mesh: Mesh,
|
||||
}
|
||||
|
||||
#[derive(Debug,GMFElement)]
|
||||
#[gmf_tagged(17, 2)]
|
||||
pub struct TransformMatrix {
|
||||
pub name: String,
|
||||
pub data: [f32; 12],
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Mesh {
|
||||
pub time: u32,
|
||||
pub vertex_count: u32,
|
||||
pub face_count: u32,
|
||||
pub tvertex_count: u32,
|
||||
pub cvertex_count: u32,
|
||||
pub material_ref: u32,
|
||||
|
||||
pub vertices: Vec<Point>,
|
||||
pub faces: Vec<Face>,
|
||||
|
||||
pub tvertices: Vec<Point>,
|
||||
pub tfaces: Vec<TFace>,
|
||||
|
||||
pub channels: Option<Vec<TextureChannel>>,
|
||||
|
||||
pub cvertices: Vec<Point>,
|
||||
pub cfaces: Vec<TFace>,
|
||||
|
||||
pub normals: Vec<FaceNormal>,
|
||||
|
||||
pub backface_cull: u32,
|
||||
}
|
||||
|
||||
impl GMFElement for Mesh {
|
||||
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
|
||||
let tlv = r.tlv()?;
|
||||
if tlv.tag != 16 || tlv.flags != 4 {
|
||||
return Err(r.error("unexpected tag/flags (wanted 16/4)"));
|
||||
}
|
||||
|
||||
let time = r.read("time")?;
|
||||
let vertex_count = r.read("vertex_count")?;
|
||||
let face_count = r.read("face_count")?;
|
||||
let tvertex_count = r.read("tvertex_count")?;
|
||||
let cvertex_count = r.read("cvertex_count")?;
|
||||
let material_ref = r.read("material_ref")?;
|
||||
|
||||
let res = Self {
|
||||
time, vertex_count, face_count, tvertex_count,
|
||||
cvertex_count, material_ref,
|
||||
|
||||
vertices: (0..vertex_count).map(|_| r.read("vertices")).collect::<ReadResult<Vec<Point>>>()?,
|
||||
faces: (0..face_count).map(|_| r.read("faces")).collect::<ReadResult<Vec<Face>>>()?,
|
||||
tvertices: (0..tvertex_count).map(|_| r.read("tvertices")).collect::<ReadResult<Vec<Point>>>()?,
|
||||
tfaces: if tvertex_count > 0 {
|
||||
(0..face_count).map(|_| r.read("tfaces")).collect::<ReadResult<Vec<TFace>>>()?
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
channels: r.read("channels")?,
|
||||
cvertices: (0..cvertex_count).map(|_| r.read("cvertices")).collect::<ReadResult<Vec<Point>>>()?,
|
||||
cfaces: if cvertex_count > 0 {
|
||||
(0..face_count).map(|_| r.read("cfaces")).collect::<ReadResult<Vec<TFace>>>()?
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
normals: (0..face_count).map(|_| r.read("normals")).collect::<ReadResult<Vec<FaceNormal>>>()?,
|
||||
|
||||
backface_cull: r.read("backface_cull")?,
|
||||
};
|
||||
// Broken for stock files.
|
||||
//tlv.check(r)?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug,GMFElement)]
|
||||
pub struct Point {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub z: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug,GMFElement)]
|
||||
pub struct Face {
|
||||
pub a: u32,
|
||||
pub b: u32,
|
||||
pub c: u32,
|
||||
pub mtlid: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug,GMFElement)]
|
||||
pub struct TFace {
|
||||
pub a: u32,
|
||||
pub b: u32,
|
||||
pub c: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug,GMFElement)]
|
||||
pub struct FaceNormal {
|
||||
pub face: [f32; 3],
|
||||
pub vertex: [[f32; 3]; 3],
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TextureChannel {
|
||||
pub unk1: u32,
|
||||
pub tvertex_count: u32,
|
||||
pub unk2: u32,
|
||||
pub face_count: u32,
|
||||
pub unk3: u32,
|
||||
pub unk4: u32,
|
||||
pub tvertex_count2: u32,
|
||||
pub face_count2: u32,
|
||||
|
||||
pub tvertices: Vec<Point>,
|
||||
pub tfaces: Vec<TFace>,
|
||||
}
|
||||
|
||||
impl GMFElement for TextureChannel {
|
||||
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
|
||||
let unk1 = r.read("unk1")?;
|
||||
let tvertex_count = r.read("tvertex_count")?;
|
||||
let unk2 = r.read("unk2")?;
|
||||
let face_count = r.read("face_count")?;
|
||||
let unk3 = r.read("unk3")?;
|
||||
let unk4 = r.read("unk4")?;
|
||||
let tvertex_count2 = r.read("tvertex_count2")?;
|
||||
let face_count2 = r.read("face_count2")?;
|
||||
|
||||
let res = Self {
|
||||
unk1, tvertex_count,
|
||||
unk2, face_count,
|
||||
unk3, unk4,
|
||||
tvertex_count2,
|
||||
face_count2,
|
||||
|
||||
tvertices: (0..tvertex_count).map(|_| r.read("tvertices")).collect::<ReadResult<Vec<_>>>()?,
|
||||
tfaces: if tvertex_count > 0 {
|
||||
(0..face_count).map(|_| r.read("tfaces")).collect::<ReadResult<Vec<_>>>()?
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
};
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug,GMFElement)]
|
||||
pub struct LightObject {
|
||||
pub name: String,
|
||||
pub tm: TransformMatrix,
|
||||
pub target: String,
|
||||
pub light_type: LightType,
|
||||
pub shadows: LightShadows,
|
||||
pub uselight: u32,
|
||||
pub color: Color,
|
||||
pub intensity: f32,
|
||||
pub aspect: f32,
|
||||
pub unk1: [u8; 8],
|
||||
pub attn_start: f32,
|
||||
pub attn_end: f32,
|
||||
pub tdist: f32,
|
||||
pub use_for_attn: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug,GMFElement)]
|
||||
pub enum LightType {
|
||||
Omni = 0,
|
||||
}
|
||||
|
||||
#[derive(Debug,GMFElement)]
|
||||
pub enum LightShadows {
|
||||
Off = 0,
|
||||
}
|
||||
|
||||
#[derive(Debug,GMFElement)]
|
||||
pub struct AttachmentPointObject {
|
||||
pub name: String,
|
||||
pub tm: TransformMatrix,
|
||||
pub user_data: String,
|
||||
}
|
||||
|
||||
#[derive(Debug,GMFElement)]
|
||||
pub struct ConstraintSolverObject {
|
||||
pub name: String,
|
||||
pub threshold: f32,
|
||||
pub rb_collection_name: String,
|
||||
pub constraints: Option<ConstraintList>,
|
||||
}
|
||||
|
||||
#[derive(Debug,GMFElement)]
|
||||
#[gmf_tagged(44, 2)]
|
||||
pub struct ConstraintList {
|
||||
pub constraints: Vec<Constraint>,
|
||||
}
|
||||
|
||||
#[derive(Debug,GMFElement)]
|
||||
pub enum Constraint {
|
||||
Foo = 1,
|
||||
}
|
||||
|
||||
#[derive(Debug,GMFElement)]
|
||||
pub struct SimulationObject {
|
||||
pub name: String,
|
||||
pub gravity: Point,
|
||||
pub worldscale: f32,
|
||||
pub simtolerance: f32,
|
||||
pub resolver: u32,
|
||||
pub incl_drag: u8,
|
||||
pub linear_drag: f32,
|
||||
pub angular_drag: f32,
|
||||
pub incl_deactivator: u8,
|
||||
pub shortfreq: f32,
|
||||
pub longfreq: f32,
|
||||
pub last_subspace: u8,
|
||||
pub updates_per_timestamp: f32,
|
||||
pub collision_pairs: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug,GMFElement)]
|
||||
pub struct RBCollectionObject {
|
||||
pub name: String,
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "gmfmacros"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = "1"
|
||||
quote = "1"
|
||||
proc-macro2 = "1"
|
|
@ -0,0 +1,99 @@
|
|||
use proc_macro::TokenStream;
|
||||
use syn::{parse_macro_input, DeriveInput, parenthesized};
|
||||
use quote::quote;
|
||||
|
||||
struct GMFTagged {
|
||||
paren_token: syn::token::Paren,
|
||||
fields: syn::punctuated::Punctuated<syn::LitInt, syn::Token![,]>,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for GMFTagged {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
|
||||
let content;
|
||||
Ok(Self {
|
||||
paren_token: parenthesized!(content in input),
|
||||
fields: content.parse_terminated(syn::LitInt::parse)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[proc_macro_derive(GMFElement, attributes(gmf_tagged))]
|
||||
pub fn gmf_element_macro(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let ident = input.ident;
|
||||
|
||||
let mut tlv_header = quote! { };
|
||||
let mut tlv_footer = quote! { };
|
||||
|
||||
if let Some(attr) = input.attrs.iter().find(|a| a.path.is_ident("gmf_tagged")) {
|
||||
let tagged = syn::parse2::<GMFTagged>(attr.tokens.clone()).expect("invalid gmf_tagged attribute");
|
||||
if tagged.fields.len() != 2 {
|
||||
panic!("gmf_tagged takes two arguments");
|
||||
}
|
||||
let a: u32 = tagged.fields[0].base10_parse().expect("gmf_tagged arg 1 not a number");
|
||||
let b: u32 = tagged.fields[1].base10_parse().expect("gmf_tagged arg 2 not a number");
|
||||
|
||||
tlv_header = quote! {
|
||||
let tlv = r.tlv()?;
|
||||
if tlv.tag != #a || tlv.flags != #b {
|
||||
return Err(tlv.error("unsupported tag/flags (wanted ...)"));
|
||||
}
|
||||
};
|
||||
tlv_footer = quote! {
|
||||
tlv.check(r)?;
|
||||
};
|
||||
}
|
||||
|
||||
let expanded = match input.data {
|
||||
syn::Data::Enum(en) => {
|
||||
let branches: Vec<proc_macro2::TokenStream> = en.variants.iter().map(|variant| {
|
||||
let vident = &variant.ident;
|
||||
let (_, expr) = variant.discriminant.as_ref().expect("enum variants must have values");
|
||||
quote! {
|
||||
#expr => #ident::#vident,
|
||||
}
|
||||
}).collect();
|
||||
|
||||
quote! {
|
||||
impl GMFElement for #ident {
|
||||
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
|
||||
#tlv_header
|
||||
let v: u32 = r.read("value")?;
|
||||
let res = match v {
|
||||
#(#branches)*
|
||||
_ => return Err(r.error("unknown ")),
|
||||
};
|
||||
#tlv_footer
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
syn::Data::Struct(syn::DataStruct { fields: syn::Fields::Named(fields), .. }) => {
|
||||
let fields: Vec<proc_macro2::TokenStream> = fields.named.iter().map(|field| {
|
||||
let field_ident = &field.ident;
|
||||
let field_ident_str = field_ident.as_ref().unwrap().to_string();
|
||||
let field_ident_quoted = syn::LitStr::new(&field_ident_str, proc_macro2::Span::call_site());
|
||||
quote! {
|
||||
#field_ident: r.read(#field_ident_quoted)?,
|
||||
}
|
||||
}).collect();
|
||||
quote! {
|
||||
impl GMFElement for #ident {
|
||||
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
|
||||
#tlv_header
|
||||
let res = Self {
|
||||
#(#fields)*
|
||||
};
|
||||
#tlv_footer
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => panic!("unimplemented"),
|
||||
};
|
||||
|
||||
TokenStream::from(expanded)
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "gmftool"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
gmflib = { path = "../gmflib" }
|
|
@ -0,0 +1,8 @@
|
|||
fn main() -> std::io::Result<()> {
|
||||
let f = std::fs::File::open("/home/q3k/Games/RA2/Robot Arena 2 v1.4/Arenas/box/boxarena.gmf")?;
|
||||
let gmi = gmflib::GMI::parse(f).unwrap();
|
||||
|
||||
println!("{:#?}", gmi);
|
||||
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in New Issue