boxarena.gmf now decompiles exactly
parent
cbf7e67221
commit
13c9fe6678
|
@ -5,3 +5,7 @@ members = [
|
||||||
"gmftool",
|
"gmftool",
|
||||||
"gmfmacros",
|
"gmfmacros",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
strip = true
|
||||||
|
lto = true
|
||||||
|
|
|
@ -0,0 +1,149 @@
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct WriteError {
|
||||||
|
pub msg: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type WriteResult<T> = Result<T, WriteError>;
|
||||||
|
|
||||||
|
pub trait Serializable: Sized {
|
||||||
|
fn write<W: std::io::Write, S: std::string::ToString>(&self, name: S, r: &mut WriteStream<W>) -> WriteResult<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WriteStream<W: std::io::Write> {
|
||||||
|
backing: W,
|
||||||
|
depth: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Atom {
|
||||||
|
fn atom(&self) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Atom for str {
|
||||||
|
fn atom(&self) -> String {
|
||||||
|
self.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Atom for String {
|
||||||
|
fn atom(&self) -> String {
|
||||||
|
self.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Atom for f32 {
|
||||||
|
fn atom(&self) -> String {
|
||||||
|
format!("{:.6}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for [f32; 3] {
|
||||||
|
fn write<W: std::io::Write, S: std::string::ToString>(&self, name: S, w: &mut WriteStream<W>) -> WriteResult<()> {
|
||||||
|
w.emit(&format!("{} {:.6}\t{:.6}\t{:.6}", name.to_string(), self[0], self[1], self[2]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Atom for i32 {
|
||||||
|
fn atom(&self) -> String {
|
||||||
|
format!("{}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Atom for u32 {
|
||||||
|
fn atom(&self) -> String {
|
||||||
|
format!("{}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Atom for u8 {
|
||||||
|
fn atom(&self) -> String {
|
||||||
|
format!("{}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Atom for usize {
|
||||||
|
fn atom(&self) -> String {
|
||||||
|
format!("{}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: Serializable> Serializable for Option<I> {
|
||||||
|
fn write<W: std::io::Write, S: std::string::ToString>(&self, name: S, w: &mut WriteStream<W>) -> WriteResult<()> {
|
||||||
|
if let Some(s) = self {
|
||||||
|
s.write(name, w)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: Serializable> Serializable for Vec<I> {
|
||||||
|
fn write<W: std::io::Write, S: std::string::ToString>(&self, name: S, w: &mut WriteStream<W>) -> WriteResult<()> {
|
||||||
|
if name.to_string() == "*" {
|
||||||
|
w.emit_pair("*COUNT", &self.len())?;
|
||||||
|
} else {
|
||||||
|
w.emit_pair(&(name.to_string() + "_COUNT"), &self.len())?;
|
||||||
|
}
|
||||||
|
for elem in self.iter() {
|
||||||
|
elem.write(name.to_string(), w)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A: Atom> Serializable for A {
|
||||||
|
fn write<W: std::io::Write, S: std::string::ToString>(&self, name: S, w: &mut WriteStream<W>) -> WriteResult<()> {
|
||||||
|
if self.atom().len() > 0 {
|
||||||
|
w.emit_pair(&name.to_string(), self)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <W: std::io::Write> WriteStream<W> {
|
||||||
|
pub fn new(w: W) -> Self {
|
||||||
|
Self {
|
||||||
|
backing: w,
|
||||||
|
depth: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn error<S: std::string::ToString>(&self, msg: S) -> WriteError {
|
||||||
|
WriteError {
|
||||||
|
msg: msg.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn emit<S: Atom + ?Sized>(&mut self, elem: &S) -> WriteResult<()> {
|
||||||
|
match write!(self.backing, "{}{}\n", "\t".repeat(self.depth), elem.atom()) {
|
||||||
|
Ok(_) => return Ok(()),
|
||||||
|
Err(_) => return Err(self.error("write failed")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn emit_pair<S: Atom + ?Sized, T: Atom + ?Sized>(&mut self, a: &S, b: &T) -> WriteResult<()> {
|
||||||
|
let a = a.atom();
|
||||||
|
if a == "" || a == "*" {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let b = b.atom();
|
||||||
|
match write!(self.backing, "{}{}\t{}\n", "\t".repeat(self.depth), a, b) {
|
||||||
|
Ok(_) => return Ok(()),
|
||||||
|
Err(_) => return Err(self.error("write failed")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push<S: std::string::ToString>(&mut self, el: S) -> WriteResult<()> {
|
||||||
|
self.emit(&el.to_string())?;
|
||||||
|
self.emit("{")?;
|
||||||
|
self.depth += 1;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop(&mut self) -> WriteResult<()> {
|
||||||
|
if self.depth == 0 {
|
||||||
|
return Err(self.error("stack underflow"));
|
||||||
|
}
|
||||||
|
self.depth -= 1;
|
||||||
|
self.emit("}")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,3 @@
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
pub struct ReadError {
|
pub struct ReadError {
|
||||||
pub msg: String,
|
pub msg: String,
|
||||||
pub stack: Vec<ParseFrame>,
|
pub stack: Vec<ParseFrame>,
|
||||||
|
@ -11,8 +9,8 @@ pub struct ParseFrame {
|
||||||
pub loc: usize,
|
pub loc: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for ReadError {
|
impl std::fmt::Debug for ReadError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let stack = self.stack.iter().map(|pf| format!("{}@{:x}", pf.name, pf.loc)).collect::<Vec<String>>().join("->");
|
let stack = self.stack.iter().map(|pf| format!("{}@{:x}", pf.name, pf.loc)).collect::<Vec<String>>().join("->");
|
||||||
f.debug_struct("ReadError")
|
f.debug_struct("ReadError")
|
||||||
.field("msg", &self.msg)
|
.field("msg", &self.msg)
|
||||||
|
@ -23,22 +21,15 @@ impl fmt::Debug for ReadError {
|
||||||
|
|
||||||
pub type ReadResult<T> = Result<T, ReadError>;
|
pub type ReadResult<T> = Result<T, ReadError>;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
pub trait Serializable: Sized {
|
||||||
pub struct WriteError {
|
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self>;
|
||||||
pub msg: String,
|
fn read_parametrized<R: std::io::Read>(r: &mut ReadStream<R>, _params: &[u32]) -> ReadResult<Self> {
|
||||||
}
|
|
||||||
|
|
||||||
pub type WriteResult<T> = Result<T, WriteError>;
|
|
||||||
|
|
||||||
pub trait GMISerializable: Sized {
|
|
||||||
fn read<R: std::io::Read>(r: &mut GMIReadStream<R>) -> ReadResult<Self>;
|
|
||||||
fn read_parametrized<R: std::io::Read>(r: &mut GMIReadStream<R>, params: &[u32]) -> ReadResult<Self> {
|
|
||||||
Err(r.error("called with unexpected parameters"))
|
Err(r.error("called with unexpected parameters"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: GMISerializable> GMISerializable for Vec<T> {
|
impl<T: Serializable> Serializable for Vec<T> {
|
||||||
fn read<R: std::io::Read>(r: &mut GMIReadStream<R>) -> ReadResult<Self> {
|
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
|
||||||
let count: u32 = r.read("count")?;
|
let count: u32 = r.read("count")?;
|
||||||
// Safety check. A reasonable value of ~32MiB of smallest possible
|
// Safety check. A reasonable value of ~32MiB of smallest possible
|
||||||
// element (u32).
|
// element (u32).
|
||||||
|
@ -47,28 +38,27 @@ impl<T: GMISerializable> GMISerializable for Vec<T> {
|
||||||
}
|
}
|
||||||
(0..count).map(|i| r.read(format!("elem[{}]", i))).collect()
|
(0..count).map(|i| r.read(format!("elem[{}]", i))).collect()
|
||||||
}
|
}
|
||||||
fn read_parametrized<R: std::io::Read>(r: &mut GMIReadStream<R>, params: &[u32]) -> ReadResult<Self> {
|
fn read_parametrized<R: std::io::Read>(r: &mut ReadStream<R>, params: &[u32]) -> ReadResult<Self> {
|
||||||
let count = params[0];
|
let count = params[0];
|
||||||
(0..count).map(|i| r.read(format!("elem[{}]", i))).collect()
|
(0..count).map(|i| r.read(format!("elem[{}]", i))).collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: GMISerializable, const N: usize> GMISerializable for [T; N] {
|
impl<T: Serializable, const N: usize> Serializable for [T; N] {
|
||||||
fn read<R: std::io::Read>(r: &mut GMIReadStream<R>) -> ReadResult<Self> {
|
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"))
|
(0..N).map(|_| r.read("data")).collect::<ReadResult<Vec<T>>>()?.try_into().map_err(|_| r.error("eof"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Serializable> Serializable for Option<T> {
|
||||||
impl<T: GMISerializable> GMISerializable for Option<T> {
|
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
|
||||||
fn read<R: std::io::Read>(r: &mut GMIReadStream<R>) -> ReadResult<Self> {
|
|
||||||
let count: u32 = r.read("count")?;
|
let count: u32 = r.read("count")?;
|
||||||
if count != 0 {
|
if count != 0 {
|
||||||
return Ok(Some(r.read("data")?));
|
return Ok(Some(r.read("data")?));
|
||||||
}
|
}
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
fn read_parametrized<R: std::io::Read>(r: &mut GMIReadStream<R>, params: &[u32]) -> ReadResult<Self> {
|
fn read_parametrized<R: std::io::Read>(r: &mut ReadStream<R>, params: &[u32]) -> ReadResult<Self> {
|
||||||
let count = params[0];
|
let count = params[0];
|
||||||
if count != 0 {
|
if count != 0 {
|
||||||
return Ok(Some(r.read("data")?));
|
return Ok(Some(r.read("data")?));
|
||||||
|
@ -77,28 +67,35 @@ impl<T: GMISerializable> GMISerializable for Option<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GMISerializable for u8 {
|
impl Serializable for u8 {
|
||||||
fn read<R: std::io::Read>(r: &mut GMIReadStream<R>) -> ReadResult<Self> {
|
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
|
||||||
Ok(r.bytes(1)?[0])
|
Ok(r.bytes(1)?[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GMISerializable for u32 {
|
impl Serializable for u32 {
|
||||||
fn read<R: std::io::Read>(r: &mut GMIReadStream<R>) -> ReadResult<Self> {
|
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
|
||||||
let buf: [u8; 4] = r.bytes(4)?.try_into().unwrap();
|
let buf: [u8; 4] = r.bytes(4)?.try_into().unwrap();
|
||||||
Ok(u32::from_le_bytes(buf))
|
Ok(u32::from_le_bytes(buf))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GMISerializable for f32 {
|
impl Serializable for i32 {
|
||||||
fn read<R: std::io::Read>(r: &mut GMIReadStream<R>) -> ReadResult<Self> {
|
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
|
||||||
|
let buf: [u8; 4] = r.bytes(4)?.try_into().unwrap();
|
||||||
|
Ok(i32::from_le_bytes(buf))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for f32 {
|
||||||
|
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
|
||||||
let buf: [u8; 4] = r.bytes(4)?.try_into().unwrap();
|
let buf: [u8; 4] = r.bytes(4)?.try_into().unwrap();
|
||||||
Ok(f32::from_le_bytes(buf))
|
Ok(f32::from_le_bytes(buf))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GMISerializable for String {
|
impl Serializable for String {
|
||||||
fn read<R: std::io::Read>(r: &mut GMIReadStream<R>) -> ReadResult<Self> {
|
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
|
||||||
let len: u32 = r.read("len")?;
|
let len: u32 = r.read("len")?;
|
||||||
r.push("bytes".to_string());
|
r.push("bytes".to_string());
|
||||||
let bytes = r.bytes(len as usize)?;
|
let bytes = r.bytes(len as usize)?;
|
||||||
|
@ -109,13 +106,13 @@ impl GMISerializable for String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GMIReadStream<R: std::io::Read> {
|
pub struct ReadStream<R: std::io::Read> {
|
||||||
backing: R,
|
backing: R,
|
||||||
stack: Vec<ParseFrame>,
|
stack: Vec<ParseFrame>,
|
||||||
pos: usize,
|
pos: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: std::io::Read> GMIReadStream<R> {
|
impl<R: std::io::Read> ReadStream<R> {
|
||||||
pub fn new(r: R) -> Self {
|
pub fn new(r: R) -> Self {
|
||||||
Self {
|
Self {
|
||||||
backing: r,
|
backing: r,
|
||||||
|
@ -138,7 +135,7 @@ impl<R: std::io::Read> GMIReadStream<R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <R: std::io::Read> GMIReadStream<R> {
|
impl <R: std::io::Read> ReadStream<R> {
|
||||||
pub fn bytes(&mut self, n: usize) -> ReadResult<Vec<u8>> {
|
pub fn bytes(&mut self, n: usize) -> ReadResult<Vec<u8>> {
|
||||||
let mut buf: Vec<u8> = vec![0; n];
|
let mut buf: Vec<u8> = vec![0; n];
|
||||||
self.pos += n;
|
self.pos += n;
|
||||||
|
@ -158,14 +155,14 @@ impl <R: std::io::Read> GMIReadStream<R> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read<T: GMISerializable, S: std::string::ToString>(&mut self, ctx: S) -> ReadResult<T> {
|
pub fn read<T: Serializable, S: std::string::ToString>(&mut self, ctx: S) -> ReadResult<T> {
|
||||||
self.push(ctx.to_string());
|
self.push(ctx.to_string());
|
||||||
let res = T::read(self);
|
let res = T::read(self);
|
||||||
self.pop();
|
self.pop();
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_parametrized<T: GMISerializable, S: std::string::ToString>(&mut self, ctx: S, params: &[u32]) -> ReadResult<T> {
|
pub fn read_parametrized<T: Serializable, S: std::string::ToString>(&mut self, ctx: S, params: &[u32]) -> ReadResult<T> {
|
||||||
self.push(ctx.to_string());
|
self.push(ctx.to_string());
|
||||||
let res = T::read_parametrized(self, params);
|
let res = T::read_parametrized(self, params);
|
||||||
self.pop();
|
self.pop();
|
||||||
|
@ -189,14 +186,14 @@ pub struct TLV {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TLV {
|
impl TLV {
|
||||||
pub fn error(&self, msg: &str) -> ReadError {
|
pub fn error<S: std::string::ToString>(&self, msg: S) -> ReadError {
|
||||||
ReadError {
|
ReadError {
|
||||||
msg: msg.to_string(),
|
msg: msg.to_string(),
|
||||||
stack: vec![],
|
stack: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check<R: std::io::Read>(&self, r: &mut GMIReadStream<R>) -> ReadResult<()> {
|
pub fn check<R: std::io::Read>(&self, r: &mut ReadStream<R>) -> ReadResult<()> {
|
||||||
let expected = self.start_pos + self.length as usize + 12usize;
|
let expected = self.start_pos + self.length as usize + 12usize;
|
||||||
if expected != r.pos {
|
if expected != r.pos {
|
||||||
return Err(ReadError {
|
return Err(ReadError {
|
||||||
|
@ -207,3 +204,4 @@ impl TLV {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
mod machinery;
|
mod gmi;
|
||||||
|
mod gma;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
pub use types::*;
|
pub use types::*;
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
use gmfmacros::GMISerializable;
|
use std::{io, string};
|
||||||
|
|
||||||
use crate::machinery::{ReadResult, GMIReadStream, GMISerializable};
|
use gmfmacros::{GMISerializable, GMASerializable};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
gmi, gma,
|
||||||
|
gmi::Serializable as GMISerializable,
|
||||||
|
gma::Serializable as GMASerializable,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct GMI {
|
pub struct GMF {
|
||||||
pub version: u32,
|
pub version: u32,
|
||||||
pub model_type: ModelType,
|
pub model_type: ModelType,
|
||||||
pub unk1: u32,
|
pub unk1: u32,
|
||||||
|
@ -14,19 +20,27 @@ pub struct GMI {
|
||||||
pub objects: ObjectList,
|
pub objects: ObjectList,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GMI {
|
impl GMF {
|
||||||
pub fn parse<R: std::io::Read>(r: R) -> ReadResult<Self> {
|
pub fn read_gmi<R: io::Read>(r: R) -> gmi::ReadResult<Self> {
|
||||||
let mut r = GMIReadStream::new(r);
|
let mut r = gmi::ReadStream::new(r);
|
||||||
r.read("gmi")
|
GMF::read(&mut r)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_gma<W: io::Write>(&self, w: W) -> gma::WriteResult<()> {
|
||||||
|
let mut w = gma::WriteStream::new(w);
|
||||||
|
self.write("", &mut w)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GMISerializable for GMI {
|
impl gmi::Serializable for GMF {
|
||||||
fn read<R: std::io::Read>(r: &mut GMIReadStream<R>) -> ReadResult<Self> {
|
fn read<R: io::Read>(r: &mut gmi::ReadStream<R>) -> gmi::ReadResult<Self> {
|
||||||
if r.bytes(3)? != b"GMI".to_vec() {
|
if r.bytes(3)? != b"GMI".to_vec() {
|
||||||
return Err(r.error("invaid magic"));
|
return Err(r.error("invaid magic"));
|
||||||
}
|
}
|
||||||
let version: u32 = r.read("version")?;
|
let version: u32 = r.read("version")?;
|
||||||
|
if version != 3 {
|
||||||
|
return Err(r.error(format!("unsupported version {}", version)));
|
||||||
|
}
|
||||||
let model_type = r.read("model_type")?;
|
let model_type = r.read("model_type")?;
|
||||||
let unk1: u32 = r.read("unk1")?;
|
let unk1: u32 = r.read("unk1")?;
|
||||||
if unk1 != 0 {
|
if unk1 != 0 {
|
||||||
|
@ -45,6 +59,18 @@ impl GMISerializable for GMI {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl gma::Serializable for GMF {
|
||||||
|
fn write<W: io::Write, S: string::ToString>(&self, _name: S, w: &mut gma::WriteStream<W>) -> gma::WriteResult<()> {
|
||||||
|
w.emit("GMA")?;
|
||||||
|
w.emit_pair("*GABRIEL_ASCIIEXPORT", "3")?;
|
||||||
|
self.model_type.write("*MODEL_TYPE", w)?;
|
||||||
|
self.scene.write("", w)?;
|
||||||
|
self.materials.write("", w)?;
|
||||||
|
self.objects.write("", w)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug,GMISerializable)]
|
#[derive(Debug,GMISerializable)]
|
||||||
pub struct Color {
|
pub struct Color {
|
||||||
pub r: u8,
|
pub r: u8,
|
||||||
|
@ -53,6 +79,18 @@ pub struct Color {
|
||||||
pub a: u8,
|
pub a: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl gma::Atom for Color {
|
||||||
|
fn atom(&self) -> String {
|
||||||
|
let mut val: u32 = 0;
|
||||||
|
val |= (self.b as u32) << 16;
|
||||||
|
val |= (self.g as u32) << 8;
|
||||||
|
val |= (self.r as u32) << 0;
|
||||||
|
// TODO: figure out alpha support, if at all.
|
||||||
|
format!("0x{:x}", val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug,GMISerializable)]
|
#[derive(Debug,GMISerializable)]
|
||||||
pub enum MapKind {
|
pub enum MapKind {
|
||||||
Diffuse = 1,
|
Diffuse = 1,
|
||||||
|
@ -60,23 +98,34 @@ pub enum MapKind {
|
||||||
Opacity = 6,
|
Opacity = 6,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,GMISerializable)]
|
impl gma::Serializable for MapKind {
|
||||||
|
fn write<W: io::Write, S: string::ToString>(&self, _name: S, w: &mut gma::WriteStream<W>) -> gma::WriteResult<()> {
|
||||||
|
match self {
|
||||||
|
MapKind::Diffuse => w.emit("*MAP_DIFFUSE"),
|
||||||
|
MapKind::SelfIllum => w.emit("*MAP_SELFILLUM"),
|
||||||
|
MapKind::Opacity => w.emit("*MAP_OPACITY"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug,GMISerializable,GMASerializable)]
|
||||||
pub enum MapType {
|
pub enum MapType {
|
||||||
Screen = 4,
|
Screen = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,GMISerializable)]
|
#[derive(Debug,GMISerializable,GMASerializable)]
|
||||||
pub enum BitmapFilter {
|
pub enum BitmapFilter {
|
||||||
Pyramidal = 0,
|
Pyramidal = 0,
|
||||||
SAT = 1,
|
SAT = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, GMISerializable)]
|
#[derive(Debug,GMISerializable,GMASerializable)]
|
||||||
pub enum ModelType {
|
pub enum ModelType {
|
||||||
|
#[gma_value("Basic Model")]
|
||||||
BasicModel = 1,
|
BasicModel = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,GMISerializable)]
|
#[derive(Debug,GMISerializable,GMASerializable)]
|
||||||
#[gmi_tagged(1,2)]
|
#[gmi_tagged(1,2)]
|
||||||
pub struct Scene {
|
pub struct Scene {
|
||||||
pub filename: String,
|
pub filename: String,
|
||||||
|
@ -84,19 +133,24 @@ pub struct Scene {
|
||||||
pub last_frame: u32,
|
pub last_frame: u32,
|
||||||
pub frame_speed: u32,
|
pub frame_speed: u32,
|
||||||
pub ticks_per_frame: u32,
|
pub ticks_per_frame: u32,
|
||||||
|
#[gma_name("SCENE_BACKGROUND_STATIC")]
|
||||||
pub background: Color,
|
pub background: Color,
|
||||||
|
#[gma_name("SCENE_AMBIENT_STATIC")]
|
||||||
pub ambient: Color,
|
pub ambient: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,GMISerializable)]
|
#[derive(Debug,GMISerializable,GMASerializable)]
|
||||||
#[gmi_tagged(7, 2)]
|
#[gmi_tagged(7, 2)]
|
||||||
|
#[gma_name("MATERIAL_LIST")]
|
||||||
pub struct MaterialList {
|
pub struct MaterialList {
|
||||||
|
#[gma_name("MATERIAL")]
|
||||||
pub materials: Vec<Material>,
|
pub materials: Vec<Material>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,GMISerializable)]
|
#[derive(Debug,GMISerializable,GMASerializable)]
|
||||||
#[gmi_tagged(8, 2)]
|
#[gmi_tagged(8, 2)]
|
||||||
pub struct Material {
|
pub struct Material {
|
||||||
|
#[gma_name("MATERIAL_REF_NO")]
|
||||||
pub ref_no: u32,
|
pub ref_no: u32,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub class: String,
|
pub class: String,
|
||||||
|
@ -105,70 +159,115 @@ pub struct Material {
|
||||||
pub specular: Color,
|
pub specular: Color,
|
||||||
pub shine: f32,
|
pub shine: f32,
|
||||||
pub shine_strength: f32,
|
pub shine_strength: f32,
|
||||||
pub wiresize: f32,
|
|
||||||
pub transparency: f32,
|
pub transparency: f32,
|
||||||
|
pub wiresize: f32,
|
||||||
pub shading: Shading,
|
pub shading: Shading,
|
||||||
|
#[gma_name("MATERIAL_XP_FALLOFF")]
|
||||||
pub xp_falloff: f32,
|
pub xp_falloff: f32,
|
||||||
pub selfillum: f32,
|
pub selfillum: f32,
|
||||||
pub falloff: Falloff,
|
pub falloff: Falloff,
|
||||||
|
#[gma_name("MATERIAL_XP_TYPE")]
|
||||||
pub xp_type: XPType,
|
pub xp_type: XPType,
|
||||||
pub textures: Option<TextureList>,
|
pub textures: Option<TextureList>,
|
||||||
pub sub: Option<MaterialList>,
|
pub sub: Option<MaterialList>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,GMISerializable)]
|
#[derive(Debug,GMISerializable,GMASerializable)]
|
||||||
pub enum Shading {
|
pub enum Shading {
|
||||||
Other = 0,
|
Other = 0,
|
||||||
Blinn = 0xc,
|
Blinn = 0xc,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, GMISerializable)]
|
#[derive(Debug, GMISerializable,GMASerializable)]
|
||||||
pub enum Falloff {
|
pub enum Falloff {
|
||||||
In = 0,
|
In = 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,GMISerializable)]
|
#[derive(Debug,GMISerializable,GMASerializable)]
|
||||||
pub enum XPType {
|
pub enum XPType {
|
||||||
Other = 0,
|
Other = 0,
|
||||||
Filter = 1,
|
Filter = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,GMISerializable)]
|
#[derive(Debug,GMISerializable,GMASerializable)]
|
||||||
#[gmi_tagged(14, 2)]
|
#[gmi_tagged(14, 2)]
|
||||||
|
#[gma_name("TEXTURE_LIST")]
|
||||||
pub struct TextureList {
|
pub struct TextureList {
|
||||||
|
#[gma_name("TEXTURE")]
|
||||||
pub textures: Vec<Texture>,
|
pub textures: Vec<Texture>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,GMISerializable)]
|
#[derive(Debug,GMISerializable,GMASerializable)]
|
||||||
#[gmi_tagged(15, 4)]
|
#[gmi_tagged(15, 4)]
|
||||||
pub struct Texture {
|
pub struct Texture {
|
||||||
|
#[gma_name("MAP_NAME")]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
#[gma_name("MAP_CLASS")]
|
||||||
pub class: String,
|
pub class: String,
|
||||||
|
#[gma_name("BITMAP")]
|
||||||
pub bitmap: String,
|
pub bitmap: String,
|
||||||
|
#[gma_name("MAP_AMOUNT")]
|
||||||
pub amount: f32,
|
pub amount: f32,
|
||||||
pub kind: MapKind,
|
pub kind: MapKind,
|
||||||
|
#[gma_name("MAP_TYPE")]
|
||||||
pub map_type: MapType,
|
pub map_type: MapType,
|
||||||
|
#[gma_name("UVW_U_OFFSET")]
|
||||||
pub u_offset: f32,
|
pub u_offset: f32,
|
||||||
|
#[gma_name("UVW_V_OFFSET")]
|
||||||
pub v_offset: f32,
|
pub v_offset: f32,
|
||||||
|
#[gma_name("UVW_U_TILING")]
|
||||||
pub u_tiling: f32,
|
pub u_tiling: f32,
|
||||||
|
#[gma_name("UVW_V_TILING")]
|
||||||
pub v_tiling: f32,
|
pub v_tiling: f32,
|
||||||
|
#[gma_name("UVW_ANGLE")]
|
||||||
pub angle: f32,
|
pub angle: f32,
|
||||||
|
#[gma_name("UVW_BLUR")]
|
||||||
pub blur: f32,
|
pub blur: f32,
|
||||||
|
#[gma_name("UVW_BLUR_OFFSET")]
|
||||||
pub blur_offset: f32,
|
pub blur_offset: f32,
|
||||||
pub noise_amt: f32,
|
#[gma_name("UVW_NOUSE_AMT")]
|
||||||
|
pub noise_amount: f32,
|
||||||
|
#[gma_name("UVW_NOISE_SIZE")]
|
||||||
pub noise_size: f32,
|
pub noise_size: f32,
|
||||||
|
#[gma_name("UVW_NOISE_LEVEL")]
|
||||||
pub noise_level: u32,
|
pub noise_level: u32,
|
||||||
|
#[gma_name("UVW_NOISE_PHASE")]
|
||||||
pub noise_phase: f32,
|
pub noise_phase: f32,
|
||||||
|
#[gma_name("")]
|
||||||
pub invert: u32,
|
pub invert: u32,
|
||||||
|
#[gma_name("")]
|
||||||
pub unknown: u32,
|
pub unknown: u32,
|
||||||
|
#[gma_name("BITMAP_FILTER")]
|
||||||
pub filter: BitmapFilter,
|
pub filter: BitmapFilter,
|
||||||
|
#[gma_name("BITMAP_MAP_CHANNEL")]
|
||||||
pub channel: u32,
|
pub channel: u32,
|
||||||
pub sub: Option<TextureList>,
|
pub sub: Option<TextureList>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,GMISerializable)]
|
#[derive(Debug)]
|
||||||
|
pub struct ObjectName(String);
|
||||||
|
|
||||||
|
impl gmi::Serializable for ObjectName {
|
||||||
|
fn read<R: io::Read>(r: &mut gmi::ReadStream<R>) -> gmi::ReadResult<Self> {
|
||||||
|
String::read(r).map(|e| ObjectName(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl gma::Serializable for ObjectName {
|
||||||
|
fn write<W: io::Write, S: string::ToString>(&self, name: S, w: &mut gma::WriteStream<W>) -> gma::WriteResult<()> {
|
||||||
|
let val = match self.0.len() {
|
||||||
|
0 => "(null)".to_string(),
|
||||||
|
_ => self.0.clone(),
|
||||||
|
};
|
||||||
|
val.write(name, w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug,GMISerializable,GMASerializable)]
|
||||||
#[gmi_tagged_nolen(18,2)]
|
#[gmi_tagged_nolen(18,2)]
|
||||||
|
#[gma_name("OBJECT_LIST")]
|
||||||
pub struct ObjectList {
|
pub struct ObjectList {
|
||||||
|
#[gma_name("OBJECT")]
|
||||||
pub objects: Vec<Object>,
|
pub objects: Vec<Object>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,8 +281,8 @@ pub enum Object {
|
||||||
RBCollection(RBCollectionObject),
|
RBCollection(RBCollectionObject),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GMISerializable for Object {
|
impl gmi::Serializable for Object {
|
||||||
fn read<R: std::io::Read>(r: &mut GMIReadStream<R>) -> ReadResult<Self> {
|
fn read<R: io::Read>(r: &mut gmi::ReadStream<R>) -> gmi::ReadResult<Self> {
|
||||||
let tlv = r.tlv()?;
|
let tlv = r.tlv()?;
|
||||||
let (res, check) = match (tlv.tag, tlv.flags) {
|
let (res, check) = match (tlv.tag, tlv.flags) {
|
||||||
(2, 4) => (Object::Geometry(r.read("geometry")?), false),
|
(2, 4) => (Object::Geometry(r.read("geometry")?), false),
|
||||||
|
@ -194,7 +293,6 @@ impl GMISerializable for Object {
|
||||||
(31, 4) => (Object::RBCollection(r.read("rigidbodycollection")?), false),
|
(31, 4) => (Object::RBCollection(r.read("rigidbodycollection")?), false),
|
||||||
_ => return Err(r.error(format!("unknown object type ({}, {})", tlv.tag, tlv.flags))),
|
_ => return Err(r.error(format!("unknown object type ({}, {})", tlv.tag, tlv.flags))),
|
||||||
};
|
};
|
||||||
println!("{:#?}", res);
|
|
||||||
if check {
|
if check {
|
||||||
tlv.check(r)?;
|
tlv.check(r)?;
|
||||||
}
|
}
|
||||||
|
@ -202,20 +300,47 @@ impl GMISerializable for Object {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,GMISerializable)]
|
impl gma::Serializable for Object {
|
||||||
|
fn write<W: io::Write, S: string::ToString>(&self, _name: S, w: &mut gma::WriteStream<W>) -> gma::WriteResult<()> {
|
||||||
|
match self {
|
||||||
|
Object::Geometry(o) => o.write("", w),
|
||||||
|
Object::Light(o) => o.write("", w),
|
||||||
|
Object::AttachmentPoint(o) => o.write("", w),
|
||||||
|
Object::ConstraintSolver(o) => o.write("", w),
|
||||||
|
Object::Simulation(o) => o.write("", w),
|
||||||
|
Object::RBCollection(o) => o.write("", w),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug,GMISerializable,GMASerializable)]
|
||||||
|
#[gma_name("GEOMOBJECT")]
|
||||||
pub struct GeometryObject {
|
pub struct GeometryObject {
|
||||||
|
#[gma_name("NODE_NAME")]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
#[gma_name("NODE_PARENT")]
|
||||||
pub parent: String,
|
pub parent: String,
|
||||||
|
#[gma_name("NODE_SHADEVERTS")]
|
||||||
pub shade_verts: u8,
|
pub shade_verts: u8,
|
||||||
|
#[gma_name("NODE_TM")]
|
||||||
pub tm: TransformMatrix,
|
pub tm: TransformMatrix,
|
||||||
pub mesh: Mesh,
|
pub mesh: Mesh,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,GMISerializable)]
|
#[derive(Debug,GMISerializable,GMASerializable)]
|
||||||
#[gmi_tagged(17, 2)]
|
#[gmi_tagged(17, 2)]
|
||||||
|
#[gma_name("NODE_TM")]
|
||||||
pub struct TransformMatrix {
|
pub struct TransformMatrix {
|
||||||
|
#[gma_name("NODE_NAME")]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub data: [f32; 12],
|
#[gma_name("TM_ROW0")]
|
||||||
|
pub row0: [f32; 3],
|
||||||
|
#[gma_name("TM_ROW1")]
|
||||||
|
pub row1: [f32; 3],
|
||||||
|
#[gma_name("TM_ROW2")]
|
||||||
|
pub row2: [f32; 3],
|
||||||
|
#[gma_name("TM_ROW3")]
|
||||||
|
pub row3: [f32; 3],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, GMISerializable)]
|
#[derive(Debug, GMISerializable)]
|
||||||
|
@ -226,39 +351,190 @@ pub struct Mesh {
|
||||||
pub face_count: u32,
|
pub face_count: u32,
|
||||||
pub tvertex_count: u32,
|
pub tvertex_count: u32,
|
||||||
pub cvertex_count: u32,
|
pub cvertex_count: u32,
|
||||||
pub material_ref: u32,
|
pub material_ref: i32,
|
||||||
|
|
||||||
#[gmi_read_parameters(vertex_count)]
|
#[gmi_read_parameters(vertex_count)]
|
||||||
pub vertices: Vec<Point>,
|
pub vertices: MeshVertexList,
|
||||||
#[gmi_read_parameters(face_count)]
|
#[gmi_read_parameters(face_count)]
|
||||||
pub faces: Vec<Face>,
|
pub faces: MeshFaceList,
|
||||||
|
|
||||||
#[gmi_read_parameters(tvertex_count)]
|
#[gmi_read_parameters(tvertex_count)]
|
||||||
pub tvertices: Vec<Point>,
|
pub tvertices: MeshTVertexList,
|
||||||
#[gmi_read_parameters(if tvertex_count > 0 { face_count } else { 0 })]
|
#[gmi_read_parameters(if tvertex_count > 0 { face_count } else { 0 })]
|
||||||
pub tfaces: Vec<TFace>,
|
pub tfaces: MeshTFaceList,
|
||||||
|
|
||||||
pub channels: Option<Vec<TextureChannel>>,
|
pub channels: Option<Vec<TextureChannel>>,
|
||||||
|
|
||||||
#[gmi_read_parameters(cvertex_count)]
|
#[gmi_read_parameters(cvertex_count)]
|
||||||
pub cvertices: Vec<Point>,
|
pub cvertices: MeshCVertexList,
|
||||||
#[gmi_read_parameters(if cvertex_count > 0 { face_count } else { 0 })]
|
#[gmi_read_parameters(if cvertex_count > 0 { face_count } else { 0 })]
|
||||||
pub cfaces: Vec<TFace>,
|
pub cfaces: MeshCFaceList,
|
||||||
|
|
||||||
#[gmi_read_parameters(face_count)]
|
#[gmi_read_parameters(face_count)]
|
||||||
pub normals: Vec<FaceNormal>,
|
pub normals: MeshNormalList,
|
||||||
|
|
||||||
pub backface_cull: u32,
|
pub backface_cull: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,GMISerializable)]
|
impl gma::Serializable for Mesh {
|
||||||
|
fn write<W: io::Write, S: string::ToString>(&self, _name: S, w: &mut gma::WriteStream<W>) -> gma::WriteResult<()> {
|
||||||
|
w.push("*MESH")?;
|
||||||
|
self.time.write("*TIMEVALUE", w)?;
|
||||||
|
self.vertex_count.write("*MESH_NUMVERTEX", w)?;
|
||||||
|
self.face_count.write("*MESH_NUMFACES", w)?;
|
||||||
|
self.vertices.write("", w)?;
|
||||||
|
self.faces.write("", w)?;
|
||||||
|
if self.tvertex_count > 0 {
|
||||||
|
self.tvertex_count.write("*MESH_NUMTVERTEX", w)?;
|
||||||
|
self.tvertices.write("", w)?;
|
||||||
|
self.face_count.write("*MESH_NUMTVFACES", w)?;
|
||||||
|
self.tfaces.write("", w)?;
|
||||||
|
}
|
||||||
|
if let Some(channels) = &self.channels {
|
||||||
|
for (i, channel) in channels.iter().enumerate() {
|
||||||
|
w.push(format!("*MESH_MAPPINGCHANNEL\t{}", i+2))?;
|
||||||
|
channel.tvertex_count.write("*MESH_NUMTVERTEX", w)?;
|
||||||
|
channel.tvertices.write("", w)?;
|
||||||
|
channel.face_count.write("*MESH_NUMTVFACES", w)?;
|
||||||
|
channel.tfaces.write("", w)?;
|
||||||
|
w.pop()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.cvertices.0.len() > 0 {
|
||||||
|
self.cvertices.0.len().write("*MESH_NUMCVERTEX", w)?;
|
||||||
|
self.cvertices.write("", w)?;
|
||||||
|
}
|
||||||
|
if self.cfaces.0.len() > 0 {
|
||||||
|
self.cfaces.0.len().write("*MESH_NUMCVFACES", w)?;
|
||||||
|
self.cfaces.write("", w)?;
|
||||||
|
}
|
||||||
|
if self.normals.0.len() > 0 {
|
||||||
|
self.normals.write(w, &self.faces)?;
|
||||||
|
}
|
||||||
|
self.backface_cull.write("*BACKFACE_CULL", w)?;
|
||||||
|
self.material_ref.write("*MATERIAL_REF", w)?;
|
||||||
|
w.pop()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! specialized_vec {
|
||||||
|
($newname:ident, $elemname:ident, $header:expr, $format:expr) => {
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct $newname(Vec<$elemname>);
|
||||||
|
impl gmi::Serializable for $newname {
|
||||||
|
fn read<R: io::Read>(r: &mut gmi::ReadStream<R>) -> gmi::ReadResult<Self> {
|
||||||
|
<Vec<$elemname> as gmi::Serializable>::read(r).map(|r| $newname(r))
|
||||||
|
}
|
||||||
|
fn read_parametrized<R: io::Read>(r: &mut gmi::ReadStream<R>, params: &[u32]) -> gmi::ReadResult<Self> {
|
||||||
|
<Vec<$elemname> as gmi::Serializable>::read_parametrized(r, params).map(|r| $newname(r))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl gma::Serializable for $newname {
|
||||||
|
fn write<W: io::Write, S: string::ToString>(&self, _name: S, w: &mut gma::WriteStream<W>) -> gma::WriteResult<()> {
|
||||||
|
w.push($header)?;
|
||||||
|
for (i, v) in self.0.iter().enumerate() {
|
||||||
|
w.emit(&$format(i, v))?;
|
||||||
|
}
|
||||||
|
w.pop()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
specialized_vec!(
|
||||||
|
MeshVertexList, Point,
|
||||||
|
"*MESH_VERTEX_LIST",
|
||||||
|
(|i, v: &Point| {
|
||||||
|
format!("*MESH_VERTEX\t{}\t{:.6}\t{:.6}\t{:.6}", i, v.x, v.y, v.z)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
specialized_vec!(
|
||||||
|
MeshFaceList, Face,
|
||||||
|
"*MESH_FACE_LIST",
|
||||||
|
(|i, f: &Face| {
|
||||||
|
format!(
|
||||||
|
"*MESH_FACE\t{:>4}\tA:{:>5}\tB:{:>5}\tC:{:>5}\t*MESH_MTLID {}",
|
||||||
|
i, f.a, f.b, f.c, f.mtlid)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
specialized_vec!(
|
||||||
|
MeshTVertexList, Point,
|
||||||
|
"*MESH_TVERTLIST",
|
||||||
|
(|i, v: &Point| {
|
||||||
|
format!("*MESH_TVERT\t{}\t{:.6}\t{:.6}\t{:.6}", i, v.x, v.y, v.z)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
specialized_vec!(
|
||||||
|
MeshTFaceList, TFace,
|
||||||
|
"*MESH_TFACELIST",
|
||||||
|
(|i, f: &TFace| {
|
||||||
|
format!("*MESH_TFACE\t{}\t{}\t{}\t{}", i, f.a, f.b, f.c)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
specialized_vec!(
|
||||||
|
MeshCVertexList, Point,
|
||||||
|
"*MESH_CVERTLIST",
|
||||||
|
(|i, v: &Point| {
|
||||||
|
format!("*MESH_VERTCOL\t{}\t{:.6}\t{:.6}\t{:.6}", i, v.x, v.y, v.z)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
specialized_vec!(
|
||||||
|
MeshCFaceList, TFace,
|
||||||
|
"*MESH_CFACELIST",
|
||||||
|
(|i, f: &TFace| {
|
||||||
|
format!("*MESH_CFACE\t{}\t{}\t{}\t{}", i, f.a, f.b, f.c)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
specialized_vec!(
|
||||||
|
MeshNormalList, FaceNormal,
|
||||||
|
"*MESH_NORMALS",
|
||||||
|
(|_, _: &FaceNormal| -> String {
|
||||||
|
// This implementation is never used.
|
||||||
|
unreachable!()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
impl MeshNormalList {
|
||||||
|
fn write<W: io::Write>(&self, w: &mut gma::WriteStream<W>, faces: &MeshFaceList) -> gma::WriteResult<()> {
|
||||||
|
w.push("*MESH_NORMALS")?;
|
||||||
|
for (i, (normal, face)) in self.0.iter().zip(faces.0.iter()).enumerate() {
|
||||||
|
w.emit(&format!("*MESH_FACENORMAL\t{}\t{:.6}\t{:.6}\t{:.6}",
|
||||||
|
i, normal.face[0], normal.face[1], normal.face[2]))?;
|
||||||
|
for j in 0..3 {
|
||||||
|
let vn = normal.vertex[j];
|
||||||
|
let vno = match j {
|
||||||
|
0 => face.a,
|
||||||
|
1 => face.b,
|
||||||
|
2 => face.c,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
w.emit(&format!("\t*MESH_VERTEXNORMAL\t{}\t{:.6}\t{:.6}\t{:.6}",
|
||||||
|
vno, vn[0], vn[1], vn[2]))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.pop()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug,GMISerializable,GMASerializable)]
|
||||||
pub struct Point {
|
pub struct Point {
|
||||||
pub x: f32,
|
pub x: f32,
|
||||||
pub y: f32,
|
pub y: f32,
|
||||||
pub z: f32,
|
pub z: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,GMISerializable)]
|
#[derive(Debug,GMISerializable,GMASerializable)]
|
||||||
pub struct Face {
|
pub struct Face {
|
||||||
pub a: u32,
|
pub a: u32,
|
||||||
pub b: u32,
|
pub b: u32,
|
||||||
|
@ -266,7 +542,7 @@ pub struct Face {
|
||||||
pub mtlid: u32,
|
pub mtlid: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,GMISerializable)]
|
#[derive(Debug,GMISerializable,GMASerializable)]
|
||||||
pub struct TFace {
|
pub struct TFace {
|
||||||
pub a: u32,
|
pub a: u32,
|
||||||
pub b: u32,
|
pub b: u32,
|
||||||
|
@ -279,7 +555,13 @@ pub struct FaceNormal {
|
||||||
pub vertex: [[f32; 3]; 3],
|
pub vertex: [[f32; 3]; 3],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, GMISerializable)]
|
impl gma::Serializable for FaceNormal {
|
||||||
|
fn write<W: io::Write, S: string::ToString>(&self, _name: S, _w: &mut gma::WriteStream<W>) -> gma::WriteResult<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, GMISerializable,GMASerializable)]
|
||||||
pub struct TextureChannel {
|
pub struct TextureChannel {
|
||||||
pub unk1: u32,
|
pub unk1: u32,
|
||||||
pub tvertex_count: u32,
|
pub tvertex_count: u32,
|
||||||
|
@ -291,87 +573,149 @@ pub struct TextureChannel {
|
||||||
pub face_count2: u32,
|
pub face_count2: u32,
|
||||||
|
|
||||||
#[gmi_read_parameters(tvertex_count)]
|
#[gmi_read_parameters(tvertex_count)]
|
||||||
pub tvertices: Vec<Point>,
|
pub tvertices: MeshTVertexList,
|
||||||
#[gmi_read_parameters(face_count)]
|
#[gmi_read_parameters(face_count)]
|
||||||
pub tfaces: Vec<TFace>,
|
pub tfaces: MeshTFaceList,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,GMISerializable)]
|
#[derive(Debug,GMISerializable,GMASerializable)]
|
||||||
|
#[gma_name("LIGHT")]
|
||||||
pub struct LightObject {
|
pub struct LightObject {
|
||||||
|
#[gma_name("NODE_NAME")]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub tm: TransformMatrix,
|
pub tm: TransformMatrix,
|
||||||
|
#[gma_skip]
|
||||||
pub target: String,
|
pub target: String,
|
||||||
|
#[gma_name("LIGHT_TYPE")]
|
||||||
pub light_type: LightType,
|
pub light_type: LightType,
|
||||||
pub shadows: LightShadows,
|
pub shadows: LightShadows,
|
||||||
pub uselight: u32,
|
pub uselight: u32,
|
||||||
|
pub spotshape: LightSpotShape,
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
|
#[gma_name("LIGHT_INTENS")]
|
||||||
pub intensity: f32,
|
pub intensity: f32,
|
||||||
pub aspect: f32,
|
pub aspect: f32,
|
||||||
|
#[gma_skip]
|
||||||
pub unk1: [u8; 8],
|
pub unk1: [u8; 8],
|
||||||
pub attn_start: f32,
|
pub attn_start: f32,
|
||||||
pub attn_end: f32,
|
pub attn_end: f32,
|
||||||
pub tdist: f32,
|
pub tdist: f32,
|
||||||
pub use_for_attn: u32,
|
#[gma_name("USE FAR ATTENUATION = ")]
|
||||||
|
pub use_far_attenuation: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,GMISerializable)]
|
#[derive(Debug,GMISerializable,GMASerializable)]
|
||||||
pub enum LightType {
|
pub enum LightType {
|
||||||
Omni = 0,
|
Omni = 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,GMISerializable)]
|
#[derive(Debug,GMISerializable,GMASerializable)]
|
||||||
pub enum LightShadows {
|
pub enum LightShadows {
|
||||||
Off = 0,
|
Off = 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,GMISerializable)]
|
#[derive(Debug,GMASerializable)]
|
||||||
|
pub enum LightSpotShape {
|
||||||
|
Circle = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl gmi::Serializable for LightSpotShape {
|
||||||
|
fn read<R: io::Read>(_: &mut gmi::ReadStream<R>) -> gmi::ReadResult<Self> {
|
||||||
|
Ok(LightSpotShape::Circle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug,GMISerializable,GMASerializable)]
|
||||||
|
#[gma_name("GMID_ATTACHMENTPT")]
|
||||||
pub struct AttachmentPointObject {
|
pub struct AttachmentPointObject {
|
||||||
|
#[gma_name("NODE_NAME")]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub tm: TransformMatrix,
|
pub tm: TransformMatrix,
|
||||||
|
#[gma_name_bare("USER DATA")]
|
||||||
pub user_data: String,
|
pub user_data: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,GMISerializable)]
|
#[derive(Debug,GMISerializable,GMASerializable)]
|
||||||
|
#[gma_name("GMID_HAVOK_CONSTRAINTSOLVER")]
|
||||||
pub struct ConstraintSolverObject {
|
pub struct ConstraintSolverObject {
|
||||||
|
#[gma_name("NODE_NAME")]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
#[gma_name("THRESHOLD")]
|
||||||
pub threshold: f32,
|
pub threshold: f32,
|
||||||
|
#[gma_name("RB_COLLECTION_NAME")]
|
||||||
pub rb_collection_name: String,
|
pub rb_collection_name: String,
|
||||||
pub constraints: Option<ConstraintList>,
|
pub constraints: Option<ConstraintList>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,GMISerializable)]
|
#[derive(Debug,GMISerializable,GMASerializable)]
|
||||||
#[gmi_tagged(44, 2)]
|
#[gmi_tagged(44, 2)]
|
||||||
pub struct ConstraintList {
|
pub struct ConstraintList {
|
||||||
pub constraints: Vec<Constraint>,
|
pub constraints: Vec<Constraint>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,GMISerializable)]
|
#[derive(Debug,GMISerializable,GMASerializable)]
|
||||||
pub enum Constraint {
|
pub enum Constraint {
|
||||||
Foo = 1,
|
Foo = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,GMISerializable)]
|
#[derive(Debug,GMISerializable,GMASerializable)]
|
||||||
|
#[gma_name("GMID_HAVOK_SIMOBJECT")]
|
||||||
pub struct SimulationObject {
|
pub struct SimulationObject {
|
||||||
pub name: String,
|
#[gma_name("NODE_NAME")]
|
||||||
pub gravity: Point,
|
pub name: ObjectName,
|
||||||
|
#[gma_name("GRAVITY")]
|
||||||
|
pub gravity: Gravity,
|
||||||
|
#[gma_name("WORLDSCALE")]
|
||||||
pub worldscale: f32,
|
pub worldscale: f32,
|
||||||
|
#[gma_name("SIMTOLERANCE")]
|
||||||
pub simtolerance: f32,
|
pub simtolerance: f32,
|
||||||
|
#[gma_name("RESOLVER")]
|
||||||
pub resolver: u32,
|
pub resolver: u32,
|
||||||
|
#[gma_name("INCLUDE_DRAG")]
|
||||||
pub incl_drag: u8,
|
pub incl_drag: u8,
|
||||||
|
#[gma_name("LINEAR_DRAG")]
|
||||||
pub linear_drag: f32,
|
pub linear_drag: f32,
|
||||||
|
#[gma_name("ANGULAR_DRAG")]
|
||||||
pub angular_drag: f32,
|
pub angular_drag: f32,
|
||||||
|
#[gma_name("INCLUDE_DEACTIVATOR")]
|
||||||
pub incl_deactivator: u8,
|
pub incl_deactivator: u8,
|
||||||
|
#[gma_name("SHORTFREQ")]
|
||||||
pub shortfreq: f32,
|
pub shortfreq: f32,
|
||||||
|
#[gma_name("LONGFREQ")]
|
||||||
pub longfreq: f32,
|
pub longfreq: f32,
|
||||||
pub last_subspace: u8,
|
#[gma_name("USE_FAST_SUBSPACE")]
|
||||||
|
pub use_fast_subspace: u8,
|
||||||
|
#[gma_name("UPDATES_PER_TIMESTEP")]
|
||||||
pub updates_per_timestamp: f32,
|
pub updates_per_timestamp: f32,
|
||||||
|
#[gma_name("NUM_COLLISION_PAIRS")]
|
||||||
pub collision_pairs: u32,
|
pub collision_pairs: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,GMISerializable)]
|
#[derive(Debug)]
|
||||||
|
pub struct Gravity(Point);
|
||||||
|
|
||||||
|
impl gmi::Serializable for Gravity {
|
||||||
|
fn read<R: io::Read>(r: &mut gmi::ReadStream<R>) -> gmi::ReadResult<Self> {
|
||||||
|
Point::read(r).map(|e| Gravity(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl gma::Serializable for Gravity {
|
||||||
|
fn write<W: io::Write, S: string::ToString>(&self, name: S, w: &mut gma::WriteStream<W>) -> gma::WriteResult<()> {
|
||||||
|
w.emit(&format!("{}\t{:.6} {:.6} {:.6}",
|
||||||
|
name.to_string(), self.0.x, self.0.y, self.0.z))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug,GMISerializable,GMASerializable)]
|
||||||
|
#[gma_name("GMID_HAVOK_RBCOLLECTION")]
|
||||||
pub struct RBCollectionObject {
|
pub struct RBCollectionObject {
|
||||||
|
#[gma_name("NODE_NAME")]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
#[gma_name("NUM_DISABLED_PAIRS")]
|
||||||
pub disabled_pairs: u32,
|
pub disabled_pairs: u32,
|
||||||
|
#[gma_name("SOLVER_TYPE")]
|
||||||
pub solver_type: u32,
|
pub solver_type: u32,
|
||||||
pub rigidbody_list: RigidBodyList,
|
pub rigidbody_list: RigidBodyList,
|
||||||
#[gmi_read_parameters(disabled_pairs)]
|
#[gmi_read_parameters(disabled_pairs)]
|
||||||
|
@ -384,29 +728,81 @@ pub struct RigidBodyList {
|
||||||
pub rigidbodies: Vec<RigidBody>,
|
pub rigidbodies: Vec<RigidBody>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,GMISerializable)]
|
impl gma::Serializable for RigidBodyList {
|
||||||
#[gmi_tagged_nolen(32, 4)]
|
fn write<W: io::Write, S: string::ToString>(&self, _name: S, w: &mut gma::WriteStream<W>) -> gma::WriteResult<()> {
|
||||||
pub struct RigidBody {
|
self.rigidbodies.len().write("*COUNT", w)?;
|
||||||
pub name: String,
|
w.push("*GMID_HAVOK_RIGIDBODY_LIST")?;
|
||||||
pub mass: f32,
|
self.rigidbodies.len().write("*COUNT", w)?;
|
||||||
pub elasticity: f32,
|
for rigidbody in self.rigidbodies.iter() {
|
||||||
pub friction: f32,
|
rigidbody.write("", w)?;
|
||||||
pub optimization_level: f32,
|
}
|
||||||
pub unyielding: u32,
|
w.pop()?;
|
||||||
pub simulation_geometry: u32,
|
Ok(())
|
||||||
pub geometry_proxy_name: String,
|
}
|
||||||
pub use_display_proxy: u8,
|
|
||||||
pub disable_collisions: u8,
|
|
||||||
pub inactive: u8,
|
|
||||||
pub display_proxy_name: String,
|
|
||||||
pub tm: TransformMatrix,
|
|
||||||
pub geo_type: u32,
|
|
||||||
pub children: Option<RigidBodyList>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,GMISerializable)]
|
#[derive(Debug,GMISerializable,GMASerializable)]
|
||||||
|
#[gmi_tagged_nolen(32, 4)]
|
||||||
|
#[gma_name("GMID_HAVOK_RIGIDBODY")]
|
||||||
|
pub struct RigidBody {
|
||||||
|
#[gma_name("NODE_NAME")]
|
||||||
|
pub name: String,
|
||||||
|
#[gma_name("MASS")]
|
||||||
|
pub mass: f32,
|
||||||
|
#[gma_name("ELASTICITY")]
|
||||||
|
pub elasticity: f32,
|
||||||
|
#[gma_name("FRICTION")]
|
||||||
|
pub friction: f32,
|
||||||
|
#[gma_name("OPTIMIZATION_LEVEL")]
|
||||||
|
pub optimization_level: f32,
|
||||||
|
#[gma_name("UNYIELDING")]
|
||||||
|
pub unyielding: u32,
|
||||||
|
#[gma_name("SIMULATION_GEOMETRY")]
|
||||||
|
pub simulation_geometry: u32,
|
||||||
|
#[gma_name("GEOMETRY_PROXY_NAME")]
|
||||||
|
pub geometry_proxy_name: ObjectName,
|
||||||
|
#[gma_name("USE_DISPLAY_PROXY")]
|
||||||
|
pub use_display_proxy: u8,
|
||||||
|
#[gma_name("DISABLE_COLLISIONS")]
|
||||||
|
pub disable_collisions: u8,
|
||||||
|
#[gma_name("INACTIVE")]
|
||||||
|
pub inactive: u8,
|
||||||
|
#[gma_name("DISPLAY_PROXY_NAME")]
|
||||||
|
pub display_proxy_name: ObjectName,
|
||||||
|
pub tm: TransformMatrix,
|
||||||
|
#[gma_name("HAVOK_GEO_TYPE")]
|
||||||
|
pub geo_type: GeoType,
|
||||||
|
pub children: NestedRigidBodyList,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct NestedRigidBodyList(Option<RigidBodyList>);
|
||||||
|
|
||||||
|
impl gmi::Serializable for NestedRigidBodyList {
|
||||||
|
fn read<R: io::Read>(r: &mut gmi::ReadStream<R>) -> gmi::ReadResult<Self> {
|
||||||
|
Option::<RigidBodyList>::read(r).map(|el| NestedRigidBodyList(el))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl gma::Serializable for NestedRigidBodyList {
|
||||||
|
fn write<W: io::Write, S: string::ToString>(&self, name: S, w: &mut gma::WriteStream<W>) -> gma::WriteResult<()> {
|
||||||
|
let count = self.0.as_ref().map(|l| l.rigidbodies.len()).unwrap_or(0);
|
||||||
|
count.write("*NUMBER_OF_CHILDREN", w)?;
|
||||||
|
self.0.write(name, w)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug,GMISerializable,GMASerializable)]
|
||||||
|
pub enum GeoType {
|
||||||
|
Standard = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug,GMISerializable,GMASerializable)]
|
||||||
#[gmi_tagged_nolen(51, 2)]
|
#[gmi_tagged_nolen(51, 2)]
|
||||||
|
#[gma_name("GMID_HAVOK_DIS_COLLISION_PAIRS")]
|
||||||
pub struct DisabledCollisionPairList {
|
pub struct DisabledCollisionPairList {
|
||||||
|
#[gma_name("")]
|
||||||
pub pairs: Vec<DisabledCollisionPair>,
|
pub pairs: Vec<DisabledCollisionPair>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -415,3 +811,10 @@ pub struct DisabledCollisionPair {
|
||||||
pub a: String,
|
pub a: String,
|
||||||
pub b: String,
|
pub b: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl gma::Serializable for DisabledCollisionPair {
|
||||||
|
fn write<W: io::Write, S: string::ToString>(&self, _name: S, w: &mut gma::WriteStream<W>) -> gma::WriteResult<()> {
|
||||||
|
w.emit(&format!("{{ {}\t{} }}",
|
||||||
|
self.a, self.b))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,8 +3,13 @@ use syn::{parse_macro_input, DeriveInput, parenthesized};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
|
||||||
struct GMITagged {
|
struct GMITagged {
|
||||||
|
#[allow(dead_code)]
|
||||||
paren_token: syn::token::Paren,
|
paren_token: syn::token::Paren,
|
||||||
fields: syn::punctuated::Punctuated<syn::LitInt, syn::Token![,]>,
|
fields: syn::punctuated::Punctuated<syn::LitInt, syn::Token![,]>,
|
||||||
|
|
||||||
|
nolen: bool,
|
||||||
|
ty: u32,
|
||||||
|
flag: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl syn::parse::Parse for GMITagged {
|
impl syn::parse::Parse for GMITagged {
|
||||||
|
@ -13,11 +18,31 @@ impl syn::parse::Parse for GMITagged {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
paren_token: parenthesized!(content in input),
|
paren_token: parenthesized!(content in input),
|
||||||
fields: content.parse_terminated(syn::LitInt::parse)?,
|
fields: content.parse_terminated(syn::LitInt::parse)?,
|
||||||
|
|
||||||
|
nolen: false,
|
||||||
|
ty: 0,
|
||||||
|
flag: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl GMITagged {
|
||||||
|
fn from_attrs(attrs: &Vec<syn::Attribute>) -> Option<Self> {
|
||||||
|
let attr = attrs.iter().find(|a| a.path.is_ident("gmi_tagged") || a.path.is_ident("gmi_tagged_nolen"))?;
|
||||||
|
let mut res = syn::parse2::<GMITagged>(attr.tokens.clone()).expect("invalid gmi_tagged attribute");
|
||||||
|
if res.fields.len() != 2 {
|
||||||
|
panic!("gmi_tagged(_len) takes two arguments");
|
||||||
|
}
|
||||||
|
|
||||||
|
res.nolen = attr.path.is_ident("gmi_tagged_nolen");
|
||||||
|
res.ty = res.fields[0].base10_parse().expect("gmi_tagged arg 1 not a number");
|
||||||
|
res.flag = res.fields[1].base10_parse().expect("gmi_tagged arg 2 not a number");
|
||||||
|
Some(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct GMIReadParameters {
|
struct GMIReadParameters {
|
||||||
|
#[allow(dead_code)]
|
||||||
paren_token: syn::token::Paren,
|
paren_token: syn::token::Paren,
|
||||||
fields: syn::punctuated::Punctuated<syn::Expr, syn::Token![,]>,
|
fields: syn::punctuated::Punctuated<syn::Expr, syn::Token![,]>,
|
||||||
}
|
}
|
||||||
|
@ -32,6 +57,16 @@ impl syn::parse::Parse for GMIReadParameters {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl GMIReadParameters {
|
||||||
|
fn from_attrs(attrs: &Vec<syn::Attribute>) -> Option<Vec<syn::Expr>> {
|
||||||
|
if let Some(attr) = attrs.iter().find(|a| a.path.is_ident("gmi_read_parameters")) {
|
||||||
|
let parameters = syn::parse2::<GMIReadParameters>(attr.tokens.clone()).expect("invalid gmi_read_parameters attribute");
|
||||||
|
return Some(parameters.fields.iter().cloned().collect());
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[proc_macro_derive(GMISerializable, attributes(gmi_tagged, gmi_tagged_nolen, gmi_read_parameters))]
|
#[proc_macro_derive(GMISerializable, attributes(gmi_tagged, gmi_tagged_nolen, gmi_read_parameters))]
|
||||||
pub fn gmi_serializable_macro(input: TokenStream) -> TokenStream {
|
pub fn gmi_serializable_macro(input: TokenStream) -> TokenStream {
|
||||||
let input = parse_macro_input!(input as DeriveInput);
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
@ -41,22 +76,16 @@ pub fn gmi_serializable_macro(input: TokenStream) -> TokenStream {
|
||||||
let mut tlv_header = quote! { };
|
let mut tlv_header = quote! { };
|
||||||
let mut tlv_footer = quote! { };
|
let mut tlv_footer = quote! { };
|
||||||
|
|
||||||
if let Some(attr) = input.attrs.iter().find(|a| a.path.is_ident("gmi_tagged") || a.path.is_ident("gmi_tagged_nolen")) {
|
if let Some(tagged) = GMITagged::from_attrs(&input.attrs) {
|
||||||
let nolen = attr.path.is_ident("gmi_tagged_nolen");
|
let (ty, flags) = (tagged.ty, tagged.flag);
|
||||||
let tagged = syn::parse2::<GMITagged>(attr.tokens.clone()).expect("invalid gmi_tagged attribute");
|
|
||||||
if tagged.fields.len() != 2 {
|
|
||||||
panic!("gmi_tagged takes two arguments");
|
|
||||||
}
|
|
||||||
let a: u32 = tagged.fields[0].base10_parse().expect("gmi_tagged arg 1 not a number");
|
|
||||||
let b: u32 = tagged.fields[1].base10_parse().expect("gmi_tagged arg 2 not a number");
|
|
||||||
|
|
||||||
tlv_header = quote! {
|
tlv_header = quote! {
|
||||||
let tlv = _r.tlv()?;
|
let tlv = _r.tlv()?;
|
||||||
if tlv.tag != #a || tlv.flags != #b {
|
if tlv.tag != #ty || tlv.flags != #flags {
|
||||||
return Err(tlv.error("unsupported tag/flags (wanted ...)"));
|
return Err(tlv.error(format!("unsupported tag/flags (wanted {}/{}, got {}/{})", #ty, #flags, tlv.tag, tlv.flags)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if !nolen {
|
if !tagged.nolen {
|
||||||
tlv_footer = quote! {
|
tlv_footer = quote! {
|
||||||
tlv.check(_r)?;
|
tlv.check(_r)?;
|
||||||
};
|
};
|
||||||
|
@ -74,8 +103,8 @@ pub fn gmi_serializable_macro(input: TokenStream) -> TokenStream {
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
impl GMISerializable for #ident {
|
impl gmi::Serializable for #ident {
|
||||||
fn read<R: std::io::Read>(_r: &mut GMIReadStream<R>) -> ReadResult<Self> {
|
fn read<R: std::io::Read>(_r: &mut gmi::ReadStream<R>) -> gmi::ReadResult<Self> {
|
||||||
#tlv_header
|
#tlv_header
|
||||||
let v: u32 = _r.read("value")?;
|
let v: u32 = _r.read("value")?;
|
||||||
let res = match v {
|
let res = match v {
|
||||||
|
@ -94,8 +123,7 @@ pub fn gmi_serializable_macro(input: TokenStream) -> TokenStream {
|
||||||
let field_type = &field.ty;
|
let field_type = &field.ty;
|
||||||
let field_ident_str = field_ident.as_ref().unwrap().to_string();
|
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());
|
let field_ident_quoted = syn::LitStr::new(&field_ident_str, proc_macro2::Span::call_site());
|
||||||
let parameters = field_parameters(&field.attrs);
|
if let Some(p) = GMIReadParameters::from_attrs(&field.attrs) {
|
||||||
if let Some(p) = parameters {
|
|
||||||
quote! {
|
quote! {
|
||||||
let #field_ident: #field_type = _r.read_parametrized(#field_ident_quoted, &[#(#p),*])?;
|
let #field_ident: #field_type = _r.read_parametrized(#field_ident_quoted, &[#(#p),*])?;
|
||||||
}
|
}
|
||||||
|
@ -112,8 +140,8 @@ pub fn gmi_serializable_macro(input: TokenStream) -> TokenStream {
|
||||||
}
|
}
|
||||||
}).collect();
|
}).collect();
|
||||||
quote! {
|
quote! {
|
||||||
impl GMISerializable for #ident {
|
impl gmi::Serializable for #ident {
|
||||||
fn read<R: std::io::Read>(_r: &mut GMIReadStream<R>) -> ReadResult<Self> {
|
fn read<R: std::io::Read>(_r: &mut gmi::ReadStream<R>) -> gmi::ReadResult<Self> {
|
||||||
#tlv_header
|
#tlv_header
|
||||||
#(#temporaries)*
|
#(#temporaries)*
|
||||||
let res = Self {
|
let res = Self {
|
||||||
|
@ -131,10 +159,149 @@ pub fn gmi_serializable_macro(input: TokenStream) -> TokenStream {
|
||||||
TokenStream::from(expanded)
|
TokenStream::from(expanded)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn field_parameters(attrs: &Vec<syn::Attribute>) -> Option<Vec<syn::Expr>> {
|
struct GMAName {
|
||||||
if let Some(attr) = attrs.iter().find(|a| a.path.is_ident("gmi_read_parameters")) {
|
#[allow(dead_code)]
|
||||||
let parameters = syn::parse2::<GMIReadParameters>(attr.tokens.clone()).expect("invalid gmi_read_parameters attribute");
|
paren_token: syn::token::Paren,
|
||||||
return Some(parameters.fields.iter().cloned().collect());
|
fields: syn::punctuated::Punctuated<syn::LitStr, syn::Token![,]>,
|
||||||
}
|
|
||||||
None
|
name: String,
|
||||||
|
bare: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl syn::parse::Parse for GMAName {
|
||||||
|
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::LitStr as syn::parse::Parse>::parse)?,
|
||||||
|
name: String::new(),
|
||||||
|
bare: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GMAName {
|
||||||
|
fn from_attrs(attrs: &Vec<syn::Attribute>) -> Option<Self> {
|
||||||
|
let attr = attrs.iter()
|
||||||
|
.find(|a| a.path.is_ident("gma_name")
|
||||||
|
|| a.path.is_ident("gma_name_bare"))?;
|
||||||
|
let mut res = syn::parse2::<Self>(attr.tokens.clone()).expect("invalid gma_name attribute");
|
||||||
|
if res.fields.len() != 1 {
|
||||||
|
panic!("gma_name takes one argument");
|
||||||
|
}
|
||||||
|
|
||||||
|
res.name = res.fields[0].value();
|
||||||
|
res.bare = attr.path.is_ident("gma_name_bare");
|
||||||
|
Some(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct GMAValue {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
paren_token: syn::token::Paren,
|
||||||
|
fields: syn::punctuated::Punctuated<syn::LitStr, syn::Token![,]>,
|
||||||
|
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl syn::parse::Parse for GMAValue {
|
||||||
|
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::LitStr as syn::parse::Parse>::parse)?,
|
||||||
|
name: String::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GMAValue {
|
||||||
|
fn from_attrs(attrs: &Vec<syn::Attribute>) -> Option<Self> {
|
||||||
|
let attr = attrs.iter().find(|a| a.path.is_ident("gma_value"))?;
|
||||||
|
let mut res = syn::parse2::<Self>(attr.tokens.clone()).expect("invalid gma_value attribute");
|
||||||
|
if res.fields.len() != 1 {
|
||||||
|
panic!("gma_value takes one argument");
|
||||||
|
}
|
||||||
|
|
||||||
|
res.name = res.fields[0].value();
|
||||||
|
Some(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(GMASerializable, attributes(gma_name, gma_name_bare, gma_value, gma_skip))]
|
||||||
|
pub fn gma_serializable_macro(input: TokenStream) -> TokenStream {
|
||||||
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
let ident = input.ident;
|
||||||
|
|
||||||
|
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 value: String = GMAValue::from_attrs(&variant.attrs)
|
||||||
|
.map(|s| s.name.to_string())
|
||||||
|
.or_else(|| {
|
||||||
|
Some(vident.to_string())
|
||||||
|
}).unwrap();
|
||||||
|
quote! {
|
||||||
|
#ident::#vident => #value,
|
||||||
|
}
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
impl gma::Serializable for #ident {
|
||||||
|
fn write<W: std::io::Write, S: std::string::ToString>(&self, _name: S, _w: &mut gma::WriteStream<W>) -> gma::WriteResult<()> {
|
||||||
|
_w.emit_pair(&_name.to_string(), match self {
|
||||||
|
#(#branches)*
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
syn::Data::Struct(syn::DataStruct { fields: syn::Fields::Named(fields), .. }) => {
|
||||||
|
let struct_name = GMAName::from_attrs(&input.attrs)
|
||||||
|
.map(|s| "*".to_string() + &s.name)
|
||||||
|
.or_else(|| {
|
||||||
|
Some("*".to_string() + &gma_name_from_ident(&ident))
|
||||||
|
}).unwrap();
|
||||||
|
let writes: Vec<proc_macro2::TokenStream> = fields.named.iter().map(|field| {
|
||||||
|
let field_ident = &field.ident;
|
||||||
|
let field_skip = field.attrs.iter().any(|a| a.path.is_ident("gma_skip"));
|
||||||
|
let field_name = GMAName::from_attrs(&field.attrs)
|
||||||
|
.map(|s|
|
||||||
|
if s.bare {
|
||||||
|
s.name
|
||||||
|
} else {
|
||||||
|
"*".to_string() + &s.name
|
||||||
|
})
|
||||||
|
.or_else(|| {
|
||||||
|
Some(struct_name.clone() + "_" + &gma_name_from_ident(field_ident.as_ref().unwrap()))
|
||||||
|
}).unwrap();
|
||||||
|
if field_skip {
|
||||||
|
quote! { }
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
self.#field_ident.write(#field_name, _w)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).collect();
|
||||||
|
quote! {
|
||||||
|
impl gma::Serializable for #ident {
|
||||||
|
fn write<W: std::io::Write, S: std::string::ToString>(&self, _name: S, _w: &mut gma::WriteStream<W>) -> gma::WriteResult<()> {
|
||||||
|
_w.push(#struct_name)?;
|
||||||
|
#(#writes)*
|
||||||
|
_w.pop()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => panic!("unimplemented"),
|
||||||
|
};
|
||||||
|
|
||||||
|
TokenStream::from(expanded)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gma_name_from_ident(ident: &syn::Ident) -> String {
|
||||||
|
ident.to_string().to_uppercase().replace("_", "")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
fn main() -> std::io::Result<()> {
|
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 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();
|
let gmf = gmflib::GMF::read_gmi(f).unwrap();
|
||||||
|
|
||||||
println!("{:#?}", gmi);
|
gmf.write_gma(std::io::stdout()).unwrap();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue