Migrate recent HLLib work to BOS

It's better suited for the model of BOS and not an update to HLLibSharp
This commit is contained in:
Matt Nadareski
2022-12-24 12:57:10 -08:00
parent 3a862f343c
commit d8aec5aa97
101 changed files with 5399 additions and 22 deletions

View File

@@ -0,0 +1,190 @@
using System.IO;
using BurnOutSharp.Models.BSP;
using BurnOutSharp.Utilities;
namespace BurnOutSharp.Builders
{
public static class BSP
{
#region Constants
/// <summary>
/// Number of lumps in a BSP
/// </summary>
private const int HL_BSP_LUMP_COUNT = 15;
/// <summary>
/// Index for the entities lump
/// </summary>
public const int HL_BSP_LUMP_ENTITIES = 0;
/// <summary>
/// Index for the texture data lump
/// </summary>
public const int HL_BSP_LUMP_TEXTUREDATA = 2;
/// <summary>
/// Number of valid mipmap levels
/// </summary>
public const int HL_BSP_MIPMAP_COUNT = 4;
#endregion
#region Byte Data
/// <summary>
/// Parse a byte array into a Half-Life Level
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled Half-Life Level on success, null on error</returns>
public static Models.BSP.File ParseFile(byte[] data, int offset)
{
// If the data is invalid
if (data == null)
return null;
// If the offset is out of bounds
if (offset < 0 || offset >= data.Length)
return null;
// Create a memory stream and parse that
MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset);
return ParseFile(dataStream);
}
#endregion
#region Stream Data
/// <summary>
/// Parse a Stream into a Half-Life Level
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Level on success, null on error</returns>
public static Models.BSP.File ParseFile(Stream data)
{
// If the data is invalid
if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
return null;
// If the offset is out of bounds
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
int initialOffset = (int)data.Position;
// Create a new Half-Life Level to fill
var file = new Models.BSP.File();
#region Header
// Try to parse the header
var header = ParseHeader(data);
if (header == null)
return null;
// Set the level header
file.Header = header;
#endregion
#region Lumps
// Create the lump array
file.Lumps = new Lump[HL_BSP_LUMP_COUNT];
// Try to parse the lumps
for (int i = 0; i < HL_BSP_LUMP_COUNT; i++)
{
var lump = ParseLump(data);
file.Lumps[i] = lump;
}
#endregion
#region Texture header
// Try to get the texture header lump
var textureDataLump = file.Lumps[HL_BSP_LUMP_TEXTUREDATA];
if (textureDataLump.Offset == 0 || textureDataLump.Length == 0)
return null;
// Seek to the texture header
data.Seek(textureDataLump.Offset, SeekOrigin.Begin);
// Try to parse the texture header
var textureHeader = ParseTextureHeader(data);
if (textureHeader == null)
return null;
// Set the texture header
file.TextureHeader = textureHeader;
#endregion
return file;
}
/// <summary>
/// Parse a Stream into a Half-Life Level header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Level header on success, null on error</returns>
private static Header ParseHeader(Stream data)
{
// TODO: Use marshalling here instead of building
Header header = new Header();
// Only recognized versions are 29 and 30
header.Version = data.ReadUInt32();
if (header.Version != 29 && header.Version != 30)
return null;
return header;
}
/// <summary>
/// Parse a Stream into a lump
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled lump on success, null on error</returns>
private static Lump ParseLump(Stream data)
{
// TODO: Use marshalling here instead of building
Lump lump = new Lump();
lump.Offset = data.ReadUInt32();
lump.Length = data.ReadUInt32();
return lump;
}
/// <summary>
/// Parse a Stream into a Half-Life Level texture header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Level texture header on success, null on error</returns>
private static TextureHeader ParseTextureHeader(Stream data)
{
// TODO: Use marshalling here instead of building
TextureHeader textureHeader = new TextureHeader();
textureHeader.TextureCount = data.ReadUInt32();
var offsets = new uint[textureHeader.TextureCount];
for (int i = 0; i < textureHeader.TextureCount; i++)
{
offsets[i] = data.ReadUInt32();
}
textureHeader.Offsets = offsets;
return textureHeader;
}
#endregion
}
}

View File

@@ -0,0 +1,773 @@
using System.IO;
using System.Text;
using BurnOutSharp.Models.GCF;
using BurnOutSharp.Utilities;
namespace BurnOutSharp.Builders
{
public static class GCF
{
#region Constants
/// <summary>
/// The item is a file.
/// </summary>
private const int HL_GCF_FLAG_FILE = 0x00004000;
/// <summary>
/// The item is encrypted.
/// </summary>
private const int HL_GCF_FLAG_ENCRYPTED = 0x00000100;
/// <summary>
/// Backup the item before overwriting it.
/// </summary>
private const int HL_GCF_FLAG_BACKUP_LOCAL = 0x00000040;
/// <summary>
/// The item is to be copied to the disk.
/// </summary>
private const int HL_GCF_FLAG_COPY_LOCAL = 0x0000000A;
/// <summary>
/// Don't overwrite the item if copying it to the disk and the item already exists.
/// </summary>
private const int HL_GCF_FLAG_COPY_LOCAL_NO_OVERWRITE = 0x00000001;
/// <summary>
/// The maximum data allowed in a 32 bit checksum.
/// </summary>
private const int HL_GCF_CHECKSUM_LENGTH = 0x00008000;
#endregion
#region Byte Data
/// <summary>
/// Parse a byte array into a Half-Life Game Cache
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled Half-Life Game Cache on success, null on error</returns>
public static Models.GCF.File ParseFile(byte[] data, int offset)
{
// If the data is invalid
if (data == null)
return null;
// If the offset is out of bounds
if (offset < 0 || offset >= data.Length)
return null;
// Create a memory stream and parse that
MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset);
return ParseFile(dataStream);
}
#endregion
#region Stream Data
/// <summary>
/// Parse a Stream into a Half-Life Game Cache
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Game Cache on success, null on error</returns>
public static Models.GCF.File ParseFile(Stream data)
{
// If the data is invalid
if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
return null;
// If the offset is out of bounds
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
long initialOffset = data.Position;
// Create a new Half-Life Game Cache to fill
var file = new Models.GCF.File();
#region Header
// Try to parse the header
var header = ParseHeader(data);
if (header == null)
return null;
// Set the game cache header
file.Header = header;
#endregion
#region Block Entry Header
// Try to parse the block entry header
var blockEntryHeader = ParseBlockEntryHeader(data);
if (blockEntryHeader == null)
return null;
// Set the game cache block entry header
file.BlockEntryHeader = blockEntryHeader;
#endregion
#region Block Entries
// Create the block entry array
file.BlockEntries = new BlockEntry[blockEntryHeader.BlockCount];
// Try to parse the block entries
for (int i = 0; i < blockEntryHeader.BlockCount; i++)
{
var blockEntry = ParseBlockEntry(data);
file.BlockEntries[i] = blockEntry;
}
#endregion
#region Fragmentation Map Header
// Try to parse the fragmentation map header
var fragmentationMapHeader = ParseFragmentationMapHeader(data);
if (fragmentationMapHeader == null)
return null;
// Set the game cache fragmentation map header
file.FragmentationMapHeader = fragmentationMapHeader;
#endregion
#region Fragmentation Maps
// Create the fragmentation map array
file.FragmentationMaps = new FragmentationMap[fragmentationMapHeader.BlockCount];
// Try to parse the fragmentation maps
for (int i = 0; i < fragmentationMapHeader.BlockCount; i++)
{
var fragmentationMap = ParseFragmentationMap(data);
file.FragmentationMaps[i] = fragmentationMap;
}
#endregion
#region Block Entry Map Header
if (header.MinorVersion < 6)
{
// Try to parse the block entry map header
var blockEntryMapHeader = ParseBlockEntryMapHeader(data);
if (blockEntryMapHeader == null)
return null;
// Set the game cache block entry map header
file.BlockEntryMapHeader = blockEntryMapHeader;
}
#endregion
#region Block Entry Maps
if (header.MinorVersion < 6)
{
// Create the block entry map array
file.BlockEntryMaps = new BlockEntryMap[file.BlockEntryMapHeader.BlockCount];
// Try to parse the block entry maps
for (int i = 0; i < file.BlockEntryMapHeader.BlockCount; i++)
{
var blockEntryMap = ParseBlockEntryMap(data);
file.BlockEntryMaps[i] = blockEntryMap;
}
}
#endregion
// Cache the current offset
initialOffset = data.Position;
#region Directory Header
// Try to parse the directory header
var directoryHeader = ParseDirectoryHeader(data);
if (directoryHeader == null)
return null;
// Set the game cache directory header
file.DirectoryHeader = directoryHeader;
#endregion
#region Directory Entries
// Create the directory entry array
file.DirectoryEntries = new DirectoryEntry[directoryHeader.ItemCount];
// Try to parse the directory entries
for (int i = 0; i < directoryHeader.ItemCount; i++)
{
var directoryEntry = ParseDirectoryEntry(data);
file.DirectoryEntries[i] = directoryEntry;
}
#endregion
#region Directory Names
// Read the directory names as a single string
byte[] directoryNames = data.ReadBytes((int)directoryHeader.NameSize);
file.DirectoryNames = Encoding.ASCII.GetString(directoryNames);
#endregion
#region Directory Info 1 Entries
// Create the directory info 1 entry array
file.DirectoryInfo1Entries = new DirectoryInfo1Entry[directoryHeader.Info1Count];
// Try to parse the directory info 1 entries
for (int i = 0; i < directoryHeader.Info1Count; i++)
{
var directoryInfo1Entry = ParseDirectoryInfo1Entry(data);
file.DirectoryInfo1Entries[i] = directoryInfo1Entry;
}
#endregion
#region Directory Info 2 Entries
// Create the directory info 2 entry array
file.DirectoryInfo2Entries = new DirectoryInfo2Entry[directoryHeader.ItemCount];
// Try to parse the directory info 2 entries
for (int i = 0; i < directoryHeader.ItemCount; i++)
{
var directoryInfo2Entry = ParseDirectoryInfo2Entry(data);
file.DirectoryInfo2Entries[i] = directoryInfo2Entry;
}
#endregion
#region Directory Copy Entries
// Create the directory copy entry array
file.DirectoryCopyEntries = new DirectoryCopyEntry[directoryHeader.CopyCount];
// Try to parse the directory copy entries
for (int i = 0; i < directoryHeader.CopyCount; i++)
{
var directoryCopyEntry = ParseDirectoryCopyEntry(data);
file.DirectoryCopyEntries[i] = directoryCopyEntry;
}
#endregion
#region Directory Local Entries
// Create the directory local entry array
file.DirectoryLocalEntries = new DirectoryLocalEntry[directoryHeader.LocalCount];
// Try to parse the directory local entries
for (int i = 0; i < directoryHeader.LocalCount; i++)
{
var directoryLocalEntry = ParseDirectoryLocalEntry(data);
file.DirectoryLocalEntries[i] = directoryLocalEntry;
}
#endregion
// Seek to end of directory section, just in case
data.Seek(initialOffset + directoryHeader.DirectorySize, SeekOrigin.Begin);
#region Directory Map Header
if (header.MinorVersion >= 5)
{
// Try to parse the directory map header
var directoryMapHeader = ParseDirectoryMapHeader(data);
if (directoryMapHeader == null)
return null;
// Set the game cache directory map header
file.DirectoryMapHeader = directoryMapHeader;
}
#endregion
#region Directory Map Entries
// Create the directory map entry array
file.DirectoryMapEntries = new DirectoryMapEntry[directoryHeader.ItemCount];
// Try to parse the directory map entries
for (int i = 0; i < directoryHeader.ItemCount; i++)
{
var directoryMapEntry = ParseDirectoryMapEntry(data);
file.DirectoryMapEntries[i] = directoryMapEntry;
}
#endregion
#region Checksum Header
// Try to parse the checksum header
var checksumHeader = ParseChecksumHeader(data);
if (checksumHeader == null)
return null;
// Set the game cache checksum header
file.ChecksumHeader = checksumHeader;
#endregion
// Cache the current offset
initialOffset = data.Position;
#region Checksum Map Header
// Try to parse the checksum map header
var checksumMapHeader = ParseChecksumMapHeader(data);
if (checksumMapHeader == null)
return null;
// Set the game cache checksum map header
file.ChecksumMapHeader = checksumMapHeader;
#endregion
#region Checksum Map Entries
// Create the checksum map entry array
file.ChecksumMapEntries = new ChecksumMapEntry[checksumMapHeader.ItemCount];
// Try to parse the checksum map entries
for (int i = 0; i < checksumMapHeader.ItemCount; i++)
{
var checksumMapEntry = ParseChecksumMapEntry(data);
file.ChecksumMapEntries[i] = checksumMapEntry;
}
#endregion
#region Checksum Entries
// Create the checksum entry array
file.ChecksumEntries = new ChecksumEntry[checksumMapHeader.ChecksumCount];
// Try to parse the checksum entries
for (int i = 0; i < checksumMapHeader.ChecksumCount; i++)
{
var checksumEntry = ParseChecksumEntry(data);
file.ChecksumEntries[i] = checksumEntry;
}
#endregion
// Seek to end of checksum section, just in case
data.Seek(initialOffset + checksumHeader.ChecksumSize, SeekOrigin.Begin);
#region Data Block Header
// Try to parse the data block header
var dataBlockHeader = ParseDataBlockHeader(data, header.MinorVersion);
if (dataBlockHeader == null)
return null;
// Set the game cache data block header
file.DataBlockHeader = dataBlockHeader;
#endregion
return file;
}
/// <summary>
/// Parse a Stream into a Half-Life Game Cache header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Game Cache on success, null on error</returns>
private static Header ParseHeader(Stream data)
{
// TODO: Use marshalling here instead of building
Header header = new Header();
header.Dummy0 = data.ReadUInt32();
if (header.Dummy0 != 0x00000001)
return null;
header.MajorVersion = data.ReadUInt32();
if (header.MajorVersion != 0x00000001)
return null;
header.MinorVersion = data.ReadUInt32();
if (header.MinorVersion != 3 && header.MinorVersion != 5 && header.MinorVersion != 6)
return null;
header.CacheID = data.ReadUInt32();
header.LastVersionPlayed = data.ReadUInt32();
header.Dummy1 = data.ReadUInt32();
header.Dummy2 = data.ReadUInt32();
header.FileSize = data.ReadUInt32();
header.BlockSize = data.ReadUInt32();
header.BlockCount = data.ReadUInt32();
header.Dummy3 = data.ReadUInt32();
return header;
}
/// <summary>
/// Parse a Stream into a Half-Life Game Cache block entry header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Game Cache block entry header on success, null on error</returns>
private static BlockEntryHeader ParseBlockEntryHeader(Stream data)
{
// TODO: Use marshalling here instead of building
BlockEntryHeader blockEntryHeader = new BlockEntryHeader();
blockEntryHeader.BlockCount = data.ReadUInt32();
blockEntryHeader.BlocksUsed = data.ReadUInt32();
blockEntryHeader.Dummy0 = data.ReadUInt32();
blockEntryHeader.Dummy1 = data.ReadUInt32();
blockEntryHeader.Dummy2 = data.ReadUInt32();
blockEntryHeader.Dummy3 = data.ReadUInt32();
blockEntryHeader.Dummy4 = data.ReadUInt32();
blockEntryHeader.Checksum = data.ReadUInt32();
return blockEntryHeader;
}
/// <summary>
/// Parse a Stream into a Half-Life Game Cache block entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Game Cache block entry on success, null on error</returns>
private static BlockEntry ParseBlockEntry(Stream data)
{
// TODO: Use marshalling here instead of building
BlockEntry blockEntry = new BlockEntry();
blockEntry.EntryFlags = data.ReadUInt32();
blockEntry.FileDataOffset = data.ReadUInt32();
blockEntry.FileDataSize = data.ReadUInt32();
blockEntry.FirstDataBlockIndex = data.ReadUInt32();
blockEntry.NextBlockEntryIndex = data.ReadUInt32();
blockEntry.PreviousBlockEntryIndex = data.ReadUInt32();
blockEntry.DirectoryIndex = data.ReadUInt32();
return blockEntry;
}
/// <summary>
/// Parse a Stream into a Half-Life Game Cache fragmentation map header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Game Cache fragmentation map header on success, null on error</returns>
private static FragmentationMapHeader ParseFragmentationMapHeader(Stream data)
{
// TODO: Use marshalling here instead of building
FragmentationMapHeader fragmentationMapHeader = new FragmentationMapHeader();
fragmentationMapHeader.BlockCount = data.ReadUInt32();
fragmentationMapHeader.FirstUnusedEntry = data.ReadUInt32();
fragmentationMapHeader.Terminator = data.ReadUInt32();
fragmentationMapHeader.Checksum = data.ReadUInt32();
return fragmentationMapHeader;
}
/// <summary>
/// Parse a Stream into a Half-Life Game Cache fragmentation map
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Game Cache fragmentation map on success, null on error</returns>
private static FragmentationMap ParseFragmentationMap(Stream data)
{
// TODO: Use marshalling here instead of building
FragmentationMap fragmentationMap = new FragmentationMap();
fragmentationMap.NextDataBlockIndex = data.ReadUInt32();
return fragmentationMap;
}
/// <summary>
/// Parse a Stream into a Half-Life Game Cache block entry map header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Game Cache block entry map header on success, null on error</returns>
private static BlockEntryMapHeader ParseBlockEntryMapHeader(Stream data)
{
// TODO: Use marshalling here instead of building
BlockEntryMapHeader blockEntryMapHeader = new BlockEntryMapHeader();
blockEntryMapHeader.BlockCount = data.ReadUInt32();
blockEntryMapHeader.FirstBlockEntryIndex = data.ReadUInt32();
blockEntryMapHeader.LastBlockEntryIndex = data.ReadUInt32();
blockEntryMapHeader.Dummy0 = data.ReadUInt32();
blockEntryMapHeader.Checksum = data.ReadUInt32();
return blockEntryMapHeader;
}
/// <summary>
/// Parse a Stream into a Half-Life Game Cache block entry map
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Game Cache block entry map on success, null on error</returns>
private static BlockEntryMap ParseBlockEntryMap(Stream data)
{
// TODO: Use marshalling here instead of building
BlockEntryMap blockEntryMap = new BlockEntryMap();
blockEntryMap.PreviousBlockEntryIndex = data.ReadUInt32();
blockEntryMap.NextBlockEntryIndex = data.ReadUInt32();
return blockEntryMap;
}
/// <summary>
/// Parse a Stream into a Half-Life Game Cache directory header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Game Cache directory header on success, null on error</returns>
private static DirectoryHeader ParseDirectoryHeader(Stream data)
{
// TODO: Use marshalling here instead of building
DirectoryHeader directoryHeader = new DirectoryHeader();
directoryHeader.Dummy0 = data.ReadUInt32();
directoryHeader.CacheID = data.ReadUInt32();
directoryHeader.LastVersionPlayed = data.ReadUInt32();
directoryHeader.ItemCount = data.ReadUInt32();
directoryHeader.FileCount = data.ReadUInt32();
directoryHeader.Dummy1 = data.ReadUInt32();
directoryHeader.DirectorySize = data.ReadUInt32();
directoryHeader.NameSize = data.ReadUInt32();
directoryHeader.Info1Count = data.ReadUInt32();
directoryHeader.CopyCount = data.ReadUInt32();
directoryHeader.LocalCount = data.ReadUInt32();
directoryHeader.Dummy2 = data.ReadUInt32();
directoryHeader.Dummy3 = data.ReadUInt32();
directoryHeader.Checksum = data.ReadUInt32();
return directoryHeader;
}
/// <summary>
/// Parse a Stream into a Half-Life Game Cache directory entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Game Cache directory entry on success, null on error</returns>
private static DirectoryEntry ParseDirectoryEntry(Stream data)
{
// TODO: Use marshalling here instead of building
DirectoryEntry directoryEntry = new DirectoryEntry();
directoryEntry.NameOffset = data.ReadUInt32();
directoryEntry.ItemSize = data.ReadUInt32();
directoryEntry.ChecksumIndex = data.ReadUInt32();
directoryEntry.DirectoryFlags = data.ReadUInt32();
directoryEntry.ParentIndex = data.ReadUInt32();
directoryEntry.NextIndex = data.ReadUInt32();
directoryEntry.FirstIndex = data.ReadUInt32();
return directoryEntry;
}
/// <summary>
/// Parse a Stream into a Half-Life Game Cache directory info 1 entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Game Cache directory info 1 entry on success, null on error</returns>
private static DirectoryInfo1Entry ParseDirectoryInfo1Entry(Stream data)
{
// TODO: Use marshalling here instead of building
DirectoryInfo1Entry directoryInfo1Entry = new DirectoryInfo1Entry();
directoryInfo1Entry.Dummy0 = data.ReadUInt32();
return directoryInfo1Entry;
}
/// <summary>
/// Parse a Stream into a Half-Life Game Cache directory info 2 entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Game Cache directory info 2 entry on success, null on error</returns>
private static DirectoryInfo2Entry ParseDirectoryInfo2Entry(Stream data)
{
// TODO: Use marshalling here instead of building
DirectoryInfo2Entry directoryInfo2Entry = new DirectoryInfo2Entry();
directoryInfo2Entry.Dummy0 = data.ReadUInt32();
return directoryInfo2Entry;
}
/// <summary>
/// Parse a Stream into a Half-Life Game Cache directory copy entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Game Cache directory copy entry on success, null on error</returns>
private static DirectoryCopyEntry ParseDirectoryCopyEntry(Stream data)
{
// TODO: Use marshalling here instead of building
DirectoryCopyEntry directoryCopyEntry = new DirectoryCopyEntry();
directoryCopyEntry.DirectoryIndex = data.ReadUInt32();
return directoryCopyEntry;
}
/// <summary>
/// Parse a Stream into a Half-Life Game Cache directory local entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Game Cache directory local entry on success, null on error</returns>
private static DirectoryLocalEntry ParseDirectoryLocalEntry(Stream data)
{
// TODO: Use marshalling here instead of building
DirectoryLocalEntry directoryLocalEntry = new DirectoryLocalEntry();
directoryLocalEntry.DirectoryIndex = data.ReadUInt32();
return directoryLocalEntry;
}
/// <summary>
/// Parse a Stream into a Half-Life Game Cache directory map header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Game Cache directory map header on success, null on error</returns>
private static DirectoryMapHeader ParseDirectoryMapHeader(Stream data)
{
// TODO: Use marshalling here instead of building
DirectoryMapHeader directoryMapHeader = new DirectoryMapHeader();
directoryMapHeader.Dummy0 = data.ReadUInt32();
if (directoryMapHeader.Dummy0 != 0x00000001)
return null;
directoryMapHeader.Dummy1 = data.ReadUInt32();
if (directoryMapHeader.Dummy0 != 0x00000000)
return null;
return directoryMapHeader;
}
/// <summary>
/// Parse a Stream into a Half-Life Game Cache directory map entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Game Cache directory map entry on success, null on error</returns>
private static DirectoryMapEntry ParseDirectoryMapEntry(Stream data)
{
// TODO: Use marshalling here instead of building
DirectoryMapEntry directoryMapEntry = new DirectoryMapEntry();
directoryMapEntry.FirstBlockIndex = data.ReadUInt32();
return directoryMapEntry;
}
/// <summary>
/// Parse a Stream into a Half-Life Game Cache checksum header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Game Cache checksum header on success, null on error</returns>
private static ChecksumHeader ParseChecksumHeader(Stream data)
{
// TODO: Use marshalling here instead of building
ChecksumHeader checksumHeader = new ChecksumHeader();
checksumHeader.Dummy0 = data.ReadUInt32();
checksumHeader.ChecksumSize = data.ReadUInt32();
return checksumHeader;
}
/// <summary>
/// Parse a Stream into a Half-Life Game Cache checksum map header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Game Cache checksum map header on success, null on error</returns>
private static ChecksumMapHeader ParseChecksumMapHeader(Stream data)
{
// TODO: Use marshalling here instead of building
ChecksumMapHeader checksumMapHeader = new ChecksumMapHeader();
checksumMapHeader.Dummy0 = data.ReadUInt32();
if (checksumMapHeader.Dummy0 != 0x14893721)
return null;
checksumMapHeader.Dummy1 = data.ReadUInt32();
if (checksumMapHeader.Dummy0 != 0x00000001)
return null;
checksumMapHeader.ItemCount = data.ReadUInt32();
checksumMapHeader.ChecksumCount = data.ReadUInt32();
return checksumMapHeader;
}
/// <summary>
/// Parse a Stream into a Half-Life Game Cache checksum map entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Game Cache checksum map entry on success, null on error</returns>
private static ChecksumMapEntry ParseChecksumMapEntry(Stream data)
{
// TODO: Use marshalling here instead of building
ChecksumMapEntry checksumMapEntry = new ChecksumMapEntry();
checksumMapEntry.ChecksumCount = data.ReadUInt32();
checksumMapEntry.FirstChecksumIndex = data.ReadUInt32();
return checksumMapEntry;
}
/// <summary>
/// Parse a Stream into a Half-Life Game Cache checksum entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Game Cache checksum entry on success, null on error</returns>
private static ChecksumEntry ParseChecksumEntry(Stream data)
{
// TODO: Use marshalling here instead of building
ChecksumEntry checksumEntry = new ChecksumEntry();
checksumEntry.Checksum = data.ReadUInt32();
return checksumEntry;
}
/// <summary>
/// Parse a Stream into a Half-Life Game Cache data block header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="minorVersion">Minor version field from the header</param>
/// <returns>Filled Half-Life Game Cache data block header on success, null on error</returns>
private static DataBlockHeader ParseDataBlockHeader(Stream data, uint minorVersion)
{
// TODO: Use marshalling here instead of building
DataBlockHeader dataBlockHeader = new DataBlockHeader();
// In version 3 the DataBlockHeader is missing the LastVersionPlayed field.
if (minorVersion >= 5)
dataBlockHeader.LastVersionPlayed = data.ReadUInt32();
dataBlockHeader.BlockCount = data.ReadUInt32();
dataBlockHeader.BlockSize = data.ReadUInt32();
dataBlockHeader.FirstBlockOffset = data.ReadUInt32();
dataBlockHeader.BlocksUsed = data.ReadUInt32();
dataBlockHeader.Checksum = data.ReadUInt32();
return dataBlockHeader;
}
#endregion
}
}

