mirror of
https://mirror.ghproxy.com/https://github.com/dexyfex/CodeWalker
synced 2024-11-26 17:02:53 +08:00
AWC improvements
This commit is contained in:
parent
4020327e64
commit
668e8a2841
@ -23,9 +23,15 @@ namespace CodeWalker.GameFiles
|
|||||||
public int StreamCount { get; set; }
|
public int StreamCount { get; set; }
|
||||||
public int InfoOffset { get; set; }
|
public int InfoOffset { get; set; }
|
||||||
|
|
||||||
|
public bool UnkUshortsFlag { get { return ((Flags & 1) == 1); } }
|
||||||
|
public bool SingleChannelEncryptFlag { get { return ((Flags & 2) == 2); } }
|
||||||
|
public bool MultiChannelFlag { get { return ((Flags & 4) == 4); } }
|
||||||
|
public bool MultiChannelEncryptFlag { get { return ((Flags & 8) == 8); } }
|
||||||
|
|
||||||
public ushort[] UnkUshorts { get; set; } //offsets of some sort?
|
public ushort[] UnkUshorts { get; set; } //offsets of some sort?
|
||||||
|
|
||||||
public bool MultiChannel { get; set; }
|
|
||||||
|
public bool WholeFileEncrypted { get; set; }
|
||||||
|
|
||||||
public AwcStreamInfo[] StreamInfos { get; set; }
|
public AwcStreamInfo[] StreamInfos { get; set; }
|
||||||
public uint[] AudioIds { get; set; }
|
public uint[] AudioIds { get; set; }
|
||||||
@ -80,6 +86,7 @@ namespace CodeWalker.GameFiles
|
|||||||
{
|
{
|
||||||
Decrypt_RSXXTEA(data, GTA5Keys.PC_AWC_KEY);
|
Decrypt_RSXXTEA(data, GTA5Keys.PC_AWC_KEY);
|
||||||
Magic = BitConverter.ToUInt32(data, 0);
|
Magic = BitConverter.ToUInt32(data, 0);
|
||||||
|
WholeFileEncrypted = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -111,27 +118,13 @@ namespace CodeWalker.GameFiles
|
|||||||
InfoOffset = r.ReadInt32();
|
InfoOffset = r.ReadInt32();
|
||||||
|
|
||||||
|
|
||||||
//notes from libertyV:
|
|
||||||
// first bit - means that there are unknown word for each stream after this header
|
|
||||||
// second bit - I think that it means that not all the tags are in the start of the file, but all the tags of a stream are near the data tag
|
|
||||||
// third bit - Multi channel audio
|
|
||||||
|
|
||||||
if ((Flags >> 8) != 0xFF)
|
if ((Flags >> 8) != 0xFF)
|
||||||
{
|
{
|
||||||
ErrorMessage = "Flags 0 not supported!";
|
ErrorMessage = "Flags 0 not supported!";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ((Flags & 0xF8) != 0)
|
|
||||||
{
|
|
||||||
//ErrorMessage = "Flags 1 not supported!";
|
|
||||||
//return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MultiChannel = ((Flags & 4) == 4);
|
if (UnkUshortsFlag)
|
||||||
|
|
||||||
|
|
||||||
var flag0 = ((Flags & 1) == 1);
|
|
||||||
if (flag0)
|
|
||||||
{
|
{
|
||||||
UnkUshorts = new ushort[StreamCount]; //offsets of some sort?
|
UnkUshorts = new ushort[StreamCount]; //offsets of some sort?
|
||||||
for (int i = 0; i < StreamCount; i++)
|
for (int i = 0; i < StreamCount; i++)
|
||||||
@ -141,9 +134,9 @@ namespace CodeWalker.GameFiles
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var infoStart = 16 + (flag0 ? (StreamCount * 2) : 0);
|
var infoStart = 16 + (UnkUshortsFlag ? (StreamCount * 2) : 0);
|
||||||
if (ms.Position != infoStart)
|
if (ms.Position != infoStart)
|
||||||
{ }
|
{ }//no hit
|
||||||
ms.Position = infoStart;
|
ms.Position = infoStart;
|
||||||
|
|
||||||
|
|
||||||
@ -169,8 +162,9 @@ namespace CodeWalker.GameFiles
|
|||||||
|
|
||||||
StreamInfos = infos.ToArray();
|
StreamInfos = infos.ToArray();
|
||||||
|
|
||||||
//ReadAudiosOrig(infoDict, r);
|
|
||||||
ReadAudios(r);
|
ReadAudios(r);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,6 +173,7 @@ namespace CodeWalker.GameFiles
|
|||||||
|
|
||||||
List<uint> audioIds = new List<uint>();
|
List<uint> audioIds = new List<uint>();
|
||||||
List<AwcAudio> audios = new List<AwcAudio>();
|
List<AwcAudio> audios = new List<AwcAudio>();
|
||||||
|
AwcAudio multiaudio = null;
|
||||||
|
|
||||||
for (int i = 0; i < StreamCount; i++)
|
for (int i = 0; i < StreamCount; i++)
|
||||||
{
|
{
|
||||||
@ -211,17 +206,17 @@ namespace CodeWalker.GameFiles
|
|||||||
case 90: //unk5A
|
case 90: //unk5A
|
||||||
audio.UnkData5A = new AwcAudioUnk(r, chunk);
|
audio.UnkData5A = new AwcAudioUnk(r, chunk);
|
||||||
break;
|
break;
|
||||||
case 104://unk68
|
case 104://MIDI
|
||||||
audio.UnkData68 = new AwcAudioUnk(r, chunk);
|
audio.MIDIData = new AwcAudioMIDI(r, chunk);
|
||||||
break;
|
break;
|
||||||
case 217://unkD9 - length 200+
|
case 217://unkD9 - length 200+
|
||||||
audio.UnkDataD9 = new AwcAudioUnk(r, chunk);
|
audio.UnkDataD9 = new AwcAudioUnk(r, chunk);
|
||||||
break;
|
break;
|
||||||
case 72: //multi channel info
|
case 72: //multi channel info
|
||||||
audio.MultiChannelInfo = new AwcChannelChunkInfo(r);
|
audio.MultiChannelInfo = new AwcChannelBlockInfo(r);
|
||||||
break;
|
break;
|
||||||
case 163://multi unkA3
|
case 163://multi unkA3
|
||||||
audio.MultiChannelOffsets = new AwcChannelOffsetsInfo(r, chunk);
|
audio.MultiChannelSampleOffsets = new AwcChannelOffsetsInfo(r, chunk);
|
||||||
break;
|
break;
|
||||||
case 189://multi unkBD
|
case 189://multi unkBD
|
||||||
audio.UnkDataBD = new AwcAudioUnk(r, chunk);
|
audio.UnkDataBD = new AwcAudioUnk(r, chunk);
|
||||||
@ -233,75 +228,95 @@ namespace CodeWalker.GameFiles
|
|||||||
{ }//make sure everything was read!
|
{ }//make sure everything was read!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//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<AwcChannelDataBlock>();
|
||||||
|
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);
|
audios.Add(audio);
|
||||||
audioIds.Add(info.Id);
|
audioIds.Add(info.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (multiaudio != null)
|
||||||
|
{
|
||||||
|
var multichaninfos = multiaudio.MultiChannelInfo;
|
||||||
|
for (int i = 0; i < audios.Count; 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Audios = audios.ToArray();
|
Audios = audios.ToArray();
|
||||||
AudioIds = audioIds.ToArray();
|
AudioIds = audioIds.ToArray();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReadAudiosOrig(Dictionary<uint, AwcStreamInfo> infoDict, DataReader r)
|
|
||||||
{
|
|
||||||
|
|
||||||
//just leaving this here temporarily for reference about how libertyV parsed multichannel data
|
|
||||||
|
|
||||||
|
|
||||||
//r.Position = cdata.Offset;
|
|
||||||
//var lastPos = cdata.Offset + cdata.Size;
|
|
||||||
////int chunkSize = 0x800;
|
|
||||||
//uint bigChunkSize = chanInfo.ChunkSize;
|
|
||||||
//var chanCount = chanInfo.ChannelCount;
|
|
||||||
|
|
||||||
//MultiChannelData = r.ReadBytes(cdata.Size);
|
|
||||||
//r.Position = cdata.Offset;
|
|
||||||
|
|
||||||
////this doesn't seem to work :(
|
|
||||||
//while (ms.Position < lastPos)
|
|
||||||
//{
|
|
||||||
// uint totalChunks = 0;
|
|
||||||
// var startPos = ms.Position;
|
|
||||||
// var curPos = startPos;
|
|
||||||
// //byte[] chunkdata = r.ReadBytes(chunkSize);
|
|
||||||
// //ms.Position = startPos;
|
|
||||||
// AwcChannelChunkHeader[] chanHeaders = new AwcChannelChunkHeader[chanCount];
|
|
||||||
// for (int i = 0; i < chanCount; i++)
|
|
||||||
// {
|
|
||||||
// var chanHeader = new AwcChannelChunkHeader(r);
|
|
||||||
// chanHeaders[i] = chanHeader;
|
|
||||||
// totalChunks += chanHeader.ChunkCount;
|
|
||||||
// }
|
|
||||||
// int headerSize = (int)(totalChunks * 4 + chanInfo.ChannelCount * AwcChannelChunkHeader.Size);
|
|
||||||
// headerSize += (((-headerSize) % chunkSize) + chunkSize) % chunkSize; //todo: simplify this!
|
|
||||||
// curPos += headerSize;
|
|
||||||
// AwcChannelChunk[] chanChunks = new AwcChannelChunk[chanCount];
|
|
||||||
// for (int i = 0; i < chanCount; i++)
|
|
||||||
// {
|
|
||||||
// var chanChunk = new AwcChannelChunk(r, chanHeaders[i], chunkItems[i]);
|
|
||||||
// chanChunks[i] = chanChunk;
|
|
||||||
// curPos += chanChunk.TotalDataSize;
|
|
||||||
// }
|
|
||||||
// if (curPos - startPos > chanInfo.ChunkSize)
|
|
||||||
// {
|
|
||||||
// ErrorMessage = "Chunk was bigger than the chunk size";
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// if ((totalChunks == 0) || ((startPos + chanInfo.ChunkSize) > lastPos))
|
|
||||||
// {
|
|
||||||
// ErrorMessage = "Unable to read chunk";
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// var newPos = startPos + bigChunkSize;
|
|
||||||
// if (newPos >= lastPos) break;
|
|
||||||
// ms.Position = newPos;
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum AwcCodecFormat
|
||||||
|
{
|
||||||
|
PCM = 0,
|
||||||
|
ADPCM = 4
|
||||||
|
}
|
||||||
|
|
||||||
[TC(typeof(EXP))] public class AwcStreamInfo
|
[TC(typeof(EXP))] public class AwcStreamInfo
|
||||||
{
|
{
|
||||||
@ -346,25 +361,25 @@ namespace CodeWalker.GameFiles
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TC(typeof(EXP))] public class AwcChannelChunkInfo
|
[TC(typeof(EXP))] public class AwcChannelBlockInfo
|
||||||
{
|
{
|
||||||
public uint ChunkCount { get; set; }
|
public uint BlockCount { get; set; }
|
||||||
public uint ChunkSize { get; set; }
|
public uint BlockSize { get; set; }
|
||||||
public uint ChannelCount { get; set; }
|
public uint ChannelCount { get; set; }
|
||||||
public AwcChannelChunkItemInfo[] Channels { get; set; }
|
public AwcChannelBlockItemInfo[] Channels { get; set; }
|
||||||
|
|
||||||
public uint TotalSize { get { return ChunkCount * ChunkSize; } }
|
public uint TotalSize { get { return BlockCount * BlockSize; } }
|
||||||
|
|
||||||
public AwcChannelChunkInfo(DataReader r)
|
public AwcChannelBlockInfo(DataReader r)
|
||||||
{
|
{
|
||||||
ChunkCount = r.ReadUInt32();
|
BlockCount = r.ReadUInt32();
|
||||||
ChunkSize = r.ReadUInt32();
|
BlockSize = r.ReadUInt32();
|
||||||
ChannelCount = r.ReadUInt32();
|
ChannelCount = r.ReadUInt32();
|
||||||
|
|
||||||
List<AwcChannelChunkItemInfo> channels = new List<AwcChannelChunkItemInfo>();
|
List<AwcChannelBlockItemInfo> channels = new List<AwcChannelBlockItemInfo>();
|
||||||
for (int i = 0; i < ChannelCount; i++)
|
for (int i = 0; i < ChannelCount; i++)
|
||||||
{
|
{
|
||||||
var itemInfo = new AwcChannelChunkItemInfo(r);
|
var itemInfo = new AwcChannelBlockItemInfo(r);
|
||||||
channels.Add(itemInfo);
|
channels.Add(itemInfo);
|
||||||
}
|
}
|
||||||
Channels = channels.ToArray();
|
Channels = channels.ToArray();
|
||||||
@ -373,34 +388,107 @@ namespace CodeWalker.GameFiles
|
|||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return ChunkCount.ToString() + ": " + ChunkSize.ToString() + ", " + ChannelCount.ToString() + " channels";
|
return BlockCount.ToString() + ": " + BlockSize.ToString() + ", " + ChannelCount.ToString() + " channels";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TC(typeof(EXP))] public class AwcChannelChunkItemInfo
|
[TC(typeof(EXP))] public class AwcChannelBlockItemInfo
|
||||||
{
|
{
|
||||||
public uint Id { get; set; }
|
public uint Id { get; set; }
|
||||||
public uint Samples { get; set; }
|
public uint Samples { get; set; }
|
||||||
public ushort Unk0 { get; set; }
|
public ushort Unk0 { get; set; }//flags?
|
||||||
public ushort SamplesPerSecond { get; set; }
|
public ushort SamplesPerSecond { get; set; }
|
||||||
public byte Unk1 { get; set; }
|
public AwcCodecFormat Codec { get; set; }
|
||||||
public byte RoundSize { get; set; }
|
public byte RoundSize { get; set; }
|
||||||
public ushort Unk2 { get; set; }
|
public ushort Unk2 { get; set; }
|
||||||
|
|
||||||
public AwcChannelChunkItemInfo(DataReader r)
|
public AwcChannelBlockItemInfo(DataReader r)
|
||||||
{
|
{
|
||||||
Id = r.ReadUInt32();
|
Id = r.ReadUInt32();
|
||||||
Samples = r.ReadUInt32();
|
Samples = r.ReadUInt32();
|
||||||
Unk0 = r.ReadUInt16();
|
Unk0 = r.ReadUInt16();
|
||||||
SamplesPerSecond = r.ReadUInt16();
|
SamplesPerSecond = r.ReadUInt16();
|
||||||
Unk1 = r.ReadByte();
|
Codec = (AwcCodecFormat)r.ReadByte();
|
||||||
RoundSize = r.ReadByte();
|
RoundSize = r.ReadByte();
|
||||||
Unk2 = r.ReadUInt16();
|
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()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return Id.ToString() + ": " + Samples.ToString() + " samples, " + SamplesPerSecond.ToString() + " samples/sec, size: " + RoundSize.ToString();
|
return Id.ToString() + ", " + Codec.ToString() + ": " + Samples.ToString() + " samples, " + SamplesPerSecond.ToString() + " samples/sec, size: " + RoundSize.ToString() + " unk0: " + Unk0.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,12 +504,7 @@ namespace CodeWalker.GameFiles
|
|||||||
public byte PlayBegin { get; set; }
|
public byte PlayBegin { get; set; }
|
||||||
public uint UnkExtra { get; set; }
|
public uint UnkExtra { get; set; }
|
||||||
|
|
||||||
public enum CodecFormat
|
public AwcCodecFormat Codec { get; set; }
|
||||||
{
|
|
||||||
PCM = 0,
|
|
||||||
ADPCM = 4
|
|
||||||
}
|
|
||||||
public CodecFormat Codec { get; set; }
|
|
||||||
|
|
||||||
public AwcFormatChunk(DataReader r, AwcChunkInfo info)
|
public AwcFormatChunk(DataReader r, AwcChunkInfo info)
|
||||||
{
|
{
|
||||||
@ -433,7 +516,7 @@ namespace CodeWalker.GameFiles
|
|||||||
LoopEnd = r.ReadUInt16();
|
LoopEnd = r.ReadUInt16();
|
||||||
PlayEnd = r.ReadUInt16();
|
PlayEnd = r.ReadUInt16();
|
||||||
PlayBegin = r.ReadByte();
|
PlayBegin = r.ReadByte();
|
||||||
Codec = (CodecFormat)r.ReadByte();
|
Codec = (AwcCodecFormat)r.ReadByte();
|
||||||
|
|
||||||
switch (info.Size) //Apparently sometimes this struct is longer?
|
switch (info.Size) //Apparently sometimes this struct is longer?
|
||||||
{
|
{
|
||||||
@ -466,26 +549,31 @@ namespace CodeWalker.GameFiles
|
|||||||
public AwcAudioUnk UnkData2B { get; set; }
|
public AwcAudioUnk UnkData2B { get; set; }
|
||||||
public AwcAudioUnk UnkData36 { get; set; }
|
public AwcAudioUnk UnkData36 { get; set; }
|
||||||
public AwcAudioUnk UnkData5A { get; set; }
|
public AwcAudioUnk UnkData5A { get; set; }
|
||||||
public AwcAudioUnk UnkData68 { get; set; }
|
public AwcAudioMIDI MIDIData { get; set; }
|
||||||
public AwcAudioUnk UnkDataD9 { get; set; }
|
public AwcAudioUnk UnkDataD9 { get; set; }
|
||||||
public AwcChannelChunkInfo MultiChannelInfo { get; set; }
|
public AwcChannelBlockInfo MultiChannelInfo { get; set; }
|
||||||
public AwcChannelOffsetsInfo MultiChannelOffsets { get; set; }
|
public AwcChannelOffsetsInfo MultiChannelSampleOffsets { get; set; }
|
||||||
|
public AwcChannelDataBlock[] MultiChannelBlocks { get; set; }
|
||||||
public AwcAudioUnk UnkDataBD { get; set; }
|
public AwcAudioUnk UnkDataBD { get; set; }
|
||||||
|
|
||||||
|
public AwcAudio MultiChannelSource { get; set; }
|
||||||
|
public AwcChannelBlockItemInfo MultiChannelFormat { get; set; }
|
||||||
|
public int MultiChannelIndex { get; set; }
|
||||||
|
|
||||||
public short Channels = 1;
|
public short Channels = 1;
|
||||||
public short BitsPerSample = 16;
|
public short BitsPerSample = 16;
|
||||||
public int SamplesPerSecond
|
public int SamplesPerSecond
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return Format?.SamplesPerSecond ?? 0;
|
return Format?.SamplesPerSecond ?? MultiChannelFormat?.SamplesPerSecond ?? 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public int SampleCount
|
public int SampleCount
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return (int)(Format?.Samples ?? 0);
|
return (int)(Format?.Samples ?? MultiChannelFormat?.Samples ?? 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -501,24 +589,34 @@ namespace CodeWalker.GameFiles
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (Format == null) return "Unknown";
|
if (MIDIData != null)
|
||||||
|
|
||||||
string codec;
|
|
||||||
switch (Format.Codec)
|
|
||||||
{
|
{
|
||||||
case AwcFormatChunk.CodecFormat.PCM:
|
return "MIDI";
|
||||||
codec = "PCM";
|
}
|
||||||
break;
|
|
||||||
case AwcFormatChunk.CodecFormat.ADPCM:
|
var fc = AwcCodecFormat.PCM;
|
||||||
codec = "ADPCM";
|
var hz = 0;
|
||||||
|
if (Format != null)
|
||||||
|
{
|
||||||
|
fc = Format.Codec;
|
||||||
|
hz = Format.SamplesPerSecond;
|
||||||
|
}
|
||||||
|
if (MultiChannelFormat != null)
|
||||||
|
{
|
||||||
|
fc = MultiChannelFormat.Codec;
|
||||||
|
hz = MultiChannelFormat.SamplesPerSecond;
|
||||||
|
}
|
||||||
|
string codec = fc.ToString();
|
||||||
|
switch (fc)
|
||||||
|
{
|
||||||
|
case AwcCodecFormat.PCM:
|
||||||
|
case AwcCodecFormat.ADPCM:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
codec = "Unknown";
|
codec = "Unknown";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var hz = Format.SamplesPerSecond;
|
|
||||||
|
|
||||||
return codec + ((hz > 0) ? (", " + hz.ToString() + " Hz") : "");
|
return codec + ((hz > 0) ? (", " + hz.ToString() + " Hz") : "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -527,10 +625,11 @@ namespace CodeWalker.GameFiles
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return Format == null ? 0 : (float)Format.Samples / Format.SamplesPerSecond;
|
if (Format != null) return (float)Format.Samples / Format.SamplesPerSecond;
|
||||||
|
if (MultiChannelFormat != null) return (float)MultiChannelFormat.Samples / MultiChannelFormat.SamplesPerSecond;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string LengthStr
|
public string LengthStr
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@ -539,6 +638,39 @@ namespace CodeWalker.GameFiles
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int ByteLength
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (MIDIData?.Data != null)
|
||||||
|
{
|
||||||
|
return MIDIData.Data?.Length ?? 0;
|
||||||
|
}
|
||||||
|
if (Data != null)
|
||||||
|
{
|
||||||
|
return Data.Length;
|
||||||
|
}
|
||||||
|
if (MultiChannelSource != null)
|
||||||
|
{
|
||||||
|
int c = 0;
|
||||||
|
if (MultiChannelSource?.MultiChannelBlocks != null)
|
||||||
|
{
|
||||||
|
foreach (var blk in MultiChannelSource.MultiChannelBlocks)
|
||||||
|
{
|
||||||
|
if (MultiChannelIndex < (blk?.Channels?.Length ?? 0))
|
||||||
|
{
|
||||||
|
var chan = blk.Channels[MultiChannelIndex];
|
||||||
|
c += chan?.ChannelData?.Length ?? 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public AwcAudio()
|
public AwcAudio()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -553,8 +685,20 @@ namespace CodeWalker.GameFiles
|
|||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
var hash = (StreamInfo?.Id.ToString("X") ?? "0").PadLeft(8, '0');
|
var hash = "0x" + (StreamInfo?.Id.ToString("X") ?? "0").PadLeft(8, '0') + ": ";
|
||||||
return "0x" + hash + ": " + Format?.ToString() ?? "AwcAudio";
|
if (Format != null)
|
||||||
|
{
|
||||||
|
return hash + Format?.ToString() ?? "AwcAudio";
|
||||||
|
}
|
||||||
|
if (MultiChannelFormat != null)
|
||||||
|
{
|
||||||
|
return hash + MultiChannelFormat?.ToString() ?? "AwcAudio";
|
||||||
|
}
|
||||||
|
if (MIDIData != null)
|
||||||
|
{
|
||||||
|
return hash + MIDIData.ToString();
|
||||||
|
}
|
||||||
|
return hash + "Unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] DecodeADPCM(byte[] data, int sampleCount)
|
public byte[] DecodeADPCM(byte[] data, int sampleCount)
|
||||||
@ -635,27 +779,56 @@ namespace CodeWalker.GameFiles
|
|||||||
{
|
{
|
||||||
byte[] dataPCM = null;
|
byte[] dataPCM = null;
|
||||||
int bitsPerSample = BitsPerSample;
|
int bitsPerSample = BitsPerSample;
|
||||||
|
var channels = Channels;
|
||||||
|
|
||||||
switch (Format.Codec)
|
if (MultiChannelFormat != null)
|
||||||
{
|
{
|
||||||
case AwcFormatChunk.CodecFormat.PCM:
|
var ms = new MemoryStream();
|
||||||
dataPCM = Data;
|
var bw = new BinaryWriter(ms);
|
||||||
break;
|
if (MultiChannelSource?.MultiChannelBlocks != null)
|
||||||
case AwcFormatChunk.CodecFormat.ADPCM:
|
{
|
||||||
dataPCM = new byte[Data.Length];
|
foreach (var blk in MultiChannelSource.MultiChannelBlocks)
|
||||||
Buffer.BlockCopy(Data, 0, dataPCM, 0, Data.Length);
|
{
|
||||||
AwcFile.Decrypt_RSXXTEA(dataPCM, GTA5Keys.PC_AWC_KEY);
|
if (MultiChannelIndex < (blk?.Channels?.Length ?? 0))
|
||||||
|
{
|
||||||
|
var chan = blk.Channels[MultiChannelIndex];
|
||||||
|
var cdata = chan.ChannelData;
|
||||||
|
bw.Write(cdata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bw.Flush();
|
||||||
|
ms.Position = 0;
|
||||||
|
dataPCM = new byte[ms.Length];
|
||||||
|
ms.Read(dataPCM, 0, (int)ms.Length);
|
||||||
|
if (MultiChannelFormat.Codec == AwcCodecFormat.ADPCM)
|
||||||
|
{
|
||||||
dataPCM = DecodeADPCM(dataPCM, SampleCount);
|
dataPCM = DecodeADPCM(dataPCM, SampleCount);
|
||||||
bitsPerSample = 16;
|
}
|
||||||
break;
|
channels = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (Format.Codec)
|
||||||
|
{
|
||||||
|
case AwcCodecFormat.PCM:
|
||||||
|
dataPCM = Data;
|
||||||
|
break;
|
||||||
|
case AwcCodecFormat.ADPCM:
|
||||||
|
dataPCM = new byte[Data.Length];
|
||||||
|
Buffer.BlockCopy(Data, 0, dataPCM, 0, Data.Length);
|
||||||
|
dataPCM = DecodeADPCM(dataPCM, SampleCount);
|
||||||
|
bitsPerSample = 16;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int byteRate = SamplesPerSecond * Channels * bitsPerSample / 8;
|
int byteRate = SamplesPerSecond * channels * bitsPerSample / 8;
|
||||||
short blockAlign = (short)(Channels * bitsPerSample / 8);
|
short blockAlign = (short)(channels * bitsPerSample / 8);
|
||||||
|
|
||||||
MemoryStream stream = new MemoryStream();
|
MemoryStream stream = new MemoryStream();
|
||||||
BinaryWriter w = new BinaryWriter(stream);
|
BinaryWriter w = new BinaryWriter(stream);
|
||||||
int wavLength = 12 + 24 + 8 + Data.Length;
|
int wavLength = 12 + 24 + 8 + dataPCM.Length;
|
||||||
|
|
||||||
// RIFF chunk
|
// RIFF chunk
|
||||||
w.Write("RIFF".ToCharArray());
|
w.Write("RIFF".ToCharArray());
|
||||||
@ -666,7 +839,7 @@ namespace CodeWalker.GameFiles
|
|||||||
w.Write("fmt ".ToCharArray());
|
w.Write("fmt ".ToCharArray());
|
||||||
w.Write((int)16); // fmt size
|
w.Write((int)16); // fmt size
|
||||||
w.Write((short)1); // 1 = WAVE_FORMAT_PCM
|
w.Write((short)1); // 1 = WAVE_FORMAT_PCM
|
||||||
w.Write((short)Channels);
|
w.Write((short)channels);
|
||||||
w.Write((int)SamplesPerSecond);
|
w.Write((int)SamplesPerSecond);
|
||||||
w.Write((int)byteRate);
|
w.Write((int)byteRate);
|
||||||
w.Write((short)blockAlign);
|
w.Write((short)blockAlign);
|
||||||
@ -750,10 +923,28 @@ namespace CodeWalker.GameFiles
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[TC(typeof(EXP))] public class AwcAudioMIDI
|
||||||
|
{
|
||||||
|
public AwcChunkInfo ChunkInfo { get; set; }
|
||||||
|
public byte[] Data { get; set; }
|
||||||
|
|
||||||
|
public AwcAudioMIDI(DataReader r, AwcChunkInfo info)
|
||||||
|
{
|
||||||
|
ChunkInfo = info;
|
||||||
|
Data = r.ReadBytes(info.Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return "MIDI - " + (Data?.Length??0).ToString() + " bytes";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[TC(typeof(EXP))] public class AwcChannelOffsetsInfo
|
[TC(typeof(EXP))] public class AwcChannelOffsetsInfo
|
||||||
{
|
{
|
||||||
public AwcChunkInfo ChunkInfo { get; set; }
|
public AwcChunkInfo ChunkInfo { get; set; }
|
||||||
public uint[] Offsets { get; set; }
|
public uint[] SampleOffsets { get; set; }
|
||||||
|
|
||||||
public AwcChannelOffsetsInfo(DataReader r, AwcChunkInfo info)
|
public AwcChannelOffsetsInfo(DataReader r, AwcChunkInfo info)
|
||||||
{
|
{
|
||||||
@ -762,71 +953,119 @@ namespace CodeWalker.GameFiles
|
|||||||
var rem = info.Size % 4;
|
var rem = info.Size % 4;
|
||||||
if (rem != 0)
|
if (rem != 0)
|
||||||
{ }
|
{ }
|
||||||
Offsets = new uint[count];
|
SampleOffsets = new uint[count];
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
Offsets[i] = r.ReadUInt32();
|
SampleOffsets[i] = r.ReadUInt32();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return (Offsets?.Length ?? 0).ToString() + " items";
|
return (SampleOffsets?.Length ?? 0).ToString() + " items";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TC(typeof(EXP))] public class AwcChannelDataBlock
|
||||||
|
|
||||||
[TC(typeof(EXP))] public class AwcChannelChunkHeader
|
|
||||||
{
|
{
|
||||||
public static uint Size = 16; //24 for ps3...
|
|
||||||
|
|
||||||
public uint StartChunk { get; set; }
|
|
||||||
public uint ChunkCount { get; set; }
|
|
||||||
public uint SamplesToSkip { get; set; } //mostly 0
|
|
||||||
public uint SamplesPerChunk { get; set; }
|
|
||||||
public uint DataSize { get; set; }
|
|
||||||
|
|
||||||
public AwcChannelChunkHeader(DataReader r)
|
|
||||||
{
|
|
||||||
StartChunk = r.ReadUInt32();
|
|
||||||
ChunkCount = r.ReadUInt32();
|
|
||||||
SamplesToSkip = r.ReadUInt32();
|
|
||||||
SamplesPerChunk = r.ReadUInt32();
|
|
||||||
DataSize = ChunkCount * 0x800;
|
|
||||||
|
|
||||||
//for ps3, two extra ints:
|
|
||||||
//uint unk0 = r.ReadUint32();
|
|
||||||
//DataSize = r.ReadUint32();
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
[TC(typeof(EXP))] public class AwcChannelChunk
|
|
||||||
{
|
|
||||||
public AwcChannelChunkHeader Header { get; set; }
|
|
||||||
public AwcChannelChunkItemInfo Info { get; set; }
|
|
||||||
public byte[] Data { get; set; }
|
public byte[] Data { get; set; }
|
||||||
|
public int Index { get; set; }
|
||||||
|
public int SampleOffset { get; set; }
|
||||||
|
|
||||||
public uint TotalDataSize { get; set; }
|
public AwcChannelDataItem[] Channels { get; set; }
|
||||||
|
|
||||||
public AwcChannelChunk(DataReader r, AwcChannelChunkHeader h, AwcChannelChunkItemInfo i)
|
|
||||||
|
public AwcChannelDataBlock(byte[] data, AwcChannelBlockInfo channelInfo, Endianess endianess, int index, int sampleOffset)
|
||||||
{
|
{
|
||||||
Header = h;
|
Data = data;
|
||||||
Info = i;
|
Index = index;
|
||||||
|
SampleOffset = sampleOffset;
|
||||||
|
|
||||||
TotalDataSize = h.DataSize;
|
using (var ms = new MemoryStream(data))
|
||||||
|
|
||||||
var rs = i?.RoundSize ?? 0;
|
|
||||||
int ds = (int)h.DataSize;
|
|
||||||
if (rs != 0)
|
|
||||||
{
|
{
|
||||||
TotalDataSize = (uint)(TotalDataSize + (((-ds) % rs) + rs) % rs);
|
var r = new DataReader(ms, endianess);
|
||||||
|
|
||||||
|
var channels = channelInfo?.Channels;
|
||||||
|
var channelcount = channelInfo?.ChannelCount ?? 0;
|
||||||
|
|
||||||
|
var ilist = new List<AwcChannelDataItem>();
|
||||||
|
for (int i = 0; i < channelcount; i++)
|
||||||
|
{
|
||||||
|
var channel = new AwcChannelDataItem(r);
|
||||||
|
channel.ChannelInfo = ((channels != null) && (i < channels.Length)) ? channels[i] : null;
|
||||||
|
ilist.Add(channel);
|
||||||
|
}
|
||||||
|
Channels = ilist.ToArray();
|
||||||
|
|
||||||
|
foreach (var channel in Channels)
|
||||||
|
{
|
||||||
|
channel.ReadOffsets(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
var padc = (0x800 - (r.Position % 0x800)) % 0x800;
|
||||||
|
var padb = r.ReadBytes((int)padc);
|
||||||
|
|
||||||
|
foreach (var channel in Channels)
|
||||||
|
{
|
||||||
|
var bcnt = channel.OffsetCount * 2048;
|
||||||
|
channel.ChannelData = r.ReadBytes(bcnt);
|
||||||
|
}
|
||||||
|
if (r.Position != r.Length)
|
||||||
|
{ }//still more, just padding?
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return (Data?.Length ?? 0).ToString() + " bytes";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TC(typeof(EXP))] public class AwcChannelDataItem
|
||||||
|
{
|
||||||
|
public int Unk0 { get; set; }
|
||||||
|
public int OffsetCount { get; set; }
|
||||||
|
public int Unk2 { get; set; }
|
||||||
|
public int SampleCount { get; set; }
|
||||||
|
public int Unk4 { get; set; }
|
||||||
|
public int Unk5 { get; set; }
|
||||||
|
|
||||||
|
public int[] SampleOffsets { get; set; }
|
||||||
|
|
||||||
|
public AwcChannelBlockItemInfo ChannelInfo { get; set; } //for convenience
|
||||||
|
|
||||||
|
public byte[] ChannelData { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public AwcChannelDataItem(DataReader r)
|
||||||
|
{
|
||||||
|
Unk0 = r.ReadInt32();
|
||||||
|
OffsetCount = r.ReadInt32();
|
||||||
|
Unk2 = r.ReadInt32();
|
||||||
|
SampleCount = r.ReadInt32();
|
||||||
|
Unk4 = r.ReadInt32();
|
||||||
|
Unk5 = r.ReadInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReadOffsets(DataReader r)
|
||||||
|
{
|
||||||
|
var olist = new List<int>();
|
||||||
|
for (int i = 0; i < OffsetCount; i++)
|
||||||
|
{
|
||||||
|
var v = r.ReadInt32();
|
||||||
|
olist.Add(v);
|
||||||
|
}
|
||||||
|
SampleOffsets = olist.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Unk0.ToString() + ", " + OffsetCount.ToString() + ", " + Unk2.ToString() + ", " + SampleCount.ToString() + ", " + Unk4.ToString() + ", " + Unk5.ToString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2844,8 +2844,8 @@ namespace CodeWalker.GameFiles
|
|||||||
{
|
{
|
||||||
foreach (RpfEntry entry in file.AllEntries)
|
foreach (RpfEntry entry in file.AllEntries)
|
||||||
{
|
{
|
||||||
try
|
//try
|
||||||
{
|
//{
|
||||||
var n = entry.NameLower;
|
var n = entry.NameLower;
|
||||||
if (n.EndsWith(".awc"))
|
if (n.EndsWith(".awc"))
|
||||||
{
|
{
|
||||||
@ -2854,11 +2854,11 @@ namespace CodeWalker.GameFiles
|
|||||||
if (awcfile != null)
|
if (awcfile != null)
|
||||||
{ }
|
{ }
|
||||||
}
|
}
|
||||||
}
|
//}
|
||||||
catch (Exception ex)
|
//catch (Exception ex)
|
||||||
{
|
//{
|
||||||
UpdateStatus("Error! " + ex.ToString());
|
// UpdateStatus("Error! " + ex.ToString());
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
19
Forms/AwcForm.Designer.cs
generated
19
Forms/AwcForm.Designer.cs
generated
@ -32,7 +32,6 @@
|
|||||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AwcForm));
|
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AwcForm));
|
||||||
this.MainTabControl = new System.Windows.Forms.TabControl();
|
this.MainTabControl = new System.Windows.Forms.TabControl();
|
||||||
this.PlayerTabPage = new System.Windows.Forms.TabPage();
|
this.PlayerTabPage = new System.Windows.Forms.TabPage();
|
||||||
this.label1 = new System.Windows.Forms.Label();
|
|
||||||
this.LabelInfo = new System.Windows.Forms.Label();
|
this.LabelInfo = new System.Windows.Forms.Label();
|
||||||
this.LabelTime = new System.Windows.Forms.Label();
|
this.LabelTime = new System.Windows.Forms.Label();
|
||||||
this.StopButton = new System.Windows.Forms.Button();
|
this.StopButton = new System.Windows.Forms.Button();
|
||||||
@ -75,7 +74,6 @@
|
|||||||
//
|
//
|
||||||
// PlayerTabPage
|
// PlayerTabPage
|
||||||
//
|
//
|
||||||
this.PlayerTabPage.Controls.Add(this.label1);
|
|
||||||
this.PlayerTabPage.Controls.Add(this.LabelInfo);
|
this.PlayerTabPage.Controls.Add(this.LabelInfo);
|
||||||
this.PlayerTabPage.Controls.Add(this.LabelTime);
|
this.PlayerTabPage.Controls.Add(this.LabelTime);
|
||||||
this.PlayerTabPage.Controls.Add(this.StopButton);
|
this.PlayerTabPage.Controls.Add(this.StopButton);
|
||||||
@ -95,16 +93,6 @@
|
|||||||
this.PlayerTabPage.Text = "Player";
|
this.PlayerTabPage.Text = "Player";
|
||||||
this.PlayerTabPage.UseVisualStyleBackColor = true;
|
this.PlayerTabPage.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// label1
|
|
||||||
//
|
|
||||||
this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
|
||||||
this.label1.AutoSize = true;
|
|
||||||
this.label1.Location = new System.Drawing.Point(244, 247);
|
|
||||||
this.label1.Name = "label1";
|
|
||||||
this.label1.Size = new System.Drawing.Size(316, 13);
|
|
||||||
this.label1.TabIndex = 13;
|
|
||||||
this.label1.Text = "WARNING: Work in progress! Some audio may not play correctly!";
|
|
||||||
//
|
|
||||||
// LabelInfo
|
// LabelInfo
|
||||||
//
|
//
|
||||||
this.LabelInfo.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
this.LabelInfo.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||||
@ -133,6 +121,7 @@
|
|||||||
this.StopButton.TabIndex = 10;
|
this.StopButton.TabIndex = 10;
|
||||||
this.StopButton.Text = "◼";
|
this.StopButton.Text = "◼";
|
||||||
this.StopButton.UseVisualStyleBackColor = true;
|
this.StopButton.UseVisualStyleBackColor = true;
|
||||||
|
this.StopButton.Click += new System.EventHandler(this.StopButton_Click);
|
||||||
//
|
//
|
||||||
// VolumeLabel
|
// VolumeLabel
|
||||||
//
|
//
|
||||||
@ -208,6 +197,7 @@
|
|||||||
this.PlayListView.TabIndex = 0;
|
this.PlayListView.TabIndex = 0;
|
||||||
this.PlayListView.UseCompatibleStateImageBehavior = false;
|
this.PlayListView.UseCompatibleStateImageBehavior = false;
|
||||||
this.PlayListView.View = System.Windows.Forms.View.Details;
|
this.PlayListView.View = System.Windows.Forms.View.Details;
|
||||||
|
this.PlayListView.SelectedIndexChanged += new System.EventHandler(this.PlayListView_SelectedIndexChanged);
|
||||||
this.PlayListView.DoubleClick += new System.EventHandler(this.PlayListView_DoubleClick);
|
this.PlayListView.DoubleClick += new System.EventHandler(this.PlayListView_DoubleClick);
|
||||||
//
|
//
|
||||||
// PlaylistNameHeader
|
// PlaylistNameHeader
|
||||||
@ -237,12 +227,12 @@
|
|||||||
this.contextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
this.contextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
this.ExportAsWav});
|
this.ExportAsWav});
|
||||||
this.contextMenuStrip.Name = "contextMenuStrip1";
|
this.contextMenuStrip.Name = "contextMenuStrip1";
|
||||||
this.contextMenuStrip.Size = new System.Drawing.Size(149, 26);
|
this.contextMenuStrip.Size = new System.Drawing.Size(150, 26);
|
||||||
//
|
//
|
||||||
// ExportAsWav
|
// ExportAsWav
|
||||||
//
|
//
|
||||||
this.ExportAsWav.Name = "ExportAsWav";
|
this.ExportAsWav.Name = "ExportAsWav";
|
||||||
this.ExportAsWav.Size = new System.Drawing.Size(148, 22);
|
this.ExportAsWav.Size = new System.Drawing.Size(149, 22);
|
||||||
this.ExportAsWav.Text = "Export as .wav";
|
this.ExportAsWav.Text = "Export as .wav";
|
||||||
this.ExportAsWav.Click += new System.EventHandler(this.ExportAsWav_Click);
|
this.ExportAsWav.Click += new System.EventHandler(this.ExportAsWav_Click);
|
||||||
//
|
//
|
||||||
@ -352,6 +342,5 @@
|
|||||||
private System.Windows.Forms.ToolStripMenuItem ExportAsWav;
|
private System.Windows.Forms.ToolStripMenuItem ExportAsWav;
|
||||||
private System.Windows.Forms.SaveFileDialog saveFileDialog;
|
private System.Windows.Forms.SaveFileDialog saveFileDialog;
|
||||||
private System.Windows.Forms.ColumnHeader PlaylistSizeHeader;
|
private System.Windows.Forms.ColumnHeader PlaylistSizeHeader;
|
||||||
private System.Windows.Forms.Label label1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,7 +16,7 @@ namespace CodeWalker.Forms
|
|||||||
private XAudio2 xAudio2;
|
private XAudio2 xAudio2;
|
||||||
private MasteringVoice masteringVoice;
|
private MasteringVoice masteringVoice;
|
||||||
private AudioBuffer audioBuffer;
|
private AudioBuffer audioBuffer;
|
||||||
private SourceVoice sourceVoice;
|
private SourceVoice sourceVoice;
|
||||||
|
|
||||||
private string fileName;
|
private string fileName;
|
||||||
public string FileName
|
public string FileName
|
||||||
@ -60,7 +60,7 @@ namespace CodeWalker.Forms
|
|||||||
{
|
{
|
||||||
fileName = awc?.FileEntry?.Name;
|
fileName = awc?.FileEntry?.Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayListView.Items.Clear();
|
PlayListView.Items.Clear();
|
||||||
|
|
||||||
float totalLength = 0;
|
float totalLength = 0;
|
||||||
@ -68,16 +68,17 @@ namespace CodeWalker.Forms
|
|||||||
{
|
{
|
||||||
foreach (var audio in awc.Audios)
|
foreach (var audio in awc.Audios)
|
||||||
{
|
{
|
||||||
|
if (audio.MultiChannelBlocks != null) continue;//don't display multichannel source audios
|
||||||
var item = PlayListView.Items.Add(audio.Name);
|
var item = PlayListView.Items.Add(audio.Name);
|
||||||
item.SubItems.Add(audio.Type);
|
item.SubItems.Add(audio.Type);
|
||||||
item.SubItems.Add(audio.LengthStr);
|
item.SubItems.Add(audio.LengthStr);
|
||||||
item.SubItems.Add(TextUtil.GetBytesReadable(audio.Data?.Length ?? 0));
|
item.SubItems.Add(TextUtil.GetBytesReadable(audio.ByteLength));
|
||||||
item.Tag = audio;
|
item.Tag = audio;
|
||||||
totalLength += audio.Length;
|
totalLength += audio.Length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LabelInfo.Text = awc.Audios.Length.ToString() + " track(s), Length: " + TimeSpan.FromSeconds((float)totalLength).ToString("m\\:ss");
|
LabelInfo.Text = awc.Audios.Length.ToString() + " track(s), Length: " + TimeSpan.FromSeconds((float)totalLength).ToString("h\\:mm\\:ss");
|
||||||
UpdateFormTitle();
|
UpdateFormTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +120,7 @@ namespace CodeWalker.Forms
|
|||||||
LabelTime.Visible = false;
|
LabelTime.Visible = false;
|
||||||
StopButton.Enabled = true;
|
StopButton.Enabled = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
playerState = newState;
|
playerState = newState;
|
||||||
UpdateUI();
|
UpdateUI();
|
||||||
@ -129,7 +130,7 @@ namespace CodeWalker.Forms
|
|||||||
private void InitializeAudio(AwcAudio audio, float playBegin = 0)
|
private void InitializeAudio(AwcAudio audio, float playBegin = 0)
|
||||||
{
|
{
|
||||||
currentAudio = audio;
|
currentAudio = audio;
|
||||||
trackLength = audio.Length;
|
trackLength = audio.Length;
|
||||||
|
|
||||||
if (xAudio2 == null)
|
if (xAudio2 == null)
|
||||||
{
|
{
|
||||||
@ -160,7 +161,7 @@ namespace CodeWalker.Forms
|
|||||||
wavStream.Close();
|
wavStream.Close();
|
||||||
|
|
||||||
trackFinished = false;
|
trackFinished = false;
|
||||||
sourceVoice = new SourceVoice(xAudio2, soundStream.Format, true);
|
sourceVoice = new SourceVoice(xAudio2, soundStream.Format, true);
|
||||||
sourceVoice.SubmitSourceBuffer(audioBuffer, soundStream.DecodedPacketsInfo);
|
sourceVoice.SubmitSourceBuffer(audioBuffer, soundStream.DecodedPacketsInfo);
|
||||||
sourceVoice.BufferEnd += (context) => trackFinished = true;
|
sourceVoice.BufferEnd += (context) => trackFinished = true;
|
||||||
sourceVoice.SetVolume((float)VolumeTrackBar.Value / 100);
|
sourceVoice.SetVolume((float)VolumeTrackBar.Value / 100);
|
||||||
@ -175,12 +176,16 @@ namespace CodeWalker.Forms
|
|||||||
var item = PlayListView.SelectedItems[0];
|
var item = PlayListView.SelectedItems[0];
|
||||||
var audio = item.Tag as AwcAudio;
|
var audio = item.Tag as AwcAudio;
|
||||||
|
|
||||||
if (audio != null)
|
if ((audio?.Format != null) || (audio?.MultiChannelFormat != null))
|
||||||
{
|
{
|
||||||
InitializeAudio(audio);
|
InitializeAudio(audio);
|
||||||
sourceVoice.Start();
|
sourceVoice.Start();
|
||||||
SetPlayerState(PlayerState.Playing);
|
SetPlayerState(PlayerState.Playing);
|
||||||
}
|
}
|
||||||
|
else if (audio.MIDIData != null)
|
||||||
|
{
|
||||||
|
//todo: play MIDI?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,7 +222,7 @@ namespace CodeWalker.Forms
|
|||||||
private void Pause()
|
private void Pause()
|
||||||
{
|
{
|
||||||
if (playerState == PlayerState.Playing)
|
if (playerState == PlayerState.Playing)
|
||||||
{
|
{
|
||||||
sourceVoice.Stop();
|
sourceVoice.Stop();
|
||||||
SetPlayerState(PlayerState.Paused);
|
SetPlayerState(PlayerState.Paused);
|
||||||
}
|
}
|
||||||
@ -256,6 +261,11 @@ namespace CodeWalker.Forms
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void StopButton_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
Stop();
|
||||||
|
}
|
||||||
|
|
||||||
private void PrevButton_Click(object sender, EventArgs e)
|
private void PrevButton_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
PlayPrevious();
|
PlayPrevious();
|
||||||
@ -327,16 +337,45 @@ namespace CodeWalker.Forms
|
|||||||
var item = PlayListView.SelectedItems[0];
|
var item = PlayListView.SelectedItems[0];
|
||||||
var audio = item.Tag as AwcAudio;
|
var audio = item.Tag as AwcAudio;
|
||||||
|
|
||||||
saveFileDialog.FileName = audio.Name + ".wav";
|
var ext = ".wav";
|
||||||
|
if (audio?.MIDIData != null)
|
||||||
|
{
|
||||||
|
ext = ".midi";
|
||||||
|
}
|
||||||
|
|
||||||
|
saveFileDialog.FileName = audio.Name + ext;
|
||||||
if (saveFileDialog.ShowDialog() == DialogResult.OK)
|
if (saveFileDialog.ShowDialog() == DialogResult.OK)
|
||||||
{
|
{
|
||||||
Stream wavStream = audio.GetWavStream();
|
if (audio?.MIDIData != null)
|
||||||
FileStream stream = File.Create(saveFileDialog.FileName);
|
{
|
||||||
wavStream.CopyTo(stream);
|
File.WriteAllBytes(saveFileDialog.FileName, audio.MIDIData.Data);
|
||||||
stream.Close();
|
}
|
||||||
wavStream.Close();
|
else if ((audio?.Format != null) || (audio?.MultiChannelFormat != null))
|
||||||
|
{
|
||||||
|
Stream wavStream = audio.GetWavStream();
|
||||||
|
FileStream stream = File.Create(saveFileDialog.FileName);
|
||||||
|
wavStream.CopyTo(stream);
|
||||||
|
stream.Close();
|
||||||
|
wavStream.Close();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void PlayListView_SelectedIndexChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
ExportAsWav.Text = "Export as .wav";
|
||||||
|
if (PlayListView.SelectedItems.Count == 1)
|
||||||
|
{
|
||||||
|
var item = PlayListView.SelectedItems[0];
|
||||||
|
var audio = item.Tag as AwcAudio;
|
||||||
|
if (audio?.MIDIData != null)
|
||||||
|
{
|
||||||
|
ExportAsWav.Text = "Export as .midi";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user