use std::fs::File;
use std::io::{self, Read, Seek, SeekFrom, Write};
use crate::iso::boot_catalog::{BootCatalogEntry, write_boot_catalog};
use crate::iso::dir_record::IsoDirEntry;
use crate::iso::fs_node::{IsoDirectory, IsoFsNode};
use crate::iso::volume_descriptor::{update_total_sectors_in_pvd, write_volume_descriptors};
use crate::utils::{ISO_SECTOR_SIZE, seek_to_lba};
pub fn write_descriptors(
iso_file: &mut File,
volume_id: Option<&str>,
root_lba: u32,
total_sectors: u32,
) -> io::Result<()> {
let root_entry = IsoDirEntry {
lba: root_lba,
size: ISO_SECTOR_SIZE as u32,
flags: 0x02,
name: ".",
};
write_volume_descriptors(iso_file, volume_id, total_sectors, &root_entry)
}
pub fn write_boot_catalog_to_iso(
iso_file: &mut File,
boot_catalog_lba: u32,
boot_entries: Vec<BootCatalogEntry>,
) -> io::Result<()> {
if !boot_entries.is_empty() {
iso_file.seek(SeekFrom::Start(
(boot_catalog_lba as u64) * ISO_SECTOR_SIZE as u64,
))?;
write_boot_catalog(iso_file, boot_entries)?;
}
Ok(())
}
pub fn write_directories(
iso_file: &mut File,
dir: &IsoDirectory,
parent_lba: u32,
) -> io::Result<()> {
seek_to_lba(iso_file, dir.lba)?;
let mut dir_entries = Vec::new();
dir_entries.push(IsoDirEntry {
lba: dir.lba,
size: ISO_SECTOR_SIZE as u32,
flags: 0x02,
name: ".",
});
dir_entries.push(IsoDirEntry {
lba: parent_lba,
size: ISO_SECTOR_SIZE as u32,
flags: 0x02,
name: "..",
});
for_sorted_children!(dir, |name, node| {
let (lba, size, flags) = match node {
IsoFsNode::File(file) => {
let file_size_u32 = u32::try_from(file.size).map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidInput,
format!(
"File '{}' is too large for ISO9660 (exceeds u32::MAX bytes)",
name
),
)
})?;
(file.lba, file_size_u32, 0x00)
}
IsoFsNode::Directory(subdir) => (subdir.lba, ISO_SECTOR_SIZE as u32, 0x02),
};
dir_entries.push(IsoDirEntry {
lba,
size,
flags,
name: name.as_str(),
});
});
let mut dir_sector = [0u8; ISO_SECTOR_SIZE];
let mut offset = 0;
for entry in &dir_entries {
let entry_bytes = entry.to_bytes();
dir_sector[offset..offset + entry_bytes.len()].copy_from_slice(&entry_bytes);
offset += entry_bytes.len();
}
iso_file.write_all(&dir_sector)?;
for_sorted_children!(dir, |_name, node| {
if let IsoFsNode::Directory(subdir) = node {
write_directories(iso_file, subdir, dir.lba)?;
}
});
Ok(())
}
pub fn copy_files(iso_file: &mut File, dir: &IsoDirectory) -> io::Result<()> {
for_sorted_children!(dir, |_name, node| {
match node {
IsoFsNode::File(file) => {
seek_to_lba(iso_file, file.lba)?;
let mut real_file = File::open(&file.path)?;
io::copy(&mut real_file, iso_file)?;
}
IsoFsNode::Directory(subdir) => {
copy_files(iso_file, subdir)?;
}
}
});
Ok(())
}
pub fn finalize_iso(iso_file: &mut File, total_sectors: &mut u32) -> io::Result<()> {
let current_pos = iso_file.stream_position()?;
let remainder = current_pos % ISO_SECTOR_SIZE as u64;
if remainder != 0 {
let padding_bytes = ISO_SECTOR_SIZE as u64 - remainder;
io::copy(&mut io::repeat(0).take(padding_bytes), iso_file)?;
}
let final_pos = iso_file.stream_position()?;
let total_sectors_u64 = final_pos.div_ceil(ISO_SECTOR_SIZE as u64);
*total_sectors = u32::try_from(total_sectors_u64)
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "ISO image too large"))?;
update_total_sectors_in_pvd(iso_file, *total_sectors)?;
Ok(())
}