Mercurial > touhou
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 |