def readData(dlPath): fname = dlPath + '/darkland.enm' data = map(ord, open(fname).read()) dataLen = len(data) enemyTypes = [] pos = 0 for i in xrange(0, 71): et = OrderedDict() et['image_group'] = sread(data[pos:pos+4]) ; pos += 4 et['name'] = sread(data[pos:pos+10]) ; pos += 10 et['num_variants'] = data[pos] ; pos += 1 et['pal_cnt'] = data[pos] ; pos += 1 # number of palettes in enemypal? et['unknown2'] = data[pos] ; pos += 1 et['pal_start'] = data[pos] ; pos += 1 # palettes starting index in enemypal et['unknown4'] = bread(data[pos:pos+2]) ; pos += 2 attrs = {} for attr in ('end','str','agl','per','int','chr','df'): attrs[attr] = data[pos] ; pos += 1 et['attrs'] = attrs skills = {} for s in ('wEdg','wImp','wFll','wPol','wThr','wBow','wMsl','alch','relg','virt','spkC','spkL','r_w','heal','artf','stlh','strW','ride','wdWs'): skills[s] = data[pos] ; pos += 1 et['skills'] = skills et['unknown5'] = data[pos] ; pos += 1 pos += 1 # const 0 et['unknown6'] = data[pos] ; pos += 1 # underground related? et['unknown7'] = data[pos] ; pos += 1 et['unknown8'] = data[pos:pos+0x42] ; pos += 0x42 et['unknown9'] = data[pos:pos+0x1e] ; pos += 0x1e et['unknown10'] = data[pos:pos+2] ; pos += 2 et['vital_arm_type'] = data[pos] ; pos += 1 et['limb_arm_typetype'] = data[pos] ; pos += 1 et['armor_q'] = data[pos] ; pos += 1 et['unknown11'] = data[pos] ; pos += 1 et['shield_type'] = data[pos] ; pos += 1 et['shield_q'] = data[pos] ; pos += 1 et['unknown12'] = data[pos:pos+6] ; pos += 6 et['unknown13'] = data[pos:pos+6] ; pos += 6 et['weapon_types'] = data[pos:pos+6] ; pos += 6 et['weapon_q'] = data[pos] ; pos += 1 et['unknown14'] = data[pos:pos+11] ; pos += 11 et['unknown15'] = data[pos:pos+20] ; pos += 20 enemyTypes.append(et) enemies = [] for i in xrange(0, 82): e = OrderedDict() e['type'] = bread(data[pos:pos+2]) ; pos += 2 e['type_str'] = enemyTypes[e['type']]['name'] e['name'] = sread(data[pos:pos+12]) ; pos += 12 pos += 8 # all 0 e['unknown'] = bread(data[pos:pos+2]) ; pos += 2 e['unknown_bin'] = bin(e['unknown']) enemies.append(e) return enemyTypes, enemies
def readData(dlPath): fname = dlPath + '/tacanim.db' data = map(ord, open(fname).read()) dataLen = len(data) tacanim = {} pos = 0 while pos < dataLen: charCode = sread(data[pos:pos + 4]) pos += 4 tacanim[charCode] = data[pos:pos + 10] pos += 10 # last four bytes looks like 2 times width, depth for walk & combat?? # Nope? :( change of last 4 bytes has no effect in battle return {'tacanim': tacanim}
def readData(dlPath): fname = dlPath + '/darkland.loc' data = map(ord, open(fname).read()) dataLen = len(data) #print fname, dataLen, 'B' pos = 0 cnt = bread(data[pos:pos + 2]) #print cnt, bread(data[pos:pos+1]) pos += 2 locs = [] for i in xrange(0, cnt): c = OrderedDict() c['icon'] = bread(data[pos:pos + 2]) pos += 2 # (enum location_icon) #Map image for the location. Note that this basically corresponds to the 'type' of location. #print c['icon'] c['str_loc_type'] = locTypes[ c['icon']] if c['icon'] < len(locTypes) else str(c['icon']) c['unknown1'] = bread(data[pos:pos + 2]) pos += 2 #c['unknown1_bin'] = bin(c['unknown1']) #0 for cities, other locations range from 0x08-0x0e. c['coords'] = (bread(data[pos:pos + 2]), bread(data[pos + 2:pos + 4])) pos += 4 #Map coordinates. c['unknown2'] = bread(data[pos:pos + 2]) pos += 2 #Ranges from 1-10. #Seems to be 4 or 9 for live Raubritters (1 for dead); perhaps it's a strength? c['unknown3'] = bread(data[pos:pos + 2]) pos += 2 #Most range from 1-5, except pagan altars, which are 0x63 (99). c['menu'] = bread(data[pos:pos + 2]) pos += 2 #Card displayed on entering the location. c['unknown4'] = bread(data[pos:pos + 2]) pos += 2 #Always 0x62 except for castles currently occupied by Raubritters (icon=2); those are 0x92. c['unknown5'] = data[pos] pos += 1 c['city_size'] = data[pos] pos += 1 #Size of the city. #Cities range from 3 (small) to 8 (Koln); non-cities are always 1. c['local_rep'] = bread(data[pos:pos + 2]) pos += 2 #Local reputation. #In this file, this is always zero. The copy of this structure that lives in the saved game files gets non-zero values. #Ranges from -150 to 150 (although others claim to have observed numbers outside this range). c['unknown6'] = data[pos] pos += 1 #Zero seems to indicate an "active" site. #Ruins of a Raubritter castle get 0x04, as do destroyed villages. #Some other locations get 0x20 or 0x24. c['unknown7_c'] = bread(data[pos:pos + 3]) pos += 3 # [constant: { 0x19, 0x19, 0x19 }] c['inn_cache_idx'] = bread(data[pos:pos + 2]) pos += 2 #In this file, this is always 0xffff (-1). #In a saved game file, if the party stores items at an inn (in a city), this value becomes an index into cache_offsets (found in dksaveXX.sav). c['unknown8_c'] = bread(data[pos:pos + 2]) pos += 2 #[constant: 0x0000] c['unknown9'] = bread(data[pos:pos + 2]) pos += 2 #All are zero except for Nurnberg, which is 0xc0. c['unknown10_c'] = bread(data[pos:pos + 8]) pos += 8 # [constant: all 0x00] c['name'] = sread(data[pos:pos + 20]) pos += 20 locs.append(c) return locs
from utils import bread, sread # catalog filename fname = sys.argv[1] # output dir ddir = sys.argv[2] data = map(ord, open(fname).read()) dataLen = len(data) print fname, dataLen, 'B' pos = 0 cnt = bread(data[pos:pos + 2]) print cnt pos += 2 for i in xrange(0, cnt): fn = sread(data[pos:pos + 12]) pos += 12 # dword TS pos += 4 dataLen = bread(data[pos:pos + 4]) pos += 4 dataOffs = bread(data[pos:pos + 4]) pos += 4 print fn, dataOffs, dataLen fh = open(ddir + '/' + fn, "wb") fh.write(bytearray(data[dataOffs:dataOffs + dataLen])) fh.close()
def readData(dlPath): fname = dlPath + '/darkland.lst' data = map(ord, open(fname).read()) dataLen = len(data) #print fname, dataLen, 'B' itemCnt, saintCnt, formCnt = data[0], data[1], data[2] pos = 3 items = [] for i in xrange(0, itemCnt): c = OrderedDict() c['name'] = sread(data[pos:pos + 20]) pos += 20 c['short_name'] = sread(data[pos:pos + 10]) pos += 10 c['type'] = bread(data[pos:pos + 2]) pos += 2 flags = (('is_edged', 'is_impact', 'is_polearm', 'is_flail', 'is_thrown', 'is_bow', 'is_metal_armor', 'is_shield'), ('is_unknown1', 'is_unknown2', 'is_component', 'is_potion', 'is_relic', 'is_horse', 'is_quest_1', 'is_const0_1'), ('is_lockpicks', 'is_light', 'is_arrow', 'is_const0_2', 'is_quarrel', 'is_ball', 'is_const0_3', 'is_quest_2'), ('is_throw_potion', 'is_const0_4', 'is_nonmetal_armor', 'is_missile_weapon', 'is_unknown3', 'is_music', 'is_const0_6', 'is_const0_7'), ('is_unknown4', 'is_unknown5', 'is_const0_8', 'is_const0_9', 'is_const0_10', 'is_const0_11', 'is_const0_12', 'is_unknown6')) for f in flags: bits = data[pos] pos += 1 for b, n in enumerate(f): c[n] = True if bits & (1 << b) else False c['weight'] = data[pos] pos += 1 c['quality'] = data[pos] pos += 1 c['rarity'] = data[pos] pos += 1 # missing in Merle's doc! c['unknown1'] = bread(data[pos:pos + 2]) pos += 2 #Non-zero only for relics. #Ranges from 0x06 (St. Edward's Ring) to 0x50 (St. Gabriel's Horn). c['unknown2'] = bread(data[pos:pos + 2]) pos += 2 #Non-zero only for relics, and for the "residency permit" (which is unused by the game). #Ranges from 0x05 to 0x27 (residency permit). c['value'] = bread(data[pos:pos + 2]) pos += 2 items.append(c) saints = [] for i in xrange(0, saintCnt): s = '' while data[pos]: s += chr(data[pos]) pos += 1 pos += 1 saints.append({'name': s}) for i in xrange(0, saintCnt): s = '' while data[pos]: s += chr(data[pos]) pos += 1 pos += 1 saints[i]['short_name'] = s formulae = [] for i in xrange(0, formCnt): s = '' while data[pos]: s += chr(data[pos]) pos += 1 pos += 1 formulae.append({'name': s}) for i in xrange(0, formCnt): s = '' while data[pos]: s += chr(data[pos]) pos += 1 pos += 1 formulae[i]['short_name'] = s # read saints descriptions fname = dlPath + '/darkland.snt' data = map(ord, open(fname).read()) pos = 1 for i in xrange(0, len(saints)): saints[i]['description'] = sread(data[pos:pos + 0x168]) pos += 0x168 return items, saints, formulae
def readData(dlPath): fname = dlPath + '/darkland.cty' data = map(ord, open(fname).read()) dataLen = len(data) #print fname, dataLen, 'B' pos = 0 cnt = data[pos] #print cnt, bread(data[pos:pos+1]) pos += 1 cities = [] for i in xrange(0, cnt): c = OrderedDict() c['short_name'] = sread(data[pos:pos + 32]) pos += 32 c['name'] = sread(data[pos:pos + 32]) pos += 32 #city_data = (size 0x2e) dataPos = pos c['city_size'] = bread(data[pos:pos + 2]) pos += 2 #Size of the city. Ranges from 3 (small) to 8 (Koln). c['entry_coords'] = (bread(data[pos:pos + 2]), bread(data[pos + 2:pos + 4])) pos += 4 # City location on the map. c['exit_coords'] = (bread(data[pos:pos + 2]), bread(data[pos + 2:pos + 4])) pos += 4 # Party coordinates when leaving a city. # When you leave a city, you don't exit at the same point as you entered. The exit coordinates were (usually) selected so as not to place you in an untenable position (the ocean, trapped by a river loop, etc). dests = [] for i in xrange(0, 4): tgt = bread(data[pos:pos + 2]) pos += 2 if tgt != 0xffff: dests.append(tgt) c['dock_destinations'] = dests # Dock destination cities. # This contains the indices (in the cities array) of the destinations available via the city docks the docks. # 0xffff is used for "no destination". Inland cities have all 0xffffs. c['coast'] = bread(data[pos:pos + 2]) pos += 2 #if coastal, side of the river ???[Hamburg] TODO. # Values are: 0xffff (inland), 0 (north of the river), 1 (south of the river) # 0 and 1 cities are on or near tidal zones (swamps), and may be subject to flooding. c['unknown_cd_1'] = bread(data[pos:pos + 2]) pos += 2 # [constant: 4] c['pseudo_ordinal'] = bread(data[pos:pos + 2]) pos += 2 # At first glance, this looks like an ordinal offset running from 0 to 0x5b, but 0x18 is missing, and 0x3c repeats. # This value is probably not used. c['city_type'] = bread(data[pos:pos + 2]) pos += 2 # Type of city c['unknown_cd_2'] = bread(data[pos:pos + 2]) pos += 2 # 0, 1, 2, or 3. c['unknown_cd_3'] = bread(data[pos:pos + 2]) pos += 2 # [constant: 0] #city_contents = bread(data[pos:pos+2])# bitmask[16 bits] city_contents = (data[pos] << 8) | data[pos + 1] # bitmask[16 bits] pos += 2 #Buildings and locations in the city. # Bits are on iff there is one of that type of building. opts = ('has_kloster', 'has_slums', 'has_unknown1', 'has_cathedral', 'has_unknown2', 'has_no_fortress', 'has_town_hall', 'has_polit', 'has_constant1', 'has_constant2', 'has_constant3', 'has_constant4', 'has_docks', 'has_unknown3', 'has_pawnshop', 'has_university') for i, o in enumerate(opts): c[o] = 1 if city_contents & (1 << (15 - i)) else 0 c['bin_city_contents'] = bin(city_contents) c['unknown_cd_4'] = bread(data[pos:pos + 2]) pos += 2 # [constant: 0] c['qual_black'] = data[pos] pos += 1 #Quality of the blacksmith. # This, and the other nine qualities, all seem to work in the same way. # A zero value indicates that the city does not have that particular shop. # Non-zero values do not exactly equal the quality of the items available, but merely indicate relative qualities! For example, Nurnberg has a 0x31 (49) listed for the armory, but offers q37 (0x25) armor. However, if one city has a higher value than another, then that city's items will be of equal or greater quality. # The quality of the healer is not stored here, but is apparently random. (TODO: verify?) # TODO: comments about Quality of the alchemist, university, pharmacist being the seed thing. c['qual_merch'] = data[pos] pos += 1 #Quality of the merchant. c['qual_sword'] = data[pos] pos += 1 #Quality of the swordsmith. c['qual_armor'] = data[pos] pos += 1 #Quality of the armorer. c['qual_unk1'] = data[pos] pos += 1 c['qual_bow'] = data[pos] pos += 1 #Quality of the bowyer. c['qual_tink'] = data[pos] pos += 1 #Quality of the tinker. c['qual_unk2'] = data[pos] pos += 1 c['qual_cloth'] = data[pos] pos += 1 # Quality of the clothing merchant. c['qual_unk3'] = data[pos] pos += 1 # [constant: 0] c['unknown_cd_5'] = data[pos] pos += 1 # unknown byte # Since the following byte is 0 or 1, this and that might actually be a single word value. c['unknown_cd_6'] = data[pos] pos += 1 # unknown byte # Either zero or one (only a couple of ones). c['unknown_cd_5-6'] = bread(data[pos - 2:pos]) # word c['leader_name'] = sread(data[pos:pos + 32]) pos += 32 c['ruler_name'] = sread(data[pos:pos + 32]) pos += 32 c['unknown'] = sread(data[pos:pos + 32]) pos += 32 #TODO: is this non-empty ever? (ditto for other two unknowns) c['polit_name'] = sread(data[pos:pos + 32]) pos += 32 #Name of the political center or town square. #TODO: describe what empty values look like c['town_hall_name'] = sread(data[pos:pos + 32]) pos += 32 c['fortress_name'] = sread(data[pos:pos + 32]) pos += 32 #Name of the city fortress or castle. c['cathedral_name'] = sread(data[pos:pos + 32]) pos += 32 c['church_name'] = sread(data[pos:pos + 32]) pos += 32 c['market_name'] = sread(data[pos:pos + 32]) pos += 32 #Name of the marketplace. c['unknown2'] = sread(data[pos:pos + 32]) pos += 32 #Often contains "Munzenplatz". Possibly this is "central square name". c['slum_name'] = sread(data[pos:pos + 32]) pos += 32 c['unknown3'] = sread(data[pos:pos + 32]) pos += 32 #Many places have "Zeughaus", which translates to "armoury"; others end in "-turm" (tower?) or "-tor" (gate?). Quite possible this is for one of the unused "rebellion" codepath. c['pawnshop_name'] = sread(data[pos:pos + 32]) pos += 32 #Name of the pawnshop. #All pawnshops are named the same; this is either 'Leifhaus' or is empty. c['kloster_name'] = sread(data[pos:pos + 32]) pos += 32 #Name of the kloster (church law and administration building). c['inn_name'] = sread(data[pos:pos + 32]) pos += 32 c['university_name'] = sread(data[pos:pos + 32]) pos += 32 cities.append(c) # read descriptions + generate some attrs fname = dlPath + '/darkland.dsc' data = map(ord, open(fname).read()) pos = 1 for c in cities: c['description'] = sread(data[pos:pos + 80]) pos += 80 c['str_dock_destinations'] = ', '.join( [cities[d]['short_name'] for d in c['dock_destinations']]) c['str_city_type'] = cityTypes[c['city_type']] return cities