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