1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use crate::error::{LoftyError, Result};
use crate::ogg::constants::VORBIS_SETUP_HEAD;

use std::fs::File;
use std::io::{Cursor, Read, Seek, SeekFrom, Write};

use byteorder::{LittleEndian, ReadBytesExt};
use ogg_pager::Page;

pub(crate) fn write_to(
	data: &mut File,
	writer: &mut Vec<u8>,
	first_md_content: Vec<u8>,
	ser: u32,
	pages: &mut [Page],
) -> Result<()> {
	let mut remaining = Vec::new();

	let reached_md_end: bool;

	// Find the total comment count in the first page's content
	let mut c = Cursor::new(first_md_content);

	// Skip the header
	c.seek(SeekFrom::Start(7))?;

	// Skip the vendor
	let vendor_len = c.read_u32::<LittleEndian>()?;
	c.seek(SeekFrom::Current(i64::from(vendor_len)))?;

	let total_comments = c.read_u32::<LittleEndian>()?;
	let comments_pos = c.seek(SeekFrom::Current(0))?;

	c.seek(SeekFrom::End(0))?;

	loop {
		let p = Page::read(data, false)?;

		if p.header_type != 1 {
			data.seek(SeekFrom::Start(p.start as u64))?;
			data.read_to_end(&mut remaining)?;

			reached_md_end = true;
			break;
		}

		c.write_all(&p.content)?;
	}

	if !reached_md_end {
		return Err(LoftyError::Vorbis("File ends with comment header"));
	}

	c.seek(SeekFrom::Start(comments_pos))?;

	for _ in 0..total_comments {
		let len = c.read_u32::<LittleEndian>()?;
		c.seek(SeekFrom::Current(i64::from(len)))?;
	}

	if c.read_u8()? != 1 {
		return Err(LoftyError::Vorbis("File is missing a framing bit"));
	}

	// Comments should be followed by the setup header
	let mut header_ident = [0; 7];
	c.read_exact(&mut header_ident)?;

	if header_ident != VORBIS_SETUP_HEAD {
		return Err(LoftyError::Vorbis("File is missing setup header"));
	}

	c.seek(SeekFrom::Current(-7))?;

	let mut setup = Vec::new();
	c.read_to_end(&mut setup)?;

	let pages_len = pages.len() - 1;

	for (i, mut p) in pages.iter_mut().enumerate() {
		p.serial = ser;

		if i == pages_len {
			// Add back the framing bit
			p.content.push(1);

			// The segment tables of current page and the setup header have to be combined
			let mut seg_table = Vec::new();
			seg_table.extend(p.segments().iter());
			seg_table.extend(ogg_pager::segments(&*setup));

			let mut seg_table_len = seg_table.len();

			if seg_table_len > 255 {
				seg_table = seg_table.split_at(255).0.to_vec();
				seg_table_len = 255;
			}

			seg_table.insert(0, seg_table_len as u8);

			let page = p.extend(&*setup);

			let mut p_bytes = p.as_bytes();
			let seg_count = p_bytes[26] as usize;

			// Replace segment table and checksum
			p_bytes.splice(26..27 + seg_count, seg_table);
			p_bytes.splice(22..26, ogg_pager::crc32(&*p_bytes).to_le_bytes().to_vec());

			writer.write_all(&*p_bytes)?;

			if let Some(mut page) = page {
				page.serial = ser;
				page.gen_crc();

				writer.write_all(&*page.as_bytes())?;
			}

			break;
		}

		p.gen_crc();
		writer.write_all(&*p.as_bytes())?;
	}

	writer.write_all(&*remaining)?;

	Ok(())
}