Skip to content

Commit

Permalink
feat: add serialization logic
Browse files Browse the repository at this point in the history
  • Loading branch information
justjanne committed Feb 25, 2024
1 parent 3c8336f commit a03bd59
Show file tree
Hide file tree
Showing 24 changed files with 588 additions and 92 deletions.
4 changes: 0 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@
This project contains a fully fledged parser for Brother's FCM format,
written in Rust using Nom.

## Roadmap

- Writing FCM files

## License

> This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
Expand Down
Binary file added samples/test/square_roundtrip.fcm
Binary file not shown.
33 changes: 33 additions & 0 deletions src/alignment_data.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use crate::encode::Encode;
use crate::point;
use crate::point::Point;
use crate::util::bool32;
use nom::combinator::map;
use nom::multi::length_count;
use nom::number::complete::le_u32;
use nom::sequence::tuple;
use nom::IResult;

#[derive(Debug)]
pub struct AlignmentData {
pub needed: bool,
pub marks: Vec<Point>,
}

pub(crate) fn read_alignment_data(input: &[u8]) -> IResult<&[u8], AlignmentData> {
map(
tuple((bool32, length_count(le_u32, point::read_point))),
|(needed, marks)| AlignmentData { needed, marks },
)(input)
}

impl Encode for AlignmentData {
fn encode(&self, buffer: &mut Vec<u8>) -> std::io::Result<()> {
(self.needed as u32).encode(buffer)?;
(self.marks.len() as u32).encode(buffer)?;
for mark in &self.marks {
mark.encode(buffer)?;
}
Ok(())
}
}
43 changes: 22 additions & 21 deletions src/cut_data.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
use nom::combinator::{cond, flat_map, map};
use nom::multi::length_count;
use nom::number::complete::le_u32;
use nom::sequence::tuple;
use nom::IResult;

use crate::alignment_data::{read_alignment_data, AlignmentData};
use crate::encode::Encode;
use crate::file_type;
use crate::file_type::FileType;
use crate::point::Point;
use crate::util::bool32;
use crate::{file_type, point};

#[derive(Debug)]
pub struct CutData {
Expand All @@ -16,8 +15,7 @@ pub struct CutData {
pub cut_width: u32,
pub cut_height: u32,
pub seam_allowance_width: u32,
pub align_needed: Option<bool>,
pub align_marks: Option<Vec<Point>>,
pub alignment: Option<AlignmentData>,
}

pub(crate) fn read_cut_data(input: &[u8]) -> IResult<&[u8], CutData> {
Expand All @@ -28,28 +26,31 @@ pub(crate) fn read_cut_data(input: &[u8]) -> IResult<&[u8], CutData> {
le_u32,
le_u32,
le_u32,
cond(file_type == FileType::PrintAndCut, bool32),
cond(
file_type == FileType::PrintAndCut,
length_count(le_u32, point::read_point),
),
cond(file_type == FileType::PrintAndCut, read_alignment_data),
)),
move |(
mat_id,
cut_width,
cut_height,
seam_allowance_width,
align_needed,
align_marks,
)| CutData {
move |(mat_id, cut_width, cut_height, seam_allowance_width, alignment)| CutData {
file_type,
mat_id,
cut_width,
cut_height,
seam_allowance_width,
align_needed,
align_marks,
alignment,
},
)
})(input)
}

impl Encode for CutData {
fn encode(&self, buffer: &mut Vec<u8>) -> std::io::Result<()> {
self.file_type.encode(buffer)?;
self.mat_id.encode(buffer)?;
self.cut_width.encode(buffer)?;
self.cut_height.encode(buffer)?;
self.seam_allowance_width.encode(buffer)?;
if let Some(alignment) = &self.alignment {
alignment.encode(buffer)?;
}

Ok(())
}
}
96 changes: 96 additions & 0 deletions src/encode.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use std::io;
use std::io::Write;

pub(crate) trait Encode {
fn encode(&self, buffer: &mut Vec<u8>) -> io::Result<()>;

fn encode_to_vec(&self) -> io::Result<Vec<u8>> {
let mut buffer = Vec::new();
self.encode(&mut buffer)?;
Ok(buffer)
}
}

