Decode MPINIT stage terrain atlas

This commit is contained in:
gamer147
2026-07-23 21:31:43 -04:00
parent 3a46b2236a
commit 9646b23ae2
11 changed files with 703 additions and 25 deletions

View File

@@ -25,6 +25,10 @@ levels, joined to the runtime condition-state ABI and RECOVER policy.
CVINIT is a special numeric-mode registry: thirteen voice-configuration preview
slots, twelve slot-to-unit joins, and the matching unit-to-setting inverse map.
MPINIT is a special footer-mode terrain atlas: each footer copy owns the fifty
authored cells of one 53-cell half-tile grid row. STINIT2's per-stage tile
bounds select rectangles after multiplying both coordinates by two.
Records are {id, name?, desc?, fields:{"0x<col_base>": value}} or, for footer tables,
{id, global_addr, footer_off, values:[...]}. Column addresses are raw engine globals;
confirmed names come from the generated engine global registry while raw keys remain provenance.
@@ -644,6 +648,23 @@ RECOVER_REMAINING_TURNS = 0x5295F
RECOVER_BASELINE_LEVELS = 0x52F3B
RECOVER_POLICY = 0xAACB4
MAP_TERRAIN_ATLAS_BASE = 0xCCC93
MAP_TERRAIN_CURRENT_BASE = 0x341AB
MAP_GRID_ROW_STRIDE = 53
MAP_GRID_FIRST_COLUMN = 1
MAP_GRID_AUTHORED_COLUMNS = 50
MAP_TILE_TO_GRID_SCALE = 2
MAP_STAGE_MIN_X = 0xEC4DD
MAP_STAGE_MAX_X = 0xEC8C5
MAP_STAGE_MIN_Y = 0xECCAD
MAP_STAGE_MAX_Y = 0xED095
TERRAIN_NAME_BASE = 0x26B5
TERRAIN_TEXTURE_SLOT_BASE = 0xE6AA4
TERRAIN_AREA_FILL_BASE = 0xE6AC2
TERRAIN_LAYOUT_CLASS_BASE = 0xE6AE0
TERRAIN_DEFINITION_SPAN = 30
def resolve(name: str) -> Path:
for cand in (paths.GAME_DIR / f"{name}.BIN", paths.DATA1 / f"{name}.BIN"):
@@ -3291,6 +3312,312 @@ def extract_footer(scr):
return records, {}
def _terrain_definitions(max_terrain_id: int) -> list[dict]:
"""Decode the LAINIT fields consumed by MPINIT's terrain ids."""
terrain_scr = sys4load.load(resolve("LAINIT"))
names: dict[int, str] = {}
arrays = {
"texture_slot_index": (TERRAIN_TEXTURE_SLOT_BASE, {}),
"area_fill_flag": (TERRAIN_AREA_FILL_BASE, {}),
"layout_class": (TERRAIN_LAYOUT_CLASS_BASE, {}),
}
for ins in terrain_scr.instructions:
if (
ins.opcode == SET_STRING
and len(ins.args) >= 2
and ins.args[0][0] == T_GLOBAL_STRING
):
terrain_id = ins.args[0][1] - TERRAIN_NAME_BASE
if 0 <= terrain_id < TERRAIN_DEFINITION_SPAN:
names[terrain_id] = terrain_scr.strings[ins.args[1][1]][0]
write = _static_global_write(ins)
if write is None:
continue
destination, value = write
for _, (base, cells) in arrays.items():
terrain_id = destination - base
if 0 <= terrain_id < TERRAIN_DEFINITION_SPAN:
cells[terrain_id] = value
layout_class_names = {
0: "blocked_or_boundary",
1: "open_area",
2: "passage",
3: "hidden",
}
definitions = []
for terrain_id in range(max_terrain_id + 1):
texture_slot = arrays["texture_slot_index"][1].get(terrain_id, 0)
area_fill = arrays["area_fill_flag"][1].get(terrain_id, 0)
layout_class = arrays["layout_class"][1].get(terrain_id, 0)
definitions.append({
"id": terrain_id,
"name": names.get(terrain_id),
"texture_slot_index": texture_slot,
"area_fill_flag": area_fill,
"layout_class": layout_class,
"layout_class_name": layout_class_names.get(
layout_class, "unknown"
),
})
return definitions
def _map_stage_definitions() -> list[dict]:
"""Read the STINIT2 records that own all four terrain-atlas bounds."""
stage_scr = sys4load.load(resolve("STINIT2"))
stage_records, _ = extract_name(stage_scr)
bounds = (
MAP_STAGE_MIN_X,
MAP_STAGE_MAX_X,
MAP_STAGE_MIN_Y,
MAP_STAGE_MAX_Y,
)
definitions = []
for record in stage_records:
fields = record.get("fields", {})
keys = [f"0x{address:x}" for address in bounds]
if not all(key in fields for key in keys):
continue
min_x, max_x, min_y, max_y = (fields[key] for key in keys)
definitions.append({
"id": record["id"],
"name": record.get("name", ""),
"tile_bounds": {
"min_x": min_x,
"max_x": max_x,
"min_y": min_y,
"max_y": max_y,
},
"grid_bounds": {
"min_x": min_x * MAP_TILE_TO_GRID_SCALE,
"max_x": max_x * MAP_TILE_TO_GRID_SCALE,
"min_y": min_y * MAP_TILE_TO_GRID_SCALE,
"max_y": max_y * MAP_TILE_TO_GRID_SCALE,
},
})
return definitions
def extract_map_terrain_atlas(scr):
"""Extract MPINIT's sparse 53-column, doubled-coordinate terrain atlas."""
rows = []
rows_by_y: dict[int, list[int]] = {}
classified_offsets = set()
for ins in scr.instructions:
if (
ins.opcode != COPY_LOCAL_ARRAY
or len(ins.args) < 2
or ins.args[0][0] != T_GLOBAL_INT
):
continue
destination = ins.args[0][1]
footer_off = ins.args[1][1]
values = read_footer_array(scr, footer_off)
if values is None:
raise ValueError(
f"{scr.path.name}: invalid terrain row footer 0x{footer_off:x}"
)
delta = destination - MAP_TERRAIN_ATLAS_BASE
grid_y, grid_x = divmod(delta, MAP_GRID_ROW_STRIDE)
if grid_x != MAP_GRID_FIRST_COLUMN:
raise ValueError(
f"{scr.path.name}: terrain row at 0x{destination:x} starts "
f"in grid column {grid_x}, expected {MAP_GRID_FIRST_COLUMN}"
)
if len(values) != MAP_GRID_AUTHORED_COLUMNS:
raise ValueError(
f"{scr.path.name}: terrain row {grid_y} has {len(values)} "
f"cells, expected {MAP_GRID_AUTHORED_COLUMNS}"
)
if grid_y in rows_by_y:
raise ValueError(
f"{scr.path.name}: duplicate terrain row {grid_y}"
)
rows_by_y[grid_y] = values
classified_offsets.add(ins.offset)
rows.append({
"id": grid_y,
"grid_y": grid_y,
"grid_x": grid_x,
"global_addr": f"0x{destination:x}",
"footer_off": f"0x{footer_off:x}",
"length": len(values),
"values": values,
"nonzero_cell_count": sum(value != 0 for value in values),
"terrain_ids_used": sorted(set(values) - {0}),
})
exit_offsets = {
ins.offset
for ins in scr.instructions
if sys4load.display_label(ins.opcode) == "exit"
}
classified_offsets.update(exit_offsets)
unclassified = [
f"0x{ins.offset:x}"
for ins in scr.instructions
if ins.offset not in classified_offsets
]
if unclassified:
raise ValueError(
f"{scr.path.name}: unclassified instructions "
+ ", ".join(unclassified)
)
if not rows:
raise ValueError(f"{scr.path.name}: no terrain rows")
max_terrain_id = max(
value for values in rows_by_y.values() for value in values
)
terrain_definitions = _terrain_definitions(max_terrain_id)
terrain_names = {
definition["id"]: definition["name"]
for definition in terrain_definitions
}
stage_maps = []
rectangle_stage_ids: dict[
tuple[int, int, int, int], list[int]
] = collections.defaultdict(list)
covered_nonzero_cells = set()
for stage in _map_stage_definitions():
bounds = stage["grid_bounds"]
min_x = bounds["min_x"]
max_x = bounds["max_x"]
min_y = bounds["min_y"]
max_y = bounds["max_y"]
rectangle = (min_x, max_x, min_y, max_y)
rectangle_stage_ids[rectangle].append(stage["id"])
terrain_rows = []
value_counts = collections.Counter()
for grid_y in range(min_y, max_y + 1):
atlas_row = rows_by_y.get(
grid_y, [0] * MAP_GRID_AUTHORED_COLUMNS
)
terrain_ids = atlas_row[min_x - 1:max_x]
terrain_rows.append({
"grid_y": grid_y,
"terrain_ids": terrain_ids,
})
value_counts.update(terrain_ids)
covered_nonzero_cells.update(
(grid_x, grid_y)
for grid_x, value in enumerate(terrain_ids, min_x)
if value != 0
)
used_ids = sorted(value for value in value_counts if value != 0)
stage_maps.append({
**stage,
"tile_width": (
stage["tile_bounds"]["max_x"]
- stage["tile_bounds"]["min_x"]
+ 1
),
"tile_height": (
stage["tile_bounds"]["max_y"]
- stage["tile_bounds"]["min_y"]
+ 1
),
"grid_width": max_x - min_x + 1,
"grid_height": max_y - min_y + 1,
"terrain_ids_used": used_ids,
"terrain_names_used": [
terrain_names.get(terrain_id) for terrain_id in used_ids
],
"terrain_id_counts": {
str(terrain_id): count
for terrain_id, count in sorted(value_counts.items())
},
"terrain_rows": terrain_rows,
})
all_nonzero_cells = {
(grid_x, grid_y)
for grid_y, values in rows_by_y.items()
for grid_x, value in enumerate(values, MAP_GRID_FIRST_COLUMN)
if value != 0
}
missing_rows = sorted(
set(range(min(rows_by_y), max(rows_by_y) + 1)) - set(rows_by_y)
)
shared_rectangles = [
{
"grid_bounds": {
"min_x": rectangle[0],
"max_x": rectangle[1],
"min_y": rectangle[2],
"max_y": rectangle[3],
},
"stage_ids": stage_ids,
}
for rectangle, stage_ids in sorted(rectangle_stage_ids.items())
if len(stage_ids) > 1
]
return rows, {
"schema": "stage-terrain-atlas",
"atlas_base": f"0x{MAP_TERRAIN_ATLAS_BASE:x}",
"current_stage_grid_base": f"0x{MAP_TERRAIN_CURRENT_BASE:x}",
"row_stride": MAP_GRID_ROW_STRIDE,
"first_authored_column": MAP_GRID_FIRST_COLUMN,
"authored_column_count": MAP_GRID_AUTHORED_COLUMNS,
"tile_to_grid_scale": MAP_TILE_TO_GRID_SCALE,
"authored_grid_y_min": min(rows_by_y),
"authored_grid_y_max": max(rows_by_y),
"authored_row_count": len(rows),
"implicit_zero_rows": missing_rows,
"implicit_zero_row_count": len(missing_rows),
"authored_cell_count": len(rows) * MAP_GRID_AUTHORED_COLUMNS,
"nonzero_cell_count": len(all_nonzero_cells),
"stage_rectangle_nonzero_cell_count": len(covered_nonzero_cells),
"outside_stage_rectangle_nonzero_cell_count": len(
all_nonzero_cells - covered_nonzero_cells
),
"terrain_ids_used": sorted({
value
for values in rows_by_y.values()
for value in values
}),
"terrain_definitions": terrain_definitions,
"stage_metadata_source": "STINIT2.BIN",
"stage_bounds_arrays": {
"min_tile_x": f"0x{MAP_STAGE_MIN_X:x}",
"max_tile_x": f"0x{MAP_STAGE_MAX_X:x}",
"min_tile_y": f"0x{MAP_STAGE_MIN_Y:x}",
"max_tile_y": f"0x{MAP_STAGE_MAX_Y:x}",
},
"stage_map_count": len(stage_maps),
"unique_atlas_rectangle_count": len(rectangle_stage_ids),
"shared_atlas_rectangles": shared_rectangles,
"stage_maps": stage_maps,
"footer_array_count": len(rows),
"footer_array_columns": [f"0x{MAP_TERRAIN_ATLAS_BASE:x}"],
"array_layouts": {
f"0x{MAP_TERRAIN_ATLAS_BASE:x}": {
"stride": MAP_GRID_ROW_STRIDE,
"first_authored_column": MAP_GRID_FIRST_COLUMN,
"authored_columns": MAP_GRID_AUTHORED_COLUMNS,
}
},
"schema_field_semantics": {
f"0x{MAP_TERRAIN_ATLAS_BASE:x}": "stage_terrain_atlas",
},
"consumer_contract": {
"FIELD.BIN": (
"clear the 2000-by-53 current-stage grid, double the selected "
"STINIT2 tile bounds, and copy that atlas rectangle into it"
),
"DRAWMINIMAP.BIN": (
"read the current-stage grid inside the selected bounds and "
"fall back to the immutable atlas outside them for border context"
),
"RESETLAND.BIN": (
"restore a changed current-stage terrain cell from the atlas"
),
},
"classified_instruction_count": len(classified_offsets),
}
def join_messages(records: list[dict], message_scr) -> dict:
"""Join a message-dispatch script to INIT records by runtime id."""
messages, message_meta = extract_message_table.extract_messages(message_scr)
@@ -3642,6 +3969,11 @@ def write_data_index(data_dir: Path) -> None:
"clips, twelve slot-to-unit joins, and the matching unit-to-suppression-setting",
"inverse map used by story, history, field, and battle voice filters.",
"",
"MPINIT's dedicated terrain-atlas schema exposes 1,472 authored rows of a sparse",
"53-column half-tile grid. It joins STINIT2's doubled tile-bound rectangles to 66",
"stage definitions, preserves implicit-zero rows and raw footer provenance, and",
"links the used terrain ids to LAINIT's names and rendering/layout classes.",
"",
"Mixed-mode tables preserve the sparse selector id, branch offset, condition strings,",
"scalar fields, cells within preallocated buffers, and length-prefixed footer arrays.",
"STINIT additionally joins confirmed parallel buffers into per-slot `object_placements`",
@@ -3727,6 +4059,8 @@ def main() -> int:
extractor = extract_alchemy_recipes
elif mode == "numeric" and name == "CVINIT":
extractor = extract_voice_configuration
elif mode == "footer" and name == "MPINIT":
extractor = extract_map_terrain_atlas
recs, meta = extractor(scr)
if mode == "name" and name in MESSAGE_TABLES:
message_name = MESSAGE_TABLES[name]
@@ -3735,7 +4069,10 @@ def main() -> int:
)
cols = sorted({c for r in recs for c in r.get("fields", {})}, key=lambda h: int(h, 16))
semantics = field_semantics(recs, meta.get("array_layouts"))
semantics = {
**meta.pop("schema_field_semantics", {}),
**field_semantics(recs, meta.get("array_layouts")),
}
attach_semantic_fields(recs, semantics)
if mode == "mixed" and name == "STINIT":
meta["object_definition_table"] = "OBINIT"

