comparison pytouhou/formats/exe.py @ 230:1c24a6d93c1b

Improve find_character_defs, clean up a bit, and disable attack types 2 and 3 for now
author Thibaut Girka <thib@sitedethib.com>
date Fri, 30 Dec 2011 19:10:49 +0100
parents 5afc75f71fed
children e59bd7979ddc
comparison
equal deleted inserted replaced
229:5afc75f71fed 230:1c24a6d93c1b
32 self.hitbox = (0., 0.) 32 self.hitbox = (0., 0.)
33 self.angle = 0. 33 self.angle = 0.
34 self.speed = 0. 34 self.speed = 0.
35 self.damage = 0 35 self.damage = 0
36 self.orb = 0 36 self.orb = 0
37 self.shot_type = 0 37 self.type = 0
38 self.sprite = 0 38 self.sprite = 0
39 self.unknown1 = None 39 self.unknown1 = None
40
41 def __repr__(self):
42 return '(%d, %d, %f, %f, %d, %d, %d, %d, %d)' % (self.interval, self.delay, self.angle, self.speed, self.damage, self.orb, self.shot_type, self.sprite, self.unknown1)
43 40
44 41
45 class SHT(object): 42 class SHT(object):
46 def __init__(self): 43 def __init__(self):
47 #self.unknown1 = None 44 #self.unknown1 = None
59 self.diagonal_focused_speed = 0. 56 self.diagonal_focused_speed = 0.
60 self.shots = {} 57 self.shots = {}
61 58
62 59
63 @classmethod 60 @classmethod
64 def find_character_records(self, file, pe_file): 61 def find_character_defs(cls, pe_file):
62 """Generator returning the possible VA of character definition blocks.
63
64 Based on knowledge of the structure, it tries to find valid definition blocks
65 without embedding any copyrighted material or hard-coded offsets that would
66 only be useful for a specific build of the game.
67 """
68
65 format = Struct('<4f2I') 69 format = Struct('<4f2I')
66 data_section = [section for section in pe_file.sections if section.Name.startswith('.data')][0] 70 data_section = [section for section in pe_file.sections
67 text_section = [section for section in pe_file.sections if section.Name.startswith('.text')][0] 71 if section.Name.startswith('.data')][0]
72 text_section = [section for section in pe_file.sections
73 if section.Name.startswith('.text')][0]
68 data_va = pe_file.image_base + data_section.VirtualAddress 74 data_va = pe_file.image_base + data_section.VirtualAddress
75 data_size = data_section.SizeOfRawData
69 text_va = pe_file.image_base + text_section.VirtualAddress 76 text_va = pe_file.image_base + text_section.VirtualAddress
77 text_size = text_section.SizeOfRawData
70 78
71 for addr in xrange(data_va, data_va + data_section.SizeOfRawData, 4): 79 # Search the whole data segment for 4 successive character definitions
80 for addr in xrange(data_va, data_va + data_size, 4):
72 for character_id in xrange(4): 81 for character_id in xrange(4):
73 pe_file.seek_to_va(addr + character_id * 24) 82 pe_file.seek_to_va(addr + character_id * 24)
74 speed1, speed2, speed3, speed4, ptr1, ptr2 = format.unpack(file.read(format.size)) 83 (speed1, speed2, speed3, speed4,
84 ptr1, ptr2) = format.unpack(pe_file.file.read(format.size))
75 85
76 if not (all(0. < x < 8. for x in (speed1, speed2, speed3, speed4)) 86 # Check whether the character's speed make sense,
87 # and whether the function pointers point to valid addresses
88 if not (all(0. < x < 10. for x in (speed1, speed2, speed3, speed4))
77 and speed2 <= speed1 89 and speed2 <= speed1
78 and 0 <= ptr1 - text_va < text_section.SizeOfRawData - 8 90 and 0 <= ptr1 - text_va < text_size - 8
79 and 0 <= ptr2 - text_va < text_section.SizeOfRawData - 8): 91 and 0 <= ptr2 - text_va < text_size - 8):
80 break 92 break
81 93
94 # So far, this character definition seems to be valid.
95 # Now, make sure the shoot function wrappers pass valid addresses
82 pe_file.seek_to_va(ptr1 + 4) 96 pe_file.seek_to_va(ptr1 + 4)
83 shtptr1, = unpack('<I', file.read(4)) 97 shtptr1, = unpack('<I', pe_file.file.read(4))
84 pe_file.seek_to_va(ptr2 + 4) 98 pe_file.seek_to_va(ptr2 + 4)
85 shtptr2, = unpack('<I', file.read(4)) 99 shtptr2, = unpack('<I', pe_file.file.read(4))
100 if not (0 <= shtptr1 - data_va < data_size - 12
101 and 0 <= shtptr2 - data_va < data_size - 12):
102 break
86 103
87 if not (0 <= shtptr1 - data_va < data_section.SizeOfRawData 104 # It is unlikely this character record is *not* valid, but
88 and 0 <= shtptr2 - data_va < data_section.SizeOfRawData): 105 # just to be sure, let's check the first SHT definition.
106 pe_file.seek_to_va(shtptr1)
107 nb_shots, power, shotsptr = unpack('<III', pe_file.file.read(12))
108 if not (0 < nb_shots <= 1000
109 and 0 <= power < 1000
110 and 0 <= shotsptr - data_va < data_size - 36*nb_shots):
89 break 111 break
90 else: # XXX: Obscure python feature! This gets executed if there were no break! 112
113 else:
114 # XXX: Obscure python feature! This only gets executed if the
115 # XXX: loop ended without a break statement.
116 # In our case, it's only executed if all the 4 character
117 # definitions are considered valid.
91 yield addr 118 yield addr
92 119
93 120
94 @classmethod 121 @classmethod
95 def read(cls, file): 122 def read(cls, file):
96 pe_file = PEFile(file) 123 pe_file = PEFile(file)
97 124
98 character_records_va = list(cls.find_character_records(file, pe_file))[0] 125 character_records_va = list(cls.find_character_defs(pe_file))[0]
99 126
100 characters = [] 127 characters = []
101 shots_offsets = [] 128 shots_offsets = []
102 for character in xrange(4): 129 for character in xrange(4):
103 sht = cls() 130 sht = cls()
140 for i in xrange(shots_count): 167 for i in xrange(shots_count):
141 shot = Shot() 168 shot = Shot()
142 169
143 data = unpack('<HH6fHBBhh', file.read(36)) 170 data = unpack('<HH6fHBBhh', file.read(36))
144 (shot.interval, shot.delay, x, y, hitbox_x, hitbox_y, 171 (shot.interval, shot.delay, x, y, hitbox_x, hitbox_y,
145 shot.angle, shot.speed, shot.damage, shot.orb, shot.shot_type, 172 shot.angle, shot.speed, shot.damage, shot.orb, shot.type,
146 shot.sprite, shot.unknown1) = data 173 shot.sprite, shot.unknown1) = data
147 174
148 shot.pos = (x, y) 175 shot.pos = (x, y)
149 shot.hitbox = (hitbox_x, hitbox_y) 176 shot.hitbox = (hitbox_x, hitbox_y)
150 177