use crate::cdrom::{CD_FRAME_SIZE, CD_MAX_SECTOR_DATA, CD_MAX_SUBCODE_DATA, CD_SYNC_HEADER};
use crate::compression::ecc::ErrorCorrectedSector;
use crate::compression::lzma::LzmaCodec;
use crate::compression::zlib::ZlibCodec;
use crate::compression::{
CodecImplementation, CompressionCodec, CompressionCodecType, DecompressResult,
};
use crate::error::{Error, Result};
use crate::header::CodecType;
use std::convert::TryFrom;
pub type CdLzmaCodec = CdCodec<LzmaCodec, ZlibCodec>;
pub type CdZlibCodec = CdCodec<ZlibCodec, ZlibCodec>;
impl CompressionCodecType for CdLzmaCodec {
fn codec_type(&self) -> CodecType {
CodecType::LzmaCdV5
}
}
impl CompressionCodecType for CdZlibCodec {
fn codec_type(&self) -> CodecType {
CodecType::ZLibCdV5
}
}
impl CompressionCodec for CdZlibCodec {}
impl CompressionCodec for CdLzmaCodec {}
pub struct CdCodec<Engine: CodecImplementation, SubEngine: CodecImplementation> {
engine: Engine,
sub_engine: SubEngine,
buffer: Vec<u8>,
}
impl<Engine: CodecImplementation, SubEngine: CodecImplementation> CodecImplementation
for CdCodec<Engine, SubEngine>
{
fn new(hunk_size: u32) -> Result<Self> {
if hunk_size % CD_FRAME_SIZE != 0 {
return Err(Error::CodecError);
}
let buffer = vec![0u8; hunk_size as usize];
Ok(CdCodec {
engine: Engine::new((hunk_size / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA)?,
sub_engine: SubEngine::new((hunk_size / CD_FRAME_SIZE) * CD_MAX_SUBCODE_DATA)?,
buffer,
})
}
fn decompress(&mut self, input: &[u8], output: &mut [u8]) -> Result<DecompressResult> {
let frames = output.len() / CD_FRAME_SIZE as usize;
let complen_bytes = if output.len() < 65536 { 2 } else { 3 };
let ecc_bytes = (frames + 7) / 8;
let header_bytes = ecc_bytes + complen_bytes;
#[allow(clippy::identity_op)]
let mut sector_compressed_len: u32 =
(input[ecc_bytes + 0] as u32) << 8 | input[ecc_bytes + 1] as u32;
if complen_bytes > 2 {
sector_compressed_len = sector_compressed_len << 8 | input[ecc_bytes + 2] as u32;
}
let frame_res = self.engine.decompress(
&input[header_bytes..][..sector_compressed_len as usize],
&mut self.buffer[..frames * CD_MAX_SECTOR_DATA as usize],
)?;
#[cfg(feature = "want_subcode")]
let sub_res = self.sub_engine.decompress(
&input[header_bytes + sector_compressed_len as usize..],
&mut self.buffer[frames * CD_MAX_SECTOR_DATA as usize..]
[..frames * CD_MAX_SUBCODE_DATA as usize],
)?;
#[cfg(not(feature = "want_subcode"))]
let sub_res = DecompressResult::default();
for (frame_num, chunk) in self.buffer[..frames * CD_MAX_SECTOR_DATA as usize]
.chunks_exact(CD_MAX_SECTOR_DATA as usize)
.enumerate()
{
output[frame_num * CD_FRAME_SIZE as usize..][..CD_MAX_SECTOR_DATA as usize]
.copy_from_slice(chunk);
}
#[cfg(feature = "want_subcode")]
for (frame_num, chunk) in self.buffer[frames * CD_MAX_SECTOR_DATA as usize..]
.chunks_exact(CD_MAX_SUBCODE_DATA as usize)
.enumerate()
{
output[frame_num * CD_FRAME_SIZE as usize + CD_MAX_SECTOR_DATA as usize..]
[..CD_MAX_SUBCODE_DATA as usize]
.copy_from_slice(chunk);
}
#[cfg(feature = "want_raw_data_sector")]
for frame_num in 0..frames {
let mut sector = <&mut [u8; CD_MAX_SECTOR_DATA as usize]>::try_from(
&mut output[frame_num * CD_FRAME_SIZE as usize..][..CD_MAX_SECTOR_DATA as usize],
)?;
if (input[frame_num / 8] & (1 << (frame_num % 8))) != 0 {
sector[0..12].copy_from_slice(&CD_SYNC_HEADER);
sector.generate_ecc();
}
}
Ok(frame_res + sub_res)
}
}