goldforge 0.9.0

Library for handling file formats used by GoldSrc and related engines.
Documentation
// Copyright 2025-2026 Gabriel Bjørnager Jensen.
//
// This Source Code Form is subject to the terms of
// the Mozilla Public License, v. 2.0. If a copy of
// the MPL was not distributed with this file, you
// can obtain one at:
// <https://mozilla.org/MPL/2.0/>.

//! [`Builder::with_lump`].

use super::Inner;

use crate::wad::{
	BuildError,
	Builder,
	LumpDescriptor,
	LumpType,
};
use crate::wad::ffi::{doom, halflife, quake};

use oct::IntoOctets;

impl Builder {
	/// Constructs a lump and adds it to the directory.
	///
	/// The lump is given the higher priority amongst
	/// those with the same name. Thus, a successive
	/// call to [`flatten`] will only keep this one.
	///
	/// [`flatten`]: Self::flatten
	///
	/// # Errors
	///
	/// If the provided name is longer than `8` (DOOM)
	/// or `16` (WAD2 and WAD3) characters or contains
	/// any null characters, this method will yield an
	/// [`Err`] instance.
	///
	/// If either the length of the provided data or
	/// the index of the lump cannot be represented by
	/// [`i32`], this method will yield an [`Err`]
	/// instance.
	pub fn with_lump<'a>(&mut self, desc: LumpDescriptor<'a>) -> Result<(), BuildError<'a>> {
		// Do validations that are needed for all WAD ver-
		// sions first.

		let filepos = size_of::<doom::wadinfo_t>() + self.data.len();

		let Ok(filepos) = i32::try_from(filepos) else {
			return Err(BuildError::large_size(filepos));
		};

		let Ok(disksize) = i32::try_from(desc.data.len()) else {
			return Err(BuildError::large_size(desc.data.len()));
		};

		if desc.name.contains('\0') {
			return Err(BuildError::null_lump_name(desc.name));
		}

		if let Inner::Wad { ref mut directory, .. } = self.inner {
			// Do validations that are only needed for WAD.

			if desc.name.len() > 8 {
				return Err(BuildError::long_lump_name(desc.name));
			}

			// Approved!

			let mut raw_name = [0_i8; _];
			raw_name.as_octets_mut()[..desc.name.len()].copy_from_slice(desc.name.as_bytes());

			let lump = doom::filelump_t {
				filepos,
				size:    disksize,
				name:    raw_name,
			};

			directory.push(lump);
		} else {
			// Do validations that are needed for both WAD2 and
			// WAD3.

			if desc.name.len() > 16 {
				return Err(BuildError::long_lump_name(desc.name));
			}

			let mut raw_name = [0_i8; _];
			raw_name.as_octets_mut()[..desc.name.len()].copy_from_slice(desc.name.as_bytes());

			let size = disksize;

			let r#type = desc.lump_type.map(LumpType::to_i8)
				.unwrap_or_default();

			let compression = quake::CMP_NONE;

			// Approved!

			match self.inner {
				Inner::Wad2 { ref mut directory, .. } => {
					let lump = quake::lumpinfo_t {
						filepos,
						disksize,
						size,
						r#type,
						compression,
						name:        raw_name,

						..Default::default()
					};

					directory.push(lump);
				}

				Inner::Wad3 { ref mut directory, .. } => {
					let lump = halflife::lumpinfo_t {
						filepos,
						disksize,
						size,
						r#type,
						compression,
						name:        raw_name,

						..Default::default()
					};

					directory.push(lump);
				}

				_ => unreachable!(),
			}
		}

		self.data.extend_from_slice(desc.data);

		Ok(())
	}
}