View File

@@ -0,0 +1,524 @@
using System.IO;
using System.Text;
using BurnOutSharp.Models.NCF;
using BurnOutSharp.Utilities;
namespace BurnOutSharp.Builders
{
public static class NCF
{
#region Constants
/// <summary>
/// The item is a file.
/// </summary>
public const int HL_NCF_FLAG_FILE = 0x00004000;
/// <summary>
/// The item is encrypted.
/// </summary>
public const int HL_NCF_FLAG_ENCRYPTED = 0x00000100;
/// <summary>
/// Backup the item before overwriting it.
/// </summary>
public const int HL_NCF_FLAG_BACKUP_LOCAL = 0x00000040;
/// <summary>
/// The item is to be copied to the disk.
/// </summary>
public const int HL_NCF_FLAG_COPY_LOCAL = 0x0000000a;
/// <summary>
/// Don't overwrite the item if copying it to the disk and the item already exis
/// </summary>
public const int HL_NCF_FLAG_COPY_LOCAL_NO_OVERWRITE = 0x00000001;
#endregion
#region Byte Data
/// <summary>
/// Parse a byte array into a Half-Life No Cache
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled Half-Life No Cache on success, null on error</returns>
public static Models.NCF.File ParseFile(byte[] data, int offset)
{
// If the data is invalid
if (data == null)
return null;
// If the offset is out of bounds
if (offset < 0 || offset >= data.Length)
return null;
// Create a memory stream and parse that
MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset);
return ParseFile(dataStream);
}
#endregion
#region Stream Data
/// <summary>
/// Parse a Stream into a Half-Life No Cache
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life No Cache on success, null on error</returns>
public static Models.NCF.File ParseFile(Stream data)
{
// If the data is invalid
if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
return null;
// If the offset is out of bounds
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
long initialOffset = data.Position;
// Create a new Half-Life No Cache to fill
var file = new Models.NCF.File();
#region Header
// Try to parse the header
var header = ParseHeader(data);
if (header == null)
return null;
// Set the no cache header
file.Header = header;
#endregion
// Cache the current offset
initialOffset = data.Position;
#region Directory Header
// Try to parse the directory header
var directoryHeader = ParseDirectoryHeader(data);
if (directoryHeader == null)
return null;
// Set the game cache directory header
file.DirectoryHeader = directoryHeader;
#endregion
#region Directory Entries
// Create the directory entry array
file.DirectoryEntries = new DirectoryEntry[directoryHeader.ItemCount];
// Try to parse the directory entries
for (int i = 0; i < directoryHeader.ItemCount; i++)
{
var directoryEntry = ParseDirectoryEntry(data);
file.DirectoryEntries[i] = directoryEntry;
}
#endregion
#region Directory Names
// Read the directory names as a single string
byte[] directoryNames = data.ReadBytes((int)directoryHeader.NameSize);
file.DirectoryNames = Encoding.ASCII.GetString(directoryNames);
#endregion
#region Directory Info 1 Entries
// Create the directory info 1 entry array
file.DirectoryInfo1Entries = new DirectoryInfo1Entry[directoryHeader.Info1Count];
// Try to parse the directory info 1 entries
for (int i = 0; i < directoryHeader.Info1Count; i++)
{
var directoryInfo1Entry = ParseDirectoryInfo1Entry(data);
file.DirectoryInfo1Entries[i] = directoryInfo1Entry;
}
#endregion
#region Directory Info 2 Entries
// Create the directory info 2 entry array
file.DirectoryInfo2Entries = new DirectoryInfo2Entry[directoryHeader.ItemCount];
// Try to parse the directory info 2 entries
for (int i = 0; i < directoryHeader.ItemCount; i++)
{
var directoryInfo2Entry = ParseDirectoryInfo2Entry(data);
file.DirectoryInfo2Entries[i] = directoryInfo2Entry;
}
#endregion
#region Directory Copy Entries
// Create the directory copy entry array
file.DirectoryCopyEntries = new DirectoryCopyEntry[directoryHeader.CopyCount];
// Try to parse the directory copy entries
for (int i = 0; i < directoryHeader.CopyCount; i++)
{
var directoryCopyEntry = ParseDirectoryCopyEntry(data);
file.DirectoryCopyEntries[i] = directoryCopyEntry;
}
#endregion
#region Directory Local Entries
// Create the directory local entry array
file.DirectoryLocalEntries = new DirectoryLocalEntry[directoryHeader.LocalCount];
// Try to parse the directory local entries
for (int i = 0; i < directoryHeader.LocalCount; i++)
{
var directoryLocalEntry = ParseDirectoryLocalEntry(data);
file.DirectoryLocalEntries[i] = directoryLocalEntry;
}
#endregion
// Seek to end of directory section, just in case
data.Seek(initialOffset + directoryHeader.DirectorySize, SeekOrigin.Begin);
#region Unknown Header
// Try to parse the unknown header
var unknownHeader = ParseUnknownHeader(data);
if (unknownHeader == null)
return null;
// Set the game cache unknown header
file.UnknownHeader = unknownHeader;
#endregion
#region Directory Map Entries
// Create the unknown entry array
file.UnknownEntries = new UnknownEntry[directoryHeader.ItemCount];
// Try to parse the unknown entries
for (int i = 0; i < directoryHeader.ItemCount; i++)
{
var unknownEntry = ParseUnknownEntry(data);
file.UnknownEntries[i] = unknownEntry;
}
#endregion
#region Checksum Header
// Try to parse the checksum header
var checksumHeader = ParseChecksumHeader(data);
if (checksumHeader == null)
return null;
// Set the game cache checksum header
file.ChecksumHeader = checksumHeader;
#endregion
// Cache the current offset
initialOffset = data.Position;
#region Checksum Map Header
// Try to parse the checksum map header
var checksumMapHeader = ParseChecksumMapHeader(data);
if (checksumMapHeader == null)
return null;
// Set the game cache checksum map header
file.ChecksumMapHeader = checksumMapHeader;
#endregion
#region Checksum Map Entries
// Create the checksum map entry array
file.ChecksumMapEntries = new ChecksumMapEntry[checksumMapHeader.ItemCount];
// Try to parse the checksum map entries
for (int i = 0; i < checksumMapHeader.ItemCount; i++)
{
var checksumMapEntry = ParseChecksumMapEntry(data);
file.ChecksumMapEntries[i] = checksumMapEntry;
}
#endregion
#region Checksum Entries
// Create the checksum entry array
file.ChecksumEntries = new ChecksumEntry[checksumMapHeader.ChecksumCount];
// Try to parse the checksum entries
for (int i = 0; i < checksumMapHeader.ChecksumCount; i++)
{
var checksumEntry = ParseChecksumEntry(data);
file.ChecksumEntries[i] = checksumEntry;
}
#endregion
// Seek to end of checksum section, just in case
data.Seek(initialOffset + checksumHeader.ChecksumSize, SeekOrigin.Begin);
return file;
}
/// <summary>
/// Parse a Stream into a Half-Life No Cache header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life No Cache header on success, null on error</returns>
private static Header ParseHeader(Stream data)
{
// TODO: Use marshalling here instead of building
Header header = new Header();
header.Dummy0 = data.ReadUInt32();
header.MajorVersion = data.ReadUInt32();
if (header.MajorVersion != 2)
return null;
header.MinorVersion = data.ReadUInt32();
if (header.MinorVersion != 1)
return null;
header.CacheID = data.ReadUInt32();
header.LastVersionPlayed = data.ReadUInt32();
header.Dummy3 = data.ReadUInt32();
header.Dummy4 = data.ReadUInt32();
header.FileSize = data.ReadUInt32();
header.BlockSize = data.ReadUInt32();
header.BlockCount = data.ReadUInt32();
header.Dummy5 = data.ReadUInt32();
return header;
}
/// <summary>
/// Parse a Stream into a Half-Life No Cache directory header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life No Cache directory header on success, null on error</returns>
private static DirectoryHeader ParseDirectoryHeader(Stream data)
{
// TODO: Use marshalling here instead of building
DirectoryHeader directoryHeader = new DirectoryHeader();
directoryHeader.Dummy0 = data.ReadUInt32();
directoryHeader.CacheID = data.ReadUInt32();
directoryHeader.LastVersionPlayed = data.ReadUInt32();
directoryHeader.ItemCount = data.ReadUInt32();
directoryHeader.FileCount = data.ReadUInt32();
directoryHeader.Dummy1 = data.ReadUInt32();
directoryHeader.DirectorySize = data.ReadUInt32();
directoryHeader.NameSize = data.ReadUInt32();
directoryHeader.Info1Count = data.ReadUInt32();
directoryHeader.CopyCount = data.ReadUInt32();
directoryHeader.LocalCount = data.ReadUInt32();
directoryHeader.Dummy2 = data.ReadUInt32();
directoryHeader.Checksum = data.ReadUInt32();
return directoryHeader;
}
/// <summary>
/// Parse a Stream into a Half-Life No Cache directory entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life No Cache directory entry on success, null on error</returns>
private static DirectoryEntry ParseDirectoryEntry(Stream data)
{
// TODO: Use marshalling here instead of building
DirectoryEntry directoryEntry = new DirectoryEntry();
directoryEntry.NameOffset = data.ReadUInt32();
directoryEntry.ItemSize = data.ReadUInt32();
directoryEntry.ChecksumIndex = data.ReadUInt32();
directoryEntry.DirectoryFlags = data.ReadUInt32();
directoryEntry.ParentIndex = data.ReadUInt32();
directoryEntry.NextIndex = data.ReadUInt32();
directoryEntry.FirstIndex = data.ReadUInt32();
return directoryEntry;
}
/// <summary>
/// Parse a Stream into a Half-Life No Cache directory info 1 entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life No Cache directory info 1 entry on success, null on error</returns>
private static DirectoryInfo1Entry ParseDirectoryInfo1Entry(Stream data)
{
// TODO: Use marshalling here instead of building
DirectoryInfo1Entry directoryInfo1Entry = new DirectoryInfo1Entry();
directoryInfo1Entry.Dummy0 = data.ReadUInt32();
return directoryInfo1Entry;
}
/// <summary>
/// Parse a Stream into a Half-Life No Cache directory info 2 entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life No Cache directory info 2 entry on success, null on error</returns>
private static DirectoryInfo2Entry ParseDirectoryInfo2Entry(Stream data)
{
// TODO: Use marshalling here instead of building
DirectoryInfo2Entry directoryInfo2Entry = new DirectoryInfo2Entry();
directoryInfo2Entry.Dummy0 = data.ReadUInt32();
return directoryInfo2Entry;
}
/// <summary>
/// Parse a Stream into a Half-Life No Cache directory copy entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life No Cache directory copy entry on success, null on error</returns>
private static DirectoryCopyEntry ParseDirectoryCopyEntry(Stream data)
{
// TODO: Use marshalling here instead of building
DirectoryCopyEntry directoryCopyEntry = new DirectoryCopyEntry();
directoryCopyEntry.DirectoryIndex = data.ReadUInt32();
return directoryCopyEntry;
}
/// <summary>
/// Parse a Stream into a Half-Life No Cache directory local entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life No Cache directory local entry on success, null on error</returns>
private static DirectoryLocalEntry ParseDirectoryLocalEntry(Stream data)
{
// TODO: Use marshalling here instead of building
DirectoryLocalEntry directoryLocalEntry = new DirectoryLocalEntry();
directoryLocalEntry.DirectoryIndex = data.ReadUInt32();
return directoryLocalEntry;
}
/// <summary>
/// Parse a Stream into a Half-Life No Cache unknown header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life No Cache unknown header on success, null on error</returns>
private static UnknownHeader ParseUnknownHeader(Stream data)
{
// TODO: Use marshalling here instead of building
UnknownHeader unknownHeader = new UnknownHeader();
unknownHeader.Dummy0 = data.ReadUInt32();
if (unknownHeader.Dummy0 != 0x00000001)
return null;
unknownHeader.Dummy1 = data.ReadUInt32();
if (unknownHeader.Dummy0 != 0x00000000)
return null;
return unknownHeader;
}
/// <summary>
/// Parse a Stream into a Half-Life No Cache unknown entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life No Cacheunknown entry on success, null on error</returns>
private static UnknownEntry ParseUnknownEntry(Stream data)
{
// TODO: Use marshalling here instead of building
UnknownEntry unknownEntry = new UnknownEntry();
unknownEntry.Dummy0 = data.ReadUInt32();
return unknownEntry;
}
/// <summary>
/// Parse a Stream into a Half-Life No Cache checksum header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life No Cache checksum header on success, null on error</returns>
private static ChecksumHeader ParseChecksumHeader(Stream data)
{
// TODO: Use marshalling here instead of building
ChecksumHeader checksumHeader = new ChecksumHeader();
checksumHeader.Dummy0 = data.ReadUInt32();
checksumHeader.ChecksumSize = data.ReadUInt32();
return checksumHeader;
}
/// <summary>
/// Parse a Stream into a Half-Life No Cache checksum map header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life No Cache checksum map header on success, null on error</returns>
private static ChecksumMapHeader ParseChecksumMapHeader(Stream data)
{
// TODO: Use marshalling here instead of building
ChecksumMapHeader checksumMapHeader = new ChecksumMapHeader();
checksumMapHeader.Dummy0 = data.ReadUInt32();
checksumMapHeader.Dummy1 = data.ReadUInt32();
checksumMapHeader.ItemCount = data.ReadUInt32();
checksumMapHeader.ChecksumCount = data.ReadUInt32();
return checksumMapHeader;
}
/// <summary>
/// Parse a Stream into a Half-Life No Cache checksum map entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life No Cache checksum map entry on success, null on error</returns>
private static ChecksumMapEntry ParseChecksumMapEntry(Stream data)
{
// TODO: Use marshalling here instead of building
ChecksumMapEntry checksumMapEntry = new ChecksumMapEntry();
checksumMapEntry.ChecksumCount = data.ReadUInt32();
checksumMapEntry.FirstChecksumIndex = data.ReadUInt32();
return checksumMapEntry;
}
/// <summary>
/// Parse a Stream into a Half-Life No Cache checksum entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life No Cache checksum entry on success, null on error</returns>
private static ChecksumEntry ParseChecksumEntry(Stream data)
{
// TODO: Use marshalling here instead of building
ChecksumEntry checksumEntry = new ChecksumEntry();
checksumEntry.Checksum = data.ReadUInt32();
return checksumEntry;
}
#endregion
}
}

