diff --git a/CodeWalker.Core/GameFiles/FileTypes/AwcFile.cs b/CodeWalker.Core/GameFiles/FileTypes/AwcFile.cs index a2dd8fb..f5816a8 100644 --- a/CodeWalker.Core/GameFiles/FileTypes/AwcFile.cs +++ b/CodeWalker.Core/GameFiles/FileTypes/AwcFile.cs @@ -14,7 +14,6 @@ namespace CodeWalker.GameFiles { public string Name { get; set; } public RpfFileEntry FileEntry { get; set; } - public byte[] Data { get; set; } public string ErrorMessage { get; set; } @@ -35,8 +34,7 @@ namespace CodeWalker.GameFiles public bool WholeFileEncrypted { get; set; } public AwcStreamInfo[] StreamInfos { get; set; } - public uint[] AudioIds { get; set; } - public AwcAudio[] Audios { get; set; } + public AwcStream[] Streams { get; set; } static public void Decrypt_RSXXTEA(byte[] data, uint[] key) { @@ -65,7 +63,6 @@ namespace CodeWalker.GameFiles { Name = entry.Name; FileEntry = entry; - Data = data; if ((data == null) || (data.Length < 8)) { @@ -111,6 +108,24 @@ namespace CodeWalker.GameFiles } } + public byte[] Save() + { + MemoryStream s = new MemoryStream(); + DataWriter w = new DataWriter(s); + + Write(w); + + var buf = new byte[s.Length]; + s.Position = 0; + s.Read(buf, 0, buf.Length); + return buf; + } + + + + + + private void Read(DataReader r) { Magic = r.ReadUInt32(); @@ -120,11 +135,8 @@ namespace CodeWalker.GameFiles InfoOffset = r.ReadInt32(); - if ((Flags >> 8) != 0xFF) - { - ErrorMessage = "Flags 0 not supported!"; - return; - } + //if ((Flags >> 8) != 0xFF) + //{ }//no hit if (UnkUshortsFlag) { @@ -136,19 +148,17 @@ namespace CodeWalker.GameFiles } - var infoStart = 16 + (UnkUshortsFlag ? (StreamCount * 2) : 0); - if (r.Position != infoStart) - { }//no hit + //var infoStart = 16 + (UnkUshortsFlag ? (StreamCount * 2) : 0); + //if (r.Position != infoStart) + //{ }//no hit - List infos = new List(); - Dictionary infoDict = new Dictionary(); + var infos = new List(); for (int i = 0; i < StreamCount; i++) { var info = new AwcStreamInfo(r); infos.Add(info); - infoDict[info.Id] = info; } for (int i = 0; i < StreamCount; i++) { @@ -156,209 +166,145 @@ namespace CodeWalker.GameFiles for (int j = 0; j < info.ChunkCount; j++) { var chunk = new AwcChunkInfo(r); - info.ChunkList.Add(chunk); - info.ChunkDict[chunk.Tag] = chunk; + info.Chunks[j] = chunk; } } StreamInfos = infos.ToArray(); - var infoOffset = infoStart + StreamCount * 4; - foreach (var info in infos) infoOffset += (int)info.ChunkCount * 8; - if (infoOffset != InfoOffset) - { }//no hit - if (r.Position != InfoOffset) - { }//no hit + //var infoOffset = infoStart + StreamCount * 4; + //foreach (var info in infos) infoOffset += (int)info.ChunkCount * 8; + //if (infoOffset != InfoOffset) + //{ }//no hit + //if (r.Position != InfoOffset) + //{ }//no hit - ReadAudios(r); - } - - private void ReadAudios(DataReader r) - { - - List audioIds = new List(); - List audios = new List(); - AwcAudio multiaudio = null; + var streams = new List(); + AwcStream multisource = null; for (int i = 0; i < StreamCount; i++) { var info = StreamInfos[i]; - var audio = new AwcAudio(); - audio.StreamInfo = info; + var stream = new AwcStream(info, this); + stream.Read(r); + streams.Add(stream); - for (int j = 0; j < info.ChunkList.Count; j++) + stream.UnkUshort = (UnkUshorts != null) ? (ushort?)UnkUshorts[i] : null; + + if (MultiChannelFlag && (stream.DataChunk != null)) { - var chunk = info.ChunkList[j]; - r.Position = chunk.Offset; - - if ((chunk.Offset % 2) != 0) - { } - - switch (chunk.Tag) - { - case 85: //data - audio.DataInfo = chunk; - audio.Data = r.ReadBytes(chunk.Size); - break; - case 250://format - audio.Format = new AwcFormatChunk(r, chunk); - break; - case 92: //animation - YCD resource chunk - audio.ClipDict = new AwcAudioAnimClipDict(r, chunk); - break; - case 43: //0x2B - audio.UnkData2B = new AwcAudioUnk2B(r, chunk); - break; - case 54: //0x36 - peak - audio.PeakData = new AwcAudioPeak(r, chunk); - break; - case 90: //0x5A - audio.UnkData5A = new AwcAudioUnk5A(r, chunk); - break; - case 104://MIDI - audio.MIDIData = new AwcAudioMIDI(r, chunk); - break; - case 217://0xD9 - length 200+ - audio.UnkDataD9 = new AwcAudioUnkD9(r, chunk); - break; - case 72: //multi channel info - audio.MultiChannelInfo = new AwcChannelBlockInfo(r); - break; - case 163://multi sample offsets - audio.MultiChannelSampleOffsets = new AwcChannelOffsetsInfo(r, chunk); - break; - case 189://multi 0xBD - markers - audio.MarkersData = new AwcAudioMarkers(r, chunk); - break; - default: - break; - } - if ((r.Position - chunk.Offset) != chunk.Size) - { }//make sure everything was read! + multisource = stream; } - - - //decrypt data where necessary - if ((audio.Data != null) && (!WholeFileEncrypted)) - { - if (MultiChannelFlag) - { - var ocount = (int)(audio.MultiChannelSampleOffsets?.SampleOffsets?.Length ?? 0); - var ccount = (int)(audio.MultiChannelInfo?.ChannelCount ?? 0); - var bcount = (int)(audio.MultiChannelInfo?.BlockCount ?? 0); - var bsize = (int)(audio.MultiChannelInfo?.BlockSize ?? 0); - var blist = new List(); - for (int b = 0; b < bcount; b++) - { - int srcoff = b * bsize; - int mcsoff = (b < ocount) ? (int)audio.MultiChannelSampleOffsets.SampleOffsets[b] : 0; - int blen = Math.Max(Math.Min(bsize, audio.Data.Length - srcoff), 0); - var bdat = new byte[blen]; - Buffer.BlockCopy(audio.Data, srcoff, bdat, 0, blen); - if (MultiChannelEncryptFlag) - { - AwcFile.Decrypt_RSXXTEA(bdat, GTA5Keys.PC_AWC_KEY); - } - var blk = new AwcChannelDataBlock(bdat, audio.MultiChannelInfo, r.Endianess, b, mcsoff); - blist.Add(blk); - } - audio.MultiChannelBlocks = blist.ToArray(); - multiaudio = audio; - } - else - { - if (SingleChannelEncryptFlag) - { - AwcFile.Decrypt_RSXXTEA(audio.Data, GTA5Keys.PC_AWC_KEY); - } - } - } - if ((audio.Data != null) && WholeFileEncrypted && MultiChannelFlag) - { }//no hit - - - audios.Add(audio); - audioIds.Add(info.Id); } - - if (multiaudio != null) + if (multisource != null) { - var multichaninfos = multiaudio.MultiChannelInfo; - for (int i = 0; i < audios.Count; i++) + multisource.AssignMultiChannelSources(streams); + } + + Streams = streams.ToArray(); + + } + + private void Write(DataWriter w) + { + var infoStart = 16 + (UnkUshortsFlag ? (StreamCount * 2) : 0); + var infoOffset = infoStart + StreamCount * 4; + foreach (var info in StreamInfos) infoOffset += (int)info.ChunkCount * 8; + InfoOffset = infoOffset; + StreamCount = StreamInfos?.Length ?? 0; + + w.Write(Magic); + w.Write(Version); + w.Write(Flags); + w.Write(StreamCount); + w.Write(InfoOffset); + + if (UnkUshortsFlag) + { + for (int i = 0; i < StreamCount; i++) { - var audio = audios[i]; - if (audio != multiaudio) - { - var id = audio.StreamInfo?.Id ?? 0; - var srcind = 0; - var chancnt = multiaudio.MultiChannelInfo?.Channels?.Length ?? 0; - var found = false; - for (int ind = 0; ind < chancnt; ind++) - { - var mchan = multiaudio.MultiChannelInfo.Channels[ind]; - if (mchan.Id == id) - { - srcind = ind; - found = true; - break; - } - } - if (!found) - { }//no hit - - audio.MultiChannelSource = multiaudio; - audio.MultiChannelFormat = (srcind < multichaninfos?.Channels?.Length) ? multichaninfos.Channels[srcind] : null; - audio.MultiChannelIndex = srcind; - } - + w.Write((i < (UnkUshorts?.Length ?? 0)) ? UnkUshorts[i] : 0); } } - Audios = audios.ToArray(); - AudioIds = audioIds.ToArray(); + for (int i = 0; i < StreamCount; i++) + { + var info = StreamInfos[i]; + info.Write(w); + } + for (int i = 0; i < StreamCount; i++) + { + var info = StreamInfos[i]; + for (int j = 0; j < info.ChunkCount; j++) + { + var chunk = info.Chunks[j]; + chunk.Write(w); + } + } + + for (int i = 0; i < StreamCount; i++) + { + var stream = Streams[i]; + stream.Write(w); + } + + } - public byte[] Save() - { - //TODO - return new byte[1]; - } - - - - - - - - public void WriteXml(StringBuilder sb, int indent, string wavfolder) { - //AwcXml.StringTag(sb, indent, "Name", AwcXml.XmlEscape(Name)); AwcXml.ValueTag(sb, indent, "Version", Version.ToString()); - - - if (UnkUshorts != null) + if (MultiChannelFlag) { - AwcXml.WriteRawArray(sb, UnkUshorts, indent, "UnkUshorts", "", null, 32); + AwcXml.ValueTag(sb, indent, "MultiChannel", true.ToString()); + } + if ((Streams?.Length ?? 0) > 0) + { + AwcXml.OpenTag(sb, indent, "Streams"); + for (int i = 0; i < Streams.Length; i++) + { + AwcXml.OpenTag(sb, indent + 1, "Item"); + Streams[i].WriteXml(sb, indent + 2, wavfolder); + AwcXml.CloseTag(sb, indent + 1, "Item"); + } + AwcXml.CloseTag(sb, indent, "Streams"); } } public void ReadXml(XmlNode node, string wavfolder) { - //Name = Xml.GetChildInnerText(node, "Name"); Version = (ushort)Xml.GetChildUIntAttribute(node, "Version"); + MultiChannelFlag = Xml.GetChildBoolAttribute(node, "MultiChannel"); + var unkUshorts = new List(); + var hasUshorts = false; - - var unode = node.SelectSingleNode("UnkUshorts"); - if (unode != null) + var snode = node.SelectSingleNode("Streams"); + if (snode != null) { - UnkUshorts = Xml.GetRawUshortArray(node); + var slist = new List(); + var inodes = node.SelectNodes("Item"); + foreach (XmlNode inode in inodes) + { + var stream = new AwcStream(this); + stream.ReadXml(inode, wavfolder); + slist.Add(stream); + + hasUshorts = hasUshorts || stream.UnkUshort.HasValue; + unkUshorts.Add(stream.UnkUshort ?? 0); + } + slist.Sort((a, b) => a.Hash.Hash.CompareTo(b.Hash.Hash)); + Streams = slist.ToArray(); + } + + if (hasUshorts) + { + UnkUshorts = unkUshorts.ToArray(); UnkUshortsFlag = true; } @@ -383,7 +329,7 @@ namespace CodeWalker.GameFiles } - public enum AwcCodecFormat + public enum AwcCodecType { PCM = 0, ADPCM = 4 @@ -395,14 +341,27 @@ namespace CodeWalker.GameFiles public uint ChunkCount { get; set; } public uint Id { get; set; } - public List ChunkList { get; set; } = new List(); - public Dictionary ChunkDict { get; set; } = new Dictionary(); + public AwcChunkInfo[] Chunks { get; set; } + + public AwcStreamInfo() + { + } public AwcStreamInfo(DataReader r) { RawVal = r.ReadUInt32(); ChunkCount = (RawVal >> 29); Id = (RawVal & 0x1FFFFFFF); + + Chunks = new AwcChunkInfo[ChunkCount]; + } + + public void Write(DataWriter w) + { + ChunkCount = (uint)(Chunks?.Length ?? 0); + RawVal = (Id & 0x1FFFFFFF) + (ChunkCount << 29); + + w.Write(RawVal); } public override string ToString() @@ -414,222 +373,55 @@ namespace CodeWalker.GameFiles [TC(typeof(EXP))] public class AwcChunkInfo { public ulong RawVal { get; set; } - public byte Tag { get; set; } + public AwcChunkType Type { get; set; } public int Size { get; set; } public int Offset { get; set; } + public AwcChunkInfo(AwcChunkType type) + { + Type = type; + } public AwcChunkInfo(DataReader r) { RawVal = r.ReadUInt64(); - Tag = (byte)(RawVal >> 56); + Type = (AwcChunkType)(RawVal >> 56); Size = (int)((RawVal >> 28) & 0x0FFFFFFF); Offset = (int)(RawVal & 0x0FFFFFFF); } - public override string ToString() + public void Write(DataWriter w) { - return Tag.ToString() + ": " + Size.ToString() + ", " + Offset.ToString(); - } - } - - [TC(typeof(EXP))] public class AwcChannelBlockInfo - { - public uint BlockCount { get; set; } - public uint BlockSize { get; set; } - public uint ChannelCount { get; set; } - public AwcChannelBlockItemInfo[] Channels { get; set; } - - public uint TotalSize { get { return BlockCount * BlockSize; } } - - public AwcChannelBlockInfo(DataReader r) - { - BlockCount = r.ReadUInt32(); - BlockSize = r.ReadUInt32(); - ChannelCount = r.ReadUInt32(); - - List channels = new List(); - for (int i = 0; i < ChannelCount; i++) - { - var itemInfo = new AwcChannelBlockItemInfo(r); - channels.Add(itemInfo); - } - Channels = channels.ToArray(); - + RawVal = (((ulong)Offset) & 0x0FFFFFFF) + ((((ulong)Size) & 0x0FFFFFFF) << 28) + (((ulong)Type) << 56); + w.Write(RawVal); } public override string ToString() { - return BlockCount.ToString() + ": " + BlockSize.ToString() + ", " + ChannelCount.ToString() + " channels"; + return Type.ToString() + ": " + Size.ToString() + ", " + Offset.ToString(); } } - [TC(typeof(EXP))] public class AwcChannelBlockItemInfo - { - public uint Id { get; set; } - public uint Samples { get; set; } - public ushort Unk0 { get; set; }//flags? - public ushort SamplesPerSecond { get; set; } - public AwcCodecFormat Codec { get; set; } - public byte RoundSize { get; set; } - public ushort Unk2 { get; set; } - - public AwcChannelBlockItemInfo(DataReader r) - { - Id = r.ReadUInt32(); - Samples = r.ReadUInt32(); - Unk0 = r.ReadUInt16(); - SamplesPerSecond = r.ReadUInt16(); - Codec = (AwcCodecFormat)r.ReadByte(); - RoundSize = r.ReadByte(); - Unk2 = r.ReadUInt16(); - - //switch (Unk0) - //{ - // case 65395: - // case 65485: - // case 33: - // case 9: - // case 674: - // case 529: - // case 11: - // case 105: - // case 65396: - // case 437: - // case 477: - // case 65475: - // case 519: - // case 349: - // case 65517: - // case 106: - // case 219: - // case 58: - // case 63: - // case 65530: - // case 65511: - // case 191: - // case 65439: - // case 65345: - // case 560: - // case 640: - // case 65335: - // case 208: - // case 392: - // case 1968: - // case 2257: - // case 1101: - // case 906: - // case 1087: - // case 718: - // case 65362: - // case 259: - // case 234: - // case 299: - // case 307: - // case 75: - // case 67: - // case 4: - // case 290: - // case 587: - // case 65448: - // case 15: - // case 398: - // case 399: - // case 65375: - // case 65376: - // case 65336: - // break; - // default: - // break;//more... flags? - //} - //switch (Codec) - //{ - // case AwcCodecFormat.ADPCM: - // break; - // default: - // break;//no hit - //} - //switch (Unk2) - //{ - // case 0: - // break; - // default: - // break;//no hit - //} - } - - public override string ToString() - { - return Id.ToString() + ", " + Codec.ToString() + ": " + Samples.ToString() + " samples, " + SamplesPerSecond.ToString() + " samples/sec, size: " + RoundSize.ToString() + " unk0: " + Unk0.ToString(); - } - } - - [TC(typeof(EXP))] public class AwcFormatChunk - { - public uint Samples { get; set; } - public int LoopPoint { get; set; } - public ushort SamplesPerSecond { get; set; } - public short Headroom { get; set; } - public ushort LoopBegin { get; set; } - public ushort LoopEnd { get; set; } - public ushort PlayEnd { get; set; } - public byte PlayBegin { get; set; } - public uint UnkExtra { get; set; } - - public AwcCodecFormat Codec { get; set; } - - public AwcFormatChunk(DataReader r, AwcChunkInfo info) - { - Samples = r.ReadUInt32(); - LoopPoint = r.ReadInt32(); - SamplesPerSecond = r.ReadUInt16(); - Headroom = r.ReadInt16(); - LoopBegin = r.ReadUInt16(); - LoopEnd = r.ReadUInt16(); - PlayEnd = r.ReadUInt16(); - PlayBegin = r.ReadByte(); - Codec = (AwcCodecFormat)r.ReadByte(); - - switch (info.Size) //Apparently sometimes this struct is longer? - { - case 20: - break; - case 24: - UnkExtra = r.ReadUInt32(); - break; - default: - break;//no hit - } - - } - - - public override string ToString() - { - return Headroom.ToString() + ", " + Codec.ToString() + ": " + Samples.ToString() + " samples, " + SamplesPerSecond.ToString() + " samples/sec"; - } - } - - [TC(typeof(EXP))] public class AwcAudio + [TC(typeof(EXP))] public class AwcStream { + public AwcFile Awc { get; set; } public AwcStreamInfo StreamInfo { get; set; } - public AwcFormatChunk Format { get; set; } - public AwcChunkInfo DataInfo { get; set; } - public byte[] Data { get; set; } + public AwcChunk[] Chunks { get; set; } + public AwcFormatChunk FormatChunk { get; set; } + public AwcDataChunk DataChunk { get; set; } + public AwcAnimationChunk AnimationChunk { get; set; } + public AwcGestureChunk GestureChunk { get; set; } + public AwcPeakChunk PeakChunk { get; set; } + public AwcMIDIChunk MidiChunk { get; set; } + public AwcMarkersChunk MarkersChunk { get; set; } + public AwcGranularGrainsChunk GranularGrainsChunk { get; set; } + public AwcGranularLoopsChunk GranularLoopsChunk { get; set; } + public AwcStreamFormatChunk StreamFormatChunk { get; set; } + public AwcSeekTableChunk SeekTableChunk { get; set; } + public AwcStream StreamSource { get; set; } + public AwcStreamFormat StreamFormat { get; set; } + public AwcStreamDataBlock[] StreamBlocks { get; set; } + public int StreamChannelIndex { get; set; } - public AwcAudioAnimClipDict ClipDict { get; set; } - public AwcAudioUnk2B UnkData2B { get; set; } - public AwcAudioPeak PeakData { get; set; } - public AwcAudioUnk5A UnkData5A { get; set; } - public AwcAudioMIDI MIDIData { get; set; } - public AwcAudioUnkD9 UnkDataD9 { get; set; } - public AwcChannelBlockInfo MultiChannelInfo { get; set; } - public AwcChannelOffsetsInfo MultiChannelSampleOffsets { get; set; } - public AwcChannelDataBlock[] MultiChannelBlocks { get; set; } - public AwcAudioMarkers MarkersData { get; set; } - - public AwcAudio MultiChannelSource { get; set; } - public AwcChannelBlockItemInfo MultiChannelFormat { get; set; } - public int MultiChannelIndex { get; set; } public short Channels = 1; public short BitsPerSample = 16; @@ -637,18 +429,33 @@ namespace CodeWalker.GameFiles { get { - return Format?.SamplesPerSecond ?? MultiChannelFormat?.SamplesPerSecond ?? 0; + return FormatChunk?.SamplesPerSecond ?? StreamFormat?.SamplesPerSecond ?? 0; } } public int SampleCount { get { - return (int)(Format?.Samples ?? MultiChannelFormat?.Samples ?? 0); + return (int)(FormatChunk?.Samples ?? StreamFormat?.Samples ?? 0); } } + public ushort? UnkUshort { get; set; } // stored in root of AWC, will have value if present + public MetaHash Hash + { + get + { + return StreamInfo?.Id ?? 0; + } + set + { + if (StreamInfo != null) + { + StreamInfo.Id = value & 0x1FFFFFFF; + } + } + } public string Name { get @@ -660,28 +467,28 @@ namespace CodeWalker.GameFiles { get { - if (MIDIData != null) + if (MidiChunk != null) { return "MIDI"; } - var fc = AwcCodecFormat.PCM; + var fc = AwcCodecType.PCM; var hz = 0; - if (Format != null) + if (FormatChunk != null) { - fc = Format.Codec; - hz = Format.SamplesPerSecond; + fc = FormatChunk.Codec; + hz = FormatChunk.SamplesPerSecond; } - if (MultiChannelFormat != null) + if (StreamFormat != null) { - fc = MultiChannelFormat.Codec; - hz = MultiChannelFormat.SamplesPerSecond; + fc = StreamFormat.Codec; + hz = StreamFormat.SamplesPerSecond; } string codec = fc.ToString(); switch (fc) { - case AwcCodecFormat.PCM: - case AwcCodecFormat.ADPCM: + case AwcCodecType.PCM: + case AwcCodecType.ADPCM: break; default: codec = "Unknown"; @@ -696,8 +503,8 @@ namespace CodeWalker.GameFiles { get { - if (Format != null) return (float)Format.Samples / Format.SamplesPerSecond; - if (MultiChannelFormat != null) return (float)MultiChannelFormat.Samples / MultiChannelFormat.SamplesPerSecond; + if (FormatChunk != null) return (float)FormatChunk.Samples / FormatChunk.SamplesPerSecond; + if (StreamFormat != null) return (float)StreamFormat.Samples / StreamFormat.SamplesPerSecond; return 0; } } @@ -713,25 +520,25 @@ namespace CodeWalker.GameFiles { get { - if (MIDIData?.Data != null) + if (MidiChunk?.Data != null) { - return MIDIData.Data?.Length ?? 0; + return MidiChunk.Data.Length; } - if (Data != null) + if (DataChunk?.Data != null) { - return Data.Length; + return DataChunk.Data.Length; } - if (MultiChannelSource != null) + if (StreamSource != null) { int c = 0; - if (MultiChannelSource?.MultiChannelBlocks != null) + if (StreamSource?.StreamBlocks != null) { - foreach (var blk in MultiChannelSource.MultiChannelBlocks) + foreach (var blk in StreamSource.StreamBlocks) { - if (MultiChannelIndex < (blk?.Channels?.Length ?? 0)) + if (StreamChannelIndex < (blk?.Channels?.Length ?? 0)) { - var chan = blk.Channels[MultiChannelIndex]; - c += chan?.ChannelData?.Length ?? 0; + var chan = blk.Channels[StreamChannelIndex]; + c += chan?.Data?.Length ?? 0; } } } @@ -742,32 +549,256 @@ namespace CodeWalker.GameFiles } - public AwcAudio() + public AwcStream(AwcFile awc) { + Awc = awc; + StreamInfo = new AwcStreamInfo(); } - public AwcAudio(DataReader r, AwcStreamInfo s, AwcFormatChunk f, AwcChunkInfo d) + public AwcStream(AwcStreamInfo s, AwcFile awc) { + Awc = awc; StreamInfo = s; - Format = f; - DataInfo = d; - - Data = r.ReadBytes(d.Size); } + public void AssignMultiChannelSources(List audios) + { + for (int i = 0; i < audios.Count; i++) + { + var audio = audios[i]; + if (audio != this) + { + var id = audio.StreamInfo?.Id ?? 0; + var srcind = 0; + var chancnt = StreamFormatChunk?.Channels?.Length ?? 0; + var found = false; + for (int ind = 0; ind < chancnt; ind++) + { + var mchan = StreamFormatChunk.Channels[ind]; + if (mchan.Id == id) + { + srcind = ind; + found = true; + break; + } + } + if (!found) + { }//no hit + + audio.StreamSource = this; + audio.StreamFormat = (srcind < chancnt) ? StreamFormatChunk.Channels[srcind] : null; + audio.StreamChannelIndex = srcind; + } + + } + } + + + public void Read(DataReader r) + { + + var chunklist = new List(); + for (int j = 0; j < StreamInfo.Chunks?.Length; j++) + { + var cinfo = StreamInfo.Chunks[j]; + r.Position = cinfo.Offset; + + var chunk = CreateChunk(cinfo); + chunk?.Read(r); + chunklist.Add(chunk); + + if (chunk is AwcDataChunk dataChunk) DataChunk = dataChunk; + if (chunk is AwcFormatChunk formatChunk) FormatChunk = formatChunk; + if (chunk is AwcAnimationChunk animChunk) AnimationChunk = animChunk; + if (chunk is AwcMarkersChunk markersChunk) MarkersChunk = markersChunk; + if (chunk is AwcGestureChunk gestureChunk) GestureChunk = gestureChunk; + if (chunk is AwcPeakChunk peakChunk) PeakChunk = peakChunk; + if (chunk is AwcMIDIChunk midiChunk) MidiChunk = midiChunk; + if (chunk is AwcStreamFormatChunk streamformatChunk) StreamFormatChunk = streamformatChunk; + if (chunk is AwcSeekTableChunk seektableChunk) SeekTableChunk = seektableChunk; + if (chunk is AwcGranularGrainsChunk ggChunk) GranularGrainsChunk = ggChunk; + if (chunk is AwcGranularLoopsChunk glChunk) GranularLoopsChunk = glChunk; + + if ((r.Position - cinfo.Offset) != cinfo.Size) + { }//make sure everything was read! + } + Chunks = chunklist.ToArray(); + + + //create multichannel blocks and decrypt data where necessary + if (DataChunk?.Data != null) + { + if (Awc.MultiChannelFlag) + { + var ocount = (int)(SeekTableChunk?.SeekTable?.Length ?? 0); + var ccount = (int)(StreamFormatChunk?.ChannelCount ?? 0); + var bcount = (int)(StreamFormatChunk?.BlockCount ?? 0); + var bsize = (int)(StreamFormatChunk?.BlockSize ?? 0); + var blist = new List(); + for (int b = 0; b < bcount; b++) + { + int srcoff = b * bsize; + int mcsoff = (b < ocount) ? (int)SeekTableChunk.SeekTable[b] : 0; + int blen = Math.Max(Math.Min(bsize, DataChunk.Data.Length - srcoff), 0); + var bdat = new byte[blen]; + Buffer.BlockCopy(DataChunk.Data, srcoff, bdat, 0, blen); + if (Awc.MultiChannelEncryptFlag && !Awc.WholeFileEncrypted) + { + AwcFile.Decrypt_RSXXTEA(bdat, GTA5Keys.PC_AWC_KEY); + } + var blk = new AwcStreamDataBlock(bdat, StreamFormatChunk, r.Endianess, b, mcsoff); + blist.Add(blk); + } + StreamBlocks = blist.ToArray(); + } + else + { + if (Awc.SingleChannelEncryptFlag && !Awc.WholeFileEncrypted) + { + AwcFile.Decrypt_RSXXTEA(DataChunk.Data, GTA5Keys.PC_AWC_KEY); + } + } + } + //if ((Data != null) && awc.WholeFileEncrypted && awc.MultiChannelFlag) + //{ }//no hit + + + } + + public void Write(DataWriter w) + { + + } + + public void WriteXml(StringBuilder sb, int indent, string wavfolder) + { + AwcXml.StringTag(sb, indent, "Name", AwcXml.HashString(Hash)); + if (StreamFormatChunk == null) + { + //skip the wave file output for multichannel sources + AwcXml.StringTag(sb, indent, "FileName", AwcXml.XmlEscape(Name + ".wav")); + try + { + if (!string.IsNullOrEmpty(wavfolder)) + { + if (!Directory.Exists(wavfolder)) + { + Directory.CreateDirectory(wavfolder); + } + var filepath = Path.Combine(wavfolder, (Name ?? "null") + ".wav"); + var wav = GetWavFile(); + File.WriteAllBytes(filepath, wav); + } + } + catch { } + } + if (StreamFormat != null) + { + AwcXml.OpenTag(sb, indent, "StreamFormat"); + StreamFormat.WriteXml(sb, indent + 1); + AwcXml.CloseTag(sb, indent, "StreamFormat"); + } + if (UnkUshort.HasValue) + { + AwcXml.ValueTag(sb, indent, "UnkUshort", UnkUshort.Value.ToString()); + } + if ((Chunks?.Length ?? 0) > 0) + { + AwcXml.OpenTag(sb, indent, "Chunks"); + for (int i = 0; i < Chunks.Length; i++) + { + AwcXml.OpenTag(sb, indent + 1, "Item"); + Chunks[i].WriteXml(sb, indent + 2); + AwcXml.CloseTag(sb, indent + 1, "Item"); + } + AwcXml.CloseTag(sb, indent, "Chunks"); + } + } + + public void ReadXml(XmlNode node, string wavfolder) + { + Hash = XmlMeta.GetHash(Xml.GetChildInnerText(node, "Name")); + var filename = Xml.GetChildInnerText(node, "FileName"); + if (!string.IsNullOrEmpty(filename) && !string.IsNullOrEmpty(wavfolder)) + { + try + { + var filepath = Path.Combine(wavfolder, filename); + if (File.Exists(filepath)) + { + var wav = File.ReadAllBytes(filepath); + + //TODO: deal with wave file!! + + } + } + catch { } + + } + var fnode = node.SelectSingleNode("StreamFormat"); + if (fnode != null) + { + StreamFormat = new AwcStreamFormat(); + StreamFormat.ReadXml(fnode); + } + var unode = node.SelectSingleNode("UnkUshort"); + if (unode != null) + { + UnkUshort = (ushort)Xml.GetUIntAttribute(unode, "value"); + } + var cnode = node.SelectSingleNode("Chunks"); + if (cnode != null) + { + var clist = new List(); + var inodes = node.SelectNodes("Item"); + foreach (XmlNode inode in inodes) + { + var type = Xml.GetChildEnumInnerText(node, "Type"); + var info = new AwcChunkInfo(type); + var chunk = CreateChunk(info); + chunk?.ReadXml(inode); + clist.Add(chunk); + } + Chunks = clist.ToArray(); + } + + } + + + public static AwcChunk CreateChunk(AwcChunkInfo info) + { + switch (info.Type) + { + case AwcChunkType.data: return new AwcDataChunk(info); + case AwcChunkType.format: return new AwcFormatChunk(info); + case AwcChunkType.animation: return new AwcAnimationChunk(info); + case AwcChunkType.peak: return new AwcPeakChunk(info); + case AwcChunkType.mid: return new AwcMIDIChunk(info); + case AwcChunkType.gesture: return new AwcGestureChunk(info); + case AwcChunkType.granulargrains: return new AwcGranularGrainsChunk(info); + case AwcChunkType.granularloops: return new AwcGranularLoopsChunk(info); + case AwcChunkType.markers: return new AwcMarkersChunk(info); + case AwcChunkType.streamformat: return new AwcStreamFormatChunk(info); + case AwcChunkType.seektable: return new AwcSeekTableChunk(info); + } + return null; + } + + + public override string ToString() { var hash = "0x" + (StreamInfo?.Id.ToString("X") ?? "0").PadLeft(8, '0') + ": "; - if (Format != null) + if (FormatChunk != null) { - return hash + Format?.ToString() ?? "AwcAudio"; + return hash + FormatChunk?.ToString() ?? "AwcAudio"; } - if (MultiChannelFormat != null) + if (StreamFormat != null) { - return hash + MultiChannelFormat?.ToString() ?? "AwcAudio"; + return hash + StreamFormat?.ToString() ?? "AwcAudio"; } - if (MIDIData != null) + if (MidiChunk != null) { - return hash + MIDIData.ToString(); + return hash + MidiChunk.ToString(); } return hash + "Unknown"; } @@ -775,31 +806,40 @@ namespace CodeWalker.GameFiles public byte[] GetWavData() { - if (MultiChannelFormat != null) + if (StreamFormat != null) { - if (Data == null) + if (DataChunk?.Data == null) { var ms = new MemoryStream(); var bw = new BinaryWriter(ms); - if (MultiChannelSource?.MultiChannelBlocks != null) + if (StreamSource?.StreamBlocks != null) { - foreach (var blk in MultiChannelSource.MultiChannelBlocks) + foreach (var blk in StreamSource.StreamBlocks) { - if (MultiChannelIndex < (blk?.Channels?.Length ?? 0)) + if (StreamChannelIndex < (blk?.Channels?.Length ?? 0)) { - var chan = blk.Channels[MultiChannelIndex]; - var cdata = chan.ChannelData; + var chan = blk.Channels[StreamChannelIndex]; + var cdata = chan.Data; bw.Write(cdata); } } } bw.Flush(); ms.Position = 0; - Data = new byte[ms.Length]; - ms.Read(Data, 0, (int)ms.Length); + DataChunk = new AwcDataChunk(null); + DataChunk.Data = new byte[ms.Length]; + ms.Read(DataChunk.Data, 0, (int)ms.Length); } } - return Data; + return DataChunk.Data; + } + + public byte[] GetWavFile() + { + var ms = GetWavStream(); + var data = new byte[ms.Length]; + ms.Read(data, 0, (int)ms.Length); + return data; } public Stream GetWavStream() @@ -807,9 +847,9 @@ namespace CodeWalker.GameFiles byte[] dataPCM = GetWavData(); var bitsPerSample = BitsPerSample; var channels = Channels; - var codec = MultiChannelFormat?.Codec ?? Format?.Codec ?? AwcCodecFormat.PCM; + var codec = StreamFormat?.Codec ?? FormatChunk?.Codec ?? AwcCodecType.PCM; - if (codec == AwcCodecFormat.ADPCM)//just convert ADPCM to PCM for compatibility reasons + if (codec == AwcCodecType.ADPCM)//just convert ADPCM to PCM for compatibility reasons { dataPCM = ADPCMCodec.DecodeADPCM(dataPCM, SampleCount); bitsPerSample = 16; @@ -871,14 +911,300 @@ namespace CodeWalker.GameFiles } } - [TC(typeof(EXP))] public class AwcAudioAnimClipDict + public enum AwcChunkType : byte + { + //should be the last byte of the hash of the name. + data = 0x55, // 0x5EB5E655 + format = 0xFA, // 0x6061D4FA + animation = 0x5C, // 0x938C925C not correct + peak = 0x36, // 0x8B946236 + mid = 0x68, // 0x71DE4C68 + gesture = 0x2B, // 0x23097A2B + granulargrains = 0x5A, // 0xE787895A + granularloops = 0xD9, // 0x252C20D9 + markers = 0xBD, // 0xD4CB98BD + streamformat = 0x48, // 0x81F95048 + seektable = 0xA3, // 0x021E86A3 + } + + [TC(typeof(EXP))] public abstract class AwcChunk : IMetaXmlItem + { + public AwcChunkInfo ChunkInfo { get; set; } + + public AwcChunk(AwcChunkInfo info) + { + ChunkInfo = info; + } + + public abstract void Read(DataReader r); + public abstract void Write(DataWriter w); + public abstract void WriteXml(StringBuilder sb, int indent); + public abstract void ReadXml(XmlNode node); + } + + [TC(typeof(EXP))] public class AwcDataChunk : AwcChunk + { + public byte[] Data { get; set; } + + public AwcDataChunk(AwcChunkInfo info) : base(info) + { } + + public override void Read(DataReader r) + { + Data = r.ReadBytes(ChunkInfo.Size); + } + public override void Write(DataWriter w) + { + w.Write(Data); + } + public override void WriteXml(StringBuilder sb, int indent) + { + AwcXml.StringTag(sb, indent, "Type", ChunkInfo?.Type.ToString()); + //this is just a placeholder. in XML, channel data is written as WAV files + } + public override void ReadXml(XmlNode node) + { + } + + public override string ToString() + { + return "data: " + (Data?.Length ?? 0).ToString() + " bytes"; + } + } + + [TC(typeof(EXP))] public class AwcFormatChunk : AwcChunk + { + public uint Samples { get; set; } + public int LoopPoint { get; set; } + public ushort SamplesPerSecond { get; set; } + public short Headroom { get; set; } + public ushort LoopBegin { get; set; } + public ushort LoopEnd { get; set; } + public ushort PlayEnd { get; set; } + public byte PlayBegin { get; set; } + public AwcCodecType Codec { get; set; } + public uint? UnkExtra { get; set; } + + + public AwcFormatChunk(AwcChunkInfo info) : base(info) + { } + + public override void Read(DataReader r) + { + Samples = r.ReadUInt32(); + LoopPoint = r.ReadInt32(); + SamplesPerSecond = r.ReadUInt16(); + Headroom = r.ReadInt16(); + LoopBegin = r.ReadUInt16(); + LoopEnd = r.ReadUInt16(); + PlayEnd = r.ReadUInt16(); + PlayBegin = r.ReadByte(); + Codec = (AwcCodecType)r.ReadByte(); + + switch (ChunkInfo.Size) //Apparently sometimes this struct is longer? + { + case 20: + break; + case 24: + UnkExtra = r.ReadUInt32(); + break; + default: + break;//no hit + } + } + public override void Write(DataWriter w) + { + w.Write(Samples); + w.Write(LoopPoint); + w.Write(SamplesPerSecond); + w.Write(Headroom); + w.Write(LoopBegin); + w.Write(LoopEnd); + w.Write(PlayEnd); + w.Write(PlayBegin); + w.Write((byte)Codec); + if (UnkExtra.HasValue) + { + w.Write(UnkExtra.Value); + } + } + public override void WriteXml(StringBuilder sb, int indent) + { + AwcXml.StringTag(sb, indent, "Type", ChunkInfo?.Type.ToString()); + AwcXml.StringTag(sb, indent, "Codec", Codec.ToString()); + AwcXml.ValueTag(sb, indent, "Samples", Samples.ToString()); + AwcXml.ValueTag(sb, indent, "SampleRate", SamplesPerSecond.ToString()); + AwcXml.ValueTag(sb, indent, "Headroom", Headroom.ToString()); + AwcXml.ValueTag(sb, indent, "PlayBegin", PlayBegin.ToString()); + AwcXml.ValueTag(sb, indent, "PlayEnd", PlayEnd.ToString()); + AwcXml.ValueTag(sb, indent, "LoopBegin", LoopBegin.ToString()); + AwcXml.ValueTag(sb, indent, "LoopEnd", LoopEnd.ToString()); + if (UnkExtra.HasValue) + { + AwcXml.ValueTag(sb, indent, "UnkExtra", UnkExtra.Value.ToString()); + } + } + public override void ReadXml(XmlNode node) + { + Codec = Xml.GetChildEnumInnerText(node, "Codec"); + Samples = Xml.GetChildUIntAttribute(node, "Samples"); + SamplesPerSecond = (ushort)Xml.GetChildUIntAttribute(node, "SampleRate"); + Headroom = (short)Xml.GetChildIntAttribute(node, "Headroom"); + PlayBegin = (byte)Xml.GetChildUIntAttribute(node, "PlayBegin"); + PlayEnd = (ushort)Xml.GetChildUIntAttribute(node, "PlayEnd"); + LoopBegin = (ushort)Xml.GetChildUIntAttribute(node, "LoopBegin"); + LoopEnd = (ushort)Xml.GetChildUIntAttribute(node, "LoopEnd"); + var unode = node.SelectSingleNode("UnkExtra"); + if (unode != null) + { + UnkExtra = Xml.GetUIntAttribute(unode, "value"); + } + } + + public override string ToString() + { + return "format: " + Codec.ToString() + ": " + Samples.ToString() + " samples, " + SamplesPerSecond.ToString() + " samples/sec, headroom: " + Headroom.ToString(); + } + } + + [TC(typeof(EXP))] public class AwcStreamFormatChunk : AwcChunk + { + public uint BlockCount { get; set; } + public uint BlockSize { get; set; } + public uint ChannelCount { get; set; } + public AwcStreamFormat[] Channels { get; set; } + + public AwcStreamFormatChunk(AwcChunkInfo info) : base(info) + { } + + public override void Read(DataReader r) + { + BlockCount = r.ReadUInt32(); + BlockSize = r.ReadUInt32(); + ChannelCount = r.ReadUInt32(); + + var channels = new List(); + for (int i = 0; i < ChannelCount; i++) + { + var itemInfo = new AwcStreamFormat(); + itemInfo.Read(r); + channels.Add(itemInfo); + } + Channels = channels.ToArray(); + } + public override void Write(DataWriter w) + { + w.Write(BlockCount); + w.Write(BlockSize); + w.Write(ChannelCount); + for (int i = 0; i < ChannelCount; i++) + { + Channels[i].Write(w); + } + } + public override void WriteXml(StringBuilder sb, int indent) + { + AwcXml.StringTag(sb, indent, "Type", ChunkInfo?.Type.ToString()); + //this is just a placeholder. in XML, channel format is written with each channel stream + } + public override void ReadXml(XmlNode node) + { + } + + public override string ToString() + { + return "streamformat: " + ChannelCount.ToString() + " channels, " + BlockCount.ToString() + " blocks, " + BlockSize.ToString() + " bytes per block"; + } + } + + [TC(typeof(EXP))] public class AwcStreamFormat + { + public uint Id { get; set; } + public uint Samples { get; set; } + public short Headroom { get; set; } + public ushort SamplesPerSecond { get; set; } + public AwcCodecType Codec { get; set; } = AwcCodecType.ADPCM; + public byte Unused1 { get; set; } + public ushort Unused2 { get; set; } + + + public void Read(DataReader r) + { + Id = r.ReadUInt32(); + Samples = r.ReadUInt32(); + Headroom = r.ReadInt16(); + SamplesPerSecond = r.ReadUInt16(); + Codec = (AwcCodecType)r.ReadByte(); + Unused1 = r.ReadByte(); + Unused2 = r.ReadUInt16(); + + #region test + //switch (Codec) + //{ + // case AwcCodecFormat.ADPCM: + // break; + // default: + // break;//no hit + //} + //switch (Unused1) + //{ + // case 0: + // break; + // default: + // break;//no hit + //} + //switch (Unused2) + //{ + // case 0: + // break; + // default: + // break;//no hit + //} + #endregion + } + public void Write(DataWriter w) + { + w.Write(Id); + w.Write(Samples); + w.Write(Headroom); + w.Write(SamplesPerSecond); + w.Write((byte)Codec); + w.Write(Unused1); + w.Write(Unused2); + } + public void WriteXml(StringBuilder sb, int indent) + { + AwcXml.StringTag(sb, indent, "Codec", Codec.ToString()); + AwcXml.ValueTag(sb, indent, "Samples", Samples.ToString()); + AwcXml.ValueTag(sb, indent, "SampleRate", SamplesPerSecond.ToString()); + AwcXml.ValueTag(sb, indent, "Headroom", Headroom.ToString()); + } + public void ReadXml(XmlNode node) + { + Codec = Xml.GetChildEnumInnerText(node, "Codec"); + Samples = Xml.GetChildUIntAttribute(node, "Samples"); + SamplesPerSecond = (ushort)Xml.GetChildUIntAttribute(node, "SampleRate"); + Headroom = (short)Xml.GetChildIntAttribute(node, "Headroom"); + } + + public override string ToString() + { + return Id.ToString() + ", " + Codec.ToString() + ": " + Samples.ToString() + " samples, " + SamplesPerSecond.ToString() + " samples/sec, headroom: " + Headroom.ToString(); + } + } + + [TC(typeof(EXP))] public class AwcAnimationChunk : AwcChunk { public byte[] Data { get; set; } public ClipDictionary ClipDict { get; set; } - public AwcAudioAnimClipDict(DataReader r, AwcChunkInfo info) + public AwcAnimationChunk(AwcChunkInfo info) : base(info) + { } + + public override void Read(DataReader r) { - Data = r.ReadBytes(info.Size); + + Data = r.ReadBytes(ChunkInfo.Size); if ((Data == null) || (Data.Length < 16)) return; @@ -905,27 +1231,38 @@ namespace CodeWalker.GameFiles ClipDict = rd.ReadBlock(); + } + public override void Write(DataWriter w) + { + } + public override void WriteXml(StringBuilder sb, int indent) + { + AwcXml.StringTag(sb, indent, "Type", ChunkInfo?.Type.ToString()); } + public override void ReadXml(XmlNode node) + { + } public override string ToString() { - return (ClipDict?.ClipsMapEntries ?? 0).ToString() + " entries"; + return "animation: " + (ClipDict?.ClipsMapEntries ?? 0).ToString() + " entries"; } } - [TC(typeof(EXP))] public class AwcAudioPeak + [TC(typeof(EXP))] public class AwcPeakChunk : AwcChunk { - public AwcChunkInfo ChunkInfo { get; set; } public ushort[] Data { get; set; } - public AwcAudioPeak(DataReader r, AwcChunkInfo info) - { - ChunkInfo = info; + public AwcPeakChunk(AwcChunkInfo info) : base(info) + { } - //if ((info.Size % 2) != 0) + public override void Read(DataReader r) + { + + //if ((ChunkInfo.Size % 2) != 0) //{ }//no hit - var count = info.Size / 2; + var count = ChunkInfo.Size / 2; Data = new ushort[count]; for (int i = 0; i < count; i++) { @@ -933,6 +1270,17 @@ namespace CodeWalker.GameFiles } } + public override void Write(DataWriter w) + { + } + public override void WriteXml(StringBuilder sb, int indent) + { + AwcXml.StringTag(sb, indent, "Type", ChunkInfo?.Type.ToString()); + + } + public override void ReadXml(XmlNode node) + { + } public override string ToString() { @@ -943,16 +1291,15 @@ namespace CodeWalker.GameFiles if (sb.Length > 0) sb.Append(' '); sb.Append(Data[i].ToString()); } - return sb.ToString(); + return "peak: " + sb.ToString(); } } - [TC(typeof(EXP))] public class AwcAudioUnk2B + [TC(typeof(EXP))] public class AwcGestureChunk : AwcChunk { - public AwcChunkInfo ChunkInfo { get; set; } - public Item[] Items { get; set; } + public Gesture[] Gestures { get; set; } - public class Item + public class Gesture { public MetaHash Name { get; set; } public uint UnkUint1 { get; set; } @@ -964,7 +1311,7 @@ namespace CodeWalker.GameFiles public float UnkFloat6 { get; set; } public uint UnkUint2 { get; set; } - public Item(DataReader r) + public Gesture(DataReader r) { Name = r.ReadUInt32(); UnkUint1 = r.ReadUInt32(); @@ -1023,43 +1370,55 @@ namespace CodeWalker.GameFiles } } - public AwcAudioUnk2B(DataReader r, AwcChunkInfo info) + public AwcGestureChunk(AwcChunkInfo info) : base(info) + { } + + public override void Read(DataReader r) { - ChunkInfo = info; // (hash, uint, 6x floats, uint) * n - //if ((info.Size % 36) != 0) + //if ((ChunkInfo.Size % 36) != 0) //{ }//no hit - var count = info.Size / 36; - Items = new Item[count]; + var count = ChunkInfo.Size / 36; + Gestures = new Gesture[count]; for (int i = 0; i < count; i++) { - Items[i] = new Item(r); + Gestures[i] = new Gesture(r); } } + public override void Write(DataWriter w) + { + } + public override void WriteXml(StringBuilder sb, int indent) + { + AwcXml.StringTag(sb, indent, "Type", ChunkInfo?.Type.ToString()); + + } + public override void ReadXml(XmlNode node) + { + } public override string ToString() { - return (Items?.Length ?? 0).ToString() + " items"; + return "gesture: " + (Gestures?.Length ?? 0).ToString() + " items"; } } - [TC(typeof(EXP))] public class AwcAudioUnk5A + [TC(typeof(EXP))] public class AwcGranularGrainsChunk : AwcChunk { - public AwcChunkInfo ChunkInfo { get; set; } - public Item[] Items { get; set; } + public GranularGrain[] GranularGrains { get; set; } public float UnkFloat1 { get; set; } - public class Item + public class GranularGrain { public uint UnkUint1 { get; set; } public float UnkFloat1 { get; set; } public ushort UnkUshort1 { get; set; } public ushort UnkUshort2 { get; set; } - public Item(DataReader r) + public GranularGrain(DataReader r) { UnkUint1 = r.ReadUInt32(); UnkFloat1 = r.ReadSingle(); @@ -1073,19 +1432,22 @@ namespace CodeWalker.GameFiles } } - public AwcAudioUnk5A(DataReader r, AwcChunkInfo info) + public AwcGranularGrainsChunk(AwcChunkInfo info) : base(info) + { + } + + public override void Read(DataReader r) { - ChunkInfo = info; //int, (2x floats, int) * n ? - //if ((info.Size % 12) != 4) + //if ((ChunkInfo.Size % 12) != 4) //{ }//no hit - var count = (info.Size - 4) / 12; - Items = new Item[count]; + var count = (ChunkInfo.Size - 4) / 12; + GranularGrains = new GranularGrain[count]; for (int i = 0; i < count; i++) { - Items[i] = new Item(r); + GranularGrains[i] = new GranularGrain(r); } UnkFloat1 = r.ReadSingle(); @@ -1093,36 +1455,47 @@ namespace CodeWalker.GameFiles //{ }//no hit //if (UnkFloat1 < 0.45833f) //{ }//no hit + + } + public override void Write(DataWriter w) + { + } + public override void WriteXml(StringBuilder sb, int indent) + { + AwcXml.StringTag(sb, indent, "Type", ChunkInfo?.Type.ToString()); + + } + public override void ReadXml(XmlNode node) + { } public override string ToString() { - return (Items?.Length ?? 0).ToString() + " items"; + return "granulargrains: " + (GranularGrains?.Length ?? 0).ToString() + " items"; } } - [TC(typeof(EXP))] public class AwcAudioUnkD9 + [TC(typeof(EXP))] public class AwcGranularLoopsChunk : AwcChunk { - public AwcChunkInfo ChunkInfo { get; set; } - public uint ItemCount { get; set; } - public Item[] Items { get; set; } + public uint GranularLoopsCount { get; set; } + public GranularLoop[] GranularLoops { get; set; } - public class Item + public class GranularLoop { public uint UnkUint1 { get; set; } = 2; - public uint ItemCount { get; set; } + public uint GrainCount { get; set; } public MetaHash Hash { get; set; } = 0x4c633d07; - public uint[] Items { get; set; } + public uint[] Grains { get; set; } - public Item(DataReader r) + public GranularLoop(DataReader r) { UnkUint1 = r.ReadUInt32(); - ItemCount = r.ReadUInt32(); + GrainCount = r.ReadUInt32(); Hash = r.ReadUInt32(); - Items = new uint[ItemCount]; - for (int i = 0; i < ItemCount; i++) + Grains = new uint[GrainCount]; + for (int i = 0; i < GrainCount; i++) { - Items[i] = r.ReadUInt32(); + Grains[i] = r.ReadUInt32(); } switch (UnkUint1) @@ -1143,45 +1516,48 @@ namespace CodeWalker.GameFiles public override string ToString() { - return Hash.ToString() + ": " + UnkUint1.ToString() + ": " + ItemCount.ToString() + " items"; + return Hash.ToString() + ": " + UnkUint1.ToString() + ": " + GrainCount.ToString() + " items"; } } - public AwcAudioUnkD9(DataReader r, AwcChunkInfo info) + public AwcGranularLoopsChunk(AwcChunkInfo info) : base(info) { - ChunkInfo = info; + } + public override void Read(DataReader r) + { //uint count // [count*items]: uint(type?), uint(count2), hash, [count2*uint] - ItemCount = r.ReadUInt32(); - Items = new Item[ItemCount]; - for (int i = 0; i < ItemCount; i++) + GranularLoopsCount = r.ReadUInt32(); + GranularLoops = new GranularLoop[GranularLoopsCount]; + for (int i = 0; i < GranularLoopsCount; i++) { - Items[i] = new Item(r); + GranularLoops[i] = new GranularLoop(r); } - ////if ((info.Size % 4) != 0) - ////{ }//no hit - //var count = info.Size / 4; - //Data = new uint[count]; - //for (int i = 0; i < count; i++) - //{ - // Data[i] = r.ReadUInt32(); - //} + } + public override void Write(DataWriter w) + { + } + public override void WriteXml(StringBuilder sb, int indent) + { + AwcXml.StringTag(sb, indent, "Type", ChunkInfo?.Type.ToString()); } + public override void ReadXml(XmlNode node) + { + } public override string ToString() { - return (Items?.Length ?? 0).ToString() + " items"; + return "granularloops: " + (GranularLoops?.Length ?? 0).ToString() + " items"; } } - [TC(typeof(EXP))] public class AwcAudioMarkers + [TC(typeof(EXP))] public class AwcMarkersChunk : AwcChunk { - public AwcChunkInfo ChunkInfo { get; set; } public Marker[] Markers { get; set; } public class Marker @@ -1257,13 +1633,15 @@ namespace CodeWalker.GameFiles } - public AwcAudioMarkers(DataReader r, AwcChunkInfo info) - { - ChunkInfo = info; + public AwcMarkersChunk(AwcChunkInfo info) : base(info) + { } - //if ((info.Size % 16) != 0) + public override void Read(DataReader r) + { + + //if ((ChunkInfo.Size % 16) != 0) //{ }//no hit - var count = info.Size / 16; + var count = ChunkInfo.Size / 16; Markers = new Marker[count]; for (int i = 0; i < count; i++) { @@ -1271,65 +1649,102 @@ namespace CodeWalker.GameFiles } } + public override void Write(DataWriter w) + { + } + public override void WriteXml(StringBuilder sb, int indent) + { + AwcXml.StringTag(sb, indent, "Type", ChunkInfo?.Type.ToString()); + + } + public override void ReadXml(XmlNode node) + { + } public override string ToString() { - return (Markers?.Length ?? 0).ToString() + " markers"; + return "markers: " + (Markers?.Length ?? 0).ToString() + " markers"; } } - [TC(typeof(EXP))] public class AwcAudioMIDI + [TC(typeof(EXP))] public class AwcMIDIChunk : AwcChunk { - public AwcChunkInfo ChunkInfo { get; set; } public byte[] Data { get; set; } - public AwcAudioMIDI(DataReader r, AwcChunkInfo info) + public AwcMIDIChunk(AwcChunkInfo info) : base(info) + { + } + + public override void Read(DataReader r) + { + Data = r.ReadBytes(ChunkInfo.Size); + } + public override void Write(DataWriter w) + { + } + public override void WriteXml(StringBuilder sb, int indent) + { + AwcXml.StringTag(sb, indent, "Type", ChunkInfo?.Type.ToString()); + + } + public override void ReadXml(XmlNode node) { - ChunkInfo = info; - Data = r.ReadBytes(info.Size); } public override string ToString() { - return "MIDI - " + (Data?.Length??0).ToString() + " bytes"; + return "mid: " + (Data?.Length??0).ToString() + " bytes"; } } - [TC(typeof(EXP))] public class AwcChannelOffsetsInfo + [TC(typeof(EXP))] public class AwcSeekTableChunk : AwcChunk { - public AwcChunkInfo ChunkInfo { get; set; } - public uint[] SampleOffsets { get; set; } + public uint[] SeekTable { get; set; } - public AwcChannelOffsetsInfo(DataReader r, AwcChunkInfo info) + public AwcSeekTableChunk(AwcChunkInfo info) : base(info) + { } + + public override void Read(DataReader r) { - ChunkInfo = info; - //if ((info.Size % 4) != 0) + //if ((ChunkInfo.Size % 4) != 0) //{ }//no hit - var count = info.Size / 4; - SampleOffsets = new uint[count]; + var count = ChunkInfo.Size / 4; + SeekTable = new uint[count]; for (int i = 0; i < count; i++) { - SampleOffsets[i] = r.ReadUInt32(); + SeekTable[i] = r.ReadUInt32(); } + + } + public override void Write(DataWriter w) + { + } + public override void WriteXml(StringBuilder sb, int indent) + { + AwcXml.StringTag(sb, indent, "Type", ChunkInfo?.Type.ToString()); + + } + public override void ReadXml(XmlNode node) + { } public override string ToString() { - return (SampleOffsets?.Length ?? 0).ToString() + " items"; + return "seektable: " + (SeekTable?.Length ?? 0).ToString() + " items"; } } - [TC(typeof(EXP))] public class AwcChannelDataBlock + [TC(typeof(EXP))] public class AwcStreamDataBlock { public byte[] Data { get; set; } public int Index { get; set; } public int SampleOffset { get; set; } - public AwcChannelDataItem[] Channels { get; set; } + public AwcStreamDataChannel[] Channels { get; set; } - public AwcChannelDataBlock(byte[] data, AwcChannelBlockInfo channelInfo, Endianess endianess, int index, int sampleOffset) + public AwcStreamDataBlock(byte[] data, AwcStreamFormatChunk channelInfo, Endianess endianess, int index, int sampleOffset) { Data = data; Index = index; @@ -1339,14 +1754,12 @@ namespace CodeWalker.GameFiles { var r = new DataReader(ms, endianess); - var channels = channelInfo?.Channels; var channelcount = channelInfo?.ChannelCount ?? 0; - var ilist = new List(); + var ilist = new List(); for (int i = 0; i < channelcount; i++) { - var channel = new AwcChannelDataItem(r); - channel.ChannelInfo = ((channels != null) && (i < channels.Length)) ? channels[i] : null; + var channel = new AwcStreamDataChannel(r); ilist.Add(channel); } Channels = ilist.ToArray(); @@ -1362,7 +1775,7 @@ namespace CodeWalker.GameFiles foreach (var channel in Channels) { var bcnt = channel.OffsetCount * 2048; - channel.ChannelData = r.ReadBytes(bcnt); + channel.Data = r.ReadBytes(bcnt); } if (r.Position != r.Length) { }//still more, just padding? @@ -1379,7 +1792,7 @@ namespace CodeWalker.GameFiles } } - [TC(typeof(EXP))] public class AwcChannelDataItem + [TC(typeof(EXP))] public class AwcStreamDataChannel { public int Unk0 { get; set; } public int OffsetCount { get; set; } @@ -1390,12 +1803,10 @@ namespace CodeWalker.GameFiles public int[] SampleOffsets { get; set; } - public AwcChannelBlockItemInfo ChannelInfo { get; set; } //for convenience - - public byte[] ChannelData { get; set; } + public byte[] Data { get; set; } - public AwcChannelDataItem(DataReader r) + public AwcStreamDataChannel(DataReader r) { Unk0 = r.ReadInt32(); OffsetCount = r.ReadInt32(); diff --git a/ExploreForm.cs b/ExploreForm.cs index a1d13a7..6c8d3e4 100644 --- a/ExploreForm.cs +++ b/ExploreForm.cs @@ -2661,7 +2661,7 @@ namespace CodeWalker case MetaFormat.Awc: { var awc = XmlAwc.GetAwc(doc, fpathin); - if (awc.Audios == null) + if (awc.Streams == null) { MessageBox.Show(fname + ": Schema not supported.", "Cannot import AWC XML"); continue; diff --git a/Forms/AwcForm.Designer.cs b/Forms/AwcForm.Designer.cs index bd75db4..50ef71f 100644 --- a/Forms/AwcForm.Designer.cs +++ b/Forms/AwcForm.Designer.cs @@ -53,24 +53,30 @@ this.DetailsPropertyGrid = new CodeWalker.WinForms.PropertyGridFix(); this.Timer = new System.Windows.Forms.Timer(this.components); this.saveFileDialog = new System.Windows.Forms.SaveFileDialog(); + this.XmlTabPage = new System.Windows.Forms.TabPage(); + this.XmlTextBox = new FastColoredTextBoxNS.FastColoredTextBox(); this.MainTabControl.SuspendLayout(); this.PlayerTabPage.SuspendLayout(); this.contextMenuStrip.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.VolumeTrackBar)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.PositionTrackBar)).BeginInit(); this.DetailsTabPage.SuspendLayout(); + this.XmlTabPage.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.XmlTextBox)).BeginInit(); this.SuspendLayout(); // // MainTabControl // this.MainTabControl.Controls.Add(this.PlayerTabPage); this.MainTabControl.Controls.Add(this.DetailsTabPage); + this.MainTabControl.Controls.Add(this.XmlTabPage); this.MainTabControl.Dock = System.Windows.Forms.DockStyle.Fill; this.MainTabControl.Location = new System.Drawing.Point(0, 0); this.MainTabControl.Name = "MainTabControl"; this.MainTabControl.SelectedIndex = 0; - this.MainTabControl.Size = new System.Drawing.Size(576, 361); + this.MainTabControl.Size = new System.Drawing.Size(709, 448); this.MainTabControl.TabIndex = 0; + this.MainTabControl.SelectedIndexChanged += new System.EventHandler(this.MainTabControl_SelectedIndexChanged); // // PlayerTabPage // @@ -88,7 +94,7 @@ this.PlayerTabPage.Location = new System.Drawing.Point(4, 22); this.PlayerTabPage.Name = "PlayerTabPage"; this.PlayerTabPage.Padding = new System.Windows.Forms.Padding(3); - this.PlayerTabPage.Size = new System.Drawing.Size(568, 335); + this.PlayerTabPage.Size = new System.Drawing.Size(701, 422); this.PlayerTabPage.TabIndex = 0; this.PlayerTabPage.Text = "Player"; this.PlayerTabPage.UseVisualStyleBackColor = true; @@ -97,7 +103,7 @@ // this.LabelInfo.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.LabelInfo.AutoSize = true; - this.LabelInfo.Location = new System.Drawing.Point(8, 247); + this.LabelInfo.Location = new System.Drawing.Point(8, 334); this.LabelInfo.Name = "LabelInfo"; this.LabelInfo.Size = new System.Drawing.Size(0, 13); this.LabelInfo.TabIndex = 12; @@ -105,7 +111,7 @@ // LabelTime // this.LabelTime.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.LabelTime.Location = new System.Drawing.Point(285, 308); + this.LabelTime.Location = new System.Drawing.Point(418, 395); this.LabelTime.Name = "LabelTime"; this.LabelTime.Size = new System.Drawing.Size(114, 17); this.LabelTime.TabIndex = 11; @@ -115,7 +121,7 @@ // this.StopButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.StopButton.Enabled = false; - this.StopButton.Location = new System.Drawing.Point(211, 304); + this.StopButton.Location = new System.Drawing.Point(211, 391); this.StopButton.Name = "StopButton"; this.StopButton.Size = new System.Drawing.Size(31, 23); this.StopButton.TabIndex = 10; @@ -127,9 +133,9 @@ // this.VolumeLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.VolumeLabel.AutoSize = true; - this.VolumeLabel.Location = new System.Drawing.Point(405, 308); + this.VolumeLabel.Location = new System.Drawing.Point(538, 395); this.VolumeLabel.Name = "VolumeLabel"; - this.VolumeLabel.Size = new System.Drawing.Size(56, 13); + this.VolumeLabel.Size = new System.Drawing.Size(60, 13); this.VolumeLabel.TabIndex = 9; this.VolumeLabel.Text = "🕩 Volume"; // @@ -137,7 +143,7 @@ // this.chbAutoJump.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.chbAutoJump.AutoSize = true; - this.chbAutoJump.Location = new System.Drawing.Point(17, 308); + this.chbAutoJump.Location = new System.Drawing.Point(17, 395); this.chbAutoJump.Name = "chbAutoJump"; this.chbAutoJump.Size = new System.Drawing.Size(108, 17); this.chbAutoJump.TabIndex = 8; @@ -147,7 +153,7 @@ // PrevButton // this.PrevButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.PrevButton.Location = new System.Drawing.Point(137, 304); + this.PrevButton.Location = new System.Drawing.Point(137, 391); this.PrevButton.Name = "PrevButton"; this.PrevButton.Size = new System.Drawing.Size(31, 23); this.PrevButton.TabIndex = 2; @@ -158,7 +164,7 @@ // NextButton // this.NextButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.NextButton.Location = new System.Drawing.Point(248, 304); + this.NextButton.Location = new System.Drawing.Point(248, 391); this.NextButton.Name = "NextButton"; this.NextButton.Size = new System.Drawing.Size(31, 23); this.NextButton.TabIndex = 4; @@ -169,7 +175,7 @@ // PlayButton // this.PlayButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.PlayButton.Location = new System.Drawing.Point(174, 304); + this.PlayButton.Location = new System.Drawing.Point(174, 391); this.PlayButton.Name = "PlayButton"; this.PlayButton.Size = new System.Drawing.Size(31, 23); this.PlayButton.TabIndex = 3; @@ -193,7 +199,7 @@ this.PlayListView.Location = new System.Drawing.Point(6, 6); this.PlayListView.MultiSelect = false; this.PlayListView.Name = "PlayListView"; - this.PlayListView.Size = new System.Drawing.Size(556, 238); + this.PlayListView.Size = new System.Drawing.Size(689, 325); this.PlayListView.TabIndex = 0; this.PlayListView.UseCompatibleStateImageBehavior = false; this.PlayListView.View = System.Windows.Forms.View.Details; @@ -203,7 +209,7 @@ // PlaylistNameHeader // this.PlaylistNameHeader.Text = "Name"; - this.PlaylistNameHeader.Width = 260; + this.PlaylistNameHeader.Width = 337; // // PlaylistTypeHeader // @@ -241,7 +247,7 @@ this.VolumeTrackBar.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.VolumeTrackBar.BackColor = System.Drawing.SystemColors.ControlLightLight; this.VolumeTrackBar.LargeChange = 10; - this.VolumeTrackBar.Location = new System.Drawing.Point(455, 304); + this.VolumeTrackBar.Location = new System.Drawing.Point(588, 391); this.VolumeTrackBar.Maximum = 100; this.VolumeTrackBar.Name = "VolumeTrackBar"; this.VolumeTrackBar.Size = new System.Drawing.Size(105, 45); @@ -256,10 +262,10 @@ | System.Windows.Forms.AnchorStyles.Right))); this.PositionTrackBar.BackColor = System.Drawing.SystemColors.ControlLightLight; this.PositionTrackBar.LargeChange = 1000; - this.PositionTrackBar.Location = new System.Drawing.Point(6, 266); + this.PositionTrackBar.Location = new System.Drawing.Point(6, 353); this.PositionTrackBar.Maximum = 1000; this.PositionTrackBar.Name = "PositionTrackBar"; - this.PositionTrackBar.Size = new System.Drawing.Size(554, 45); + this.PositionTrackBar.Size = new System.Drawing.Size(687, 45); this.PositionTrackBar.TabIndex = 1; this.PositionTrackBar.TickStyle = System.Windows.Forms.TickStyle.None; this.PositionTrackBar.Scroll += new System.EventHandler(this.PositionTrackBar_Scroll); @@ -270,7 +276,7 @@ this.DetailsTabPage.Location = new System.Drawing.Point(4, 22); this.DetailsTabPage.Name = "DetailsTabPage"; this.DetailsTabPage.Padding = new System.Windows.Forms.Padding(3); - this.DetailsTabPage.Size = new System.Drawing.Size(568, 335); + this.DetailsTabPage.Size = new System.Drawing.Size(701, 422); this.DetailsTabPage.TabIndex = 1; this.DetailsTabPage.Text = "Details"; this.DetailsTabPage.UseVisualStyleBackColor = true; @@ -281,7 +287,7 @@ this.DetailsPropertyGrid.HelpVisible = false; this.DetailsPropertyGrid.Location = new System.Drawing.Point(3, 3); this.DetailsPropertyGrid.Name = "DetailsPropertyGrid"; - this.DetailsPropertyGrid.Size = new System.Drawing.Size(562, 329); + this.DetailsPropertyGrid.Size = new System.Drawing.Size(695, 416); this.DetailsPropertyGrid.TabIndex = 0; // // Timer @@ -295,11 +301,63 @@ this.saveFileDialog.DefaultExt = "wav"; this.saveFileDialog.Filter = "Wave files (*.wav)|*.wav"; // + // XmlTabPage + // + this.XmlTabPage.Controls.Add(this.XmlTextBox); + this.XmlTabPage.Location = new System.Drawing.Point(4, 22); + this.XmlTabPage.Name = "XmlTabPage"; + this.XmlTabPage.Size = new System.Drawing.Size(701, 422); + this.XmlTabPage.TabIndex = 2; + this.XmlTabPage.Text = "XML"; + this.XmlTabPage.UseVisualStyleBackColor = true; + // + // XmlTextBox + // + this.XmlTextBox.AutoCompleteBracketsList = new char[] { + '(', + ')', + '{', + '}', + '[', + ']', + '\"', + '\"', + '\'', + '\''}; + this.XmlTextBox.AutoIndentChars = false; + this.XmlTextBox.AutoIndentCharsPatterns = ""; + this.XmlTextBox.AutoIndentExistingLines = false; + this.XmlTextBox.AutoScrollMinSize = new System.Drawing.Size(27, 14); + this.XmlTextBox.BackBrush = null; + this.XmlTextBox.CharHeight = 14; + this.XmlTextBox.CharWidth = 8; + this.XmlTextBox.CommentPrefix = null; + this.XmlTextBox.Cursor = System.Windows.Forms.Cursors.IBeam; + this.XmlTextBox.DelayedEventsInterval = 1; + this.XmlTextBox.DisabledColor = System.Drawing.Color.FromArgb(((int)(((byte)(100)))), ((int)(((byte)(180)))), ((int)(((byte)(180)))), ((int)(((byte)(180))))); + this.XmlTextBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.XmlTextBox.IsReplaceMode = false; + this.XmlTextBox.Language = FastColoredTextBoxNS.Language.XML; + this.XmlTextBox.LeftBracket = '<'; + this.XmlTextBox.LeftBracket2 = '('; + this.XmlTextBox.Location = new System.Drawing.Point(0, 0); + this.XmlTextBox.Name = "XmlTextBox"; + this.XmlTextBox.Paddings = new System.Windows.Forms.Padding(0); + this.XmlTextBox.RightBracket = '>'; + this.XmlTextBox.RightBracket2 = ')'; + this.XmlTextBox.SelectionColor = System.Drawing.Color.FromArgb(((int)(((byte)(60)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(255))))); + this.XmlTextBox.ServiceColors = ((FastColoredTextBoxNS.ServiceColors)(resources.GetObject("XmlTextBox.ServiceColors"))); + this.XmlTextBox.Size = new System.Drawing.Size(701, 422); + this.XmlTextBox.TabIndex = 2; + this.XmlTextBox.Zoom = 100; + this.XmlTextBox.TextChanged += new System.EventHandler(this.XmlTextBox_TextChanged); + this.XmlTextBox.VisibleRangeChangedDelayed += new System.EventHandler(this.XmlTextBox_VisibleRangeChangedDelayed); + // // AwcForm // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(576, 361); + this.ClientSize = new System.Drawing.Size(709, 448); this.Controls.Add(this.MainTabControl); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.MinimumSize = new System.Drawing.Size(592, 300); @@ -313,6 +371,8 @@ ((System.ComponentModel.ISupportInitialize)(this.VolumeTrackBar)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.PositionTrackBar)).EndInit(); this.DetailsTabPage.ResumeLayout(false); + this.XmlTabPage.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.XmlTextBox)).EndInit(); this.ResumeLayout(false); } @@ -342,5 +402,7 @@ private System.Windows.Forms.ToolStripMenuItem ExportAsWav; private System.Windows.Forms.SaveFileDialog saveFileDialog; private System.Windows.Forms.ColumnHeader PlaylistSizeHeader; + private System.Windows.Forms.TabPage XmlTabPage; + private FastColoredTextBoxNS.FastColoredTextBox XmlTextBox; } } \ No newline at end of file diff --git a/Forms/AwcForm.cs b/Forms/AwcForm.cs index fe85668..1c4cc69 100644 --- a/Forms/AwcForm.cs +++ b/Forms/AwcForm.cs @@ -1,10 +1,12 @@ using CodeWalker.GameFiles; +using FastColoredTextBoxNS; using SharpDX.Multimedia; using SharpDX.XAudio2; using System; using System.IO; using System.Diagnostics; using System.Windows.Forms; +using System.Drawing; namespace CodeWalker.Forms { @@ -12,7 +14,7 @@ namespace CodeWalker.Forms { public AwcFile Awc { get; set; } - private AwcAudio currentAudio; + private AwcStream currentAudio; private XAudio2 xAudio2; private MasteringVoice masteringVoice; private AudioBuffer audioBuffer; @@ -38,6 +40,10 @@ namespace CodeWalker.Forms private float trackLength; private bool trackFinished; + private bool LoadingXml = false; + private bool DelayHighlight = false; + + public AwcForm() { InitializeComponent(); @@ -50,6 +56,48 @@ namespace CodeWalker.Forms Text = fileName + " - AWC Player - CodeWalker by dexyfex"; } + private void UpdateXmlTextBox(string xml) + { + LoadingXml = true; + XmlTextBox.Text = ""; + XmlTextBox.Language = Language.XML; + DelayHighlight = false; + + if (string.IsNullOrEmpty(xml)) + { + LoadingXml = false; + return; + } + //if (xml.Length > (1048576 * 5)) + //{ + // XmlTextBox.Language = Language.Custom; + // XmlTextBox.Text = "[XML size > 10MB - Not shown due to performance limitations - Please use an external viewer for this file.]"; + // return; + //} + //else + if (xml.Length > (1024 * 512)) + { + XmlTextBox.Language = Language.Custom; + DelayHighlight = true; + } + //else + //{ + // XmlTextBox.Language = Language.XML; + //} + + + Cursor = Cursors.WaitCursor; + + + + XmlTextBox.Text = xml; + //XmlTextBox.IsChanged = false; + XmlTextBox.ClearUndo(); + + Cursor = Cursors.Default; + LoadingXml = false; + } + public void LoadAwc(AwcFile awc) { Awc = awc; @@ -64,11 +112,11 @@ namespace CodeWalker.Forms PlayListView.Items.Clear(); float totalLength = 0; - if (awc.Audios != null) + if (awc.Streams != null) { - foreach (var audio in awc.Audios) + foreach (var audio in awc.Streams) { - if (audio.MultiChannelBlocks != null) continue;//don't display multichannel source audios + if (audio.StreamBlocks != null) continue;//don't display multichannel source audios var item = PlayListView.Items.Add(audio.Name); item.SubItems.Add(audio.Type); item.SubItems.Add(audio.LengthStr); @@ -78,10 +126,44 @@ namespace CodeWalker.Forms } } - LabelInfo.Text = awc.Audios.Length.ToString() + " track(s), Length: " + TimeSpan.FromSeconds((float)totalLength).ToString("h\\:mm\\:ss"); + LabelInfo.Text = awc.Streams.Length.ToString() + " track(s), Length: " + TimeSpan.FromSeconds((float)totalLength).ToString("h\\:mm\\:ss"); UpdateFormTitle(); } + public void LoadXml() + { + if (Awc != null) + { + var xml = AwcXml.GetXml(Awc); + UpdateXmlTextBox(xml); + } + } + + private void HTMLSyntaxHighlight(Range range) + { + try + { + Style BlueStyle = new TextStyle(Brushes.Blue, null, FontStyle.Regular); + Style RedStyle = new TextStyle(Brushes.Red, null, FontStyle.Regular); + Style MaroonStyle = new TextStyle(Brushes.Maroon, null, FontStyle.Regular); + + //clear style of changed range + range.ClearStyle(BlueStyle, MaroonStyle, RedStyle); + //tag brackets highlighting + range.SetStyle(BlueStyle, @"<|/>|"); + //tag name + range.SetStyle(MaroonStyle, @"<(?[!\w]+)"); + //end of tag + range.SetStyle(MaroonStyle, @"\w+)>"); + //attributes + range.SetStyle(RedStyle, @"(?\S+?)='[^']*'|(?\S+)=""[^""]*""|(?\S+)=\S+"); + //attribute values + range.SetStyle(BlueStyle, @"\S+?=(?'[^']*')|\S+=(?""[^""]*"")|\S+=(?\S+)"); + } + catch + { } + } + private void Stop() { if (playerState != PlayerState.Stopped) @@ -127,7 +209,7 @@ namespace CodeWalker.Forms } } - private void InitializeAudio(AwcAudio audio, float playBegin = 0) + private void InitializeAudio(AwcStream audio, float playBegin = 0) { currentAudio = audio; trackLength = audio.Length; @@ -174,15 +256,15 @@ namespace CodeWalker.Forms if (PlayListView.SelectedItems.Count == 1) { var item = PlayListView.SelectedItems[0]; - var audio = item.Tag as AwcAudio; + var audio = item.Tag as AwcStream; - if ((audio?.Format != null) || (audio?.MultiChannelFormat != null)) + if ((audio?.FormatChunk != null) || (audio?.StreamFormat != null)) { InitializeAudio(audio); sourceVoice.Start(); SetPlayerState(PlayerState.Playing); } - else if (audio.MIDIData != null) + else if (audio.MidiChunk != null) { //todo: play MIDI? } @@ -335,10 +417,10 @@ namespace CodeWalker.Forms if (PlayListView.SelectedItems.Count == 1) { var item = PlayListView.SelectedItems[0]; - var audio = item.Tag as AwcAudio; + var audio = item.Tag as AwcStream; var ext = ".wav"; - if (audio?.MIDIData != null) + if (audio?.MidiChunk != null) { ext = ".midi"; } @@ -346,11 +428,11 @@ namespace CodeWalker.Forms saveFileDialog.FileName = audio.Name + ext; if (saveFileDialog.ShowDialog() == DialogResult.OK) { - if (audio?.MIDIData != null) + if (audio?.MidiChunk != null) { - File.WriteAllBytes(saveFileDialog.FileName, audio.MIDIData.Data); + File.WriteAllBytes(saveFileDialog.FileName, audio.MidiChunk.Data); } - else if ((audio?.Format != null) || (audio?.MultiChannelFormat != null)) + else if ((audio?.FormatChunk != null) || (audio?.StreamFormat != null)) { Stream wavStream = audio.GetWavStream(); FileStream stream = File.Create(saveFileDialog.FileName); @@ -369,13 +451,42 @@ namespace CodeWalker.Forms if (PlayListView.SelectedItems.Count == 1) { var item = PlayListView.SelectedItems[0]; - var audio = item.Tag as AwcAudio; - if (audio?.MIDIData != null) + var audio = item.Tag as AwcStream; + if (audio?.MidiChunk != null) { ExportAsWav.Text = "Export as .midi"; } } } + private void MainTabControl_SelectedIndexChanged(object sender, EventArgs e) + { + if (MainTabControl.SelectedTab == XmlTabPage) + { + if (string.IsNullOrEmpty(XmlTextBox.Text)) + { + LoadXml(); + } + } + } + + private void XmlTextBox_VisibleRangeChangedDelayed(object sender, EventArgs e) + { + //this approach is much faster to load, but no outlining is available + + //highlight only visible area of text + if (DelayHighlight) + { + HTMLSyntaxHighlight(XmlTextBox.VisibleRange); + } + } + + private void XmlTextBox_TextChanged(object sender, TextChangedEventArgs e) + { + if (!LoadingXml) + { + + } + } } } diff --git a/Forms/AwcForm.resx b/Forms/AwcForm.resx index 5fa59c9..9e7f0b2 100644 --- a/Forms/AwcForm.resx +++ b/Forms/AwcForm.resx @@ -120,6 +120,24 @@ 99, 17 + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdGYXN0Q29sb3JlZFRleHRCb3gsIFZlcnNpb249Mi4xNi4yNC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWZiOGFhMTJiOTk0ZWY2MWIMAwAAAFFTeXN0 + ZW0uRHJhd2luZywgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2Vu + PWIwM2Y1ZjdmMTFkNTBhM2EFAQAAACJGYXN0Q29sb3JlZFRleHRCb3hOUy5TZXJ2aWNlQ29sb3JzBgAA + ACg8Q29sbGFwc2VNYXJrZXJGb3JlQ29sb3I+a19fQmFja2luZ0ZpZWxkKDxDb2xsYXBzZU1hcmtlckJh + Y2tDb2xvcj5rX19CYWNraW5nRmllbGQqPENvbGxhcHNlTWFya2VyQm9yZGVyQ29sb3I+a19fQmFja2lu + Z0ZpZWxkJjxFeHBhbmRNYXJrZXJGb3JlQ29sb3I+a19fQmFja2luZ0ZpZWxkJjxFeHBhbmRNYXJrZXJC + YWNrQ29sb3I+a19fQmFja2luZ0ZpZWxkKDxFeHBhbmRNYXJrZXJCb3JkZXJDb2xvcj5rX19CYWNraW5n + RmllbGQEBAQEBAQUU3lzdGVtLkRyYXdpbmcuQ29sb3IDAAAAFFN5c3RlbS5EcmF3aW5nLkNvbG9yAwAA + ABRTeXN0ZW0uRHJhd2luZy5Db2xvcgMAAAAUU3lzdGVtLkRyYXdpbmcuQ29sb3IDAAAAFFN5c3RlbS5E + cmF3aW5nLkNvbG9yAwAAABRTeXN0ZW0uRHJhd2luZy5Db2xvcgMAAAACAAAABfz///8UU3lzdGVtLkRy + YXdpbmcuQ29sb3IEAAAABG5hbWUFdmFsdWUKa25vd25Db2xvcgVzdGF0ZQEAAAAJBwcDAAAACgAAAAAA + AAAAlgABAAH7/////P///woAAAAAAAAAAKQAAQAB+v////z///8KAAAAAAAAAACWAAEAAfn////8//// + CgAAAAAAAAAATgABAAH4/////P///woAAAAAAAAAAKQAAQAB9/////z///8KAAAAAAAAAACWAAEACw== + + 17, 17