2 using System.Diagnostics;
4 using System.Text.RegularExpressions;
9 using Hjg.Pngcs.Chunks;
13 namespace ShardProcessor
17 //private ILogger Logger { get; set; }
18 const string chunkFile_filter = @"*_*.png";
19 static Regex chunkShardRegex = new Regex(@"(?<X>[\d]+)_(?<Z>[\d]+)\.png", RegexOptions.Singleline);
20 static string mapPath;
21 internal const string _chunkPath = @"Chunks";
24 -Process existing PNGs: Report/Dump contents of Chunk Metadata, as per current version
25 -Grayscale Heightmap extraction from P.Buf heightmap from shards
26 -Extract contents of game's SQLLite map DB, INTO Automap type shards...
27 -Other stuff? chunk fixing / validation?
29 public static void Main(string[ ] args)
31 Console.WriteLine("AUTOMAP Offline Shard processor v0.1");
32 //Called once - thus it can only be in a static constructor.
33 PngChunk.FactoryRegister(PngMetadataChunk.ID, typeof(PngMetadataChunk));
39 private static void ArgsDecoder(string[ ] args)
41 //#1 Path to maps '~/ApplicationData/vintagestory/Map/World_1234567890
42 mapPath = args.Length > 1 ? args[1] : String.Empty;
44 //#0 Command: Heightmaps (Generation from existing shard data)
45 string command = args[0];
66 case @"--protoheader":
71 Console.WriteLine("Unrecognized Command: {0}", command);
79 private static void Process_ShardData( )
81 var shardsDir = new DirectoryInfo( Path.Combine(mapPath , _chunkPath));
83 var shardFiles = shardsDir.GetFiles(chunkFile_filter);
85 if (shardFiles.Length > 0) {
87 //Logger.VerboseDebug("Metadata reloading from {0} shards", shardFiles.Length);
90 foreach (var shardFile in shardFiles) {
92 if (shardFile.Length < 1024) continue;
93 var result = chunkShardRegex.Match(shardFile.Name);
94 if (!result.Success) continue;
96 int X_chunk_pos = int.Parse(result.Groups["X"].Value);
97 int Z_chunk_pos = int.Parse(result.Groups["Z"].Value);
100 using (var fileStream = shardFile.OpenRead( )) {
102 PngReader pngRead = new PngReader(fileStream);
103 pngRead.ReadSkippingAllRows( );
105 //Parse PNG chunks for METADATA in shard
106 PngMetadataChunk metadataFromPng = pngRead.GetChunksList( ).GetById1(PngMetadataChunk.ID) as PngMetadataChunk;
107 ColumnMeta columnData = metadataFromPng.ChunkMetadata;
108 //columnData.HeightMap //Should be sane Heightmap...
114 } catch (PngjException someEx) {
115 //Logger.Error("PNG Corruption file '{0}' - Reason: {1}", shardFile.Name, someEx);
117 } catch (ProtoException protoEx) {
118 //Logger.Error("ProtoBuf invalid! file:'{0}' - Reason: {1}", shardFile.Name, protoEx);
128 private static void Scan_PointsData( )
131 var eoiFile = new FileInfo(Path.Combine(mapPath, @"eoi_binary"));
132 var poiFile = new FileInfo(Path.Combine(mapPath, @"poi_binary"));
133 uint entities = 0, points = 0;
135 if (eoiFile.Exists) {
136 using (var eoiStream = eoiFile.OpenRead( )) {
138 EntitiesOfInterest eoiData = Serializer.Deserialize<EntitiesOfInterest>(eoiStream);
140 foreach (var entry in eoiData) {
141 Console.WriteLine("#{0}, [{1}], '{2}', {3}",
145 entry.Timestamp.ToUniversalTime( )
149 Console.WriteLine("Entities Of Interest: {0}", entities);
153 if (poiFile.Exists) {
154 using (var poiStream = poiFile.OpenRead( )) {
156 PointsOfInterest poiData = Serializer.Deserialize<PointsOfInterest>(poiStream);
157 foreach (var entry in poiData) {
158 Console.WriteLine("[{0}], {1}, {2}, {3}",
162 entry.Timestamp.ToUniversalTime( )
167 Console.WriteLine("Points Of Interest: {0}", points);
171 } catch (Exception uhOh) {
172 Console.WriteLine(uhOh);
177 private static void Scan_ShardData( )
179 var shardsDir = new DirectoryInfo(Path.Combine(mapPath, _chunkPath));
180 ulong count = 0,errors = 0, flat = 0;
181 var shardFiles = shardsDir.GetFiles(chunkFile_filter);
183 if (shardFiles.Length > 0) {
185 //Logger.VerboseDebug("Metadata reloading from {0} shards", shardFiles.Length);
188 foreach (var shardFile in shardFiles) {
190 if (shardFile.Length < 1024) {
191 Console.WriteLine("File: '{0}' too small to be valid; skipping!", shardFile.FullName);
196 var result = chunkShardRegex.Match(shardFile.Name);
197 if (!result.Success) continue;
199 int X_chunk_pos = int.Parse(result.Groups["X"].Value);
200 int Z_chunk_pos = int.Parse(result.Groups["Z"].Value);
203 using (var fileStream = shardFile.OpenRead( )) {
205 PngReader pngRead = new PngReader(fileStream);
206 pngRead.ReadSkippingAllRows( );
208 //Parse PNG chunks for METADATA in shard
209 PngMetadataChunk metadataFromPng = pngRead.GetChunksList( ).GetById1(PngMetadataChunk.ID) as PngMetadataChunk;
210 ColumnMeta columnData = metadataFromPng.ChunkMetadata;
212 Console.Write("X{0,6:D} Y{1,6:D} Age:{2:N1} ", columnData.Location.X, columnData.Location.Y, columnData.ChunkAge.TotalDays);
213 Console.Write("YMax:{0:D3} ChkS:{1} Air:{2,7:D} NotAir:{3,7:D} ",
214 columnData.YMax,columnData.ChunkSize , columnData.AirBlocks, columnData.NonAirBlocks
216 if (columnData.HeightMap != null) {
217 Console.Write("(Heights [{0}x{1}] ", columnData.HeightMap.GetLength(0), columnData.HeightMap.GetLength(1));
218 ushort lowest = ushort.MaxValue, highest = 0;
220 foreach (var hmEntry in columnData.HeightMap) {
221 lowest = Math.Min(lowest, hmEntry);
222 highest = Math.Max(highest, hmEntry);
225 Console.Write("Max:{0,3}, Min:{1,3}, ", highest, lowest);
226 if (sum > 0) Console.Write("Avg:{0:F1})", ( float )sum / (columnData.ChunkSize * columnData.ChunkSize));
227 Console.WriteLine( );
228 /*------ROCK RATIOs mini-table----------*/
229 if (columnData.RockRatio != null && columnData.RockRatio.Count > 0) {
230 Console.Write("Ratios({0,2:D})[",columnData.RockRatio.Count);
231 foreach (var rock in columnData.RockRatio) {
232 Console.Write("ID:{0,5:D} x{1,4:D}, ", rock.Key, rock.Value);
234 Console.Write(" ]\n");
237 if ( sum == 0 || columnData.YMax == 0) flat++;
246 } catch (PngjException someEx) {
247 Console.WriteLine("PNG Corruption file '{0}' - Reason: {1}", shardFile.Name, someEx);
250 } catch (ProtoException protoEx) {
251 Console.WriteLine("ProtoBuf invalid! file:'{0}' - Reason: {1}", shardFile.Name, protoEx);
259 Console.WriteLine("Scanned {0} files, {1} errors, {2} FLAT entries", count, errors, flat);
262 private static void Scan_OneShard( )
264 //--oneshard ~/ApplicationData/vintagestory/Maps/World_1316328588/Chunks/9363_9379.png
265 var oneChunkFile = new FileInfo(mapPath);
266 if (oneChunkFile.Exists) {
270 using (var fileStream = oneChunkFile.OpenRead( )) {
272 PngReader pngRead = new PngReader(fileStream);
273 pngRead.ReadSkippingAllRows( );
275 //Parse PNG chunks for METADATA in shard
276 PngMetadataChunk metadataFromPng = pngRead.GetChunksList( ).GetById1(PngMetadataChunk.ID) as PngMetadataChunk;
277 ColumnMeta columnData = metadataFromPng.ChunkMetadata;
278 var metadata = pngRead.GetMetadata( );
279 var pngWriteTime = metadata.GetTime( );
280 var chunkX = metadata.GetTxtForKey(@"Chunk_X");
281 var chunkY = metadata.GetTxtForKey(@"Chunk_Y");
282 var pixelSize = metadata.GetTxtForKey(@"PxSz");
283 var gameDate = metadata.GetTxtForKey(@"GameDY");
284 var dateBlob = pngWriteTime.GetYMDHMS( );
295 Console.WriteLine($"PNG-Timestamp: Y{dateBlob[0] - 456960} M{dateBlob[1]} D{dateBlob[2]} H{dateBlob[3]} M {dateBlob[4]} S{dateBlob[5]} Chunk: X {chunkX} Y {chunkY} PixelSize:{pixelSize} Game-Date: {gameDate}");
299 } catch (Exception darn) {
300 Debug.Write("Oops! File causes: {0}", darn.ToString( ));
306 private static void Emit_ProtoHeader( )
308 Console.WriteLine("Created Protobuf Header files.");
309 using (var entitiesProto = File.CreateText("Entities.protoc"))
311 entitiesProto.Write(Serializer.GetProto<EntitiesOfInterest>( ));
312 entitiesProto.Flush( );
316 using (var pointsProto = File.CreateText("Points.protoc")) {
317 pointsProto.Write(Serializer.GetProto<PointsOfInterest>( ));
318 pointsProto.Flush( );