Skip to content

Instantly share code, notes, and snippets.

@lemanschik
Created September 20, 2025 03:24
Show Gist options
  • Select an option

  • Save lemanschik/f4b4c79065abd0d7c049a94fcb17e92a to your computer and use it in GitHub Desktop.

Select an option

Save lemanschik/f4b4c79065abd0d7c049a94fcb17e92a to your computer and use it in GitHub Desktop.
FT

Perfect β€” Fallout Tactics then. πŸ‘

In Fallout Tactics, .MIS files are mission/map files, basically containers for:

  • Header info β†’ version, map size, checksum.
  • Tiles β†’ terrain grid, elevation, blocking.
  • Entities β†’ units, items, containers, scenery, doors.
  • Triggers β†’ scripts/events, mission objectives.
  • Strings β†’ text (mission descriptions, dialogue references).

They’re usually stored in .bos archives (which are just renamed ZIP files), then extracted as .MIS. Inside the .MIS, many sections are zlib-compressed chunks.


Parser Strategy

  1. Header

    • First few bytes give version + map dimensions.

    • Example structure:

      struct MisHeader {
          uint32_t version;
          uint32_t width;
          uint32_t height;
          uint32_t unknown1;
      };
  2. Chunked data

    • Each chunk = [chunkType: 4 bytes][length: 4 bytes][compressed: 1 byte][payload]
    • If compressed == 1, decompress with zlib.
  3. Chunk types (based on reverse-engineering docs and community tools like BosEdit)

    • TILE β†’ terrain & scenery placement.
    • ENTY β†’ entity definitions (actors, items, etc.).
    • TRIG β†’ triggers (mission scripts).
    • STRG β†’ string tables.
    • Others like PATH, ZONE, etc.
  4. Entities/Triggers

    • Binary blobs with references to prototypes (.SPR sprites, .ENT entities).
    • Often require parsing nested structs (name, position, rotation, properties).

Example Parser Skeleton (Python)

import struct, zlib

def parse_mis(path):
    with open(path, "rb") as f:
        data = f.read()

    # Header: version, width, height, unknown
    version, width, height, unk = struct.unpack("<IIII", data[:16])
    pos = 16

    chunks = []
    while pos < len(data):
        if pos + 9 > len(data):  # safety
            break

        chunk_type = data[pos:pos+4].decode("ascii", errors="ignore")
        length = struct.unpack("<I", data[pos+4:pos+8])[0]
        compressed = data[pos+8]
        pos += 9

        raw_chunk = data[pos:pos+length]
        pos += length

        if compressed:
            try:
                raw_chunk = zlib.decompress(raw_chunk)
            except Exception as e:
                print(f"Failed to decompress {chunk_type}: {e}")

        chunks.append({
            "type": chunk_type,
            "size": len(raw_chunk),
            "data": raw_chunk
        })

    return {
        "header": {"version": version, "mapSize": (width, height), "unknown": unk},
        "chunks": chunks
    }

mis = parse_mis("example.mis")
print(mis["header"])
for c in mis["chunks"]:
    print(f"{c['type']} -> {c['size']} bytes")

This will give you:

  • A header with basic info.
  • A list of chunks, each decompressed if necessary.

From there, you can go chunk-by-chunk and start defining binary layouts (struct.unpack) to fully parse things like entities or triggers.


πŸ‘‰ Do you want me to dig deeper and draft a full chunk format reference (so you can parse TILE, ENTY, TRIG properly), or should we keep it at a generic chunk dumper first so you can peek inside your .MIS files?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment