private void ParseBank(EndianReader ms, bool isEmbedded) { int numEntriesWithData = 1; //long dataStartPosition = ms.Position; //string shouldBeRiff = ms.ReadString(4, false); //if (shouldBeRiff != "RIFF") //{ // Debug.WriteLine("Not a RIFF!"); //} //uint riffSize = ms.ReadUInt32(); //var riffType = ms.ReadString(8, false); //technically not type, its just how this file format works //ISBankEntry isbEntry = null; //uint blocksize = 0; //int currentCounter = counter; //bool endOfFile = false; //if (isEmbedded && riffType != "isbftitl") //{ // //its an icbftitl, which never has data. // ms.Seek(riffSize - 8, SeekOrigin.Current); //skip it //} //else //{ // //get full data // var pos = ms.Position; // ms.Position -= 8; //go back 8 // var fulldata = new byte[riffSize + 8]; // ms.Read(fulldata, 0, fulldata.Length); // ms.Position = pos; //reset // isbEntry = new ISBankEntry(); //start of a new file // isbEntry.FullData = fulldata; // blocksize = ms.ReadUInt32(); //size of isfbtitl chunk // ms.Seek(blocksize, SeekOrigin.Current); //skip it //} ////todo change to this //// while AudioFile.Position <> BundleReader.OffsetsArray[FileNo] + BundleReader.FileSizesArray[FileNo] do uint chunksize = 0; ISBankEntry isbEntry = null; while (ms.BaseStream.Position < ms.BaseStream.Length) { chunksize = 0; //reset var chunkStartPos = ms.BaseStream.Position; string blockName = ms.ReadEndianASCIIString(4); //Debug.WriteLine(blockName + " at " + (ms.Position - 4).ToString("X8")); switch (blockName) { case "LIST": chunksize = ms.ReadUInt32(); var nextblockname = ms.ReadEndianASCIIString(4); var nextblockname2 = ms.ReadEndianASCIIString(4); if (nextblockname == "samp" && nextblockname2 == "titl") { if (!isEmbedded) { //upcoming sample data //add old ISB entry if (isbEntry?.DataAsStored != null) { BankEntries.Add(isbEntry); } isbEntry = new ISBankEntry(); } } else { //maybe isb container, ignore ms.BaseStream.Position = chunksize + 8 + chunkStartPos; //Debug.WriteLine($"Skipping non-sample LIST at 0x{chunkStartPos:X8}"); continue; } chunksize = ms.ReadUInt32(); //size of block string tempStr = ""; //we have to build it manually because of how they chose to store it in a weird non-ASCII/unicode way bool endOfStr = false; for (int i = 0; i < chunksize / 2; i++) { short value = ms.ReadInt16(); if (value != 0 && !endOfStr) { tempStr += (char)value; } else { //used to skip the rest of the block endOfStr = true; } } isbEntry.FileName = tempStr; break; case "sinf": chunksize = ms.ReadUInt32(); var pos = ms.BaseStream.Position; ms.ReadInt64(); //skip 8 isbEntry.sampleRate = ms.ReadUInt32(); // This is actually codec dependent. e.g. this can list 48K but actual samplerate is 44.1K isbEntry.pcmBytes = ms.ReadUInt32(); isbEntry.bps = ms.ReadInt16(); ms.BaseStream.Position = pos + chunksize; //skip to next chunk break; case "chnk": ms.Seek(4, SeekOrigin.Current); isbEntry.numberOfChannels = ms.ReadUInt32(); break; case "cmpi": //Codec/compression index var size = ms.ReadInt32(); pos = ms.BaseStream.Position; isbEntry.CodecID = ms.ReadInt32(); isbEntry.CodecID2 = ms.ReadInt32(); ms.BaseStream.Position = pos + size; break; case "data": numEntriesWithData++; chunksize = ms.ReadUInt32(); //size of block isbEntry.DataOffset = (uint)ms.BaseStream.Position; MemoryStream data = new MemoryStream(); ms.BaseStream.CopyToEx(data, (int)chunksize); data.Position = 0; var str = data.ReadStringASCII(4); isbEntry.DataAsStored = data.ToArray(); break; case "FFIR": case "RIFF": if (blockName == "FFIR") { ms.Endian = Endian.Big; } if (isEmbedded) { //EMBEDDED ISB //this is the start of a new file. var riffSize = ms.ReadUInt32(); //size of isfbtitl chunk var riffType = ms.ReadEndianASCIIString(4); //type of ISB riff var riffType2 = ms.ReadEndianASCIIString(4); //type of ISB riff if (riffType != "isbf" && riffType2 == "titl") { //its an icbftitl, which never has data. ms.Seek(riffSize - 8, SeekOrigin.Current); //skip it continue; //skip } //add old ISB entry if (isbEntry?.DataAsStored != null) { BankEntries.Add(isbEntry); } isbEntry = new ISBankEntry { FileEndianness = ms.Endian, FullData = new byte[riffSize + 8] }; pos = ms.BaseStream.Position; ms.BaseStream.Position = ms.BaseStream.Position - 16; ms.Read(isbEntry.FullData, 0, (int)riffSize + 8); ms.BaseStream.Position = pos; chunksize = ms.ReadUInt32(); //size of isfbtitl chunk ms.Seek(chunksize, SeekOrigin.Current); //skip it } else { //ISB file - has external RIFF header and samptitl's separating each data section var riffSize = ms.ReadUInt32(); //size of isfbtitl chunk var riffType = ms.ReadEndianASCIIString(4); //type of ISB riff var riffType2 = ms.ReadEndianASCIIString(4); //type of ISB riff if (riffType != "isbf" && riffType2 != "titl") { //its an icbftitl, which never has data, or is not ISB ms.Seek(riffSize - 8, SeekOrigin.Current); //skip it continue; //skip } //can't do += here it seems ms.Seek(ms.ReadInt32(), SeekOrigin.Current); //skip this title section //we will continue to parse through ISB header until we find a LIST object for sample data } break; default: //skip the block chunksize = ms.ReadUInt32(); //size of block ///sDebug.WriteLine($"Skipping block {blockName} of size {chunksize} at 0x{ms.Position:X8}"); ms.Seek(chunksize, SeekOrigin.Current); //skip it break; } if (chunksize % 2 != 0) { ms.Seek(1, SeekOrigin.Current); //byte align } } if (isbEntry?.DataAsStored != null) { BankEntries.Add(isbEntry); } }
private void ParseBank(MemoryStream ms, bool isEmbedded) { int counter = 1; //Skip RIFF, isbftitl long dataStartPosition = ms.Position; string shouldBeRiff = ms.ReadString(4, false); if (shouldBeRiff != "RIFF") { Debug.WriteLine("Not a RIFF!"); } uint riffSize = ms.ReadUInt32(); var riffType = ms.ReadString(8, false); //technically not type, its just how this file format works ISBankEntry isbEntry = null; uint blocksize = 0; int currentCounter = counter; bool endOfFile = false; if (isEmbedded && riffType != "isbftitl") { //its an icbftitl, which never has data. ms.Seek(riffSize - 8, SeekOrigin.Current); //skip it } else { blocksize = ms.ReadUInt32(); //size of isfbtitl chunk ms.Seek(blocksize, SeekOrigin.Current); //skip it isbEntry = new ISBankEntry(); //start of a new file isbEntry.HeaderOffset = (uint)ms.Position; } //todo change to this // while AudioFile.Position <> BundleReader.OffsetsArray[FileNo] + BundleReader.FileSizesArray[FileNo] do while (ms.Position < ms.Length && !endOfFile) { blocksize = 0; //reset if (currentCounter != counter) { //Debug.WriteLine("Sound #" + currentCounter); //Debug.WriteLine(currentFileName); //Debug.WriteLine("Sample Rate: " + sampleRate); //Debug.WriteLine("Channels: " + pcChannels); //Debug.WriteLine("Is Ogg: " + isOgg); //Debug.WriteLine("Is PCM: " + isPCM); if (isbEntry != null) { BankEntries.Add(isbEntry); } //Debug.WriteLine(isbEntry.GetTextSummary()); //Debug.WriteLine("======================="); isbEntry = new ISBankEntry(); isbEntry.HeaderOffset = (uint)ms.Position; currentCounter = counter; } string blockName = ms.ReadString(4); //Debug.WriteLine(blockName + " at " + (ms.Position - 4).ToString("X8")); switch (blockName) { case "LIST": /* * AudioFile.Seek(4, sofromcurrent); //list block size * AudioFile.Seek(8, sofromcurrent); //Seek past ''isbftitl'' bytes * blocksize:=Audiofile.ReadDWord; * * TempString:=''; * //filename:=''; * for I := 0 to blocksize - 1 do * begin * TempString:=chr(Audiofile.ReadByte); * if TempString=#0 then * else * //filename:=filename + TempString; * end; * continue;*/ ms.Seek(4, SeekOrigin.Current); //list block size ms.Seek(8, SeekOrigin.Current); //Seek past ''isbftitl'' bytes blocksize = ms.ReadUInt32(); //size of block string tempStr = ""; //we have to build it manually because of how they chose to store it in a weird non-ASCII/unicode way bool endOfStr = false; for (int i = 0; i < blocksize / 2; i++) { short value = ms.ReadInt16(); if (value != 0 && !endOfStr) { tempStr += (char)value; } else { //used to skip the rest of the block endOfStr = true; } } //string str = ms.ReadString(blocksize); isbEntry.FileName = tempStr; break; case "sinf": /* if blockname='sinf' then * begin * AudioFile.Seek(12, sofromcurrent); * samplerate:=Audiofile.ReadDWord; * AudioFile.Seek(8, sofromcurrent); * continue; * end;*/ ms.Seek(12, SeekOrigin.Current); isbEntry.sampleRate = ms.ReadUInt32(); ms.Seek(8, SeekOrigin.Current); break; case "chnk": /* * if blockname='chnk' then * begin * AudioFile.Seek(4, sofromcurrent); * PCchannels:=Audiofile.ReadDWord; * continue; * end;*/ ms.Seek(4, SeekOrigin.Current); isbEntry.numberOfChannels = ms.ReadUInt32(); break; case "cmpi": /* * if blockname='cmpi' then * begin * AudioFile.Seek(24, sofromcurrent); * if Audiofile.ReadDWord=1053609165 then * IsPCM:=true * else * IsPCM:=false; * continue; * end;*/ ms.Seek(20, SeekOrigin.Current); int pcmSignature1 = ms.ReadInt32(); int pcmSignature2 = ms.ReadInt32(); isbEntry.CodecID = pcmSignature1; isbEntry.CodecID2 = pcmSignature2; isbEntry.isPCM = pcmSignature2 == 1053609165; break; case "data": counter++; //Debug.WriteLine(counter + " Data for " + isbEntry.FileName); blocksize = ms.ReadUInt32(); //size of block string encodedType = ms.ReadString(4, false); isbEntry.isOgg = encodedType == "OggS"; ms.Seek(-4, SeekOrigin.Current); //go to block start isbEntry.DataOffset = (uint)ms.Position; MemoryStream data = new MemoryStream(); ms.CopyToEx(data, (int)blocksize); data.Position = 0; var str = data.ReadString(4, false); isbEntry.DataAsStored = data.ToArray(); //ms.Seek(blocksize, SeekOrigin.Current); //go to next block /* * if blockname='data' then * begin * inc(Counter); * blocksize:=Audiofile.ReadDWord; * if blocksize mod 2 <> 0 then * blocksize:=blocksize+1; * * if Audiofile.ReadBlockName='OggS' then * IsOgg:=true * else * IsOgg:=false; * * AudioFile.Seek(-4, sofromcurrent); * * if Counter=FileNo then * begin * SavePcFile(filename, destdir, blocksize, IsOgg, IsPcm, False, PcChannels, SampleRate); * application.processmessages; * continue; * end * else * begin * Audiofile.Seek(blocksize, sofromcurrent); * application.processmessages; * continue; * end; * end;*/ break; case "RIFF": //this is the start of a new file. riffSize = ms.ReadUInt32(); //size of isfbtitl chunk counter++; riffType = ms.ReadString(8, false); //technically not type, its just how this file format works if (riffType != "isbftitl") { //its an icbftitl, which never has data. ms.Seek(riffSize - 8, SeekOrigin.Current); //skip it } blocksize = ms.ReadUInt32(); //size of isfbtitl chunk ms.Seek(blocksize, SeekOrigin.Current); //skip it break; default: //skip the block blocksize = ms.ReadUInt32(); //size of block //Debug.WriteLine("Skipping block of size " + blocksize + " at 0x" + ms.Position.ToString("X5")); ms.Seek(blocksize, SeekOrigin.Current); //skip it break; } if (blocksize % 2 != 0) { ms.Seek(1, SeekOrigin.Current); //byte align } } if (isbEntry != null && isbEntry.DataAsStored != null) { BankEntries.Add(isbEntry); } isbEntry = new ISBankEntry(); }