protected void ReadFile() { try { if (_disposed) { throw new ObjectDisposedException("RegionFile", "Attempting to use a RegionFile after it has been disposed."); } /*// Get last udpate time * long newModified = -1; * try * { * if (File.Exists(fileName)) * { * newModified = Timestamp(File.GetLastWriteTime(fileName)); * } * } * catch (UnauthorizedAccessException e) * { * Console.WriteLine(e.Message); * return; * } * * // If it hasn't been modified, we don't need to do anything * if (newModified == lastModified) * { * return; * }*/ try { lock (this.fileLock) { file = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Read /*Write*/, FileShare.ReadWrite); /*//using (file) { * if (file.Length < SectorBytes) * { * byte[] int0 = BitConverter.GetBytes((int)0); * * // we need to write the chunk offset table * for (int i = 0; i < SectorInts; ++i) * { * file.Write(int0, 0, 4); * } * // write another sector for the timestamp info * for (int i = 0; i < SectorInts; ++i) * { * file.Write(int0, 0, 4); * } * * file.Flush(); * * sizeDelta += SectorBytes * 2; * } * * if ((file.Length & 0xfff) != 0) * { * // the file size is not a multiple of 4KB, grow it * file.Seek(0, SeekOrigin.End); * for (int i = 0; i < (file.Length & 0xfff); ++i) * { * file.WriteByte(0); * } * * file.Flush(); * }*/ // set up the available sector map int nSectors = (int)file.Length / SectorBytes; sectorFree = new List <Boolean>(nSectors); for (int i = 0; i < nSectors; ++i) { sectorFree.Add(true); } sectorFree[0] = false; // chunk offset table sectorFree[1] = false; // for the last modified info file.Seek(0, SeekOrigin.Begin); for (int i = 0; i < SectorInts; ++i) { byte[] offsetBytes = new byte[4]; file.Read(offsetBytes, 0, 4); if (BitConverter.IsLittleEndian) { Array.Reverse(offsetBytes); } int offset = BitConverter.ToInt32(offsetBytes, 0); offsets[i] = offset; if (offset != 0 && (offset >> 8) + (offset & 0xFF) <= sectorFree.Count) { for (int sectorNum = 0; sectorNum < (offset & 0xFF); ++sectorNum) { sectorFree[(offset >> 8) + sectorNum] = false; } } } for (int i = 0; i < SectorInts; ++i) { byte[] modBytes = new byte[4]; file.Read(modBytes, 0, 4); if (BitConverter.IsLittleEndian) { Array.Reverse(modBytes); } int lastModValue = BitConverter.ToInt32(modBytes, 0); chunkTimestamps[i] = lastModValue; } } } catch (IOException Excepción) { Depurador.Escribir_Excepción(Excepción != null ? Excepción.ToString() : null); //System.Console.WriteLine(Excepción.Message); //System.Console.WriteLine(Excepción.StackTrace); } } catch (Exception Excepción) { Depurador.Escribir_Excepción(Excepción != null ? Excepción.ToString() : null); } }
/** * @param inputFile The XWB file to extract * @param outputDirectory The directory to put the extracted files inside * @param statusReporter The status reporter to use for reporting which tracks that are currently extracted. * * @throws XnbException If the XWB file was malformed * @throws IOException If an I/O error occurs */ public static bool Extract(string inputFile, string outputDirectory) { try { //Status("Parsing XWB file header"); //Percentage(0f); //ByteBuffer buffer = ByteBuffer.wrap(FileUtils.readFileToByteArray(inputFile)); //buffer.order(ByteOrder.LITTLE_ENDIAN); BinaryReader reader = new BinaryReader(new FileStream(inputFile, FileMode.Open)); int Format = 0; int PlayRegionLength = 0; int PlayRegionOffset = 0; int wavebank_offset = 0; if (!CompareBytesToString(reader.ReadBytes(4), "WBND")) { throw new XwbException("not an XWB file: " + Path.GetFileName(inputFile)); } int Version = reader.ReadInt32(); // Skip trailing bytes of the version reader.ReadInt32(); if (Version != 46) { throw new XwbException("unsupported version: " + Version); } int[] segmentOffsets = new int[5]; int[] segmentLengths = new int[5]; for (int i = 0; i < 5; i++) { segmentOffsets[i] = reader.ReadInt32(); segmentLengths[i] = reader.ReadInt32(); } reader.BaseStream.Position = segmentOffsets[0]; int Flags = reader.ReadInt32(); int EntryCount = reader.ReadInt32(); // Skip terraria's wave bank's name. "Wave Bank". reader.BaseStream.Position += 64; int EntryMetaDataElementSize = reader.ReadInt32(); reader.ReadInt32(); // EntryNameElementSize reader.ReadInt32(); // Alignment wavebank_offset = segmentOffsets[1]; if ((Flags & Flag_Compact) != 0) { throw new XwbException("compact wavebanks are not supported"); } int playregion_offset = segmentOffsets[4]; for (int current_entry = 0; current_entry < EntryCount; current_entry++) { String track = current_entry < TrackNames.Length ? TrackNames[current_entry] : (current_entry + 1) + " Unknown"; //Status("Extracting " + track); //Percentage(0.1f + (0.9f / EntryCount) * current_entry); reader.BaseStream.Position = wavebank_offset; if (EntryMetaDataElementSize >= 4) { reader.ReadInt32(); // FlagsAndDuration } if (EntryMetaDataElementSize >= 8) { Format = reader.ReadInt32(); } if (EntryMetaDataElementSize >= 12) { PlayRegionOffset = reader.ReadInt32(); } if (EntryMetaDataElementSize >= 16) { PlayRegionLength = reader.ReadInt32(); } if (EntryMetaDataElementSize >= 20) { reader.ReadInt32(); // LoopRegionOffset } if (EntryMetaDataElementSize >= 24) { reader.ReadInt32(); // LoopRegionLength } wavebank_offset += EntryMetaDataElementSize; PlayRegionOffset += playregion_offset; int codec = (Format) & ((1 << 2) - 1); int chans = (Format >> (2)) & ((1 << 3) - 1); int rate = (Format >> (2 + 3)) & ((1 << 18) - 1); int align = (Format >> (2 + 3 + 18)) & ((1 << 8) - 1); reader.BaseStream.Position = PlayRegionOffset; byte[] audiodata = reader.ReadBytes(PlayRegionLength); // The codecs used by Terraria are currently xWMA and ADPCM. // The xWMA format is not supported by FNA, so it's only used // on Windows. This implementation uses ffmpeg to convert the raw // xWMA data to WAVE; a minified Windows executable is embedded. // PCM was introduced for the last tracks in the 1.3.3 update. string path = Path.Combine(outputDirectory, track + ".wav"); if (codec == MiniFormatTag_PCM) { Depurador.Escribir_Excepción("MiniFormatTag_PCM"); FileStream stream = new FileStream(path, FileMode.OpenOrCreate); BinaryWriter writer = new BinaryWriter(stream); stream.SetLength(0); writer.Write(Label_RIFF); // chunk id writer.Write(audiodata.Length + 36); // chunk size writer.Write(Label_WAVE); // RIFF type writer.Write(Label_fmt); // chunk id writer.Write((int)16); // format header size writer.Write((short)1); // format (PCM) writer.Write((short)chans); // channels writer.Write(rate); // samples per second int bitsPerSample = 16; int blockAlign = (bitsPerSample / 8) * chans; writer.Write(rate * blockAlign); // byte rate/ average bytes per second writer.Write((short)blockAlign); writer.Write((short)bitsPerSample); writer.Write(Label_data); // chunk id writer.Write(audiodata.Length); // data size writer.Write(audiodata); writer.Close(); } else if (codec == MiniFormatTag_WMA) { Depurador.Escribir_Excepción("MiniFormatTag_WMA"); // Note that it could still be another codec than xWma, // but that scenario isn't handled here. // This part has been ported from XWMA-to-pcm-u8 // Not the most beautiful code in the world, // but it does the job. // I do not know if this code outputs valid XWMA files, // but FFMPEG accepts them so it's all right for this usage. //File xWmaFile = new File(outputDirectory, track + ".wma"); //FileOutputStream xWmaOutput = FileUtils.openOutputStream(xWmaFile); // xWmaOutput.write(output.array(), output.arrayOffset(), output.position()); string wmaPath = Path.Combine(outputDirectory, track + ".wma"); BinaryWriter writer = new BinaryWriter(new FileStream(wmaPath, FileMode.OpenOrCreate)); //BufferWriter output = new BufferWriter(xWmaOutput); //output.setOrder(ByteOrder.LITTLE_ENDIAN); writer.Write(Label_RIFF); // chunk id writer.Write(0); // Full file size, ignored by ffmpeg writer.Write(Label_XWMA); // RIFF type writer.Write(Label_fmt); // chunk id writer.Write((int)18); // format header size writer.Write((short)0x161); // format (PCM) writer.Write((short)chans); // channels writer.Write(rate); // samples per second int[] wmaAverageBytesPerSec = new int[] { 12000, 24000, 4000, 6000, 8000, 20000 }; int[] wmaBlockAlign = new int[] { 929, 1487, 1280, 2230, 8917, 8192, 4459, 5945, 2304, 1536, 1485, 1008, 2731, 4096, 6827, 5462 }; int averageBytesPerSec = align > wmaAverageBytesPerSec.Length ? wmaAverageBytesPerSec[align >> 5] : wmaAverageBytesPerSec[align]; int blockAlign = align > wmaBlockAlign.Length ? wmaBlockAlign[align & 0xf] : wmaBlockAlign[align]; writer.Write(averageBytesPerSec); writer.Write((short)blockAlign); writer.Write((short)16); writer.Write((short)0); writer.Write(Label_dpds); int packetLength = blockAlign; int packetNum = audiodata.Length / packetLength; writer.Write(packetNum * 4); int fullSize = (PlayRegionLength * averageBytesPerSec % 4096 != 0) ? (1 + (int)(PlayRegionLength * averageBytesPerSec / 4096)) * 4096 : PlayRegionLength; int allBlocks = fullSize / 4096; int avgBlocksPerPacket = allBlocks / packetNum; int spareBlocks = allBlocks - (avgBlocksPerPacket * packetNum); int accu = 0; for (int i = 0; i < packetNum; ++i) { accu += avgBlocksPerPacket * 4096; if (spareBlocks != 0) { accu += 4096; --spareBlocks; } writer.Write(accu); } writer.Write(Label_data); writer.Write(PlayRegionLength); writer.Write(audiodata); // Replacing the file size placeholder, doesn't matter with ffmpeg // int pos = output.position(); // output.position(odRIChunkSize); // output.putInt(pos - 8); // output.position(pos); writer.Close(); FFmpeg.Convert(wmaPath, path); File.Delete(wmaPath); } else if (codec == MiniFormatTag_ADPCM) { Depurador.Escribir_Excepción("MiniFormatTag_ADPCM"); // Convert ADPCM data to PCM /*audiodata = ADPCMConverter.ConvertToPCM(audiodata, (short)chans, (short)align);*/ // Encode PCM as a WAVE file; note that most magic values used // here were obtained via trial and error, so it might break... //ByteBuffer writeBuffer = ByteBuffer.allocate(wavHeaderSize); //writeBuffer.order(ByteOrder.LITTLE_ENDIAN); FileStream stream = new FileStream(path, FileMode.OpenOrCreate); BinaryWriter writer = new BinaryWriter(stream); stream.SetLength(0); /*writer.Write(Label_RIFF); * writer.Write(audiodata.Length + 36); * writer.Write(Label_WAVE); * writer.Write(Label_fmt); * writer.Write((int)16); * writer.Write((short)1); // format code * writer.Write((short)chans); // channels * writer.Write(rate); // blocks per second * writer.Write(rate * 4); // bytes per second * writer.Write((short)4); // data block alignment * writer.Write((short)16); // bits per sample * writer.Write(Label_data); * writer.Write(audiodata.Length); // dataChunkSize */ writer.Write(audiodata); writer.Close(); } else { Depurador.Escribir_Excepción("unimplemented"); throw new XwbException("unimplemented codec " + codec); } } reader.Close(); return(true); } catch (Exception Excepción) { Depurador.Escribir_Excepción(Excepción != null ? Excepción.ToString() : null); } return(false); }
/* * gets an (uncompressed) stream representing the chunk data returns null if * the chunk is not found or an error occurs */ public Stream GetChunkDataInputStream(int x, int z, out string Compresión) { Compresión = null; if (_disposed) { throw new ObjectDisposedException("RegionFile", "Attempting to use a RegionFile after it has been disposed."); } if (OutOfBounds(x, z)) { Debugln("READ", x, z, "out of bounds"); return(null); } try { int offset = GetOffset(x, z); if (offset == 0) { // Debugln("READ", x, z, "miss"); return(null); } int sectorNumber = offset >> 8; int numSectors = offset & 0xFF; lock (this.fileLock) { if (sectorNumber + numSectors > sectorFree.Count) { Debugln("READ", x, z, "invalid sector"); return(null); } file.Seek(sectorNumber * SectorBytes, SeekOrigin.Begin); byte[] lengthBytes = new byte[4]; file.Read(lengthBytes, 0, 4); if (BitConverter.IsLittleEndian) { Array.Reverse(lengthBytes); } int length = BitConverter.ToInt32(lengthBytes, 0); if (length > SectorBytes * numSectors) { Debugln("READ", x, z, "invalid length: " + length + " > 4096 * " + numSectors); return(null); } byte version = (byte)file.ReadByte(); if (version == VERSION_GZIP) { byte[] data = new byte[length - 1]; file.Read(data, 0, data.Length); Stream ret = new GZipStream(new MemoryStream(data), CompressionMode.Decompress); Compresión = "[GZipStream] "; return(ret); } else if (version == VERSION_DEFLATE) { byte[] data = new byte[length - 1]; file.Read(data, 0, data.Length); Stream ret = new ZlibStream(new MemoryStream(data), CompressionMode.Decompress, true); Compresión = "[ZlibStream] "; return(ret); /*MemoryStream sinkZ = new MemoryStream(); * ZlibStream zOut = new ZlibStream(sinkZ, CompressionMode.Decompress, true); * zOut.Write(data, 0, data.Length); * zOut.Flush(); * zOut.Close(); * * sinkZ.Seek(0, SeekOrigin.Begin); * return sinkZ;*/ } Debugln("READ", x, z, "unknown version " + version); return(null); } } catch (IOException Excepción) { Depurador.Escribir_Excepción(Excepción != null ? Excepción.ToString() : null); //Debugln("READ", x, z, "exception"); return(null); } }