mirror of
https://github.com/SabreTools/BinaryObjectScanner.git
synced 2026-02-12 21:31:30 +00:00
Add MoPaQ builder (nw)
This commit is contained in:
812
BurnOutSharp.Builder/MoPaQ.cs
Normal file
812
BurnOutSharp.Builder/MoPaQ.cs
Normal file
@@ -0,0 +1,812 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Models.MoPaQ;
|
||||
|
||||
namespace BurnOutSharp.Builder
|
||||
{
|
||||
// TODO: Make Stream Data rely on Byte Data
|
||||
public class MoPaQ
|
||||
{
|
||||
#region Constants
|
||||
|
||||
#region User Data
|
||||
|
||||
public const int UserDataSize = 0x10;
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable signature
|
||||
/// </summary>
|
||||
public static readonly string UserDataSignatureString = $"MPQ{(char)0x1B}";
|
||||
|
||||
/// <summary>
|
||||
/// Signature as an unsigned Int32 value
|
||||
/// </summary>
|
||||
public const uint UserDataSignatureValue = 0x1B51504D;
|
||||
|
||||
/// <summary>
|
||||
/// Signature as a byte array
|
||||
/// </summary>
|
||||
public static readonly byte[] UserDataSignatureBytes = new byte[] { 0x4D, 0x50, 0x51, 0x1B };
|
||||
|
||||
#endregion
|
||||
|
||||
#region Archive Header
|
||||
|
||||
#region Signatures
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable signature
|
||||
/// </summary>
|
||||
public static readonly string ArchiveHeaderSignatureString = $"MPQ{(char)0x1A}";
|
||||
|
||||
/// <summary>
|
||||
/// Signature as an unsigned Int32 value
|
||||
/// </summary>
|
||||
public const uint ArchiveHeaderSignatureValue = 0x1A51504D;
|
||||
|
||||
/// <summary>
|
||||
/// Signature as a byte array
|
||||
/// </summary>
|
||||
public static readonly byte[] ArchiveHeaderSignatureBytes = new byte[] { 0x4D, 0x50, 0x51, 0x1A };
|
||||
|
||||
#endregion
|
||||
|
||||
#region Header Sizes
|
||||
|
||||
public const int ArchiveHeaderHeaderVersion1Size = 0x20;
|
||||
|
||||
public const int ArchiveHeaderHeaderVersion2Size = 0x2C;
|
||||
|
||||
public const int ArchiveHeaderHeaderVersion3Size = 0x44;
|
||||
|
||||
public const int ArchiveHeaderHeaderVersion4Size = 0xD0;
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region HET Table
|
||||
|
||||
public const int HetTableSize = 0x44;
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable signature
|
||||
/// </summary>
|
||||
public static readonly string HetTableSignatureString = $"HET{(char)0x1A}";
|
||||
|
||||
/// <summary>
|
||||
/// Signature as an unsigned Int32 value
|
||||
/// </summary>
|
||||
public const uint HetTableSignatureValue = 0x1A544548;
|
||||
|
||||
/// <summary>
|
||||
/// Signature as a byte array
|
||||
/// </summary>
|
||||
public static readonly byte[] HetTableSignatureBytes = new byte[] { 0x48, 0x45, 0x54, 0x1A };
|
||||
|
||||
#endregion
|
||||
|
||||
#region BET Table
|
||||
|
||||
public const int BetTableSize = 0x88;
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable signature
|
||||
/// </summary>
|
||||
public static readonly string BetTableSignatureString = $"BET{(char)0x1A}";
|
||||
|
||||
/// <summary>
|
||||
/// Signature as an unsigned Int32 value
|
||||
/// </summary>
|
||||
public const uint BetTableSignatureValue = 0x1A544542;
|
||||
|
||||
/// <summary>
|
||||
/// Signature as a byte array
|
||||
/// </summary>
|
||||
public static readonly byte[] BetTableSignatureBytes = new byte[] { 0x42, 0x45, 0x54, 0x1A };
|
||||
|
||||
#endregion
|
||||
|
||||
#region Hash Entry
|
||||
|
||||
public const int HashEntrySize = 0x10;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Block Entry
|
||||
|
||||
public const int BlockEntrySize = 0x10;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Patch Header
|
||||
|
||||
#region Signatures
|
||||
|
||||
#region Patch Header
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable signature
|
||||
/// </summary>
|
||||
public static readonly string PatchSignatureString = $"PTCH";
|
||||
|
||||
/// <summary>
|
||||
/// Signature as an unsigned Int32 value
|
||||
/// </summary>
|
||||
public const uint PatchSignatureValue = 0x48435450;
|
||||
|
||||
/// <summary>
|
||||
/// Signature as a byte array
|
||||
/// </summary>
|
||||
public static readonly byte[] PatchSignatureBytes = new byte[] { 0x50, 0x54, 0x43, 0x48 };
|
||||
|
||||
#endregion
|
||||
|
||||
#region MD5 Block
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable signature
|
||||
/// </summary>
|
||||
public static readonly string Md5SignatureString = $"MD5_";
|
||||
|
||||
/// <summary>
|
||||
/// Signature as an unsigned Int32 value
|
||||
/// </summary>
|
||||
public const uint Md5SignatureValue = 0x5F35444D;
|
||||
|
||||
/// <summary>
|
||||
/// Signature as a byte array
|
||||
/// </summary>
|
||||
public static readonly byte[] Md5SignatureBytes = new byte[] { 0x4D, 0x44, 0x35, 0x5F };
|
||||
|
||||
#endregion
|
||||
|
||||
#region XFRM Block
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable signature
|
||||
/// </summary>
|
||||
public static readonly string XFRMSignatureString = $"XFRM";
|
||||
|
||||
/// <summary>
|
||||
/// Signature as an unsigned Int32 value
|
||||
/// </summary>
|
||||
public const uint XFRMSignatureValue = 0x4D524658;
|
||||
|
||||
/// <summary>
|
||||
/// Signature as a byte array
|
||||
/// </summary>
|
||||
public static readonly byte[] XFRMSignatureBytes = new byte[] { 0x58, 0x46, 0x52, 0x4D };
|
||||
|
||||
#endregion
|
||||
|
||||
#region BSDIFF Patch Type
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable signature
|
||||
/// </summary>
|
||||
public static readonly string BSDIFF40SignatureString = $"BSDIFF40";
|
||||
|
||||
/// <summary>
|
||||
/// Signature as an unsigned Int64 value
|
||||
/// </summary>
|
||||
public const ulong BSDIFF40SignatureValue = 0x3034464649445342;
|
||||
|
||||
/// <summary>
|
||||
/// Signature as a byte array
|
||||
/// </summary>
|
||||
public static readonly byte[] BSDIFF40SignatureBytes = new byte[] { 0x42, 0x53, 0x44, 0x49, 0x46, 0x46, 0x34, 0x30 };
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Byte Data
|
||||
|
||||
/// <summary>
|
||||
/// Parse a byte array into a MoPaQ archive
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array to parse</param>
|
||||
/// <param name="offset">Offset into the byte array</param>
|
||||
/// <returns>Filled archive on success, null on error</returns>
|
||||
public static Archive ParseArchive(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;
|
||||
|
||||
// Cache the current offset
|
||||
int initialOffset = offset;
|
||||
|
||||
// Create a new archive to fill
|
||||
var archive = new Archive();
|
||||
|
||||
#region User Data
|
||||
|
||||
// Check for User Data
|
||||
uint possibleSignature = BitConverter.ToUInt32(data, offset);
|
||||
if (possibleSignature == UserDataSignatureValue)
|
||||
{
|
||||
// Save the current position for offset correction
|
||||
int basePtr = offset;
|
||||
|
||||
// Deserialize the user data, returning null if invalid
|
||||
var userData = ParseUserData(data, ref offset);
|
||||
if (userData == null)
|
||||
return null;
|
||||
|
||||
// Set the user data
|
||||
archive.UserData = userData;
|
||||
|
||||
// Set the starting position according to the header offset
|
||||
offset = basePtr + (int)archive.UserData.HeaderOffset;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Archive Header
|
||||
|
||||
// Check for the Header
|
||||
possibleSignature = BitConverter.ToUInt32(data, offset);
|
||||
if (possibleSignature == ArchiveHeaderSignatureValue)
|
||||
{
|
||||
// Try to parse the archive header
|
||||
var archiveHeader = ParseArchiveHeader(data, ref offset);
|
||||
if (archiveHeader == null)
|
||||
return null;
|
||||
|
||||
// Set the archive header
|
||||
archive.ArchiveHeader = archiveHeader;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// TODO: Read in Hash Table
|
||||
// TODO: Read in Block Table
|
||||
// TODO: Read in Hi-Block Table
|
||||
// TODO: Read in BET Table
|
||||
// TODO: Read in HET Table
|
||||
|
||||
return archive;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a byte array into a archive header
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array to parse</param>
|
||||
/// <param name="offset">Offset into the byte array</param>
|
||||
/// <returns>Filled archive header on success, null on error</returns>
|
||||
private static ArchiveHeader ParseArchiveHeader(byte[] data, ref int offset)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
ArchiveHeader archiveHeader = new ArchiveHeader();
|
||||
|
||||
// V1 - Common
|
||||
archiveHeader.Signature = data.ReadUInt32(ref offset);
|
||||
if (archiveHeader.Signature != ArchiveHeaderSignatureValue)
|
||||
return null;
|
||||
|
||||
archiveHeader.HeaderSize = data.ReadUInt32(ref offset);
|
||||
archiveHeader.ArchiveSize = data.ReadUInt32(ref offset);
|
||||
archiveHeader.FormatVersion = data.ReadUInt16(ref offset);
|
||||
archiveHeader.BlockSize = data.ReadUInt16(ref offset);
|
||||
archiveHeader.HashTablePosition = data.ReadUInt32(ref offset);
|
||||
archiveHeader.BlockTablePosition = data.ReadUInt32(ref offset);
|
||||
archiveHeader.HashTableSize = data.ReadUInt32(ref offset);
|
||||
archiveHeader.BlockTableSize = data.ReadUInt32(ref offset);
|
||||
|
||||
// V2
|
||||
if (archiveHeader.FormatVersion >= 2 && archiveHeader.HeaderSize >= ArchiveHeaderHeaderVersion2Size)
|
||||
{
|
||||
archiveHeader.HiBlockTablePosition = data.ReadUInt64(ref offset);
|
||||
archiveHeader.HashTablePositionHi = data.ReadUInt16(ref offset);
|
||||
archiveHeader.BlockTablePositionHi = data.ReadUInt16(ref offset);
|
||||
}
|
||||
|
||||
// V3
|
||||
if (archiveHeader.FormatVersion >= 3 && archiveHeader.HeaderSize >= ArchiveHeaderHeaderVersion3Size)
|
||||
{
|
||||
archiveHeader.ArchiveSizeLong = data.ReadUInt64(ref offset);
|
||||
archiveHeader.BetTablePosition = data.ReadUInt64(ref offset);
|
||||
archiveHeader.HetTablePosition = data.ReadUInt64(ref offset);
|
||||
}
|
||||
|
||||
// V4
|
||||
if (archiveHeader.FormatVersion >= 4 && archiveHeader.HeaderSize >= ArchiveHeaderHeaderVersion4Size)
|
||||
{
|
||||
archiveHeader.HashTableSizeLong = data.ReadUInt64(ref offset);
|
||||
archiveHeader.BlockTableSizeLong = data.ReadUInt64(ref offset);
|
||||
archiveHeader.HiBlockTableSize = data.ReadUInt64(ref offset);
|
||||
archiveHeader.HetTableSize = data.ReadUInt64(ref offset);
|
||||
archiveHeader.BetTablesize = data.ReadUInt64(ref offset);
|
||||
archiveHeader.RawChunkSize = data.ReadUInt32(ref offset);
|
||||
|
||||
archiveHeader.BlockTableMD5 = data.ReadBytes(ref offset, 0x10);
|
||||
archiveHeader.HashTableMD5 = data.ReadBytes(ref offset, 0x10);
|
||||
archiveHeader.HiBlockTableMD5 = data.ReadBytes(ref offset, 0x10);
|
||||
archiveHeader.BetTableMD5 = data.ReadBytes(ref offset, 0x10);
|
||||
archiveHeader.HetTableMD5 = data.ReadBytes(ref offset, 0x10);
|
||||
archiveHeader.HetTableMD5 = data.ReadBytes(ref offset, 0x10);
|
||||
}
|
||||
|
||||
return archiveHeader;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a byte array into a user data object
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array to parse</param>
|
||||
/// <param name="offset">Offset into the byte array</param>
|
||||
/// <returns>Filled user data on success, null on error</returns>
|
||||
private static UserData ParseUserData(byte[] data, ref int offset)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
UserData userData = new UserData();
|
||||
|
||||
userData.Signature = data.ReadUInt32(ref offset);
|
||||
if (userData.Signature != UserDataSignatureValue)
|
||||
return null;
|
||||
|
||||
userData.UserDataSize = data.ReadUInt32(ref offset);
|
||||
userData.HeaderOffset = data.ReadUInt32(ref offset);
|
||||
userData.UserDataHeaderSize = data.ReadUInt32(ref offset);
|
||||
|
||||
return userData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a byte array into a HET table
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array to parse</param>
|
||||
/// <param name="offset">Offset into the byte array</param>
|
||||
/// <returns>Filled HET table on success, null on error</returns>
|
||||
private static HetTable ParseHetTable(byte[] data, ref int offset)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
HetTable hetTable = new HetTable();
|
||||
|
||||
// Common Headers
|
||||
hetTable.Signature = data.ReadUInt32(ref offset);
|
||||
if (hetTable.Signature != HetTableSignatureValue)
|
||||
return null;
|
||||
|
||||
hetTable.Version = data.ReadUInt32(ref offset);
|
||||
hetTable.DataSize = data.ReadUInt32(ref offset);
|
||||
|
||||
// HET-Specific
|
||||
hetTable.TableSize = data.ReadUInt32(ref offset);
|
||||
hetTable.MaxFileCount = data.ReadUInt32(ref offset);
|
||||
hetTable.HashTableSize = data.ReadUInt32(ref offset);
|
||||
hetTable.TotalIndexSize = data.ReadUInt32(ref offset);
|
||||
hetTable.IndexSizeExtra = data.ReadUInt32(ref offset);
|
||||
hetTable.IndexSize = data.ReadUInt32(ref offset);
|
||||
hetTable.BlockTableSize = data.ReadUInt32(ref offset);
|
||||
hetTable.HashTable = data.ReadBytes(ref offset, (int)hetTable.HashTableSize);
|
||||
|
||||
// TODO: Populate the file indexes array
|
||||
hetTable.FileIndexes = new byte[(int)hetTable.HashTableSize][];
|
||||
|
||||
return hetTable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a byte array into a BET table
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array to parse</param>
|
||||
/// <param name="offset">Offset into the byte array</param>
|
||||
/// <returns>Filled BET table on success, null on error</returns>
|
||||
private static BetTable ParseBetTable(byte[] data, ref int offset)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
BetTable betTable = new BetTable();
|
||||
|
||||
// Common Headers
|
||||
betTable.Signature = data.ReadUInt32(ref offset);
|
||||
if (betTable.Signature != BetTableSignatureValue)
|
||||
return null;
|
||||
|
||||
betTable.Version = data.ReadUInt32(ref offset);
|
||||
betTable.DataSize = data.ReadUInt32(ref offset);
|
||||
|
||||
// BET-Specific
|
||||
betTable.TableSize = data.ReadUInt32(ref offset);
|
||||
betTable.FileCount = data.ReadUInt32(ref offset);
|
||||
betTable.Unknown = data.ReadUInt32(ref offset);
|
||||
betTable.TableEntrySize = data.ReadUInt32(ref offset);
|
||||
|
||||
betTable.FilePositionBitIndex = data.ReadUInt32(ref offset);
|
||||
betTable.FileSizeBitIndex = data.ReadUInt32(ref offset);
|
||||
betTable.CompressedSizeBitIndex = data.ReadUInt32(ref offset);
|
||||
betTable.FlagIndexBitIndex = data.ReadUInt32(ref offset);
|
||||
betTable.UnknownBitIndex = data.ReadUInt32(ref offset);
|
||||
|
||||
betTable.FilePositionBitCount = data.ReadUInt32(ref offset);
|
||||
betTable.FileSizeBitCount = data.ReadUInt32(ref offset);
|
||||
betTable.CompressedSizeBitCount = data.ReadUInt32(ref offset);
|
||||
betTable.FlagIndexBitCount = data.ReadUInt32(ref offset);
|
||||
betTable.UnknownBitCount = data.ReadUInt32(ref offset);
|
||||
|
||||
betTable.TotalBetHashSize = data.ReadUInt32(ref offset);
|
||||
betTable.BetHashSizeExtra = data.ReadUInt32(ref offset);
|
||||
betTable.BetHashSize = data.ReadUInt32(ref offset);
|
||||
betTable.BetHashArraySize = data.ReadUInt32(ref offset);
|
||||
betTable.FlagCount = data.ReadUInt32(ref offset);
|
||||
|
||||
betTable.FlagsArray = new uint[betTable.FlagCount];
|
||||
Buffer.BlockCopy(data, offset, betTable.FlagsArray, 0, (int)betTable.FlagCount * 4);
|
||||
offset += (int)betTable.FlagCount * 4;
|
||||
|
||||
// TODO: Populate the file table
|
||||
// TODO: Populate the hash table
|
||||
|
||||
return betTable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a byte array into a hash entry
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array to parse</param>
|
||||
/// <param name="offset">Offset into the byte array</param>
|
||||
/// <returns>Filled hash entry on success, null on error</returns>
|
||||
private static HashEntry ParseHashEntry(byte[] data, ref int offset)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
HashEntry hashEntry = new HashEntry();
|
||||
|
||||
hashEntry.NameHashPartA = data.ReadUInt32(ref offset);
|
||||
hashEntry.NameHashPartB = data.ReadUInt32(ref offset);
|
||||
hashEntry.Locale = (Locale)data.ReadUInt16(ref offset);
|
||||
hashEntry.Platform = data.ReadUInt16(ref offset);
|
||||
hashEntry.BlockIndex = data.ReadUInt32(ref offset);
|
||||
|
||||
return hashEntry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a byte array into a block entry
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array to parse</param>
|
||||
/// <param name="offset">Offset into the byte array</param>
|
||||
/// <returns>Filled block entry on success, null on error</returns>
|
||||
private static BlockEntry ParseBlockEntry(byte[] data, ref int offset)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
BlockEntry blockEntry = new BlockEntry();
|
||||
|
||||
blockEntry.FilePosition = data.ReadUInt32(ref offset);
|
||||
blockEntry.CompressedSize = data.ReadUInt32(ref offset);
|
||||
blockEntry.UncompressedSize = data.ReadUInt32(ref offset);
|
||||
blockEntry.Flags = (FileFlags)data.ReadUInt32(ref offset);
|
||||
|
||||
return blockEntry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a byte array into a patch info
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array to parse</param>
|
||||
/// <param name="offset">Offset into the byte array</param>
|
||||
/// <returns>Filled patch info on success, null on error</returns>
|
||||
private static PatchInfo ParsePatchInfo(byte[] data, ref int offset)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
PatchInfo patchInfo = new PatchInfo();
|
||||
|
||||
patchInfo.Length = data.ReadUInt32(ref offset);
|
||||
patchInfo.Flags = data.ReadUInt32(ref offset);
|
||||
patchInfo.DataSize = data.ReadUInt32(ref offset);
|
||||
patchInfo.MD5 = data.ReadBytes(ref offset, 0x10);
|
||||
|
||||
// TODO: Fill the sector offset table
|
||||
|
||||
return patchInfo;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Stream Data
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a MoPaQ archive
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled archive on success, null on error</returns>
|
||||
public static Archive ParseCabinet(Stream data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null)
|
||||
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 archive to fill
|
||||
var archive = new Archive();
|
||||
|
||||
#region User Data
|
||||
|
||||
// Check for User Data
|
||||
uint possibleSignature = data.ReadUInt32();
|
||||
data.Seek(-4, SeekOrigin.Current);
|
||||
if (possibleSignature == UserDataSignatureValue)
|
||||
{
|
||||
// Save the current position for offset correction
|
||||
long basePtr = data.Position;
|
||||
|
||||
// Deserialize the user data, returning null if invalid
|
||||
var userData = ParseUserData(data);
|
||||
if (userData == null)
|
||||
return null;
|
||||
|
||||
// Set the user data
|
||||
archive.UserData = userData;
|
||||
|
||||
// Set the starting position according to the header offset
|
||||
data.Seek(basePtr + (int)archive.UserData.HeaderOffset, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Archive Header
|
||||
|
||||
// Check for the Header
|
||||
possibleSignature = data.ReadUInt32();
|
||||
data.Seek(-4, SeekOrigin.Current);
|
||||
if (possibleSignature == ArchiveHeaderSignatureValue)
|
||||
{
|
||||
// Try to parse the archive header
|
||||
var archiveHeader = ParseArchiveHeader(data);
|
||||
if (archiveHeader == null)
|
||||
return null;
|
||||
|
||||
// Set the archive header
|
||||
archive.ArchiveHeader = archiveHeader;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// TODO: Read in Hash Table
|
||||
// TODO: Read in Block Table
|
||||
// TODO: Read in Hi-Block Table
|
||||
// TODO: Read in BET Table
|
||||
// TODO: Read in HET Table
|
||||
|
||||
return archive;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a archive header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled archive header on success, null on error</returns>
|
||||
private static ArchiveHeader ParseArchiveHeader(Stream data)
|
||||
{
|
||||
ArchiveHeader archiveHeader = new ArchiveHeader();
|
||||
|
||||
// V1 - Common
|
||||
archiveHeader.Signature = data.ReadUInt32();
|
||||
if (archiveHeader.Signature != ArchiveHeaderSignatureValue)
|
||||
return null;
|
||||
|
||||
archiveHeader.HeaderSize = data.ReadUInt32();
|
||||
archiveHeader.ArchiveSize = data.ReadUInt32();
|
||||
archiveHeader.FormatVersion = data.ReadUInt16();
|
||||
archiveHeader.BlockSize = data.ReadUInt16();
|
||||
archiveHeader.HashTablePosition = data.ReadUInt32();
|
||||
archiveHeader.BlockTablePosition = data.ReadUInt32();
|
||||
archiveHeader.HashTableSize = data.ReadUInt32();
|
||||
archiveHeader.BlockTableSize = data.ReadUInt32();
|
||||
|
||||
// V2
|
||||
if (archiveHeader.FormatVersion >= 2 && archiveHeader.HeaderSize >= ArchiveHeaderHeaderVersion2Size)
|
||||
{
|
||||
archiveHeader.HiBlockTablePosition = data.ReadUInt64();
|
||||
archiveHeader.HashTablePositionHi = data.ReadUInt16();
|
||||
archiveHeader.BlockTablePositionHi = data.ReadUInt16();
|
||||
}
|
||||
|
||||
// V3
|
||||
if (archiveHeader.FormatVersion >= 3 && archiveHeader.HeaderSize >= ArchiveHeaderHeaderVersion3Size)
|
||||
{
|
||||
archiveHeader.ArchiveSizeLong = data.ReadUInt64();
|
||||
archiveHeader.BetTablePosition = data.ReadUInt64();
|
||||
archiveHeader.HetTablePosition = data.ReadUInt64();
|
||||
}
|
||||
|
||||
// V4
|
||||
if (archiveHeader.FormatVersion >= 4 && archiveHeader.HeaderSize >= ArchiveHeaderHeaderVersion4Size)
|
||||
{
|
||||
archiveHeader.HashTableSizeLong = data.ReadUInt64();
|
||||
archiveHeader.BlockTableSizeLong = data.ReadUInt64();
|
||||
archiveHeader.HiBlockTableSize = data.ReadUInt64();
|
||||
archiveHeader.HetTableSize = data.ReadUInt64();
|
||||
archiveHeader.BetTablesize = data.ReadUInt64();
|
||||
archiveHeader.RawChunkSize = data.ReadUInt32();
|
||||
|
||||
archiveHeader.BlockTableMD5 = data.ReadBytes(0x10);
|
||||
archiveHeader.HashTableMD5 = data.ReadBytes(0x10);
|
||||
archiveHeader.HiBlockTableMD5 = data.ReadBytes(0x10);
|
||||
archiveHeader.BetTableMD5 = data.ReadBytes(0x10);
|
||||
archiveHeader.HetTableMD5 = data.ReadBytes(0x10);
|
||||
archiveHeader.HetTableMD5 = data.ReadBytes(0x10);
|
||||
}
|
||||
|
||||
return archiveHeader;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a user data object
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled user data on success, null on error</returns>
|
||||
private static UserData ParseUserData(Stream data)
|
||||
{
|
||||
UserData userData = new UserData();
|
||||
|
||||
userData.Signature = data.ReadUInt32();
|
||||
if (userData.Signature != UserDataSignatureValue)
|
||||
return null;
|
||||
|
||||
userData.UserDataSize = data.ReadUInt32();
|
||||
userData.HeaderOffset = data.ReadUInt32();
|
||||
userData.UserDataHeaderSize = data.ReadUInt32();
|
||||
|
||||
return userData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a HET table
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled HET table on success, null on error</returns>
|
||||
private static HetTable ParseHetTable(Stream data)
|
||||
{
|
||||
HetTable hetTable = new HetTable();
|
||||
|
||||
// Common Headers
|
||||
hetTable.Signature = data.ReadUInt32();
|
||||
if (hetTable.Signature != HetTableSignatureValue)
|
||||
return null;
|
||||
|
||||
hetTable.Version = data.ReadUInt32();
|
||||
hetTable.DataSize = data.ReadUInt32();
|
||||
|
||||
// HET-Specific
|
||||
hetTable.TableSize = data.ReadUInt32();
|
||||
hetTable.MaxFileCount = data.ReadUInt32();
|
||||
hetTable.HashTableSize = data.ReadUInt32();
|
||||
hetTable.TotalIndexSize = data.ReadUInt32();
|
||||
hetTable.IndexSizeExtra = data.ReadUInt32();
|
||||
hetTable.IndexSize = data.ReadUInt32();
|
||||
hetTable.BlockTableSize = data.ReadUInt32();
|
||||
hetTable.HashTable = data.ReadBytes((int)hetTable.HashTableSize);
|
||||
|
||||
// TODO: Populate the file indexes array
|
||||
hetTable.FileIndexes = new byte[(int)hetTable.HashTableSize][];
|
||||
|
||||
return hetTable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a BET table
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled BET table on success, null on error</returns>
|
||||
private static BetTable ParseBetTable(Stream data)
|
||||
{
|
||||
BetTable betTable = new BetTable();
|
||||
|
||||
// Common Headers
|
||||
betTable.Signature = data.ReadUInt32();
|
||||
if (betTable.Signature != BetTableSignatureValue)
|
||||
return null;
|
||||
|
||||
betTable.Version = data.ReadUInt32();
|
||||
betTable.DataSize = data.ReadUInt32();
|
||||
|
||||
// BET-Specific
|
||||
betTable.TableSize = data.ReadUInt32();
|
||||
betTable.FileCount = data.ReadUInt32();
|
||||
betTable.Unknown = data.ReadUInt32();
|
||||
betTable.TableEntrySize = data.ReadUInt32();
|
||||
|
||||
betTable.FilePositionBitIndex = data.ReadUInt32();
|
||||
betTable.FileSizeBitIndex = data.ReadUInt32();
|
||||
betTable.CompressedSizeBitIndex = data.ReadUInt32();
|
||||
betTable.FlagIndexBitIndex = data.ReadUInt32();
|
||||
betTable.UnknownBitIndex = data.ReadUInt32();
|
||||
|
||||
betTable.FilePositionBitCount = data.ReadUInt32();
|
||||
betTable.FileSizeBitCount = data.ReadUInt32();
|
||||
betTable.CompressedSizeBitCount = data.ReadUInt32();
|
||||
betTable.FlagIndexBitCount = data.ReadUInt32();
|
||||
betTable.UnknownBitCount = data.ReadUInt32();
|
||||
|
||||
betTable.TotalBetHashSize = data.ReadUInt32();
|
||||
betTable.BetHashSizeExtra = data.ReadUInt32();
|
||||
betTable.BetHashSize = data.ReadUInt32();
|
||||
betTable.BetHashArraySize = data.ReadUInt32();
|
||||
betTable.FlagCount = data.ReadUInt32();
|
||||
|
||||
betTable.FlagsArray = new uint[betTable.FlagCount];
|
||||
byte[] flagsArray = data.ReadBytes((int)betTable.FlagCount * 4);
|
||||
Buffer.BlockCopy(flagsArray, 0, betTable.FlagsArray, 0, (int)betTable.FlagCount * 4);
|
||||
|
||||
// TODO: Populate the file table
|
||||
// TODO: Populate the hash table
|
||||
|
||||
return betTable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a hash entry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled hash entry on success, null on error</returns>
|
||||
private static HashEntry ParseHashEntry(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
HashEntry hashEntry = new HashEntry();
|
||||
|
||||
hashEntry.NameHashPartA = data.ReadUInt32();
|
||||
hashEntry.NameHashPartB = data.ReadUInt32();
|
||||
hashEntry.Locale = (Locale)data.ReadUInt16();
|
||||
hashEntry.Platform = data.ReadUInt16();
|
||||
hashEntry.BlockIndex = data.ReadUInt32();
|
||||
|
||||
return hashEntry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a block entry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled block entry on success, null on error</returns>
|
||||
private static BlockEntry ParseBlockEntry(Stream data)
|
||||
{
|
||||
BlockEntry blockEntry = new BlockEntry();
|
||||
|
||||
blockEntry.FilePosition = data.ReadUInt32();
|
||||
blockEntry.CompressedSize = data.ReadUInt32();
|
||||
blockEntry.UncompressedSize = data.ReadUInt32();
|
||||
blockEntry.Flags = (FileFlags)data.ReadUInt32();
|
||||
|
||||
return blockEntry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a patch info
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="offset">Offset into the byte array</param>
|
||||
/// <returns>Filled patch info on success, null on error</returns>
|
||||
private static PatchInfo ParsePatchInfo(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
PatchInfo patchInfo = new PatchInfo();
|
||||
|
||||
patchInfo.Length = data.ReadUInt32();
|
||||
patchInfo.Flags = data.ReadUInt32();
|
||||
patchInfo.DataSize = data.ReadUInt32();
|
||||
patchInfo.MD5 = data.ReadBytes(0x10);
|
||||
|
||||
// TODO: Fill the sector offset table
|
||||
|
||||
return patchInfo;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,6 @@ namespace BurnOutSharp.Models.MoPaQ
|
||||
/// <summary>
|
||||
/// Flags for the file.
|
||||
/// </summary>
|
||||
public MoPaQFileFlags Flags;
|
||||
public FileFlags Flags;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace BurnOutSharp.Models.MoPaQ
|
||||
{
|
||||
[Flags]
|
||||
public enum MoPaQFileFlags : uint
|
||||
public enum FileFlags : uint
|
||||
{
|
||||
/// <summary>
|
||||
/// File is compressed using PKWARE Data compression library
|
||||
@@ -76,7 +76,7 @@ namespace BurnOutSharp.Models.MoPaQ
|
||||
EnglishUK = 0x809,
|
||||
}
|
||||
|
||||
public enum MoPaQPatchType : uint
|
||||
public enum PatchType : uint
|
||||
{
|
||||
/// <summary>
|
||||
/// Blizzard-modified version of BSDIFF40 incremental patch
|
||||
|
||||
@@ -73,7 +73,7 @@ namespace BurnOutSharp.Models.MoPaQ
|
||||
/// <summary>
|
||||
/// Type of patch ('BSD0' or 'COPY')
|
||||
/// </summary>
|
||||
public MoPaQPatchType PatchType { get; private set; }
|
||||
public PatchType PatchType { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -95,541 +95,5 @@ namespace BurnOutSharp.FileType
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// http://zezula.net/en/mpq/mpqformat.html
|
||||
#region TEMPORARY AREA FOR MPQ FORMAT
|
||||
|
||||
internal class MoPaQArchive
|
||||
{
|
||||
//#region Serialization
|
||||
|
||||
///// <summary>
|
||||
///// Deserialize <paramref name="data"/> at <paramref name="dataPtr"/> into a new MoPaQArchive object
|
||||
///// </summary>
|
||||
//public static MoPaQArchive Deserialize(byte[] data, ref int dataPtr)
|
||||
//{
|
||||
// if (data == null || dataPtr < 0)
|
||||
// return null;
|
||||
|
||||
// MoPaQArchive archive = new MoPaQArchive();
|
||||
|
||||
// // Check for User Data
|
||||
// uint possibleSignature = BitConverter.ToUInt32(data, dataPtr);
|
||||
// if (possibleSignature == MoPaQUserData.SignatureValue)
|
||||
// {
|
||||
// // Save the current position for offset correction
|
||||
// int basePtr = dataPtr;
|
||||
|
||||
// // Deserialize the user data, returning null if invalid
|
||||
// archive.UserData = MoPaQUserData.Deserialize(data, ref dataPtr);
|
||||
// if (archive.UserData == null)
|
||||
// return null;
|
||||
|
||||
// // Set the starting position according to the header offset
|
||||
// dataPtr = basePtr + (int)archive.UserData.HeaderOffset;
|
||||
// }
|
||||
|
||||
// // Check for the Header
|
||||
// possibleSignature = BitConverter.ToUInt32(data, dataPtr);
|
||||
// if (possibleSignature == MoPaQArchiveHeader.SignatureValue)
|
||||
// {
|
||||
// // Deserialize the header, returning null if invalid
|
||||
// archive.ArchiveHeader = MoPaQArchiveHeader.Deserialize(data, ref dataPtr);
|
||||
// if (archive.ArchiveHeader == null)
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// // If we don't have a header, return null
|
||||
// if (archive.ArchiveHeader == null)
|
||||
// return null;
|
||||
|
||||
// // TODO: Read in Hash Table
|
||||
// // TODO: Read in Block Table
|
||||
// // TODO: Read in Hi-Block Table
|
||||
// // TODO: Read in BET Table
|
||||
// // TODO: Read in HET Table
|
||||
|
||||
// return archive;
|
||||
//}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
|
||||
internal class MoPaQUserData
|
||||
{
|
||||
//#region Constants
|
||||
|
||||
//public const int Size = 0x10;
|
||||
|
||||
///// <summary>
|
||||
///// Human-readable signature
|
||||
///// </summary>
|
||||
//public static readonly string SignatureString = $"MPQ{(char)0x1B}";
|
||||
|
||||
///// <summary>
|
||||
///// Signature as an unsigned Int32 value
|
||||
///// </summary>
|
||||
//public const uint SignatureValue = 0x1B51504D;
|
||||
|
||||
///// <summary>
|
||||
///// Signature as a byte array
|
||||
///// </summary>
|
||||
//public static readonly byte[] SignatureBytes = new byte[] { 0x4D, 0x50, 0x51, 0x1B };
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Serialization
|
||||
|
||||
///// <summary>
|
||||
///// Deserialize <paramref name="data"/> at <paramref name="dataPtr"/> into a new MoPaQUserData object
|
||||
///// </summary>
|
||||
//public static MoPaQUserData Deserialize(byte[] data, ref int dataPtr)
|
||||
//{
|
||||
// if (data == null || dataPtr < 0)
|
||||
// return null;
|
||||
|
||||
// MoPaQUserData userData = new MoPaQUserData();
|
||||
|
||||
// userData.Signature = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// if (userData.Signature != SignatureValue)
|
||||
// return null;
|
||||
|
||||
// userData.UserDataSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// userData.HeaderOffset = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// userData.UserDataHeaderSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
|
||||
// return userData;
|
||||
//}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
|
||||
internal class MoPaQArchiveHeader
|
||||
{
|
||||
//#region Constants
|
||||
|
||||
//#region Signatures
|
||||
|
||||
///// <summary>
|
||||
///// Human-readable signature
|
||||
///// </summary>
|
||||
//public static readonly string SignatureString = $"MPQ{(char)0x1A}";
|
||||
|
||||
///// <summary>
|
||||
///// Signature as an unsigned Int32 value
|
||||
///// </summary>
|
||||
//public const uint SignatureValue = 0x1A51504D;
|
||||
|
||||
///// <summary>
|
||||
///// Signature as a byte array
|
||||
///// </summary>
|
||||
//public static readonly byte[] SignatureBytes = new byte[] { 0x4D, 0x50, 0x51, 0x1A };
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Header Sizes
|
||||
|
||||
//public const int HeaderVersion1Size = 0x20;
|
||||
|
||||
//public const int HeaderVersion2Size = 0x2C;
|
||||
|
||||
//public const int HeaderVersion3Size = 0x44;
|
||||
|
||||
//public const int HeaderVersion4Size = 0xD0;
|
||||
|
||||
//#endregion
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Serialization
|
||||
|
||||
///// <summary>
|
||||
///// Deserialize <paramref name="data"/> at <paramref name="dataPtr"/> into a new MoPaQArchiveHeader object
|
||||
///// </summary>
|
||||
//public static MoPaQArchiveHeader Deserialize(byte[] data, ref int dataPtr)
|
||||
//{
|
||||
// if (data == null || dataPtr < 0)
|
||||
// return null;
|
||||
|
||||
// MoPaQArchiveHeader archiveHeader = new MoPaQArchiveHeader();
|
||||
|
||||
// // V1 - Common
|
||||
// archiveHeader.Signature = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// if (archiveHeader.Signature != SignatureValue)
|
||||
// return null;
|
||||
|
||||
// archiveHeader.HeaderSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// archiveHeader.ArchiveSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// archiveHeader.FormatVersion = BitConverter.ToUInt16(data, dataPtr); dataPtr += 2;
|
||||
// archiveHeader.BlockSize = BitConverter.ToUInt16(data, dataPtr); dataPtr += 2;
|
||||
// archiveHeader.HashTablePosition = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// archiveHeader.BlockTablePosition = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// archiveHeader.HashTableSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// archiveHeader.BlockTableSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
|
||||
// // V2
|
||||
// if (archiveHeader.FormatVersion >= 2 && archiveHeader.HeaderSize >= HeaderVersion2Size)
|
||||
// {
|
||||
// archiveHeader.HiBlockTablePosition = BitConverter.ToUInt64(data, dataPtr); dataPtr += 8;
|
||||
// archiveHeader.HashTablePositionHi = BitConverter.ToUInt16(data, dataPtr); dataPtr += 2;
|
||||
// archiveHeader.BlockTablePositionHi = BitConverter.ToUInt16(data, dataPtr); dataPtr += 2;
|
||||
// }
|
||||
|
||||
// // V3
|
||||
// if (archiveHeader.FormatVersion >= 3 && archiveHeader.HeaderSize >= HeaderVersion3Size)
|
||||
// {
|
||||
// archiveHeader.ArchiveSizeLong = BitConverter.ToUInt64(data, dataPtr); dataPtr += 8;
|
||||
// archiveHeader.BetTablePosition = BitConverter.ToUInt64(data, dataPtr); dataPtr += 8;
|
||||
// archiveHeader.HetTablePosition = BitConverter.ToUInt64(data, dataPtr); dataPtr += 8;
|
||||
// }
|
||||
|
||||
// // V4
|
||||
// if (archiveHeader.FormatVersion >= 4 && archiveHeader.HeaderSize >= HeaderVersion4Size)
|
||||
// {
|
||||
// archiveHeader.HashTableSizeLong = BitConverter.ToUInt64(data, dataPtr); dataPtr += 8;
|
||||
// archiveHeader.BlockTableSizeLong = BitConverter.ToUInt64(data, dataPtr); dataPtr += 8;
|
||||
// archiveHeader.HiBlockTableSize = BitConverter.ToUInt64(data, dataPtr); dataPtr += 8;
|
||||
// archiveHeader.HetTableSize = BitConverter.ToUInt64(data, dataPtr); dataPtr += 8;
|
||||
// archiveHeader.BetTablesize = BitConverter.ToUInt64(data, dataPtr); dataPtr += 8;
|
||||
// archiveHeader.RawChunkSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
|
||||
// Array.Copy(data, dataPtr, archiveHeader.BlockTableMD5, 0, 0x10); dataPtr += 0x10;
|
||||
// Array.Copy(data, dataPtr, archiveHeader.HashTableMD5, 0, 0x10); dataPtr += 0x10;
|
||||
// Array.Copy(data, dataPtr, archiveHeader.HiBlockTableMD5, 0, 0x10); dataPtr += 0x10;
|
||||
// Array.Copy(data, dataPtr, archiveHeader.BetTableMD5, 0, 0x10); dataPtr += 0x10;
|
||||
// Array.Copy(data, dataPtr, archiveHeader.HetTableMD5, 0, 0x10); dataPtr += 0x10;
|
||||
// Array.Copy(data, dataPtr, archiveHeader.MpqHeaderMD5, 0, 0x10); dataPtr += 0x10;
|
||||
// }
|
||||
|
||||
// return archiveHeader;
|
||||
//}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
|
||||
internal class MoPaQHetTable
|
||||
{
|
||||
//#region Constants
|
||||
|
||||
//public const int Size = 0x44;
|
||||
|
||||
///// <summary>
|
||||
///// Human-readable signature
|
||||
///// </summary>
|
||||
//public static readonly string SignatureString = $"HET{(char)0x1A}";
|
||||
|
||||
///// <summary>
|
||||
///// Signature as an unsigned Int32 value
|
||||
///// </summary>
|
||||
//public const uint SignatureValue = 0x1A544548;
|
||||
|
||||
///// <summary>
|
||||
///// Signature as a byte array
|
||||
///// </summary>
|
||||
//public static readonly byte[] SignatureBytes = new byte[] { 0x48, 0x45, 0x54, 0x1A };
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Serialization
|
||||
|
||||
///// <summary>
|
||||
///// Deserialize <paramref name="data"/> at <paramref name="dataPtr"/> into a new MoPaQHetTable object
|
||||
///// </summary>
|
||||
//public static MoPaQHetTable Deserialize(byte[] data, ref int dataPtr)
|
||||
//{
|
||||
// if (data == null || dataPtr < 0)
|
||||
// return null;
|
||||
|
||||
// MoPaQHetTable hetTable = new MoPaQHetTable();
|
||||
|
||||
// // Common Headers
|
||||
// hetTable.Signature = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// if (hetTable.Signature != SignatureValue)
|
||||
// return null;
|
||||
|
||||
// hetTable.Version = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// hetTable.DataSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
|
||||
// // HET-Specific
|
||||
// hetTable.TableSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// hetTable.MaxFileCount = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// hetTable.HashTableSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// hetTable.TotalIndexSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// hetTable.IndexSizeExtra = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// hetTable.IndexSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// hetTable.BlockTableSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
|
||||
// hetTable.HashTable = new byte[hetTable.HashTableSize];
|
||||
// Array.Copy(data, dataPtr, hetTable.HashTable, 0, hetTable.HashTableSize);
|
||||
// dataPtr += (int)hetTable.HashTableSize;
|
||||
|
||||
// // TODO: Populate the file indexes array
|
||||
// hetTable.FileIndexes = new byte[(int)hetTable.HashTableSize][];
|
||||
|
||||
// return hetTable;
|
||||
//}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
|
||||
internal class MoPaQBetTable
|
||||
{
|
||||
//#region Constants
|
||||
|
||||
//public const int Size = 0x88;
|
||||
|
||||
///// <summary>
|
||||
///// Human-readable signature
|
||||
///// </summary>
|
||||
//public static readonly string SignatureString = $"BET{(char)0x1A}";
|
||||
|
||||
///// <summary>
|
||||
///// Signature as an unsigned Int32 value
|
||||
///// </summary>
|
||||
//public const uint SignatureValue = 0x1A544542;
|
||||
|
||||
///// <summary>
|
||||
///// Signature as a byte array
|
||||
///// </summary>
|
||||
//public static readonly byte[] SignatureBytes = new byte[] { 0x42, 0x45, 0x54, 0x1A };
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Serialization
|
||||
|
||||
///// <summary>
|
||||
///// Deserialize <paramref name="data"/> at <paramref name="dataPtr"/> into a new MoPaQBetTable object
|
||||
///// </summary>
|
||||
//public static MoPaQBetTable Deserialize(byte[] data, ref int dataPtr)
|
||||
//{
|
||||
// if (data == null || dataPtr < 0)
|
||||
// return null;
|
||||
|
||||
// MoPaQBetTable betTable = new MoPaQBetTable();
|
||||
|
||||
// // Common Headers
|
||||
// betTable.Signature = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// if (betTable.Signature != SignatureValue)
|
||||
// return null;
|
||||
|
||||
// betTable.Version = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// betTable.DataSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
|
||||
// // BET-Specific
|
||||
// betTable.TableSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// betTable.FileCount = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// betTable.Unknown = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// betTable.TableEntrySize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
|
||||
// betTable.FilePositionBitIndex = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// betTable.FileSizeBitIndex = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// betTable.CompressedSizeBitIndex = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// betTable.FlagIndexBitIndex = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// betTable.UnknownBitIndex = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
|
||||
// betTable.FilePositionBitCount = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// betTable.FileSizeBitCount = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// betTable.CompressedSizeBitCount = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// betTable.FlagIndexBitCount = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// betTable.UnknownBitCount = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
|
||||
// betTable.TotalBetHashSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// betTable.BetHashSizeExtra = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// betTable.BetHashSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// betTable.BetHashArraySize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// betTable.FlagCount = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
|
||||
// betTable.FlagsArray = new uint[betTable.FlagCount];
|
||||
// Buffer.BlockCopy(data, dataPtr, betTable.FlagsArray, 0, (int)betTable.FlagCount * 4);
|
||||
// dataPtr += (int)betTable.FlagCount * 4;
|
||||
|
||||
// // TODO: Populate the file table
|
||||
// // TODO: Populate the hash table
|
||||
|
||||
// return betTable;
|
||||
//}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
|
||||
internal class MoPaQHashEntry
|
||||
{
|
||||
//#region Constants
|
||||
|
||||
//public const int Size = 0x10;
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Serialization
|
||||
|
||||
///// <summary>
|
||||
///// Deserialize <paramref name="data"/> at <paramref name="dataPtr"/> into a new MoPaQHashEntry object
|
||||
///// </summary>
|
||||
//public static MoPaQHashEntry Deserialize(byte[] data, ref int dataPtr)
|
||||
//{
|
||||
// if (data == null || dataPtr < 0)
|
||||
// return null;
|
||||
|
||||
// MoPaQHashEntry hashEntry = new MoPaQHashEntry();
|
||||
|
||||
// hashEntry.NameHashPartA = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// hashEntry.NameHashPartB = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// hashEntry.Locale = (MoPaQLocale)BitConverter.ToUInt16(data, dataPtr); dataPtr += 2;
|
||||
// hashEntry.Platform = BitConverter.ToUInt16(data, dataPtr); dataPtr += 2;
|
||||
// hashEntry.BlockIndex = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
|
||||
// return hashEntry;
|
||||
//}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
|
||||
internal class MoPaQBlockEntry
|
||||
{
|
||||
//#region Constants
|
||||
|
||||
//public const int Size = 0x10;
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Serialization
|
||||
|
||||
///// <summary>
|
||||
///// Deserialize <paramref name="data"/> at <paramref name="dataPtr"/> into a new MoPaQBlockEntry object
|
||||
///// </summary>
|
||||
//public static MoPaQBlockEntry Deserialize(byte[] data, ref int dataPtr)
|
||||
//{
|
||||
// if (data == null || dataPtr < 0)
|
||||
// return null;
|
||||
|
||||
// MoPaQBlockEntry blockEntry = new MoPaQBlockEntry();
|
||||
|
||||
// blockEntry.FilePosition = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// blockEntry.CompressedSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// blockEntry.UncompressedSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// blockEntry.Flags = (MoPaQFileFlags)BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
|
||||
// return blockEntry;
|
||||
//}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
|
||||
internal class MoPaQPatchInfo
|
||||
{
|
||||
//#region Serialization
|
||||
|
||||
///// <summary>
|
||||
///// Deserialize <paramref name="data"/> at <paramref name="dataPtr"/> into a new MoPaQPatchInfo object
|
||||
///// </summary>
|
||||
//public static MoPaQPatchInfo Deserialize(byte[] data, ref int dataPtr)
|
||||
//{
|
||||
// if (data == null || dataPtr < 0)
|
||||
// return null;
|
||||
|
||||
// MoPaQPatchInfo patchInfo = new MoPaQPatchInfo();
|
||||
|
||||
// patchInfo.Length = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// patchInfo.Flags = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// patchInfo.DataSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
|
||||
// Array.Copy(data, dataPtr, patchInfo.MD5, 0, 0x10); dataPtr += 0x10;
|
||||
|
||||
// // TODO: Fill the sector offset table
|
||||
|
||||
// return patchInfo;
|
||||
//}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
|
||||
internal class MoPaQPatchHeader
|
||||
{
|
||||
//#region Constants
|
||||
|
||||
//#region Signatures
|
||||
|
||||
//#region Patch Header
|
||||
|
||||
///// <summary>
|
||||
///// Human-readable signature
|
||||
///// </summary>
|
||||
//public static readonly string PatchSignatureString = $"PTCH";
|
||||
|
||||
///// <summary>
|
||||
///// Signature as an unsigned Int32 value
|
||||
///// </summary>
|
||||
//public const uint PatchSignatureValue = 0x48435450;
|
||||
|
||||
///// <summary>
|
||||
///// Signature as a byte array
|
||||
///// </summary>
|
||||
//public static readonly byte[] PatchSignatureBytes = new byte[] { 0x50, 0x54, 0x43, 0x48 };
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region MD5 Block
|
||||
|
||||
///// <summary>
|
||||
///// Human-readable signature
|
||||
///// </summary>
|
||||
//public static readonly string Md5SignatureString = $"MD5_";
|
||||
|
||||
///// <summary>
|
||||
///// Signature as an unsigned Int32 value
|
||||
///// </summary>
|
||||
//public const uint Md5SignatureValue = 0x5F35444D;
|
||||
|
||||
///// <summary>
|
||||
///// Signature as a byte array
|
||||
///// </summary>
|
||||
//public static readonly byte[] Md5SignatureBytes = new byte[] { 0x4D, 0x44, 0x35, 0x5F };
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region XFRM Block
|
||||
|
||||
///// <summary>
|
||||
///// Human-readable signature
|
||||
///// </summary>
|
||||
//public static readonly string XFRMSignatureString = $"XFRM";
|
||||
|
||||
///// <summary>
|
||||
///// Signature as an unsigned Int32 value
|
||||
///// </summary>
|
||||
//public const uint XFRMSignatureValue = 0x4D524658;
|
||||
|
||||
///// <summary>
|
||||
///// Signature as a byte array
|
||||
///// </summary>
|
||||
//public static readonly byte[] XFRMSignatureBytes = new byte[] { 0x58, 0x46, 0x52, 0x4D };
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region BSDIFF Patch Type
|
||||
|
||||
///// <summary>
|
||||
///// Human-readable signature
|
||||
///// </summary>
|
||||
//public static readonly string BSDIFF40SignatureString = $"BSDIFF40";
|
||||
|
||||
///// <summary>
|
||||
///// Signature as an unsigned Int64 value
|
||||
///// </summary>
|
||||
//public const ulong BSDIFF40SignatureValue = 0x3034464649445342;
|
||||
|
||||
///// <summary>
|
||||
///// Signature as a byte array
|
||||
///// </summary>
|
||||
//public static readonly byte[] BSDIFF40SignatureBytes = new byte[] { 0x42, 0x53, 0x44, 0x49, 0x46, 0x46, 0x34, 0x30 };
|
||||
|
||||
|
||||
//#endregion
|
||||
|
||||
//#endregion
|
||||
|
||||
//#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user