public AWCFile(Stream stream) { this.Stream = stream; Structs.AWCHeader header = new Structs.AWCHeader(stream); if ((header.Flags >> 8) != 0xFF) { throw new Exception("Unsupported flag"); } // 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 ((header.Flags & 0xF8) != 0) { throw new Exception("Unsupported flag"); } MultiChannel = ((header.Flags & 4) == 4); // 0x10 - Header size int audioInfoStart = 0x10 + ((header.Flags & 1) == 1 ? (2 * header.StreamsCount) : 0); stream.Seek(audioInfoStart, SeekOrigin.Begin); List <Structs.StreamInfo> info = new List <Structs.StreamInfo>(); Dictionary <int, Dictionary <byte, Structs.ChunkInfo> > streamsChunks = new Dictionary <int, Dictionary <byte, Structs.ChunkInfo> >(); for (int i = 0; i < header.StreamsCount; ++i) { info.Add(new Structs.StreamInfo(Stream, header.BigEndian)); } for (int i = 0; i < header.StreamsCount; ++i) { streamsChunks[info[i].Id] = new Dictionary <byte, Structs.ChunkInfo>(); for (int j = 0; j < info[i].TagsCount; ++j) { Structs.ChunkInfo chunk = new Structs.ChunkInfo(stream, header.BigEndian); streamsChunks[info[i].Id][chunk.Tag] = chunk; } } if (MultiChannel) { List <Structs.ChannelsInfoChunkItem> streamsInfo = new List <Structs.ChannelsInfoChunkItem>(); Structs.ChannelsInfoChunkHeader channelsInfoHeader; // Haven't figured out that hash yet // stream with id 0 is just info on the other channels using (Stream chunkReader = new ChunkStream(this.Stream, streamsChunks[0][Tag(0x81F95048)])) { channelsInfoHeader = new Structs.ChannelsInfoChunkHeader(chunkReader, header.BigEndian); if (channelsInfoHeader.ChannelsCount != header.StreamsCount - 1) { throw new Exception("Unexcepted number of channels"); } for (int i = 0; i < channelsInfoHeader.ChannelsCount; ++i) { streamsInfo.Add(new Structs.ChannelsInfoChunkItem(chunkReader, header.BigEndian)); AudioIds.Add(info[i + 1].Id); } } AudioStreams.Add(new MultiChannelAudio(new ChunkStream(this.Stream, streamsChunks[0][Tag("data")]), channelsInfoHeader, streamsInfo, header.BigEndian)); } else { List <Structs.FormatChunk> streamsInfo = new List <Structs.FormatChunk>(); for (int i = 0; i < header.StreamsCount; ++i) { using (Stream chunkReader = new ChunkStream(this.Stream, streamsChunks[info[i].Id][Tag("format")])) { AudioStreams.Add(new Audio(new ChunkStream(this.Stream, streamsChunks[info[i].Id][Tag("data")]), new Structs.FormatChunk(chunkReader, header.BigEndian))); AudioIds.Add(info[i].Id); } } } // note: there is fourth unknown 0x21E86A3 tag }
public MultiChannelAudio(Stream data, Structs.ChannelsInfoChunkHeader channelsInfoHeader, List <Structs.ChannelsInfoChunkItem> channelsInfo, bool bigEndian) { int chunkSize = 0x800; List <Stream>[] channelsStreams = new List <Stream> [channelsInfoHeader.ChannelsCount]; List <Tuple <int, int> >[] samples = new List <Tuple <int, int> > [channelsInfoHeader.ChannelsCount]; for (int i = 0; i < channelsInfoHeader.ChannelsCount; ++i) { channelsStreams[i] = new List <Stream>(); samples[i] = new List <Tuple <int, int> >(); } while (data.Position != data.Length) { int totalChunks = 0; long startPos = data.Position; long pos = startPos; int[] dataSizes = new int[channelsInfoHeader.ChannelsCount]; int[] firstNewData = new int[channelsInfoHeader.ChannelsCount]; for (int i = 0; i < channelsInfoHeader.ChannelsCount; ++i) { Structs.ChannelChunkHeader header = new Structs.ChannelChunkHeader(data, bigEndian); dataSizes[i] = header.DataSize; totalChunks += header.Chunks; samples[i].Add(Tuple.Create(header.SamplesSkip, (int)header.Samples - header.SamplesSkip)); } int headerSize = totalChunks * 4 + channelsInfoHeader.ChannelsCount * Structs.ChannelChunkHeader.Size; headerSize += (((-headerSize) % chunkSize) + chunkSize) % chunkSize; pos += headerSize; for (int i = 0; i < channelsInfoHeader.ChannelsCount; ++i) { channelsStreams[i].Add(new PartialStream(data, pos, dataSizes[i])); if (channelsInfo[i].RoundSize != 0) { dataSizes[i] += (((-dataSizes[i]) % channelsInfo[i].RoundSize) + channelsInfo[i].RoundSize) % channelsInfo[i].RoundSize; } pos += dataSizes[i]; } if (pos - startPos > channelsInfoHeader.BigChunkSize) { throw new Exception("Chunks too big"); } if (totalChunks == 0 || startPos + channelsInfoHeader.BigChunkSize > data.Length) { throw new Exception("Unexpected value"); } // After each chunk, there is header's size zeros block data.Seek(startPos + channelsInfoHeader.BigChunkSize, SeekOrigin.Begin); } for (int i = 0; i < channelsInfoHeader.ChannelsCount; ++i) { Channels.Add(new SplittedAudio(channelsStreams[i], samples[i], channelsInfo[i].Samples, channelsInfo[i].SamplesPerSecond)); } // following the headers, there is a seek table: For each chunk, there is the dword - which is the first sample in the block }