AWC/XML conversion peak chunk auto generation

This commit is contained in:
dexy 2020-03-09 23:52:15 +11:00
parent 33048b07bd
commit 408bcaf837
3 changed files with 169 additions and 35 deletions

View File

@ -218,6 +218,7 @@ namespace CodeWalker.GameFiles
BuildStreamDict(); BuildStreamDict();
//BuildPeakChunks();//for testing purposes only
//TestChunkOrdering(r.Length); //TestChunkOrdering(r.Length);
} }
@ -333,6 +334,8 @@ namespace CodeWalker.GameFiles
} }
BuildPeakChunks();
BuildChunkIndices(); BuildChunkIndices();
BuildStreamInfos(); BuildStreamInfos();
@ -470,6 +473,126 @@ namespace CodeWalker.GameFiles
} }
} }
public void BuildPeakChunks()
{
if (Streams == null) return;
foreach (var stream in Streams)
{
if (stream.FormatChunk == null)
{ continue; } // peak chunk is always null here
if (stream.FormatChunk.Peak == null)
{ continue; } //rare, only in boar_near.awc
//if (stream.FormatChunk.PeakUnk != 0)
//{ }//some hits??
//if ((stream.PeakChunk != null) && (stream.FormatChunk.Peak == null))
//{ }//no hit
var pcmData = stream.GetPcmData();
var codec = stream.FormatChunk.Codec;
var smpCount = stream.FormatChunk.Samples;
var peak0 = stream.FormatChunk.PeakVal;//orig value for comparison
var peakvals0 = stream.PeakChunk?.Data;//orig values for comparison
var peakSize = 4096;
var peakCount = (smpCount - peakSize) / peakSize;
if (peakCount != (peakvals0?.Length ?? 0))
{ }//no hit
var peakvals = new ushort[peakCount];
ushort getSample(int i)
{
var ei = i * 2;
if ((ei + 2) >= pcmData.Length) return 0;
var smp = BitConverter.ToInt16(pcmData, i * 2);
return (ushort)Math.Min(Math.Abs((int)smp) * 2, 65535);
}
ushort getPeak(int n)
{
var o = n * peakSize;
var p = (ushort)0;
for (int i = 0; i < peakSize; i++)
{
var s = getSample(i + o);
p = Math.Max(p, s);
}
return p;
}
var peak = getPeak(0);
stream.FormatChunk.PeakVal = peak;
//// testing
//if (peak != peak0)
//{
// if (codec == AwcCodecType.PCM)
// { }//just 1 hit here for a tiny file in melee.awc
// if (codec == AwcCodecType.ADPCM)
// {
// var diff = Math.Abs(peak - peak0);
// if (diff > 25000)
// { }//no hit
// }
//}
for (int n = 0; n < peakCount; n++)
{
var peakn = getPeak(n + 1);
peakvals[n] = peakn;
//// testing
//if (peakvals0 != null)
//{
// var peakc = peakvals0[n];
// if (peakn != peakc)
// {
// if (codec == AwcCodecType.PCM)
// { }//no hit
// if (codec == AwcCodecType.ADPCM)
// {
// var diff = Math.Abs(peakn - peakc);
// if (diff > 33000)
// { }//no hit
// }
// }
//}
}
if (stream.PeakChunk == null)
{
if (peakvals.Length > 0)
{
//need to add a new peak chunk for the extra data (could happen on XML import)
var chunk = AwcStream.CreateChunk(new AwcChunkInfo() { Type = AwcChunkType.peak });
var chunklist = stream.Chunks?.ToList() ?? new List<AwcChunk>();
chunklist.Add(chunk);
stream.Chunks = chunklist.ToArray();
stream.PeakChunk = chunk as AwcPeakChunk;
}
}
if (stream.PeakChunk != null)
{
if (peakvals.Length > 0)
{
stream.PeakChunk.Data = peakvals;
}
else
{
//remove any unneeded peak chunk (could happen on XML import)
var chunklist = stream.Chunks?.ToList() ?? new List<AwcChunk>();
chunklist.Remove(stream.PeakChunk);
stream.Chunks = chunklist.ToArray();
stream.PeakChunk = null;
}
}
}
}
public void BuildChunkIndices() public void BuildChunkIndices()
{ {
if (Streams == null) return; if (Streams == null) return;
@ -1258,7 +1381,7 @@ namespace CodeWalker.GameFiles
} }
public byte[] GetWavData() public byte[] GetRawData()
{ {
if (StreamFormat != null) if (StreamFormat != null)
{ {
@ -1288,6 +1411,19 @@ namespace CodeWalker.GameFiles
return DataChunk.Data; return DataChunk.Data;
} }
public byte[] GetPcmData()
{
var data = GetRawData();
var codec = StreamFormat?.Codec ?? FormatChunk?.Codec ?? AwcCodecType.PCM;
if (codec == AwcCodecType.ADPCM)//just convert ADPCM to PCM for compatibility reasons
{
data = ADPCMCodec.DecodeADPCM(data, SampleCount);
}
return data;
}
public byte[] GetWavFile() public byte[] GetWavFile()
{ {
var ms = GetWavStream(); var ms = GetWavStream();
@ -1298,16 +1434,9 @@ namespace CodeWalker.GameFiles
public Stream GetWavStream() public Stream GetWavStream()
{ {
byte[] dataPCM = GetWavData(); byte[] dataPCM = GetPcmData();
var bitsPerSample = 16; var bitsPerSample = 16;
var channels = 1; var channels = 1;
var codec = StreamFormat?.Codec ?? FormatChunk?.Codec ?? AwcCodecType.PCM;
if (codec == AwcCodecType.ADPCM)//just convert ADPCM to PCM for compatibility reasons
{
dataPCM = ADPCMCodec.DecodeADPCM(dataPCM, SampleCount);
}
short formatcodec = 1; // 1 = WAVE_FORMAT_PCM short formatcodec = 1; // 1 = WAVE_FORMAT_PCM
int byteRate = SamplesPerSecond * channels * bitsPerSample / 8; int byteRate = SamplesPerSecond * channels * bitsPerSample / 8;
short blockAlign = (short)(channels * bitsPerSample / 8); short blockAlign = (short)(channels * bitsPerSample / 8);
@ -1509,7 +1638,7 @@ namespace CodeWalker.GameFiles
[TC(typeof(EXP))] public class AwcFormatChunk : AwcChunk [TC(typeof(EXP))] public class AwcFormatChunk : AwcChunk
{ {
public override int ChunkSize => UnkExtra.HasValue ? 24 : 20; public override int ChunkSize => Peak.HasValue ? 24 : 20;
public uint Samples { get; set; } public uint Samples { get; set; }
public int LoopPoint { get; set; } public int LoopPoint { get; set; }
@ -1520,7 +1649,9 @@ namespace CodeWalker.GameFiles
public ushort PlayEnd { get; set; } public ushort PlayEnd { get; set; }
public byte PlayBegin { get; set; } public byte PlayBegin { get; set; }
public AwcCodecType Codec { get; set; } public AwcCodecType Codec { get; set; }
public uint? UnkExtra { get; set; } public uint? Peak { get; set; }
public ushort PeakVal { get { return (ushort)((Peak ?? 0) & 0xFFFF); } set { Peak = ((Peak ?? 0) & 0xFFFF0000) + value; } }
public ushort PeakUnk { get { return (ushort)((Peak ?? 0) >> 16); } set { Peak = ((Peak ?? 0) & 0xFFFF) + (ushort)(value << 16); } }
public AwcFormatChunk(AwcChunkInfo info) : base(info) public AwcFormatChunk(AwcChunkInfo info) : base(info)
@ -1543,7 +1674,7 @@ namespace CodeWalker.GameFiles
case 20: case 20:
break; break;
case 24: case 24:
UnkExtra = r.ReadUInt32(); Peak = r.ReadUInt32();
break; break;
default: default:
break;//no hit break;//no hit
@ -1560,9 +1691,9 @@ namespace CodeWalker.GameFiles
w.Write(PlayEnd); w.Write(PlayEnd);
w.Write(PlayBegin); w.Write(PlayBegin);
w.Write((byte)Codec); w.Write((byte)Codec);
if (UnkExtra.HasValue) if (Peak.HasValue)
{ {
w.Write(UnkExtra.Value); w.Write(Peak.Value);
} }
} }
public override void WriteXml(StringBuilder sb, int indent) public override void WriteXml(StringBuilder sb, int indent)
@ -1577,9 +1708,9 @@ namespace CodeWalker.GameFiles
AwcXml.ValueTag(sb, indent, "LoopBegin", LoopBegin.ToString()); AwcXml.ValueTag(sb, indent, "LoopBegin", LoopBegin.ToString());
AwcXml.ValueTag(sb, indent, "LoopEnd", LoopEnd.ToString()); AwcXml.ValueTag(sb, indent, "LoopEnd", LoopEnd.ToString());
AwcXml.ValueTag(sb, indent, "LoopPoint", LoopPoint.ToString()); AwcXml.ValueTag(sb, indent, "LoopPoint", LoopPoint.ToString());
if (UnkExtra.HasValue) if (Peak.HasValue)
{ {
AwcXml.ValueTag(sb, indent, "UnkExtra", UnkExtra.Value.ToString()); AwcXml.ValueTag(sb, indent, "Peak", PeakUnk.ToString(), "unk");
} }
} }
public override void ReadXml(XmlNode node) public override void ReadXml(XmlNode node)
@ -1593,10 +1724,10 @@ namespace CodeWalker.GameFiles
LoopBegin = (ushort)Xml.GetChildUIntAttribute(node, "LoopBegin"); LoopBegin = (ushort)Xml.GetChildUIntAttribute(node, "LoopBegin");
LoopEnd = (ushort)Xml.GetChildUIntAttribute(node, "LoopEnd"); LoopEnd = (ushort)Xml.GetChildUIntAttribute(node, "LoopEnd");
LoopPoint = Xml.GetChildIntAttribute(node, "LoopPoint"); LoopPoint = Xml.GetChildIntAttribute(node, "LoopPoint");
var unode = node.SelectSingleNode("UnkExtra"); var pnode = node.SelectSingleNode("Peak");
if (unode != null) if (pnode != null)
{ {
UnkExtra = Xml.GetUIntAttribute(unode, "value"); PeakUnk = (ushort)Xml.GetUIntAttribute(pnode, "unk");
} }
} }
@ -1860,11 +1991,12 @@ namespace CodeWalker.GameFiles
public override void WriteXml(StringBuilder sb, int indent) public override void WriteXml(StringBuilder sb, int indent)
{ {
AwcXml.StringTag(sb, indent, "Type", ChunkInfo?.Type.ToString()); AwcXml.StringTag(sb, indent, "Type", ChunkInfo?.Type.ToString());
AwcXml.WriteRawArray(sb, Data, indent, "Data", ""); ////this is just a placeholder. in XML, peak data is generated from imported WAV data
//AwcXml.WriteRawArray(sb, Data, indent, "Data", "");
} }
public override void ReadXml(XmlNode node) public override void ReadXml(XmlNode node)
{ {
Data = Xml.GetChildRawUshortArray(node, "Data"); //Data = Xml.GetChildRawUshortArray(node, "Data");
} }
public override string ToString() public override string ToString()
@ -2045,10 +2177,10 @@ namespace CodeWalker.GameFiles
public class GranularGrain : IMetaXmlItem public class GranularGrain : IMetaXmlItem
{ {
public uint UnkUint1 { get; set; } public uint UnkUint1 { get; set; } //sample offset?
public float UnkFloat1 { get; set; } public float UnkFloat1 { get; set; } // duration..? rpm..?
public ushort UnkUshort1 { get; set; } public ushort UnkUshort1 { get; set; } //peak low?
public ushort UnkUshort2 { get; set; } public ushort UnkUshort2 { get; set; } //peak high?
public void Read(DataReader r) public void Read(DataReader r)
{ {
@ -2222,16 +2354,16 @@ namespace CodeWalker.GameFiles
public class GranularLoop : IMetaXmlItem public class GranularLoop : IMetaXmlItem
{ {
public uint UnkUint1 { get; set; } = 2; public uint UnkUint1 { get; set; } = 2; //style="walk"?
public uint GrainCount { get; set; } public uint GrainCount { get; set; }
public MetaHash Hash { get; set; } = 0x4c633d07; public MetaHash Identifier { get; set; } = 0x4c633d07; // "loop"
public uint[] Grains { get; set; } public uint[] Grains { get; set; }
public void Read(DataReader r) public void Read(DataReader r)
{ {
UnkUint1 = r.ReadUInt32(); UnkUint1 = r.ReadUInt32();
GrainCount = r.ReadUInt32(); GrainCount = r.ReadUInt32();
Hash = r.ReadUInt32(); Identifier = r.ReadUInt32();
Grains = new uint[GrainCount]; Grains = new uint[GrainCount];
for (int i = 0; i < GrainCount; i++) for (int i = 0; i < GrainCount; i++)
{ {
@ -2258,7 +2390,7 @@ namespace CodeWalker.GameFiles
GrainCount = (uint)(Grains?.Length ?? 0); GrainCount = (uint)(Grains?.Length ?? 0);
w.Write(UnkUint1); w.Write(UnkUint1);
w.Write(GrainCount); w.Write(GrainCount);
w.Write(Hash); w.Write(Identifier);
for (int i = 0; i < GrainCount; i++) for (int i = 0; i < GrainCount; i++)
{ {
w.Write(Grains[i]); w.Write(Grains[i]);
@ -2267,19 +2399,19 @@ namespace CodeWalker.GameFiles
public void WriteXml(StringBuilder sb, int indent) public void WriteXml(StringBuilder sb, int indent)
{ {
//AwcXml.ValueTag(sb, indent, "UnkUint1", UnkUint1.ToString()); //AwcXml.ValueTag(sb, indent, "UnkUint1", UnkUint1.ToString());
//AwcXml.StringTag(sb, indent, "Hash", AwcXml.HashString(Hash)); //AwcXml.StringTag(sb, indent, "Identifier", AwcXml.HashString(Hash));
AwcXml.WriteRawArray(sb, Grains, indent, "Grains", ""); AwcXml.WriteRawArray(sb, Grains, indent, "Grains", "");
} }
public void ReadXml(XmlNode node) public void ReadXml(XmlNode node)
{ {
//UnkUint1 = Xml.GetChildUIntAttribute(node, "UnkUint1"); //UnkUint1 = Xml.GetChildUIntAttribute(node, "UnkUint1");
//Hash = XmlMeta.GetHash(Xml.GetChildInnerText(node, "Hash")); //Hash = XmlMeta.GetHash(Xml.GetChildInnerText(node, "Identifier"));
Grains = Xml.GetChildRawUintArray(node, "Grains"); Grains = Xml.GetChildRawUintArray(node, "Grains");
} }
public override string ToString() public override string ToString()
{ {
return Hash.ToString() + ": " + UnkUint1.ToString() + ": " + GrainCount.ToString() + " items"; return Identifier.ToString() + ": " + UnkUint1.ToString() + ": " + GrainCount.ToString() + " items";
} }
} }

View File

@ -3548,7 +3548,7 @@ namespace CodeWalker.GameFiles
exportcamera = 962998194, //cutscene related stuff exportcamera = 962998194, //cutscene related stuff
loop = 1281572103, //found in AWCs GranularLoop

View File

@ -1782,12 +1782,14 @@ namespace CodeWalker.GameFiles
sb.Append(">"); sb.Append(">");
if (appendLine) sb.AppendLine(); if (appendLine) sb.AppendLine();
} }
public static void ValueTag(StringBuilder sb, int indent, string name, string val) public static void ValueTag(StringBuilder sb, int indent, string name, string val, string attr = "value")
{ {
Indent(sb, indent); Indent(sb, indent);
sb.Append("<"); sb.Append("<");
sb.Append(name); sb.Append(name);
sb.Append(" value=\""); sb.Append(" ");
sb.Append(attr);
sb.Append("=\"");
sb.Append(val); sb.Append(val);
sb.Append("\" />"); sb.Append("\" />");
sb.AppendLine(); sb.AppendLine();