def _read(self): f = File(self.file_path) n_entries = f.readUInt16() for i in range(n_entries): self.offsets.append(f.readUInt32()) unknown1 = f.readUInt8() unknown2 = f.readUInt8() unknown3 = f.readUInt8() unknown4 = f.readUInt8() for i in range(n_entries): print(f.pos(), self.offsets[i]) assert f.pos() == self.offsets[i] width = f.readUInt16() height = f.readUInt16() image = Image(width, height) for y in range(height): for x in range(width): colour = self.palette[f.readUInt8()] image.set(colour, x, y) self.images.append(image)
def savegame(input_path): f = File(input_path) data = {} # Header assert (f.readString() == "STTNG_GAME") blocks = readBlockHeaders(f) assert (len(blocks) == 9) print(blocks[0]) data |= readBlockCompstat(f, blocks[0]) print(blocks[1]) data |= readBlockAststat(f, blocks[1]) print(blocks[2]) print(blocks[3]) print(blocks[4]) print(blocks[5]) data |= readBlockTravelHistory(f, blocks[5]) print(blocks[6]) try: data |= readBlockObjects(f, blocks[6]) except: print( "AN ERROR OCURRED FETCHING OBJECTS - SAVEGAME SUPPORT IS A WORK IN PROGRESS" ) print(blocks[7]) print(blocks[8]) return data
def lst(file_path): f = File(file_path) n = f.readUInt32() entries = [] while not f.eof(): entries.append(f.readString()) assert (n == len(entries)) return entries
def adviceDat(file_path): # Reads w{world}a000.dat files. The contain information about characters giving the player advie. # I'm pretty sure this file is not for the game to read. # There're so many typos and variations, I'm pretty sure they're developer notes. f = File(file_path) entries = [] while not f.eof(): level = _adviceEntry(f) if level is None: break entries.append(level) return entries
def astromapDb(file_path): f = File(file_path) eof = len(f) offsets = [] while True: offset = f.readUInt32() if offset == eof: break offsets.append(offset) sectors = [] for offset in offsets: assert (f.pos() == offset) sector = readSector(f) for j in range(sector["n_systems"]): system = readSystem(f) system.pop("name_offset") system.pop("alias_offset") system.pop("notable_offset") assert (system.pop("description_offset") == 0) system.pop("station_offset") sector["systems"].append(system) for j in range(sector["n_bodies"]): body = readBody(f) body.pop("name_offset") sector["bodies"].append(body) for j in range(sector["n_stations"]): station = readStation(f) station.pop("name_offset") sector["stations"].append(station) sectors.append(sector) _addSectorNames(sectors, file_path) return sectors
def worldList(file_path): f = File(file_path) data = {} while not f.eof(): scene_id = f.readUInt16() assert(f.readUInt16() == scene_id) assert(f.readUInt16() == 0) comment = f.readStringBuffer(21) data[scene_id] = comment return data
def worldObj(file_path): f = File(file_path) screen = [] while not f.eof(): counter = f.readUInt16() object_id = _readObjectId(f) assert(object_id["id"] == counter) name = f.readStringBuffer(30).strip() description = f.readStringBuffer(260).strip() screen.append({ "id": object_id, "name": name, "description": description, }) return screen
def singlePalette(palette_path): """Read a single (128 colour) palette from a file""" return readSinglePalette(File(palette_path))
def sprite(sprite_path, background_path, palette_path=None): palette_path = Palette.getGlobalPalettePath(sprite_path, palette_path) global_palette = Palette.singlePalette(palette_path) local_palette = Palette.singlePalette(background_path) palette = Palette.combinePalettes(local_palette, global_palette) f = File(sprite_path) spr = { "name": sprite_path.name, "blocks": [], "images": {}, } block = _readBlockHeader(f) assert (block["name"] == "SPRT") assert (f.readUInt32() == 0x100) eof = block["offset"] + block["length"] block_offsets = [] while f.pos() < eof: block = _readBlockHeader(f) if block["name"] == "LIST": block["entries"] = [] length = f.readUInt32() for i in range(length): offset = f.readUInt32() block["entries"].append({"offset": block["offset"] + offset}) elif block["name"] == "SETF": block["flag"] = f.readUInt32() # 0, 1, 2, 3 or 4 elif block["name"] == "POSN": block["x"] = f.readUInt32() block["y"] = f.readUInt32() elif block["name"] == "COMP": image = _readImage(f, block, palette) offset = image.pop("offset") block["image_offset"] = offset if image["image"] is not None: image["image"].name = _imageName(sprite_path, offset) spr["images"][offset] = image elif block["name"] == "TIME": block["time"] = f.readUInt32() elif block["name"] == "MARK": pass elif block["name"] == "MASK": pass elif block["name"] == "SCOM": image = _readImage(f, block, palette) offset = image.pop("offset") block["image_offset"] = offset if image["image"] is not None: image["image"].name = _imageName(sprite_path, offset) spr["images"][offset] = image elif block["name"] == "RAND": rand_extra = f.readUInt32() block["min"] = f.readUInt32() block["max"] = block["min"] + rand_extra elif block["name"] == "JUMP": block["index"] = f.readUInt32() elif block["name"] == "RPOS": block["dx"] = f.readSInt32() block["dy"] = f.readSInt32() elif block["name"] == "SNDF": sound_volume = f.readUInt32() # 75, 95 or 100. volume? assert (f.readUInt32() == 0) sound_file = str(f.read(16)) block["volume"] = sound_volume block["file"] = sound_file elif block["name"] == "MPOS": block["x"] = f.readUInt32() block["y"] = f.readUInt32() elif block["name"] == "PAUS": pass elif block["name"] == "EXIT": pass elif block["name"] == "STAT": pass elif block["name"] == "RGBP": palette = Palette.readFullPalette(f) else: raise ValueError("Unknown block {} at {:#x}".format( block["name"], block["start"])) block_offsets.append(block["offset"]) block.pop("length") # we don't need this any more spr["blocks"].append(block) for block in spr["blocks"]: if block["name"] == "LIST": for entry in block["entries"]: entry["index"] = block_offsets.index(entry["offset"]) return spr
def triggers(file_path): f = File(file_path) data = [] while not f.eof(): trigger = {"id": f.readUInt32()} assert (f.readUInt16() == 0xffff) trigger["unknown_a"] = f.readUInt8() assert (f.readUInt8() == 0xff) trigger["type"] = TriggerType(f.readUInt32()) trigger["target"] = _readObjectId(f) trigger["enabled"] = bool(f.readUInt8()) trigger["unknown_b"] = f.readUInt8() assert (f.readUInt16() == 0xffff) trigger["timer_start"] = f.readUInt32() if trigger["type"] == TriggerType.TIMER: assert (f.readUInt32() == 0) assert (f.readUInt32() == 0) elif trigger["type"] == TriggerType.PROXIMITY: trigger["distance"] = f.readUInt16() assert (f.readUInt16() == 0xffff) trigger["from"] = _readObjectId(f) trigger["to"] = _readObjectId(f) trigger["reversed"] = bool(f.readUInt8()) trigger["instant"] = bool(f.readUInt8()) assert (f.readUInt16() == 0xffff) elif trigger["type"] == TriggerType.UNUSED: assert (f.readUInt32() == 0) data.append(trigger) return data
def compstat(file_path): f = File(file_path) return readCompstat(f)
def worldStrt(file_path): f = File(file_path) data = [] while not f.eof(): screen = {} screen["advice_id"] = f.readUInt16() screen["advice_timer"] = f.readUInt16() screen["unknown1"] = f.readUInt8() screen["unknown2"] = f.readUInt8() screen["id"] = f.readUInt16() assert (screen["id"] == len(data)) screen["target"] = _readObjectId(f) screen["action_type"] = f.readUInt8() screen["who"] = _readObjectId(f) screen["other"] = _readObjectId(f) assert(f.readUInt32() == 0xffffffff) # unknown3 screen["unknown4"] = f.readUInt16() assert(screen["unknown4"] in (0xff, 0x1, 0x0)) assert(f.readUInt16() == 0x0) # unknown5 assert(f.readUInt8() == 0x0) # unknown6 for i in range(15): assert (f.readUInt8() == 0) data.append(screen) return data
def astStatDat(file_path): f = File(file_path) state = readAstroState(f) assert (f.eof()) return state
def sectorAst(file_path): f = File(file_path) return {i: f.readLine() for i in range(N_SECTORS)}
def worldSlScr(file_path): f = File(file_path) n_screens = f.readUInt16() screens = [] for i in range(n_screens): screen_id = f.readUInt32() screen_offset = f.readUInt32() screens.append( (screen_id,screen_offset) ) assert( f.readUInt32() == 0xFF ) eof = f.readUInt32() world_screens = [] for screen_id,screen_offset in screens: assert( f.pos() == screen_offset ) screen = { "id": screen_id, "entrances": [], } background_file_length = f.readUInt8() background_file = f.readString() assert(len(background_file) + 1 == background_file_length) screen["background"] = background_file polygons_file_length = f.readUInt8() polygons_file = f.readString() assert(len(polygons_file) + 1 == polygons_file_length) screen["polygons"] = polygons_file n_entrances = f.readUInt8() for i in range(n_entrances): entrance_id = f.readUInt8() # unknown is 0x1 for every single screen in every world except # screen 11 in world 2, where it's 0x8d. # If you don't unwind by one byte then, you end up overrunning # the block, and the entrance positions are nonsense. entrance_unknown = f.readUInt8() if( entrance_unknown != 0x1 ): f.setPosition(f.pos() - 1) entrance_vertices = [] for j in range(4): entrance_vertices.append({"x": f.readUInt16(), "y": f.readUInt16()}) entrance = { "entrance_id": entrance_id, "unknown": entrance_unknown, "vertices": entrance_vertices, } screen["entrances"].append(entrance) world_screens.append(screen) assert(f.pos() == eof) return { "screens": world_screens }
def worldStScr(file_path): f = File(file_path) n_entries = f.readUInt16() entries = [] for i in range(n_entries): id = f.readUInt32() offset = f.readUInt32() entries.append( (id,offset) ) assert(f.readUInt32() == 0xFF) eof = f.readUInt32() n_screen_unknown = f.readUInt8() screen_unknown = [f.readUInt16() for x in range(n_screen_unknown)] screen_polygons = [] for id,offset in entries: assert(f.pos() == offset) poly_type = f.readUInt8() assert(poly_type in [0,1,3,4]) assert(f.readUInt16() == 0) n_vertices = f.readUInt8() poly_vertices = [] for i in range(n_vertices): x = f.readUInt16() y = f.readUInt16() distance = f.readUInt16() poly_vertices.append({ "x": x, "y": y, "distance": distance, }) n_poly_unknown = f.readUInt8() poly_unknown = [] for i in range(n_poly_unknown): poly_unknown.append((f.readUInt8(), f.readUInt8())) assert(f.readUInt16() == 0) screen_polygons.append({ "type": poly_type, "vertices": poly_vertices, "unknown": poly_unknown, }) assert(f.pos() == eof) screen = { "unknown": screen_unknown, "polgons": screen_polygons, } return screen
def fullPalette(local_path, global_path): """Read a full (256 colour) palette, getting the local and global parts from two separate files""" local_palette = readSinglePalette(File(local_path)) global_palette = readSinglePalette(File(global_path)) return local_palette + global_palette