View File

@@ -0,0 +1,133 @@
using System.IO;
using System.Text;
using BurnOutSharp.Models.PAK;
using BurnOutSharp.Utilities;
namespace BurnOutSharp.Builders
{
public static class PAK
{
#region Byte Data
/// <summary>
/// Parse a byte array into a Half-Life Package
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled Half-Life Package on success, null on error</returns>
public static Models.PAK.File ParseFile(byte[] data, int offset)
{
// If the data is invalid
if (data == null)
return null;
// If the offset is out of bounds
if (offset < 0 || offset >= data.Length)
return null;
// Create a memory stream and parse that
MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset);
return ParseFile(dataStream);
}
#endregion
#region Stream Data
/// <summary>
/// Parse a Stream into a Half-Life Package
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Package on success, null on error</returns>
public static Models.PAK.File ParseFile(Stream data)
{
// If the data is invalid
if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
return null;
// If the offset is out of bounds
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
long initialOffset = data.Position;
// Create a new Half-Life Package to fill
var file = new Models.PAK.File();
#region Header
// Try to parse the header
var header = ParseHeader(data);
if (header == null)
return null;
// Set the package header
file.Header = header;
#endregion
#region Directory Items
// Get the directory items offset
uint directoryItemsOffset = header.DirectoryOffset;
if (directoryItemsOffset < 0 || directoryItemsOffset >= data.Length)
return null;
// Seek to the directory items
data.Seek(directoryItemsOffset, SeekOrigin.Begin);
// Create the directory item array
file.DirectoryItems = new DirectoryItem[header.DirectoryLength / 64];
// Try to parse the directory items
for (int i = 0; i < file.DirectoryItems.Length; i++)
{
var directoryItem = ParseDirectoryItem(data);
file.DirectoryItems[i] = directoryItem;
}
#endregion
return file;
}
/// <summary>
/// Parse a Stream into a Half-Life Package header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Package header on success, null on error</returns>
private static Header ParseHeader(Stream data)
{
// TODO: Use marshalling here instead of building
Header header = new Header();
byte[] signature = data.ReadBytes(4);
header.Signature = Encoding.ASCII.GetString(signature);
header.DirectoryOffset = data.ReadUInt32();
header.DirectoryLength = data.ReadUInt32();
return header;
}
/// <summary>
/// Parse a Stream into a Half-Life Package directory item
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Package directory item on success, null on error</returns>
private static DirectoryItem ParseDirectoryItem(Stream data)
{
// TODO: Use marshalling here instead of building
DirectoryItem directoryItem = new DirectoryItem();
byte[] itemName = data.ReadBytes(56);
directoryItem.ItemName = Encoding.ASCII.GetString(itemName);
directoryItem.ItemOffset = data.ReadUInt32();
directoryItem.ItemLength = data.ReadUInt32();
return directoryItem;
}
#endregion
}
}

View File

