make the commandline application actually useful
parent
3329fde767
commit
5b3c8fcbe1
|
@ -18,6 +18,8 @@ failure = "0.1"
|
|||
generic-array = "0.12"
|
||||
#gtk = "0.4"
|
||||
memmap = "0.6"
|
||||
serde = {version = "1.0", features = ["derive"]}
|
||||
serde_yaml = "0.8"
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 1
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
//! Binary data conversion utilities.
|
||||
|
||||
use crate::durandal::err::*;
|
||||
use serde::Serialize;
|
||||
use std::{fmt, num::NonZeroU16, slice::SliceIndex};
|
||||
|
||||
/// Returns a byte array from `b` at `i`.
|
||||
|
@ -144,6 +145,7 @@ pub type Ident = [u8; 4];
|
|||
|
||||
/// An object identified by a `u16` which may be `u16::max_value()` to
|
||||
/// represent None.
|
||||
#[derive(Serialize)]
|
||||
pub struct ObjID(Option<NonZeroU16>);
|
||||
|
||||
// EOF
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
//! File utilities.
|
||||
|
||||
use crate::durandal::err::*;
|
||||
use std::fs;
|
||||
|
||||
pub fn validate_folder_path(p: &str) -> ResultS<()>
|
||||
{
|
||||
let at = fs::metadata(p)?;
|
||||
if !at.is_dir() {
|
||||
Err(err_msg("not a directory"))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// EOF
|
|
@ -1,6 +1,8 @@
|
|||
use serde::Serialize;
|
||||
use std::{fmt::{self, Write},
|
||||
ops};
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Fx32(i32);
|
||||
|
||||
impl Fx32
|
||||
|
|
|
@ -8,6 +8,7 @@ pub mod err;
|
|||
pub mod bin;
|
||||
pub mod chunk;
|
||||
pub mod crc;
|
||||
pub mod file;
|
||||
pub mod fx32;
|
||||
pub mod image;
|
||||
pub mod text;
|
||||
|
|
200
src/main.rs
200
src/main.rs
|
@ -1,98 +1,149 @@
|
|||
use maraiah::{durandal::{bin::*, chunk::*, err::*, image::*, text::*},
|
||||
use maraiah::{durandal::{bin::*, chunk::*, err::*, file::*, image::*, text::*},
|
||||
marathon::{machdr, map, pict, shp, term, wad}};
|
||||
use std::{fs,
|
||||
use std::{collections::HashSet,
|
||||
fs,
|
||||
io::{self, Write}};
|
||||
|
||||
fn write_chunk(cid: &Ident, cnk: &[u8], eid: u16) -> ResultS<()>
|
||||
fn make_tga(fname: &str, im: &impl Image) -> ResultS<()>
|
||||
{
|
||||
let fname = format!("out/{:04}{}.bin", eid, mac_roman_conv(cid));
|
||||
let out = fs::File::create(fname)?;
|
||||
let mut out = io::BufWriter::new(out);
|
||||
write_tga(&mut out, im)
|
||||
}
|
||||
|
||||
fn make_chunk(opt: &Options, cid: &Ident, cnk: &[u8], eid: u16) -> ResultS<()>
|
||||
{
|
||||
let cid = mac_roman_conv(cid);
|
||||
let fname = format!("{}/{:04}{}.bin", opt.out_dir, eid, cid);
|
||||
let out = fs::File::create(&fname)?;
|
||||
let mut out = io::BufWriter::new(out);
|
||||
out.write(cnk)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_chunk(cid: &Ident, cnk: &[u8], eid: u16) -> ResultS<()>
|
||||
fn make_yaml(data: &impl serde::Serialize) -> ResultS<()>
|
||||
{
|
||||
serde_yaml::to_writer(io::stdout(), &data)?;
|
||||
println!();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn dump_chunk(opt: &Options, cid: &Ident, cnk: &[u8], eid: u16) -> ResultS<()>
|
||||
{
|
||||
if opt.wad_all {
|
||||
make_chunk(opt, cid, cnk, eid)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
match cid {
|
||||
b"PICT" => {
|
||||
let im = pict::load_pict(cnk)?;
|
||||
eprintln!("entry {} has PICT {}x{}", eid, im.w(), im.h());
|
||||
let out = fs::File::create(&format!("out/{}.ppm", eid))?;
|
||||
let mut out = io::BufWriter::new(out);
|
||||
write_ppm(&mut out, &im)?;
|
||||
if opt.wad_chunks.contains(cid) {
|
||||
let im = pict::load_pict(cnk)?;
|
||||
make_tga(&format!("{}/pict_{}.tga", opt.out_dir, eid), &im)?;
|
||||
}
|
||||
}
|
||||
b"Minf" => {
|
||||
let minf = map::Minf::chunk(cnk)?;
|
||||
eprintln!("entry {} has {:#?}", eid, minf);
|
||||
if opt.wad_chunks.contains(cid) {
|
||||
make_yaml(&map::Minf::chunk(cnk)?)?;
|
||||
}
|
||||
}
|
||||
b"EPNT" => {
|
||||
let epnt = map::Endpoint::chunk(cnk)?;
|
||||
eprintln!("entry {} has EPNT {:#?}", eid, epnt);
|
||||
if opt.wad_chunks.contains(cid) {
|
||||
make_yaml(&map::Endpoint::chunk(cnk)?)?;
|
||||
}
|
||||
}
|
||||
b"PNTS" => {
|
||||
let epnt = map::Point::chunk(cnk)?;
|
||||
eprintln!("entry {} has PNTS {:#?}", eid, epnt);
|
||||
if opt.wad_chunks.contains(cid) {
|
||||
make_yaml(&map::Point::chunk(cnk)?)?;
|
||||
}
|
||||
}
|
||||
b"LINS" => {
|
||||
let line = map::Line::chunk(cnk)?;
|
||||
eprintln!("entry {} has LINS {:#?}", eid, line);
|
||||
if opt.wad_chunks.contains(cid) {
|
||||
make_yaml(&map::Line::chunk(cnk)?)?;
|
||||
}
|
||||
}
|
||||
b"SIDS" => {
|
||||
let line = map::Side::chunk(cnk)?;
|
||||
eprintln!("entry {} has SIDS {:#?}", eid, line);
|
||||
if opt.wad_chunks.contains(cid) {
|
||||
make_yaml(&map::Side::chunk(cnk)?)?;
|
||||
}
|
||||
}
|
||||
b"term" => {
|
||||
let term = term::Terminal::chunk(cnk)?;
|
||||
eprintln!("entry {} has term {:#?}", eid, term);
|
||||
if opt.wad_chunks.contains(cid) {
|
||||
make_yaml(&term::Terminal::chunk(cnk)?)?;
|
||||
}
|
||||
}
|
||||
cid => {
|
||||
write_chunk(cid, cnk, eid)?;
|
||||
if opt.wad_unknown {
|
||||
make_chunk(opt, cid, cnk, eid)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_wad(b: &[u8]) -> ResultS<()>
|
||||
fn process_wad(opt: &Options, b: &[u8]) -> ResultS<()>
|
||||
{
|
||||
let wad = wad::Wad::new(b)?;
|
||||
|
||||
eprintln!("{:#?}", wad);
|
||||
if opt.wad_header {
|
||||
make_yaml(&wad)?;
|
||||
}
|
||||
|
||||
for (eid, ent) in wad.entries {
|
||||
for (cid, cnk) in ent.chunks {
|
||||
read_chunk(&cid, cnk, eid)?;
|
||||
dump_chunk(opt, &cid, cnk, eid)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn dump_bitmaps(c: &shp::Collection, i: usize) -> ResultS<()>
|
||||
fn dump_bitmaps(opt: &Options, c: &shp::Collection, i: usize) -> ResultS<()>
|
||||
{
|
||||
if !opt.shp_bmp {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for (j, bmp) in c.bmps.iter().enumerate() {
|
||||
for (k, tab) in c.tabs.iter().enumerate() {
|
||||
let fname = format!("out/shape{}_{}_{}.tga", i, j, k);
|
||||
let out = fs::File::create(&fname)?;
|
||||
let mut out = io::BufWriter::new(out);
|
||||
write_tga(&mut out, &shp::ImageShp::new(bmp, &tab))?;
|
||||
if opt.shp_bmp_all {
|
||||
for (k, tab) in c.tabs.iter().enumerate() {
|
||||
let fname = format!("{}/shape{}_{}_{}.tga", opt.out_dir, i, j, k);
|
||||
make_tga(&fname, &shp::ImageShp::new(bmp, &tab))?;
|
||||
}
|
||||
} else {
|
||||
let fname = format!("{}/shape{}_{}.tga", opt.out_dir, i, j);
|
||||
make_tga(&fname, &shp::ImageShp::new(bmp, &c.tabs[0]))?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_shp(b: &[u8]) -> ResultS<()>
|
||||
fn write_shp_objs(opt: &Options, cl: &shp::Collection) -> ResultS<()>
|
||||
{
|
||||
if opt.shp_tab {
|
||||
make_yaml(&cl.tabs)?;
|
||||
}
|
||||
if opt.shp_frm {
|
||||
make_yaml(&cl.frms)?;
|
||||
}
|
||||
if opt.shp_seq {
|
||||
make_yaml(&cl.seqs)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_shp(opt: &Options, b: &[u8]) -> ResultS<()>
|
||||
{
|
||||
for (i, cl) in shp::read_shapes(b)?.iter().enumerate() {
|
||||
if let Some(cl) = &cl.0 {
|
||||
dump_bitmaps(cl, i)?;
|
||||
eprintln!("<{} lo> {:#?}\n{:#?}", i, cl.frms, cl.seqs);
|
||||
dump_bitmaps(opt, cl, i)?;
|
||||
write_shp_objs(opt, cl)?;
|
||||
}
|
||||
if let Some(cl) = &cl.1 {
|
||||
dump_bitmaps(cl, i + 100)?;
|
||||
eprintln!("<{} hi> {:#?}\n{:#?}", i, cl.frms, cl.seqs);
|
||||
dump_bitmaps(opt, cl, i + 100)?;
|
||||
write_shp_objs(opt, cl)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,7 +155,7 @@ fn main() -> ResultS<()>
|
|||
use argparse::*;
|
||||
use memmap::Mmap;
|
||||
|
||||
let mut args: Vec<String> = Vec::new();
|
||||
let mut opt: Options = Default::default();
|
||||
{
|
||||
let mut ap = ArgumentParser::new();
|
||||
ap.set_description(env!("CARGO_PKG_DESCRIPTION"));
|
||||
|
@ -113,12 +164,58 @@ fn main() -> ResultS<()>
|
|||
env!("CARGO_PKG_NAME"),
|
||||
env!("CARGO_PKG_VERSION"))),
|
||||
"Show the version");
|
||||
ap.refer(&mut args)
|
||||
.add_argument("inputs", List, "Input files");
|
||||
ap.refer(&mut opt.shp_tab)
|
||||
.add_option(&["--shp-write-tab"], StoreTrue,
|
||||
"shp: Dump all CLUTs as YAML to standard output");
|
||||
ap.refer(&mut opt.shp_bmp)
|
||||
.add_option(&["--shp-dump-bitmaps"], StoreTrue,
|
||||
"shp: Dump bitmaps into a folder");
|
||||
ap.refer(&mut opt.shp_bmp_all)
|
||||
.add_option(&["--shp-dump-more-bitmaps"], StoreTrue,
|
||||
"shp: Dump all color variations of each bitmap");
|
||||
ap.refer(&mut opt.shp_frm)
|
||||
.add_option(&["--shp-write-frm"], StoreTrue,
|
||||
"shp: Dump all frames as YAML to standard output");
|
||||
ap.refer(&mut opt.shp_seq)
|
||||
.add_option(&["--shp-write-seq"], StoreTrue,
|
||||
"shp: Dump all sequences as YAML to standard output");
|
||||
ap.refer(&mut opt.wad_all)
|
||||
.add_option(&["--wad-dump-all"], StoreTrue,
|
||||
"wad: Dump all chunks into a folder");
|
||||
ap.refer(&mut opt.wad_unknown)
|
||||
.add_option(&["--wad-dump-unknown"], StoreTrue,
|
||||
"wad: Dump all unknown chunks into a folder");
|
||||
ap.refer(&mut opt.wad_header)
|
||||
.add_option(&["--wad-write-header"], StoreTrue,
|
||||
"wad: Dump header info as YAML to standard output");
|
||||
ap.refer(&mut opt.wad_c_temp)
|
||||
.add_option(&["--wad-write-chunks"], Store,
|
||||
"wad: Dump specified chunks in various formats");
|
||||
ap.refer(&mut opt.out_dir)
|
||||
.add_option(&["--out-dir"], Store,
|
||||
"Sets output directory for dump options");
|
||||
ap.refer(&mut opt.inputs)
|
||||
.add_argument("inputs", Collect, "Input files");
|
||||
ap.parse_args_or_exit();
|
||||
}
|
||||
|
||||
for arg in &args {
|
||||
if opt.shp_bmp_all {
|
||||
opt.shp_bmp = true;
|
||||
}
|
||||
|
||||
if opt.out_dir.is_empty() {
|
||||
opt.out_dir = ".".to_string();
|
||||
}
|
||||
|
||||
if !opt.wad_c_temp.is_empty() {
|
||||
for ctyp in opt.wad_c_temp.split(',') {
|
||||
opt.wad_chunks.insert(c_iden(ctyp.as_bytes(), 0)?);
|
||||
}
|
||||
}
|
||||
|
||||
validate_folder_path(&opt.out_dir)?;
|
||||
|
||||
for arg in &opt.inputs {
|
||||
let (typ, fna) = if let Some(st) = arg.find(':') {
|
||||
arg.split_at(st + 1)
|
||||
} else {
|
||||
|
@ -130,8 +227,8 @@ fn main() -> ResultS<()>
|
|||
let b = c_data(&mm, machdr::try_mac_header(&mm)..)?;
|
||||
|
||||
match typ {
|
||||
"wad:" => process_wad(b),
|
||||
"shp:" => process_shp(b),
|
||||
"wad:" => process_wad(&opt, b),
|
||||
"shp:" => process_shp(&opt, b),
|
||||
_ => Err(err_msg("invalid file type specified on commandline")),
|
||||
}?;
|
||||
}
|
||||
|
@ -139,4 +236,21 @@ fn main() -> ResultS<()>
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Options
|
||||
{
|
||||
inputs: Vec<String>,
|
||||
out_dir: String,
|
||||
shp_tab: bool,
|
||||
shp_bmp: bool,
|
||||
shp_bmp_all: bool,
|
||||
shp_frm: bool,
|
||||
shp_seq: bool,
|
||||
wad_all: bool,
|
||||
wad_unknown: bool,
|
||||
wad_header: bool,
|
||||
wad_chunks: HashSet<Ident>,
|
||||
wad_c_temp: String,
|
||||
}
|
||||
|
||||
// EOF
|
||||
|
|
|
@ -2,6 +2,7 @@ use crate::{durandal::{bin::*, chunk::*, err::*, fx32::*,
|
|||
text::mac_roman_conv},
|
||||
marathon::xfer::TransferMode};
|
||||
use bitflags::bitflags;
|
||||
use serde::Serialize;
|
||||
use std::fmt;
|
||||
|
||||
impl Chunked<Point> for Point
|
||||
|
@ -118,13 +119,14 @@ impl Chunker<Minf> for Minf
|
|||
|
||||
type Unit = i16;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Point
|
||||
{
|
||||
x: Unit,
|
||||
y: Unit,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Endpoint
|
||||
{
|
||||
flags: EndpFlags,
|
||||
|
@ -134,7 +136,7 @@ pub struct Endpoint
|
|||
support: u16,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Line
|
||||
{
|
||||
flags: LineFlags,
|
||||
|
@ -149,14 +151,14 @@ pub struct Line
|
|||
poly_b: u16,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct SideTex
|
||||
{
|
||||
offs: Point,
|
||||
tex_id: ObjID,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Side
|
||||
{
|
||||
stype: u16,
|
||||
|
@ -176,7 +178,7 @@ pub struct Side
|
|||
shade: Fx32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Minf
|
||||
{
|
||||
env_code: u16,
|
||||
|
@ -189,6 +191,7 @@ pub struct Minf
|
|||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Serialize)]
|
||||
pub struct EndpFlags: u16
|
||||
{
|
||||
const Solid = 0x00_01;
|
||||
|
@ -198,6 +201,7 @@ bitflags! {
|
|||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Serialize)]
|
||||
pub struct LineFlags: u16
|
||||
{
|
||||
const TransSide = 0x02_00;
|
||||
|
@ -210,6 +214,7 @@ bitflags! {
|
|||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Serialize)]
|
||||
pub struct SideFlags: u16
|
||||
{
|
||||
const Status = 0x00_01;
|
||||
|
@ -224,6 +229,7 @@ bitflags! {
|
|||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Serialize)]
|
||||
pub struct EnvFlags: u16
|
||||
{
|
||||
const Vacuum = 0x00_01;
|
||||
|
@ -243,6 +249,7 @@ bitflags! {
|
|||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Serialize)]
|
||||
pub struct EntFlags: u32
|
||||
{
|
||||
const Solo = 0x00_01;
|
||||
|
@ -257,6 +264,7 @@ bitflags! {
|
|||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Serialize)]
|
||||
pub struct MsnFlags: u16
|
||||
{
|
||||
const Extermination = 0x00_01;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
use crate::{durandal::{bin::*, err::*, fx32::*, image::*, text::*},
|
||||
marathon::xfer::TransferMode};
|
||||
use bitflags::bitflags;
|
||||
use serde::Serialize;
|
||||
|
||||
fn color(b: &[u8]) -> ResultS<(usize, ColorShp)>
|
||||
{
|
||||
|
@ -313,7 +314,7 @@ impl Color for ColorShp
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub enum ColorShp
|
||||
{
|
||||
Translucent,
|
||||
|
@ -342,7 +343,7 @@ pub struct ImageShp<'a, 'b>
|
|||
clut: &'b [ColorShp],
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Frame
|
||||
{
|
||||
flags: FrameFlags,
|
||||
|
@ -356,7 +357,7 @@ pub struct Frame
|
|||
wrl_y: i16,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Sequence
|
||||
{
|
||||
name: String,
|
||||
|
@ -393,6 +394,7 @@ bitflags! {
|
|||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Serialize)]
|
||||
pub struct FrameFlags: u16
|
||||
{
|
||||
const Obscure = 0x20_00;
|
||||
|
@ -402,7 +404,7 @@ bitflags! {
|
|||
}
|
||||
|
||||
c_enum! {
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub enum CollectionType: u16
|
||||
{
|
||||
0 => Unused,
|
||||
|
@ -414,7 +416,7 @@ c_enum! {
|
|||
}
|
||||
|
||||
c_enum! {
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub enum ViewType: u16
|
||||
{
|
||||
1 => Anim,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::durandal::{bin::*, chunk::*, err::*, text::*};
|
||||
use serde::Serialize;
|
||||
use std::fmt;
|
||||
|
||||
fn read_group(b: &[u8], text: &[u8]) -> ResultS<Group>
|
||||
|
@ -78,7 +79,7 @@ impl Chunker<Vec<Terminal>> for Terminal
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Terminal
|
||||
{
|
||||
lines: u16,
|
||||
|
@ -86,7 +87,7 @@ pub struct Terminal
|
|||
faces: Vec<Face>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Face
|
||||
{
|
||||
start: usize,
|
||||
|
@ -94,6 +95,7 @@ pub struct Face
|
|||
color: u16,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Group
|
||||
{
|
||||
ttype: GroupType,
|
||||
|
@ -103,7 +105,7 @@ pub struct Group
|
|||
}
|
||||
|
||||
c_enum! {
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub enum GroupType: u16
|
||||
{
|
||||
0 => Logon,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
//! Marathon Wad format handling.
|
||||
|
||||
use crate::durandal::{bin::*, err::*, text::mac_roman_conv};
|
||||
use serde::Serialize;
|
||||
use std::{collections::BTreeMap, fmt};
|
||||
|
||||
impl Wad<'_>
|
||||
|
@ -85,18 +86,19 @@ pub struct Entry<'a>
|
|||
pub appdata: &'a [u8],
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Wad<'a>
|
||||
{
|
||||
wadver: Ver,
|
||||
dataver: u16,
|
||||
origname: String,
|
||||
appsize: usize,
|
||||
#[serde(skip)]
|
||||
pub entries: EntryMap<'a>,
|
||||
}
|
||||
|
||||
c_enum! {
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub enum Ver: u16
|
||||
{
|
||||
0 => Base,
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::durandal::err::*;
|
||||
use serde::Serialize;
|
||||
|
||||
c_enum! {
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub enum TransferMode: u16
|
||||
{
|
||||
0 => Normal,
|
||||
|
|
Loading…
Reference in New Issue