goldforge 0.8.1

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/>.

//! The [`Lump`] type.

use crate::wad::{Compression, LumpType, RawLump};

use oct::IntoOcts;

/// A WAD lump.
#[derive(Clone, Debug)]
pub struct Lump<'a> {
	/// The lump name.
	pub(super) name: &'a [i8],

	/// The lump's uncompressed data.
	pub(super) data: &'a [u8],

	/// The lump's compression format.
	pub(super) compression: Option<Compression>,

	/// The lump type.
	pub(super) lump_type: Option<LumpType>,
}

impl<'a> Lump<'a> {
	/// Constructs a validated lump from a raw one.
	///
	/// # Panics
	///
	/// If the provided, raw lump is not valid, this
	/// function will panic.
	#[must_use]
	#[track_caller]
	pub(super) fn from_raw(buf: &'a [u8], raw: RawLump<'a>) -> Self {
		// Assume that all fields have already been vali-
		// dated.

		debug_assert!(usize::try_from(raw.filepos().cast_unsigned()).is_ok());
		debug_assert!(usize::try_from(raw.disksize().cast_unsigned()).is_ok());
		debug_assert!(usize::try_from(raw.size().cast_unsigned()).is_ok());

		let name = raw.name();

		let data = if raw.disksize() > 0 {
			#[allow(clippy::cast_possible_truncation)]
			#[allow(clippy::cast_possible_wrap)]
			let start = raw.filepos().cast_unsigned() as usize;

			#[allow(clippy::cast_possible_truncation)]
			#[allow(clippy::cast_possible_wrap)]
			let stop = start + raw.disksize().cast_unsigned() as usize;

			&buf[start..stop]
		} else {
			Default::default()
		};

		let compression = Compression::from_i8(raw.compression());

		let lump_type = LumpType::from_i8(raw.r#type());

		Lump {
			name,
			data,
			compression,
			lump_type,
		}
	}

	/// Retrieves the lump's name.
	///
	/// If the name cannot be represented as a UTF-8
	/// string, this method returns [`None`]. See
	/// [`raw_name`] for more information.
	///
	/// [`raw_name`]: Self::raw_name
	#[inline]
	#[must_use]
	pub fn name(&self) -> Option<&'a str> {
		str::from_utf8(self.raw_name().as_octs()).ok()
	}

	/// Retrieves the lump's raw name.
	///
	/// The format of the returned data is technically
	/// unspecified but was initially often either
	/// ASCII or the [code page 437] format used by the
	/// IBM PC. For new WAD files, it is recommended to
	/// use ASCII if backwards-compatibility is
	/// necessary and UTF-8 if it isn't.
	///
	/// [code page 437]: <https://en.wikipedia.org/wiki/Code_page_437>
	#[inline]
	#[must_use]
	pub fn raw_name(&self) -> &'a [i8] {
		let name_len = self.name.iter()
			.position(|&c| c == 0)
			.unwrap_or(self.name.len());

		&self.name[..name_len]
	}

	/// Retrieves the uncompressed data for the lump.
	///
	/// Note that GoldForge does not currently implement
	/// the LZSS algorithm that is theoretically
	/// supported by Quake. Users should therefore
	/// uncompress such lumps themselves.
	#[inline(always)]
	#[must_use]
	pub fn data(&self) -> &'a [u8] {
		self.data
	}

	/// Retrieves the compression format.
	///
	/// For DOOM lumps, this always yields [`None`]..
	#[inline(always)]
	#[must_use]
	pub fn compression(&self) -> Option<Compression> {
		self.compression
	}

	/// Retrieves the lump type.
	///
	/// For DOOM lumps, this always yields [`None`].
	#[inline(always)]
	#[must_use]
	pub fn lump_type(&self) -> Option<LumpType> {
		self.lump_type
	}
}