#!/usr/bin/python # Copyright (c) 1999-2002 Thomas Wouters # All rights reserved import sys,struct,string class _Typelist: def __init__(self, d): self.map = d for k,v in d.items(): setattr(self, v, k) _gnames = { 0: "MECH", 1: "DEBUG", 2: "MECHREP", 3: "MAP", 4: "CHARGEN", 5: "AUTO", 6: "TURRET", 7: "CUSTOM", 8: "SCEN", 9: "SSIDE", 10: "SSOBJ", 11: "SSINS", 12: "SSEXT", 13: "FMAP", 14: "FMAPBLOCK", 15: "FLOC", 16: "FCHAR", 17: "FOBJ", 18: "FMOD", # Disabled MT_LIST support # 127: "MT_LIST" } gtypes = _Typelist(_gnames) NUM_MAPOBJ = 10 DYNAMIC_MAGIC = 42 MAPFLAG_MAPO = 1 MAPOBJSTART_MAGICNUM = 27 MAPOBJEND_MAGICNUM = 39 TYPE_BITS = 8 class HCodeDB: def __init__(self, fp=None): self.data = [] self.db = {} if fp: self.fp = fp self.readdb() def hcodeobj(self, key, type, size, data): if type == gtypes.MECH: return MECHObject(key, type, size, data) elif type == gtypes.MAP: return MAPObject(key, type, size, data) else: return HCodeObject(key, type, size, data) def readdb(self): # version, [key, type, size]+ # version == char # key == int # type == unsigned char # size == unsigned short self.version = ord(self.fp.read(1)) keydata = self.fp.read(4) while keydata and len(keydata) == 4: key = struct.unpack("=i", keydata)[0] if key < 0: break header = self.fp.read(3) type, size = struct.unpack("=bH", header) data = self.fp.read(size) # Disabled MT_LIST support, MUX doesn't use it # data = [self.fp.read(size),] # if type == gtypes.MT_LIST: # sys.stderr.write("Found MT_LIST_TYPE.\n") # self.recursize_readlist(data) obj = self.hcodeobj(key, type, size, data) self.data.append(obj) self.db[key] = obj keydata = self.fp.read(4) sys.stderr.write("Done loading xcode tree: %d\n"%self.fp.tell()) # Read postdata, objtypes and all # (God, this db format is f*cked) for meth in ("load_update1", "load_update2", "load_update3", "load_update4"): for obj in self.data: if hasattr(obj, meth): getattr(obj,meth)(self.fp) sys.stderr.write("Done pass " + meth + ": %d\n"%self.fp.tell()) class HCodeObject: def __init__(self, key, type, size, data): self.key = key self.type = type self.size = size self.data = data def __repr__(self): return "<HCodeObject key %s type %s>"%(self.key, gtypes.map[self.type]) # MECH struct is: # dbref mynum (4 bytes) # int mapnumber (4 bytes) # dbref mapindex (4 bytes) # char ID[2] (2 bytes (2-char string)) # char brief (1 byte) # unsigned long tic[4][3] (48 bytes, 4-list of 3-list of int ?) # char chantitle[16][16] (256 bytes, 16-list of lenght-15 strings (w/ nul byte)) # int freq[16] (64 bytes, 16-list of ints) # int freqmodes[16] (64 bytes, 16-list of ints) # --> mech_ud follows, which is: # char mech_name[31] (31 bytes, maybe no nul byte) # char mech_type[15] (15 bytes, maybe no nul byte) # char type (1 byte) # char move (1 byte) # int tons (4 bytes) # short radio_range (2 bytes) # char tac_range (1 byte) # char lrs_range (1 byte) # char scan_range (1 byte) # --> 8 times session_struct, which is: # unsigned char armor (1 byte) # unsigned char internal (1 byte) # unsigned char rear (1 byte) # unsigned char armor_orig (1 byte) # unsigned char internal_orig (1 byte) # unsigned char rear_orig (1 byte) # char basetohit (1 byte) # char config (1 byte) # char recycle (1 byte) # --> 12 times critical_slot, which is: # unsigned short type (2 bytes) # unsigned char data (1 byte) # unsigned short mode (2 bytes) # unsigned char brand (1 byte) # <-- end of critslot (6 bytes times 12 is 72 bytes) # <-- end of session_struct (81 bytes times 8 is 648 bytes) # char si (1 byte) # char si_orig (1 byte) # int fuel (4 bytes) # int fuel_orig (4 bytes) # float maxspeed (4 bytes) # char computer (1 byte) # char radio (1 byte) # char radioinfo (1 byte) # int mechbv (4 bytes) # int cargospace (4 bytes) # int unused[8] (32 bytes) # <-- end of mech_ud (772 bytes ?) # # --> mech_pd, which is: # dbref pilot (4 bytes) # char pilotstatus (1 byte) # short hexes_walked (2 byte) # char terrian (1 byte) # char elev (1 byte) # short facing (2 bytes) # dbref master_c3_node (4 bytes) # char team (1 byte) # short x (2 bytes) # short y (2 bytes) # short z (2 bytes) # short last_x (2 bytes) # short last_y (2 bytes) # float fx (4 bytes) # float fy (4 bytes) # float fz (4 bytes) # int unusable_arcs (4 bytes) # int stall (4 bytes) # dbref bay[4] (16 bytes, 4-list of ints) # dbref turret[3] (12 bytes, 3-list of ints) # <-- mech_pd (74 bytes) # # --> mech_rd, which is: # float startfx (4 bytes) # float startfy (4 bytes) # float startfz (4 bytes) # float endfz (4 bytes) # short jumplength (2 bytes) # char jumptop (1 byte) # short goingx (2 bytes) # short goingy (2 bytes) # float verticalspeed (4 bytes) # short desiredfacing (2 bytes) # short angle ( 2 bytes) # float speed ( 4 bytes) # float desired_speed ( 4 bytes) # float jumpspeed ( 4 bytes) # short jumpheading (4 bytes) # short targx (4 bytes) # short targy (4 bytes) # short targz (4 bytes) # short turretfacing (4 bytes) # # char aim ( 1 byte) # char pilotskillbase (1 byte) # short turndamage (1 byte) # char basetohit (1 byte) # # dbref chgtarget (4 byte) # dbref dfatarget (4 bytes) # dbref target (4 bytes) # dbref swarming (4 bytes) # dbref carrying (4 bytes) # dbref spotter (4 bytes) # # char engineheat (1 byte) # float heat (4 bytes) # float weapheat (4 bytes) # float plus_heat (4 bytes) # float minus_heat (4 bytes) # int critstatus (4 bytes) # int status (4 bytes) # int specials (4 bytes) # # char masc_value (1 byte) # time_t last_weapon_recycle (4 bytes) # char sensor[2] (2 bytes, 2-char string) # byte fire_adjustment (1 byte) # int cargo_weight (4 bytes) # short lateral (2 bytes) # short num_seen (2 bytes) # int lastrndu (4 bytes) # int rnd (4 bytes) (30 4bytes) # int last_ds_msg (4 bytes) # int boom_start (4 bytes) # int maxfuel (4 bytes) # int lastused (4 bytes) # int cocoon (4 bytes) # int commconv (4 bytes) # int commconv_last (4 bytes) # int onumsinks (4 bytes) # int disabled_hs (4 bytes) # int autopilot_num (4 bytes) # char aim_type (4 bytes) # int heatboom_last (4 bytes) # char vis_mod (4 bytes) # int sspin (4 bytes) # int can_see (4 bytes) # short lx (2 bytes) # short ly (2 bytes) # int row (4 bytes) # int rcw (4 bytes) # float rspd (4 bytes) # int erat (4 bytes) # int per (4 bytes) # int wxf (4 bytes) # char chargetimer (1 byte) # char chargedist (1 byte) # short mech_prefs (2 bytes) # int last_startup (4 bytes) # int specialsstatus (4 bytes) # int tankcritstatus (4 bytes) # int specials2 (4 bytes) # int unused[7] (28 bytes, 7-list of ints) # <-- end of mech_rd (280 bytes) # # Is a grand total of 1548 real bytes, # 1752 with byte alignment, and 1768 with sub-struct alignment class MECHObject(HCodeObject): def __init__(self, key, type, size, data): self.key = key self.type = type header_format = "iii2sb" + 4*3*"L" + 16*"16s" + "16i" + "16i" critslot_format = "HbHb0H" section_format = "BBBBBBbbb" + critslot_format*12 ud_format = ("31s 15s bbiHbbb" + section_format*8 + "bbiifbbbii8i0H" ) pd_format = "ibHbbHibHHHHHfffii4i3i0H" rd_format = ("ffff HbHHfHHfffHHHHH bbHb iiiiii " "cffffiii" "ci2bbiHHiiiiii" "iiiiiibibiiHHiifiiibbHiiii7i0H") self.format = ("@" + header_format + ud_format + pd_format + rd_format) self.data = data self.parsedata(data, size) def parsedata(self, data, size): class _Dummy: pass def _cull(s): return s[:string.find(s, "\000")] pdata = struct.unpack(self.format, data) (self.mynum, self.mapnumber, self.mapindex, self.ID, self.brief), pdata = pdata[:5], pdata[5:] self.tic = [] for i in range(4): self.tic.append(pdata[:3]) pdata = pdata[3:] self.chantitles, pdata = list(pdata[:16]), pdata[16:] for i in range(16): self.chantitles[i] = _cull(self.chantitles[i]) self.freqs, pdata = pdata[:16], pdata[16:] self.freqmodes, pdata = pdata[:16], pdata[16:] ud = _Dummy() (ud.mech_name, ud.mech_type, type, move, tons, radio_range, tac_range, lrs_range, scan_range), pdata = pdata[:9], pdata[9:] ud.mech_name = _cull(ud.mech_name) ud.mech_type = _cull(ud.mech_type) ud.sections = [] for i in range(8): section = _Dummy() (section.armor, section.internal, section.rear, section.armor_orig, section.internal_orig, section.rear_orig, section.basetohit, section.config, section.recycle), pdata = pdata[:9], pdata[9:] section.crits = [] for i in range(12): crit = _Dummy() (crit.type, crit.data, crit.mode, crit.brand ), pdata = pdata[:4], pdata[4:] section.crits.append(crit) ud.sections.append(section) (ud.si, ud.si_orig, ud.fuel, ud.fuel_orig, ud.maxspeed, ud.computer, ud.radio, ud.radioinfo, ud.mechbv, ud.cargospace, ), pdata = pdata[:10], pdata[10:] ud.unused, pdata = pdata[:8], pdata[8:] self.ud = ud pd = _Dummy() (pd.pilot, pd.pilotstatus, pd.hexes_walked, pd.terrain, pd.elev, pd.facing, pd.master_c3_node, pd.team, pd.x, pd.y, pd.z, pd.last_x, pd.last_y, pd.fx, pd.fy, pd.fz, pd.unusable_arcs, pd.stall), pdata = pdata[:18], pdata[18:] pd.bays, pdata = pdata[:4], pdata[4:] pd.turrets, pdata = pdata[:4], pdata[4:] self.pd = pd rd = _Dummy() (rd.startfx, rd.startfy, rd.startfz, rd.endfz, rd.jumplength, rd.jumptop, rd.goingx, rd.goingy, rd.verticalspeed, rd.desiredfacing, rd.angle, rd.speed, rd.desired_speed, rd.jumpspeed, rd.jumpheading, rd.targx, rd.targy, rd.targz, rd.turretfacing, rd.aim, rd.pilotskillbase, rd.turndamage, rd.basetohit, rd.chgtarget, rd.dfatarget, rd.target, rd.swarming, rd.carrying, rd.spotter, rd.engineheat, rd.heat, rd.weapheat, rd.plus_heat, rd.minus_heat, rd.critstatus, rd.status, rd.specials, rd.masc_value, rd.last_weapon_recycle, rd.sensor, rd.fire_adjustment, rd.cargo_weight, rd.lateral, rd.num_seen, rd.lastrndu, rd.rnd, rd.last_ds_msg, rd.boom_start, rd.maxfuel, rd.lastused, rd.cocoon, rd.commconv, rd.commconv_last, rd.onumsinks, rd.disabled_hs, rd.autopilot_num, rd.aim_type, rd.heatboom_last, rd.vis_mod, rd.sspin, rd.can_see, rd.lx, rd.ly, rd.row, rd.rcw, rd.rspd, rd.erat, rd.per, rd.wxf, rd.chargetimer, rd.chargedist, rd.mech_prefs, rd.last_startup, rd.specialstatus, rd.tankcritstatus, rd.specials2), pdata = pdata[:76],pdata[76:] rd.unused, pdata = pdata[:7], pdata[7:] self.rd = rd if pdata: sys.stderr.write("pdata left! %s\n"%(pdata,)) sys.stderr.write("length of data: %s\n"%size) # MAP struct is: # dbref mynum (4 bytes) # unsigned char * map[] (4 bytes) # char mapname[31] (31 bytes) # short map_width (2 bytes) # short map_height (2 bytes) # char temp ( 1 byte) # unsigned char grav (1 byte) # short cloudbase (2 bytes) # char unused_char (1 byte) # char mapvis (1 byte) # short maxvis (2 bytes) # char maplight (1 byte) # short winddir (2 bytes) # short windspeed (2 bytes) # unsigned char flags (1 byte) # struct mapobj * mapobj (4 bytes) # short cf (2 bytes) # short cfmax (2 bytes) # dbref onmap (4 bytes) # char buildflag (1 byte) # unsigned char first_free (1 byte) # dbref * mechsOnMap (4 bytes) # unsigned short * LOSInfo[] (4 bytes) # char * mechflags[] (4 bytes) # short moves (1 byte) # short movemod (1 byte) # # Resulting in: # @ii31sHHbBHbbHbHHbiHHibBiiiHH class MAPObject(HCodeObject): def __init__(self, key, type, size, data): self.key = key self.type = type self.format = "@ii31sHHbBHbbHbHHb10iHHibBiiiHH" self.parsedata(data, size) def parsedata(self, data, size): csize = struct.calcsize(self.format) if (size <> csize): sys.stderr.write("Wrong size: %d vs %d"%(size, csize)) if size < csize: data += "\0"*(csize - size) else: data = data[:csize] pdata = list(struct.unpack(self.format, data)) (self.mynum, x, self.name, self.width, self.height, self.temp, self.grav, self.cloudbase, self.unused, self.vis, self.maxvis, self.light, self.winddir, self.windspeed, self.flags), pdata = pdata[:15], pdata[15:] # Skip mapobjs pdata = pdata[10:] (self.cf, self.maxcf, self.onmap, self.buildflag, self.first_free, x, x, x, self.moves, self.movemod) = pdata def load_update1(self, fp): self.map = [] self.losinfo = [] num = self.first_free if num: fmt = "@" + "i" * num self.mechsonmap = _unpack(fmt, fp) fmt = "@" + "b" * num self.mechflags = _unpack(fmt, fp) fmt = "@" + "H" * num for x in range(num): self.losinfo.append(_unpack(fmt, fp)) else: self.mechsonmap = [] self.mapobj = [] magic = ord(fp.read(1)) if magic <> DYNAMIC_MAGIC: sys.stderr.write("Did not find DYNAMIC_MAGIC for #%d\n"%self.mynum) sys.stderr.write("Wanted %d, got %d\n"%(DYNAMIC_MAGIC, magic)) sys.exit(1) if self.flags & MAPFLAG_MAPO: self.load_mapobj(fp) def load_mapobj(self, fp): magic = ord(fp.read(1)) if magic <> MAPOBJSTART_MAGICNUM: sys.stderr.write("Did not find mapobjstartmagic for #%d\n"%self.mynum) sys.stderr.write("Wanted %d, got %d\n"%(MAPOBJSTART_MAGICNUM, magic)) self.mapobj = [] for i in range(NUM_MAPOBJ): self.mapobj.append([]) nextbyte = ord(fp.read(1)) while nextbyte: if nextbyte - 1 == TYPE_BITS: self.load_bits(fp) else: self.mapobj[nextbyte - 1].append(MapobjObject(fp)) nextbyte = ord(fp.read(1)) magic = ord(fp.read(1)) if magic <> MAPOBJEND_MAGICNUM: sys.stderr.write("no mapobjend found for #%d!\n") sys.stderr.write("Wanted %d, got %d\n"%(MAPOBJEND_MAGICNUM, magic)) def load_bits(self, fp): self.mapbits = [] fmt = "@" + "i"*self.height foo = _unpack(fmt, fp) fmt = "@" + "B"*(self.width / 4 + ((self.width % 4) and 1 or 0)) for x in foo: if x: self.mapbits.append(_unpack(fmt, fp)) else: self.mapbits.append([]) class MapobjObject: def __init__(self, fp): fmt = "@HHiccHii" (self.x, self.y, self.obj, self.type, self.datac, self.datas, self.datai, x) = _unpack(fmt, fp) def _unpack(fmt, fp): return list(struct.unpack(fmt, fp.read(struct.calcsize(fmt)))) if __name__ == "__main__": if len(sys.argv) <> 2: print "Usage: python -i hcodedb.py <hcodedbfile>" sys.exit() db = HCodeDB(open(sys.argv[1]))