mirror of
https://github.com/SabreTools/BinaryObjectScanner.git
synced 2026-07-09 02:16:46 +00:00
Determine PLJv2 Block 3 format, add notes
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
35
BurnOutSharp.Models/PlayJ/DataFile.cs
Normal file
35
BurnOutSharp.Models/PlayJ/DataFile.cs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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/>
|
||||
|
||||
Reference in New Issue
Block a user