diff options
-rw-r--r-- | src/cubase_project.rs | 40 | ||||
-rw-r--r-- | src/main.rs | 96 |
2 files changed, 97 insertions, 39 deletions
diff --git a/src/cubase_project.rs b/src/cubase_project.rs new file mode 100644 index 0000000..49c73a7 --- /dev/null +++ b/src/cubase_project.rs @@ -0,0 +1,40 @@ +/* cbconvert -- A program to parse and convert Cubase projects + * Copyright (C) 2020 <name of copyright holder> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * A struct to hold the information about a Cubase project. + */ +#[derive(Debug, Default)] +pub struct CubaseProject { + pub app_version: PAppVersion, +} + +/* + * Version information about the app that created the file. + */ +#[derive(Debug, Default)] +pub struct PAppVersion { + pub appname: String, + pub appversion: String, + pub appdate: String, + pub num2: u32, + pub apparch: String, + pub num3: u32, + pub appencoding: String, + pub applocale: String, +} + diff --git a/src/main.rs b/src/main.rs index 9b9c12f..414f1c0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,13 +15,18 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +mod cubase_project; + +use cubase_project::*; + use nom::{ branch::alt, bytes::complete::*, - multi::{count, length_data, many0}, + multi::{count, length_data, length_value, many0}, number::complete::*, sequence::tuple, Finish, + InputTake, IResult, }; use std::{ @@ -313,21 +318,53 @@ fn chunk<'a>(data: &'a [u8]) -> IResult<&'a [u8], ()> { Ok((rest, ())) } +struct RiffChunk<'a> { + fourcc: &'a str, + payload: &'a [u8], +} + +fn riff_chunk<'a>(i: &'a [u8]) -> IResult<&'a [u8], RiffChunk> { + let (r, (fourcc, payload)) = tuple((fourcc, length_data(be_u32)))(i)?; + Ok((r, RiffChunk { fourcc, payload })) +} + + /** - * Parse the Cubase project file header. + * Split file into individual chunks. + * + * A Cubase project file is a RIFF file. + * + * That is almost. There's a strange extra tag "NUND" between the root (RIFF) header and the + * rest of the chunks. This would have been ok, if the NUND tag was followed by a length field, + * as it would just be another chunk. This is not the case however, the tag is followed + * immefiately by another chunk header. * - * A Cubase project file is almost a standard RIFF file, with the exception of an - * extra fourcc (NUND) following the global RIFF header. The extra (NUND) fourcc - * is _not_ followed by the length of the chunk, but rather immediately followed by - * the next chunk. + * To make it even worse, the chunk size of the root chunk is four bytes short, so this makes + * the file parser itself a bit more complex than what it needs to be. */ -fn file_hdr<'a>(data: &'a [u8]) -> IResult<&'a [u8], ()> { - let (rest, (_, len, _)) = tuple((tag("RIFF"), be_u32, tag("NUND")))(data)?; - println!("RIFF: {}, NUND", len); - Ok((rest, ())) +fn split_chunks<'a>(data: &'a [u8]) -> IResult<&'a [u8], Vec<RiffChunk<'a>>> { + let (r, (_, (_, chunks))) = + tuple(( + tag(b"RIFF"), + length_value( + payload_len, + tuple((tag(b"NUND"), many0(riff_chunk))) + ) + ))(&data)?; + Ok((r, chunks)) } -pub struct CubaseProject; +/** + * Get the correct payload length from the root chunk of the file. + * + * Cubase incorrectly specifies the chunk size four bytes less than what it really is, probably due + * to the strange inserted "NUND" tag that don't really belong anywhere. This parser fixes that by + * adding four to the size read from the file. + */ +fn payload_len<'a>(data: &'a [u8]) -> IResult<&'a [u8], u32> { + let (r, len) = be_u32(data)?; + Ok((r, len + 4)) +} pub fn parse_cubase_project<P>(filename: P) -> Result<CubaseProject, Box<dyn Error>> where @@ -336,41 +373,22 @@ where println!("Reading {}...", filename.as_ref().to_str().ok_or("Invalid file name")?); let data = std::fs::read(filename)?; - let (rest, _) = tuple((file_hdr, many0(chunk)))(&data) + let (rest, chunks) = split_chunks(&data) .finish() .map_err(|e| format!("{:?}", e))?; println!("Rest: {}", rest.len()); - // let mut rest = chunk.payload; - - // while dbg!(rest.len()) > 0 { - // let (r, chunk) = parse_chunk(&rest).unwrap(); - // println!(" Chunk id : {}", chunk.tag); - // println!(" Chunk data size : {}/{} bytes", chunk.len, chunk.payload.len()); - - // match chunk.tag { - // "NUNDROOT" | "ROOT" => { - // let (_r, hm) = parse_nundroot(&chunk.payload).unwrap(); - // println!(" data : {:?}", hm); - // }, - // "ARCH" => { - // let (_r, n) = node(&chunk.payload).unwrap(); - // println!(" {}: {{", n.nodeclass); - // println!(" {:#?}", n.value); - // println!(" }},"); - // }, - // _ => (), - // }; - - // rest = r; - // std::thread::sleep(std::time::Duration::from_millis(200)); - // } - - Ok(CubaseProject {}) + let proj = CubaseProject::default(); + + for chunk in chunks { + println!("{}: {}", chunk.fourcc, chunk.payload.len()); + } + + Ok(proj) } -fn main() -> std::result::Result<(), Box<dyn std::error::Error>> { +fn main() -> Result<(), Box<dyn Error>> { let filename = std::env::args() .skip(1) .next() |