2 using System.Diagnostics;
5 using System.Text.RegularExpressions;
10 using Hjg.Pngcs.Chunks;
13 using Vintagestory.API.Common;
14 using Vintagestory.API.MathTools;
15 using Vintagestory.GameContent;
17 namespace ShardProcessor
19 public partial class MainClass
21 internal const int chunkDefaultSize = 32;
23 private static void Process_ShardData( )
25 var shardsDir = new DirectoryInfo(Path.Combine(mapPath, _chunkPath));
27 var shardFiles = shardsDir.GetFiles(chunkFile_filter);
29 if (shardFiles.Length > 0) {
31 //Logger.VerboseDebug("Metadata reloading from {0} shards", shardFiles.Length);
34 foreach (var shardFile in shardFiles) {
36 if (shardFile.Length < 1024) continue;
37 var result = chunkShardRegex.Match(shardFile.Name);
38 if (!result.Success) continue;
40 int X_chunk_pos = int.Parse(result.Groups["X"].Value);
41 int Z_chunk_pos = int.Parse(result.Groups["Z"].Value);
44 using (var fileStream = shardFile.OpenRead( )) {
46 PngReader pngRead = new PngReader(fileStream);
47 pngRead.ReadSkippingAllRows( );
49 //Parse PNG chunks for METADATA in shard
50 PngMetadataChunk metadataFromPng = pngRead.GetChunksList( ).GetById1(PngMetadataChunk.ID) as PngMetadataChunk;
51 ColumnMeta columnData = metadataFromPng.ChunkMetadata;
52 //columnData.HeightMap //Should be sane Heightmap...
58 } catch (PngjException someEx) {
59 //Logger.Error("PNG Corruption file '{0}' - Reason: {1}", shardFile.Name, someEx);
61 } catch (ProtoException protoEx) {
62 //Logger.Error("ProtoBuf invalid! file:'{0}' - Reason: {1}", shardFile.Name, protoEx);
72 private static void Scan_PointsData( )
75 var eoiFile = new FileInfo(Path.Combine(mapPath, @"eoi_binary"));
76 var poiFile = new FileInfo(Path.Combine(mapPath, @"poi_binary"));
77 uint entities = 0, points = 0;
80 using (var eoiStream = eoiFile.OpenRead( )) {
82 EntitiesOfInterest eoiData = Serializer.Deserialize<EntitiesOfInterest>(eoiStream);
84 foreach (var entry in eoiData) {
85 Console.WriteLine("#{0}, [{1}], '{2}', {3}",
89 entry.Timestamp.ToUniversalTime( )
93 Console.WriteLine("Entities Of Interest: {0}", entities);
98 using (var poiStream = poiFile.OpenRead( )) {
100 PointsOfInterest poiData = Serializer.Deserialize<PointsOfInterest>(poiStream);
101 foreach (var entry in poiData) {
102 Console.WriteLine("[{0}], {1}, {2}, {3}",
106 entry.Timestamp.ToUniversalTime( )
111 Console.WriteLine("Points Of Interest: {0}", points);
115 } catch (Exception uhOh) {
116 Console.WriteLine(uhOh);
121 private static void Scan_ShardData( )
123 var shardsDir = new DirectoryInfo(Path.Combine(mapPath, _chunkPath));
124 ulong count = 0, errors = 0, flat = 0;
125 var shardFiles = shardsDir.GetFiles(chunkFile_filter);
127 if (shardFiles.Length > 0) {
129 //Logger.VerboseDebug("Metadata reloading from {0} shards", shardFiles.Length);
132 foreach (var shardFile in shardFiles) {
134 if (shardFile.Length < 1024) {
135 Console.WriteLine("File: '{0}' too small to be valid; skipping!", shardFile.FullName);
140 var result = chunkShardRegex.Match(shardFile.Name);
141 if (!result.Success) continue;
143 int X_chunk_pos = int.Parse(result.Groups["X"].Value);
144 int Z_chunk_pos = int.Parse(result.Groups["Z"].Value);
147 using (var fileStream = shardFile.OpenRead( )) {
149 PngReader pngRead = new PngReader(fileStream);
150 pngRead.ReadSkippingAllRows( );
152 //Parse PNG chunks for METADATA in shard
153 PngMetadataChunk metadataFromPng = pngRead.GetChunksList( ).GetById1(PngMetadataChunk.ID) as PngMetadataChunk;
154 ColumnMeta columnData = metadataFromPng.ChunkMetadata;
156 Console.Write("X{0,6:D} Y{1,6:D} Age:{2:N1} ", columnData.Location.X, columnData.Location.Y, columnData.ChunkAge.TotalDays);
157 Console.Write("YMax:{0:D3} ChkS:{1} Air:{2,7:D} NotAir:{3,7:D} ",
158 columnData.YMax, columnData.ChunkSize, columnData.AirBlocks, columnData.NonAirBlocks
160 if (columnData.HeightMap != null) {
161 Console.Write("(Heights [{0}x{1}] ", columnData.HeightMap.GetLength(0), columnData.HeightMap.GetLength(1));
162 ushort lowest = ushort.MaxValue, highest = 0;
164 foreach (var hmEntry in columnData.HeightMap) {
165 lowest = Math.Min(lowest, hmEntry);
166 highest = Math.Max(highest, hmEntry);
169 Console.Write("Max:{0,3}, Min:{1,3}, ", highest, lowest);
170 if (sum > 0) Console.Write("Avg:{0:F1})", ( float )sum / (columnData.ChunkSize * columnData.ChunkSize));
171 Console.WriteLine( );
172 /*------ROCK RATIOs mini-table----------*/
173 if (columnData.RockRatio != null && columnData.RockRatio.Count > 0) {
174 Console.Write("Ratios({0,2:D})[", columnData.RockRatio.Count);
175 foreach (var rock in columnData.RockRatio) {
176 Console.Write("ID:{0,5:D} x{1,4:D}, ", rock.Key, rock.Value);
178 Console.Write(" ]\n");
181 if (sum == 0 || columnData.YMax == 0) flat++;
190 } catch (PngjException someEx) {
191 Console.WriteLine("PNG Corruption file '{0}' - Reason: {1}", shardFile.Name, someEx);
194 } catch (ProtoException protoEx) {
195 Console.WriteLine("ProtoBuf invalid! file:'{0}' - Reason: {1}", shardFile.Name, protoEx);
203 Console.WriteLine("Scanned {0} files, {1} errors, {2} FLAT entries", count, errors, flat);
206 private static void Scan_OneShard( )
208 //--oneshard ~/ApplicationData/vintagestory/Maps/World_1316328588/Chunks/9363_9379.png
209 var oneChunkFile = new FileInfo(mapPath);
210 if (oneChunkFile.Exists) {
214 using (var fileStream = oneChunkFile.OpenRead( )) {
216 PngReader pngRead = new PngReader(fileStream);
217 pngRead.ReadSkippingAllRows( );
219 //Parse PNG chunks for METADATA in shard
220 PngMetadataChunk metadataFromPng = pngRead.GetChunksList( ).GetById1(PngMetadataChunk.ID) as PngMetadataChunk;
221 ColumnMeta columnData = metadataFromPng.ChunkMetadata;
222 var metadata = pngRead.GetMetadata( );
223 var pngWriteTime = metadata.GetTime( );
224 var chunkX = metadata.GetTxtForKey(@"Chunk_X");
225 var chunkY = metadata.GetTxtForKey(@"Chunk_Y");
226 var pixelSize = metadata.GetTxtForKey(@"PxSz");
227 var gameDate = metadata.GetTxtForKey(@"GameDY");
228 var dateBlob = pngWriteTime.GetYMDHMS( );
239 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}");
243 } catch (Exception darn) {
244 Debug.Write("Oops! File causes: {0}", darn.ToString( ));
250 private static void Emit_ProtoHeader( )
252 Console.WriteLine("Created Protobuf Header files.");
253 using (var entitiesProto = File.CreateText("Entities.proto")) {
254 entitiesProto.Write(Serializer.GetProto<EntitiesOfInterest>( ));
255 entitiesProto.Flush( );
259 using (var pointsProto = File.CreateText("Points.proto")) {
260 pointsProto.Write(Serializer.GetProto<PointsOfInterest>( ));
261 pointsProto.Flush( );
264 using (var metadataProto = File.CreateText("ColumnMeta.proto")) {
265 metadataProto.Write(Serializer.GetProto<ColumnMeta>( ));
266 metadataProto.Flush( );
270 private static void Dump_Minimap( )
272 //Extract MapDB -> Shard compatible PNG?
273 var logger = new LogAdaptor( );
274 WalkableMapDB minimapDatabase = new WalkableMapDB(logger);
275 string outmsg = string.Empty;
276 logger.Event("Started Logging @{0}", DateTimeOffset.UtcNow.ToString("u"));
277 Console.WriteLine("Starting to Dump Minimap data");
278 var tilesPath = Path.GetDirectoryName(mapPath);
279 Directory.CreateDirectory(Path.Combine(tilesPath, _minimapTilesPath));
281 if (minimapDatabase.OpenOrCreate(mapPath, ref outmsg, false, false, false)) {
283 foreach (var mapPiece in minimapDatabase.WalkMapTiles( )) {
284 logger.VerboseDebug("ScanDB Tile - X:{0} Y:{1}, Bitmap Int#{2}", mapPiece.ChunkPos.X, mapPiece.ChunkPos.Y, mapPiece.Pixels.Length);
285 MinimalShardWriter(mapPiece.ChunkPos, mapPiece.Pixels, tilesPath,logger);
290 logger.Error("Failed to access Minimap Database: '{0}'", outmsg);
293 Console.WriteLine("DONE Dumping Minimap data!");
296 private static void MinimalShardWriter(Vec2i coord, int[] pixelData, string tilesPath, ILogger logger )
298 ImageInfo imageInf = new ImageInfo(chunkDefaultSize, chunkDefaultSize, 8, false);
300 string filename = $"{coord.X}_{coord.Y}.png";
301 filename = Path.Combine(tilesPath, _minimapTilesPath, filename);
303 var PngWriter = FileHelper.CreatePngWriter(filename, imageInf, true);
304 PngMetadata meta = PngWriter.GetMetadata( );
306 DateTime.UtcNow.Year,
307 DateTime.UtcNow.Month,
309 DateTime.UtcNow.Hour,
310 DateTime.UtcNow.Minute,
311 DateTime.UtcNow.Second);
312 meta.SetText("Chunk_X", coord.X.ToString("D"));
313 meta.SetText("Chunk_Y", coord.Y.ToString("D"));
314 meta.SetText("PxSz", "1");
316 var transparencyChunk = meta.CreateTRNSChunk( );
317 transparencyChunk.SetRGB(0, 0, 0);//Same as Snapshots
319 var minimalMetadata = new ColumnMeta(coord);
321 //Setup specialized meta-data PNG chunks here...
322 PngMetadataChunk pngChunkMeta = new PngMetadataChunk(PngWriter.ImgInfo) {
323 ChunkMetadata = minimalMetadata
325 PngWriter.GetChunksList( ).Queue(pngChunkMeta);
326 PngWriter.CompLevel = 5;// 9 is the maximum compression but thats too high for the small benefit it gives
327 PngWriter.CompressionStrategy = Hjg.Pngcs.Zlib.EDeflateCompressStrategy.Huffman;
329 //pre-create PNG line slices...
330 ImageLine[ ] lines = Enumerable.Repeat(new object( ), chunkDefaultSize).Select(l => new ImageLine(PngWriter.ImgInfo)).ToArray( );
332 Vec2i pixelPosn = new Vec2i();
333 for (int pixelIndex = 0; pixelIndex < (chunkDefaultSize * chunkDefaultSize); pixelIndex++) {
334 MapUtil.PosInt2d(pixelIndex, chunkDefaultSize, pixelPosn);
335 int red, green, blue;
336 red = ColorUtil.ColorB(pixelData[pixelIndex]);
337 green = ColorUtil.ColorG(pixelData[pixelIndex]);
338 blue = ColorUtil.ColorR(pixelData[pixelIndex]);
340 ImageLineHelper.SetPixel(lines[pixelPosn.Y], pixelPosn.X, red, green, blue);
345 for (int row = 0; row < PngWriter.ImgInfo.Rows; row++) {
346 PngWriter.WriteRow(lines[row], row);
350 logger.Debug("Wrote mini map tile: {0}", coord);