impl Encode for u8 {
fn encode(&self, buffer: &mut Vec<u8>) -> io::Result<()> {
buffer.write(&self.to_le_bytes())?;
Ok(())
}
}

impl Encode for u16 {
fn encode(&self, buffer: &mut Vec<u8>) -> io::Result<()> {
buffer.write(&self.to_le_bytes())?;
Ok(())
}
}

impl Encode for u32 {
fn encode(&self, buffer: &mut Vec<u8>) -> io::Result<()> {
buffer.write(&self.to_le_bytes())?;
Ok(())
}
}

impl Encode for u64 {
fn encode(&self, buffer: &mut Vec<u8>) -> io::Result<()> {
buffer.write(&self.to_le_bytes())?;
Ok(())
}
}

impl Encode for u128 {
fn encode(&self, buffer: &mut Vec<u8>) -> io::Result<()> {
buffer.write(&self.to_le_bytes())?;
Ok(())
}
}

impl Encode for i8 {
fn encode(&self, buffer: &mut Vec<u8>) -> io::Result<()> {
buffer.write(&self.to_le_bytes())?;
Ok(())
}
}

impl Encode for i16 {
fn encode(&self, buffer: &mut Vec<u8>) -> io::Result<()> {
buffer.write(&self.to_le_bytes())?;
Ok(())
}
}

impl Encode for i32 {
fn encode(&self, buffer: &mut Vec<u8>) -> io::Result<()> {
buffer.write(&self.to_le_bytes())?;
Ok(())
}
}

impl Encode for i64 {
fn encode(&self, buffer: &mut Vec<u8>) -> io::Result<()> {
buffer.write(&self.to_le_bytes())?;
Ok(())
}
}

impl Encode for i128 {
fn encode(&self, buffer: &mut Vec<u8>) -> io::Result<()> {
buffer.write(&self.to_le_bytes())?;
Ok(())
}
}

impl Encode for f32 {
fn encode(&self, buffer: &mut Vec<u8>) -> io::Result<()> {
buffer.write(&self.to_le_bytes())?;
Ok(())
}
}

impl Encode for f64 {
fn encode(&self, buffer: &mut Vec<u8>) -> io::Result<()> {
buffer.write(&self.to_le_bytes())?;
Ok(())
}
}
8 changes: 7 additions & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::fmt::{Display, Formatter};
use std::fmt::{Debug, Display, Formatter};

pub struct Error {
pub(crate) message: String,
Expand All @@ -9,3 +9,9 @@ impl Display for Error {
write!(f, "{0}", self.message)
}
}

impl Debug for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{0}", self.message)
}
}
39 changes: 29 additions & 10 deletions src/fcm_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ use nom::sequence::tuple;
use nom::IResult;

use crate::cut_data::CutData;
use crate::encode::Encode;
use crate::error::Error;
use crate::file_header::FileHeader;
use crate::piece::Piece;
use crate::{cut_data, file_header, piece};
use crate::piece_table::PieceTable;
use crate::{cut_data, file_header, piece_table};

#[derive(Debug)]
pub struct FcmFile {
pub file_header: FileHeader,
pub cut_data: CutData,
pub pieces: Vec<(u16, Piece)>,
pub piece_table: PieceTable,
}

impl FcmFile {
Expand All @@ -29,10 +30,19 @@ impl FcmFile {
let data = fs::read(file.as_ref()).map_err(|e| Error {
message: format!("Could not open file: {0}", e),
})?;
let (_, file) = read_fcm_file(data.as_slice()).map_err(|e| Error {
message: format!("Could not parse file: {0}", e),
})?;
Ok(file)
FcmFile::from_bytes(data.as_slice())
}

pub fn to_bytes(&self) -> Result<Vec<u8>, Error> {
self.encode_to_vec().map_err(|e| Error {
message: format!("Could not serialize file: {0}", e),
})
}

pub fn to_file<T: AsRef<std::path::Path>>(&self, file: T) -> Result<(), Error> {
fs::write(file, self.to_bytes()?.as_slice()).map_err(|e| Error {
message: format!("Could not write to file: {0}", e),
})
}
}

