Mercurial > touhou
comparison pytouhou/formats/exe.py @ 229:5afc75f71fed
Add “SHT” support to EoSD, and do a little cleanup.
author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
---|---|
date | Fri, 30 Dec 2011 18:37:06 +0100 |
parents | |
children | 1c24a6d93c1b |
comparison
equal
deleted
inserted
replaced
228:8f4cd1c01d22 | 229:5afc75f71fed |
---|---|
1 # -*- encoding: utf-8 -*- | |
2 ## | |
3 ## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com> | |
4 ## Copyright (C) 2011 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> | |
5 ## | |
6 ## This program is free software; you can redistribute it and/or modify | |
7 ## it under the terms of the GNU General Public License as published | |
8 ## by the Free Software Foundation; version 3 only. | |
9 ## | |
10 ## This program is distributed in the hope that it will be useful, | |
11 ## but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 ## GNU General Public License for more details. | |
14 ## | |
15 | |
16 from struct import Struct, unpack | |
17 from pytouhou.utils.pe import PEFile | |
18 | |
19 from pytouhou.utils.helpers import get_logger | |
20 | |
21 logger = get_logger(__name__) | |
22 | |
23 | |
24 SQ2 = 2. ** 0.5 / 2. | |
25 | |
26 | |
27 class Shot(object): | |
28 def __init__(self): | |
29 self.interval = 0 | |
30 self.delay = 0 | |
31 self.pos = (0., 0.) | |
32 self.hitbox = (0., 0.) | |
33 self.angle = 0. | |
34 self.speed = 0. | |
35 self.damage = 0 | |
36 self.orb = 0 | |
37 self.shot_type = 0 | |
38 self.sprite = 0 | |
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 | |
44 | |
45 class SHT(object): | |
46 def __init__(self): | |
47 #self.unknown1 = None | |
48 #self.bombs = 0. | |
49 #self.unknown2 = None | |
50 self.hitbox = 4. | |
51 self.graze_hitbox = 42. | |
52 self.autocollection_speed = 8. | |
53 self.item_hitbox = 38. | |
54 # No percentage_of_cherry_loss_on_die | |
55 self.point_of_collection = 128 #TODO: find the real default. | |
56 self.horizontal_vertical_speed = 0. | |
57 self.horizontal_vertical_focused_speed = 0. | |
58 self.diagonal_speed = 0. | |
59 self.diagonal_focused_speed = 0. | |
60 self.shots = {} | |
61 | |
62 | |
63 @classmethod | |
64 def find_character_records(self, file, pe_file): | |
65 format = Struct('<4f2I') | |
66 data_section = [section for section in pe_file.sections if section.Name.startswith('.data')][0] | |
67 text_section = [section for section in pe_file.sections if section.Name.startswith('.text')][0] | |
68 data_va = pe_file.image_base + data_section.VirtualAddress | |
69 text_va = pe_file.image_base + text_section.VirtualAddress | |
70 | |
71 for addr in xrange(data_va, data_va + data_section.SizeOfRawData, 4): | |
72 for character_id in xrange(4): | |
73 pe_file.seek_to_va(addr + character_id * 24) | |
74 speed1, speed2, speed3, speed4, ptr1, ptr2 = format.unpack(file.read(format.size)) | |
75 | |
76 if not (all(0. < x < 8. for x in (speed1, speed2, speed3, speed4)) | |
77 and speed2 <= speed1 | |
78 and 0 <= ptr1 - text_va < text_section.SizeOfRawData - 8 | |
79 and 0 <= ptr2 - text_va < text_section.SizeOfRawData - 8): | |
80 break | |
81 | |
82 pe_file.seek_to_va(ptr1 + 4) | |
83 shtptr1, = unpack('<I', file.read(4)) | |
84 pe_file.seek_to_va(ptr2 + 4) | |
85 shtptr2, = unpack('<I', file.read(4)) | |
86 | |
87 if not (0 <= shtptr1 - data_va < data_section.SizeOfRawData | |
88 and 0 <= shtptr2 - data_va < data_section.SizeOfRawData): | |
89 break | |
90 else: # XXX: Obscure python feature! This gets executed if there were no break! | |
91 yield addr | |
92 | |
93 | |
94 @classmethod | |
95 def read(cls, file): | |
96 pe_file = PEFile(file) | |
97 | |
98 character_records_va = list(cls.find_character_records(file, pe_file))[0] | |
99 | |
100 characters = [] | |
101 shots_offsets = [] | |
102 for character in xrange(4): | |
103 sht = cls() | |
104 | |
105 pe_file.seek_to_va(character_records_va + 6*4*character) | |
106 | |
107 data = unpack('<4f2I', file.read(6*4)) | |
108 (speed, speed_focused, speed_unknown1, speed_unknown2, | |
109 shots_func_offset, shots_func_offset_focused) = data | |
110 | |
111 sht.horizontal_vertical_speed = speed | |
112 sht.horizontal_vertical_focused_speed = speed_focused | |
113 sht.diagonal_speed = speed * SQ2 | |
114 sht.diagonal_focused_speed = speed_focused * SQ2 | |
115 | |
116 # Read from “push” operand | |
117 pe_file.seek_to_va(shots_func_offset + 4) | |
118 offset = unpack('<I', file.read(4))[0] | |
119 shots_offsets.append(offset) | |
120 | |
121 characters.append(sht) | |
122 | |
123 character = 0 | |
124 for shots_offset in shots_offsets: | |
125 pe_file.seek_to_va(shots_offset) | |
126 | |
127 level_count = 9 | |
128 levels = [] | |
129 for i in xrange(level_count): | |
130 shots_count, power, offset = unpack('<III', file.read(3*4)) | |
131 levels.append((shots_count, power, offset)) | |
132 | |
133 sht = characters[character] | |
134 sht.shots = {} | |
135 | |
136 for shots_count, power, offset in levels: | |
137 sht.shots[power] = [] | |
138 pe_file.seek_to_va(offset) | |
139 | |
140 for i in xrange(shots_count): | |
141 shot = Shot() | |
142 | |
143 data = unpack('<HH6fHBBhh', file.read(36)) | |
144 (shot.interval, shot.delay, x, y, hitbox_x, hitbox_y, | |
145 shot.angle, shot.speed, shot.damage, shot.orb, shot.shot_type, | |
146 shot.sprite, shot.unknown1) = data | |
147 | |
148 shot.pos = (x, y) | |
149 shot.hitbox = (hitbox_x, hitbox_y) | |
150 | |
151 sht.shots[power].append(shot) | |
152 | |
153 character += 1 | |
154 | |
155 | |
156 return characters | |
157 |