changeset 608:725bd24235a2

Make ANM0 pure-python again, by moving the Cython-dependent ANM class into its own module.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Wed, 26 Nov 2014 14:00:17 +0100
parents 9dbc234ea087
children 23b9418e4b2f
files pytouhou/formats/animation.pxd pytouhou/formats/animation.pyx pytouhou/formats/anm0.pxd pytouhou/formats/anm0.py pytouhou/formats/anm0.pyx pytouhou/game/sprite.pxd setup.py
diffstat 7 files changed, 272 insertions(+), 260 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pytouhou/formats/animation.pxd	Wed Nov 26 14:00:17 2014 +0100
@@ -0,0 +1,7 @@
+cdef class Animation:
+    cdef public long version
+    cdef public unicode first_name, secondary_name
+    cdef public dict sprites, scripts
+    cdef public object texture
+
+    cdef double size_inv[2]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pytouhou/formats/animation.pyx	Wed Nov 26 14:00:17 2014 +0100
@@ -0,0 +1,27 @@
+# -*- encoding: utf-8 -*-
+##
+## Copyright (C) 2014 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.
+##
+
+cdef class Animation:
+    def __init__(self):
+        self.version = 0
+        self.size_inv[:] = [0, 0]
+        self.first_name = None
+        self.secondary_name = None
+        self.sprites = {}
+        self.scripts = {}
+
+    property size:
+        def __set__(self, tuple value):
+            width, height = value
+            self.size_inv[:] = [1. / <double>width, 1. / <double>height]
--- a/pytouhou/formats/anm0.pxd	Wed Nov 26 13:37:07 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-cdef class ANM:
-    cdef public long version
-    cdef public unicode first_name, secondary_name
-    cdef public dict sprites, scripts
-    cdef public object texture
-
-    cdef double size_inv[2]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pytouhou/formats/anm0.py	Wed Nov 26 14:00:17 2014 +0100
@@ -0,0 +1,235 @@
+# -*- encoding: utf-8 -*-
+##
+## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com>
+##
+## 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.
+##
+
+"""ANM0 files handling.
+
+This module provides classes for handling the ANM0 file format.
+The ANM0 format is a format used in Touhou 6: EoSD to describe sprites
+and animations.
+Almost everything rendered in the game is described by an ANM0 file.
+"""
+
+from struct import pack, unpack
+from pytouhou.utils.helpers import read_string, get_logger
+
+from pytouhou.formats import WrongFormatError
+from pytouhou.formats.animation import Animation
+from pytouhou.formats.thtx import Texture
+
+
+logger = get_logger(__name__)
+
+#TODO: refactor/clean up
+
+
+class Script(list):
+    def __init__(self):
+        list.__init__(self)
+        self.interrupts = {}
+
+
+
+class ANM0(Animation):
+    _instructions = {0: {0: ('', 'delete'),
+                         1: ('I', 'set_sprite'),
+                         2: ('ff', 'set_scale'),
+                         3: ('I', 'set_alpha'),
+                         4: ('BBBx', 'set_color'),
+                         5: ('I', 'jump'),
+                         7: ('', 'toggle_mirrored'),
+                         9: ('fff', 'set_3d_rotations'),
+                         10: ('fff', 'set_3d_rotations_speed'),
+                         11: ('ff', 'set_scale_speed'),
+                         12: ('ii', 'fade'),
+                         13: ('', 'set_blendmode_add'),
+                         14: ('', 'set_blendmode_alphablend'),
+                         15: ('', 'keep_still'),
+                         16: ('ii', 'set_random_sprite'),
+                         17: ('fff', 'set_3d_translation'),
+                         18: ('fffi', 'move_to_linear'),
+                         19: ('fffi', 'move_to_decel'),
+                         20: ('fffi', 'move_to_accel'),
+                         21: ('', 'wait'),
+                         22: ('i', 'interrupt_label'),
+                         23: ('', 'set_corner_relative_placement'),
+                         24: ('', 'wait_ex'),
+                         25: ('i', 'set_allow_offset'), #TODO: better name
+                         26: ('i', 'set_automatic_orientation'),
+                         27: ('f', 'shift_texture_x'),
+                         28: ('f', 'shift_texture_y'),
+                         29: ('i', 'set_visible'),
+                         30: ('ffi', 'scale_in'),
+                         31: ('i', None)},
+
+                     2: {0: ('', 'noop'),
+                         1: ('', 'delete'),
+                         2: ('', 'keep_still'),
+                         3: ('I', 'set_sprite'),
+                         4: ('II', 'jump_bis'),
+                         5: ('III', 'jump_ex'),
+                         6: ('fff', 'set_3d_translation'),
+                         7: ('ff', 'set_scale'),
+                         8: ('I', 'set_alpha'),
+                         9: ('BBBx', 'set_color'),
+                         10: ('', 'toggle_mirrored'),
+                         12: ('fff', 'set_3d_rotations'),
+                         13: ('fff', 'set_3d_rotations_speed'),
+                         14: ('ff', 'set_scale_speed'),
+                         15: ('ii', 'fade'),
+                         16: ('I', 'set_blendmode'),
+                         17: ('fffi', 'move_to_linear'),
+                         18: ('fffi', 'move_to_decel'),
+                         19: ('fffi', 'move_to_accel'),
+                         20: ('', 'wait'),
+                         21: ('i', 'interrupt_label'),
+                         22: ('', 'set_corner_relative_placement'),
+                         23: ('', 'wait_ex'),
+                         24: ('i', 'set_allow_offset'), #TODO: better name
+                         25: ('i', 'set_automatic_orientation'),
+                         26: ('f', 'shift_texture_x'),
+                         27: ('f', 'shift_texture_y'),
+                         28: ('i', 'set_visible'),
+                         29: ('ffi', 'scale_in'),
+                         30: ('i', None),
+                         31: ('I', None),
+                         32: ('IIfff', 'move_in_linear_bis'),
+                         33: ('IIBBBx', 'change_color_in'),
+                         34: ('III', 'fade_bis'),
+                         35: ('IIfff', 'rotate_in_bis'),
+                         36: ('IIff', 'scale_in_bis'),
+                         37: ('II', 'set_int'),
+                         38: ('ff', 'set_float'),
+                         42: ('ff', 'decrement_float'),
+                         50: ('fff', 'add_float'),
+                         52: ('fff', 'substract_float'),
+                         55: ('III', 'divide_int'),
+                         59: ('II', 'set_random_int'),
+                         60: ('ff', 'set_random_float'),
+                         69: ('IIII', 'branch_if_not_equal'),
+                         79: ('I', 'wait_duration'),
+                         80: ('I', None)}}
+
+
+    @classmethod
+    def read(cls, file):
+        anm_list = []
+        start_offset = 0
+        while True:
+            file.seek(start_offset)
+            nb_sprites, nb_scripts, zero1 = unpack('<III', file.read(12))
+            width, height, fmt, unknown1 = unpack('<IIII', file.read(16))
+            first_name_offset, unused, secondary_name_offset = unpack('<III', file.read(12))
+            version, unknown2, texture_offset, has_data, next_offset, unknown3 = unpack('<IIIIII', file.read(24))
+
+            if version == 0:
+                assert zero1 == 0
+                assert unknown3 == 0
+                assert has_data == 0
+            elif version == 2:
+                assert zero1 == 0
+                assert secondary_name_offset == 0
+                assert has_data == 1 # Can be false but we don’t support that yet.
+            else:
+                raise WrongFormatError(version)
+
+            instructions = cls._instructions[version]
+
+            sprite_offsets = [unpack('<I', file.read(4))[0] for i in range(nb_sprites)]
+            script_offsets = [unpack('<II', file.read(8)) for i in range(nb_scripts)]
+
+            self = cls()
+
+            self.size = (width, height)
+            self.version = version
+
+            # Names
+            if first_name_offset:
+                file.seek(start_offset + first_name_offset)
+                self.first_name = read_string(file, 32, 'ascii') #TODO: 32, really?
+            if secondary_name_offset:
+                file.seek(start_offset + secondary_name_offset)
+                self.secondary_name = read_string(file, 32, 'ascii') #TODO: 32, really?
+
+
+            # Sprites
+            for offset in sprite_offsets:
+                file.seek(start_offset + offset)
+                idx, x, y, width, height = unpack('<Iffff', file.read(20))
+                self.sprites[idx] = x, y, width, height
+
+
+            # Scripts
+            for i, offset in script_offsets:
+                self.scripts[i] = Script()
+                instruction_offsets = []
+                file.seek(start_offset + offset)
+                while True:
+                    instruction_offsets.append(file.tell() - (start_offset + offset))
+                    if version == 0:
+                        time, opcode, size = unpack('<HBB', file.read(4))
+                    elif version == 2:
+                        opcode, size, time, mask = unpack('<HHHH', file.read(8))
+                        if opcode == 0xffff:
+                            break
+                        size -= 8
+                    data = file.read(size)
+                    if opcode in instructions:
+                        args = unpack('<%s' % instructions[opcode][0], data)
+                    else:
+                        args = (data,)
+                        logger.warn('unknown opcode %d', opcode)
+
+                    self.scripts[i].append((time, opcode, args))
+                    if version == 0 and opcode == 0:
+                        break
+
+                # Translate offsets to instruction pointers and register interrupts
+                for instr_offset, (j, instr) in zip(instruction_offsets, enumerate(self.scripts[i])):
+                    time, opcode, args = instr
+                    if version == 0:
+                        if opcode == 5:
+                            args = (instruction_offsets.index(args[0]),)
+                        elif opcode == 22:
+                            interrupt = args[0]
+                            self.scripts[i].interrupts[interrupt] = j + 1
+                    elif version == 2:
+                        if opcode == 4:
+                            args = (instruction_offsets.index(args[0]), args[1])
+                        elif opcode == 5:
+                            args = (args[0], instruction_offsets.index(args[1]), args[2])
+                        elif opcode == 21:
+                            interrupt = args[0]
+                            self.scripts[i].interrupts[interrupt] = j + 1
+                        elif opcode == 69:
+                            args = (args[0], args[1], instruction_offsets.index(args[2]), args[3])
+                    self.scripts[i][j] = time, opcode, args
+
+            # Texture
+            if has_data:
+                file.seek(start_offset + texture_offset)
+                magic = file.read(4)
+                assert magic == b'THTX'
+                zero, fmt, width, height, size = unpack('<HHHHI', file.read(12))
+                assert zero == 0
+                data = file.read(size)
+                self.texture = Texture(width, height, fmt, data)
+
+            anm_list.append(self)
+
+            if next_offset:
+                start_offset += next_offset
+            else:
+                break
+
+        return anm_list
--- a/pytouhou/formats/anm0.pyx	Wed Nov 26 13:37:07 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,250 +0,0 @@
-# -*- encoding: utf-8 -*-
-##
-## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com>
-##
-## 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.
-##
-
-"""ANM0 files handling.
-
-This module provides classes for handling the ANM0 file format.
-The ANM0 format is a format used in Touhou 6: EoSD to describe sprites
-and animations.
-Almost everything rendered in the game is described by an ANM0 file.
-"""
-
-from struct import pack, unpack
-from pytouhou.utils.helpers import read_string, get_logger
-
-from pytouhou.formats import WrongFormatError
-from pytouhou.formats.thtx import Texture
-
-
-logger = get_logger(__name__)
-
-#TODO: refactor/clean up
-
-
-class Script(list):
-    def __init__(self):
-        list.__init__(self)
-        self.interrupts = {}
-
-
-
-cdef class ANM:
-    def __init__(self):
-        self.version = 0
-        self.size_inv[:] = [0, 0]
-        self.first_name = None
-        self.secondary_name = None
-        self.sprites = {}
-        self.scripts = {}
-
-    property size:
-        def __set__(self, tuple value):
-            width, height = value
-            self.size_inv[:] = [1. / <double>width, 1. / <double>height]
-
-
-
-class ANM0(object):
-    _instructions = {0: {0: ('', 'delete'),
-                         1: ('I', 'set_sprite'),
-                         2: ('ff', 'set_scale'),
-                         3: ('I', 'set_alpha'),
-                         4: ('BBBx', 'set_color'),
-                         5: ('I', 'jump'),
-                         7: ('', 'toggle_mirrored'),
-                         9: ('fff', 'set_3d_rotations'),
-                         10: ('fff', 'set_3d_rotations_speed'),
-                         11: ('ff', 'set_scale_speed'),
-                         12: ('ii', 'fade'),
-                         13: ('', 'set_blendmode_add'),
-                         14: ('', 'set_blendmode_alphablend'),
-                         15: ('', 'keep_still'),
-                         16: ('ii', 'set_random_sprite'),
-                         17: ('fff', 'set_3d_translation'),
-                         18: ('fffi', 'move_to_linear'),
-                         19: ('fffi', 'move_to_decel'),
-                         20: ('fffi', 'move_to_accel'),
-                         21: ('', 'wait'),
-                         22: ('i', 'interrupt_label'),
-                         23: ('', 'set_corner_relative_placement'),
-                         24: ('', 'wait_ex'),
-                         25: ('i', 'set_allow_offset'), #TODO: better name
-                         26: ('i', 'set_automatic_orientation'),
-                         27: ('f', 'shift_texture_x'),
-                         28: ('f', 'shift_texture_y'),
-                         29: ('i', 'set_visible'),
-                         30: ('ffi', 'scale_in'),
-                         31: ('i', None)},
-
-                     2: {0: ('', 'noop'),
-                         1: ('', 'delete'),
-                         2: ('', 'keep_still'),
-                         3: ('I', 'set_sprite'),
-                         4: ('II', 'jump_bis'),
-                         5: ('III', 'jump_ex'),
-                         6: ('fff', 'set_3d_translation'),
-                         7: ('ff', 'set_scale'),
-                         8: ('I', 'set_alpha'),
-                         9: ('BBBx', 'set_color'),
-                         10: ('', 'toggle_mirrored'),
-                         12: ('fff', 'set_3d_rotations'),
-                         13: ('fff', 'set_3d_rotations_speed'),
-                         14: ('ff', 'set_scale_speed'),
-                         15: ('ii', 'fade'),
-                         16: ('I', 'set_blendmode'),
-                         17: ('fffi', 'move_to_linear'),
-                         18: ('fffi', 'move_to_decel'),
-                         19: ('fffi', 'move_to_accel'),
-                         20: ('', 'wait'),
-                         21: ('i', 'interrupt_label'),
-                         22: ('', 'set_corner_relative_placement'),
-                         23: ('', 'wait_ex'),
-                         24: ('i', 'set_allow_offset'), #TODO: better name
-                         25: ('i', 'set_automatic_orientation'),
-                         26: ('f', 'shift_texture_x'),
-                         27: ('f', 'shift_texture_y'),
-                         28: ('i', 'set_visible'),
-                         29: ('ffi', 'scale_in'),
-                         30: ('i', None),
-                         31: ('I', None),
-                         32: ('IIfff', 'move_in_linear_bis'),
-                         33: ('IIBBBx', 'change_color_in'),
-                         34: ('III', 'fade_bis'),
-                         35: ('IIfff', 'rotate_in_bis'),
-                         36: ('IIff', 'scale_in_bis'),
-                         37: ('II', 'set_int'),
-                         38: ('ff', 'set_float'),
-                         42: ('ff', 'decrement_float'),
-                         50: ('fff', 'add_float'),
-                         52: ('fff', 'substract_float'),
-                         55: ('III', 'divide_int'),
-                         59: ('II', 'set_random_int'),
-                         60: ('ff', 'set_random_float'),
-                         69: ('IIII', 'branch_if_not_equal'),
-                         79: ('I', 'wait_duration'),
-                         80: ('I', None)}}
-
-
-    @classmethod
-    def read(cls, file):
-        anm_list = []
-        start_offset = 0
-        while True:
-            file.seek(start_offset)
-            nb_sprites, nb_scripts, zero1 = unpack('<III', file.read(12))
-            width, height, fmt, unknown1 = unpack('<IIII', file.read(16))
-            first_name_offset, unused, secondary_name_offset = unpack('<III', file.read(12))
-            version, unknown2, texture_offset, has_data, next_offset, unknown3 = unpack('<IIIIII', file.read(24))
-
-            if version == 0:
-                assert zero1 == 0
-                assert unknown3 == 0
-                assert has_data == 0
-            elif version == 2:
-                assert zero1 == 0
-                assert secondary_name_offset == 0
-                assert has_data == 1 # Can be false but we don’t support that yet.
-            else:
-                raise WrongFormatError(version)
-
-            instructions = cls._instructions[version]
-
-            sprite_offsets = [unpack('<I', file.read(4))[0] for i in range(nb_sprites)]
-            script_offsets = [unpack('<II', file.read(8)) for i in range(nb_scripts)]
-
-            self = ANM()
-
-            self.size = (width, height)
-            self.version = version
-
-            # Names
-            if first_name_offset:
-                file.seek(start_offset + first_name_offset)
-                self.first_name = read_string(file, 32, 'ascii') #TODO: 32, really?
-            if secondary_name_offset:
-                file.seek(start_offset + secondary_name_offset)
-                self.secondary_name = read_string(file, 32, 'ascii') #TODO: 32, really?
-
-
-            # Sprites
-            for offset in sprite_offsets:
-                file.seek(start_offset + offset)
-                idx, x, y, width, height = unpack('<Iffff', file.read(20))
-                self.sprites[idx] = x, y, width, height
-
-
-            # Scripts
-            for i, offset in script_offsets:
-                self.scripts[i] = Script()
-                instruction_offsets = []
-                file.seek(start_offset + offset)
-                while True:
-                    instruction_offsets.append(file.tell() - (start_offset + offset))
-                    if version == 0:
-                        time, opcode, size = unpack('<HBB', file.read(4))
-                    elif version == 2:
-                        opcode, size, time, mask = unpack('<HHHH', file.read(8))
-                        if opcode == 0xffff:
-                            break
-                        size -= 8
-                    data = file.read(size)
-                    if opcode in instructions:
-                        args = unpack('<%s' % instructions[opcode][0], data)
-                    else:
-                        args = (data,)
-                        logger.warn('unknown opcode %d', opcode)
-
-                    self.scripts[i].append((time, opcode, args))
-                    if version == 0 and opcode == 0:
-                        break
-
-                # Translate offsets to instruction pointers and register interrupts
-                for instr_offset, (j, instr) in zip(instruction_offsets, enumerate(self.scripts[i])):
-                    time, opcode, args = instr
-                    if version == 0:
-                        if opcode == 5:
-                            args = (instruction_offsets.index(args[0]),)
-                        elif opcode == 22:
-                            interrupt = args[0]
-                            self.scripts[i].interrupts[interrupt] = j + 1
-                    elif version == 2:
-                        if opcode == 4:
-                            args = (instruction_offsets.index(args[0]), args[1])
-                        elif opcode == 5:
-                            args = (args[0], instruction_offsets.index(args[1]), args[2])
-                        elif opcode == 21:
-                            interrupt = args[0]
-                            self.scripts[i].interrupts[interrupt] = j + 1
-                        elif opcode == 69:
-                            args = (args[0], args[1], instruction_offsets.index(args[2]), args[3])
-                    self.scripts[i][j] = time, opcode, args
-
-            # Texture
-            if has_data:
-                file.seek(start_offset + texture_offset)
-                magic = file.read(4)
-                assert magic == b'THTX'
-                zero, fmt, width, height, size = unpack('<HHHHI', file.read(12))
-                assert zero == 0
-                data = file.read(size)
-                self.texture = Texture(width, height, fmt, data)
-
-            anm_list.append(self)
-
-            if next_offset:
-                start_offset += next_offset
-            else:
-                break
-
-        return anm_list
--- a/pytouhou/game/sprite.pxd	Wed Nov 26 13:37:07 2014 +0100
+++ b/pytouhou/game/sprite.pxd	Wed Nov 26 14:00:17 2014 +0100
@@ -1,5 +1,5 @@
 from pytouhou.utils.interpolator cimport Interpolator
-from pytouhou.formats.anm0 cimport ANM
+from pytouhou.formats.animation cimport Animation
 
 cdef class Sprite:
     cdef public long blendfunc, frame
@@ -10,7 +10,7 @@
     cdef public Interpolator scale_interpolator, fade_interpolator
     cdef public Interpolator offset_interpolator, rotation_interpolator
     cdef public Interpolator color_interpolator
-    cdef public ANM anm
+    cdef public Animation anm
 
     cdef void *_rendering_data
 
--- a/setup.py	Wed Nov 26 13:37:07 2014 +0100
+++ b/setup.py	Wed Nov 26 14:00:17 2014 +0100
@@ -122,7 +122,7 @@
             elif extension_name == 'pytouhou.ui.anmrenderer' and not anmviewer:
                 extension_names.pop()
                 continue
-            elif package == 'pytouhou.formats' and extension_name != 'pytouhou.formats.anm0':
+            elif package == 'pytouhou.formats' and extension_name != 'pytouhou.formats.animation':
                 continue
             else:
                 compile_args = package_args