Expand All @@ -41,12 +51,21 @@ pub(crate) fn read_fcm_file(input: &[u8]) -> IResult<&[u8], FcmFile> {
tuple((
file_header::read_file_header,
cut_data::read_cut_data,
piece::read_piece_table,
piece_table::read_piece_table,
)),
|(file_header, cut_data, pieces)| FcmFile {
|(file_header, cut_data, piece_table)| FcmFile {
file_header,
cut_data,
pieces,
piece_table,
},
)(input)
}

impl Encode for FcmFile {
fn encode(&self, buffer: &mut Vec<u8>) -> std::io::Result<()> {
self.file_header.encode(buffer)?;
self.cut_data.encode(buffer)?;
self.piece_table.encode(buffer)?;
Ok(())
}
}
43 changes: 39 additions & 4 deletions src/file_header.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
use std::io::Write;

use nom::combinator::{map, opt};
use nom::multi::{length_data, length_value};
use nom::number::complete::{le_u32, le_u8};
use nom::sequence::tuple;
use nom::IResult;

use crate::file_variant::Variant;
use crate::encode::Encode;
use crate::file_variant::FileVariant;
use crate::generator::Generator;
use crate::util::{bool32, read_length_utf16, read_tag, read_utf8_until_null};
use crate::{file_variant, generator};
use crate::{file_variant, generator, util};

#[derive(Debug)]
pub struct FileHeader {
pub variant: Variant,
pub variant: FileVariant,
pub version: String,
pub content_id: u32,
pub short_name: String,
Expand Down Expand Up @@ -65,7 +68,7 @@ pub(crate) fn read_file_header(input: &[u8]) -> IResult<&[u8], FileHeader> {
),
)| FileHeader {
variant,
version: version,
version,
content_id,
short_name,
long_name,
Expand All @@ -79,3 +82,35 @@ pub(crate) fn read_file_header(input: &[u8]) -> IResult<&[u8], FileHeader> {
},
)(input)
}

impl Encode for FileHeader {
fn encode(&self, buffer: &mut Vec<u8>) -> std::io::Result<()> {
self.variant.encode(buffer)?;
buffer.write(&self.version.as_bytes()[0..4])?;
self.content_id.encode(buffer)?;

let mut variable_header: Vec<u8> = vec![];

util::write_utf8_fixed(&self.short_name, &mut variable_header)?;
util::write_utf16_str(&self.long_name, &mut variable_header)?;
util::write_utf16_str(&self.author_name, &mut variable_header)?;
util::write_utf16_str(&self.copyright, &mut variable_header)?;

self.thumbnail_block_size_width
.encode(&mut variable_header)?;
self.thumbnail_block_size_height
.encode(&mut variable_header)?;
(self.thumbnail.len() as u32).encode(&mut variable_header)?;
variable_header.write(&self.thumbnail)?;

self.generator.encode(&mut variable_header)?;
if let Some(print_to_cut) = &self.print_to_cut {
(*print_to_cut as u32).encode(&mut variable_header)?;
}

(variable_header.len() as u32).encode(buffer)?;
buffer.write(&variable_header)?;

Ok(())
}
}
14 changes: 14 additions & 0 deletions src/file_type.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use std::io::Write;

use nom::combinator::map_res;
use nom::number::complete::le_u32;
use nom::IResult;

use crate::encode::Encode;

#[derive(Debug, PartialEq, Copy, Clone)]
pub enum FileType {
Cut,
Expand All @@ -15,3 +19,13 @@ pub(crate) fn read_file_type(input: &[u8]) -> IResult<&[u8], FileType> {
_ => Err(format!("Unable to parse file type: {data}")),
})(input)
}

impl Encode for FileType {
fn encode(&self, buffer: &mut Vec<u8>) -> std::io::Result<()> {
match self {
FileType::Cut => buffer.write(&0x10u32.to_le_bytes())?,
FileType::PrintAndCut => buffer.write(&0x38u32.to_le_bytes())?,
};
Ok(())
}
}
Loading

0 comments on commit a03bd59

Please sign in to comment.