use std::io::{Cursor, Read, Seek, SeekFrom, Write};
use crate::{
get_bytes,
mxmd::{PackedExternalTexture, PackedExternalTextures, TextureUsage},
parse_count32_offset32, parse_opt_ptr32, parse_ptr32, until_eof,
xbc1::Xbc1,
xc3_write_binwrite_impl,
};
use bilge::prelude::*;
use binrw::{BinRead, BinResult, BinWrite, args, binread};
use xc3_write::{Xc3Write, Xc3WriteOffsets};
pub mod streaming;
#[binread]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, Xc3Write, PartialEq, Clone)]
#[br(magic(b"DRSM"))]
#[xc3(magic(b"DRSM"))]
pub struct Msrd {
pub version: u32,
#[br(parse_with = parse_data)]
#[xc3(offset(u32), align(16))]
pub data: Vec<u8>,
#[br(parse_with = parse_ptr32)]
#[xc3(offset(u32))]
pub streaming: Streaming,
}
#[binread]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
#[br(stream = r)]
#[xc3(base_offset)]
pub struct Streaming {
#[br(temp, try_calc = r.stream_position())]
base_offset: u64,
#[br(args_raw(base_offset))]
pub inner: StreamingInner,
}
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
#[br(import_raw(base_offset: u64))]
pub enum StreamingInner {
#[br(magic(0u32))]
#[xc3(magic(0u32))]
StreamingLegacy(#[br(args_raw(base_offset))] StreamingDataLegacy),
#[br(magic(4097u32))]
#[xc3(magic(4097u32))]
Streaming(#[br(args_raw(base_offset))] StreamingData),
}
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
#[br(import_raw(base_offset: u64))]
pub struct StreamingDataLegacy {
pub flags: StreamingFlagsLegacy,
#[br(args_raw(base_offset))]
pub inner: StreamingDataLegacyInner<TextureUsage>,
pub low_texture_data_offset: u32,
pub texture_data_offset: u32,
pub low_texture_data_uncompressed_size: u32,
pub texture_data_uncompressed_size: u32,
pub low_texture_data_compressed_size: u32,
pub texture_data_compressed_size: u32,
}
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
#[br(import_raw(base_offset: u64))]
pub struct StreamingDataLegacyInner<U>
where
U: Xc3Write + 'static,
for<'a> U: BinRead<Args<'a> = ()>,
for<'a> U::Offsets<'a>: Xc3WriteOffsets<Args = ()>,
{
#[br(parse_with = parse_ptr32, offset = base_offset)]
#[xc3(offset(u32))]
pub low_textures: PackedExternalTextures<U>,
#[br(parse_with = parse_opt_ptr32, offset = base_offset)]
#[xc3(offset(u32))]
pub textures: Option<PackedExternalTextures<U>>,
#[br(parse_with = parse_ptr32)]
#[br(args { offset: base_offset, inner: args! { count: low_textures.textures.len() }})]
#[xc3(offset(u32))]
pub low_texture_indices: Vec<u16>,
#[br(parse_with = parse_opt_ptr32)]
#[br(args {
offset: base_offset,
inner: args! { count: textures.as_ref().map(|t| t.textures.len()).unwrap_or_default() }
})]
#[xc3(offset(u32))]
pub texture_indices: Option<Vec<u16>>,
}
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, BinRead, BinWrite, Clone, Copy, PartialEq, Eq, Hash)]
#[brw(repr(u32))]
pub enum StreamingFlagsLegacy {
Uncompressed = 1,
Xbc1 = 2,
}
#[binread]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
#[br(import_raw(base_offset: u64))]
pub struct StreamingData {
pub flags: StreamFlags,
#[br(temp, restore_position)]
offset: (u32, u32),
#[br(parse_with = parse_count32_offset32, offset = base_offset)]
#[xc3(count_offset(u32, u32))]
pub stream_entries: Vec<StreamEntry>,
#[br(parse_with = parse_count32_offset32, offset = base_offset)]
#[xc3(count_offset(u32, u32))]
pub streams: Vec<Stream>,
pub vertex_data_entry_index: u32,
pub shader_entry_index: u32,
pub low_textures_entry_index: u32,
pub low_textures_stream_index: u32,
pub textures_stream_index: u32,
pub textures_stream_entry_start_index: u32,
pub textures_stream_entry_count: u32,
#[br(args { base_offset, size: offset.1 })]
pub texture_resources: TextureResources,
}
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, BinRead, Xc3Write, PartialEq, Clone)]
#[br(import { base_offset: u64, size: u32 })]
pub struct TextureResources {
#[br(parse_with = parse_count32_offset32, offset = base_offset)]
#[xc3(count_offset(u32, u32))]
pub texture_indices: Vec<u16>,
#[br(parse_with = parse_opt_ptr32, offset = base_offset)]
#[xc3(offset(u32), align(2))]
pub low_textures: Option<PackedExternalTextures<TextureUsage>>,
pub unk1: u32,
#[br(if(size == 92), args_raw(base_offset))]
pub chr_textures: Option<ChrTexTextures>,
pub unk: [u32; 2],
}
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
#[br(import_raw(base_offset: u64))]
pub struct ChrTexTextures {
#[br(parse_with = parse_count32_offset32, offset = base_offset)]
#[xc3(count_offset(u32, u32))]
pub chr_textures: Vec<ChrTexTexture>,
pub unk: [u32; 2],
}
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
pub struct ChrTexTexture {
pub hash: u32,
pub decompressed_size: u32,
pub compressed_size: u32,
pub base_mip_decompressed_size: u32,
pub base_mip_compressed_size: u32,
}
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, BinRead, BinWrite, PartialEq, Eq, Clone)]
pub struct StreamEntry {
pub offset: u32,
pub size: u32,
pub texture_base_mip_stream_index: u16,
pub entry_type: EntryType,
pub unk: [u32; 2],
}
#[bitsize(32)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(DebugBits, FromBits, BinRead, BinWrite, PartialEq, Eq, Clone, Copy)]
#[br(map = u32::into)]
#[bw(map = |&x| u32::from(x))]
pub struct StreamFlags {
pub has_vertex: bool,
pub has_spch: bool,
pub has_low_textures: bool,
pub has_textures: bool,
pub unk5: bool,
pub unk6: bool,
pub has_chr_textures: bool,
pub unk: u25,
}
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, BinRead, BinWrite, PartialEq, Eq, Clone, Copy)]
#[brw(repr(u16))]
pub enum EntryType {
Vertex = 0,
Shader = 1,
LowTextures = 2,
Texture = 3,
}
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
pub struct Stream {
pub compressed_size: u32,
pub decompressed_size: u32,
pub xbc1_offset: u32,
}
impl Stream {
pub fn read_xbc1(&self, data: &[u8], first_xbc1_offset: u32) -> binrw::BinResult<Xbc1> {
let start = self.xbc1_offset.saturating_sub(first_xbc1_offset);
let bytes = get_bytes(data, start, Some(self.compressed_size))?;
Xbc1::from_bytes(bytes)
}
}
impl StreamingInner {
pub fn has_chr_textures(&self) -> bool {
match self {
StreamingInner::StreamingLegacy(_) => false,
StreamingInner::Streaming(data) => data.texture_resources.chr_textures.is_some(),
}
}
}
fn parse_data<R>(reader: &mut R, endian: binrw::Endian, _args: ()) -> BinResult<Vec<u8>>
where
R: Read + Seek,
{
let offset = u32::read_options(reader, endian, ())?;
let saved_pos = reader.stream_position()?;
if offset == 0 {
return Err(binrw::Error::AssertFail {
pos: saved_pos,
message: "unexpected null offset".to_string(),
});
}
reader.seek(SeekFrom::Start(offset as u64 + 16))?;
let bytes = until_eof(reader, endian, ())?;
reader.seek(SeekFrom::Start(saved_pos))?;
Ok(bytes)
}
xc3_write_binwrite_impl!(StreamEntry, StreamFlags, StreamingFlagsLegacy);
impl Xc3WriteOffsets for MsrdOffsets<'_> {
type Args = ();
fn write_offsets<W: Write + Seek>(
&self,
writer: &mut W,
base_offset: u64,
data_ptr: &mut u64,
endian: xc3_write::Endian,
_args: Self::Args,
) -> xc3_write::Xc3Result<()> {
self.streaming
.write_full(writer, base_offset, data_ptr, endian, ())?;
self.data
.write_full(writer, base_offset + 16, data_ptr, endian, ())?;
Ok(())
}
}
impl Xc3WriteOffsets for TextureResourcesOffsets<'_> {
type Args = ();
fn write_offsets<W: Write + Seek>(
&self,
writer: &mut W,
base_offset: u64,
data_ptr: &mut u64,
endian: xc3_write::Endian,
_args: Self::Args,
) -> xc3_write::Xc3Result<()> {
self.chr_textures
.write_offsets(writer, base_offset, data_ptr, endian, ())?;
self.texture_indices
.write_full(writer, base_offset, data_ptr, endian, ())?;
self.low_textures
.write_full(writer, base_offset, data_ptr, endian, ())?;
Ok(())
}
}