@@ -0,0 +1,688 @@
using System.Collections.Generic;
using System.IO;
using System.Text;
using BurnOutSharp.Models.SGA;
using BurnOutSharp.Utilities;
namespace BurnOutSharp.Builders
{
public static class SGA
{
#region Constants
/// <summary>
/// Length of a SGA checksum in bytes
/// </summary>
public const int HL_SGA_CHECKSUM_LENGTH = 0x00008000;
#endregion
#region Byte Data
/// <summary>
/// Parse a byte array into an SGA
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled SGA on success, null on error</returns>
public static Models.SGA.File ParseFile(byte[] data, int offset)
{
// If the data is invalid
if (data == null)
return null;
// If the offset is out of bounds
if (offset < 0 || offset >= data.Length)
return null;
// Create a memory stream and parse that
MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset);
return ParseFile(dataStream);
}
#endregion
#region Stream Data
/// <summary>
/// Parse a Stream into an SGA
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled SGA on success, null on error</returns>
public static Models.SGA.File ParseFile(Stream data)
{
// If the data is invalid
if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
return null;
// If the offset is out of bounds
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
long initialOffset = data.Position;
// Create a new SGA to fill
var file = new Models.SGA.File();
#region Header
// Try to parse the header
var header = ParseHeader(data);
if (header == null)
return null;
// Set the SGA header
file.Header = header;
#endregion
#region Directory
// Try to parse the directory
var directory = ParseDirectory(data, header.MajorVersion);
if (directory == null)
return null;
// Set the SGA directory
file.Directory = directory;
#endregion
return file;
}
/// <summary>
/// Parse a Stream into an SGA header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled SGA header on success, null on error</returns>
private static Header ParseHeader(Stream data)
{
// TODO: Use marshalling here instead of building
byte[] signatureBytes = data.ReadBytes(8);
string signature = Encoding.ASCII.GetString(signatureBytes);
if (signature != "_ARCHIVE")
return null;
ushort majorVersion = data.ReadUInt16();
ushort minorVersion = data.ReadUInt16();
if (minorVersion != 0)
return null;
switch (majorVersion)
{
// Versions 4 and 5 share the same header
case 4:
case 5:
Header4 header4 = new Header4();
header4.Signature = signature;
header4.MajorVersion = majorVersion;
header4.MinorVersion = minorVersion;
header4.FileMD5 = data.ReadBytes(0x10);
byte[] header4Name = data.ReadBytes(64);
header4.Name = Encoding.ASCII.GetString(header4Name);
header4.HeaderMD5 = data.ReadBytes(0x10);
header4.HeaderLength = data.ReadUInt32();
header4.FileDataOffset = data.ReadUInt32();
header4.Dummy0 = data.ReadUInt32();
return header4;
// Versions 6 and 7 share the same header
case 6:
case 7:
Header6 header6 = new Header6();
header6.Signature = signature;
header6.MajorVersion = majorVersion;
header6.MinorVersion = minorVersion;
byte[] header6Name = data.ReadBytes(64);
header6.Name = Encoding.ASCII.GetString(header6Name);
header6.HeaderLength = data.ReadUInt32();
header6.FileDataOffset = data.ReadUInt32();
header6.Dummy0 = data.ReadUInt32();
return header6;
// No other major versions are recognized
default:
return null;
}
}
/// <summary>
/// Parse a Stream into an SGA directory
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="majorVersion">SGA major version</param>
/// <returns>Filled SGA directory on success, null on error</returns>
private static Models.SGA.Directory ParseDirectory(Stream data, ushort majorVersion)
{
#region Directory
// Create the appropriate type of directory
Models.SGA.Directory directory;
switch (majorVersion)
{
case 4: directory = new Directory4(); break;
case 5: directory = new Directory5(); break;
case 6: directory = new Directory6(); break;
case 7: directory = new Directory7(); break;
default: return null;
}
#endregion
#region Directory Header
// Try to parse the directory header
var directoryHeader = ParseDirectoryHeader(data, majorVersion);
if (directoryHeader == null)
return null;
// Set the directory header
switch (majorVersion)
{
case 4: (directory as Directory4).DirectoryHeader = directoryHeader as DirectoryHeader4; break;
case 5: (directory as Directory5).DirectoryHeader = directoryHeader as DirectoryHeader5; break;
case 6: (directory as Directory6).DirectoryHeader = directoryHeader as DirectoryHeader5; break;
case 7: (directory as Directory7).DirectoryHeader = directoryHeader as DirectoryHeader7; break;
default: return null;
}
#endregion
#region Sections
// Get the sections offset
uint sectionOffset;
switch (majorVersion)
{
case 4: sectionOffset = (directoryHeader as DirectoryHeader4).SectionOffset; break;
case 5: sectionOffset = (directoryHeader as DirectoryHeader5).SectionOffset; break;
case 6: sectionOffset = (directoryHeader as DirectoryHeader5).SectionOffset; break;
case 7: sectionOffset = (directoryHeader as DirectoryHeader7).SectionOffset; break;
default: return null;
}
// Validate the offset
if (sectionOffset < 0 || sectionOffset >= data.Length)
return null;
// Seek to the sections
data.Seek(sectionOffset, SeekOrigin.Begin);
// Get the section count
uint sectionCount;
switch (majorVersion)
{
case 4: sectionCount = (directoryHeader as DirectoryHeader4).SectionCount; break;
case 5: sectionCount = (directoryHeader as DirectoryHeader5).SectionCount; break;
case 6: sectionCount = (directoryHeader as DirectoryHeader5).SectionCount; break;
case 7: sectionCount = (directoryHeader as DirectoryHeader7).SectionCount; break;
default: return null;
}
// Create the sections array
object[] sections;
switch (majorVersion)
{
case 4: sections = new Section4[sectionCount]; break;
case 5: sections = new Section5[sectionCount]; break;
case 6: sections = new Section5[sectionCount]; break;
case 7: sections = new Section5[sectionCount]; break;
default: return null;
}
// Try to parse the sections
for (int i = 0; i < sections.Length; i++)
{
switch (majorVersion)
{
case 4: sections[i] = ParseSection4(data); break;
case 5: sections[i] = ParseSection5(data); break;
case 6: sections[i] = ParseSection5(data); break;
case 7: sections[i] = ParseSection5(data); break;
default: return null;
}
}
// Assign the sections
switch (majorVersion)
{
case 4: (directory as Directory4).Sections = sections as Section4[]; break;
case 5: (directory as Directory5).Sections = sections as Section5[]; break;
case 6: (directory as Directory6).Sections = sections as Section5[]; break;
case 7: (directory as Directory7).Sections = sections as Section5[]; break;
default: return null;
}
#endregion
#region Folders
// Get the folders offset
uint folderOffset;
switch (majorVersion)
{
case 4: folderOffset = (directoryHeader as DirectoryHeader4).FolderOffset; break;
case 5: folderOffset = (directoryHeader as DirectoryHeader5).FolderOffset; break;
case 6: folderOffset = (directoryHeader as DirectoryHeader5).FolderOffset; break;
case 7: folderOffset = (directoryHeader as DirectoryHeader7).FolderOffset; break;
default: return null;
}
// Validate the offset
if (folderOffset < 0 || folderOffset >= data.Length)
return null;
// Seek to the folders
data.Seek(folderOffset, SeekOrigin.Begin);
// Get the folder count
uint folderCount;
switch (majorVersion)
{
case 4: folderCount = (directoryHeader as DirectoryHeader4).FolderCount; break;
case 5: folderCount = (directoryHeader as DirectoryHeader5).FolderCount; break;
case 6: folderCount = (directoryHeader as DirectoryHeader5).FolderCount; break;
case 7: folderCount = (directoryHeader as DirectoryHeader7).FolderCount; break;
default: return null;
}
// Create the folders array
object[] folders;
switch (majorVersion)
{
case 4: folders = new Folder4[folderCount]; break;
case 5: folders = new Folder5[folderCount]; break;
case 6: folders = new Folder5[folderCount]; break;
case 7: folders = new Folder5[folderCount]; break;
default: return null;
}
// Try to parse the folders
for (int i = 0; i < folders.Length; i++)
{
switch (majorVersion)
{
case 4: folders[i] = ParseFolder4(data); break;
case 5: folders[i] = ParseFolder5(data); break;
case 6: folders[i] = ParseFolder5(data); break;
case 7: folders[i] = ParseFolder5(data); break;
default: return null;
}
}
// Assign the folders
switch (majorVersion)
{
case 4: (directory as Directory4).Folders = folders as Folder4[]; break;
case 5: (directory as Directory5).Folders = folders as Folder5[]; break;
case 6: (directory as Directory6).Folders = folders as Folder5[]; break;
case 7: (directory as Directory7).Folders = folders as Folder5[]; break;
default: return null;
}
#endregion
#region Files
// Get the files offset
uint fileOffset;
switch (majorVersion)
{
case 4: fileOffset = (directoryHeader as DirectoryHeader4).FileOffset; break;
case 5: fileOffset = (directoryHeader as DirectoryHeader5).FileOffset; break;
case 6: fileOffset = (directoryHeader as DirectoryHeader5).FileOffset; break;
case 7: fileOffset = (directoryHeader as DirectoryHeader7).FileOffset; break;
default: return null;
}
// Validate the offset
if (fileOffset < 0 || fileOffset >= data.Length)
return null;
// Seek to the files
data.Seek(fileOffset, SeekOrigin.Begin);
// Get the file count
uint fileCount;
switch (majorVersion)
{
case 4: fileCount = (directoryHeader as DirectoryHeader4).FileCount; break;
case 5: fileCount = (directoryHeader as DirectoryHeader5).FileCount; break;
case 6: fileCount = (directoryHeader as DirectoryHeader5).FileCount; break;
case 7: fileCount = (directoryHeader as DirectoryHeader7).FileCount; break;
default: return null;
}
// Create the files array
object[] files;
switch (majorVersion)
{
case 4: files = new File4[fileCount]; break;
case 5: files = new File4[fileCount]; break;
case 6: files = new File6[fileCount]; break;
case 7: files = new File7[fileCount]; break;
default: return null;
}
// Try to parse the files
for (int i = 0; i < files.Length; i++)
{
switch (majorVersion)
{
case 4: files[i] = ParseFile4(data); break;
case 5: files[i] = ParseFile4(data); break;
case 6: files[i] = ParseFile6(data); break;
case 7: files[i] = ParseFile7(data); break;
default: return null;
}
}
// Assign the files
switch (majorVersion)
{
case 4: (directory as Directory4).Files = files as File4[]; break;
case 5: (directory as Directory5).Files = files as File4[]; break;
case 6: (directory as Directory6).Files = files as File6[]; break;
case 7: (directory as Directory7).Files = files as File7[]; break;
default: return null;
}
#endregion
#region String Table
// Get the string table offset
uint stringTableOffset;
switch (majorVersion)
{
case 4: stringTableOffset = (directoryHeader as DirectoryHeader4).StringTableOffset; break;
case 5: stringTableOffset = (directoryHeader as DirectoryHeader5).StringTableOffset; break;
case 6: stringTableOffset = (directoryHeader as DirectoryHeader5).StringTableOffset; break;
case 7: stringTableOffset = (directoryHeader as DirectoryHeader7).StringTableOffset; break;
default: return null;
}
// Validate the offset
if (stringTableOffset < 0 || stringTableOffset >= data.Length)
return null;
// Seek to the string table
data.Seek(stringTableOffset, SeekOrigin.Begin);
// Get the string table count
uint stringCount;
switch (majorVersion)
{
case 4: stringCount = (directoryHeader as DirectoryHeader4).StringTableCount; break;
case 5: stringCount = (directoryHeader as DirectoryHeader5).StringTableCount; break;
case 6: stringCount = (directoryHeader as DirectoryHeader5).StringTableCount; break;
case 7: stringCount = (directoryHeader as DirectoryHeader7).StringTableCount; break;
default: return null;
}
// Create the strings array
Dictionary<long, string> strings = new Dictionary<long, string>((int)stringCount);
// Try to parse the strings
for (int i = 0; i < stringCount; i++)
{
long currentPosition = data.Position;
strings[currentPosition] = data.ReadString(Encoding.ASCII);
}
// Assign the files
switch (majorVersion)
{
case 4: (directory as Directory4).StringTable = strings; break;
case 5: (directory as Directory5).StringTable = strings; break;
case 6: (directory as Directory6).StringTable = strings; break;
case 7: (directory as Directory7).StringTable = strings; break;
default: return null;
}
#endregion
return directory;
}
/// <summary>
/// Parse a Stream into an SGA directory header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="majorVersion">SGA major version</param>
/// <returns>Filled SGA directory header on success, null on error</returns>
private static object ParseDirectoryHeader(Stream data, ushort majorVersion)
{
switch (majorVersion)
{
case 4: return ParseDirectory4Header(data);
case 5: return ParseDirectory5Header(data);
case 6: return ParseDirectory5Header(data);
case 7: return ParseDirectory7Header(data);
default: return null;
}
}
/// <summary>
/// Parse a Stream into an SGA directory header version 4
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled SGA directory header version 4 on success, null on error</returns>
private static DirectoryHeader4 ParseDirectory4Header(Stream data)
{
DirectoryHeader4 directoryHeader4 = new DirectoryHeader4();
directoryHeader4.SectionOffset = data.ReadUInt32();
directoryHeader4.SectionCount = data.ReadUInt16();
directoryHeader4.FolderOffset = data.ReadUInt32();
directoryHeader4.FolderCount = data.ReadUInt16();
directoryHeader4.FileOffset = data.ReadUInt32();
directoryHeader4.FileCount = data.ReadUInt16();
directoryHeader4.StringTableOffset = data.ReadUInt32();
directoryHeader4.StringTableCount = data.ReadUInt16();
return directoryHeader4;
}
/// <summary>
/// Parse a Stream into an SGA directory header version 5
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled SGA directory header version 5 on success, null on error</returns>
private static DirectoryHeader5 ParseDirectory5Header(Stream data)
{
DirectoryHeader5 directoryHeader5 = new DirectoryHeader5();
directoryHeader5.SectionOffset = data.ReadUInt32();
directoryHeader5.SectionCount = data.ReadUInt32();
directoryHeader5.FolderOffset = data.ReadUInt32();
directoryHeader5.FolderCount = data.ReadUInt32();
directoryHeader5.FileOffset = data.ReadUInt32();
directoryHeader5.FileCount = data.ReadUInt32();
directoryHeader5.StringTableOffset = data.ReadUInt32();
directoryHeader5.StringTableCount = data.ReadUInt32();
return directoryHeader5;
}
/// <summary>
/// Parse a Stream into an SGA directory header version 7
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled SGA directory header version 7 on success, null on error</returns>
private static DirectoryHeader7 ParseDirectory7Header(Stream data)
{
DirectoryHeader7 directoryHeader7 = new DirectoryHeader7();
directoryHeader7.SectionOffset = data.ReadUInt32();
directoryHeader7.SectionCount = data.ReadUInt32();
directoryHeader7.FolderOffset = data.ReadUInt32();
directoryHeader7.FolderCount = data.ReadUInt32();
directoryHeader7.FileOffset = data.ReadUInt32();
directoryHeader7.FileCount = data.ReadUInt32();
directoryHeader7.StringTableOffset = data.ReadUInt32();
directoryHeader7.StringTableCount = data.ReadUInt32();
directoryHeader7.HashTableOffset = data.ReadUInt32();
directoryHeader7.BlockSize = data.ReadUInt32();
return directoryHeader7;
}
/// <summary>
/// Parse a Stream into an SGA section version 4
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="majorVersion">SGA major version</param>
/// <returns>Filled SGA section version 4 on success, null on error</returns>
private static Section4 ParseSection4(Stream data)
{
Section4 section4 = new Section4();
byte[] section4Alias = data.ReadBytes(64);
section4.Alias = Encoding.ASCII.GetString(section4Alias);
byte[] section4Name = data.ReadBytes(64);
section4.Name = Encoding.ASCII.GetString(section4Name);
section4.FolderStartIndex = data.ReadUInt16();
section4.FolderEndIndex = data.ReadUInt16();
section4.FileStartIndex = data.ReadUInt16();
section4.FileEndIndex = data.ReadUInt16();
section4.FolderRootIndex = data.ReadUInt16();
return section4;
}
/// <summary>
/// Parse a Stream into an SGA section version 5
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="majorVersion">SGA major version</param>
/// <returns>Filled SGA section version 5 on success, null on error</returns>
private static Section5 ParseSection5(Stream data)
{
Section5 section5 = new Section5();
byte[] section5Alias = data.ReadBytes(64);
section5.Alias = Encoding.ASCII.GetString(section5Alias);
byte[] section5Name = data.ReadBytes(64);
section5.Name = Encoding.ASCII.GetString(section5Name);
section5.FolderStartIndex = data.ReadUInt32();
section5.FolderEndIndex = data.ReadUInt32();
section5.FileStartIndex = data.ReadUInt32();
section5.FileEndIndex = data.ReadUInt32();
section5.FolderRootIndex = data.ReadUInt32();
return section5;
}
/// <summary>
/// Parse a Stream into an SGA folder version 4
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="majorVersion">SGA major version</param>
/// <returns>Filled SGA folder version 4 on success, null on error</returns>
private static Folder4 ParseFolder4(Stream data)
{
Folder4 folder4 = new Folder4();
folder4.NameOffset = data.ReadUInt32();
folder4.FolderStartIndex =data.ReadUInt16();
folder4.FolderEndIndex =data.ReadUInt16();
folder4.FileStartIndex =data.ReadUInt16();
folder4.FileEndIndex =data.ReadUInt16();
return folder4;
}
/// <summary>
/// Parse a Stream into an SGA folder version 5
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="majorVersion">SGA major version</param>
/// <returns>Filled SGA folder version 5 on success, null on error</returns>
private static Folder5 ParseFolder5(Stream data)
{
Folder5 folder5 = new Folder5();
folder5.NameOffset = data.ReadUInt32();
folder5.FolderStartIndex = data.ReadUInt32();
folder5.FolderEndIndex = data.ReadUInt32();
folder5.FileStartIndex = data.ReadUInt32();
folder5.FileEndIndex = data.ReadUInt32();
return folder5;
}
/// <summary>
/// Parse a Stream into an SGA file version 4
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="majorVersion">SGA major version</param>
/// <returns>Filled SGA file version 4 on success, null on error</returns>
private static File4 ParseFile4(Stream data)
{
File4 file4 = new File4();
file4.NameOffset = data.ReadUInt32();
file4.Offset = data.ReadUInt32();
file4.SizeOnDisk = data.ReadUInt32();
file4.Size = data.ReadUInt32();
file4.TimeModified = data.ReadUInt32();
file4.Dummy0 = data.ReadByteValue();
file4.Type = data.ReadByteValue();
return file4;
}
/// <summary>
/// Parse a Stream into an SGA file version 6
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="majorVersion">SGA major version</param>
/// <returns>Filled SGA file version 6 on success, null on error</returns>
private static File6 ParseFile6(Stream data)
{
File6 file6 = new File6();
file6.NameOffset = data.ReadUInt32();
file6.Offset = data.ReadUInt32();
file6.SizeOnDisk = data.ReadUInt32();
file6.Size = data.ReadUInt32();
file6.TimeModified = data.ReadUInt32();
file6.Dummy0 = data.ReadByteValue();
file6.Type = data.ReadByteValue();
file6.CRC32 = data.ReadUInt32();
return file6;
}
/// <summary>
/// Parse a Stream into an SGA file version 7
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="majorVersion">SGA major version</param>
/// <returns>Filled SGA file version 7 on success, null on error</returns>
private static File7 ParseFile7(Stream data)
{
File7 file7 = new File7();
file7.NameOffset = data.ReadUInt32();
file7.Offset = data.ReadUInt32();
file7.SizeOnDisk = data.ReadUInt32();
file7.Size = data.ReadUInt32();
file7.TimeModified = data.ReadUInt32();
file7.Dummy0 = data.ReadByteValue();
file7.Type = data.ReadByteValue();
file7.CRC32 = data.ReadUInt32();
file7.HashOffset = data.ReadUInt32();
return file7;
}
#endregion
}
}

View File

@@ -0,0 +1,179 @@
using System.IO;
using System.Text;
using BurnOutSharp.Models.VBSP;
using BurnOutSharp.Utilities;
namespace BurnOutSharp.Builders
{
public static class VBSP
{
#region Constants
/// <summary>
/// Total number of lumps in the package
/// </summary>
public const int HL_VBSP_LUMP_COUNT = 64;
/// <summary>
/// Index for the entities lump
/// </summary>
public const int HL_VBSP_LUMP_ENTITIES = 0;
/// <summary>
/// Idnex for the pakfile lump
/// </summary>
public const int HL_VBSP_LUMP_PAKFILE = 40;
/// <summary>
/// Zip local file header signature as an integer
/// </summary>
public const int HL_VBSP_ZIP_LOCAL_FILE_HEADER_SIGNATURE = 0x04034b50;
/// <summary>
/// Zip file header signature as an integer
/// </summary>
public const int HL_VBSP_ZIP_FILE_HEADER_SIGNATURE = 0x02014b50;
/// <summary>
/// Zip end of central directory record signature as an integer
/// </summary>
public const int HL_VBSP_ZIP_END_OF_CENTRAL_DIRECTORY_RECORD_SIGNATURE = 0x06054b50;
/// <summary>
/// Length of a ZIP checksum in bytes
/// </summary>
public const int HL_VBSP_ZIP_CHECKSUM_LENGTH = 0x00008000;
#endregion
#region Byte Data
/// <summary>
/// Parse a byte array into a Half-Life 2 Level
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled Half-Life 2 Level on success, null on error</returns>
public static Models.VBSP.File ParseFile(byte[] data, int offset)
{
// If the data is invalid
if (data == null)
return null;
// If the offset is out of bounds
if (offset < 0 || offset >= data.Length)
return null;
// Create a memory stream and parse that
MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset);
return ParseFile(dataStream);
}
#endregion
#region Stream Data
/// <summary>
/// Parse a Stream into a Half-Life 2 Level
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life 2 Level on success, null on error</returns>
public static Models.VBSP.File ParseFile(Stream data)
{
// If the data is invalid
if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
return null;
// If the offset is out of bounds
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
long initialOffset = data.Position;
// Create a new Half-Life 2 Level to fill
var file = new Models.VBSP.File();
#region Header
// Try to parse the header
var header = ParseHeader(data);
if (header == null)
return null;
// Set the package header
file.Header = header;
#endregion
return file;
}
/// <summary>
/// Parse a Stream into a Half-Life 2 Level header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life 2 Level header on success, null on error</returns>
private static Header ParseHeader(Stream data)
{
// TODO: Use marshalling here instead of building
Header header = new Header();
byte[] signature = data.ReadBytes(4);
header.Signature = Encoding.ASCII.GetString(signature);
if (header.Signature != "VBSP")
return null;
header.Version = data.ReadInt32();
if ((header.Version < 19 || header.Version > 22) && header.Version != 0x00040014)
return null;
header.Lumps = new Lump[HL_VBSP_LUMP_COUNT];
for (int i = 0; i < HL_VBSP_LUMP_COUNT; i++)
{
header.Lumps[i] = ParseLump(data, header.Version);
}
header.MapRevision = data.ReadInt32();
return header;
}
/// <summary>
/// Parse a Stream into a Half-Life 2 Level lump
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="version">VBSP version</param>
/// <returns>Filled Half-Life 2 Level lump on success, null on error</returns>
private static Lump ParseLump(Stream data, int version)
{
// TODO: Use marshalling here instead of building
Lump lump = new Lump();
lump.Offset = data.ReadUInt32();
lump.Length = data.ReadUInt32();
lump.Version = data.ReadUInt32();
lump.FourCC = new char[4];
for (int i = 0; i < 4; i++)
{
lump.FourCC[i] = (char)data.ReadByte();
}
// This block was commented out because test VBSPs with header
// version 21 had the values in the "right" order already and
// were causing decompression issues
//if (version >= 21 && version != 0x00040014)
//{
// uint temp = lump.Version;
// lump.Version = lump.Offset;
// lump.Offset = lump.Length;
// lump.Length = temp;
//}
return lump;
}
#endregion
}
}

View File

