Determine PLJv2 Block 3 format, add notes

This commit is contained in:
Matt Nadareski
2023-01-15 02:07:56 -08:00
parent 62b1627b04
commit e99ba48f07
6 changed files with 220 additions and 37 deletions

View File

@@ -168,47 +168,88 @@ namespace BurnOutSharp.Builders
#endregion
#region Unknown Value 2
#region V1 Only
// If we have an unknown value 2 offset
if (entryHeader.UnknownOffset2 > 0)
// If we have a V1 file
if (entryHeader.Version == 0x00000000)
{
// Get the unknown value 2 offset
long offset = entryHeader.UnknownOffset2 + adjust;
if (offset < 0 || offset >= data.Length)
#region Unknown Value 2
// If we have an unknown value 2 offset
if (entryHeader.UnknownOffset2 > 0)
{
// Get the unknown value 2 offset
long offset = entryHeader.UnknownOffset2 + adjust;
if (offset < 0 || offset >= data.Length)
return null;
// Seek to the unknown value 2
data.Seek(offset, SeekOrigin.Begin);
}
// Set the unknown value 2
audioFile.UnknownValue2 = data.ReadUInt32();
#endregion
#region Unknown Block 3
// If we have an unknown block 3 offset
if (entryHeader.UnknownOffset2 > 0)
{
// Get the unknown block 3 offset
long offset = entryHeader.UnknownOffset3 + adjust;
if (offset < 0 || offset >= data.Length)
return null;
// Seek to the unknown block 3
data.Seek(offset, SeekOrigin.Begin);
}
// Try to parse the unknown block 3
var unknownBlock3 = ParseUnknownBlock3(data);
if (unknownBlock3 == null)
return null;
// Seek to the unknown value 2
data.Seek(offset, SeekOrigin.Begin);
}
// Set the unknown block 3
audioFile.UnknownBlock3 = unknownBlock3;
// Set the unknown value 2
audioFile.UnknownValue2 = data.ReadUInt32();
#endregion
}
#endregion
#region Unknown Block 3
#region V2 Only
// If we have an unknown block 3 offset
if (entryHeader.UnknownOffset3 > 0)
// If we have a V2 file
if (entryHeader.Version == 0x0000000A)
{
// Get the unknown block 3 offset
long offset = entryHeader.UnknownOffset3 + adjust;
if (offset < 0 || offset >= data.Length)
return null;
#region Data Files Count
// Seek to the unknown block 3
data.Seek(offset, SeekOrigin.Begin);
// Set the data files count
audioFile.DataFilesCount = data.ReadUInt32();
#endregion
#region Data Files
// Create the data files array
audioFile.DataFiles = new DataFile[audioFile.DataFilesCount];
// Try to parse the data files
for (int i = 0; i < audioFile.DataFiles.Length; i++)
{
var dataFile = ParseDataFile(data);
if (dataFile == null)
return null;
audioFile.DataFiles[i] = dataFile;
}
#endregion
}
// Try to parse the unknown block 3
var unknownBlock3 = ParseUnknownBlock3(data);
if (unknownBlock3 == null)
return null;
// Set the unknown block 3
audioFile.UnknownBlock3 = unknownBlock3;
#endregion
return audioFile;
@@ -326,8 +367,8 @@ namespace BurnOutSharp.Builders
// TODO: Use marshalling here instead of building
UnknownBlock1 unknownBlock1 = new UnknownBlock1();
unknownBlock1.Length = data.ReadUInt16();
unknownBlock1.Data = data.ReadBytes(unknownBlock1.Length);
unknownBlock1.Length = data.ReadUInt32();
unknownBlock1.Data = data.ReadBytes((int)unknownBlock1.Length);
return unknownBlock1;
}
@@ -347,6 +388,27 @@ namespace BurnOutSharp.Builders
return unknownBlock3;
}
/// <summary>
/// Parse a Stream into a data file
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled data file on success, null on error</returns>
private static DataFile ParseDataFile(Stream data)
{
// TODO: Use marshalling here instead of building
DataFile dataFile = new DataFile();
dataFile.FileNameLength = data.ReadUInt16();
byte[] fileName = data.ReadBytes(dataFile.FileNameLength);
if (fileName != null)
dataFile.FileName = Encoding.ASCII.GetString(fileName);
dataFile.DataLength = data.ReadUInt32();
dataFile.Data = data.ReadBytes((int)dataFile.DataLength);
return dataFile;
}
#endregion
}
}

View File

@@ -15,6 +15,8 @@ namespace BurnOutSharp.Models.PlayJ
/// </summary>
public UnknownBlock1 UnknownBlock1 { get; set; }
#region V1 Only
/// <summary>
/// Value referred to by <see cref="EntryHeader.UnknownOffset2"/>
/// </summary>
@@ -22,8 +24,27 @@ namespace BurnOutSharp.Models.PlayJ
public uint UnknownValue2 { get; set; }
/// <summary>
/// Unknown block 3
/// Unknown block 3 (V1 only)
/// </summary>
public UnknownBlock3 UnknownBlock3 { get; set; }
#endregion
#region V2 Only
/// <summary>
/// Number of data files embedded
/// </summary>
public uint DataFilesCount { get; set; }
/// <summary>
/// Data files (V2 only)
/// </summary>
public DataFile[] DataFiles { get; set; }
// After the data files is a block starting with 0x00000001
// This block then contains highly repeating data, possible audio samples?
#endregion
}
}

