v2 tagged-section container (D-030): raw ISLA magic + u32 version gate, [u32 tag][u64 length][payload] sections — params (resolved generation inputs incl. impact centre), heights f32, biomes u8, towns with the highway-node flag, four road tiers identified by tag not position. - BlueprintFormat.cs: the single tag/constant registry. - BlueprintWriter.WriteV2: blueprint-typed, callable outside a generation run; generator and harness are both just callers. - ExportMapData dual-writes: v2 under the primary seed name, legacy v1 beside it as _v1.dat (safety net; removal is a future task). - MapDataParser: LoadMapDataFromPath entry point; v1 parse body extracted intact as LoadV1 (v2 reader lands in the next commit). - RoundTripHarness scene: load v1 -> write v2 -> load v2 -> semantic equality, headless, seconds per cycle. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
54 lines
2.5 KiB
C#
54 lines
2.5 KiB
C#
namespace IslaApocalypse.Core
|
|
{
|
|
/// <summary>
|
|
/// The single registry of constants for the v2 blueprint container — magic, version,
|
|
/// section tags, validation bounds, and the sentinel values used when a v2 file is
|
|
/// re-encoded from a v1 source that cannot supply real generation parameters.
|
|
///
|
|
/// The byte-accurate layout lives in Core/Scripts/BLUEPRINT_FORMAT.md. Writer:
|
|
/// BlueprintWriter.WriteV2. Reader: MapDataParser (v2 branch). Tags are FourCC codes
|
|
/// stored as little-endian u32, so the raw bytes on disk read as ASCII ("PRMS", "HGTS",
|
|
/// ...) in a hex dump.
|
|
/// </summary>
|
|
public static class BlueprintFormat
|
|
{
|
|
// "ISLA" as raw bytes 'I','S','L','A' == little-endian u32 0x414C5349.
|
|
// Deliberately NOT a length-prefixed .NET string: the v1 header starts with the
|
|
// 7-bit length prefix 0x07, so the first byte alone (0x49 vs 0x07) identifies the
|
|
// format and the reader can dispatch without ambiguity.
|
|
public const uint MAGIC = 0x414C5349;
|
|
public const uint VERSION = 2;
|
|
|
|
// Section tags (FourCC, little-endian). Reader rule: known tag -> parse,
|
|
// unknown tag -> skip by length and continue. Never reuse a retired tag value.
|
|
public const uint TAG_PARAMS = 0x534D5250; // "PRMS"
|
|
public const uint TAG_HEIGHTS = 0x53544748; // "HGTS"
|
|
public const uint TAG_BIOMES = 0x4D4F4942; // "BIOM"
|
|
public const uint TAG_TOWNS = 0x4E574F54; // "TOWN"
|
|
public const uint TAG_ROADS_HIGHWAY = 0x57484452; // "RDHW"
|
|
public const uint TAG_ROADS_BRANCH = 0x52424452; // "RDBR"
|
|
public const uint TAG_ROADS_RUGGED = 0x47524452; // "RDRG"
|
|
public const uint TAG_ROADS_TRAIL = 0x4C544452; // "RDTL"
|
|
|
|
// MapSize sanity bounds, checked before any allocation on read.
|
|
public const int MIN_MAP_SIZE = 256;
|
|
public const int MAX_MAP_SIZE = 32768;
|
|
|
|
// Sentinels written into the params section when the source blueprint came from a
|
|
// v1 file (a re-encode cannot know the original generation inputs). WorldSeed 0 is
|
|
// also the config "randomize" sentinel, which no real resolved seed ever is.
|
|
public const int SENTINEL_WORLD_SEED = 0;
|
|
public const float SENTINEL_CRATER_RADIUS = -1f;
|
|
public const float SENTINEL_DENSITY_MULTIPLIER = -1f;
|
|
public const float SENTINEL_IMPACT_CENTER = -1f; // both components
|
|
|
|
/// <summary>Renders a tag as its 4 ASCII characters, for log messages.</summary>
|
|
public static string TagToString(uint tag)
|
|
{
|
|
return new string(new[] {
|
|
(char)(tag & 0xFF), (char)((tag >> 8) & 0xFF),
|
|
(char)((tag >> 16) & 0xFF), (char)((tag >> 24) & 0xFF)
|
|
});
|
|
}
|
|
}
|
|
}
|