boxarena.gmf now decompiles exactly

main
q3k 2022-04-14 22:06:10 +00:00
parent cbf7e67221
commit 13c9fe6678
7 changed files with 861 additions and 139 deletions

View File

@ -5,3 +5,7 @@ members = [
"gmftool",
"gmfmacros",
]
[profile.release]
strip = true
lto = true

149
gmflib/src/gma.rs Normal file
View File

@ -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(())
}
}

View File

@ -1,5 +1,3 @@
use std::fmt;
pub struct ReadError {
pub msg: String,
pub stack: Vec<ParseFrame>,
@ -11,8 +9,8 @@ pub struct ParseFrame {
pub loc: usize,
}
impl fmt::Debug for ReadError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
impl std::fmt::Debug for ReadError {
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("->");
f.debug_struct("ReadError")
.field("msg", &self.msg)
@ -23,22 +21,15 @@ impl fmt::Debug for ReadError {
pub type ReadResult<T> = Result<T, ReadError>;
#[derive(Debug, Clone)]
pub struct WriteError {
pub msg: String,
}
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> {
pub trait Serializable: Sized {
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self>;
fn read_parametrized<R: std::io::Read>(r: &mut ReadStream<R>, _params: &[u32]) -> ReadResult<Self> {
Err(r.error("called with unexpected parameters"))
}
}
impl<T: GMISerializable> GMISerializable for Vec<T> {
fn read<R: std::io::Read>(r: &mut GMIReadStream<R>) -> ReadResult<Self> {
impl<T: Serializable> Serializable for Vec<T> {
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
let count: u32 = r.read("count")?;
// Safety check. A reasonable value of ~32MiB of smallest possible
// element (u32).
@ -47,28 +38,27 @@ impl<T: GMISerializable> GMISerializable for Vec<T> {
}
(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];
(0..count).map(|i| r.read(format!("elem[{}]", i))).collect()
}
}
impl<T: GMISerializable, const N: usize> GMISerializable for [T; N] {
fn read<R: std::io::Read>(r: &mut GMIReadStream<R>) -> ReadResult<Self> {
impl<T: Serializable, const N: usize> Serializable 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: GMISerializable> GMISerializable for Option<T> {
fn read<R: std::io::Read>(r: &mut GMIReadStream<R>) -> ReadResult<Self> {
impl<T: Serializable> Serializable 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);
}
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];
if count != 0 {
return Ok(Some(r.read("data")?));
@ -77,28 +67,35 @@ impl<T: GMISerializable> GMISerializable for Option<T> {
}
}
impl GMISerializable for u8 {
fn read<R: std::io::Read>(r: &mut GMIReadStream<R>) -> ReadResult<Self> {
impl Serializable for u8 {
fn read<R: std::io::Read>(r: &mut ReadStream<R>) -> ReadResult<Self> {
Ok(r.bytes(1)?[0])
}
}
impl GMISerializable for u32 {
fn read<R: std::io::Read>(r: &mut GMIReadStream<R>) -> ReadResult<Self> {
impl Serializable 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 GMISerializable for f32 {
fn read<R: std::io::Read>(r: &mut GMIReadStream<R>) -> ReadResult<Self> {
impl Serializable for i32 {
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();
Ok(f32::from_le_bytes(buf))
}
}
impl GMISerializable for String {
fn read<R: std::io::Read>(r: &mut GMIReadStream<R>) -> ReadResult<Self> {
impl Serializable 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)?;
@ -109,13 +106,13 @@ impl GMISerializable for String {
}
}
pub struct GMIReadStream<R: std::io::Read> {
pub struct ReadStream<R: std::io::Read> {
backing: R,
stack: Vec<ParseFrame>,
pos: usize,
}
impl<R: std::io::Read> GMIReadStream<R> {
impl<R: std::io::Read> ReadStream<R> {
pub fn new(r: R) -> Self {
Self {
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>> {
let mut buf: Vec<u8> = vec![0; 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());
let res = T::read(self);
self.pop();
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());
let res = T::read_parametrized(self, params);
self.pop();
@ -189,14 +186,14 @@ pub struct TLV {
}
impl TLV {
pub fn error(&self, msg: &str) -> ReadError {
pub fn error<S: std::string::ToString>(&self, msg: S) -> ReadError {
ReadError {
msg: msg.to_string(),
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;
if expected != r.pos {
return Err(ReadError {
@ -207,3 +204,4 @@ impl TLV {
Ok(())
}
}

View File

@ -1,4 +1,5 @@
mod machinery;
mod gmi;
mod gma;
pub mod types;
pub use types::*;

View File

@ -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)]
pub struct GMI {
pub struct GMF {
pub version: u32,
pub model_type: ModelType,
pub unk1: u32,
@ -14,19 +20,27 @@ pub struct GMI {
pub objects: ObjectList,
}
impl GMI {
pub fn parse<R: std::io::Read>(r: R) -> ReadResult<Self> {
let mut r = GMIReadStream::new(r);
r.read("gmi")
impl GMF {
pub fn read_gmi<R: io::Read>(r: R) -> gmi::ReadResult<Self> {
let mut r = gmi::ReadStream::new(r);
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 {
fn read<R: std::io::Read>(r: &mut GMIReadStream<R>) -> ReadResult<Self> {
impl gmi::Serializable for GMF {
fn read<R: io::Read>(r: &mut gmi::ReadStream<R>) -> gmi::ReadResult<Self> {
if r.bytes(3)? != b"GMI".to_vec() {
return Err(r.error("invaid magic"));
}
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 unk1: u32 = r.read("unk1")?;
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)]
pub struct Color {
pub r: u8,
@ -53,6 +79,18 @@ pub struct Color {
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)]
pub enum MapKind {
Diffuse = 1,
@ -60,23 +98,34 @@ pub enum MapKind {
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 {
Screen = 4,
}
#[derive(Debug,GMISerializable)]
#[derive(Debug,GMISerializable,GMASerializable)]
pub enum BitmapFilter {
Pyramidal = 0,
SAT = 1,
}
#[derive(Debug, GMISerializable)]
#[derive(Debug,GMISerializable,GMASerializable)]
pub enum ModelType {
#[gma_value("Basic Model")]
BasicModel = 1,
}
#[derive(Debug,GMISerializable)]
#[derive(Debug,GMISerializable,GMASerializable)]
#[gmi_tagged(1,2)]
pub struct Scene {
pub filename: String,
@ -84,19 +133,24 @@ pub struct Scene {
pub last_frame: u32,
pub frame_speed: u32,
pub ticks_per_frame: u32,
#[gma_name("SCENE_BACKGROUND_STATIC")]
pub background: Color,
#[gma_name("SCENE_AMBIENT_STATIC")]
pub ambient: Color,
}
#[derive(Debug,GMISerializable)]
#[derive(Debug,GMISerializable,GMASerializable)]
#[gmi_tagged(7, 2)]
#[gma_name("MATERIAL_LIST")]
pub struct MaterialList {
#[gma_name("MATERIAL")]
pub materials: Vec<Material>,
}
#[derive(Debug,GMISerializable)]
#[derive(Debug,GMISerializable,GMASerializable)]
#[gmi_tagged(8, 2)]
pub struct Material {
#[gma_name("MATERIAL_REF_NO")]
pub ref_no: u32,
pub name: String,
pub class: String,
@ -105,70 +159,115 @@ pub struct Material {
pub specular: Color,
pub shine: f32,
pub shine_strength: f32,
pub wiresize: f32,
pub transparency: f32,
pub wiresize: f32,
pub shading: Shading,
#[gma_name("MATERIAL_XP_FALLOFF")]
pub xp_falloff: f32,
pub selfillum: f32,
pub falloff: Falloff,
#[gma_name("MATERIAL_XP_TYPE")]
pub xp_type: XPType,
pub textures: Option<TextureList>,
pub sub: Option<MaterialList>,
}
#[derive(Debug,GMISerializable)]
#[derive(Debug,GMISerializable,GMASerializable)]
pub enum Shading {
Other = 0,
Blinn = 0xc,
}
#[derive(Debug, GMISerializable)]
#[derive(Debug, GMISerializable,GMASerializable)]
pub enum Falloff {
In = 0,
}
#[derive(Debug,GMISerializable)]
#[derive(Debug,GMISerializable,GMASerializable)]
pub enum XPType {
Other = 0,
Filter = 1,
}
#[derive(Debug,GMISerializable)]
#[derive(Debug,GMISerializable,GMASerializable)]
#[gmi_tagged(14, 2)]
#[gma_name("TEXTURE_LIST")]
pub struct TextureList {
#[gma_name("TEXTURE")]
pub textures: Vec<Texture>,
}
#[derive(Debug,GMISerializable)]
#[derive(Debug,GMISerializable,GMASerializable)]
#[gmi_tagged(15, 4)]
pub struct Texture {
#[gma_name("MAP_NAME")]
pub name: String,
#[gma_name("MAP_CLASS")]
pub class: String,
#[gma_name("BITMAP")]
pub bitmap: String,
#[gma_name("MAP_AMOUNT")]
pub amount: f32,
pub kind: MapKind,
#[gma_name("MAP_TYPE")]
pub map_type: MapType,
#[gma_name("UVW_U_OFFSET")]
pub u_offset: f32,
#[gma_name("UVW_V_OFFSET")]
pub v_offset: f32,
#[gma_name("UVW_U_TILING")]
pub u_tiling: f32,
#[gma_name("UVW_V_TILING")]
pub v_tiling: f32,
#[gma_name("UVW_ANGLE")]
pub angle: f32,
#[gma_name("UVW_BLUR")]
pub blur: f32,
#[gma_name("UVW_BLUR_OFFSET")]
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,
#[gma_name("UVW_NOISE_LEVEL")]
pub noise_level: u32,
#[gma_name("UVW_NOISE_PHASE")]
pub noise_phase: f32,
#[gma_name("")]
pub invert: u32,
#[gma_name("")]
pub unknown: u32,
#[gma_name("BITMAP_FILTER")]
pub filter: BitmapFilter,
#[gma_name("BITMAP_MAP_CHANNEL")]
pub channel: u32,
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)]
#[gma_name("OBJECT_LIST")]
pub struct ObjectList {
#[gma_name("OBJECT")]
pub objects: Vec<Object>,
}
@ -182,8 +281,8 @@ pub enum Object {
RBCollection(RBCollectionObject),
}
impl GMISerializable for Object {
fn read<R: std::io::Read>(r: &mut GMIReadStream<R>) -> ReadResult<Self> {
impl gmi::Serializable for Object {
fn read<R: io::Read>(r: &mut gmi::ReadStream<R>) -> gmi::ReadResult<Self> {
let tlv = r.tlv()?;
let (res, check) = match (tlv.tag, tlv.flags) {
(2, 4) => (Object::Geometry(r.read("geometry")?), false),
@ -194,7 +293,6 @@ impl GMISerializable for Object {
(31, 4) => (Object::RBCollection(r.read("rigidbodycollection")?), false),
_ => return Err(r.error(format!("unknown object type ({}, {})", tlv.tag, tlv.flags))),
};
println!("{:#?}", res);
if check {
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 {
#[gma_name("NODE_NAME")]
pub name: String,
#[gma_name("NODE_PARENT")]
pub parent: String,
#[gma_name("NODE_SHADEVERTS")]
pub shade_verts: u8,
#[gma_name("NODE_TM")]
pub tm: TransformMatrix,
pub mesh: Mesh,
}
#[derive(Debug,GMISerializable)]
#[derive(Debug,GMISerializable,GMASerializable)]
#[gmi_tagged(17, 2)]
#[gma_name("NODE_TM")]
pub struct TransformMatrix {
#[gma_name("NODE_NAME")]
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)]
@ -226,39 +351,190 @@ pub struct Mesh {
pub face_count: u32,
pub tvertex_count: u32,
pub cvertex_count: u32,
pub material_ref: u32,
pub material_ref: i32,
#[gmi_read_parameters(vertex_count)]
pub vertices: Vec<Point>,
pub vertices: MeshVertexList,
#[gmi_read_parameters(face_count)]
pub faces: Vec<Face>,
pub faces: MeshFaceList,
#[gmi_read_parameters(tvertex_count)]
pub tvertices: Vec<Point>,
pub tvertices: MeshTVertexList,
#[gmi_read_parameters(if tvertex_count > 0 { face_count } else { 0 })]
pub tfaces: Vec<TFace>,
pub tfaces: MeshTFaceList,
pub channels: Option<Vec<TextureChannel>>,
#[gmi_read_parameters(cvertex_count)]
pub cvertices: Vec<Point>,
pub cvertices: MeshCVertexList,
#[gmi_read_parameters(if cvertex_count > 0 { face_count } else { 0 })]
pub cfaces: Vec<TFace>,
pub cfaces: MeshCFaceList,
#[gmi_read_parameters(face_count)]
pub normals: Vec<FaceNormal>,
pub normals: MeshNormalList,
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 x: f32,
pub y: f32,
pub z: f32,
}
#[derive(Debug,GMISerializable)]
#[derive(Debug,GMISerializable,GMASerializable)]
pub struct Face {
pub a: u32,
pub b: u32,
@ -266,7 +542,7 @@ pub struct Face {
pub mtlid: u32,
}
#[derive(Debug,GMISerializable)]
#[derive(Debug,GMISerializable,GMASerializable)]
pub struct TFace {
pub a: u32,
pub b: u32,
@ -279,7 +555,13 @@ pub struct FaceNormal {
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 unk1: u32,
pub tvertex_count: u32,
@ -291,87 +573,149 @@ pub struct TextureChannel {
pub face_count2: u32,
#[gmi_read_parameters(tvertex_count)]
pub tvertices: Vec<Point>,
pub tvertices: MeshTVertexList,
#[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 {
#[gma_name("NODE_NAME")]
pub name: String,
pub tm: TransformMatrix,
#[gma_skip]
pub target: String,
#[gma_name("LIGHT_TYPE")]
pub light_type: LightType,
pub shadows: LightShadows,
pub uselight: u32,
pub spotshape: LightSpotShape,
pub color: Color,
#[gma_name("LIGHT_INTENS")]
pub intensity: f32,
pub aspect: f32,
#[gma_skip]
pub unk1: [u8; 8],
pub attn_start: f32,
pub attn_end: 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 {
Omni = 0,
}
#[derive(Debug,GMISerializable)]
#[derive(Debug,GMISerializable,GMASerializable)]
pub enum LightShadows {
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 {
#[gma_name("NODE_NAME")]
pub name: String,
pub tm: TransformMatrix,
#[gma_name_bare("USER DATA")]
pub user_data: String,
}
#[derive(Debug,GMISerializable)]
#[derive(Debug,GMISerializable,GMASerializable)]
#[gma_name("GMID_HAVOK_CONSTRAINTSOLVER")]
pub struct ConstraintSolverObject {
#[gma_name("NODE_NAME")]
pub name: String,
#[gma_name("THRESHOLD")]
pub threshold: f32,
#[gma_name("RB_COLLECTION_NAME")]
pub rb_collection_name: String,
pub constraints: Option<ConstraintList>,
}
#[derive(Debug,GMISerializable)]
#[derive(Debug,GMISerializable,GMASerializable)]
#[gmi_tagged(44, 2)]
pub struct ConstraintList {
pub constraints: Vec<Constraint>,
}
#[derive(Debug,GMISerializable)]
#[derive(Debug,GMISerializable,GMASerializable)]
pub enum Constraint {
Foo = 1,
}
#[derive(Debug,GMISerializable)]
#[derive(Debug,GMISerializable,GMASerializable)]
#[gma_name("GMID_HAVOK_SIMOBJECT")]
pub struct SimulationObject {
pub name: String,
pub gravity: Point,
#[gma_name("NODE_NAME")]
pub name: ObjectName,
#[gma_name("GRAVITY")]
pub gravity: Gravity,
#[gma_name("WORLDSCALE")]
pub worldscale: f32,
#[gma_name("SIMTOLERANCE")]
pub simtolerance: f32,
#[gma_name("RESOLVER")]
pub resolver: u32,
#[gma_name("INCLUDE_DRAG")]
pub incl_drag: u8,
#[gma_name("LINEAR_DRAG")]
pub linear_drag: f32,
#[gma_name("ANGULAR_DRAG")]
pub angular_drag: f32,
#[gma_name("INCLUDE_DEACTIVATOR")]
pub incl_deactivator: u8,
#[gma_name("SHORTFREQ")]
pub shortfreq: f32,
#[gma_name("LONGFREQ")]
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,
#[gma_name("NUM_COLLISION_PAIRS")]
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 {
#[gma_name("NODE_NAME")]
pub name: String,
#[gma_name("NUM_DISABLED_PAIRS")]
pub disabled_pairs: u32,
#[gma_name("SOLVER_TYPE")]
pub solver_type: u32,
pub rigidbody_list: RigidBodyList,
#[gmi_read_parameters(disabled_pairs)]
@ -384,29 +728,81 @@ pub struct RigidBodyList {
pub rigidbodies: Vec<RigidBody>,
}
#[derive(Debug,GMISerializable)]
#[gmi_tagged_nolen(32, 4)]
pub struct RigidBody {
pub name: String,
pub mass: f32,
pub elasticity: f32,
pub friction: f32,
pub optimization_level: f32,
pub unyielding: u32,
pub simulation_geometry: u32,
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>,
impl gma::Serializable for RigidBodyList {
fn write<W: io::Write, S: string::ToString>(&self, _name: S, w: &mut gma::WriteStream<W>) -> gma::WriteResult<()> {
self.rigidbodies.len().write("*COUNT", w)?;
w.push("*GMID_HAVOK_RIGIDBODY_LIST")?;
self.rigidbodies.len().write("*COUNT", w)?;
for rigidbody in self.rigidbodies.iter() {
rigidbody.write("", w)?;
}
w.pop()?;
Ok(())
}
}
#[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)]
#[gma_name("GMID_HAVOK_DIS_COLLISION_PAIRS")]
pub struct DisabledCollisionPairList {
#[gma_name("")]
pub pairs: Vec<DisabledCollisionPair>,
}
@ -415,3 +811,10 @@ pub struct DisabledCollisionPair {
pub a: 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))
}
}

View File

@ -3,8 +3,13 @@ use syn::{parse_macro_input, DeriveInput, parenthesized};
use quote::quote;
struct GMITagged {
#[allow(dead_code)]
paren_token: syn::token::Paren,
fields: syn::punctuated::Punctuated<syn::LitInt, syn::Token![,]>,
nolen: bool,
ty: u32,
flag: u32,
}
impl syn::parse::Parse for GMITagged {
@ -13,11 +18,31 @@ impl syn::parse::Parse for GMITagged {
Ok(Self {
paren_token: parenthesized!(content in input),
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 {
#[allow(dead_code)]
paren_token: syn::token::Paren,
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))]
pub fn gmi_serializable_macro(input: TokenStream) -> TokenStream {
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_footer = quote! { };
if let Some(attr) = input.attrs.iter().find(|a| a.path.is_ident("gmi_tagged") || a.path.is_ident("gmi_tagged_nolen")) {
let nolen = attr.path.is_ident("gmi_tagged_nolen");
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");
if let Some(tagged) = GMITagged::from_attrs(&input.attrs) {
let (ty, flags) = (tagged.ty, tagged.flag);
tlv_header = quote! {
let tlv = _r.tlv()?;
if tlv.tag != #a || tlv.flags != #b {
return Err(tlv.error("unsupported tag/flags (wanted ...)"));
if tlv.tag != #ty || tlv.flags != #flags {
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.check(_r)?;
};
@ -74,8 +103,8 @@ pub fn gmi_serializable_macro(input: TokenStream) -> TokenStream {
}).collect();
quote! {
impl GMISerializable for #ident {
fn read<R: std::io::Read>(_r: &mut GMIReadStream<R>) -> ReadResult<Self> {
impl gmi::Serializable for #ident {
fn read<R: std::io::Read>(_r: &mut gmi::ReadStream<R>) -> gmi::ReadResult<Self> {
#tlv_header
let v: u32 = _r.read("value")?;
let res = match v {
@ -94,8 +123,7 @@ pub fn gmi_serializable_macro(input: TokenStream) -> TokenStream {
let field_type = &field.ty;
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 parameters = field_parameters(&field.attrs);
if let Some(p) = parameters {
if let Some(p) = GMIReadParameters::from_attrs(&field.attrs) {
quote! {
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();
quote! {
impl GMISerializable for #ident {
fn read<R: std::io::Read>(_r: &mut GMIReadStream<R>) -> ReadResult<Self> {
impl gmi::Serializable for #ident {
fn read<R: std::io::Read>(_r: &mut gmi::ReadStream<R>) -> gmi::ReadResult<Self> {
#tlv_header
#(#temporaries)*
let res = Self {
@ -131,10 +159,149 @@ pub fn gmi_serializable_macro(input: TokenStream) -> TokenStream {
TokenStream::from(expanded)
}
fn field_parameters(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
struct GMAName {
#[allow(dead_code)]
paren_token: syn::token::Paren,
fields: syn::punctuated::Punctuated<syn::LitStr, syn::Token![,]>,
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("_", "")
}

View File

@ -1,8 +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();
let gmf = gmflib::GMF::read_gmi(f).unwrap();
println!("{:#?}", gmi);
gmf.write_gma(std::io::stdout()).unwrap();
Ok(())
}