diff --git a/BurnOutSharp.Builders/PlayJ.cs b/BurnOutSharp.Builders/PlayJ.cs
index 4ae28ab2..d2a3aca2 100644
--- a/BurnOutSharp.Builders/PlayJ.cs
+++ b/BurnOutSharp.Builders/PlayJ.cs
@@ -357,6 +357,130 @@ namespace BurnOutSharp.Builders
return entryHeader;
}
+ ///
+ /// Parse a Stream into an audio header
+ ///
+ /// Stream to parse
+ /// Filled audio header on success, null on error
+ private static AudioHeader ParseAudioHeader(Stream data)
+ {
+ // Cache the current offset
+ long initialOffset = data.Position;
+
+ // TODO: Use marshalling here instead of building
+ AudioHeader audioHeader;
+
+ // Get the common header pieces
+ uint signature = data.ReadUInt32();
+ if (signature != SignatureUInt32)
+ return null;
+
+ uint version = data.ReadUInt32();
+
+ // Build the header according to version
+ uint unknownOffset1;
+ switch (version)
+ {
+ // Version 1
+ case 0x00000000:
+ AudioHeaderV1 v1 = new AudioHeaderV1();
+
+ v1.Signature = signature;
+ v1.Version = version;
+ v1.TrackID = data.ReadUInt32();
+ v1.UnknownOffset1 = data.ReadUInt32();
+ v1.UnknownOffset2 = data.ReadUInt32();
+ v1.UnknownOffset3 = data.ReadUInt32();
+ v1.Unknown1 = data.ReadUInt32();
+ v1.Unknown2 = data.ReadUInt32();
+ v1.Year = data.ReadUInt32();
+ v1.TrackNumber = data.ReadByteValue();
+ v1.Subgenre = (Subgenre)data.ReadByteValue();
+ v1.Duration = data.ReadUInt32();
+
+ audioHeader = v1;
+ unknownOffset1 = v1.UnknownOffset1;
+ break;
+
+ // Version 2
+ case 0x0000000A:
+ AudioHeaderV2 v2 = new AudioHeaderV2();
+
+ v2.Signature = signature;
+ v2.Version = version;
+ v2.Unknown1 = data.ReadUInt32();
+ v2.Unknown2 = data.ReadUInt32();
+ v2.Unknown3 = data.ReadUInt32();
+ v2.Unknown4 = data.ReadUInt32();
+ v2.Unknown5 = data.ReadUInt32();
+ v2.Unknown6 = data.ReadUInt32();
+ v2.UnknownOffset1 = data.ReadUInt32();
+ v2.Unknown7 = data.ReadUInt32();
+ v2.Unknown8 = data.ReadUInt32();
+ v2.Unknown9 = data.ReadUInt32();
+ v2.UnknownOffset2 = data.ReadUInt32();
+ v2.Unknown10 = data.ReadUInt32();
+ v2.Unknown11 = data.ReadUInt32();
+ v2.Unknown12 = data.ReadUInt32();
+ v2.TrackID = data.ReadUInt32();
+ v2.Year = data.ReadUInt32();
+ v2.TrackNumber = data.ReadUInt32();
+ v2.Unknown13 = data.ReadUInt32();
+
+ audioHeader = v2;
+ unknownOffset1 = v2.UnknownOffset1 + 0x54;
+ break;
+
+ // No other version are recognized
+ default:
+ return null;
+ }
+
+ audioHeader.Signature = data.ReadUInt32();
+ if (audioHeader.Signature != SignatureUInt32)
+ return null;
+
+ audioHeader.TrackLength = data.ReadUInt16();
+ byte[] track = data.ReadBytes(audioHeader.TrackLength);
+ if (track != null)
+ audioHeader.Track = Encoding.ASCII.GetString(track);
+
+ audioHeader.ArtistLength = data.ReadUInt16();
+ byte[] artist = data.ReadBytes(audioHeader.ArtistLength);
+ if (artist != null)
+ audioHeader.Artist = Encoding.ASCII.GetString(artist);
+
+ audioHeader.AlbumLength = data.ReadUInt16();
+ byte[] album = data.ReadBytes(audioHeader.AlbumLength);
+ if (album != null)
+ audioHeader.Album = Encoding.ASCII.GetString(album);
+
+ audioHeader.WriterLength = data.ReadUInt16();
+ byte[] writer = data.ReadBytes(audioHeader.WriterLength);
+ if (writer != null)
+ audioHeader.Writer = Encoding.ASCII.GetString(writer);
+
+ audioHeader.PublisherLength = data.ReadUInt16();
+ byte[] publisher = data.ReadBytes(audioHeader.PublisherLength);
+ if (publisher != null)
+ audioHeader.Publisher = Encoding.ASCII.GetString(publisher);
+
+ audioHeader.LabelLength = data.ReadUInt16();
+ byte[] label = data.ReadBytes(audioHeader.LabelLength);
+ if (label != null)
+ audioHeader.Label = Encoding.ASCII.GetString(label);
+
+ if (data.Position - initialOffset < unknownOffset1)
+ {
+ audioHeader.CommentsLength = data.ReadUInt16();
+ byte[] comments = data.ReadBytes(audioHeader.CommentsLength);
+ if (comments != null)
+ audioHeader.Comments = Encoding.ASCII.GetString(comments);
+ }
+
+ return audioHeader;
+ }
+
///
/// Parse a Stream into an unknown block 1
///
diff --git a/BurnOutSharp.Models/PlayJ/AudioHeader.cs b/BurnOutSharp.Models/PlayJ/AudioHeader.cs
new file mode 100644
index 00000000..db61aa29
--- /dev/null
+++ b/BurnOutSharp.Models/PlayJ/AudioHeader.cs
@@ -0,0 +1,93 @@
+namespace BurnOutSharp.Models.PlayJ
+{
+ ///
+ /// PlayJ audio header / CDS entry header
+ ///
+ /// V1 and V2 variants exist
+ public abstract class AudioHeader
+ {
+ ///
+ /// Signature (0x4B539DFF)
+ ///
+ public uint Signature;
+
+ ///
+ /// Version
+ ///
+ public uint Version;
+
+ // Header-specific data goes here
+
+ ///
+ /// Length of the track name
+ ///
+ public ushort TrackLength;
+
+ ///
+ /// Track name (not null-terminated)
+ ///
+ public string Track;
+
+ ///
+ /// Length of the artist name
+ ///
+ public ushort ArtistLength;
+
+ ///
+ /// Artist name (not null-terminated)
+ ///
+ public string Artist;
+
+ ///
+ /// Length of the album name
+ ///
+ public ushort AlbumLength;
+
+ ///
+ /// Album name (not null-terminated)
+ ///
+ public string Album;
+
+ ///
+ /// Length of the writer name
+ ///
+ public ushort WriterLength;
+
+ ///
+ /// Writer name (not null-terminated)
+ ///
+ public string Writer;
+
+ ///
+ /// Length of the publisher name
+ ///
+ public ushort PublisherLength;
+
+ ///
+ /// Publisher name (not null-terminated)
+ ///
+ public string Publisher;
+
+ ///
+ /// Length of the label name
+ ///
+ public ushort LabelLength;
+
+ ///
+ /// Label name (not null-terminated)
+ ///
+ public string Label;
+
+ ///
+ /// Length of the comments
+ ///
+ /// Optional field only in some samples
+ public ushort CommentsLength;
+
+ ///
+ /// Comments (not null-terminated)
+ ///
+ /// Optional field only in some samples
+ public string Comments;
+ }
+}
\ No newline at end of file
diff --git a/BurnOutSharp.Models/PlayJ/AudioHeaderV1.cs b/BurnOutSharp.Models/PlayJ/AudioHeaderV1.cs
new file mode 100644
index 00000000..5f81a493
--- /dev/null
+++ b/BurnOutSharp.Models/PlayJ/AudioHeaderV1.cs
@@ -0,0 +1,62 @@
+namespace BurnOutSharp.Models.PlayJ
+{
+ ///
+ /// PlayJ audio header / CDS entry header (V1)
+ ///
+ public sealed class AudioHeaderV1 : AudioHeader
+ {
+ ///
+ /// Download track ID
+ ///
+ /// 0xFFFFFFFF if unset
+ public uint TrackID;
+
+ ///
+ /// Offset to unknown data block 1
+ ///
+ public uint UnknownOffset1;
+
+ ///
+ /// Offset to unknown data block 2
+ ///
+ public uint UnknownOffset2;
+
+ ///
+ /// Offset to unknown data block 3
+ ///
+ public uint UnknownOffset3;
+
+ ///
+ /// Unknown
+ ///
+ /// Always 0x00000001
+ public uint Unknown1;
+
+ ///
+ /// Unknown
+ ///
+ /// Typically 0x00000001 in download titles
+ public uint Unknown2;
+
+ ///
+ /// Track year
+ ///
+ /// 0xFFFFFFFF if unset
+ public uint Year;
+
+ ///
+ /// Track number
+ ///
+ public byte TrackNumber;
+
+ ///
+ /// Subgenre
+ ///
+ public Subgenre Subgenre;
+
+ ///
+ /// Track duration in seconds
+ ///
+ public uint Duration;
+ }
+}
\ No newline at end of file
diff --git a/BurnOutSharp.Models/PlayJ/AudioHeaderV2.cs b/BurnOutSharp.Models/PlayJ/AudioHeaderV2.cs
new file mode 100644
index 00000000..9646158e
--- /dev/null
+++ b/BurnOutSharp.Models/PlayJ/AudioHeaderV2.cs
@@ -0,0 +1,101 @@
+namespace BurnOutSharp.Models.PlayJ
+{
+ ///
+ /// PlayJ audio header / CDS entry header (V2)
+ ///
+ public sealed class AudioHeaderV2 : AudioHeader
+ {
+ ///
+ /// Unknown (Always 0x00000001)
+ ///
+ public uint Unknown1;
+
+ ///
+ /// Unknown (Always 0x00000001)
+ ///
+ public uint Unknown2;
+
+ ///
+ /// Unknown (Always 0x00000000)
+ ///
+ public uint Unknown3;
+
+ ///
+ /// Unknown (Always 0x00000003)
+ ///
+ public uint Unknown4;
+
+ ///
+ /// Unknown (Always 0x00000001)
+ ///
+ public uint Unknown5;
+
+ ///
+ /// Unknown (Always 0x00000000)
+ ///
+ public uint Unknown6;
+
+ ///
+ /// Offset to unknown block 1, relative to the track ID
+ ///
+ public uint UnknownOffset1;
+
+ ///
+ /// Unknown
+ ///
+ public uint Unknown7;
+
+ ///
+ /// Unknown (Always 0x00000004)
+ ///
+ public uint Unknown8;
+
+ ///
+ /// Unknown (Always 0x00000002)
+ ///
+ public uint Unknown9;
+
+ ///
+ /// Offset to unknown block 1, relative to the track ID
+ ///
+ /// Always identical to ?
+ public uint UnknownOffset2;
+
+ ///
+ /// Unknown
+ ///
+ public uint Unknown10;
+
+ ///
+ /// Unknown (Always 0x00000000)
+ ///
+ public uint Unknown11;
+
+ ///
+ /// Unknown (Always 0x00000007)
+ ///
+ public uint Unknown12;
+
+ ///
+ /// Download track ID
+ ///
+ /// 0xFFFFFFFF if unset
+ public uint TrackID;
+
+ ///
+ /// Track year -- UNCONFIRMED
+ ///
+ /// 0xFFFFFFFF if unset
+ public uint Year;
+
+ ///
+ /// Track number
+ ///
+ public uint TrackNumber;
+
+ ///
+ /// Unknown
+ ///
+ public uint Unknown13;
+ }
+}
\ No newline at end of file
diff --git a/BurnOutSharp.Models/PlayJ/EntryHeader.cs b/BurnOutSharp.Models/PlayJ/EntryHeader.cs
index e1754e4f..c44cdca8 100644
--- a/BurnOutSharp.Models/PlayJ/EntryHeader.cs
+++ b/BurnOutSharp.Models/PlayJ/EntryHeader.cs
@@ -142,35 +142,5 @@ namespace BurnOutSharp.Models.PlayJ
///
/// Optional field only in some samples
public string Comments;
-
- #region V2 Notes
-
- // Unknown V2 data block
- // ---------------------------
- // 0x08 0x00000001 Unknown [Common]
- // 0x0C 0x00000001 Unknown [Common]
- // 0x10 0x00000000 Unknown [Common]
- // 0x14 0x00000003 Unknown [Common]
- // 0x18 0x00000001 Unknown [Common]
- // 0x1C 0x00000000 Unknown [Common]
- // 0x20 0x00000066 Unknown [Varies]
- // 0x24 0xF02F4372 Unknown [Varies]
- // 0x28 0x00000004 Unknown [Common]
- // 0x2C 0x00000002 Unknown [Common]
- // 0x30 0x00000066 Unknown [Varies]
- // 0x34 0x00005933 Unknown [Varies]
- // 0x38 0xB462688A Unknown [Varies]
- // 0x3C 0x00000005 Unknown [Common]
- // 0x40 0x00000009 Unknown [Common]
- // 0x44 0x00005999 Unknown [Varies]
- // 0x48 0x0032FF38 Unknown [Varies]
- // 0x4C 0x00000000 Unknown [Common]
- // 0x50 0x00000007 Unknown [Common]
- // 0x54 0x0001897D Track ID
- // 0x58 0xFFFFFFFF Unknown (Year?)
- // 0x5C 0x00000005 Track Number
- // 0x60 0x00033068 Unknown [Varies]
-
- #endregion
}
}
\ No newline at end of file