ScrapHacks/tools/remaster/scrap_parse/src/main.rs

907 lines
21 KiB
Rust

#![allow(clippy::upper_case_acronyms, non_camel_case_types)]
use anyhow::{anyhow, bail, Result};
use binrw::args;
use binrw::prelude::*;
use binrw::until_exclusive;
use chrono::{DateTime, NaiveDateTime, Utc};
use clap::Parser;
use configparser::ini::Ini;
use flate2::write::GzEncoder;
use flate2::Compression;
use fs_err as fs;
use indexmap::IndexMap;
use modular_bitfield::bitfield;
use modular_bitfield::specifiers::B2;
use modular_bitfield::specifiers::B4;
use modular_bitfield::BitfieldSpecifier;
use serde::Serialize;
use serde_json::Map;
use serde_json::Value;
use std::collections::HashMap;
use std::fmt::Debug;
use std::fs::File;
use std::io::{BufReader, Read, Seek};
use std::path::Path;
use std::path::PathBuf;
#[binread]
#[derive(Serialize, Debug)]
#[br(import(msg: &'static str))]
struct Unparsed<const SIZE: u64> {
#[br(count=SIZE, try_map=|data: Vec<u8>| Err(anyhow!("Unparsed data: {}\n{}", msg, rhexdump::hexdump(&data))))]
data: (),
}
#[binread]
#[derive(Serialize, Debug)]
struct RawTable<const SIZE: u32> {
num_entries: u32,
#[br(assert(entry_size==SIZE))]
entry_size: u32,
#[br(count=num_entries, args {inner: args!{count: entry_size.try_into().unwrap()}})]
data: Vec<Vec<u8>>,
}
#[binread]
#[derive(Serialize, Debug)]
struct Table<T: for<'a> BinRead<Args<'a> = ()> + 'static> {
num_entries: u32,
entry_size: u32,
#[br(count=num_entries)]
data: Vec<T>,
}
// impl<T: for<'a> BinRead<Args<'a> = ()>> Serialize for Table<T> where T: Serialize {
// fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
// where
// S: serde::Serializer {
// self.data.serialize(serializer)
// }
// }
#[binread]
struct Optional<T: for<'a> BinRead<Args<'a> = ()>> {
#[br(temp)]
has_value: u32,
#[br(if(has_value!=0))]
value: Option<T>,
}
impl<T: for<'a> BinRead<Args<'a> = ()> + Debug> Debug for Optional<T>
where
T: Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.value.fmt(f)
}
}
impl<T: for<'a> BinRead<Args<'a> = ()> + std::ops::Deref> std::ops::Deref for Optional<T> {
type Target = Option<T>;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl<T: for<'a> BinRead<Args<'a> = ()> + Serialize> Serialize for Optional<T> {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.value.serialize(serializer)
}
}
#[binread]
#[derive(Serialize, Debug)]
struct Chunk {
#[br(map=|c:[u8;4]| c.into_iter().map(|v| v as char).collect())]
magic: Vec<char>,
size: u32,
#[br(temp,count=size)]
data: Vec<u8>,
}
#[binread]
struct PascalString {
#[br(temp)]
length: u32,
#[br(count=length, map=|bytes: Vec<u8>| {
String::from_utf8_lossy(&bytes.iter().copied().take_while(|&v| v!=0).collect::<Vec<u8>>()).into_owned()
})]
string: String,
}
impl Serialize for PascalString {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.string.serialize(serializer)
}
}
impl Debug for PascalString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.string.fmt(f)
}
}
#[binread]
#[derive(Debug, Serialize)]
struct IniSection {
#[br(temp)]
num_lines: u32,
#[br(count=num_lines)]
sections: Vec<PascalString>,
}
#[binread]
#[br(magic = b"INI\0")]
#[derive(Debug)]
struct INI {
size: u32,
#[br(temp)]
num_sections: u32,
#[br(count=num_sections)]
sections: Vec<IniSection>,
}
impl Serialize for INI {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let blocks: Vec<String> = self
.sections
.iter()
.flat_map(|s| s.sections.iter())
.map(|s| s.string.clone())
.collect();
Ini::new().read(blocks.join("\n")).serialize(serializer)
}
}
#[binread]
#[derive(Debug, Serialize, Clone)]
struct RGBA {
r: u8,
g: u8,
b: u8,
a: u8,
}
#[binread]
#[derive(Debug, Serialize, Clone)]
#[br(import(n_dims: usize))]
struct TexCoords(#[br(count=n_dims)] Vec<f32>);
#[binread]
#[derive(Debug, Serialize, Clone)]
#[br(import(vert_fmt: FVF))]
// https://github.com/elishacloud/dxwrapper/blob/23ffb74c4c93c4c760bb5f1de347a0b039897210/ddraw/IDirect3DDeviceX.cpp#L2642
struct Vertex {
xyz: [f32; 3],
// #[br(if(vert_fmt.pos()==Pos::XYZRHW))] // seems to be unused
// rhw: Option<f32>,
#[br(if(vert_fmt.normal()))]
normal: Option<[f32; 3]>,
#[br(if(vert_fmt.point_size()))]
point_size: Option<[f32; 3]>,
#[br(if(vert_fmt.diffuse()))]
diffuse: Option<RGBA>,
#[br(if(vert_fmt.specular()))]
specular: Option<RGBA>,
#[br(if(vert_fmt.tex_count()>=1), args (vert_fmt.tex_dims(0),))]
tex_1: Option<TexCoords>,
#[br(if(vert_fmt.tex_count()>=2), args (vert_fmt.tex_dims(1),))]
tex_2: Option<TexCoords>,
#[br(if(vert_fmt.tex_count()>=3), args (vert_fmt.tex_dims(2),))]
tex_3: Option<TexCoords>,
#[br(if(vert_fmt.tex_count()>=4), args (vert_fmt.tex_dims(3),))]
tex_4: Option<TexCoords>,
#[br(if(vert_fmt.tex_count()>=5), args (vert_fmt.tex_dims(4),))]
tex_5: Option<TexCoords>,
#[br(if(vert_fmt.tex_count()>=6), args (vert_fmt.tex_dims(5),))]
tex_6: Option<TexCoords>,
#[br(if(vert_fmt.tex_count()>=7), args (vert_fmt.tex_dims(6),))]
tex_7: Option<TexCoords>,
#[br(if(vert_fmt.tex_count()>=8), args (vert_fmt.tex_dims(7),))]
tex_8: Option<TexCoords>,
}
#[derive(Debug, Serialize, PartialEq, Eq, BitfieldSpecifier)]
#[bits = 3]
enum Pos {
XYZ,
XYZRHW,
XYZB1,
XYZB2,
XYZB3,
XYZB4,
XYZB5,
}
#[bitfield]
#[repr(u32)]
#[derive(Debug, Serialize, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct FVF {
reserved_1: bool,
pos: Pos,
normal: bool,
point_size: bool,
diffuse: bool,
specular: bool,
tex_count: B4,
tex_1: B2,
tex_2: B2,
tex_3: B2,
tex_4: B2,
tex_5: B2,
tex_6: B2,
tex_7: B2,
tex_8: B2,
rest: B4,
}
impl FVF {
fn tex_dims(&self, tex: u8) -> usize {
let tex: u8 = match tex {
0 => self.tex_1(),
1 => self.tex_2(),
2 => self.tex_3(),
3 => self.tex_4(),
4 => self.tex_5(),
5 => self.tex_6(),
6 => self.tex_7(),
7 => self.tex_8(),
_ => unreachable!(),
};
match tex {
0 => 2,
1 => 3,
2 => 4,
3 => 1,
_ => unreachable!(),
}
}
fn num_w(&self) -> usize {
use Pos::*;
match self.pos() {
XYZ | XYZRHW => 0,
XYZB1 => 1,
XYZB2 => 2,
XYZB3 => 3,
XYZB4 => 4,
XYZB5 => 5,
}
}
}
fn vertex_size_from_id(fmt_id: u32) -> Result<u32> {
let fmt_size = match fmt_id {
0 => 0x0,
1 | 8 | 10 => 0x20,
2 => 0x28,
3 | 0xd => 0x1c,
4 | 7 => 0x24,
5 => 0x2c,
6 => 0x34,
0xb => 4,
0xc => 0x18,
0xe => 0x12,
0xf | 0x10 => 0x16,
0x11 => 0x1a,
other => bail!("Invalid vertex format id: {other}"),
};
Ok(fmt_size)
}
fn vertex_format_from_id(fmt_id: u32, fmt: u32) -> Result<FVF> {
let fvf = match fmt_id {
0 => 0x0,
1 => 0x112,
2 => 0x212,
3 => 0x1c2,
4 => 0x116,
5 => 0x252,
6 => 0x352,
7 => 0x152,
8 => 0x1c4,
10 => 0x242,
other => bail!("Invalid vertex format id: {other}"),
};
if fvf != fmt {
bail!("Vertex format mismatch: {fvf}!={fmt}");
}
Ok(FVF::from(fvf))
}
#[binread]
#[br(import(fmt_id: u32))]
#[derive(Debug, Serialize, Clone)]
struct LFVFInner {
#[br(try_map=|v: u32| vertex_format_from_id(fmt_id,v))]
vert_fmt: FVF,
#[br(assert(vert_size==vertex_size_from_id(fmt_id).unwrap()))]
vert_size: u32,
num_verts: u32,
#[br(count=num_verts, args {inner: (vert_fmt,)})]
data: Vec<Vertex>,
}
#[binread]
#[br(magic = b"LFVF")]
#[derive(Debug, Serialize)]
struct LFVF {
size: u32,
#[br(assert(version==1,"invalid LFVF version"))]
version: u32,
#[br(assert((0..=0x11).contains(&fmt_id),"invalid LFVF format_id"))]
fmt_id: u32,
#[br(if(fmt_id!=0),args(fmt_id))]
inner: Option<LFVFInner>,
}
#[binread]
#[br(magic = b"MD3D")]
#[derive(Debug, Serialize)]
struct MD3D {
// TODO: mesh
size: u32,
#[br(assert(version==1,"Invalid MD3D version"))]
version: u32,
name: PascalString,
num_tris: u32,
#[br(assert(tri_size==6,"Invalid MD3D tri size"))]
tri_size: u32,
#[br(count=num_tris)]
tris: Vec<[u16; 3]>,
mesh_data: LFVF,
unk_table_1: RawTable<2>,
// TODO:
// ==
// unk_t1_count: u32,
// #[br(assert(unk_t1_size==2))]
// unk_t1_size: u32,
// #[br(count=unk_t1_count)]
// unk_t1_list: Vec<u16>,
// // ==
// unk_t2_count: u32,
// #[br(assert(unk_t1_size==2))]
// unk_t2_size: u32,
// #[br(count=unk_t1_count)]
// unk_t2_list: Vec<u16>,
}
#[binread]
#[derive(Debug, Serialize)]
#[serde(tag = "type")]
enum NodeData {
#[br(magic = 0x0u32)]
Null,
#[br(magic = 0xa1_00_00_01_u32)]
TriangleMesh, // Empty?
#[br(magic = 0xa1_00_00_02_u32)]
Mesh(MD3D),
#[br(magic = 0xa2_00_00_04_u32)]
Camera(CAM),
#[br(magic = 0xa3_00_00_08_u32)]
Light(LUZ),
#[br(magic = 0xa4_00_00_10_u32)]
Ground(SUEL),
#[br(magic = 0xa5_00_00_20_u32)]
SisPart(Unparsed<0x10>), // TODO: Particles
#[br(magic = 0xa6_00_00_40_u32)]
Graphic3D(SPR3),
#[br(magic = 0xa6_00_00_80_u32)]
Flare(Unparsed<0x10>), // TODO: LensFlare?
#[br(magic = 0xa7_00_01_00u32)]
Portal(PORT),
}
#[binread]
#[br(magic = b"SPR3")]
#[derive(Debug, Serialize)]
struct SPR3 {
size: u32,
#[br(assert(version==1,"Invalid SPR3 version"))]
version: u32,
pos: [f32; 3],
unk_1: [u8; 8],
name_1: PascalString,
name_2: PascalString,
unk_2: u32,
}
#[binread]
#[br(magic = b"SUEL")]
#[derive(Debug, Serialize)]
struct SUEL {
size: u32,
#[br(assert(version==1,"Invalid SUEL version"))]
version: u32,
bbox: [[f32; 3]; 2],
pos: [f32; 3],
unk_3: [u8; 4],
num_nodes: u32,
unk_4: [u8; 4],
bbox_2: [[f32; 3]; 2],
}
#[binread]
#[br(magic = b"CAM\0")]
#[derive(Debug, Serialize)]
struct CAM {
size: u32,
#[br(assert(version==1,"Invalid CAM version"))]
version: u32,
unk_1: [f32; 3],
origin: [f32; 3],
destination: [f32; 3],
unk_4: [u8; 4],
unk_5: [u8; 4],
unk_6: [u8; 4],
unk_7: [u8; 4],
unk_8: [u8; 4],
unk_9: [u8; 4],
unk_10: [u8; 4],
unk_11: [u8; 4],
}
#[binread]
#[br(magic = b"LUZ\0")]
#[derive(Debug, Serialize)]
struct LUZ {
size: u32,
#[br(assert(version==1,"Invalid LUZ version"))]
version: u32,
col: u32,
brightness: u32,
unk_3: u8,
pos: [f32; 3],
rot: [f32; 3],
unk_6: [u8; 8],
unk_7: [u8; 4],
unk_8: [u8; 4],
unk_9: [u8; 4],
unk_10: [u8; 4],
unk_11: [u8; 4],
unk_12: [u8; 4],
unk_13: u32,
}
#[binread]
#[br(magic = b"PORT")]
#[derive(Debug, Serialize)]
struct PORT {
size: u32,
#[br(assert(version==1,"Invalid PORT version"))]
version: u32,
width: u32,
height: u32,
sides: [u32; 2],
}
#[binread]
#[derive(Debug, Serialize)]
struct Node {
unk_f17_0x44: u32,
unk_f18_0x48: u32,
unk_f19_0x4c: u32,
flags: u32,
unk_f20_0x50: u32,
name: PascalString,
parent: PascalString,
unk_f7_0x1c: [f32; 3], // 0xc
unk_f10_0x28: [f32; 4], // 0x10
unk_f14_0x38: f32, // 0x4
unk_f23_0x5c: [[f32; 4]; 4], // 0x40 4x4 Matrix
unk_f39_0x9c: [[f32; 4]; 4], // 0x40 4x4 Matrix
unk_f55_0xdc: [f32; 4], // 0x10 Vector?
unk_f59_0xec: [f32; 3], // 0xc Vector?
node_info: Optional<INI>,
content: Optional<NodeData>,
}
#[binread]
#[br(magic = b"MAP\0")]
#[derive(Debug, Serialize)]
struct MAP {
size: u32,
#[br(assert((2..=3).contains(&version),"invalid MAP version"))]
version: u32,
texture: PascalString,
unk_1: [u8; 7],
unk_bbox: [[f32; 2]; 2],
unk_2: f32,
#[br(if(version==3))]
unk_3: Option<[u8; 0xc]>,
}
#[binread]
#[br(magic = b"MAT\0")]
#[derive(Debug, Serialize)]
struct MAT {
size: u32,
#[br(assert((1..=3).contains(&version),"invalid MAT version"))]
version: u32,
#[br(if(version>1))]
name: Option<PascalString>,
unk_f: [RGBA; 7],
unk_data: [RGBA; 0x18 / 4],
maps: [Optional<MAP>; 5], // Base Color, Metallic?, ???, Normal, Emission
}
#[binread]
#[br(magic = b"SCN\0")]
#[derive(Debug, Serialize)]
struct SCN {
// 0x650220
size: u32,
#[br(temp,assert(version==1))]
version: u32,
model_name: PascalString,
node_name: PascalString,
node_props: Optional<INI>,
unk_f_1: [f32; (8 + 8) / 4],
unk_1: [f32; 0x18 / 4],
unk_f_2: f32,
user_props: Optional<INI>,
num_materials: u32,
#[br(count=num_materials)]
mat: Vec<MAT>,
#[br(temp,assert(unk_3==1))]
unk_3: u32,
num_nodes: u32,
#[br(count = num_nodes)] // 32
nodes: Vec<Node>,
ani: Optional<ANI>, // TODO:?
}
fn convert_timestamp(dt: u32) -> Result<DateTime<Utc>> {
let Some(dt) = NaiveDateTime::from_timestamp_opt(dt.into(),0) else {
bail!("Invalid timestamp");
};
Ok(DateTime::from_utc(dt, Utc))
}
#[binread]
#[derive(Debug, Serialize)]
struct VertexAnim {
n_tr: u32,
maybe_duration: f32,
#[br(count=n_tr)]
tris: Vec<[u8; 3]>,
}
#[binread]
#[br(magic = b"EVA\0")]
#[derive(Debug, Serialize)]
struct EVA {
size: u32,
#[br(assert(version==1,"Invalid EVA version"))]
version: u32,
num_verts: u32,
#[br(count=num_verts)]
verts: Vec<Optional<VertexAnim>>,
}
#[binread]
#[br(magic = b"NAM\0")]
#[derive(Debug, Serialize)]
struct NAM {
size: u32,
#[br(assert(version==1))]
version: u32,
primer_frames: u32,
frames: u32,
#[br(assert(flags&0xffffef60==0,"Invalid NAM flags"))]
flags: u32,
#[br(assert(opt_flags&0xfff8==0,"Invalid NAM opt_flags"))]
opt_flags: u32,
#[br(assert(stm_flags&0xfff8==0,"Invalid NAM stm_flags"))]
stm_flags: u32,
#[br(map=|_:()| flags&(opt_flags|0x8000)&stm_flags)]
combined_flags: u32,
#[br(if(combined_flags&0x1!=0))]
unk_flags_1: Option<u32>,
#[br(if(combined_flags&0x2!=0))]
unk_flags_2: Option<u32>,
#[br(if(combined_flags&0x4!=0))]
unk_flags_3: Option<u32>,
#[br(if(combined_flags&0x8!=0))]
unk_flags_4: Option<u32>,
#[br(if(combined_flags&0x10!=0))]
unk_flags_5: Option<u32>,
#[br(if(combined_flags&0x80!=0))]
unk_flags_6: Option<u32>,
#[br(if(flags&0x1000!=0))]
eva: Option<EVA>,
}
#[binread]
#[br(magic = b"NABK")]
#[derive(Debug, Serialize)]
struct NABK {
size: u32,
#[br(temp,count=size)]
data: Vec<u8>,
}
#[binread]
#[br(magic = b"ANI\0")]
#[derive(Debug, Serialize)]
struct ANI {
size: u32,
#[br(assert(version==2, "Invalid ANI version"))]
version: u32,
fps: f32,
unk_1: u32,
unk_2: u32,
num_objects: u32,
unk_flags: u32,
num: u32,
#[br(temp,count=num)]
data: Vec<u8>,
nabk: NABK,
#[br(count=num_objects)]
nam: Vec<NAM>,
}
#[binread]
#[br(magic = b"SM3\0")]
#[derive(Debug, Serialize)]
struct SM3 {
size: u32,
#[br(temp,assert(const_1==0x6515f8,"Invalid timestamp"))]
const_1: u32,
#[br(try_map=convert_timestamp)]
time_1: DateTime<Utc>,
#[br(try_map=convert_timestamp)]
time_2: DateTime<Utc>,
scene: SCN,
}
#[binread]
#[br(magic = b"CM3\0")]
#[derive(Debug, Serialize)]
struct CM3 {
size: u32,
#[br(temp,assert(const_1==0x6515f8,"Invalid timestamp"))]
const_1: u32,
#[br(try_map=convert_timestamp)]
time_1: DateTime<Utc>,
#[br(try_map=convert_timestamp)]
time_2: DateTime<Utc>,
scene: SCN,
}
#[binread]
#[derive(Debug, Serialize)]
struct Dummy {
name: PascalString,
pos: [f32; 3],
rot: [f32; 3],
info: Optional<INI>,
has_next: u32,
}
#[binread]
#[br(magic = b"DUM\0")]
#[derive(Debug, Serialize)]
struct DUM {
size: u32,
#[br(assert(version==1, "Invalid DUM version"))]
version: u32,
num_dummies: u32,
unk_1: u32,
#[br(count=num_dummies)]
dummies: Vec<Dummy>,
}
#[binread]
#[br(magic = b"QUAD")]
#[derive(Debug, Serialize)]
struct QUAD {
size: u32,
#[br(assert(version==1, "Invalid QUAD version"))]
version: u32,
mesh: u32,
table: Table<u16>,
f_4: [f32; 4],
num_children: u32,
#[br(count=num_children)]
children: Vec<QUAD>,
}
#[binread]
#[br(magic = b"CMSH")]
#[derive(Debug, Serialize)]
struct CMSH {
size: u32,
#[br(assert(version==2, "Invalid CMSH version"))]
version: u32,
#[br(assert(collide_mesh_size==0x34, "Invalid collision mesh size"))]
collide_mesh_size: u32,
name: PascalString,
unk_1: u16,
sector: u16,
unk_2: u16,
index: u8,
unk_4: u8,
bbox_1: [[f32; 3]; 2],
#[br(temp)]
t_1: Table<[f32; 3]>,
#[br(temp)]
t_2: RawTable<0x1c>,
}
#[binread]
#[br(magic = b"AMC\0")]
#[derive(Debug, Serialize)]
struct AMC {
size: u32,
#[br(assert(version==100,"Invalid AMC version"))]
version: u32,
#[br(assert(version_code==0, "Invalid AMC version_code"))]
version_code: u32,
bbox_1: [[f32; 3]; 2],
scale: f32,
bbox_2: [[f32; 3]; 2],
unk: [f32; 3],
cmsh: [CMSH; 2],
num_sectors: u32,
#[br(count=num_sectors)]
sector_col: Vec<[CMSH; 2]>,
unk_num_1: u32,
unk_num_2: u32,
unk_f: [f32; 4],
num_quads: u32,
#[br(count=num_quads)]
quads: Vec<QUAD>,
}
#[binread]
#[br(import(version: u32))]
#[derive(Debug, Serialize)]
struct TriV104 {
#[br(if(version>=0x69))]
name_2: Option<PascalString>,
mat_key: u32,
map_key: u32,
num_tris: u32,
#[br(count=num_tris)]
tris: Vec<[u16; 3]>,
verts_1: LFVF,
verts_2: LFVF,
}
#[binread]
#[br(magic = b"TRI\0", import(version: u32))]
#[derive(Debug, Serialize)]
struct TRI {
size: u32,
unk_int: u32,
name: PascalString,
unk_int_2: u32, // if 0xffffffff sometimes TriV104 has no name_2 field
#[br(args(version))]
data: TriV104,
}
#[binread]
#[derive(Debug, Serialize)]
struct EMI_Textures {
key: u32,
#[br(if(key!=0))]
data: Option<(PascalString, u32, PascalString)>,
}
#[binread]
#[br(magic = b"EMI\0")]
#[derive(Debug, Serialize)]
struct EMI {
size: u32,
#[br(assert((103..=105).contains(&version)))]
version: u32,
num_materials: u32,
#[br(count=num_materials)]
materials: Vec<(u32, MAT)>,
#[br(parse_with = until_exclusive(|v: &EMI_Textures| v.key==0))]
maps: Vec<EMI_Textures>,
num_lists: u32,
#[br(count=num_lists,args{inner: (version,)})]
tri: Vec<TRI>,
}
#[binread]
#[derive(Debug, Serialize)]
enum Data {
SM3(SM3),
CM3(CM3),
DUM(DUM),
AMC(AMC),
EMI(EMI),
}
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
root: PathBuf,
path: PathBuf,
}
fn parse_file(path: &PathBuf) -> Result<Data> {
let mut rest_size = 0;
let mut fh = BufReader::new(fs::File::open(path)?);
let ret = fh.read_le()?;
let pos = fh
.stream_position()
.unwrap_or(0)
.try_into()
.unwrap_or(u32::MAX);
println!("Read {} bytes from {}", pos, path.display());
let mut buffer = [0u8; 0x1000];
if let Ok(n) = fh.read(&mut buffer) {
if n != 0 {
println!("Rest:\n{}", rhexdump::hexdump_offset(&buffer[..n], pos));
}
};
while let Ok(n) = fh.read(&mut buffer) {
if n == 0 {
break;
}
rest_size += n;
}
println!("+{rest_size} unparsed bytes");
Ok(ret)
}
fn load_ini(path: &PathBuf) -> IndexMap<String, IndexMap<String, Option<String>>> {
Ini::new().load(path).unwrap_or_default()
}
fn load_data(root: &Path, path: &Path) -> Result<Value> {
let full_path = &root.join(path);
let emi_path = full_path.join("map").join("map3d.emi");
let sm3_path = emi_path.with_extension("sm3");
let dum_path = emi_path.with_extension("dum");
let config_file = emi_path.with_extension("ini");
let moredummies = emi_path.with_file_name("moredummies").with_extension("ini");
let mut data = serde_json::to_value(HashMap::<(), ()>::default())?;
data["config"] = serde_json::to_value(load_ini(&config_file))?;
data["moredummies"] = serde_json::to_value(load_ini(&moredummies))?;
data["emi"] = serde_json::to_value(parse_file(&emi_path)?)?;
data["sm3"] = serde_json::to_value(parse_file(&sm3_path)?)?;
data["dummies"] = serde_json::to_value(parse_file(&dum_path)?)?;
data["path"] = serde_json::to_value(path)?;
data["root"] = serde_json::to_value(root)?;
Ok(data)
}
fn main() -> Result<()> {
let args = Args::try_parse()?;
let out_path = PathBuf::from(
args.path
.components()
.last()
.unwrap()
.as_os_str()
.to_string_lossy()
.into_owned(),
)
.with_extension("json.gz");
let full_path = &args.root.join(&args.path);
let data = if full_path.is_dir() {
load_data(&args.root, &args.path)?
} else {
serde_json::to_value(parse_file(full_path)?)?
};
let mut dumpfile = GzEncoder::new(File::create(&out_path)?, Compression::best());
serde_json::to_writer_pretty(&mut dumpfile, &data)?;
println!("Wrote {path}", path = out_path.display());
Ok(())
}