python/hcodedb.py

Go to the documentation of this file.
00001 #!/usr/bin/python
00002 
00003 # Copyright (c) 1999-2002 Thomas Wouters
00004 #   All rights reserved
00005 
00006 import sys,struct,string
00007 
00008 class _Typelist:
00009     def __init__(self, d):
00010         self.map = d
00011         for k,v in d.items():
00012             setattr(self, v, k)
00013 
00014 _gnames = {
00015     0: "MECH",
00016     1: "DEBUG",
00017     2: "MECHREP",
00018     3: "MAP",
00019     4: "CHARGEN",
00020     5: "AUTO",
00021     6: "TURRET",
00022     7: "CUSTOM",
00023     8: "SCEN",
00024     9: "SSIDE",
00025     10: "SSOBJ",
00026     11: "SSINS",
00027     12: "SSEXT",
00028     13: "FMAP",
00029     14: "FMAPBLOCK",
00030     15: "FLOC",
00031     16: "FCHAR",
00032     17: "FOBJ",
00033     18: "FMOD",
00034 # Disabled MT_LIST support
00035 #    127: "MT_LIST"
00036 }
00037 
00038 gtypes = _Typelist(_gnames)
00039 
00040 NUM_MAPOBJ = 10
00041 DYNAMIC_MAGIC = 42
00042 MAPFLAG_MAPO = 1
00043 MAPOBJSTART_MAGICNUM = 27
00044 MAPOBJEND_MAGICNUM = 39
00045 TYPE_BITS = 8
00046 
00047 class HCodeDB:
00048     def __init__(self, fp=None):
00049         self.data = []
00050         self.db = {}
00051         if fp:
00052             self.fp = fp
00053             self.readdb()
00054 
00055     def hcodeobj(self, key, type, size, data):
00056         if type == gtypes.MECH:
00057             return MECHObject(key, type, size, data)
00058         elif type == gtypes.MAP:
00059             return MAPObject(key, type, size, data)
00060         else:
00061             return HCodeObject(key, type, size, data)
00062 
00063     def readdb(self):
00064         # version, [key, type, size]+
00065         # version == char
00066         # key == int
00067         # type == unsigned char
00068         # size == unsigned short
00069         self.version = ord(self.fp.read(1))
00070         keydata = self.fp.read(4)
00071         while keydata and len(keydata) == 4:
00072             key = struct.unpack("=i", keydata)[0]
00073             if key < 0:
00074                 break
00075             header = self.fp.read(3)
00076             type, size = struct.unpack("=bH", header)
00077             data = self.fp.read(size)
00078 # Disabled MT_LIST support, MUX doesn't use it
00079 #            data = [self.fp.read(size),]
00080 #            if type == gtypes.MT_LIST:
00081 #                sys.stderr.write("Found MT_LIST_TYPE.\n")
00082 #                self.recursize_readlist(data)
00083             obj = self.hcodeobj(key, type, size, data)
00084             self.data.append(obj)
00085             self.db[key] = obj
00086             keydata = self.fp.read(4)
00087 
00088         sys.stderr.write("Done loading xcode tree: %d\n"%self.fp.tell())
00089         # Read postdata, objtypes and all
00090         # (God, this db format is f*cked)
00091         for meth in ("load_update1", "load_update2",
00092                      "load_update3", "load_update4"):
00093             for obj in self.data:
00094                 if hasattr(obj, meth):
00095                     getattr(obj,meth)(self.fp)
00096             sys.stderr.write("Done pass " + meth + ": %d\n"%self.fp.tell())
00097 
00098             
00099 
00100 
00101 class HCodeObject:
00102     def __init__(self, key, type, size, data):
00103         self.key = key
00104         self.type = type
00105         self.size = size
00106         self.data = data
00107     def __repr__(self):
00108         return "<HCodeObject key %s type %s>"%(self.key, gtypes.map[self.type])
00109 
00110 # MECH struct is:
00111 # dbref mynum (4 bytes)
00112 # int mapnumber (4 bytes)
00113 # dbref mapindex (4 bytes)
00114 # char ID[2] (2 bytes (2-char string))
00115 # char brief (1 byte)
00116 # unsigned long tic[4][3] (48 bytes, 4-list of 3-list of int ?)
00117 # char chantitle[16][16] (256 bytes, 16-list of lenght-15 strings (w/ nul byte))
00118 # int freq[16] (64 bytes, 16-list of ints)
00119 # int freqmodes[16] (64 bytes, 16-list of ints)
00120 # --> mech_ud follows, which is:
00121 #  char mech_name[31] (31 bytes, maybe no nul byte)
00122 #  char mech_type[15] (15 bytes, maybe no nul byte)
00123 #  char type (1 byte)
00124 #  char move (1 byte)
00125 #  int tons (4 bytes)
00126 #  short radio_range (2 bytes)
00127 #  char tac_range (1 byte)
00128 #  char lrs_range (1 byte)
00129 #  char scan_range (1 byte)
00130 #  --> 8 times session_struct, which is:
00131 #   unsigned char armor (1 byte)
00132 #   unsigned char internal (1 byte)
00133 #   unsigned char rear (1 byte)
00134 #   unsigned char armor_orig (1 byte)
00135 #   unsigned char internal_orig (1 byte)
00136 #   unsigned char rear_orig (1 byte)
00137 #   char basetohit (1 byte)
00138 #   char config (1 byte)
00139 #   char recycle (1 byte)
00140 #   --> 12 times critical_slot, which is:
00141 #    unsigned short type (2 bytes)
00142 #    unsigned char data (1 byte)
00143 #    unsigned short mode (2 bytes)
00144 #    unsigned char brand (1 byte)
00145 #   <-- end of critslot (6 bytes times 12 is 72 bytes)
00146 #  <-- end of session_struct (81 bytes times 8 is 648 bytes)
00147 #  char si (1 byte)
00148 #  char si_orig (1 byte)
00149 #  int fuel (4 bytes)
00150 #  int fuel_orig (4 bytes)
00151 #  float maxspeed (4 bytes)
00152 #  char computer (1 byte)
00153 #  char radio (1 byte)
00154 #  char radioinfo (1 byte)
00155 #  int mechbv (4 bytes)
00156 #  int cargospace (4 bytes)
00157 #  int unused[8] (32 bytes)
00158 # <-- end of mech_ud (772 bytes ?)
00159 #
00160 # --> mech_pd, which is:
00161 #  dbref pilot (4 bytes)
00162 #  char pilotstatus (1 byte)
00163 #  short hexes_walked (2 byte)
00164 #  char terrian (1 byte)
00165 #  char elev (1 byte)
00166 #  short facing (2 bytes)
00167 #  dbref master_c3_node (4 bytes)
00168 #  char team (1 byte)
00169 #  short x (2 bytes)
00170 #  short y (2 bytes)
00171 #  short z (2 bytes)
00172 #  short last_x (2 bytes)
00173 #  short last_y (2 bytes)
00174 #  float fx (4 bytes)
00175 #  float fy (4 bytes)
00176 #  float fz (4 bytes)
00177 #  int unusable_arcs (4 bytes)
00178 #  int stall (4 bytes)
00179 #  dbref bay[4] (16 bytes, 4-list of ints)
00180 #  dbref turret[3] (12 bytes, 3-list of ints)
00181 # <-- mech_pd (74 bytes)
00182 #
00183 # --> mech_rd, which is:
00184 #  float startfx (4 bytes)
00185 #  float startfy (4 bytes)
00186 #  float startfz (4 bytes)
00187 #  float endfz (4 bytes)
00188 
00189 #  short jumplength (2 bytes)
00190 #  char jumptop (1 byte)
00191 #  short goingx (2 bytes)
00192 #  short goingy (2 bytes)
00193 #  float verticalspeed (4 bytes)
00194 #  short desiredfacing (2 bytes)
00195 #  short angle ( 2 bytes)
00196 #  float speed ( 4 bytes)
00197 #  float desired_speed ( 4 bytes)
00198 #  float jumpspeed ( 4 bytes)
00199 #  short jumpheading (4 bytes)
00200 #  short targx (4 bytes)
00201 #  short targy (4 bytes)
00202 #  short targz (4 bytes)
00203 #  short turretfacing (4 bytes)
00204 #
00205 #  char aim ( 1 byte)
00206 #  char pilotskillbase (1 byte)
00207 #  short turndamage (1 byte)
00208 #  char basetohit (1 byte)
00209 #
00210 #  dbref chgtarget (4 byte)
00211 #  dbref dfatarget (4 bytes)
00212 #  dbref target (4 bytes)
00213 #  dbref swarming (4 bytes)
00214 #  dbref carrying (4 bytes)
00215 #  dbref spotter (4 bytes)
00216 #
00217 #  char engineheat (1 byte)
00218 #  float heat (4 bytes)
00219 #  float weapheat (4 bytes)
00220 #  float plus_heat (4 bytes)
00221 #  float minus_heat (4 bytes)
00222 #  int critstatus (4 bytes)
00223 #  int status (4 bytes)
00224 #  int specials (4 bytes)
00225 #
00226 #  char masc_value (1 byte)
00227 #  time_t last_weapon_recycle (4 bytes)
00228 #  char sensor[2] (2 bytes, 2-char string)
00229 #  byte fire_adjustment (1 byte)
00230 #  int cargo_weight (4 bytes)
00231 #  short lateral (2 bytes)
00232 #  short num_seen (2 bytes)
00233 #  int lastrndu (4 bytes)
00234 #  int rnd (4 bytes) (30 4bytes)
00235 #  int last_ds_msg (4 bytes)
00236 #  int boom_start (4 bytes)
00237 #  int maxfuel (4 bytes)
00238 #  int lastused (4 bytes)
00239 
00240 #  int cocoon (4 bytes)
00241 #  int commconv (4 bytes)
00242 #  int commconv_last (4 bytes)
00243 #  int onumsinks (4 bytes)
00244 #  int disabled_hs (4 bytes)
00245 #  int autopilot_num (4 bytes)
00246 #  char aim_type (4 bytes)
00247 #  int heatboom_last (4 bytes)
00248 #  char vis_mod (4 bytes)
00249 #  int sspin (4 bytes)
00250 #  int can_see (4 bytes)
00251 #  short lx (2 bytes)
00252 #  short ly (2 bytes)
00253 #  int row (4 bytes)
00254 #  int rcw (4 bytes)
00255 #  float rspd (4 bytes)
00256 #  int erat (4 bytes)
00257 #  int per (4 bytes)
00258 #  int wxf (4 bytes)
00259 #  char chargetimer (1 byte)
00260 #  char chargedist (1 byte)
00261 #  short mech_prefs (2 bytes)
00262 #  int last_startup (4 bytes)
00263 #  int specialsstatus (4 bytes)
00264 #  int tankcritstatus (4 bytes)
00265 #  int specials2 (4 bytes)
00266 #  int unused[7] (28 bytes, 7-list of ints)
00267 # <-- end of mech_rd (280 bytes)
00268 # 
00269 # Is a grand total of 1548 real bytes,
00270 # 1752 with byte alignment, and 1768 with sub-struct alignment
00271 
00272 
00273 class MECHObject(HCodeObject):
00274     def __init__(self, key, type, size, data):
00275         self.key = key
00276         self.type = type
00277 
00278         header_format = "iii2sb" + 4*3*"L" + 16*"16s" + "16i" + "16i"
00279         critslot_format = "HbHb0H"
00280         section_format = "BBBBBBbbb" + critslot_format*12
00281         ud_format = ("31s 15s bbiHbbb" + section_format*8 +
00282                     "bbiifbbbii8i0H" )
00283         pd_format = "ibHbbHibHHHHHfffii4i3i0H"
00284         rd_format = ("ffff HbHHfHHfffHHHHH bbHb iiiiii " "cffffiii"
00285                     "ci2bbiHHiiiiii" 
00286                     "iiiiiibibiiHHiifiiibbHiiii7i0H")
00287 
00288         self.format = ("@" + header_format + ud_format +
00289                        pd_format + rd_format)
00290 
00291         self.data = data
00292         self.parsedata(data, size)
00293 
00294     def parsedata(self, data, size):        
00295 
00296         class _Dummy:
00297             pass
00298 
00299         def _cull(s):
00300             return s[:string.find(s, "\000")]
00301 
00302         pdata = struct.unpack(self.format, data)
00303         (self.mynum, self.mapnumber, self.mapindex, self.ID,
00304          self.brief), pdata = pdata[:5], pdata[5:]
00305         self.tic = []
00306         for i in range(4):
00307             self.tic.append(pdata[:3])
00308             pdata = pdata[3:]
00309 
00310         self.chantitles, pdata = list(pdata[:16]), pdata[16:]
00311         for i in range(16):
00312             self.chantitles[i] = _cull(self.chantitles[i])
00313         self.freqs, pdata = pdata[:16], pdata[16:]
00314         self.freqmodes, pdata = pdata[:16], pdata[16:]
00315 
00316         ud = _Dummy()
00317         (ud.mech_name, ud.mech_type, type, move, tons, radio_range,
00318          tac_range, lrs_range, scan_range), pdata = pdata[:9], pdata[9:]
00319         ud.mech_name = _cull(ud.mech_name)
00320         ud.mech_type = _cull(ud.mech_type)
00321         ud.sections = []
00322         for i in range(8):
00323             section = _Dummy()
00324             (section.armor, section.internal, section.rear,
00325              section.armor_orig, section.internal_orig,
00326              section.rear_orig, section.basetohit, section.config,
00327              section.recycle), pdata = pdata[:9], pdata[9:]
00328             section.crits = []
00329             for i in range(12):
00330                 crit = _Dummy()
00331                 (crit.type, crit.data, crit.mode, crit.brand
00332                  ), pdata = pdata[:4], pdata[4:]
00333                 section.crits.append(crit)
00334             ud.sections.append(section)
00335         (ud.si, ud.si_orig, ud.fuel, ud.fuel_orig, ud.maxspeed,
00336          ud.computer, ud.radio, ud.radioinfo, ud.mechbv, ud.cargospace,
00337          ), pdata = pdata[:10], pdata[10:]
00338         ud.unused, pdata = pdata[:8], pdata[8:]
00339         self.ud = ud
00340 
00341         pd = _Dummy()
00342         (pd.pilot, pd.pilotstatus, pd.hexes_walked, pd.terrain,
00343          pd.elev, pd.facing, pd.master_c3_node, pd.team, pd.x,
00344          pd.y, pd.z, pd.last_x, pd.last_y, pd.fx, pd.fy, pd.fz,
00345          pd.unusable_arcs, pd.stall), pdata = pdata[:18], pdata[18:]
00346         pd.bays, pdata = pdata[:4], pdata[4:]
00347         pd.turrets, pdata = pdata[:4], pdata[4:]
00348         self.pd = pd
00349 
00350         rd = _Dummy()
00351         (rd.startfx, rd.startfy, rd.startfz, rd.endfz, rd.jumplength,
00352          rd.jumptop, rd.goingx, rd.goingy, rd.verticalspeed,
00353          rd.desiredfacing, rd.angle, rd.speed, rd.desired_speed,
00354          rd.jumpspeed, rd.jumpheading, rd.targx, rd.targy, rd.targz,
00355          rd.turretfacing, rd.aim, rd.pilotskillbase, rd.turndamage,
00356          rd.basetohit, rd.chgtarget, rd.dfatarget, rd.target,
00357          rd.swarming, rd.carrying, rd.spotter, rd.engineheat, rd.heat,
00358          rd.weapheat, rd.plus_heat, rd.minus_heat, rd.critstatus,
00359          rd.status, rd.specials, rd.masc_value, rd.last_weapon_recycle,
00360          rd.sensor, rd.fire_adjustment, rd.cargo_weight, rd.lateral,
00361          rd.num_seen, rd.lastrndu, rd.rnd, rd.last_ds_msg, rd.boom_start,
00362          rd.maxfuel, rd.lastused, rd.cocoon, rd.commconv,
00363          rd.commconv_last, rd.onumsinks, rd.disabled_hs,
00364          rd.autopilot_num, rd.aim_type, rd.heatboom_last, rd.vis_mod,
00365          rd.sspin, rd.can_see, rd.lx, rd.ly, rd.row, rd.rcw, rd.rspd,
00366          rd.erat, rd.per, rd.wxf, rd.chargetimer, rd.chargedist,
00367          rd.mech_prefs, rd.last_startup, rd.specialstatus,
00368          rd.tankcritstatus, rd.specials2), pdata = pdata[:76],pdata[76:]
00369 
00370         rd.unused, pdata = pdata[:7], pdata[7:]
00371         self.rd = rd
00372 
00373         if pdata:
00374             sys.stderr.write("pdata left! %s\n"%(pdata,))
00375             sys.stderr.write("length of data: %s\n"%size)
00376 
00377 
00378 # MAP struct is:
00379 # dbref mynum (4 bytes)
00380 # unsigned char * map[] (4 bytes)
00381 # char mapname[31] (31 bytes)
00382 # short map_width (2 bytes)
00383 # short map_height (2 bytes)
00384 # char temp ( 1 byte)
00385 # unsigned char grav (1 byte)
00386 # short cloudbase (2 bytes)
00387 # char unused_char (1 byte)
00388 # char mapvis (1 byte)
00389 # short maxvis (2 bytes)
00390 # char maplight (1 byte)
00391 # short winddir (2 bytes)
00392 # short windspeed (2 bytes)
00393 # unsigned char flags (1 byte)
00394 # struct mapobj * mapobj (4 bytes)
00395 # short cf (2 bytes)
00396 # short cfmax (2 bytes)
00397 # dbref onmap (4 bytes)
00398 # char buildflag (1 byte)
00399 # unsigned char first_free (1 byte)
00400 # dbref * mechsOnMap (4 bytes)
00401 # unsigned short * LOSInfo[] (4 bytes)
00402 # char * mechflags[] (4 bytes)
00403 # short moves (1 byte)
00404 # short movemod (1 byte)
00405 #
00406 # Resulting in:
00407 # @ii31sHHbBHbbHbHHbiHHibBiiiHH
00408 
00409 class MAPObject(HCodeObject):
00410     def __init__(self, key, type, size, data):
00411         self.key = key
00412         self.type = type
00413 
00414         self.format = "@ii31sHHbBHbbHbHHb10iHHibBiiiHH"
00415         self.parsedata(data, size)
00416 
00417     def parsedata(self, data, size):
00418         csize = struct.calcsize(self.format)
00419         if (size <> csize):
00420             sys.stderr.write("Wrong size: %d vs %d"%(size, csize))
00421             if size < csize:
00422                 data += "\0"*(csize - size)
00423             else:
00424                 data = data[:csize]
00425 
00426         pdata = list(struct.unpack(self.format, data))
00427         (self.mynum, x, self.name, self.width, self.height,
00428          self.temp, self.grav, self.cloudbase, self.unused, self.vis,
00429          self.maxvis, self.light, self.winddir, self.windspeed,
00430          self.flags), pdata = pdata[:15], pdata[15:]
00431 
00432         # Skip mapobjs
00433         pdata = pdata[10:]
00434 
00435         (self.cf, self.maxcf, self.onmap, self.buildflag,
00436          self.first_free, x, x, x,
00437          self.moves, self.movemod) = pdata
00438 
00439     def load_update1(self, fp):
00440         self.map = []
00441         self.losinfo = []
00442         num = self.first_free
00443 
00444         if num:
00445             fmt = "@" + "i" * num
00446             self.mechsonmap = _unpack(fmt, fp)
00447             fmt = "@" + "b" * num
00448             self.mechflags = _unpack(fmt, fp)
00449             fmt = "@" + "H" * num
00450             for x in range(num):
00451                 self.losinfo.append(_unpack(fmt, fp))
00452         else:
00453             self.mechsonmap = []
00454             self.mapobj = []
00455 
00456         magic = ord(fp.read(1))
00457         if magic <> DYNAMIC_MAGIC:
00458             sys.stderr.write("Did not find DYNAMIC_MAGIC for #%d\n"%self.mynum)
00459             sys.stderr.write("Wanted %d, got %d\n"%(DYNAMIC_MAGIC, magic))
00460             sys.exit(1)
00461         
00462         if self.flags & MAPFLAG_MAPO:
00463             self.load_mapobj(fp)
00464 
00465     def load_mapobj(self, fp):
00466         magic = ord(fp.read(1))
00467         if magic <> MAPOBJSTART_MAGICNUM:
00468             sys.stderr.write("Did not find mapobjstartmagic for #%d\n"%self.mynum)
00469             sys.stderr.write("Wanted %d, got %d\n"%(MAPOBJSTART_MAGICNUM, magic))
00470 
00471         self.mapobj = []
00472         for i in range(NUM_MAPOBJ):
00473             self.mapobj.append([])
00474 
00475         nextbyte = ord(fp.read(1))
00476         while nextbyte:
00477             if nextbyte - 1 == TYPE_BITS:
00478                 self.load_bits(fp)
00479             else:
00480                 self.mapobj[nextbyte - 1].append(MapobjObject(fp))
00481             nextbyte = ord(fp.read(1))
00482         magic = ord(fp.read(1))
00483         if magic <> MAPOBJEND_MAGICNUM:
00484             sys.stderr.write("no mapobjend found for #%d!\n")
00485             sys.stderr.write("Wanted %d, got %d\n"%(MAPOBJEND_MAGICNUM, magic))
00486 
00487     def load_bits(self, fp):
00488 
00489         self.mapbits = []
00490         fmt = "@" + "i"*self.height
00491         foo = _unpack(fmt, fp)
00492         fmt = "@" + "B"*(self.width / 4 + ((self.width % 4) and 1 or 0))
00493         for x in foo:
00494             if x:
00495                 self.mapbits.append(_unpack(fmt, fp))
00496             else:
00497                 self.mapbits.append([])
00498                 
00499 
00500 class MapobjObject:
00501     def __init__(self, fp):
00502         fmt = "@HHiccHii"
00503         (self.x, self.y, self.obj, self.type, self.datac,
00504          self.datas, self.datai, x) = _unpack(fmt, fp)
00505 
00506 
00507 def _unpack(fmt, fp):
00508     return list(struct.unpack(fmt, fp.read(struct.calcsize(fmt))))
00509 
00510 
00511 if __name__ == "__main__":
00512     if len(sys.argv) <> 2:
00513         print "Usage: python -i hcodedb.py <hcodedbfile>"
00514         sys.exit()
00515 
00516     db = HCodeDB(open(sys.argv[1]))
00517 
00518 

Generated on Mon May 28 04:25:18 2007 for BattletechMUX by  doxygen 1.4.7