use std::io::{Cursor, SeekFrom};
use crate::{parse_count32_offset32, parse_offset32_count32, parse_opt_ptr32, parse_string_ptr32};
use binrw::{args, binread, BinRead, BinReaderExt, BinResult};
use xc3_write::{VecOffsets, Xc3Write, Xc3WriteOffsets};
#[binread]
#[derive(Debug, Xc3Write)]
#[br(magic(b"HCPS"))]
#[xc3(magic(b"HCPS"))]
#[br(stream = r)]
#[xc3(base_offset)]
pub struct Spch {
#[br(temp, try_calc = r.stream_position().map(|p| p - 4))]
base_offset: u64,
pub version: u32,
#[br(parse_with = parse_offset32_count32, offset = base_offset)]
#[xc3(offset_count(u32, u32))]
pub slct_offsets: Vec<SlctOffset>,
#[br(parse_with = parse_offset32_count32, offset = base_offset)]
#[xc3(offset_count(u32, u32))]
pub unk4s: Vec<Unk4>,
#[br(parse_with = parse_offset32_count32, offset = base_offset)]
#[xc3(offset_count(u32, u32))]
pub slct_section: Vec<u8>,
#[br(parse_with = parse_offset32_count32, offset = base_offset)]
#[xc3(offset_count(u32, u32), align(4096))]
pub xv4_section: Vec<u8>,
#[br(parse_with = parse_offset32_count32, offset = base_offset)]
#[xc3(offset_count(u32, u32), align(8))]
pub unk_section: Vec<u8>,
#[br(parse_with = parse_opt_ptr32)]
#[br(args {
offset: base_offset,
inner: args! { base_offset, count: slct_offsets.len()
}})]
#[xc3(offset(u32))]
pub string_section: Option<StringSection>,
pub unk7: u32,
pub padding: [u32; 4],
}
#[derive(Debug, BinRead)]
#[br(import { base_offset: u64, count: usize })]
pub struct StringSection {
#[br(args { count, inner: base_offset})]
pub program_names: Vec<StringOffset>,
}
#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets)]
#[br(import_raw(base_offset: u64))]
pub struct StringOffset {
#[br(parse_with = parse_string_ptr32, offset = base_offset)]
#[xc3(offset(u32))]
pub name: String,
}
#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets)]
pub struct SlctOffset {
pub offset: u32,
pub unk1: u32,
}
#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets)]
pub struct Unk4 {
pub unk1: u32,
pub unk2: u32,
pub unk3: u32,
}
#[binread]
#[derive(Debug)]
#[br(magic(b"SLCT"))]
#[br(stream = r)]
pub struct Slct {
pub unk1: u32,
#[br(parse_with = parse_count32_offset32)]
pub unk_strings: Vec<UnkString>,
#[br(parse_with = parse_count32_offset32)]
pub programs: Vec<ShaderProgram>,
pub unk5_count: u32,
pub unk5_offset: u32,
pub unk_offset: u32,
pub unk_offset1: u32,
pub unk_item_offset: u32,
pub unk_item_total_size: u32,
pub xv4_offset: u32,
pub xv4_total_size: u32,
pub unks1: [u32; 4],
}
#[derive(BinRead, Debug)]
pub struct UnkString {
pub unk1: u32,
pub unk2: u32,
#[br(parse_with = parse_string_ptr32)]
pub text: String,
}
#[derive(BinRead, Debug)]
pub struct ShaderProgram {
#[br(parse_with = parse_offset32_count32)]
pub program_data: Vec<u8>,
}
#[derive(Debug, BinRead, Default)]
pub struct Nvsd {
pub unks2: [u32; 6],
#[br(parse_with = parse_offset32_count32)]
pub nvsd_shaders: Vec<NvsdShaders>,
pub buffers1_count: u16,
pub buffers1_index_count: u16,
#[br(parse_with = parse_opt_ptr32)]
#[br(args { inner: args! { count: buffers1_count as usize } })]
pub uniform_buffers: Option<Vec<UniformBuffer>>,
#[br(parse_with = parse_opt_ptr32)]
#[br(args { inner: args! { count: buffers1_index_count as usize } })]
pub buffers1_indices: Option<Vec<i8>>,
pub buffers2_count: u16,
pub buffers2_index_count: u16,
#[br(parse_with = parse_opt_ptr32)]
#[br(args { inner: args! { count: buffers2_count as usize } })]
pub storage_buffers: Option<Vec<UniformBuffer>>,
#[br(parse_with = parse_opt_ptr32)]
#[br(args { inner: args! { count: buffers2_index_count as usize } })]
pub buffers2_indices: Option<Vec<i8>>,
pub sampler_count: u16,
pub sampler_index_count: u16,
#[br(parse_with = parse_opt_ptr32)]
#[br(args { inner: args! { count: sampler_count as usize } })]
pub samplers: Option<Vec<Sampler>>,
#[br(parse_with = parse_opt_ptr32)]
#[br(args { inner: args! { count: sampler_index_count as usize } })]
pub samplers_indices: Option<Vec<i8>>,
pub unks2_1: [u32; 3],
#[br(parse_with = parse_count32_offset32)]
pub attributes: Vec<InputAttribute>,
#[br(parse_with = parse_count32_offset32)]
pub uniforms: Vec<Uniform>,
pub unk3_1: u32,
pub unk3_2: u32,
pub xv4_total_size: u32,
pub unk_item_total_size: u32,
}
#[derive(BinRead, Debug)]
pub struct UnkItem {
pub unk1: u32,
pub unk2: u32,
pub unk3: u32,
pub unk4: u32,
pub unk5: u32,
pub assembly_code_string_offset: u32,
pub assembly_code_string_length: u32,
pub unk8: u32,
pub unk9: u32,
#[br(seek_before = SeekFrom::Start(3968))]
pub const_buffer_offset: u32,
pub shader_size: u32,
}
#[derive(BinRead, Debug)]
pub struct NvsdShaders {
pub unk6: u32, pub vertex_xv4_size: u32,
pub fragment_xv4_size: u32,
pub vertex_unk_item_size: u32,
pub fragment_unk_item_size: u32,
}
#[derive(BinRead, Debug)]
pub struct UniformBuffer {
#[br(parse_with = parse_string_ptr32)]
pub name: String,
pub uniform_count: u16,
pub uniform_start_index: u16,
pub unk3: u32,
pub handle: Handle, pub unk5: u16, }
#[derive(BinRead, Debug)]
pub struct Handle {
pub handle: u8,
pub visibility: Visibility,
}
#[derive(BinRead, Debug)]
#[br(repr(u8))]
pub enum Visibility {
Fragment = 1,
VertexFragment = 2,
}
#[derive(BinRead, Debug)]
pub struct Sampler {
#[br(parse_with = parse_string_ptr32)]
pub name: String,
pub unk1: u32,
pub handle: Handle, pub unk: u16, }
#[derive(BinRead, Debug, Clone)]
pub struct Uniform {
#[br(parse_with = parse_string_ptr32)]
pub name: String,
pub buffer_offset: u32,
}
#[derive(BinRead, Debug)]
pub struct InputAttribute {
#[br(parse_with = parse_string_ptr32)]
pub name: String,
pub location: u32,
}
#[derive(Debug, BinRead, Default)]
pub struct Nvsp {
#[br(parse_with = parse_offset32_count32)]
pub shader_source: Vec<u8>,
pub decompressed_size: u32,
pub vertex_offset: u32,
pub vertex_length: u32,
pub fragment_offset: u32,
pub fragment_length: u32,
pub unk: [u32; 10],
}
impl SlctOffset {
pub fn read_slct(&self, slct_section: &[u8]) -> BinResult<Slct> {
let bytes = &slct_section[self.offset as usize..];
let mut reader = Cursor::new(bytes);
reader.read_le()
}
}
impl Slct {
pub fn read_unk_item(&self, unk_section: &[u8]) -> BinResult<UnkItem> {
let bytes = &unk_section[self.unk_item_offset as usize..];
let mut reader = Cursor::new(bytes);
reader.read_le()
}
}
impl ShaderProgram {
pub fn read_nvsd(&self) -> BinResult<Nvsd> {
let mut reader = Cursor::new(&self.program_data);
reader.read_le()
}
pub fn read_nvsp(&self) -> BinResult<Nvsp> {
let mut reader = Cursor::new(&self.program_data);
reader.read_le()
}
}
impl Nvsd {
fn vertex_binary<'a>(&self, offset: usize, xv4_section: &'a [u8]) -> Option<&'a [u8]> {
let shaders = self.nvsd_shaders.last()?;
if shaders.vertex_xv4_size > 0 {
xv4_section.get(offset..offset + shaders.vertex_xv4_size as usize)
} else {
None
}
}
fn fragment_binary<'a>(&self, offset: usize, xv4_section: &'a [u8]) -> Option<&'a [u8]> {
let shaders = &self.nvsd_shaders.last()?;
if shaders.fragment_xv4_size > 0 {
xv4_section.get(offset..offset + shaders.fragment_xv4_size as usize)
} else {
None
}
}
}
impl Nvsp {
pub fn vertex_fragment_source(&self) -> Option<(String, String)> {
let decompressed =
lzf::decompress(&self.shader_source, self.decompressed_size as usize).ok()?;
let vertex = String::from_utf8(
decompressed
.get(
self.vertex_offset as usize
..self.vertex_offset as usize + self.vertex_length as usize,
)?
.to_vec(),
)
.unwrap();
let fragment = String::from_utf8(
decompressed
.get(
self.fragment_offset as usize
..self.fragment_offset as usize + self.fragment_length as usize,
)?
.to_vec(),
)
.unwrap();
Some((vertex, fragment))
}
}
pub fn vertex_fragment_binaries<'a>(
nvsds: &[Nvsd],
xv4_section: &'a [u8],
xv4_offset: u32,
) -> Vec<(Option<&'a [u8]>, Option<&'a [u8]>)> {
let mut offset = xv4_offset as usize;
nvsds
.iter()
.map(|nvsd| {
let vertex = nvsd.vertex_binary(offset, xv4_section);
offset += vertex.map(|v| v.len()).unwrap_or_default();
let fragment = nvsd.fragment_binary(offset, xv4_section);
offset += fragment.map(|f| f.len()).unwrap_or_default();
(vertex, fragment)
})
.collect()
}
impl Xc3Write for StringSection {
type Offsets<'a> = VecOffsets<StringOffsetOffsets<'a>>;
fn xc3_write<W: std::io::Write + std::io::Seek>(
&self,
writer: &mut W,
data_ptr: &mut u64,
) -> xc3_write::Xc3Result<Self::Offsets<'_>> {
self.program_names.xc3_write(writer, data_ptr)
}
}
impl<'a> Xc3WriteOffsets for SpchOffsets<'a> {
fn write_offsets<W: std::io::Write + std::io::Seek>(
&self,
writer: &mut W,
_base_offset: u64,
data_ptr: &mut u64,
) -> xc3_write::Xc3Result<()> {
let base_offset = self.base_offset;
self.slct_offsets
.write_full(writer, base_offset, data_ptr)?;
self.unk4s.write_full(writer, base_offset, data_ptr)?;
self.string_section
.write_full(writer, base_offset, data_ptr)?;
self.slct_section
.write_full(writer, base_offset, data_ptr)?;
self.unk_section.write_full(writer, base_offset, data_ptr)?;
self.xv4_section.write_full(writer, base_offset, data_ptr)?;
Ok(())
}
}