Created
April 4, 2025 01:19
-
-
Save sfiera/52d9f5fa2b62bd84cdef48ef3439312c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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