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