View File

@@ -48,7 +48,7 @@ def load_table(name: str) -> dict:
raise SystemExit(f"missing extracted table: {path}")
data = json.loads(path.read_text(encoding="utf8"))
if data.get("mode") not in {
"name", "numeric", "mixed", "rules", "dispatch", "banked"
"name", "numeric", "footer", "mixed", "rules", "dispatch", "banked"
}:
raise SystemExit(f"{name}: unsupported field-profiling mode {data.get('mode')!r}")
return data
@@ -279,6 +279,38 @@ def profile_banked(data: dict) -> dict:
}
def profile_map_atlas(data: dict) -> dict:
"""Summarize MPINIT's sparse terrain rows and STINIT2 rectangle join."""
if data.get("schema") != "stage-terrain-atlas":
return {}
shared = data.get("shared_atlas_rectangles", [])
return {
"row_stride": data.get("row_stride", 0),
"authored_column_count": data.get("authored_column_count", 0),
"tile_to_grid_scale": data.get("tile_to_grid_scale", 0),
"authored_row_count": data.get("authored_row_count", 0),
"implicit_zero_row_count": data.get("implicit_zero_row_count", 0),
"authored_grid_y_min": data.get("authored_grid_y_min", 0),
"authored_grid_y_max": data.get("authored_grid_y_max", 0),
"nonzero_cell_count": data.get("nonzero_cell_count", 0),
"stage_rectangle_nonzero_cell_count": data.get(
"stage_rectangle_nonzero_cell_count", 0
),
"outside_stage_rectangle_nonzero_cell_count": data.get(
"outside_stage_rectangle_nonzero_cell_count", 0
),
"terrain_ids_used": data.get("terrain_ids_used", []),
"stage_map_count": data.get("stage_map_count", 0),
"unique_atlas_rectangle_count": data.get(
"unique_atlas_rectangle_count", 0
),
"shared_rectangle_count": len(shared),
"shared_stage_definition_count": sum(
len(row.get("stage_ids", [])) for row in shared
),
}
def profile_messages(data: dict) -> dict:
"""Summarize the joined player-facing message evidence."""
records = data["records"]
@@ -406,7 +438,27 @@ def render_markdown(data: dict, rows: list[dict], limit: int) -> str:
f"- records: {data['record_count']}",
f"- populated fields: {len(rows)}",
]
if rule_profile := profile_rules(data):
if map_profile := profile_map_atlas(data):
lines.extend([
f"- geometry: {map_profile['authored_column_count']} authored cells "
f"inside a {map_profile['row_stride']}-cell row pitch",
f"- coordinate scale: one tile = "
f"{map_profile['tile_to_grid_scale']} grid cells",
f"- authored rows: {map_profile['authored_row_count']} across grid Y "
f"{map_profile['authored_grid_y_min']}.."
f"{map_profile['authored_grid_y_max']} "
f"({map_profile['implicit_zero_row_count']} omitted zero rows)",
f"- nonzero cells: {map_profile['nonzero_cell_count']} "
f"({map_profile['stage_rectangle_nonzero_cell_count']} inside stage "
f"rectangles, "
f"{map_profile['outside_stage_rectangle_nonzero_cell_count']} border cells)",
f"- terrain ids: {map_profile['terrain_ids_used']}",
f"- stage joins: {map_profile['stage_map_count']} definitions over "
f"{map_profile['unique_atlas_rectangle_count']} unique rectangles",
f"- shared rectangles: {map_profile['shared_rectangle_count']} used by "
f"{map_profile['shared_stage_definition_count']} stage definitions",
])
elif rule_profile := profile_rules(data):
lines.extend([
f"- covered units: {rule_profile['unit_count']}",
f"- titled rules: {rule_profile['titled_rule_count']}/{data['record_count']}",
@@ -512,13 +564,18 @@ def main() -> int:
"scalar_field_count": sum(row["kind"] == "scalar-field" for row in rows),
"string_field_count": sum(row["kind"] == "string-field" for row in rows),
"array_cell_count": sum(row["kind"] == "array-cell" for row in rows),
"footer_array_count": sum(row["kind"] == "footer-array" for row in rows),
"footer_array_count": (
data.get("footer_array_count", 0)
if data.get("schema") == "stage-terrain-atlas"
else sum(row["kind"] == "footer-array" for row in rows)
),
"rule_output_count": sum(row["kind"] == "rule-output" for row in rows),
"dispatch_field_count": sum(row["kind"] == "dispatch-field" for row in rows),
"message_profile": messages,
"rule_profile": profile_rules(data),
"dispatch_profile": profile_dispatch(data),
"banked_profile": profile_banked(data),
"map_atlas_profile": profile_map_atlas(data),
"columns": sorted(rows, key=lambda row: (
int(row["base"], 16), row["stride"] or 0, row["column"] or 0
)),

View File

@@ -1359,6 +1359,96 @@ def test_voice_configuration() -> None:
)
def test_map_terrain_atlas() -> None:
scripts = paths.scripts()
script = sys4load.load(scripts["MPINIT.BIN"])
check(
extract_init.detect_mode(script) == "footer",
"MPINIT remains compatible with footer-mode auto-detection",
)
records, meta = extract_init.extract_map_terrain_atlas(script)
by_y = {record["grid_y"]: record for record in records}
check(
len(records) == 1472
and meta["footer_array_count"] == 1472
and meta["classified_instruction_count"] == 1473,
"MPINIT classifies every terrain-row footer copy and exit instruction",
)
check(
meta["atlas_base"] == "0xccc93"
and meta["row_stride"] == 53
and meta["first_authored_column"] == 1
and meta["authored_column_count"] == 50
and meta["tile_to_grid_scale"] == 2,
"MPINIT exposes its 53-cell row pitch and doubled tile coordinate system",
)
check(
meta["authored_grid_y_min"] == 2
and meta["authored_grid_y_max"] == 1600
and meta["implicit_zero_row_count"] == 127
and all(record["length"] == 50 for record in records),
"MPINIT preserves all authored rows and the omitted all-zero row gaps",
)
check(
by_y[2]["global_addr"] == "0xcccfe"
and by_y[1600]["global_addr"] == "0xe17d4"
and by_y[1199]["terrain_ids_used"] == [6]
and by_y[1199]["nonzero_cell_count"] == 25,
"MPINIT row coordinates recover directly from destination addresses",
)
definitions = {row["id"]: row for row in meta["terrain_definitions"]}
check(
definitions[1]["name"] == "通路"
and definitions[1]["layout_class_name"] == "passage"
and definitions[2]["name"] == "部屋"
and definitions[2]["area_fill_flag"] == 1
and definitions[3]["name"] == "隠し通路"
and definitions[3]["layout_class_name"] == "hidden"
and definitions[15]["name"] == "溶岩流"
and definitions[15]["texture_slot_index"] == 9,
"MPINIT terrain ids join to LAINIT names and layout/render classes",
)
stage_maps = {stage["id"]: stage for stage in meta["stage_maps"]}
stage1 = stage_maps[1]
check(
meta["stage_map_count"] == 66
and meta["unique_atlas_rectangle_count"] == 53
and stage1["name"] == "『庭園の地下空洞』"
and stage1["tile_bounds"]
== {"min_x": 9, "max_x": 17, "min_y": 1, "max_y": 8}
and stage1["grid_bounds"]
== {"min_x": 18, "max_x": 34, "min_y": 2, "max_y": 16},
"STINIT2 bounds join 66 stage definitions to their doubled atlas rectangles",
)
check(
stage1["grid_width"] == 17
and stage1["grid_height"] == 15
and stage1["terrain_ids_used"] == [1, 2, 4, 12]
and stage1["terrain_id_counts"]
== {"0": 212, "1": 12, "2": 13, "4": 2, "12": 16},
"stage joins expose complete terrain grids and value populations",
)
check(
any(
shared["stage_ids"] == [32, 33, 34]
for shared in meta["shared_atlas_rectangles"]
)
and any(
shared["stage_ids"] == [101, 104, 106, 107, 108]
for shared in meta["shared_atlas_rectangles"]
),
"MPINIT preserves intentional atlas sharing across stage variants",
)
check(
meta["nonzero_cell_count"] == 17126
and meta["stage_rectangle_nonzero_cell_count"] == 17079
and meta["outside_stage_rectangle_nonzero_cell_count"] == 47,
"MPINIT accounts for stage terrain and the raw border-context cells",
)
def test_condition_definitions() -> None:
scripts = paths.scripts()
script = sys4load.load(scripts["ILINIT.BIN"])
@@ -1573,6 +1663,7 @@ if __name__ == "__main__":
test_affinity_definitions()
test_name_entry_palette()
test_voice_configuration()
test_map_terrain_atlas()
test_condition_definitions()
test_field_semantics()
if FAILS:

View File

@@ -174,6 +174,41 @@ def main() -> int:
assert banked_summary["decoded_movement_defaulted_parameter_count"] == 1
assert banked_summary["ignored_movement_parameter_count"] == 1
map_fixture = {
"table": "MAP",
"mode": "footer",
"schema": "stage-terrain-atlas",
"record_count": 1472,
"row_stride": 53,
"authored_column_count": 50,
"tile_to_grid_scale": 2,
"authored_row_count": 1472,
"implicit_zero_row_count": 127,
"authored_grid_y_min": 2,
"authored_grid_y_max": 1600,
"nonzero_cell_count": 17126,
"stage_rectangle_nonzero_cell_count": 17079,
"outside_stage_rectangle_nonzero_cell_count": 47,
"terrain_ids_used": [0, 1, 2, 3],
"stage_map_count": 66,
"unique_atlas_rectangle_count": 53,
"shared_atlas_rectangles": [
{"stage_ids": [32, 33, 34]},
{"stage_ids": [35, 36]},
],
"records": [],
}
map_summary = profile.profile_map_atlas(map_fixture)
assert map_summary["row_stride"] == 53
assert map_summary["authored_row_count"] == 1472
assert map_summary["outside_stage_rectangle_nonzero_cell_count"] == 47
assert map_summary["stage_map_count"] == 66
assert map_summary["shared_rectangle_count"] == 2
assert map_summary["shared_stage_definition_count"] == 5
rendered_map = profile.render_markdown(map_fixture, [], 40)
assert "- geometry: 50 authored cells inside a 53-cell row pitch" in rendered_map
assert "- stage joins: 66 definitions over 53 unique rectangles" in rendered_map
messages = profile.profile_messages(fixture)
assert messages["population"] == 1
assert messages["coverage"] == 1 / 3