aboutsummaryrefslogtreecommitdiffstats
path: root/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs96
1 files changed, 57 insertions, 39 deletions
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()