@@ -0,0 +1,318 @@
using System.Collections.Generic;
using System.IO;
using System.Text;
using BurnOutSharp.Models.VPK;
using BurnOutSharp.Utilities;
namespace BurnOutSharp.Builders
{
public static class VPK
{
#region Constants
/// <summary>
/// VPK header signature as an integer
/// </summary>
public const int HL_VPK_SIGNATURE = 0x55aa1234;
/// <summary>
/// Index indicating that there is no archive
/// </summary>
public const int HL_VPK_NO_ARCHIVE = 0x7fff;
/// <summary>
/// Length of a VPK checksum in bytes
/// </summary>
public const int HL_VPK_CHECKSUM_LENGTH = 0x00008000;
#endregion
#region Byte Data
/// <summary>
/// Parse a byte array into a Valve Package
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled Valve Package on success, null on error</returns>
public static Models.VPK.File ParseFile(byte[] data, int offset)
{
// If the data is invalid
if (data == null)
return null;
// If the offset is out of bounds
if (offset < 0 || offset >= data.Length)
return null;
// Create a memory stream and parse that
MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset);
return ParseFile(dataStream);
}
#endregion
#region Stream Data
/// <summary>
/// Parse a Stream into a Valve Package
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Valve Package on success, null on error</returns>
public static Models.VPK.File ParseFile(Stream data)
{
// If the data is invalid
if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
return null;
// If the offset is out of bounds
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
long initialOffset = data.Position;
// Create a new Valve Package to fill
var file = new Models.VPK.File();
#region Header
// Try to parse the header
// The original version had no signature.
var header = ParseHeader(data);
// Set the package header
file.Header = header;
#endregion
#region Extended Header
if (header?.Version == 2)
{
// Try to parse the extended header
var extendedHeader = ParseExtendedHeader(data);
if (extendedHeader == null)
return null;
// Set the package extended header
file.ExtendedHeader = extendedHeader;
}
#endregion
#region Archive Hashes
if (header?.Version == 2 && file.ExtendedHeader != null && file.ExtendedHeader.ArchiveHashLength > 0)
{
// Create the archive hashes list
var archiveHashes = new List<ArchiveHash>();
// Cache the current offset
initialOffset = data.Position;
// Try to parse the directory items
while (data.Position < initialOffset + file.ExtendedHeader.ArchiveHashLength)
{
var archiveHash = ParseArchiveHash(data);
archiveHashes.Add(archiveHash);
}
file.ArchiveHashes = archiveHashes.ToArray();
}
#endregion
#region Directory Items
// Create the directory items tree
var directoryItems = ParseDirectoryItemTree(data);
// Set the directory items
file.DirectoryItems = directoryItems;
#endregion
return file;
}
/// <summary>
/// Parse a Stream into a Valve Package header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Valve Package header on success, null on error</returns>
private static Header ParseHeader(Stream data)
{
// TODO: Use marshalling here instead of building
Header header = new Header();
header.Signature = data.ReadUInt32();
if (header.Signature != HL_VPK_SIGNATURE)
return null;
header.Version = data.ReadUInt32();
if (header.Version > 2)
return null;
header.DirectoryLength = data.ReadUInt32();
return header;
}
/// <summary>
/// Parse a Stream into a Valve Package extended header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Valve Package extended header on success, null on error</returns>
private static ExtendedHeader ParseExtendedHeader(Stream data)
{
// TODO: Use marshalling here instead of building
ExtendedHeader extendedHeader = new ExtendedHeader();
extendedHeader.Dummy0 = data.ReadUInt32();
extendedHeader.ArchiveHashLength = data.ReadUInt32();
extendedHeader.ExtraLength = data.ReadUInt32();
extendedHeader.Dummy1 = data.ReadUInt32();
return extendedHeader;
}
/// <summary>
/// Parse a Stream into a Valve Package archive hash
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Valve Package archive hash on success, null on error</returns>
private static ArchiveHash ParseArchiveHash(Stream data)
{
// TODO: Use marshalling here instead of building
ArchiveHash archiveHash = new ArchiveHash();
archiveHash.ArchiveIndex = data.ReadUInt32();
archiveHash.ArchiveOffset = data.ReadUInt32();
archiveHash.Length = data.ReadUInt32();
archiveHash.Hash = data.ReadBytes(0x10);
return archiveHash;
}
/// <summary>
/// Parse a Stream into a Valve Package directory item tree
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Valve Package directory item tree on success, null on error</returns>
private static DirectoryItem[] ParseDirectoryItemTree(Stream data)
{
// Create the directory items list
var directoryItems = new List<DirectoryItem>();
while (true)
{
// Get the extension
string extensionString = data.ReadString(Encoding.ASCII);
if (string.IsNullOrEmpty(extensionString))
break;
while (true)
{
// Get the path
string pathString = data.ReadString(Encoding.ASCII);
if (string.IsNullOrEmpty(pathString))
break;
while (true)
{
// Get the name
string nameString = data.ReadString(Encoding.ASCII);
if (string.IsNullOrEmpty(nameString))
break;
// Get the directory item
var directoryItem = ParseDirectoryItem(data, extensionString, pathString, nameString);
// Add the directory item
directoryItems.Add(directoryItem);
}
}
}
return directoryItems.ToArray();
}
/// <summary>
/// Parse a Stream into a Valve Package directory item
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Valve Package directory item on success, null on error</returns>
private static DirectoryItem ParseDirectoryItem(Stream data, string extension, string path, string name)
{
DirectoryItem directoryItem = new DirectoryItem();
directoryItem.Extension = extension;
directoryItem.Path = path;
directoryItem.Name = name;
// Get the directory entry
var directoryEntry = ParseDirectoryEntry(data);
// Set the directory entry
directoryItem.DirectoryEntry = directoryEntry;
// Get the preload data pointer
long preloadDataPointer = -1; int preloadDataLength = -1;
if (directoryEntry.ArchiveIndex == HL_VPK_NO_ARCHIVE && directoryEntry.EntryLength > 0)
{
preloadDataPointer = directoryEntry.EntryOffset;
preloadDataLength = (int)directoryEntry.EntryLength;
}
else if (directoryEntry.PreloadBytes > 0)
{
preloadDataPointer = data.Position;
preloadDataLength = directoryEntry.PreloadBytes;
}
// If we had a valid preload data pointer
byte[] preloadData = null;
if (preloadDataPointer >= 0 && preloadDataLength > 0)
{
// Cache the current offset
long initialOffset = data.Position;
// Seek to the preload data offset
data.Seek(preloadDataPointer, SeekOrigin.Begin);
// Read the preload data
preloadData = data.ReadBytes(preloadDataLength);
// Seek back to the original offset
data.Seek(initialOffset, SeekOrigin.Begin);
}
// Set the preload data
directoryItem.PreloadData = preloadData;
return directoryItem;
}
/// <summary>
/// Parse a Stream into a Valve Package directory entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Valve Package directory entry on success, null on error</returns>
private static DirectoryEntry ParseDirectoryEntry(Stream data)
{
// TODO: Use marshalling here instead of building
DirectoryEntry directoryEntry = new DirectoryEntry();
directoryEntry.CRC = data.ReadUInt32();
directoryEntry.PreloadBytes = data.ReadUInt16();
directoryEntry.ArchiveIndex = data.ReadUInt16();
directoryEntry.EntryOffset = data.ReadUInt32();
directoryEntry.EntryLength = data.ReadUInt32();
directoryEntry.Dummy0 = data.ReadUInt16();
return directoryEntry;
}
#endregion
}
}

View File

@@ -0,0 +1,265 @@
using System.IO;
using System.Text;
using BurnOutSharp.Models.WAD;
using BurnOutSharp.Utilities;
namespace BurnOutSharp.Builders
{
public static class WAD
{
#region Byte Data
/// <summary>
/// Parse a byte array into a Half-Life Texture Package
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled Half-Life Texture Package on success, null on error</returns>
public static Models.WAD.File ParseFile(byte[] data, int offset)
{
// If the data is invalid
if (data == null)
return null;
// If the offset is out of bounds
if (offset < 0 || offset >= data.Length)
return null;
// Create a memory stream and parse that
MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset);
return ParseFile(dataStream);
}
#endregion
#region Stream Data
/// <summary>
/// Parse a Stream into a Half-Life Texture Package
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Texture Package on success, null on error</returns>
public static Models.WAD.File ParseFile(Stream data)
{
// If the data is invalid
if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
return null;
// If the offset is out of bounds
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
long initialOffset = data.Position;
// Create a new Half-Life Texture Package to fill
var file = new Models.WAD.File();
#region Header
// Try to parse the header
var header = ParseHeader(data);
if (header == null)
return null;
// Set the package header
file.Header = header;
#endregion
#region Lumps
// Get the lump offset
uint lumpOffset = header.LumpOffset;
if (lumpOffset < 0 || lumpOffset >= data.Length)
return null;
// Seek to the lump offset
data.Seek(lumpOffset, SeekOrigin.Begin);
// Create the lump array
file.Lumps = new Lump[header.LumpCount];
for (int i = 0; i < header.LumpCount; i++)
{
var lump = ParseLump(data);
file.Lumps[i] = lump;
}
#endregion
#region Lump Infos
// Create the lump info array
file.LumpInfos = new LumpInfo[header.LumpCount];
for (int i = 0; i < header.LumpCount; i++)
{
var lump = file.Lumps[i];
if (lump.Compression != 0)
{
file.LumpInfos[i] = null;
continue;
}
// Get the lump info offset
uint lumpInfoOffset = lump.Offset;
if (lumpInfoOffset < 0 || lumpInfoOffset >= data.Length)
{
file.LumpInfos[i] = null;
continue;
}
// Seek to the lump info offset
data.Seek(lumpInfoOffset, SeekOrigin.Begin);
// Try to parse the lump info -- TODO: Do we ever set the mipmap level?
var lumpInfo = ParseLumpInfo(data, lump.Type);
file.LumpInfos[i] = lumpInfo;
}
#endregion
return file;
}
/// <summary>
/// Parse a Stream into a Half-Life Texture Package header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Texture Package header on success, null on error</returns>
private static Header ParseHeader(Stream data)
{
// TODO: Use marshalling here instead of building
Header header = new Header();
byte[] signature = data.ReadBytes(4);
header.Signature = Encoding.ASCII.GetString(signature);
if (header.Signature != "WAD3")
return null;
header.LumpCount = data.ReadUInt32();
header.LumpOffset = data.ReadUInt32();
return header;
}
/// <summary>
/// Parse a Stream into a Half-Life Texture Package lump
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Half-Life Texture Package lump on success, null on error</returns>
private static Lump ParseLump(Stream data)
{
// TODO: Use marshalling here instead of building
Lump lump = new Lump();
lump.Offset = data.ReadUInt32();
lump.DiskLength = data.ReadUInt32();
lump.Length = data.ReadUInt32();
lump.Type = data.ReadByteValue();
lump.Compression = data.ReadByteValue();
lump.Padding0 = data.ReadByteValue();
lump.Padding1 = data.ReadByteValue();
byte[] name = data.ReadBytes(16);
lump.Name = Encoding.ASCII.GetString(name);
return lump;
}
/// <summary>
/// Parse a Stream into a Half-Life Texture Package lump info
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="type">Lump type</param>
/// <param name="mipmap">Mipmap level</param>
/// <returns>Filled Half-Life Texture Package lump info on success, null on error</returns>
private static LumpInfo ParseLumpInfo(Stream data, byte type, uint mipmap = 0)
{
// TODO: Use marshalling here instead of building
LumpInfo lumpInfo = new LumpInfo();
// Cache the initial offset
long initialOffset = data.Position;
// Type 0x42 has no name, type 0x43 does. Are these flags?
if (type == 0x42)
{
if (mipmap > 0)
return null;
lumpInfo.Width = data.ReadUInt32();
lumpInfo.Height = data.ReadUInt32();
lumpInfo.PixelData = data.ReadBytes((int)(lumpInfo.Width * lumpInfo.Height));
lumpInfo.PaletteSize = data.ReadUInt16();
}
else if (type == 0x43)
{
if (mipmap > 3)
return null;
byte[] name = data.ReadBytes(16);
lumpInfo.Name = Encoding.ASCII.GetString(name);
lumpInfo.Width = data.ReadUInt32();
lumpInfo.Height = data.ReadUInt32();
lumpInfo.PixelOffset = data.ReadUInt32();
_ = data.ReadBytes(12); // Unknown data
// Cache the current offset
long currentOffset = data.Position;
// Seek to the pixel data
data.Seek(initialOffset + lumpInfo.PixelOffset, SeekOrigin.Begin);
// Read the pixel data
lumpInfo.PixelData = data.ReadBytes((int)(lumpInfo.Width * lumpInfo.Height));
// Seek back to the offset
data.Seek(currentOffset, SeekOrigin.Begin);
uint pixelSize = lumpInfo.Width * lumpInfo.Height;
// Mipmap data -- TODO: How do we determine this during initial parsing?
switch (mipmap)
{
case 1: _ = data.ReadBytes((int)pixelSize); break;
case 2: _ = data.ReadBytes((int)(pixelSize + (pixelSize / 4))); break;
case 3: _ = data.ReadBytes((int)(pixelSize + (pixelSize / 4) + (pixelSize / 16))); break;
default: return null;
}
_ = data.ReadBytes((int)(pixelSize + (pixelSize / 4) + (pixelSize / 16) + (pixelSize / 64))); // Pixel data
lumpInfo.PaletteSize = data.ReadUInt16();
lumpInfo.PaletteData = data.ReadBytes((int)lumpInfo.PaletteSize * 3);
}
else
{
return null;
}
// Adjust based on mipmap level
switch (mipmap)
{
case 1:
lumpInfo.Width /= 2;
lumpInfo.Height /= 2;
break;
case 2:
lumpInfo.Width /= 4;
lumpInfo.Height /= 4;
break;
case 3:
lumpInfo.Width /= 8;
lumpInfo.Height /= 8;
break;
default:
return null;
}
return lumpInfo;
}
#endregion
}
}

View File

@@ -0,0 +1,261 @@
using System.IO;
using System.Text;
using BurnOutSharp.Models.XZP;
using BurnOutSharp.Utilities;
namespace BurnOutSharp.Builders
{
public static class XZP
{
#region Byte Data
/// <summary>
/// Parse a byte array into a XBox Package File
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled XBox Package File on success, null on error</returns>
public static Models.XZP.File ParseFile(byte[] data, int offset)
{
// If the data is invalid
if (data == null)
return null;
// If the offset is out of bounds
if (offset < 0 || offset >= data.Length)
return null;
// Create a memory stream and parse that
MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset);
return ParseFile(dataStream);
}
#endregion
#region Stream Data
/// <summary>
/// Parse a Stream into a XBox Package File
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled XBox Package File on success, null on error</returns>
public static Models.XZP.File ParseFile(Stream data)
{
// If the data is invalid
if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
return null;
// If the offset is out of bounds
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
long initialOffset = data.Position;
// Create a new XBox Package File to fill
var file = new Models.XZP.File();
#region Header
// Try to parse the header
var header = ParseHeader(data);
if (header == null)
return null;
// Set the package header
file.Header = header;
#endregion
#region Directory Entries
// Create the directory entry array
file.DirectoryEntries = new DirectoryEntry[header.DirectoryEntryCount];
// Try to parse the directory entries
for (int i = 0; i < header.DirectoryEntryCount; i++)
{
var directoryEntry = ParseDirectoryEntry(data);
file.DirectoryEntries[i] = directoryEntry;
}
#endregion
#region Preload Directory Entries
if (header.PreloadBytes > 0)
{
// Create the preload directory entry array
file.PreloadDirectoryEntries = new DirectoryEntry[header.PreloadDirectoryEntryCount];
// Try to parse the preload directory entries
for (int i = 0; i < header.PreloadDirectoryEntryCount; i++)
{
var directoryEntry = ParseDirectoryEntry(data);
file.PreloadDirectoryEntries[i] = directoryEntry;
}
}
#endregion
#region Preload Directory Mappings
if (header.PreloadBytes > 0)
{
// Create the preload directory mapping array
file.PreloadDirectoryMappings = new DirectoryMapping[header.PreloadDirectoryEntryCount];
// Try to parse the preload directory mappings
for (int i = 0; i < header.PreloadDirectoryEntryCount; i++)
{
var directoryMapping = ParseDirectoryMapping(data);
file.PreloadDirectoryMappings[i] = directoryMapping;
}
}
#endregion
#region Directory Items
if (header.DirectoryItemCount > 0)
{
// Get the directory item offset
uint directoryItemOffset = header.DirectoryItemOffset;
if (directoryItemOffset < 0 || directoryItemOffset >= data.Length)
return null;
// Seek to the directory items
data.Seek(directoryItemOffset, SeekOrigin.Begin);
// Create the directory item array
file.DirectoryItems = new DirectoryItem[header.DirectoryItemCount];
// Try to parse the directory items
for (int i = 0; i < header.PreloadDirectoryEntryCount; i++)
{
var directoryItem = ParseDirectoryItem(data);
file.DirectoryItems[i] = directoryItem;
}
}
#endregion
#region Footer
// Seek to the footer
data.Seek(-8, SeekOrigin.End);
// Try to parse the footer
var footer = ParseFooter(data);
if (footer == null)
return null;
// Set the package footer
file.Footer = footer;
#endregion
return file;
}
/// <summary>
/// Parse a Stream into a XBox Package File header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled XBox Package File header on success, null on error</returns>
private static Header ParseHeader(Stream data)
{
// TODO: Use marshalling here instead of building
Header header = new Header();
byte[] signature = data.ReadBytes(4);
header.Signature = Encoding.ASCII.GetString(signature);
if (header.Signature != "piZx")
return null;
header.Version = data.ReadUInt32();
if (header.Version != 6)
return null;
header.PreloadDirectoryEntryCount = data.ReadUInt32();
header.DirectoryEntryCount = data.ReadUInt32();
header.PreloadBytes = data.ReadUInt32();
header.HeaderLength = data.ReadUInt32();
header.DirectoryItemCount = data.ReadUInt32();
header.DirectoryItemOffset = data.ReadUInt32();
header.DirectoryItemLength = data.ReadUInt32();
return header;
}
/// <summary>
/// Parse a Stream into a XBox Package File directory entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled XBox Package File directory entry on success, null on error</returns>
private static DirectoryEntry ParseDirectoryEntry(Stream data)
{
// TODO: Use marshalling here instead of building
DirectoryEntry directoryEntry = new DirectoryEntry();
directoryEntry.FileNameCRC = data.ReadUInt32();
directoryEntry.EntryLength = data.ReadUInt32();
directoryEntry.EntryOffset = data.ReadUInt32();
return directoryEntry;
}
/// <summary>
/// Parse a Stream into a XBox Package File directory mapping
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled XBox Package File directory mapping on success, null on error</returns>
private static DirectoryMapping ParseDirectoryMapping(Stream data)
{
// TODO: Use marshalling here instead of building
DirectoryMapping directoryMapping = new DirectoryMapping();
directoryMapping.PreloadDirectoryEntryIndex = data.ReadUInt16();
return directoryMapping;
}
/// <summary>
/// Parse a Stream into a XBox Package File directory item
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled XBox Package File directory item on success, null on error</returns>
private static DirectoryItem ParseDirectoryItem(Stream data)
{
// TODO: Use marshalling here instead of building
DirectoryItem directoryItem = new DirectoryItem();
directoryItem.FileNameCRC = data.ReadUInt32();
directoryItem.NameOffset = data.ReadUInt32();
directoryItem.TimeCreated = data.ReadUInt32();
return directoryItem;
}
/// <summary>
/// Parse a Stream into a XBox Package File footer
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled XBox Package File footer on success, null on error</returns>
private static Footer ParseFooter(Stream data)
{
// TODO: Use marshalling here instead of building
Footer footer = new Footer();
footer.FileLength = data.ReadUInt32();
byte[] signature = data.ReadBytes(4);
footer.Signature = Encoding.ASCII.GetString(signature);
if (footer.Signature != "tFzX")
return null;
return footer;
}
#endregion
}
}

View File

