dm2-saves: SaveFile::tile_at / set_tile_type_attr transpose coordinates (row-major indexing into column-major map_data) #171

Closed
opened 2026-07-04 15:14:48 +02:00 by jqueuniet · 1 comment
jqueuniet commented 2026-07-04 15:14:48 +02:00 (Migrated from codeberg.org)

Problem

SaveFile::tile_at (sksave.rs:744) and SaveFile::set_tile_type_attr (sksave.rs:767) compute the map_data index as

map_offset + y * width + x     // row-major

but the on-disk tile grid is column-major (tiles[x * height + y]), as documented in dm2-dungeon (dungeon.rs, "column-major", with a dedicated tile_at_is_column_major test) and used consistently by every other consumer:

  • object_place.rs:680 and :857 (map_offset + x * height + y)
  • records.rs TileChainWalker (tile_record_link, chain_head_index)
  • linkage.rs validate_linkage

Impact

Public tile reads and edits are transposed for any tile with x != y, and on non-square maps the index can land inside a neighboring map's data block. A caller that inspects a tile via tile_at and then acts on it via the object_place APIs operates on two different tiles. Existing coverage (tests/tile_edit.rs) only probes (0, 0) — the one coordinate where the transposition is invisible.

Fix

Switch both functions to map_offset + x * height + y and extend tile_edit.rs with an asymmetric coordinate (and ideally a cross-check against TileChainWalker / a dm2-dungeon MapBody read of the same tile).

## Problem `SaveFile::tile_at` (`sksave.rs:744`) and `SaveFile::set_tile_type_attr` (`sksave.rs:767`) compute the `map_data` index as ``` map_offset + y * width + x // row-major ``` but the on-disk tile grid is column-major (`tiles[x * height + y]`), as documented in `dm2-dungeon` (`dungeon.rs`, "column-major", with a dedicated `tile_at_is_column_major` test) and used consistently by every other consumer: - `object_place.rs:680` and `:857` (`map_offset + x * height + y`) - `records.rs` `TileChainWalker` (`tile_record_link`, `chain_head_index`) - `linkage.rs` `validate_linkage` ## Impact Public tile reads and edits are transposed for any tile with `x != y`, and on non-square maps the index can land inside a neighboring map's data block. A caller that inspects a tile via `tile_at` and then acts on it via the `object_place` APIs operates on two different tiles. Existing coverage (`tests/tile_edit.rs`) only probes `(0, 0)` — the one coordinate where the transposition is invisible. ## Fix Switch both functions to `map_offset + x * height + y` and extend `tile_edit.rs` with an asymmetric coordinate (and ideally a cross-check against `TileChainWalker` / a `dm2-dungeon` `MapBody` read of the same tile).
jqueuniet commented 2026-07-06 08:47:20 +02:00 (Migrated from codeberg.org)

Fixed by #209 (merged into main).

Fixed by #209 (merged into `main`).
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
jqueuniet/dm2-tools#171
No description provided.