Skip to content

Instantly share code, notes, and snippets.

@sfiera
Created April 4, 2025 01:19
Show Gist options
  • Save sfiera/52d9f5fa2b62bd84cdef48ef3439312c to your computer and use it in GitHub Desktop.
Save sfiera/52d9f5fa2b62bd84cdef48ef3439312c to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
import struct
import sys
ROAD = "·╹╺┗╻┃┏┣╸┛━┻┓┫┳╋"
BRIDGE = "v╨╞╚╥║╔╠╡╝═╩╗╣╦╬"
def line(chars, *args):
ul = ur = ll = lr = 0
if 0 in args:
ul |= 0b0001
if 1 in args:
ul |= 0b0010
ur |= 0b1010
if 2 in args:
ll |= 0b0010
lr |= 0b1010
if 3 in args:
ll |= 0b0100
if 4 in args:
ll |= 0b1000
if 5 in args:
ul |= 0b1000
if ul and ll:
ul |= 0b0100
ll |= 0b0001
return chars[ul] + chars[ur] + chars[ll] + chars[lr]
UNITS = {
0x00: "yama",
0x01: "falc",
0x02: "javi",
0x03: "hunt",
0x04: "griz",
0x05: "arma",
0x06: "biso",
0x07: "slag",
0x08: "mons",
0x09: "gian",
0x0A: "lene",
0x0B: "nash",
0x0C: "esto",
0x0D: "rabb",
0x0E: "lynx",
0x0F: "seek",
0x10: "hawk",
0x11: "mono",
0x12: "munk",
0x13: "darb",
0x14: "drap",
0x15: "mule",
0x16: "peli",
}
TILES = {
0x3A: "····",
0x3B: "····",
0x3C: "····",
0x3D: "····",
0x3F: "MMMM",
0x40: "****",
0x45: "HHHH",
0x4D: "HHHH",
0x52: "****",
0x54: "HHHH",
0x56: \\·-",
0x57: "|·|/",
0x58: "****",
0x59: "****",
0x5D: "HHHH",
0x5E: "·/··",
0x5F: "O-|\\",
0x60: "U═╚═",
0x62: "G═╚═",
0x63: "G─└─",
0x64: "··▖·",
0x65: "HHHH",
0x66: "|·|/",
0x67: "|···",
0x6C: "▘···",
0x6D: "HHHH",
0x6E: "HHHH",
0x70: "N─└─",
0x71: "··▖·",
0x73: "··▖·",
0x75: "U─└─",
0x76: "··▖·",
0x79: "▘···",
0x7B: "▘···",
0x7E: "▘···",
0x80: "mmmm",
0x81: "mmmm",
0x82: "mmmm",
0x83: "mmmm",
0x84: "mmmm",
0x85: "mmmm",
0x99: "vvvv",
0x9A: "vvvv",
0x9B: line(ROAD, 2, 3, 5),
0x9C: "vvvv",
0xA1: "vvvv",
0xA3: "vvvv",
0xA4: "vvvv",
0xA6: "vvvv",
0xA7: "vvvv",
0xA8: "vvvv",
0xAA: "vvvv",
0xAF: line(ROAD, 0, 2, 3),
0xB1: line(ROAD, 1, 5),
0xB2: line(ROAD, 2, 4),
0xB4: line(ROAD, 0, 4),
0xB5: line(ROAD, 2, 5),
0xB6: line(ROAD, 1, 2, 4),
0xB7: "vvvv",
0xB8: line(ROAD, 1, 4),
0xB9: line(ROAD, 1, 3),
0xBB: line(BRIDGE, 1, 4),
0xBD: line(ROAD, 0, 3),
0xC2: line(ROAD, 3, 5),
0xC5: line(ROAD, 0, 2, 4),
0xC6: line(ROAD, 0, 2),
0xDF: line(BRIDGE, 0, 3),
0xE1: "vvvv",
0xE2: "vvvv",
}
def main():
for path in sys.argv[1:]:
with open(path, "rb") as f:
version = Map.VERSIONS.get(f.read(16))
if not version:
print(f"{path}: not a Nectaris map")
continue
m = version.read_map(f)
print(f"{path} ({m.title or 'untitled'})")
print_grid(m.grid)
print_units(m.units, m.factories)
print()
class Map:
VERSIONS = {}
def __init__(self, title, grid, units, factories):
self.title = title
self.grid = grid
self.units = units
self.factories = factories
@classmethod
def read_map(cls, f):
if cls.HAS_TITLE:
title = f.read(8).rstrip(b"\0").decode("ascii")
else:
title = None
grid = read_grid(f, cls.CELL_FMT)
units, factories = read_units(f)
return cls(title, grid, units, factories)
class MapV100(Map):
MAGIC = b"NEC MAP V1.00 "
HAS_TITLE = False
CELL_FMT = "B"
class MapV101(Map):
MAGIC = b"NEC MAP V1.01 "
HAS_TITLE = True
CELL_FMT = "<H"
Map.VERSIONS[MapV100.MAGIC] = MapV100
Map.VERSIONS[MapV101.MAGIC] = MapV101
def read_grid(f, cell_fmt):
w, h = struct.unpack("BB", f.read(2))
grid = []
size = struct.calcsize(cell_fmt)
for y in range(h):
row = []
for x in range(w):
(cell,) = struct.unpack(cell_fmt, f.read(size))
row.append(cell)
grid.append(row)
return grid
def read_units(f):
units, factories = {}, {}
while True:
(unit,) = struct.unpack("B", f.read(1))
if unit == 0xFF:
break
side = ["union", "guicy"][unit >> 7]
unit = UNITS[unit & 0x7F]
x, y = struct.unpack("BB", f.read(2))
fac = x >> 5
x &= 0x1F
if fac:
factories.setdefault((x, y, side, fac), []).append(unit)
else:
units[x, y] = (side, unit)
return units, factories
def print_grid(grid):
w, h = len(grid[0]), len(grid)
print(f"Size: {w}x{h}")
print(" ", end="")
for x in range(w):
print("%2d" % x, end="")
print()
for y in range(h + 1):
for offset in [[0], [0, 1]][y < h]:
if offset:
print("%2d " % y, end="")
else:
print(" ", end="")
for x in range(w):
if x % 2:
yy = y
else:
yy = y + offset - 1
if not (0 <= yy < h):
print(" ", end="")
continue
cell = grid[yy][x]
default = "%02x" % cell
default = default + default
if offset != (x % 2):
print(TILES.get(cell, default)[:2], end="")
else:
print(TILES.get(cell, default)[2:], end="")
print()
def print_units(units, factories):
for (x, y), (side, unit) in units.items():
print(f"{x:2} {y:<2} {side} {unit:<3}")
for (x, y, side, fac), units in factories.items():
print(f"{x:2} {y:<2} {side} {fac} {" ".join(units)}")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment