aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/cubase_project.rs7
-rw-r--r--src/main.rs204
2 files changed, 111 insertions, 100 deletions
diff --git a/src/cubase_project.rs b/src/cubase_project.rs
index 498936d..b58bbc9 100644
--- a/src/cubase_project.rs
+++ b/src/cubase_project.rs
@@ -18,15 +18,16 @@
/**
* A struct to hold the information about a Cubase project.
*/
-#[derive(Debug, Default)]
+#[derive(Clone, Debug, Default)]
pub struct CubaseProject {
pub app_version: PAppVersion,
+ pub arrangement: PArrangement,
}
/*
* Version information about the app that created the file.
*/
-#[derive(Debug, Default)]
+#[derive(Clone, Debug, Default)]
pub struct PAppVersion {
pub appname: String,
pub appversion: String,
@@ -38,6 +39,6 @@ pub struct PAppVersion {
pub applocale: String,
}
-#[derive(Debug, Default)]
+#[derive(Clone, Debug, Default)]
pub struct PArrangement {
}
diff --git a/src/main.rs b/src/main.rs
index a561af4..b4c6bbf 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -22,17 +22,16 @@ use cubase_project::*;
use nom::{
branch::alt,
bytes::complete::*,
- combinator::{map, rest, value},
- error::ParseError,
- multi::{length_data, length_value, many1},
+ combinator::{map},
+ multi::{fold_many0, length_data, length_value},
number::complete::*,
sequence::{preceded, terminated, tuple},
Finish,
IResult,
- Parser,
};
use std::{
error::Error,
+ fmt,
path::Path,
};
@@ -48,18 +47,6 @@ fn cmstring(input: &[u8]) -> IResult<&[u8], &str> {
map(strdata, |s| std::str::from_utf8(s).unwrap())(input)
}
-fn cmtag<'a>(t: &'a str) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], (NodeType, u16)> {
- map(
- tuple((
- node_type,
- length_value(
- be_u32, tuple((tag(t), tag(b"\0")))
- ),
- be_u16)
- ),
- |(nt, (_, _), v)| (nt, v))
-}
-
/**
* The data chunks in the file is split into what seems like a structure of containers and leaf
* nodes. Where the containers don't contain any data of themselves, but leaf nodes may contain
@@ -68,22 +55,13 @@ fn cmtag<'a>(t: &'a str) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], (NodeType,
* Each node regardless of type has a name, and a 16 bit number which meaning I'm not sure about.
* Leaf nodes also has a data payload prefixed by a 32 bit length (be).
*/
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, PartialEq)]
enum NodeType {
Container,
Object,
}
-fn node_type<'a>(input: &'a [u8]) -> IResult<&'a [u8], NodeType> {
- alt((
- value(NodeType::Container, tag(b"\xff\xff\xff\xfe")),
- value(NodeType::Object, tag(b"\xff\xff\xff\xff"))
- ))(input)
-}
-
-
-#[derive(Debug)]
struct Node<'a> {
node_type: NodeType,
name: &'a str,
@@ -91,6 +69,22 @@ struct Node<'a> {
payload: Option<&'a [u8]>,
}
+impl<'a> fmt::Debug for Node<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut r = f.debug_struct("Node");
+
+ r.field("node_type", &self.node_type)
+ .field("name", &self.name)
+ .field("num", &self.num);
+
+ if let Some(p) = self.payload {
+ r.field("payload_size", &p.len());
+ }
+
+ r.finish()
+ }
+}
+
fn p_app_version<'a>(input: &'a [u8]) -> IResult<&'a [u8], PAppVersion> {
let mut v = PAppVersion::default();
@@ -116,9 +110,16 @@ fn p_app_version<'a>(input: &'a [u8]) -> IResult<&'a [u8], PAppVersion> {
}
fn p_arrangement<'a>(input: &'a [u8]) -> IResult<&'a [u8], PArrangement> {
- let v = PArrangement::default();
- Ok((input, v))
+ fold_many0(
+ node,
+ PArrangement::default(),
+ |a: PArrangement, n: Node<'_>| {
+ println!(" {:?}", n);
+ a
+ }
+ )(input)
+
}
/**
@@ -133,16 +134,25 @@ fn root_chunk<'a>(input: &'a [u8]) -> IResult<&'a [u8], (&str, &str)> {
length_value(be_u32, tuple((cmstring, cmstring))))(input)
}
-fn arch_chunk<'a, O, E, F>(subparser: F) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], O, E>
-where
- E: ParseError<&'a [u8]>,
- F: Parser<&'a [u8], O, E>,
-{
+// fn arch_chunk<'a, O, E, F>(subparser: F) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], O, E>
+// where
+// E: ParseError<&'a [u8]>,
+// F: Parser<&'a [u8], O, E>,
+// {
+// preceded(
+// tag(b"ARCH"),
+// length_value(be_u32, subparser))
+// }
+
+
+fn arch_chunk<'a>(input: &'a [u8]) -> IResult<&'a [u8], &'a [u8]> {
preceded(
tag(b"ARCH"),
- length_value(be_u32, subparser))
+ length_data(be_u32)
+ )(input)
}
+
/**
* A container node does not contain any data of it's own, but contains
* one or more sub elements. These can be either other containers, or
@@ -163,40 +173,50 @@ fn object_node<'a>(data: &'a [u8]) -> IResult<&'a [u8], Node> {
Ok((r, Node { node_type: NodeType::Object, name, num, payload: Some(payload) } ))
}
+fn node<'a>(data: &'a [u8]) -> IResult<&'a [u8], Node> {
+ alt((container_node, object_node))(data)
+}
-fn cpr_file<'a>(input: &'a [u8]) -> IResult<&'a [u8], CubaseProject> {
- let mut proj = CubaseProject::default();
-
- let (mut payload, len) = cpr_file_header(input)?;
- assert_eq!(payload.len(), len as usize);
-
- println!("[*] Reading Cubase CPR file, {} bytes of data...", payload.len());
-
- while payload.len() > 0 {
- // Expect a root chunk first:
- let (r, (k, t)) = root_chunk(payload)?;
- println!("[*] Found root: ({}, {})", k, t);
-
- match k {
- "Version" => {
- let (r2, v) = arch_chunk(version_chunk)(r)?;
- println!("[*] {:?}", v);
- proj.app_version = v;
- payload = r2;
- },
- "Arrangement1" => {
- let (r2, a) = arch_chunk(arrangement_chunk)(r)?;
- println!("[*] {:?}", a);
- payload = r2;
- },
- _ => {
- let (r2, _) = arch_chunk(rest)(r)?;
- payload = r2;
+
+fn version_chunk<'a>(input: &'a [u8]) -> IResult<&'a [u8], PAppVersion> {
+ fold_many0(
+ node,
+ PAppVersion::default(),
+ |mut version, n: Node<'_>| {
+ println!(" {:?}", n);
+ if n.node_type == NodeType::Object {
+ let (_, v) = p_app_version(n.payload.unwrap()).unwrap();
+ version = v;
}
+ version
}
- }
+ )(input)
+ // preceded(
+ // container_node,
+ // p_app_version_node
+ // )(input)
+}
- Ok((payload, proj))
+fn arrangement_chunk<'a>(input: &'a [u8]) -> IResult<&'a [u8], PArrangement> {
+ fold_many0(
+ node,
+ PArrangement::default(),
+ |a: PArrangement, n: Node<'_>| {
+ println!(" {:?}", n);
+ if n.node_type == NodeType::Object {
+ let _ = p_arrangement(n.payload.unwrap());
+ }
+ a
+ }
+ )(input)
+}
+
+
+/**
+ * Main parser for a Cubase Project (.cpr) file.
+ */
+fn cpr_file<'a>(input: &'a [u8]) -> IResult<&'a [u8], CubaseProject> {
+ preceded(cpr_file_header, cpr_file_body)(input)
}
/**
@@ -216,41 +236,31 @@ fn cpr_file<'a>(input: &'a [u8]) -> IResult<&'a [u8], CubaseProject> {
*/
fn cpr_file_header<'a>(input: &'a [u8]) -> IResult<&'a [u8], u32> {
terminated(
- preceded(
- tag(b"RIFF"), be_u32),
- tag(b"NUND"))(input)
-}
-
-
-fn version_chunk<'a>(input: &'a [u8]) -> IResult<&'a [u8], PAppVersion> {
- preceded(
- tuple((cmtag("CmObject"), cmtag("PAppVersion"))),
- length_value(be_u32, p_app_version))(input)
+ preceded(tag(b"RIFF"), be_u32),
+ tag(b"NUND")
+ )(input)
}
-
-fn arrangement_chunk<'a>(input: &'a [u8]) -> IResult<&'a [u8], PArrangement> {
- // It varies a bit how many levels deep the actual PArrangement object is.
- //
- // File created with Cubase 4.5.x (file version 310?) seems to have this
- // structure:
- //
- // GDocument -> GModel -> CmObject
- //
- // While Cubase version 5.x (file version 400?) seems to have the CmObject
- // directly under the GDocument container.
- let (odata, c) = many1(container_node)(input)?;
- println!("[*] {:?}", c);
-
- // This is the actual PArrangeent object
- let (r2, o) = object_node(odata)?;
- println!("[*] {:?}: {} ({} bytes)", o.node_type, o.name, o.payload.unwrap().len());
- assert_eq!(r2.len(), 0);
-
- let (r2, _v) = p_arrangement(o.payload.unwrap())?;
- // assert_eq!(r2.len(), 0);
-
- Ok((r2, PArrangement::default()))
+fn cpr_file_body<'a>(input: &'a [u8]) -> IResult<&'a [u8], CubaseProject> {
+ fold_many0(
+ tuple((root_chunk, arch_chunk)),
+ CubaseProject::default(),
+ |mut prj, ((key, value), arch)| {
+ println!("Root: {} = {}", key, value);
+ match key {
+ "Version" => {
+ let (_, v) = version_chunk(arch).unwrap();
+ prj.app_version = v;
+ },
+ "Arrangement1" => {
+ let (_, a) = arrangement_chunk(arch).unwrap();
+ prj.arrangement = a;
+ }
+ _ => {},
+ }
+ prj
+ }
+ )(input)
}
pub fn parse_cubase_project<P>(filename: P) -> Result<CubaseProject, Box<dyn Error>>