View File

@@ -0,0 +1,35 @@
namespace BurnOutSharp.Models.PlayJ
{
/// <summary>
/// Embedded data file (V2 only?)
/// </summary>
public sealed class DataFile
{
/// <summary>
/// Length of the data file name
/// </summary>
public ushort FileNameLength;
/// <summary>
/// Data file name
/// </summary>
public string FileName;
/// <summary>
/// Length of the data
/// </summary>
public uint DataLength;
/// <summary>
/// Data
/// </summary>
public byte[] Data;
// Notes about Data:
// - Each data block in the samples contains a GIF header
// - Each GIF header contains an application extension: http://www.vurdalakov.net/misc/gif/application-extension
// - Each GIF doesn't always stretch to the end of the data
// - Remaining data seems to be padded as 0x00 (typically 8 bytes)
// - GIF data is fully formed and may be copied to a standalone file
}
}

View File

@@ -8,11 +8,17 @@ namespace BurnOutSharp.Models.PlayJ
/// <summary>
/// Length of the following data block
/// </summary>
public ushort Length;
public uint Length;
/// <summary>
/// Unknown data
/// </summary>
public byte[] Data;
// Notes about Data:
// - Might be UInt16 offset and UInt16 length pairs
// - Might be relevant to ad data
// - Might be relevant to encryption
// - Highly repeating patterns in the values with only a few differences
}
}

View File

@@ -9,5 +9,10 @@ namespace BurnOutSharp.Models.PlayJ
/// Unknown data
/// </summary>
public byte[] Data;
// Notes about Data:
// - This may be where the encrypted audio samples live
// - It is also possible that it's where the ad data lives and samples follow
// + See V2 for example of why this would be the case
}
}

View File

@@ -93,13 +93,15 @@ namespace BurnOutSharp.Wrappers
#region Unknown Block 1
/// <inheritdoc cref="Models.PlayJ.UnknownBlock1.Length"/>
public ushort UB1_Length => _audioFile.UnknownBlock1.Length;
public uint UB1_Length => _audioFile.UnknownBlock1.Length;
/// <inheritdoc cref="Models.PlayJ.UnknownBlock1.Data"/>
public byte[] UB1_Data => _audioFile.UnknownBlock1.Data;
#endregion
#region V1 Only
#region Unknown Value 2
/// <inheritdoc cref="Models.PlayJ.AudioFile.UnknownValue2"/>
@@ -116,6 +118,26 @@ namespace BurnOutSharp.Wrappers
#endregion
#region V2 Only
#region Data Files Count
/// <inheritdoc cref="Models.PlayJ.AudioFile.DataFilesCount"/>
public uint DataFilesCount => _audioFile.DataFilesCount;
#endregion
#region Unknown Block 3
/// <inheritdoc cref="Models.PlayJ.AudioFile.DataFiles"/>
public Models.PlayJ.DataFile[] DataFiles => _audioFile.DataFiles;
#endregion
#endregion
#endregion
#region Instance Variables
/// <summary>
@@ -192,8 +214,16 @@ namespace BurnOutSharp.Wrappers
PrintEntryHeader(builder);
PrintUnknownBlock1(builder);
PrintUnknownValue2(builder);
PrintUnknownBlock3(builder);
if (Version == 0x00000000)
{
PrintUnknownValue2(builder);
PrintUnknownBlock3(builder);
}
else if (Version == 0x0000000A)
{
PrintDataFiles(builder);
}
return builder;
}
@@ -249,7 +279,7 @@ namespace BurnOutSharp.Wrappers
}
/// <summary>
/// Print unknown value 2 information
/// Print unknown value 2 information (V1 only)
/// </summary>
/// <param name="builder">StringBuilder to append information to</param>
private void PrintUnknownValue2(StringBuilder builder)
@@ -261,7 +291,7 @@ namespace BurnOutSharp.Wrappers
}
/// <summary>
/// Print unknown block 3 information
/// Print unknown block 3 information (V1 only)
/// </summary>
/// <param name="builder">StringBuilder to append information to</param>
private void PrintUnknownBlock3(StringBuilder builder)
@@ -272,6 +302,30 @@ namespace BurnOutSharp.Wrappers
builder.AppendLine();
}
/// <summary>
/// Print data files information (V2 only)
/// </summary>
/// <param name="builder">StringBuilder to append information to</param>
private void PrintDataFiles(StringBuilder builder)
{
builder.AppendLine(" Data Files Information:");
builder.AppendLine(" -------------------------");
builder.AppendLine($" Data files count: {DataFilesCount} (0x{DataFilesCount:X})");
if (DataFilesCount != 0 && DataFiles != null && DataFiles.Length != 0)
{
for (int i = 0; i < DataFiles.Length; i++)
{
var dataFile = DataFiles[i];
builder.AppendLine($" Data File {i}:");
builder.AppendLine($" File name length: {dataFile.FileNameLength} (0x{dataFile.FileNameLength:X})");
builder.AppendLine($" File name: {dataFile.FileName ?? "[NULL]"}");
builder.AppendLine($" Data length: {dataFile.DataLength} (0x{dataFile.DataLength:X})");
builder.AppendLine($" Data: {BitConverter.ToString(dataFile.Data ?? new byte[0]).Replace('-', ' ')}");
}
}
builder.AppendLine();
}
#if NET6_0_OR_GREATER
/// <inheritdoc/>