/// <summary> /// Saves the region file to a stream. /// </summary> /// <param name="stream">The stream the region file will write to.</param> /// <param name="regionFile">The region file to save.</param> public static void SaveRegion(Stream stream, RegionFile regionFile) { // build offset and timestamp headers // write data to storage using (var writer = new BinaryWriter(stream)) { //write header information foreach (var offset in regionFile.offsets) { writer.Write(EndiannessConverter.ToInt16((short)offset.SectorOffset)); writer.Write(EndiannessConverter.ToInt16(offset.SectorSize)); } foreach (var timeStamp in regionFile.tStamps) { writer.Write(EndiannessConverter.ToInt32((int)timeStamp.Timestamp)); } var index = 0; // write chunk information foreach (var content in regionFile.Content) { stream.Seek(regionFile.offsets[index++].SectorOffset * 4096, SeekOrigin.Begin); NbtFile.SaveTag(stream, 2, content); // write blank space before writing next chunk var size = content.Size(); var csiz = (int)Math.Ceiling(size / 1024.0); for (var i = 0; i < (csiz * 1024) - size; i++) { writer.Write((byte)0); } } } }
/// <summary> /// Opens the region file from a stream. /// </summary> /// <param name="stream">The stream the region file will read from.</param> /// <returns>The parsed region file.</returns> public static RegionFile OpenRegion(Stream stream) { var region = new RegionFile(); using (var reader = new BinaryReader(stream)) { // initialize values #region Init var sectors = new int[1024]; var tstamps = new int[1024]; #endregion // read header information #region Header IO read for (int i = 0; i < 1024; i++) sectors[i] = reader.ReadInt32(); for (int i = 0; i < 1024; i++) tstamps[i] = reader.ReadInt32(); #endregion // parse header information #region Offset parse for (int i = 0; i < 1024; i++) { int sector = EndiannessConverter.ToInt32(sectors[i]); region.offsets[i] = new Offset { // get the sector size of the chunk SectorSize = (byte)(sector & 0xFF), // get the sector offset of the chunk SectorOffset = sector >> 8, }; } #endregion #region Timestamp parse for (int i = 0; i < 1024; i++) { int tstamp = EndiannessConverter.ToInt32(tstamps[i]); region.tStamps[i] = new TimeStamp { Timestamp = tstamp }; } #endregion // read content from disk #region Chunk IO read var chunkBuffer = new byte[sectors.Length][]; { for (int i = 0; i < 1024; i++) { Offset offset = region.offsets[i]; if (offset.SectorOffset <= 0) continue; // sector offset will always start at 2 stream.Seek(offset.SectorOffset * 4096, SeekOrigin.Begin); reader.ReadByte(); chunkBuffer[i] = reader.ReadBytes(EndiannessConverter.ToInt32(reader.ReadInt32()) - 1); } } #endregion // parse chunk information #region Parse content var workerThreads = new Thread[MaxThreads]; var content = new List<NbtFile>(); { for (int i = 0; i < MaxThreads; i++) { int index = i; workerThreads[i] = new Thread(() => { var buffer = new List<NbtFile>(); int offset = index * (1024 / MaxThreads); for (int n = offset; n < (chunkSlice + offset); n++) { byte[] chunk = chunkBuffer[n]; if (chunk == null) continue; using (var mmStream = new MemoryStream(chunk)) { buffer.Add(NbtFile.OpenTag(mmStream, 2)); } } lock (content) content.AddRange(buffer); }) { Name = "Worker thread " + (i + 1) }; workerThreads[i].Start(); } foreach (Thread t in workerThreads) t.Join(); region.Content = content.ToArray(); } #endregion } return region; }