@@ -0,0 +1,35 @@
namespace BurnOutSharp.Models.BMP
{
/// <summary>
/// The BITMAPFILEHEADER structure contains information about the type, size,
/// and layout of a file that contains a DIB.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapfileheader"/>
public sealed class BITMAPFILEHEADER
{
/// <summary>
/// The file type; must be BM.
/// </summary>
public ushort Type;
/// <summary>
/// The size, in bytes, of the bitmap file.
/// </summary>
public uint Size;
/// <summary>
/// Reserved; must be zero.
/// </summary>
public ushort Reserved1;
/// <summary>
/// Reserved; must be zero.
/// </summary>
public ushort Reserved2;
/// <summary>
/// The offset, in bytes, from the beginning of the BITMAPFILEHEADER structure to the bitmap bits.
/// </summary>
public uint OffBits;
}
}

View File

@@ -0,0 +1,94 @@
namespace BurnOutSharp.Models.BMP
{
/// <summary>
/// The BITMAPINFOHEADER structure contains information about the dimensions and
/// color format of a device-independent bitmap (DIB).
/// </summary>
public sealed class BITMAPINFOHEADER
{
/// <summary>
/// Specifies the number of bytes required by the structure. This value does
/// not include the size of the color table or the size of the color masks,
/// if they are appended to the end of structure.
/// </summary>
public uint Size;
/// <summary>
/// Specifies the width of the bitmap, in pixels.
/// </summary>
public int Width;
/// <summary>
/// Specifies the height of the bitmap, in pixels.
/// - For uncompressed RGB bitmaps, if biHeight is positive, the bitmap is a
/// bottom-up DIB with the origin at the lower left corner. If biHeight is
/// negative, the bitmap is a top-down DIB with the origin at the upper left
/// corner.
/// - For YUV bitmaps, the bitmap is always top-down, regardless of the sign of
/// biHeight. Decoders should offer YUV formats with positive biHeight, but for
/// backward compatibility they should accept YUV formats with either positive
/// or negative biHeight.
/// - For compressed formats, biHeight must be positive, regardless of image orientation.
/// </summary>
public int Height;
/// <summary>
/// Specifies the number of planes for the target device. This value must be set to 1.
/// </summary>
public ushort Planes;
/// <summary>
/// Specifies the number of bits per pixel (bpp). For uncompressed formats, this value
/// is the average number of bits per pixel. For compressed formats, this value is the
/// implied bit depth of the uncompressed image, after the image has been decoded.
/// </summary>
public ushort BitCount;
/// <summary>
/// For compressed video and YUV formats, this member is a FOURCC code, specified as a
/// DWORD in little-endian order. For example, YUYV video has the FOURCC 'VYUY' or
/// 0x56595559. For more information, see FOURCC Codes.
///
/// For uncompressed RGB formats, the following values are possible:
/// - BI_RGB: Uncompressed RGB.
/// - BI_BITFIELDS: Uncompressed RGB with color masks. Valid for 16-bpp and 32-bpp bitmaps.
///
/// Note that BI_JPG and BI_PNG are not valid video formats.
///
/// For 16-bpp bitmaps, if biCompression equals BI_RGB, the format is always RGB 555.
/// If biCompression equals BI_BITFIELDS, the format is either RGB 555 or RGB 565. Use
/// the subtype GUID in the AM_MEDIA_TYPE structure to determine the specific RGB type.
/// </summary>
public uint Compression;
/// <summary>
/// Specifies the size, in bytes, of the image. This can be set to 0 for uncompressed
/// RGB bitmaps.
/// </summary>
public uint SizeImage;
/// <summary>
/// Specifies the horizontal resolution, in pixels per meter, of the target device for
/// the bitmap.
/// </summary>
public int XPelsPerMeter;
/// <summary>
/// Specifies the vertical resolution, in pixels per meter, of the target device for
/// the bitmap.
/// </summary>
public int YPelsPerMeter;
/// <summary>
/// Specifies the number of color indices in the color table that are actually used by
/// the bitmap.
/// </summary>
public uint ClrUsed;
/// <summary>
/// Specifies the number of color indices that are considered important for displaying
/// the bitmap. If this value is zero, all colors are important.
/// </summary>
public uint ClrImportant;
}
}

View File

@@ -0,0 +1,24 @@
namespace BurnOutSharp.Models.BSP
{
/// <summary>
/// Half-Life Level
/// </summary>
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/BSPFile.h"/>
public sealed class File
{
/// <summary>
/// Header data
/// </summary>
public Header Header { get; set; }
/// <summary>
/// Lumps
/// </summary>
public Lump[] Lumps { get; set; }
/// <summary>
/// Texture header data
/// </summary>
public TextureHeader TextureHeader { get; set; }
}
}

View File

@@ -0,0 +1,11 @@
namespace BurnOutSharp.Models.BSP
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/BSPFile.h"/>
public sealed class Header
{
/// <summary>
/// Version
/// </summary>
public uint Version;
}
}

View File

@@ -0,0 +1,16 @@
namespace BurnOutSharp.Models.BSP
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/BSPFile.h"/>
public sealed class Lump
{
/// <summary>
/// Offset
/// </summary>
public uint Offset;
/// <summary>
/// Length
/// </summary>
public uint Length;
}
}

View File

@@ -0,0 +1,26 @@
namespace BurnOutSharp.Models.BSP
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/BSPFile.h"/>
public sealed class Texture
{
/// <summary>
/// Name
/// </summary>
public string Name;
/// <summary>
/// Width
/// </summary>
public uint Width;
/// <summary>
/// Height
/// </summary>
public uint Height;
/// <summary>
/// Offsets
/// </summary>
public uint[] Offsets;
}
}

View File

@@ -0,0 +1,16 @@
namespace BurnOutSharp.Models.BSP
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/BSPFile.h"/>
public sealed class TextureHeader
{
/// <summary>
/// Texture count
/// </summary>
public uint TextureCount;
/// <summary>
/// Offsets
/// </summary>
public uint[] Offsets;
}
}

View File

@@ -0,0 +1,41 @@
namespace BurnOutSharp.Models.GCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/GCFFile.h"/>
public sealed class BlockEntry
{
/// <summary>
/// Flags for the block entry. 0x200F0000 == Not used.
/// </summary>
public uint EntryFlags;
/// <summary>
/// The offset for the data contained in this block entry in the file.
/// </summary>
public uint FileDataOffset;
/// <summary>
/// The length of the data in this block entry.
/// </summary>
public uint FileDataSize;
/// <summary>
/// The offset to the first data block of this block entry's data.
/// </summary>
public uint FirstDataBlockIndex;
/// <summary>
/// The next block entry in the series. (N/A if == BlockCount.)
/// </summary>
public uint NextBlockEntryIndex;
/// <summary>
/// The previous block entry in the series. (N/A if == BlockCount.)
/// </summary>
public uint PreviousBlockEntryIndex;
/// <summary>
/// The offset of the block entry in the directory.
/// </summary>
public uint DirectoryIndex;
}
}

View File

@@ -0,0 +1,46 @@
namespace BurnOutSharp.Models.GCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/GCFFile.h"/>
public sealed class BlockEntryHeader
{
/// <summary>
/// Number of data blocks.
/// </summary>
public uint BlockCount;
/// <summary>
/// Number of data blocks that point to data.
/// </summary>
public uint BlocksUsed;
/// <summary>
/// Reserved
/// </summary>
public uint Dummy0;
/// <summary>
/// Reserved
/// </summary>
public uint Dummy1;
/// <summary>
/// Reserved
/// </summary>
public uint Dummy2;
/// <summary>
/// Reserved
/// </summary>
public uint Dummy3;
/// <summary>
/// Reserved
/// </summary>
public uint Dummy4;
/// <summary>
/// Header checksum.
/// </summary>
public uint Checksum;
}
}

View File

@@ -0,0 +1,19 @@
namespace BurnOutSharp.Models.GCF
{
/// <remarks>
/// Part of version 5 but not version 6.
/// </remarks>
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/GCFFile.h"/>
public sealed class BlockEntryMap
{
/// <summary>
/// The previous block entry. (N/A if == BlockCount.)
/// </summary>
public uint PreviousBlockEntryIndex;
/// <summary>
/// The next block entry. (N/A if == BlockCount.)
/// </summary>
public uint NextBlockEntryIndex;
}
}

View File

@@ -0,0 +1,34 @@
namespace BurnOutSharp.Models.GCF
{
/// <remarks>
/// Part of version 5 but not version 6.
/// </remarks>
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/GCFFile.h"/>
public sealed class BlockEntryMapHeader
{
/// <summary>
/// Number of data blocks.
/// </summary>
public uint BlockCount;
/// <summary>
/// Index of the first block entry.
/// </summary>
public uint FirstBlockEntryIndex;
/// <summary>
/// Index of the last block entry.
/// </summary>
public uint LastBlockEntryIndex;
/// <summary>
/// Reserved
/// </summary>
public uint Dummy0;
/// <summary>
/// Header checksum.
/// </summary>
public uint Checksum;
}
}

View File

@@ -0,0 +1,11 @@
namespace BurnOutSharp.Models.GCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/GCFFile.h"/>
public sealed class ChecksumEntry
{
/// <summary>
/// Checksum.
/// </summary>
public uint Checksum;
}
}

View File

@@ -0,0 +1,16 @@
namespace BurnOutSharp.Models.GCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/GCFFile.h"/>
public sealed class ChecksumHeader
{
/// <summary>
/// Always 0x00000001
/// </summary>
public uint Dummy0;
/// <summary>
/// Size of LPGCFCHECKSUMHEADER & LPGCFCHECKSUMMAPHEADER & in bytes.
/// </summary>
public uint ChecksumSize;
}
}

View File

@@ -0,0 +1,16 @@
namespace BurnOutSharp.Models.GCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/GCFFile.h"/>
public sealed class ChecksumMapEntry
{
/// <summary>
/// Number of checksums.
/// </summary>
public uint ChecksumCount;
/// <summary>
/// Index of first checksum.
/// </summary>
public uint FirstChecksumIndex;
}
}

View File

@@ -0,0 +1,26 @@
namespace BurnOutSharp.Models.GCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/GCFFile.h"/>
public sealed class ChecksumMapHeader
{
/// <summary>
/// Always 0x14893721
/// </summary>
public uint Dummy0;
/// <summary>
/// Always 0x00000001
/// </summary>
public uint Dummy1;
/// <summary>
/// Number of items.
/// </summary>
public uint ItemCount;
/// <summary>
/// Number of checksums.
/// </summary>
public uint ChecksumCount;
}
}

View File

@@ -0,0 +1,36 @@
namespace BurnOutSharp.Models.GCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/GCFFile.h"/>
public sealed class DataBlockHeader
{
/// <summary>
/// GCF file version. This field is not part of all file versions.
/// </summary>
public uint LastVersionPlayed;
/// <summary>
/// Number of data blocks.
/// </summary>
public uint BlockCount;
/// <summary>
/// Size of each data block in bytes.
/// </summary>
public uint BlockSize;
/// <summary>
/// Offset to first data block.
/// </summary>
public uint FirstBlockOffset;
/// <summary>
/// Number of data blocks that contain data.
/// </summary>
public uint BlocksUsed;
/// <summary>
/// Header checksum.
/// </summary>
public uint Checksum;
}
}

View File

@@ -0,0 +1,11 @@
namespace BurnOutSharp.Models.GCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/GCFFile.h"/>
public sealed class DirectoryCopyEntry
{
/// <summary>
/// Index of the directory item.
/// </summary>
public uint DirectoryIndex;
}
}

View File

@@ -0,0 +1,41 @@
namespace BurnOutSharp.Models.GCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/GCFFile.h"/>
public sealed class DirectoryEntry
{
/// <summary>
/// Offset to the directory item name from the end of the directory items.
/// </summary>
public uint NameOffset;
/// <summary>
/// Size of the item. (If file, file size. If folder, num items.)
/// </summary>
public uint ItemSize;
/// <summary>
/// Checksome index. (0xFFFFFFFF == None).
/// </summary>
public uint ChecksumIndex;
/// <summary>
/// Flags for the directory item. (0x00000000 == Folder).
/// </summary>
public uint DirectoryFlags;
/// <summary>
/// Index of the parent directory item. (0xFFFFFFFF == None).
/// </summary>
public uint ParentIndex;
/// <summary>
/// Index of the next directory item. (0x00000000 == None).
/// </summary>
public uint NextIndex;
/// <summary>
/// Index of the first directory item. (0x00000000 == None).
/// </summary>
public uint FirstIndex;
}
}

View File

@@ -0,0 +1,76 @@
namespace BurnOutSharp.Models.GCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/GCFFile.h"/>
public sealed class DirectoryHeader
{
/// <summary>
/// Always 0x00000004
/// </summary>
public uint Dummy0;
/// <summary>
/// Cache ID.
/// </summary>
public uint CacheID;
/// <summary>
/// GCF file version.
/// </summary>
public uint LastVersionPlayed;
/// <summary>
/// Number of items in the directory.
/// </summary>
public uint ItemCount;
/// <summary>
/// Number of files in the directory.
/// </summary>
public uint FileCount;
/// <summary>
/// Always 0x00008000. Data per checksum?
/// </summary>
public uint Dummy1;
/// <summary>
/// Size of lpGCFDirectoryEntries & lpGCFDirectoryNames & lpGCFDirectoryInfo1Entries & lpGCFDirectoryInfo2Entries & lpGCFDirectoryCopyEntries & lpGCFDirectoryLocalEntries in bytes.
/// </summary>
public uint DirectorySize;
/// <summary>
/// Size of the directory names in bytes.
/// </summary>
public uint NameSize;
/// <summary>
/// Number of Info1 entires.
/// </summary>
public uint Info1Count;
/// <summary>
/// Number of files to copy.
/// </summary>
public uint CopyCount;
/// <summary>
/// Number of files to keep local.
/// </summary>
public uint LocalCount;
/// <summary>
/// Reserved
/// </summary>
public uint Dummy2;
/// <summary>
/// Reserved
/// </summary>
public uint Dummy3;
/// <summary>
/// Header checksum.
/// </summary>
public uint Checksum;
}
}

View File

@@ -0,0 +1,11 @@
namespace BurnOutSharp.Models.GCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/GCFFile.h"/>
public sealed class DirectoryInfo1Entry
{
/// <summary>
/// Reserved
/// </summary>
public uint Dummy0;
}
}

View File

@@ -0,0 +1,11 @@
namespace BurnOutSharp.Models.GCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/GCFFile.h"/>
public sealed class DirectoryInfo2Entry
{
/// <summary>
/// Reserved
/// </summary>
public uint Dummy0;
}
}

View File

@@ -0,0 +1,11 @@
namespace BurnOutSharp.Models.GCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/GCFFile.h"/>
public sealed class DirectoryLocalEntry
{
/// <summary>
/// Index of the directory item.
/// </summary>
public uint DirectoryIndex;
}
}

View File

@@ -0,0 +1,11 @@
namespace BurnOutSharp.Models.GCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/GCFFile.h"/>
public sealed class DirectoryMapEntry
{
/// <summary>
/// Index of the first data block. (N/A if == BlockCount.)
/// </summary>
public uint FirstBlockIndex;
}
}

View File

@@ -0,0 +1,19 @@
namespace BurnOutSharp.Models.GCF
{
/// <remarks>
/// Added in version 4 or version 5.
/// </remarks>
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/GCFFile.h"/>
public sealed class DirectoryMapHeader
{
/// <summary>
/// Always 0x00000001
/// </summary>
public uint Dummy0;
/// <summary>
/// Always 0x00000000
/// </summary>
public uint Dummy1;
}
}

View File

@@ -0,0 +1,116 @@
namespace BurnOutSharp.Models.GCF
{
/// <summary>
/// Half-Life Game Cache File
/// </summary>
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/GCFFile.h"/>
public sealed class File
{
/// <summary>
/// Header data
/// </summary>
public Header Header { get; set; }
/// <summary>
/// Block entry header data
/// </summary>
public BlockEntryHeader BlockEntryHeader { get; set; }
/// <summary>
/// Block entries data
/// </summary>
public BlockEntry[] BlockEntries { get; set; }
/// <summary>
/// Fragmentation map header data
/// </summary>
public FragmentationMapHeader FragmentationMapHeader { get; set; }
/// <summary>
/// Fragmentation map data
/// </summary>
public FragmentationMap[] FragmentationMaps { get; set; }
/// <summary>
/// Block entry map header data
/// </summary>
/// <remarks>Part of version 5 but not version 6.</remarks>
public BlockEntryMapHeader BlockEntryMapHeader { get; set; }
/// <summary>
/// Block entry map data
/// </summary>
/// <remarks>Part of version 5 but not version 6.</remarks>
public BlockEntryMap[] BlockEntryMaps { get; set; }
/// <summary>
/// Directory header data
/// </summary>
public DirectoryHeader DirectoryHeader { get; set; }
/// <summary>
/// Directory entries data
/// </summary>
public DirectoryEntry[] DirectoryEntries { get; set; }
/// <summary>
/// Directory names data
/// </summary>
public string DirectoryNames { get; set; }
/// <summary>
/// Directory info 1 entries data
/// </summary>
public DirectoryInfo1Entry[] DirectoryInfo1Entries { get; set; }
/// <summary>
/// Directory info 2 entries data
/// </summary>
public DirectoryInfo2Entry[] DirectoryInfo2Entries { get; set; }
/// <summary>
/// Directory copy entries data
/// </summary>
public DirectoryCopyEntry[] DirectoryCopyEntries { get; set; }
/// <summary>
/// Directory local entries data
/// </summary>
public DirectoryLocalEntry[] DirectoryLocalEntries { get; set; }
/// <summary>
/// Directory map header data
/// </summary>
public DirectoryMapHeader DirectoryMapHeader { get; set; }
/// <summary>
/// Directory map entries data
/// </summary>
public DirectoryMapEntry[] DirectoryMapEntries { get; set; }
/// <summary>
/// Checksum header data
/// </summary>
public ChecksumHeader ChecksumHeader { get; set; }
/// <summary>
/// Checksum map header data
/// </summary>
public ChecksumMapHeader ChecksumMapHeader { get; set; }
/// <summary>
/// Checksum map entries data
/// </summary>
public ChecksumMapEntry[] ChecksumMapEntries { get; set; }
/// <summary>
/// Checksum entries data
/// </summary>
public ChecksumEntry[] ChecksumEntries { get; set; }
/// <summary>
/// Data block header data
/// </summary>
public DataBlockHeader DataBlockHeader { get; set; }
}
}

