diff --git a/SubstrateCS/Properties/AssemblyInfo.cs b/SubstrateCS/Properties/AssemblyInfo.cs index 138fd28..e9a8290 100644 --- a/SubstrateCS/Properties/AssemblyInfo.cs +++ b/SubstrateCS/Properties/AssemblyInfo.cs @@ -30,8 +30,8 @@ using System.Runtime.InteropServices; // Build Number // Revision // -[assembly: AssemblyVersion("1.3.4.0")] -[assembly: AssemblyFileVersion("1.3.4.0")] +[assembly: AssemblyVersion("1.3.5.0")] +[assembly: AssemblyFileVersion("1.3.5.0")] // This library is compatible with all CLS-compliant .NET programming languages. [assembly: CLSCompliant(true)] \ No newline at end of file diff --git a/SubstrateCS/Source/Core/RegionFile.cs b/SubstrateCS/Source/Core/RegionFile.cs index 2051ad1..bafa8fd 100644 --- a/SubstrateCS/Source/Core/RegionFile.cs +++ b/SubstrateCS/Source/Core/RegionFile.cs @@ -6,7 +6,8 @@ using Ionic.Zlib; namespace Substrate.Core { - public class RegionFile : IDisposable { + public class RegionFile : IDisposable + { private const int VERSION_GZIP = 1; private const int VERSION_DEFLATE = 2; @@ -28,9 +29,10 @@ namespace Substrate.Core private bool _disposed = false; - public RegionFile(string path) { - offsets = new int[SECTOR_INTS]; - chunkTimestamps = new int[SECTOR_INTS]; + public RegionFile (string path) + { + offsets = new int[SectorInts]; + chunkTimestamps = new int[SectorInts]; fileName = path; Debugln("REGION LOAD " + fileName); @@ -94,71 +96,71 @@ namespace Substrate.Core file = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite); //using (file) { - if (file.Length < SECTOR_BYTES) { - byte[] int0 = BitConverter.GetBytes((int)0); + if (file.Length < SectorBytes) { + byte[] int0 = BitConverter.GetBytes((int)0); - /* we need to write the chunk offset table */ - for (int i = 0; i < SECTOR_INTS; ++i) { - file.Write(int0, 0, 4); - } - // write another sector for the timestamp info - for (int i = 0; i < SECTOR_INTS; ++i) { - file.Write(int0, 0, 4); - } - - file.Flush(); - - sizeDelta += SECTOR_BYTES * 2; + /* 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); } - if ((file.Length & 0xfff) != 0) { - /* the file size is not a multiple of 4KB, grow it */ - for (int i = 0; i < (file.Length & 0xfff); ++i) { - file.WriteByte(0); - } + file.Flush(); - file.Flush(); + sizeDelta += SectorBytes * 2; + } + + if ((file.Length & 0xfff) != 0) { + /* the file size is not a multiple of 4KB, grow it */ + for (int i = 0; i < (file.Length & 0xfff); ++i) { + file.WriteByte(0); } - /* set up the available sector map */ - int nSectors = (int)file.Length / SECTOR_BYTES; - sectorFree = new List(nSectors); + file.Flush(); + } - for (int i = 0; i < nSectors; ++i) { - sectorFree.Add(true); + /* set up the available sector map */ + int nSectors = (int)file.Length / SectorBytes; + sectorFree = new List(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); - sectorFree[0] = false; // chunk offset table - sectorFree[1] = false; // for the last modified info - - file.Seek(0, SeekOrigin.Begin); - for (int i = 0; i < SECTOR_INTS; ++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; - } + 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 < SECTOR_INTS; ++i) { - byte[] modBytes = new byte[4]; - file.Read(modBytes, 0, 4); + } + 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; + if (BitConverter.IsLittleEndian) { + Array.Reverse(modBytes); } + int lastModValue = BitConverter.ToInt32(modBytes, 0); + + chunkTimestamps[i] = lastModValue; + } //} } catch (IOException e) { @@ -168,35 +170,42 @@ namespace Substrate.Core } /* the modification date of the region file when it was first opened */ - public long LastModified() { + public long LastModified () + { return lastModified; } /* gets how much the region file has grown since it was last checked */ - public int GetSizeDelta() { + public int GetSizeDelta () + { int ret = sizeDelta; sizeDelta = 0; return ret; } // various small debug printing helpers - private void Debug(String str) { - // System.Consle.Write(str); + private void Debug (String str) + { + // System.Consle.Write(str); } - private void Debugln(String str) { + private void Debugln (String str) + { Debug(str + "\n"); } - private void Debug(String mode, int x, int z, String str) { + private void Debug (String mode, int x, int z, String str) + { Debug("REGION " + mode + " " + fileName + "[" + x + "," + z + "] = " + str); } - private void Debug(String mode, int x, int z, int count, String str) { + private void Debug (String mode, int x, int z, int count, String str) + { Debug("REGION " + mode + " " + fileName + "[" + x + "," + z + "] " + count + "B = " + str); } - private void Debugln(String mode, int x, int z, String str) { + private void Debugln (String mode, int x, int z, String str) + { Debug(mode, x, z, str + "\n"); } @@ -204,7 +213,8 @@ namespace Substrate.Core * 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) { + public Stream GetChunkDataInputStream (int x, int z) + { if (_disposed) { throw new ObjectDisposedException("RegionFile", "Attempting to use a RegionFile after it has been disposed."); } @@ -229,7 +239,7 @@ namespace Substrate.Core return null; } - file.Seek(sectorNumber * SECTOR_BYTES, SeekOrigin.Begin); + file.Seek(sectorNumber * SectorBytes, SeekOrigin.Begin); byte[] lengthBytes = new byte[4]; file.Read(lengthBytes, 0, 4); @@ -238,7 +248,7 @@ namespace Substrate.Core } int length = BitConverter.ToInt32(lengthBytes, 0); - if (length > SECTOR_BYTES * numSectors) { + if (length > SectorBytes * numSectors) { Debugln("READ", x, z, "invalid length: " + length + " > 4096 * " + numSectors); return null; } @@ -250,7 +260,7 @@ namespace Substrate.Core Stream ret = new GZipStream(new MemoryStream(data), CompressionMode.Decompress); return ret; - } + } else if (version == VERSION_DEFLATE) { byte[] data = new byte[length - 1]; file.Read(data, 0, data.Length); @@ -270,13 +280,15 @@ namespace Substrate.Core Debugln("READ", x, z, "unknown version " + version); return null; - } catch (IOException) { + } + catch (IOException) { Debugln("READ", x, z, "exception"); return null; } } - public Stream GetChunkDataOutputStream(int x, int z) { + public Stream GetChunkDataOutputStream (int x, int z) + { if (OutOfBounds(x, z)) return null; return new ZlibStream(new ChunkBuffer(this, x, z), CompressionMode.Compress); @@ -293,14 +305,15 @@ namespace Substrate.Core * lets chunk writing be multithreaded by not locking the whole file as a * chunk is serializing -- only writes when serialization is over */ - class ChunkBuffer : MemoryStream { + class ChunkBuffer : MemoryStream + { private int x, z; private RegionFile region; private int? _timestamp; - public ChunkBuffer(RegionFile r, int x, int z) - : base(8096) + public ChunkBuffer (RegionFile r, int x, int z) + : base(8096) { this.region = r; this.x = x; @@ -313,7 +326,8 @@ namespace Substrate.Core _timestamp = timestamp; } - public override void Close() { + public override void Close () + { if (_timestamp == null) { region.Write(x, z, this.GetBuffer(), (int)this.Length); } @@ -329,7 +343,8 @@ namespace Substrate.Core } /* write a chunk at (x,z) with length bytes of data to disk */ - protected void Write(int x, int z, byte[] data, int length, int timestamp) { + protected void Write (int x, int z, byte[] data, int length, int timestamp) + { if (_disposed) { throw new ObjectDisposedException("RegionFile", "Attempting to use a RegionFile after it has been disposed."); } @@ -338,7 +353,7 @@ namespace Substrate.Core int offset = GetOffset(x, z); int sectorNumber = offset >> 8; int sectorsAllocated = offset & 0xFF; - int sectorsNeeded = (length + CHUNK_HEADER_SIZE) / SECTOR_BYTES + 1; + int sectorsNeeded = (length + CHUNK_HEADER_SIZE) / SectorBytes + 1; // maximum chunk size is 1MB if (sectorsNeeded >= 256) { @@ -349,7 +364,8 @@ namespace Substrate.Core /* we can simply overwrite the old sectors */ Debug("SAVE", x, z, length, "rewrite"); Write(sectorNumber, data, length); - } else { + } + else { /* we need to allocate new sectors */ /* mark the sectors previously used for this chunk as free */ @@ -365,7 +381,8 @@ namespace Substrate.Core if (runLength != 0) { if (sectorFree[i]) runLength++; else runLength = 0; - } else if (sectorFree[i]) { + } + else if (sectorFree[i]) { runStart = i; runLength = 1; } @@ -384,7 +401,8 @@ namespace Substrate.Core sectorFree[sectorNumber + i] = false; } Write(sectorNumber, data, length); - } else { + } + else { /* * no free space large enough found -- we need to grow the * file @@ -396,25 +414,28 @@ namespace Substrate.Core file.Write(emptySector, 0, emptySector.Length); sectorFree.Add(false); } - sizeDelta += SECTOR_BYTES * sectorsNeeded; + sizeDelta += SectorBytes * sectorsNeeded; Write(sectorNumber, data, length); SetOffset(x, z, (sectorNumber << 8) | sectorsNeeded); } } SetTimestamp(x, z, timestamp); - } catch (IOException e) { + } + catch (IOException e) { Console.WriteLine(e.StackTrace); } } /* write a chunk data to the region file at specified sector number */ - private void Write(int sectorNumber, byte[] data, int length) { + private void Write (int sectorNumber, byte[] data, int length) + { Debugln(" " + sectorNumber); - file.Seek(sectorNumber * SECTOR_BYTES, SeekOrigin.Begin); + file.Seek(sectorNumber * SectorBytes, SeekOrigin.Begin); byte[] bytes = BitConverter.GetBytes(length + 1); - if (BitConverter.IsLittleEndian) {; + if (BitConverter.IsLittleEndian) { + ; Array.Reverse(bytes); } file.Write(bytes, 0, 4); // chunk length @@ -428,9 +449,9 @@ namespace Substrate.Core int sectorNumber = offset >> 8; int sectorsAllocated = offset & 0xFF; - file.Seek(sectorNumber * SECTOR_BYTES, SeekOrigin.Begin); + file.Seek(sectorNumber * SectorBytes, SeekOrigin.Begin); for (int i = 0; i < sectorsAllocated; i++) { - file.Write(emptySector, 0, SECTOR_BYTES); + file.Write(emptySector, 0, SectorBytes); } SetOffset(x, z, 0); @@ -443,27 +464,32 @@ namespace Substrate.Core return x < 0 || x >= 32 || z < 0 || z >= 32; } - private int GetOffset(int x, int z) { + private int GetOffset (int x, int z) + { return offsets[x + z * 32]; } - public bool HasChunk(int x, int z) { + public bool HasChunk (int x, int z) + { return GetOffset(x, z) != 0; } - private void SetOffset(int x, int z, int offset) { + private void SetOffset (int x, int z, int offset) + { offsets[x + z * 32] = offset; file.Seek((x + z * 32) * 4, SeekOrigin.Begin); byte[] bytes = BitConverter.GetBytes(offset); - if (BitConverter.IsLittleEndian) {; + if (BitConverter.IsLittleEndian) { + ; Array.Reverse(bytes); } file.Write(bytes, 0, 4); } - private int Timestamp () { + private int Timestamp () + { DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0); return (int)((DateTime.UtcNow - epoch).Ticks / (10000L * 1000L)); } @@ -479,20 +505,38 @@ namespace Substrate.Core return chunkTimestamps[x + z * 32]; } - public void SetTimestamp(int x, int z, int value) { + public void SetTimestamp (int x, int z, int value) + { chunkTimestamps[x + z * 32] = value; - file.Seek(SECTOR_BYTES + (x + z * 32) * 4, SeekOrigin.Begin); + file.Seek(SectorBytes + (x + z * 32) * 4, SeekOrigin.Begin); byte[] bytes = BitConverter.GetBytes(value); - if (BitConverter.IsLittleEndian) {; + if (BitConverter.IsLittleEndian) { + ; Array.Reverse(bytes); } file.Write(bytes, 0, 4); } - public void Close() { + public void Close () + { file.Close(); } + + protected virtual int SectorBytes + { + get { return SECTOR_BYTES; } + } + + protected virtual int SectorInts + { + get { return SECTOR_BYTES / 4; } + } + + protected virtual byte[] EmptySector + { + get { return emptySector; } + } } }