aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Eilertsen <haraldei@anduin.net>2020-11-25 23:01:29 +0100
committerHarald Eilertsen <haraldei@anduin.net>2020-11-26 14:49:09 +0100
commit76705a7ac6d404a9db709d45190767b3e8be24c9 (patch)
tree2c652db8e906c92c02a52ff13bda5df566dc8086
parent7d2fb776b77e10e64f5fdbf8d36676f13f1832d0 (diff)
downloadcbconv-76705a7ac6d404a9db709d45190767b3e8be24c9.tar.gz
cbconv-76705a7ac6d404a9db709d45190767b3e8be24c9.tar.bz2
cbconv-76705a7ac6d404a9db709d45190767b3e8be24c9.zip
A somewhat new start.
Split the file into chunks (with their payload) to begin with. This way it will be easier to parse each chunk separately. At least that's the idea. Let's see how it goes.
-rw-r--r--src/cubase_project.rs40
-rw-r--r--src/main.rs96
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()