View File

@@ -0,0 +1,11 @@
namespace BurnOutSharp.Models.GCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/GCFFile.h"/>
public sealed class FragmentationMap
{
/// <summary>
/// The index of the next data block.
/// </summary>
public uint NextDataBlockIndex;
}
}

View File

@@ -0,0 +1,26 @@
namespace BurnOutSharp.Models.GCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/GCFFile.h"/>
public sealed class FragmentationMapHeader
{
/// <summary>
/// Number of data blocks.
/// </summary>
public uint BlockCount;
/// <summary>
/// The index of the first unused fragmentation map entry.
/// </summary>
public uint FirstUnusedEntry;
/// <summary>
/// The block entry terminator; 0 = 0x0000ffff or 1 = 0xffffffff.
/// </summary>
public uint Terminator;
/// <summary>
/// Header checksum.
/// </summary>
public uint Checksum;
}
}

View File

@@ -0,0 +1,61 @@
namespace BurnOutSharp.Models.GCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/GCFFile.h"/>
public sealed class Header
{
/// <summary>
/// Always 0x00000001
/// </summary>
public uint Dummy0;
/// <summary>
/// Always 0x00000001
/// </summary>
public uint MajorVersion;
/// <summary>
/// GCF version number.
/// </summary>
public uint MinorVersion;
/// <summary>
/// Cache ID
/// </summary>
public uint CacheID;
/// <summary>
/// Last version played
/// </summary>
public uint LastVersionPlayed;
/// <summary>
/// Reserved
/// </summary>
public uint Dummy1;
/// <summary>
/// Reserved
/// </summary>
public uint Dummy2;
/// <summary>
/// Total size of GCF file in bytes.
/// </summary>
public uint FileSize;
/// <summary>
/// Size of each data block in bytes.
/// </summary>
public uint BlockSize;
/// <summary>
/// Number of data blocks.
/// </summary>
public uint BlockCount;
/// <summary>
/// Reserved
/// </summary>
public uint Dummy3;
}
}

View File

@@ -0,0 +1,11 @@
namespace BurnOutSharp.Models.NCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/NCFFile.h"/>
public sealed class ChecksumEntry
{
/// <summary>
/// Checksum.
/// </summary>
public uint Checksum;
}
}

View File

@@ -0,0 +1,16 @@
namespace BurnOutSharp.Models.NCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/NCFFile.h"/>
public sealed class ChecksumHeader
{
/// <summary>
/// Always 0x00000001
/// </summary>
public uint Dummy0 { get; set; }
/// <summary>
/// Size of LPNCFCHECKSUMHEADER & LPNCFCHECKSUMMAPHEADER & in bytes.
/// </summary>
public uint ChecksumSize { get; set; }
}
}

View File

@@ -0,0 +1,16 @@
namespace BurnOutSharp.Models.NCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/NCFFile.h"/>
public sealed class ChecksumMapEntry
{
/// <summary>
/// Number of checksums.
/// </summary>
public uint ChecksumCount;
/// <summary>
/// Index of first checksum.
/// </summary>
public uint FirstChecksumIndex;
}
}

View File

@@ -0,0 +1,26 @@
namespace BurnOutSharp.Models.NCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/NCFFile.h"/>
public sealed class ChecksumMapHeader
{
/// <summary>
/// Always 0x14893721
/// </summary>
public uint Dummy0;
/// <summary>
/// Always 0x00000001
/// </summary>
public uint Dummy1;
/// <summary>
/// Number of items.
/// </summary>
public uint ItemCount;
/// <summary>
/// Number of checksums.
/// </summary>
public uint ChecksumCount;
}
}

View File

@@ -0,0 +1,11 @@
namespace BurnOutSharp.Models.NCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/NCFFile.h"/>
public sealed class DirectoryCopyEntry
{
/// <summary>
/// Index of the directory item.
/// </summary>
public uint DirectoryIndex;
}
}

View File

@@ -0,0 +1,41 @@
namespace BurnOutSharp.Models.NCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/NCFFile.h"/>
public sealed class DirectoryEntry
{
/// <summary>
/// Offset to the directory item name from the end of the directory items.
/// </summary>
public uint NameOffset;
/// <summary>
/// Size of the item. (If file, file size. If folder, num items.)
/// </summary>
public uint ItemSize;
/// <summary>
/// Checksome index. (0xFFFFFFFF == None).
/// </summary>
public uint ChecksumIndex;
/// <summary>
/// Flags for the directory item. (0x00000000 == Folder).
/// </summary>
public uint DirectoryFlags;
/// <summary>
/// Index of the parent directory item. (0xFFFFFFFF == None).
/// </summary>
public uint ParentIndex;
/// <summary>
/// Index of the next directory item. (0x00000000 == None).
/// </summary>
public uint NextIndex;
/// <summary>
/// Index of the first directory item. (0x00000000 == None).
/// </summary>
public uint FirstIndex;
}
}

View File

@@ -0,0 +1,76 @@
namespace BurnOutSharp.Models.NCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/NCFFile.h"/>
public sealed class DirectoryHeader
{
/// <summary>
/// Always 0x00000004
/// </summary>
public uint Dummy0;
/// <summary>
/// Cache ID.
/// </summary>
public uint CacheID;
/// <summary>
/// NCF file version.
/// </summary>
public uint LastVersionPlayed;
/// <summary>
/// Number of items in the directory.
/// </summary>
public uint ItemCount;
/// <summary>
/// Number of files in the directory.
/// </summary>
public uint FileCount;
/// <summary>
/// Always 0x00008000. Data per checksum?
/// </summary>
public uint ChecksumDataLength;
/// <summary>
/// Size of lpNCFDirectoryEntries & lpNCFDirectoryNames & lpNCFDirectoryInfo1Entries & lpNCFDirectoryInfo2Entries & lpNCFDirectoryCopyEntries & lpNCFDirectoryLocalEntries in bytes.
/// </summary>
public uint DirectorySize;
/// <summary>
/// Size of the directory names in bytes.
/// </summary>
public uint NameSize;
/// <summary>
/// Number of Info1 entires.
/// </summary>
public uint Info1Count;
/// <summary>
/// Number of files to copy.
/// </summary>
public uint CopyCount;
/// <summary>
/// Number of files to keep local.
/// </summary>
public uint LocalCount;
/// <summary>
/// Reserved
/// </summary>
public uint Dummy1;
/// <summary>
/// Reserved
/// </summary>
public uint Dummy2;
/// <summary>
/// Header checksum.
/// </summary>
public uint Checksum;
}
}

View File

@@ -0,0 +1,11 @@
namespace BurnOutSharp.Models.NCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/NCFFile.h"/>
public sealed class DirectoryInfo1Entry
{
/// <summary>
/// Reserved
/// </summary>
public uint Dummy0;
}
}

View File

@@ -0,0 +1,11 @@
namespace BurnOutSharp.Models.NCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/NCFFile.h"/>
public sealed class DirectoryInfo2Entry
{
/// <summary>
/// Reserved
/// </summary>
public uint Dummy0 { get; set; }
}
}

View File

@@ -0,0 +1,11 @@
namespace BurnOutSharp.Models.NCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/NCFFile.h"/>
public sealed class DirectoryLocalEntry
{
/// <summary>
/// Index of the directory item.
/// </summary>
public uint DirectoryIndex;
}
}

View File

@@ -0,0 +1,79 @@
namespace BurnOutSharp.Models.NCF
{
/// <summary>
/// Half-Life No Cache File
/// </summary>
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/NCFFile.h"/>
public sealed class File
{
/// <summary>
/// Header data
/// </summary>
public Header Header { get; set; }
/// <summary>
/// Directory header data
/// </summary>
public DirectoryHeader DirectoryHeader { get; set; }
/// <summary>
/// Directory entries data
/// </summary>
public DirectoryEntry[] DirectoryEntries { get; set; }
/// <summary>
/// Directory names data
/// </summary>
public string DirectoryNames { get; set; }
/// <summary>
/// Directory info 1 entries data
/// </summary>
public DirectoryInfo1Entry[] DirectoryInfo1Entries { get; set; }
/// <summary>
/// Directory info 2 entries data
/// </summary>
public DirectoryInfo2Entry[] DirectoryInfo2Entries { get; set; }
/// <summary>
/// Directory copy entries data
/// </summary>
public DirectoryCopyEntry[] DirectoryCopyEntries { get; set; }
/// <summary>
/// Directory local entries data
/// </summary>
public DirectoryLocalEntry[] DirectoryLocalEntries { get; set; }
/// <summary>
/// Unknown header data
/// </summary>
public UnknownHeader UnknownHeader { get; set; }
/// <summary>
/// Unknown entries data
/// </summary>
public UnknownEntry[] UnknownEntries { get; set; }
/// <summary>
/// Checksum header data
/// </summary>
public ChecksumHeader ChecksumHeader { get; set; }
/// <summary>
/// Checksum map header data
/// </summary>
public ChecksumMapHeader ChecksumMapHeader { get; set; }
/// <summary>
/// Checksum map entries data
/// </summary>
public ChecksumMapEntry[] ChecksumMapEntries { get; set; }
/// <summary>
/// Checksum entries data
/// </summary>
public ChecksumEntry[] ChecksumEntries { get; set; }
}
}

View File

@@ -0,0 +1,61 @@
namespace BurnOutSharp.Models.NCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/NCFFile.h"/>
public sealed class Header
{
/// <summary>
/// Always 0x00000001
/// </summary>
public uint Dummy0;
/// <summary>
/// Always 0x00000002
/// </summary>
public uint MajorVersion;
/// <summary>
/// NCF version number.
/// </summary>
public uint MinorVersion;
/// <summary>
/// Cache ID
/// </summary>
public uint CacheID;
/// <summary>
/// Last version played
/// </summary>
public uint LastVersionPlayed;
/// <summary>
/// Reserved
/// </summary>
public uint Dummy3;
/// <summary>
/// Reserved
/// </summary>
public uint Dummy4;
/// <summary>
/// Total size of NCF file in bytes.
/// </summary>
public uint FileSize;
/// <summary>
/// Size of each data block in bytes.
/// </summary>
public uint BlockSize;
/// <summary>
/// Number of data blocks.
/// </summary>
public uint BlockCount;
/// <summary>
/// Reserved
/// </summary>
public uint Dummy5;
}
}

View File

@@ -0,0 +1,11 @@
namespace BurnOutSharp.Models.NCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/NCFFile.h"/>
public sealed class UnknownEntry
{
/// <summary>
/// Reserved
/// </summary>
public uint Dummy0;
}
}

View File

@@ -0,0 +1,16 @@
namespace BurnOutSharp.Models.NCF
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/NCFFile.h"/>
public sealed class UnknownHeader
{
/// <summary>
/// Always 0x00000001
/// </summary>
public uint Dummy0;
/// <summary>
/// Always 0x00000000
/// </summary>
public uint Dummy1;
}
}

View File

@@ -0,0 +1,21 @@
namespace BurnOutSharp.Models.PAK
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/PAKFile.h"/>
public sealed class DirectoryItem
{
/// <summary>
/// Item Name
/// </summary>
public string ItemName;
/// <summary>
/// Item Offset
/// </summary>
public uint ItemOffset;
/// <summary>
/// Item Length
/// </summary>
public uint ItemLength;
}
}

View File

@@ -0,0 +1,19 @@
namespace BurnOutSharp.Models.PAK
{
/// <summary>
/// Half-Life Package File
/// </summary>
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/PAKFile.h"/>
public sealed class File
{
/// <summary>
/// Deserialized directory header data
/// </summary>
public Header Header { get; set; }
/// <summary>
/// Deserialized directory items data
/// </summary>
public DirectoryItem[] DirectoryItems { get; set; }
}
}

View File

@@ -0,0 +1,21 @@
namespace BurnOutSharp.Models.PAK
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/PAKFile.h"/>
public sealed class Header
{
/// <summary>
/// Signature
/// </summary>
public string Signature;
/// <summary>
/// Directory Offset
/// </summary>
public uint DirectoryOffset;
/// <summary>
/// Directory Length
/// </summary>
public uint DirectoryLength;
}
}

View File

@@ -0,0 +1,5 @@
namespace BurnOutSharp.Models.SGA
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/SGAFile.h"/>
public abstract class Directory { }
}

View File

@@ -0,0 +1,5 @@
namespace BurnOutSharp.Models.SGA
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/SGAFile.h"/>
public class Directory4 : SpecializedDirectory<Header4, DirectoryHeader4, Section4, Folder4, File4, ushort> { }
}

View File

@@ -0,0 +1,5 @@
namespace BurnOutSharp.Models.SGA
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/SGAFile.h"/>
public class Directory5 : SpecializedDirectory<Header4, DirectoryHeader5, Section5, Folder5, File4, uint> { }
}

View File

@@ -0,0 +1,5 @@
namespace BurnOutSharp.Models.SGA
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/SGAFile.h"/>
public class Directory6 : SpecializedDirectory<Header6, DirectoryHeader5, Section5, Folder5, File6, uint> { }
}

View File

@@ -0,0 +1,5 @@
namespace BurnOutSharp.Models.SGA
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/SGAFile.h"/>
public class Directory7 : SpecializedDirectory<Header6, DirectoryHeader7, Section5, Folder5, File7, uint> { }
}

View File

@@ -0,0 +1,22 @@
namespace BurnOutSharp.Models.SGA
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/SGAFile.h"/>
public class DirectoryHeader<T>
{
public uint SectionOffset;
public T SectionCount;
public uint FolderOffset;
public T FolderCount;
public uint FileOffset;
public T FileCount;
public uint StringTableOffset;
public T StringTableCount;
}
}

View File

@@ -0,0 +1,5 @@
namespace BurnOutSharp.Models.SGA
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/SGAFile.h"/>
public sealed class DirectoryHeader4 : DirectoryHeader<ushort> { }
}

View File

@@ -0,0 +1,5 @@
namespace BurnOutSharp.Models.SGA
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/SGAFile.h"/>
public class DirectoryHeader5 : DirectoryHeader<uint> { }
}

View File

@@ -0,0 +1,10 @@
namespace BurnOutSharp.Models.SGA
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/SGAFile.h"/>
public sealed class DirectoryHeader7 : DirectoryHeader5
{
public uint HashTableOffset;
public uint BlockSize;
}
}

View File

@@ -0,0 +1,16 @@
namespace BurnOutSharp.Models.SGA
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/SGAFile.h"/>
public class File
{
/// <summary>
///Header data
/// </summary>
public Header Header { get; set; }
/// <summary>
/// Directory data
/// </summary>
public Directory Directory { get; set; }
}
}

View File

@@ -0,0 +1,20 @@
namespace BurnOutSharp.Models.SGA
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/SGAFile.h"/>
public class File4
{
public uint NameOffset;
public uint Offset;
public uint SizeOnDisk;
public uint Size;
public uint TimeModified;
public byte Dummy0;
public byte Type;
}
}

View File

@@ -0,0 +1,8 @@
namespace BurnOutSharp.Models.SGA
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/SGAFile.h"/>
public class File6 : File4
{
public uint CRC32;
}
}

View File

@@ -0,0 +1,8 @@
namespace BurnOutSharp.Models.SGA
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/SGAFile.h"/>
public sealed class File7 : File6
{
public uint HashOffset;
}
}

View File

@@ -0,0 +1,10 @@
namespace BurnOutSharp.Models.SGA
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/SGAFile.h"/>
public sealed class FileHeader
{
public string Name;
public uint CRC32;
}
}

View File

@@ -0,0 +1,16 @@
namespace BurnOutSharp.Models.SGA
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/SGAFile.h"/>
public class Folder<T>
{
public uint NameOffset;
public T FolderStartIndex;
public T FolderEndIndex;
public T FileStartIndex;
public T FileEndIndex;
}
}

View File

@@ -0,0 +1,5 @@
namespace BurnOutSharp.Models.SGA
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/SGAFile.h"/>
public sealed class Folder4 : Folder<ushort> { }
}

View File

@@ -0,0 +1,5 @@
namespace BurnOutSharp.Models.SGA
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/SGAFile.h"/>
public sealed class Folder5 : Folder<uint> { }
}

View File

@@ -0,0 +1,12 @@
namespace BurnOutSharp.Models.SGA
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/SGAFile.h"/>
public class Header
{
public string Signature;
public ushort MajorVersion;
public ushort MinorVersion;
}
}

View File

@@ -0,0 +1,18 @@
namespace BurnOutSharp.Models.SGA
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/SGAFile.h"/>
public sealed class Header4 : Header
{
public byte[] FileMD5;
public string Name;
public byte[] HeaderMD5;
public uint HeaderLength;
public uint FileDataOffset;
public uint Dummy0;
}
}

View File

