mirror of
https://mirror.ghproxy.com/https://github.com/dexyfex/CodeWalker
synced 2024-07-15 06:11:24 +08:00
AWC/XML conversion peak chunk auto generation
This commit is contained in:
parent
33048b07bd
commit
408bcaf837
|
@ -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";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3548,7 +3548,7 @@ namespace CodeWalker.GameFiles
|
||||||
|
|
||||||
exportcamera = 962998194, //cutscene related stuff
|
exportcamera = 962998194, //cutscene related stuff
|
||||||
|
|
||||||
|
loop = 1281572103, //found in AWCs GranularLoop
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user