Mercurial > touhou
diff 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 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/pytouhou/formats/exe.py @@ -0,0 +1,157 @@ +# -*- encoding: utf-8 -*- +## +## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com> +## Copyright (C) 2011 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published +## by the Free Software Foundation; version 3 only. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## + +from struct import Struct, unpack +from pytouhou.utils.pe import PEFile + +from pytouhou.utils.helpers import get_logger + +logger = get_logger(__name__) + + +SQ2 = 2. ** 0.5 / 2. + + +class Shot(object): + def __init__(self): + self.interval = 0 + self.delay = 0 + self.pos = (0., 0.) + self.hitbox = (0., 0.) + self.angle = 0. + self.speed = 0. + self.damage = 0 + self.orb = 0 + self.shot_type = 0 + self.sprite = 0 + self.unknown1 = None + + def __repr__(self): + 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) + + +class SHT(object): + def __init__(self): + #self.unknown1 = None + #self.bombs = 0. + #self.unknown2 = None + self.hitbox = 4. + self.graze_hitbox = 42. + self.autocollection_speed = 8. + self.item_hitbox = 38. + # No percentage_of_cherry_loss_on_die + self.point_of_collection = 128 #TODO: find the real default. + self.horizontal_vertical_speed = 0. + self.horizontal_vertical_focused_speed = 0. + self.diagonal_speed = 0. + self.diagonal_focused_speed = 0. + self.shots = {} + + + @classmethod + def find_character_records(self, file, pe_file): + format = Struct('<4f2I') + data_section = [section for section in pe_file.sections if section.Name.startswith('.data')][0] + text_section = [section for section in pe_file.sections if section.Name.startswith('.text')][0] + data_va = pe_file.image_base + data_section.VirtualAddress + text_va = pe_file.image_base + text_section.VirtualAddress + + for addr in xrange(data_va, data_va + data_section.SizeOfRawData, 4): + for character_id in xrange(4): + pe_file.seek_to_va(addr + character_id * 24) + speed1, speed2, speed3, speed4, ptr1, ptr2 = format.unpack(file.read(format.size)) + + if not (all(0. < x < 8. for x in (speed1, speed2, speed3, speed4)) + and speed2 <= speed1 + and 0 <= ptr1 - text_va < text_section.SizeOfRawData - 8 + and 0 <= ptr2 - text_va < text_section.SizeOfRawData - 8): + break + + pe_file.seek_to_va(ptr1 + 4) + shtptr1, = unpack('<I', file.read(4)) + pe_file.seek_to_va(ptr2 + 4) + shtptr2, = unpack('<I', file.read(4)) + + if not (0 <= shtptr1 - data_va < data_section.SizeOfRawData + and 0 <= shtptr2 - data_va < data_section.SizeOfRawData): + break + else: # XXX: Obscure python feature! This gets executed if there were no break! + yield addr + + + @classmethod + def read(cls, file): + pe_file = PEFile(file) + + character_records_va = list(cls.find_character_records(file, pe_file))[0] + + characters = [] + shots_offsets = [] + for character in xrange(4): + sht = cls() + + pe_file.seek_to_va(character_records_va + 6*4*character) + + data = unpack('<4f2I', file.read(6*4)) + (speed, speed_focused, speed_unknown1, speed_unknown2, + shots_func_offset, shots_func_offset_focused) = data + + sht.horizontal_vertical_speed = speed + sht.horizontal_vertical_focused_speed = speed_focused + sht.diagonal_speed = speed * SQ2 + sht.diagonal_focused_speed = speed_focused * SQ2 + + # Read from “push” operand + pe_file.seek_to_va(shots_func_offset + 4) + offset = unpack('<I', file.read(4))[0] + shots_offsets.append(offset) + + characters.append(sht) + + character = 0 + for shots_offset in shots_offsets: + pe_file.seek_to_va(shots_offset) + + level_count = 9 + levels = [] + for i in xrange(level_count): + shots_count, power, offset = unpack('<III', file.read(3*4)) + levels.append((shots_count, power, offset)) + + sht = characters[character] + sht.shots = {} + + for shots_count, power, offset in levels: + sht.shots[power] = [] + pe_file.seek_to_va(offset) + + for i in xrange(shots_count): + shot = Shot() + + data = unpack('<HH6fHBBhh', file.read(36)) + (shot.interval, shot.delay, x, y, hitbox_x, hitbox_y, + shot.angle, shot.speed, shot.damage, shot.orb, shot.shot_type, + shot.sprite, shot.unknown1) = data + + shot.pos = (x, y) + shot.hitbox = (hitbox_x, hitbox_y) + + sht.shots[power].append(shot) + + character += 1 + + + return characters +