@@ -0,0 +1,14 @@
namespace BurnOutSharp.Models.SGA
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/SGAFile.h"/>
public sealed class Header6 : Header
{
public string Name;
public uint HeaderLength;
public uint FileDataOffset;
public uint Dummy0;
}
}

View File

@@ -0,0 +1,20 @@
namespace BurnOutSharp.Models.SGA
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/SGAFile.h"/>
public class Section<T>
{
public string Alias;
public string Name;
public T FolderStartIndex;
public T FolderEndIndex;
public T FileStartIndex;
public T FileEndIndex;
public T FolderRootIndex;
}
}

View File

@@ -0,0 +1,5 @@
namespace BurnOutSharp.Models.SGA
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/SGAFile.h"/>
public sealed class Section4 : Section<ushort> { }
}

View File

@@ -0,0 +1,5 @@
namespace BurnOutSharp.Models.SGA
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/SGAFile.h"/>
public sealed class Section5 : Section<uint> { }
}

View File

@@ -0,0 +1,46 @@
using System.Collections.Generic;
namespace BurnOutSharp.Models.SGA
{
/// <summary>
/// Specialization File7 and up where the CRC moved to the header and the CRC is of the compressed data and there are stronger hashes.
/// </summary>
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/SGAFile.h"/>
public class SpecializedDirectory<THeader, TDirectoryHeader, TSection, TFolder, TFile, U> : Directory
where THeader : Header
where TDirectoryHeader : DirectoryHeader<U>
where TSection : Section<U>
where TFolder : Folder<U>
where TFile : File4
{
/// <summary>
/// Source SGA file
/// </summary>
public File File { get; set; }
/// <summary>
/// Directory header data
/// </summary>
public TDirectoryHeader DirectoryHeader { get; set; }
/// <summary>
/// Sections data
/// </summary>
public TSection[] Sections { get; set; }
/// <summary>
/// Folders data
/// </summary>
public TFolder[] Folders { get; set; }
/// <summary>
/// Files data
/// </summary>
public TFile[] Files { get; set; }
/// <summary>
/// String table data
/// </summary>
public Dictionary<long, string> StringTable { get; set; }
}
}

View File

@@ -0,0 +1,14 @@
namespace BurnOutSharp.Models.VBSP
{
/// <summary>
/// Half-Life 2 Level
/// </summary>
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/VBSPFile.h"/>
public sealed class File
{
/// <summary>
/// Directory header data
/// </summary>
public Header Header { get; set; }
}
}

View File

@@ -0,0 +1,31 @@
namespace BurnOutSharp.Models.VBSP
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/VBSPFile.h"/>
public sealed class Header
{
/// <summary>
/// BSP file signature.
/// </summary>
public string Signature;
/// <summary>
/// BSP file version.
/// </summary>
/// <remarks>
/// 19-20: Source
/// 21: Source - The lump version property was moved to the start of the struct.
/// 0x00040014: Dark Messiah - Looks like the 32 bit version has been split into two 16 bit fields.
/// </remarks>
public int Version;
/// <summary>
/// Lumps.
/// </summary>
public Lump[] Lumps;
/// <summary>
/// The map's revision (iteration, version) number.
/// </summary>
public int MapRevision;
}
}

View File

@@ -0,0 +1,20 @@
namespace BurnOutSharp.Models.VBSP
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/VBSPFile.h"/>
public sealed class Lump
{
public uint Offset;
public uint Length;
/// <summary>
/// Default to zero.
/// </summary>
public uint Version;
/// <summary>
/// Default to (char)0, (char)0, (char)0, (char)0.
/// </summary>
public char[] FourCC;
}
}

View File

@@ -0,0 +1,16 @@
namespace BurnOutSharp.Models.VBSP
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/VBSPFile.h"/>
public sealed class LumpHeader
{
public int LumpOffset;
public int LumpID;
public int LumpVersion;
public int LumpLength;
public int MapRevision;
}
}

View File

@@ -0,0 +1,17 @@
namespace BurnOutSharp.Models.VPK
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/VPKFile.h"/>
public sealed class ArchiveHash
{
public uint ArchiveIndex;
public uint ArchiveOffset;
public uint Length;
/// <summary>
/// MD5
/// </summary>
public byte[] Hash;
}
}

View File

@@ -0,0 +1,21 @@
namespace BurnOutSharp.Models.VPK
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/VPKFile.h"/>
public sealed class DirectoryEntry
{
public uint CRC;
public ushort PreloadBytes;
public ushort ArchiveIndex;
public uint EntryOffset;
public uint EntryLength;
/// <summary>
/// Always 0xffff.
/// </summary>
public ushort Dummy0;
}
}

View File

@@ -0,0 +1,16 @@
namespace BurnOutSharp.Models.VPK
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/VPKFile.h"/>
public sealed class DirectoryItem
{
public string Extension;
public string Path;
public string Name;
public DirectoryEntry DirectoryEntry;
public byte[] PreloadData;
}
}

View File

@@ -0,0 +1,29 @@
namespace BurnOutSharp.Models.VPK
{
/// <summary>
/// Added in version 2.
/// </summary>
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/VPKFile.h"/>
public sealed class ExtendedHeader
{
/// <summary>
/// Reserved
/// </summary>
public uint Dummy0;
/// <summary>
/// Archive hash length
/// </summary>
public uint ArchiveHashLength;
/// <summary>
/// Looks like some more MD5 hashes.
/// </summary>
public uint ExtraLength;
/// <summary>
/// Reserved
/// </summary>
public uint Dummy1;
}
}

View File

@@ -0,0 +1,29 @@
namespace BurnOutSharp.Models.VPK
{
/// <summary>
/// Valve Package File
/// </summary>
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/VPKFile.h"/>
public sealed class File
{
/// <summary>
/// Header data
/// </summary>
public Header Header { get; set; }
/// <summary>
/// Extended header data
/// </summary>
public ExtendedHeader ExtendedHeader { get; set; }
/// <summary>
/// Archive hashes data
/// </summary>
public ArchiveHash[] ArchiveHashes { get; set; }
/// <summary>
/// Directory items data
/// </summary>
public DirectoryItem[] DirectoryItems { get; set; }
}
}

View File

@@ -0,0 +1,15 @@
namespace BurnOutSharp.Models.VPK
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/VPKFile.h"/>
public sealed class Header
{
/// <summary>
/// Always 0x55aa1234.
/// </summary>
public uint Signature;
public uint Version;
public uint DirectoryLength;
}
}

View File

@@ -0,0 +1,24 @@
namespace BurnOutSharp.Models.WAD
{
/// <summary>
/// Half-Life Texture Package File
/// </summary>
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/WADFile.h"/>
public sealed class File
{
/// <summary>
/// Deserialized header data
/// </summary>
public Header Header { get; set; }
/// <summary>
/// Deserialized lumps data
/// </summary>
public Lump[] Lumps { get; set; }
/// <summary>
/// Deserialized lump infos data
/// </summary>
public LumpInfo[] LumpInfos { get; set; }
}
}

View File

@@ -0,0 +1,12 @@
namespace BurnOutSharp.Models.WAD
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/WADFile.h"/>
public sealed class Header
{
public string Signature;
public uint LumpCount;
public uint LumpOffset;
}
}

View File

@@ -0,0 +1,22 @@
namespace BurnOutSharp.Models.WAD
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/WADFile.h"/>
public sealed class Lump
{
public uint Offset;
public uint DiskLength;
public uint Length;
public byte Type;
public byte Compression;
public byte Padding0;
public byte Padding1;
public string Name;
}
}

View File

@@ -0,0 +1,22 @@
namespace BurnOutSharp.Models.WAD
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/WADFile.h"/>
public sealed class LumpInfo
{
public string Name;
public uint Width;
public uint Height;
public uint PixelOffset;
// 12 bytes of unknown data
public byte[] PixelData;
public uint PaletteSize;
public byte[] PaletteData;
}
}

View File

@@ -0,0 +1,12 @@
namespace BurnOutSharp.Models.XZP
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/XZPFile.h"/>
public sealed class DirectoryEntry
{
public uint FileNameCRC;
public uint EntryLength;
public uint EntryOffset;
}
}

View File

@@ -0,0 +1,12 @@
namespace BurnOutSharp.Models.XZP
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/XZPFile.h"/>
public sealed class DirectoryItem
{
public uint FileNameCRC;
public uint NameOffset;
public uint TimeCreated;
}
}

View File

@@ -0,0 +1,8 @@
namespace BurnOutSharp.Models.XZP
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/XZPFile.h"/>
public sealed class DirectoryMapping
{
public ushort PreloadDirectoryEntryIndex;
}
}

View File

@@ -0,0 +1,39 @@
namespace BurnOutSharp.Models.XZP
{
/// <summary>
/// XBox Package File
/// </summary>
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/XZPFile.h"/>
public class File
{
/// <summary>
/// Header data
/// </summary>
public Header Header { get; set; }
/// <summary>
/// Directory entries data
/// </summary>
public DirectoryEntry[] DirectoryEntries { get; set; }
/// <summary>
/// Preload directory entries data
/// </summary>
public DirectoryEntry[] PreloadDirectoryEntries { get; set; }
/// <summary>
/// Preload directory mappings data
/// </summary>
public DirectoryMapping[] PreloadDirectoryMappings { get; set; }
/// <summary>
/// Directory items data
/// </summary>
public DirectoryItem[] DirectoryItems { get; set; }
/// <summary>
/// Footer data
/// </summary>
public Footer Footer { get; set; }
}
}

View File

@@ -0,0 +1,10 @@
namespace BurnOutSharp.Models.XZP
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/XZPFile.h"/>
public sealed class Footer
{
public uint FileLength;
public string Signature;
}
}

View File

@@ -0,0 +1,24 @@
namespace BurnOutSharp.Models.XZP
{
/// <see href="https://github.com/RavuAlHemio/hllib/blob/master/HLLib/XZPFile.h"/>
public sealed class Header
{
public string Signature;
public uint Version;
public uint PreloadDirectoryEntryCount;
public uint DirectoryEntryCount;
public uint PreloadBytes;
public uint HeaderLength;
public uint DirectoryItemCount;
public uint DirectoryItemOffset;
public uint DirectoryItemLength;
}
}

View File

@@ -15,6 +15,11 @@
/// </summary>
BFPK,
/// <summary>
/// Half-Life Level
/// </summary>
BSP,
/// <summary>
/// bzip2 archive
/// </summary>
@@ -25,6 +30,11 @@
/// </summary>
Executable,
/// <summary>
/// Half-Life Game Cache File
/// </summary>
GCF,
/// <summary>
/// gzip archive
/// </summary>
@@ -60,6 +70,16 @@
/// </summary>
MSI,
/// <summary>
/// Half-Life No Cache File
/// </summary>
NCF,
/// <summary>
/// Half-Life Package File
/// </summary>
PAK,
/// <summary>
/// PKWARE ZIP archive and derivatives
/// </summary>
@@ -85,6 +105,11 @@
/// </summary>
SFFS,
/// <summary>
/// SGA
/// </summary>
SGA,
/// <summary>
/// Tape archive
/// </summary>
@@ -96,13 +121,28 @@
Textfile,
/// <summary>
/// Various Valve archive formats
/// Half-Life 2 Level
/// </summary>
Valve,
VBSP,
/// <summary>
/// Valve Package File
/// </summary>
VPK,
/// <summary>
/// Half-Life Texture Package File
/// </summary>
WAD,
/// <summary>
/// xz archive
/// </summary>
XZ,
/// <summary>
/// xz archive
/// </summary>
XZP,
}
}

View File

@@ -31,7 +31,7 @@ namespace BurnOutSharp.Tools
#region BSP
if (magic.StartsWith(new byte?[] { 0x1e, 0x00, 0x00, 0x00 }))
return SupportedFileType.Valve;
return SupportedFileType.BSP;
#endregion
@@ -81,7 +81,7 @@ namespace BurnOutSharp.Tools
#region GCF
if (magic.StartsWith(new byte?[] { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }))
return SupportedFileType.Valve;
return SupportedFileType.GCF;
#endregion
@@ -139,14 +139,14 @@ namespace BurnOutSharp.Tools
#region NCF
if (magic.StartsWith(new byte?[] { 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 }))
return SupportedFileType.Valve;
return SupportedFileType.NCF;
#endregion
#region PAK
if (magic.StartsWith(new byte?[] { 0x50, 0x41, 0x43, 0x4B }))
return SupportedFileType.Valve;
return SupportedFileType.PAK;
#endregion
@@ -208,7 +208,7 @@ namespace BurnOutSharp.Tools
#region SGA
if (magic.StartsWith(new byte?[] { 0x5F, 0x41, 0x52, 0x43, 0x48, 0x49, 0x56, 0x45 }))
return SupportedFileType.Valve;
return SupportedFileType.SGA;
#endregion
@@ -255,21 +255,21 @@ namespace BurnOutSharp.Tools
#region VBSP
if (magic.StartsWith(new byte?[] { 0x56, 0x42, 0x53, 0x50 }))
return SupportedFileType.Valve;
return SupportedFileType.VBSP;
#endregion
#region VPK
if (magic.StartsWith(new byte?[] { 0x34, 0x12, 0x55, 0xaa }))
return SupportedFileType.Valve;
return SupportedFileType.VPK;
#endregion
#region WAD
if (magic.StartsWith(new byte?[] { 0x57, 0x41, 0x44, 0x33 }))
return SupportedFileType.Valve;
return SupportedFileType.WAD;
#endregion
@@ -283,7 +283,7 @@ namespace BurnOutSharp.Tools
#region XZP
if (magic.StartsWith(new byte?[] { 0x70, 0x69, 0x5A, 0x78 }))
return SupportedFileType.Valve;
return SupportedFileType.XZP;
#endregion
@@ -313,7 +313,7 @@ namespace BurnOutSharp.Tools
#region BSP
if (extension.Equals("bsp", StringComparison.OrdinalIgnoreCase))
return SupportedFileType.Valve;
return SupportedFileType.BSP;
#endregion
@@ -339,7 +339,7 @@ namespace BurnOutSharp.Tools
#region GCF
if (extension.Equals("gcf", StringComparison.OrdinalIgnoreCase))
return SupportedFileType.Valve;
return SupportedFileType.GCF;
#endregion
@@ -395,14 +395,14 @@ namespace BurnOutSharp.Tools
#region NCF
if (extension.Equals("ncf", StringComparison.OrdinalIgnoreCase))
return SupportedFileType.Valve;
return SupportedFileType.NCF;
#endregion
#region PAK
if (extension.Equals("pak", StringComparison.OrdinalIgnoreCase))
return SupportedFileType.Valve;
return SupportedFileType.PAK;
#endregion
@@ -519,7 +519,7 @@ namespace BurnOutSharp.Tools
#region SGA
if (extension.Equals("sga", StringComparison.OrdinalIgnoreCase))
return SupportedFileType.Valve;
return SupportedFileType.SGA;
#endregion
@@ -575,21 +575,21 @@ namespace BurnOutSharp.Tools
#region VBSP
if (extension.Equals("bsp", StringComparison.OrdinalIgnoreCase))
return SupportedFileType.Valve;
return SupportedFileType.VBSP;
#endregion
#region VPK
if (extension.Equals("vpk", StringComparison.OrdinalIgnoreCase))
return SupportedFileType.Valve;
return SupportedFileType.VPK;
#endregion
#region WAD
if (extension.Equals("wad", StringComparison.OrdinalIgnoreCase))
return SupportedFileType.Valve;
return SupportedFileType.WAD;
#endregion
@@ -603,7 +603,7 @@ namespace BurnOutSharp.Tools
#region XZP
if (extension.Equals("xzp", StringComparison.OrdinalIgnoreCase))
return SupportedFileType.Valve;
return SupportedFileType.XZP;
#endregion
@@ -619,8 +619,10 @@ namespace BurnOutSharp.Tools
switch (fileType)
{
case SupportedFileType.BFPK: return new FileType.BFPK();
case SupportedFileType.BSP: return new FileType.Valve();
case SupportedFileType.BZip2: return new FileType.BZip2();
case SupportedFileType.Executable: return new FileType.Executable();
case SupportedFileType.GCF: return new FileType.Valve();
case SupportedFileType.GZIP: return new FileType.GZIP();
//case FileTypes.IniFile: return new FileType.IniFile();
case SupportedFileType.InstallShieldArchiveV3: return new FileType.InstallShieldArchiveV3();
@@ -628,15 +630,21 @@ namespace BurnOutSharp.Tools
case SupportedFileType.MicrosoftCAB: return new FileType.MicrosoftCAB();
case SupportedFileType.MPQ: return new FileType.MPQ();
case SupportedFileType.MSI: return new FileType.MSI();
case SupportedFileType.NCF: return new FileType.Valve();
case SupportedFileType.PAK: return new FileType.PKZIP();
case SupportedFileType.PKZIP: return new FileType.PKZIP();
case SupportedFileType.PLJ: return new FileType.PLJ();
case SupportedFileType.RAR: return new FileType.RAR();
case SupportedFileType.SevenZip: return new FileType.SevenZip();
case SupportedFileType.SFFS: return new FileType.SFFS();
case SupportedFileType.SGA: return new FileType.Valve();
case SupportedFileType.TapeArchive: return new FileType.TapeArchive();
case SupportedFileType.Textfile: return new FileType.Textfile();
case SupportedFileType.Valve: return new FileType.Valve();
case SupportedFileType.VBSP: return new FileType.Valve();
case SupportedFileType.VPK: return new FileType.Valve();
case SupportedFileType.WAD: return new FileType.Valve();
case SupportedFileType.XZ: return new FileType.XZ();
case SupportedFileType.XZP: return new FileType.Valve();
default: return null;
}
}

Some files were not shown because too many files have changed in this diff Show More