122 lines
4 KiB
C#
122 lines
4 KiB
C#
using System;
|
|
using System.Buffers.Binary;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.IO.Compression;
|
|
using System.Text;
|
|
|
|
namespace SmartNav.Model.Navigation;
|
|
|
|
public static class WaterCacheFormat
|
|
{
|
|
public const uint FileMagic = 1129469015u;
|
|
|
|
public const int FileFormatVersion = 1;
|
|
|
|
public const string FileName = "water-cache.bin";
|
|
|
|
private const int MaxTerritoryCount = 65536;
|
|
|
|
private const int ClassBytes = 65536;
|
|
|
|
private const int SurfaceBytes = 131072;
|
|
|
|
private const int PayloadBytes = 196608;
|
|
|
|
public static void Write(Stream stream, string gameVersion, IReadOnlyList<TerritoryWaterMap> maps)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(stream, "stream");
|
|
ArgumentNullException.ThrowIfNull(gameVersion, "gameVersion");
|
|
ArgumentNullException.ThrowIfNull(maps, "maps");
|
|
List<TerritoryWaterMap> list = new List<TerritoryWaterMap>(maps.Count);
|
|
foreach (TerritoryWaterMap map in maps)
|
|
{
|
|
if (map.HasWater)
|
|
{
|
|
list.Add(map);
|
|
}
|
|
}
|
|
list.Sort((TerritoryWaterMap left, TerritoryWaterMap right) => left.TerritoryId.CompareTo(right.TerritoryId));
|
|
using BinaryWriter binaryWriter = new BinaryWriter(stream, Encoding.UTF8, leaveOpen: true);
|
|
binaryWriter.Write(1129469015u);
|
|
binaryWriter.Write(1);
|
|
binaryWriter.Write(gameVersion);
|
|
binaryWriter.Write(list.Count);
|
|
foreach (TerritoryWaterMap item in list)
|
|
{
|
|
byte[] array = new byte[196608];
|
|
item.Classes.CopyTo(array.AsSpan(0, 65536));
|
|
ReadOnlySpan<ushort> surfaceYRaw = item.SurfaceYRaw;
|
|
for (int num = 0; num < surfaceYRaw.Length; num++)
|
|
{
|
|
BinaryPrimitives.WriteUInt16LittleEndian(array.AsSpan(65536 + num * 2), surfaceYRaw[num]);
|
|
}
|
|
using MemoryStream memoryStream = new MemoryStream();
|
|
using (DeflateStream deflateStream = new DeflateStream(memoryStream, CompressionLevel.Optimal, leaveOpen: true))
|
|
{
|
|
deflateStream.Write(array);
|
|
}
|
|
binaryWriter.Write(item.TerritoryId);
|
|
binaryWriter.Write((int)memoryStream.Length);
|
|
binaryWriter.Write(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
|
|
}
|
|
}
|
|
|
|
public static (string GameVersion, List<TerritoryWaterMap> Maps) Read(Stream stream)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(stream, "stream");
|
|
using BinaryReader binaryReader = new BinaryReader(stream, Encoding.UTF8, leaveOpen: true);
|
|
uint num = binaryReader.ReadUInt32();
|
|
if (num != 1129469015)
|
|
{
|
|
throw new InvalidDataException($"water cache has magic 0x{num:X8}, expected 0x{1129469015:X8}");
|
|
}
|
|
int num2 = binaryReader.ReadInt32();
|
|
if (num2 != 1)
|
|
{
|
|
throw new InvalidDataException($"water cache has format version {num2}, expected {1}");
|
|
}
|
|
string item = binaryReader.ReadString();
|
|
int num3 = binaryReader.ReadInt32();
|
|
if (num3 < 0 || num3 > 65536)
|
|
{
|
|
throw new InvalidDataException($"water cache declares {num3} territories");
|
|
}
|
|
List<TerritoryWaterMap> list = new List<TerritoryWaterMap>(num3);
|
|
for (int i = 0; i < num3; i++)
|
|
{
|
|
ushort num4 = binaryReader.ReadUInt16();
|
|
int num5 = binaryReader.ReadInt32();
|
|
if (num5 < 0)
|
|
{
|
|
throw new InvalidDataException($"territory {num4} declares {num5} compressed bytes");
|
|
}
|
|
byte[] array = binaryReader.ReadBytes(num5);
|
|
if (array.Length != num5)
|
|
{
|
|
throw new InvalidDataException($"territory {num4} is truncated: {array.Length} of {num5} bytes");
|
|
}
|
|
byte[] array2 = new byte[196608];
|
|
using (DeflateStream deflateStream = new DeflateStream(new MemoryStream(array, writable: false), CompressionMode.Decompress))
|
|
{
|
|
deflateStream.ReadExactly(array2);
|
|
}
|
|
byte[] subArray = array2[..65536];
|
|
byte[] array3 = subArray;
|
|
foreach (byte b in array3)
|
|
{
|
|
if (b > 2)
|
|
{
|
|
throw new InvalidDataException($"territory {num4} has cell class {b}, expected 0..{2}");
|
|
}
|
|
}
|
|
ushort[] array4 = new ushort[65536];
|
|
for (int k = 0; k < array4.Length; k++)
|
|
{
|
|
array4[k] = BinaryPrimitives.ReadUInt16LittleEndian(array2.AsSpan(65536 + k * 2));
|
|
}
|
|
list.Add(new TerritoryWaterMap(num4, subArray, array4));
|
|
}
|
|
return (GameVersion: item, Maps: list);
|
|
}
|
|
}
|