Survivalcraft API 1.8.2.3 v1.8.2.3
Survivalcraft 2.4
载入中...
搜索中...
未找到
TerrainSerializer23.cs
浏览该文件的文档.
1using System.IO.Compression;
2using System.Runtime.CompilerServices;
3using System.Text;
4using Engine;
5
6namespace Game {
7 public class TerrainSerializer23 : IDisposable {
8 public interface IStorage : IDisposable {
9 void Open(string directoryName, string suffix);
10
11 int Load(Point2 coords, byte[] buffer);
12
13 void Save(Point2 coords, byte[] buffer, int size);
14 }
15
16 public class SingleFileStorage : IStorage {
17 public struct ChunkDescriptor {
18 public int Index;
19
20 public Point2 Coords;
21
22 public int StartNode;
23 }
24
25 const string FileName = "Chunks32fs.dat";
26
27 const uint FileHeaderMagic = 3735923200u;
28
29 const int FileHeaderSize = 786444;
30
32
34
36
38
39 const uint NodeHeaderMagic = 3735927296u;
40
41 const int NodeHeaderSize = 8;
42
43 public Stream Stream;
44
45 BinaryReader Reader;
46
47 BinaryWriter Writer;
48
49 Dictionary<Point2, ChunkDescriptor> ChunkDescriptors = [];
50
52
54
56
57 public virtual void Open(string directoryName, string suffix) {
58 string path = Storage.CombinePaths(directoryName, FileName + suffix);
59 try {
60 Stream = Storage.OpenFile(path, OpenFileMode.CreateOrOpen);
61 Reader = new BinaryReader(Stream);
62 Writer = new BinaryWriter(Stream);
63 if (Stream.Length == 0L) {
64 FreeNode = -1;
65 NodeSize = 1024;
67 Writer.Write(NodeSize);
68 Writer.Write(FreeNode);
69 for (int i = 0; i < FileHeaderChunkDescriptorsCount; i++) {
70 WriteChunkDescriptor(new ChunkDescriptor { Index = i, StartNode = -1 });
71 }
72 }
73 }
74 catch (Exception) {
75 Storage.DeleteFile(path);
76 throw;
77 }
78 Stream.Position = 0L;
79 if (ReverseEndianness(Reader.ReadUInt32()) != FileHeaderMagic) {
80 throw new InvalidOperationException("Invalid chunks file header magic.");
81 }
82 NodeSize = Reader.ReadInt32();
83 if (NodeSize < 64
85 throw new InvalidOperationException("Invalid chunks file header node size.");
86 }
87 FreeNode = Reader.ReadInt32();
88 for (int j = 0; j < FileHeaderChunkDescriptorsCount; j++) {
90 if (value.StartNode >= 0) {
91 ChunkDescriptors.Add(value.Coords, value);
92 }
93 }
94 }
95
96 public virtual void Dispose() {
97 if (Stream != null) {
98 Stream.Dispose();
99 }
100 }
101
102 public virtual int Load(Point2 p, byte[] buffer) {
103 if (!ChunkDescriptors.TryGetValue(p, out ChunkDescriptor value)) {
104 return -1;
105 }
106 int nextNode = value.StartNode;
107 int num = 0;
108 while (nextNode >= 0) {
109 num += ReadNode(nextNode, buffer, num, out nextNode);
110 }
111 return num;
112 }
113
114 public virtual void Save(Point2 p, byte[] buffer, int size) {
115 int count = Math.Max((size + NodeDataSize - 1) / NodeDataSize, 1);
116 List<int> freeNodes = GetFreeNodes(count);
117 ReadNode(freeNodes.Last(), null, 0, out int nextNode);
118 int num = 0;
119 for (int i = 0; i < freeNodes.Count; i++) {
120 int num2 = Math.Min(size - num, NodeDataSize);
121 WriteNode(freeNodes[i], buffer, num, num2, i < freeNodes.Count - 1 ? freeNodes[i + 1] : -1);
122 num += num2;
123 }
124 if (!ChunkDescriptors.TryGetValue(p, out ChunkDescriptor value)) {
125 ChunkDescriptor chunkDescriptor = default;
126 chunkDescriptor.Index = ChunkDescriptors.Count % FileHeaderChunkDescriptorsCount;
127 chunkDescriptor.Coords = p;
128 chunkDescriptor.StartNode = freeNodes.First();
129 value = chunkDescriptor;
130 SetAndWriteFreeNode(nextNode);
131 }
132 else {
133 int node = FindLastNode(value.StartNode);
134 WriteNode(node, null, 0, 0, nextNode);
135 SetAndWriteFreeNode(value.StartNode);
136 value.StartNode = freeNodes.First();
137 }
139 ChunkDescriptors[p] = value;
140 }
141
142 public virtual List<int> GetFreeNodes(int count) {
143 List<int> list = [];
144 int nextNode = FreeNode;
145 while (nextNode >= 0
146 && list.Count < count) {
147 list.Add(nextNode);
148 ReadNode(nextNode, null, 0, out nextNode);
149 }
150 if (list.Count < count) {
151 int num = count - list.Count;
152 int num2 = (int)((Stream.Length - FileHeaderSize) / NodeSize);
153 int num3 = num2 + num - 1;
154 Stream.SetLength(Stream.Length + NodeSize * num);
155 WriteNode(num3, null, 0, 0, -1);
156 if (list.Count > 0) {
157 WriteNode(list.Last(), null, 0, 0, num2);
158 }
159 else {
161 }
162 for (int i = num2; i <= num3; i++) {
163 list.Add(i);
164 }
165 }
166 return list;
167 }
168
169 public virtual int FindLastNode(int startNode) {
170 int num = startNode;
171 while (true) {
172 ReadNode(num, null, 0, out int nextNode);
173 if (nextNode < 0) {
174 break;
175 }
176 num = nextNode;
177 }
178 return num;
179 }
180
181 public virtual void SetAndWriteFreeNode(int freeNode) {
182 Stream.Position = NodeHeaderSize;
183 Writer.Write(freeNode);
184 FreeNode = freeNode;
185 }
186
187 public virtual ChunkDescriptor ReadChunkDescriptor(int i) {
189 ChunkDescriptor result = default;
190 result.Index = i;
191 result.Coords.X = Reader.ReadInt32();
192 result.Coords.Y = Reader.ReadInt32();
193 result.StartNode = Reader.ReadInt32();
194 return result;
195 }
196
197 public virtual void WriteChunkDescriptor(ChunkDescriptor desc) {
199 Writer.Write(desc.Coords.X);
200 Writer.Write(desc.Coords.Y);
201 Writer.Write(desc.StartNode);
202 }
203
204 public virtual int ReadNode(int node, byte[] data, int offset, out int nextNode) {
205 if (node < 0
206 || node >= (Stream.Length - FileHeaderSize) / NodeSize) {
207 throw new InvalidOperationException("Invalid node.");
208 }
209 Stream.Position = FileHeaderSize + node * NodeSize;
210 if (ReverseEndianness(Reader.ReadUInt32()) != NodeHeaderMagic) {
211 throw new InvalidOperationException("Invalid node magic.");
212 }
213 int nodeHeader = Reader.ReadInt32();
214 ParseNodeHeader(node, nodeHeader, out int dataSize, out nextNode);
215 if (data != null
216 && Stream.Read(data, offset, dataSize) != dataSize) {
217 throw new InvalidOperationException("Truncated ChunksFile.");
218 }
219 return dataSize;
220 }
221
222 public virtual void WriteNode(int node, byte[] data, int offset, int size, int nextNode) {
223 if (node < 0
224 || node >= (Stream.Length - FileHeaderSize) / NodeSize) {
225 throw new InvalidOperationException("Invalid node.");
226 }
227 Stream.Position = FileHeaderSize + node * NodeSize;
228 int value = MakeNodeHeader(node, size, nextNode);
230 Writer.Write(value);
231 if (data != null) {
232 Stream.Write(data, offset, size);
233 }
234 }
235
236 public virtual int MakeNodeHeader(int node, int dataSize, int nextNode) {
237 if (nextNode < 0) {
238 return (dataSize << 1) | 1;
239 }
240 return (nextNode - (node + 1)) << 1;
241 }
242
243 public virtual void ParseNodeHeader(int node, int nodeHeader, out int dataSize, out int nextNode) {
244 if (((uint)nodeHeader & 1u) != 0) {
245 dataSize = nodeHeader >> 1;
246 nextNode = -1;
247 }
248 else {
249 dataSize = NodeDataSize;
250 nextNode = node + 1 + (nodeHeader >> 1);
251 }
252 }
253
254 public static uint ReverseEndianness(uint n) => ((n & 0xFF000000u) >> 24)
255 | ((n & 0xFF0000) >> FileHeaderFreeNodeOffset)
256 | ((n & 0xFF00) << FileHeaderFreeNodeOffset)
257 | (n << 24);
258
259 public virtual void LogDebugInfo() {
260 Log.Information("{0} chunks:", ChunkDescriptors.Count);
261 foreach (KeyValuePair<Point2, ChunkDescriptor> chunkDescriptor in ChunkDescriptors) {
263 " Chunk {0}: p=({1}), startNode={2}",
264 chunkDescriptor.Value.Index,
265 chunkDescriptor.Key,
266 chunkDescriptor.Value.StartNode
267 );
268 }
269 int num = (int)((Stream.Length - FileHeaderSize) / NodeSize);
270 Log.Information("{0} nodes, FreeNode={1:0}:", num, FreeNode);
271 for (int i = 0; i < num; i++) {
272 int num2 = ReadNode(i, null, 0, out int nextNode);
273 Log.Information(" Node {0:0}: next={1:0}, dataSize={2}", i, nextNode, num2);
274 }
275 }
276 }
277
279 public struct DirectoryEntry {
280 public int Offset;
281
282 public int Size;
283 }
284
285 const int MaxOpenedStreams = 100;
286
287 const int ExtraSpaceBytes = 1024;
288
289 // ReSharper disable UnusedMember.Local
290 const int RegionChunksBits = 4;
291
292 const int RegionChunksCount = 16;
293
295
297
299
300 const int RegionDataOffset = 2052;
301
303 // ReSharper restore UnusedMember.Local
304
305 static uint RegionMagic = MakeFourCC("RGN1");
306
307 static uint RegionChunkMagic = MakeFourCC("CHK1");
308
309 public string RegionsDirectoryName;
310
312
313 Dictionary<Point2, Stream> StreamsByRegion = [];
314
315 Queue<Stream> OpenedStreams = new();
316
317 public virtual void Dispose() {
318 while (OpenedStreams.Count > 0) {
319 OpenedStreams.Dequeue().Dispose();
320 }
321 }
322
323 public virtual void Open(string directoryName, string suffix) {
324 RegionsDirectoryName = Storage.CombinePaths(directoryName, $"Regions{suffix}");
328 foreach (string item in Storage.ListFileNames(RegionsDirectoryName)) {
329 if (Storage.GetExtension(item) == ".new") {
330 string text = Storage.CombinePaths(RegionsDirectoryName, item);
331 string text2 = Storage.ChangeExtension(text, "");
332 if (!Storage.FileExists(text2)) {
333 Storage.MoveFile(text, text2);
334 }
335 else {
336 Storage.DeleteFile(text);
337 }
338 }
339 }
340 }
341
342 public virtual int Load(Point2 coords, byte[] buffer) {
343 Point2 region = new(coords.X >> 4, coords.Y >> 4);
344 Point2 chunk = new(coords.X & 0xF, coords.Y & 0xF);
345 Stream regionStream = GetRegionStream(region, false);
346 if (regionStream != null) {
347 using (BinaryReader reader = new(regionStream, Encoding.UTF8, true)) {
348 DirectoryEntry directoryEntry = ReadDirectoryEntry(reader, chunk);
349 if (directoryEntry.Offset > 0) {
350 ReadData(reader, directoryEntry.Offset, buffer, directoryEntry.Size);
351 return directoryEntry.Size;
352 }
353 }
354 }
355 return -1;
356 }
357
358 public virtual void Save(Point2 coords, byte[] buffer, int size) {
359 Point2 region = new(coords.X >> 4, coords.Y >> 4);
360 Point2 point = new(coords.X & 0xF, coords.Y & 0xF);
361 Stream regionStream = GetRegionStream(region, true);
362 string text = null;
363 using (BinaryReader reader = new(regionStream, Encoding.UTF8, true)) {
364 using (BinaryWriter writer = new(regionStream, Encoding.UTF8, true)) {
365 int num = point.X + ChunkSizeX * point.Y;
366 DirectoryEntry[] array = ReadDirectoryEntries(reader);
367 DirectoryEntry directoryEntry = array[num];
368 DirectoryEntry entry;
369 if (directoryEntry.Offset > 0) {
370 int num2 = FindNextEntryIndex(array, num);
371 if (num2 >= 0) {
372 int num3 = array[num2].Offset - directoryEntry.Offset - 4;
373 if (size <= num3) {
374 WriteData(writer, directoryEntry.Offset, buffer, size);
375 Point2 chunk = point;
376 entry = new DirectoryEntry { Offset = directoryEntry.Offset, Size = size };
377 WriteDirectoryEntry(writer, chunk, entry);
378 regionStream.Flush();
379 }
380 else {
381 text = GetRegionPath(region);
382 using (Stream stream = Storage.OpenFile(TmpFilePath, OpenFileMode.Create)) {
383 using (BinaryWriter binaryWriter = new(stream)) {
384 DirectoryEntry[] array2 = new DirectoryEntry[array.Length];
385 int num4 = RegionDataOffset;
386 for (int i = 0; i < array.Length; i++) {
387 if (i == num) {
388 array2[i].Offset = num4;
389 array2[i].Size = size;
390 num4 += CalculateIdealEntrySpace(array2[i].Size);
391 }
392 else if (array[i].Offset > 0) {
393 array2[i].Offset = num4;
394 array2[i].Size = array[i].Size;
395 num4 += CalculateIdealEntrySpace(array2[i].Size);
396 }
397 }
398 ResizeStream(stream, num4);
399 binaryWriter.Write(RegionMagic);
400 WriteDirectoryEntries(binaryWriter, array2);
401 byte[] buffer2 = new byte[array.Max(e => e.Size)];
402 for (int j = 0; j < array.Length; j++) {
403 if (j == num) {
404 WriteData(binaryWriter, array2[j].Offset, buffer, size);
405 }
406 else if (array[j].Offset > 0) {
407 ReadData(reader, array[j].Offset, buffer2, array[j].Size);
408 WriteData(binaryWriter, array2[j].Offset, buffer2, array2[j].Size);
409 }
410 }
411 }
412 }
413 }
414 }
415 else {
416 if (directoryEntry.Offset + 4 + size > regionStream.Length) {
417 ResizeStream(regionStream, directoryEntry.Offset + CalculateIdealEntrySpace(size));
418 }
419 WriteData(writer, directoryEntry.Offset, buffer, size);
420 Point2 chunk2 = point;
421 entry = new DirectoryEntry { Offset = directoryEntry.Offset, Size = size };
422 WriteDirectoryEntry(writer, chunk2, entry);
423 regionStream.Flush();
424 }
425 }
426 else {
427 int num5 = (int)regionStream.Length;
428 ResizeStream(regionStream, num5 + CalculateIdealEntrySpace(size));
429 WriteData(writer, num5, buffer, size);
430 Point2 chunk3 = point;
431 entry = new DirectoryEntry { Offset = num5, Size = size };
432 WriteDirectoryEntry(writer, chunk3, entry);
433 regionStream.Flush();
434 }
435 }
436 }
437 if (text != null) {
438 regionStream.Dispose();
439 string text2 = $"{text}.new";
441 Storage.MoveFile(text2, text);
442 }
443 }
444
445 public virtual string GetRegionPath(Point2 region) => $"{RegionsDirectoryName}/Region {region.X},{region.Y}.dat";
446
447 public virtual Stream GetRegionStream(Point2 region, bool createNew) {
448 if (!StreamsByRegion.TryGetValue(region, out Stream value)
449 || value == null
450 || !value.CanRead) {
451 string regionPath = GetRegionPath(region);
452 if (Storage.FileExists(regionPath)) {
453 value = Storage.OpenFile(regionPath, OpenFileMode.ReadWrite);
454 using (BinaryReader binaryReader = new(value, Encoding.UTF8, true)) {
455 if (binaryReader.ReadUInt32() != RegionMagic) {
456 throw new InvalidOperationException($"Invalid region file {region} magic.");
457 }
458 }
459 OpenedStreams.Enqueue(value);
460 }
461 else if (createNew) {
462 value = Storage.OpenFile(regionPath, OpenFileMode.Create);
463 OpenedStreams.Enqueue(value);
464 using (BinaryWriter binaryWriter = new(value, Encoding.UTF8, true)) {
465 binaryWriter.Write(RegionMagic);
466 WriteDirectoryEntries(binaryWriter, new DirectoryEntry[256]);
467 }
468 }
469 else {
470 value = null;
471 }
472 StreamsByRegion[region] = value;
473 while (OpenedStreams.Count > MaxOpenedStreams) {
474 OpenedStreams.Dequeue().Dispose();
475 }
476 }
477 return value;
478 }
479
480 public static void ReadData(BinaryReader reader, int offset, byte[] buffer, int size) {
481 reader.BaseStream.Position = offset;
482 if (reader.ReadUInt32() != RegionChunkMagic) {
483 throw new InvalidOperationException("Invalid region file chunk magic.");
484 }
485 if (reader.BaseStream.Read(buffer, 0, size) != size) {
486 throw new InvalidOperationException("Region file is truncated.");
487 }
488 }
489
490 public static DirectoryEntry ReadDirectoryEntry(BinaryReader reader) {
491 DirectoryEntry result = default;
492 result.Offset = reader.ReadInt32();
493 result.Size = reader.ReadInt32();
494 if (result.Size < 0
495 || result.Size > 1048576) {
496 throw new InvalidOperationException("Region file entry size out of bounds, likely corrupt region file.");
497 }
498 return result;
499 }
500
501 public static DirectoryEntry ReadDirectoryEntry(BinaryReader reader, Point2 chunk) {
502 int num = chunk.X + ChunkSizeX * chunk.Y;
503 reader.BaseStream.Position = 4 + num * RegionDirectoryEntrySize;
504 return ReadDirectoryEntry(reader);
505 }
506
507 public static DirectoryEntry[] ReadDirectoryEntries(BinaryReader reader) {
508 reader.BaseStream.Position = 4L;
509 DirectoryEntry[] array = new DirectoryEntry[256];
510 for (int i = 0; i < 256; i++) {
511 array[i] = ReadDirectoryEntry(reader);
512 }
513 return array;
514 }
515
516 public static void WriteData(BinaryWriter writer, int offset, byte[] buffer, int size) {
517 writer.BaseStream.Position = offset;
518 writer.Write(RegionChunkMagic);
519 writer.BaseStream.Write(buffer, 0, size);
520 }
521
522 public static void WriteDirectoryEntry(BinaryWriter writer, DirectoryEntry entry) {
523 writer.Write(entry.Offset);
524 writer.Write(entry.Size);
525 }
526
527 public static void WriteDirectoryEntry(BinaryWriter writer, Point2 chunk, DirectoryEntry entry) {
528 int num = chunk.X + 16 * chunk.Y;
529 writer.BaseStream.Position = 4 + num * RegionDirectoryEntrySize;
530 WriteDirectoryEntry(writer, entry);
531 }
532
533 public static void WriteDirectoryEntries(BinaryWriter writer, DirectoryEntry[] entries) {
534 writer.BaseStream.Position = 4L;
535 for (int i = 0; i < 256; i++) {
536 WriteDirectoryEntry(writer, entries[i]);
537 }
538 }
539
540 public static void ResizeStream(Stream stream, int size) {
541 if (size > 268435456) {
542 throw new InvalidOperationException("Region file too large.");
543 }
544 stream.SetLength(size);
545 }
546
547 public static int FindNextEntryIndex(DirectoryEntry[] entries, int index) {
548 int result = -1;
549 int num = int.MaxValue;
550 for (int i = 0; i < entries.Length; i++) {
551 int num2 = entries[i].Offset - entries[index].Offset;
552 if (num2 > 0
553 && num2 < num) {
554 num = num2;
555 result = i;
556 }
557 }
558 return result;
559 }
560
561 public static int CalculateIdealEntrySpace(int size) => size + ExtraSpaceBytes + 4;
562
563 public static uint MakeFourCC(string s) => ((uint)s[3] << 24) | ((uint)s[2] << 16) | ((uint)s[1] << 8) | s[0];
564 }
565
566 const int ChunkSizeX = 16;
567
568 const int ChunkSizeY = 256;
569
570 const int ChunkSizeZ = 16;
571
572 const int WorstCaseChunkDataSize = 262400;
573
574 object m_lock = new();
575
577
579
581
583
585
586 public TerrainSerializer23(string directoryName, string suffix = "") {
588 m_storage.Open(directoryName, suffix);
589 }
590
591 public virtual bool LoadChunk(TerrainChunk chunk) => LoadChunkData(chunk);
592
593 public virtual void SaveChunk(TerrainChunk chunk) {
594 if (chunk.State > TerrainChunkState.InvalidContents4
595 && chunk.ModificationCounter > 0) {
596 SaveChunkData(chunk);
597 chunk.ModificationCounter = 0;
598 }
599 }
600
601 public virtual bool LoadChunkData(TerrainChunk chunk) {
602 lock (m_lock) {
603 _ = Time.RealTime;
604 try {
605 int num = m_storage.Load(chunk.Coords, m_storageBuffer);
606 if (num < 0) {
607 return false;
608 }
610 }
611 catch (IOException) {
612 Dispatcher.Dispatch(() => {
613 if (m_ioExceptionDealt) {
614 return;
615 }
616 m_ioExceptionDealt = true;
619 ViewGameLogDialog dialog = new();
620 dialog.SetErrorHead(11, 14);
621 DialogsManager.ShowDialog(null, dialog);
622 }
623 );
624 return false;
625 }
626 catch (Exception e) {
627 Log.Error(ExceptionManager.MakeFullErrorMessage($"Error loading chunk ({chunk.Coords.X},{chunk.Coords.Y}).", e));
628 }
629 _ = Time.RealTime;
630 return true;
631 }
632 }
633
634 public virtual void SaveChunkData(TerrainChunk chunk) {
635 lock (m_lock) {
636 _ = Time.RealTime;
637 try {
638 int size = CompressChunkData(chunk, m_storageBuffer);
639 m_storage.Save(chunk.Coords, m_storageBuffer, size);
640 }
641 catch (Exception e) {
642 Log.Error(ExceptionManager.MakeFullErrorMessage($"Error saving chunk ({chunk.Coords.X},{chunk.Coords.Y}).", e));
643 }
644 _ = Time.RealTime;
645 }
646 }
647
648 public virtual void Dispose() {
649 Utilities.Dispose(ref m_storage);
650 }
651
652 public virtual int CompressChunkData(TerrainChunk chunk, byte[] buffer) {
653 int num = 0;
654 for (int i = 0; i < ChunkSizeX; i++) {
655 for (int j = 0; j < ChunkSizeZ; j++) {
656 int shaftValueFast = chunk.GetShaftValueFast(i, j);
657 m_compressBuffer[num++] = (byte)((Terrain.ExtractTemperature(shaftValueFast) << 4) | Terrain.ExtractHumidity(shaftValueFast));
658 }
659 }
660 int num2 = 0;
661 int num3 = -1;
662 for (int k = 0; k < ChunkSizeY; k++) {
663 for (int l = 0; l < ChunkSizeX; l++) {
664 for (int m = 0; m < ChunkSizeZ; m++) {
665 int num4 = Terrain.ReplaceLight(chunk.GetCellValueFast(m, k, l), 0);
666 if (num2 == 0) {
667 num3 = num4;
668 num2 = 1;
669 continue;
670 }
671 if (num4 != num3) {
672 num = WriteRleValueToBuffer(m_compressBuffer, num, num3, num2);
673 num3 = num4;
674 num2 = 1;
675 continue;
676 }
677 num2++;
678 if (num2 == 271) {
679 num = WriteRleValueToBuffer(m_compressBuffer, num, num3, num2);
680 num2 = 0;
681 }
682 }
683 }
684 }
685 if (num2 > 0) {
686 num = WriteRleValueToBuffer(m_compressBuffer, num, num3, num2);
687 }
688 return Deflate(m_compressBuffer, 0, num, buffer);
689 }
690
691 public virtual void DecompressChunkData(TerrainChunk chunk, byte[] buffer, int size) {
692 size = UnDeflate(buffer, 0, size, m_compressBuffer);
693 int num = 0;
694 for (int i = 0; i < ChunkSizeX; i++) {
695 for (int j = 0; j < ChunkSizeZ; j++) {
696 byte b = m_compressBuffer[num++];
697 int value = Terrain.ReplaceTemperature(Terrain.ReplaceHumidity(0, b & 0xF), b >> 4);
698 chunk.SetShaftValueFast(i, j, value);
699 }
700 }
701 int num2 = 0;
702 int num3 = 0;
703 int num4 = 0;
704 while (num < size) {
705 num = ReadRleValueFromBuffer(m_compressBuffer, num, out int value2, out int count);
706 for (int k = 0; k < count; k++) {
707 chunk.SetCellValueFast(num2, num3, num4, value2);
708 num2++;
709 if (num2 >= ChunkSizeX) {
710 num2 = 0;
711 num4++;
712 if (num4 >= ChunkSizeZ) {
713 num4 = 0;
714 num3++;
715 }
716 }
717 }
718 }
719 if (num2 != 0
720 || num3 != ChunkSizeY
721 || num4 != 0) {
722 throw new InvalidOperationException("Corrupt chunk data.");
723 }
724 }
725
726 [MethodImpl(MethodImplOptions.AggressiveInlining)]
727 public static int ReadIntFromBuffer(byte[] buffer, int i) => buffer[i] + (buffer[i + 1] << 8) + (buffer[i + 2] << 16) + (buffer[i + 3] << 24);
728
729 [MethodImpl(MethodImplOptions.AggressiveInlining)]
730 public static int ReadRleValueFromBuffer(byte[] buffer, int i, out int value, out int count) {
731 int value2 = ReadIntFromBuffer(buffer, i);
732 int num = Terrain.ExtractLight(value2);
733 value = Terrain.ReplaceLight(value2, 0);
734 if (num < 15) {
735 count = num + 1;
736 return i + 4;
737 }
738 count = buffer[i + 4] + 16;
739 return i + 5;
740 }
741
742 [MethodImpl(MethodImplOptions.AggressiveInlining)]
743 public static void WriteIntToBuffer(byte[] buffer, int i, int data) {
744 buffer[i] = (byte)data;
745 buffer[i + 1] = (byte)(data >> 8);
746 buffer[i + 2] = (byte)(data >> 16);
747 buffer[i + 3] = (byte)(data >> 24);
748 }
749
750 [MethodImpl(MethodImplOptions.AggressiveInlining)]
751 public static int WriteRleValueToBuffer(byte[] buffer, int i, int value, int count) {
752 if (count < 16) {
753 int data = Terrain.ReplaceLight(value, count - 1);
754 WriteIntToBuffer(buffer, i, data);
755 return i + 4;
756 }
757 if (count <= 271) {
758 int data2 = Terrain.ReplaceLight(value, 15);
759 WriteIntToBuffer(buffer, i, data2);
760 buffer[i + 4] = (byte)(count - 16);
761 return i + 5;
762 }
763 throw new InvalidOperationException("Count too large.");
764 }
765
766 public static int Deflate(byte[] input, int offset, int length, byte[] output) {
767 MemoryStream memoryStream = new(input, offset, length);
768 MemoryStream memoryStream2 = new(output);
769 using (DeflateStream destination = new(memoryStream2, CompressionLevel.Fastest, true)) {
770 memoryStream.CopyTo(destination);
771 }
772 return (int)memoryStream2.Position;
773 }
774
775 public static int UnDeflate(byte[] input, int offset, int length, byte[] output) {
776 MemoryStream stream = new(input, offset, length);
777 MemoryStream memoryStream = new(output);
778 using (DeflateStream deflateStream = new(stream, CompressionMode.Decompress)) {
779 deflateStream.CopyTo(memoryStream);
780 }
781 return (int)memoryStream.Position;
782 }
783 }
784}
static void Dispatch(Action action, bool waitUntilCompleted=false)
static void Error(object message)
定义 Log.cs:80
static void Information(object message)
定义 Log.cs:56
static void MoveFile(string sourcePath, string destinationPath)
static void CreateDirectory(string path)
static IEnumerable< string > ListFileNames(string path)
static string ChangeExtension(string path, string extension)
static Stream OpenFile(string path, OpenFileMode openFileMode)
static string GetExtension(string path)
static bool FileExists(string path)
static void DeleteFile(string path)
static string CombinePaths(params string[] paths)
static double RealTime
定义 Time.cs:38
static void ShowDialog(ContainerWidget parentWidget, Dialog dialog)
static string MakeFullErrorMessage(Exception e)
static void DisposeProject()
static void SwitchScreen(string name, params object[] parameters)
virtual void SetShaftValueFast(int x, int z, int value)
TerrainChunkState State
virtual int GetCellValueFast(int index)
virtual void SetCellValueFast(int x, int y, int z, int value)
virtual int GetShaftValueFast(int x, int z)
static int ExtractTemperature(int value)
static int ReplaceHumidity(int value, int humidity)
static int ExtractLight(int value)
static int ExtractHumidity(int value)
static int ReplaceTemperature(int value, int temperature)
static int ReplaceLight(int value, int light)
static void WriteData(BinaryWriter writer, int offset, byte[] buffer, int size)
virtual void Save(Point2 coords, byte[] buffer, int size)
static DirectoryEntry ReadDirectoryEntry(BinaryReader reader, Point2 chunk)
virtual Stream GetRegionStream(Point2 region, bool createNew)
static DirectoryEntry[] ReadDirectoryEntries(BinaryReader reader)
static void WriteDirectoryEntry(BinaryWriter writer, Point2 chunk, DirectoryEntry entry)
static void ResizeStream(Stream stream, int size)
static void WriteDirectoryEntry(BinaryWriter writer, DirectoryEntry entry)
virtual void Open(string directoryName, string suffix)
virtual int Load(Point2 coords, byte[] buffer)
static DirectoryEntry ReadDirectoryEntry(BinaryReader reader)
static void WriteDirectoryEntries(BinaryWriter writer, DirectoryEntry[] entries)
static int FindNextEntryIndex(DirectoryEntry[] entries, int index)
static void ReadData(BinaryReader reader, int offset, byte[] buffer, int size)
virtual void WriteChunkDescriptor(ChunkDescriptor desc)
virtual void WriteNode(int node, byte[] data, int offset, int size, int nextNode)
virtual void ParseNodeHeader(int node, int nodeHeader, out int dataSize, out int nextNode)
Dictionary< Point2, ChunkDescriptor > ChunkDescriptors
virtual int MakeNodeHeader(int node, int dataSize, int nextNode)
virtual int ReadNode(int node, byte[] data, int offset, out int nextNode)
virtual void Open(string directoryName, string suffix)
virtual void Save(Point2 p, byte[] buffer, int size)
static void WriteIntToBuffer(byte[] buffer, int i, int data)
static int WriteRleValueToBuffer(byte[] buffer, int i, int value, int count)
static int UnDeflate(byte[] input, int offset, int length, byte[] output)
virtual bool LoadChunkData(TerrainChunk chunk)
static int ReadIntFromBuffer(byte[] buffer, int i)
virtual void SaveChunk(TerrainChunk chunk)
static int ReadRleValueFromBuffer(byte[] buffer, int i, out int value, out int count)
virtual bool LoadChunk(TerrainChunk chunk)
virtual void DecompressChunkData(TerrainChunk chunk, byte[] buffer, int size)
TerrainSerializer23(string directoryName, string suffix="")
virtual int CompressChunkData(TerrainChunk chunk, byte[] buffer)
static int Deflate(byte[] input, int offset, int length, byte[] output)
virtual void SaveChunkData(TerrainChunk chunk)
void SetErrorHead(int headLangIndex, int adviceLangIndex)
int Load(Point2 coords, byte[] buffer)
void Save(Point2 coords, byte[] buffer, int size)
void Open(string directoryName, string suffix)
int Offset
int Size