This utility generates a valid empty staidx0.mul and a matching empty statics0.mul for Ultima Online style maps. It is handy for prototyping small custom maps, bootstrapping tools, and learning how UO static layers are indexed and stored.
map0.mulholds terrain tiles only.art.mulandartidx.mulhold the graphics for tiles and items.statics0.mulis not graphics. It is a list of placement records that say “place TileID T at local (x, y) in this 8×8 block at elevation z with hue h.”staidx0.mulis a block index. One 12-byte entry per 8×8 tile block gives the byte offset and length of that block’s placement records insidestatics0.mul.
In other words: art.mul is the catalog of pictures, statics0.mul is where those pictures are placed, and staidx0.mul tells you where each block’s placements live inside statics0.mul.
- Prompts for Blocks X and Blocks Y where each block is 8×8 tiles.
- Computes entry count and writes
staidx0.mulwith one 12-byte entry per block using little endian layout<iii. - Writes each index entry as
lookup = -1, length = 0, extra = 0which means “this block has no statics.” - Creates an empty
statics0.mulfile.
Example: 16 × 16 blocks equals 128 × 128 tiles and produces 256 index entries, 3072 bytes total.
- Binary file structures — learn how fixed-size records, indexes, and offsets make large worlds streamable and editable.
- C-style struct layouts — see how 32-bit integers are laid out in memory and why endianness matters.
- Game data layering — understand how engines layer terrain, placements, and art to render massive maps efficiently.
- Python systems programming — practice byte-level file I/O and use
struct.pack/struct.unpackto serialize data in a portable way.
- Block occupancy visualizer — parse
staidx0.muland show a grid heatmap of which 8×8 blocks contain static data. - Minimal static injector — extend the tool to write a few sample placement records into
statics0.muland updatestaidx0.mul. - CSV or JSON to statics converter — define statics in a human-friendly table, then compile to a valid
.mulpair. - Integrity checker — scan a
.mulpair to detect overlapping regions, invalid lengths not divisible by 7, or out-of-range offsets. - Teaching demos — use tiny maps like 8 × 8 or 16 × 16 blocks to demonstrate binary formats, indexing, and retro asset pipelines.
python emptyX.pyFollow the prompts for Blocks X and Blocks Y. The script writes staidx0.mul and statics0.mul to the current directory.
== Create empty staidx0.mul/statics0.mul ==
Each block is 8×8 tiles. You will enter block counts, not tiles.
Blocks X (e.g., 16): 16
Blocks Y (e.g., 16): 16
Done.
Blocks: 16 × 16 (tiles: 128 × 128)
Entries written: 256
staidx0.mul size: 3072 bytes
Wrote: /path/to/staidx0.mul
Wrote: /path/to/statics0.mul
int32 lookup— little endian byte offset intostatics0.mulor -1 for emptyint32 length— length in bytes for this block’s placement list (multiple of 7)int32 extra— reserved or checksum field, commonly 0 in tooling
uint16 tile_id— which art resource to placeuint8 x_local— 0 to 7 within the blockuint8 y_local— 0 to 7 within the blockint8 z— elevationuint16 hue— color index, 0 for default
- Python 3.7 or newer
- Standard library only
MIT. See LICENSE.