use crate::gm_enum::GMEnum;
use crate::gm_enum::gm_enum;
use crate::prelude::*;
use crate::util::init::vec_with_capacity;
use crate::wad::Blob;
use crate::wad::build::builder::DataBuilder;
use crate::wad::chunk::gm_named_list_chunk;
use crate::wad::elem::GMElement;
use crate::wad::parse::reader::DataReader;
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Shaders {
pub elems: Vec<Option<Shader>>,
pub exists: bool,
}
gm_named_list_chunk!(SHDR, Shaders, Shader, nullable);
impl GMElement for Shaders {
#[expect(clippy::too_many_lines)]
fn deserialize(reader: &mut DataReader) -> Result<Self> {
let count = reader.read_u32()?;
let mut locations: Vec<u32> = vec_with_capacity(count + 1)?;
let mut real_count = count;
for _ in 0..count {
let pointer = reader.read_u32()?;
if pointer == 0 {
real_count -= 1;
} else {
locations.push(pointer);
}
}
locations.push(reader.chunk.end_pos);
let mut elems: Vec<Option<Shader>> = vec![None; real_count as usize];
for i in 0..real_count as usize {
let pointer = locations[i];
let entry_end = locations[i + 1];
reader.cur_pos = pointer;
let name: GMRef<String> = reader.read_gm_string()?;
let shader_type: Type = Type::from_i32(reader.read_i32()? & 0x7FFF_FFFF)?;
let glsl_es_vertex: GMRef<String> = reader.read_gm_string()?;
let glsl_es_fragment: GMRef<String> = reader.read_gm_string()?;
let glsl_vertex: GMRef<String> = reader.read_gm_string()?;
let glsl_fragment: GMRef<String> = reader.read_gm_string()?;
let hlsl9_vertex: GMRef<String> = reader.read_gm_string()?;
let hlsl9_fragment: GMRef<String> = reader.read_gm_string()?;
let hlsl11_vertex_ptr = reader.read_u32()?;
let hlsl11_pixel_ptr = reader.read_u32()?;
let vertex_shader_attributes: Vec<GMRef<String>> = reader.read_simple_list()?;
let mut version: i32 = 2;
let mut pssl_vertex_ptr = 0;
let mut pssl_vertex_len = 0;
let mut pssl_pixel_ptr = 0;
let mut pssl_pixel_len = 0;
let mut cg_psvita_vertex_ptr = 0;
let mut cg_psvita_vertex_len = 0;
let mut cg_psvita_pixel_ptr = 0;
let mut cg_psvita_pixel_len = 0;
let mut cg_ps3_vertex_ptr = 0;
let mut cg_ps3_vertex_len = 0;
let mut cg_ps3_pixel_ptr = 0;
let mut cg_ps3_pixel_len = 0;
if reader.general_info.wad_version > 13 {
version = reader.read_i32()?;
pssl_vertex_ptr = reader.read_u32()?;
pssl_vertex_len = reader.read_u32()?;
pssl_pixel_ptr = reader.read_u32()?;
pssl_pixel_len = reader.read_u32()?;
cg_psvita_vertex_ptr = reader.read_u32()?;
cg_psvita_vertex_len = reader.read_u32()?;
cg_psvita_pixel_ptr = reader.read_u32()?;
cg_psvita_pixel_len = reader.read_u32()?;
if version >= 2 {
cg_ps3_vertex_ptr = reader.read_u32()?;
cg_ps3_vertex_len = reader.read_u32()?;
cg_ps3_pixel_ptr = reader.read_u32()?;
cg_ps3_pixel_len = reader.read_u32()?;
}
}
let hlsl11_vertex_data: ShaderData =
read_shader_data(reader, entry_end, 8, hlsl11_vertex_ptr, 0, hlsl11_pixel_ptr)?;
let hlsl11_pixel_data: ShaderData =
read_shader_data(reader, entry_end, 8, hlsl11_pixel_ptr, 0, pssl_vertex_ptr)?;
let pssl_vertex_data: ShaderData = read_shader_data(
reader,
entry_end,
8,
pssl_vertex_ptr,
pssl_vertex_len,
pssl_pixel_ptr,
)?;
let pssl_pixel_data: ShaderData = read_shader_data(
reader,
entry_end,
8,
pssl_pixel_ptr,
pssl_pixel_len,
cg_psvita_vertex_ptr,
)?;
let cg_psvita_vertex_data: ShaderData = read_shader_data(
reader,
entry_end,
8,
cg_psvita_vertex_ptr,
cg_psvita_vertex_len,
cg_psvita_pixel_ptr,
)?;
let cg_psvita_pixel_data: ShaderData = read_shader_data(
reader,
entry_end,
8,
cg_psvita_pixel_ptr,
cg_psvita_pixel_len,
cg_ps3_vertex_ptr,
)?;
let cg_ps3_vertex_data: ShaderData = read_shader_data(
reader,
entry_end,
16,
cg_ps3_vertex_ptr,
cg_ps3_vertex_len,
cg_ps3_pixel_ptr,
)?;
let cg_ps3_pixel_data: ShaderData =
read_shader_data(reader, entry_end, 16, cg_ps3_pixel_ptr, cg_ps3_pixel_len, 0)?;
elems[i] = Some(Shader {
name,
shader_type,
glsl_es_vertex,
glsl_es_fragment,
glsl_vertex,
glsl_fragment,
hlsl9_vertex,
hlsl9_fragment,
version,
hlsl11_vertex_data,
hlsl11_pixel_data,
pssl_vertex_data,
pssl_pixel_data,
cg_psvita_vertex_data,
cg_psvita_pixel_data,
cg_ps3_vertex_data,
cg_ps3_pixel_data,
vertex_shader_attributes,
});
}
Ok(Self { elems, exists: true })
}
fn serialize(&self, builder: &mut DataBuilder) -> Result<()> {
builder.write_pointer_list_opt(&self.elems)?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Shader {
pub name: GMRef<String>,
pub shader_type: Type,
pub glsl_es_vertex: GMRef<String>,
pub glsl_es_fragment: GMRef<String>,
pub glsl_vertex: GMRef<String>,
pub glsl_fragment: GMRef<String>,
pub hlsl9_vertex: GMRef<String>,
pub hlsl9_fragment: GMRef<String>,
pub version: i32,
pub hlsl11_vertex_data: ShaderData,
pub hlsl11_pixel_data: ShaderData,
pub pssl_vertex_data: ShaderData,
pub pssl_pixel_data: ShaderData,
pub cg_psvita_vertex_data: ShaderData,
pub cg_psvita_pixel_data: ShaderData,
pub cg_ps3_vertex_data: ShaderData,
pub cg_ps3_pixel_data: ShaderData,
pub vertex_shader_attributes: Vec<GMRef<String>>,
}
impl GMElement for Shader {
fn deserialize(_: &mut DataReader) -> Result<Self> {
unimplemented!("GMShader::deserialize is not supported; use GMShaders::deserialize instead")
}
fn serialize(&self, builder: &mut DataBuilder) -> Result<()> {
builder.write_gm_string(self.name)?;
builder.write_u32(self.shader_type.as_i32() as u32 | 0x8000_0000);
builder.write_gm_string(self.glsl_es_vertex)?;
builder.write_gm_string(self.glsl_es_fragment)?;
builder.write_gm_string(self.glsl_vertex)?;
builder.write_gm_string(self.glsl_fragment)?;
builder.write_gm_string(self.hlsl9_vertex)?;
builder.write_gm_string(self.hlsl9_fragment)?;
builder.write_pointer_opt(&self.hlsl11_vertex_data);
builder.write_pointer_opt(&self.hlsl11_pixel_data);
builder.write_simple_list(&self.vertex_shader_attributes)?;
if builder.wad_version() > 13 {
builder.write_i32(self.version);
builder.write_pointer_opt(&self.pssl_vertex_data);
builder.write_usize(self.pssl_vertex_data.as_ref().map_or(0, |x| x.len()))?;
builder.write_pointer_opt(&self.pssl_pixel_data);
builder.write_usize(self.pssl_pixel_data.as_ref().map_or(0, |x| x.len()))?;
builder.write_pointer_opt(&self.cg_psvita_vertex_data);
builder.write_usize(self.cg_psvita_vertex_data.as_ref().map_or(0, |x| x.len()))?;
builder.write_pointer_opt(&self.cg_psvita_pixel_data);
builder.write_usize(self.cg_psvita_pixel_data.as_ref().map_or(0, |x| x.len()))?;
if self.version >= 2 {
builder.write_pointer_opt(&self.cg_ps3_vertex_data);
builder.write_usize(self.cg_ps3_vertex_data.as_ref().map_or(0, |x| x.len()))?;
builder.write_pointer_opt(&self.cg_ps3_pixel_data);
builder.write_usize(self.cg_ps3_pixel_data.as_ref().map_or(0, |x| x.len()))?;
}
}
write_shader_data(builder, 8, &self.hlsl11_vertex_data)?;
write_shader_data(builder, 8, &self.hlsl11_pixel_data)?;
write_shader_data(builder, 8, &self.pssl_vertex_data)?;
write_shader_data(builder, 8, &self.pssl_pixel_data)?;
write_shader_data(builder, 8, &self.cg_psvita_vertex_data)?;
write_shader_data(builder, 8, &self.cg_psvita_pixel_data)?;
write_shader_data(builder, 16, &self.cg_ps3_vertex_data)?;
write_shader_data(builder, 16, &self.cg_ps3_pixel_data)?;
Ok(())
}
}
gm_enum!(
Type {
GlslEs = 1,
Glsl = 2,
Hlsl9 = 3,
Hlsl11 = 4,
Pssl = 5,
CgPsVita = 6,
CgPs3 = 7,
});
type ShaderData = Option<Blob<Vec<u8>>>;
fn read_shader_data(
reader: &mut DataReader,
entry_end: u32,
pad: u32,
this_pointer: u32,
expected_length: u32,
next_ptr: u32,
) -> Result<ShaderData> {
const ERR_PREFIX: &str = "Failed to compute length of shader data: instructed to read";
const ERR_SUFFIX: &str =
"Shader data was the last in the shader, but given length was incorrectly padded";
if this_pointer == 0 {
return Ok(None);
}
reader.align(pad)?;
let next = if next_ptr == 0 { entry_end } else { next_ptr };
let actual_length = next - reader.cur_pos;
let is_last: bool = next_ptr == 0;
if expected_length == 0 {
let data: Vec<u8> = reader.read_bytes_dyn(actual_length)?.to_vec();
return Ok(Some(Blob(data)));
}
if expected_length > actual_length {
bail!("{ERR_PREFIX} less data than expected");
}
if expected_length < actual_length {
if is_last && (reader.cur_pos + actual_length).is_multiple_of(16) {
} else if !is_last && (reader.cur_pos + actual_length).is_multiple_of(8) {
} else if is_last {
bail!("{ERR_PREFIX} more data than expected. {ERR_SUFFIX}");
} else {
bail!("{ERR_PREFIX} more data than expected");
}
}
let data: Vec<u8> = reader.read_bytes_dyn(expected_length)?.to_vec();
Ok(Some(Blob(data)))
}
fn write_shader_data(builder: &mut DataBuilder, pad: u32, shader_data: &ShaderData) -> Result<()> {
if let Some(data) = &shader_data {
builder.align(pad);
builder.resolve_pointer(data)?;
builder.write_bytes(data);
}
Ok(())
}