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 5 files changed, 9 insertions(+), 247 deletions(-) [+]
line wrap: on
line diff
rename from pytouhou/formats/anm0.pxd
rename to pytouhou/formats/animation.pxd
--- a/pytouhou/formats/anm0.pxd
+++ b/pytouhou/formats/animation.pxd
@@ -1,4 +1,4 @@
-cdef class ANM:
+cdef class Animation:
     cdef public long version
     cdef public unicode first_name, secondary_name
     cdef public dict sprites, scripts
rename from pytouhou/formats/anm0.pyx
rename to pytouhou/formats/animation.pyx
--- a/pytouhou/formats/anm0.pyx
+++ b/pytouhou/formats/animation.pyx
@@ -1,6 +1,6 @@
 # -*- encoding: utf-8 -*-
 ##
-## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com>
+## 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
@@ -12,34 +12,7 @@
 ## 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:
+cdef class Animation:
     def __init__(self):
         self.version = 0
         self.size_inv[:] = [0, 0]
@@ -52,199 +25,3 @@ cdef class ANM:
         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
copy from pytouhou/formats/anm0.pyx
copy to pytouhou/formats/anm0.py
--- a/pytouhou/formats/anm0.pyx
+++ b/pytouhou/formats/anm0.py
@@ -24,6 +24,7 @@ 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
 
 
@@ -39,23 +40,7 @@ class Script(list):
 
 
 
-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):
+class ANM0(Animation):
     _instructions = {0: {0: ('', 'delete'),
                          1: ('I', 'set_sprite'),
                          2: ('ff', 'set_scale'),
@@ -163,7 +148,7 @@ class ANM0(object):
             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 = cls()
 
             self.size = (width, height)
             self.version = version
--- a/pytouhou/game/sprite.pxd
+++ b/pytouhou/game/sprite.pxd
@@ -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 class Sprite:
     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
+++ b/setup.py
@@ -122,7 +122,7 @@ for directory, _, files in os.walk('pyto
             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