changeset 133:2cad2e84a49e

Add reading support for the MSG format.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Sun, 11 Sep 2011 07:34:34 -0700
parents fba45c37ec99
children e9ac3640280b
files pytouhou/formats/msg.py pytouhou/resource/loader.py
diffstat 2 files changed, 99 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/pytouhou/formats/msg.py
@@ -0,0 +1,90 @@
+# -*- encoding: utf-8 -*-
+##
+## 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.
+##
+
+import struct
+from struct import pack, unpack, calcsize
+from pytouhou.utils.helpers import read_string
+
+from pytouhou.utils.helpers import get_logger
+
+logger = get_logger(__name__)
+
+class MSG(object):
+    _instructions = {0: ('', None),
+                     1: ('hh', None),
+                     2: ('hh', 'change_face'),
+                     3: ('hhs', 'display_dialog_line'),
+                     4: ('I', 'pause'),
+                     5: ('hh', 'switch'),
+                     6: ('', 'add_enemy_sprite'),
+                     7: ('I', 'change_music'),
+                     8: ('hhs', 'display_character_line'),
+                     9: ('I', None),
+                     10: ('', None),
+                     11: ('', 'next_level'),
+                     12: ('', None),
+                     13: ('I', None),
+                     14: ('', None)} #TODO
+
+
+    def __init__(self):
+        self.msgs = [[]]
+
+
+    @classmethod
+    def read(cls, file):
+        entry_count, = unpack('<I', file.read(4))
+        entry_offsets = unpack('<%dI' % entry_count, file.read(4 * entry_count))
+
+        msg = cls()
+        msg.msgs = []
+
+        new_entry = True
+        while True:
+            offset = file.tell()
+
+            try:
+                time, opcode, size = unpack('<HBB', file.read(4))
+            except:
+                return
+
+            if time == 0 and opcode == 0:
+                new_entry = True
+
+            if new_entry and opcode != 0:
+                new_entry = False
+                time = -1
+                if offset in entry_offsets:
+                    #print(entry_offsets.index(offset))
+                    msg.msgs.append([])
+
+            data = file.read(size)
+
+            if opcode in cls._instructions:
+                fmt = '<%s' % cls._instructions[opcode][0]
+                if fmt.endswith('s'):
+                    fmt = fmt[:-1]
+                    fmt = '%s%ds' % (fmt, size - calcsize(fmt))
+                args = unpack(fmt, data)
+                if fmt.endswith('s'):
+                    args = args[:-1] + (args[-1].decode('shift_jis'),)
+            else:
+                args = (data, )
+                logger.warn('unknown msg opcode %d', opcode)
+
+            msg.msgs[-1].append((time, opcode, args))
+
+
+        return msg
+
--- a/pytouhou/resource/loader.py
+++ b/pytouhou/resource/loader.py
@@ -4,6 +4,7 @@ from pytouhou.formats.pbg3 import PBG3
 from pytouhou.formats.std import Stage
 from pytouhou.formats.ecl import ECL
 from pytouhou.formats.anm0 import Animations
+from pytouhou.formats.msg import MSG
 
 
 from pytouhou.resource.anmwrapper import AnmWrapper
@@ -42,6 +43,7 @@ class Loader(object):
         self.instanced_ecls = {}
         self.instanced_anms = {}
         self.instanced_stages = {}
+        self.instanced_msgs = {}
 
 
     def scan_archives(self, paths):
@@ -84,6 +86,13 @@ class Loader(object):
         return self.instanced_ecls[name]
 
 
+    def get_msg(self, name):
+        if name not in self.instanced_msgs:
+            file = self.get_file(name)
+            self.instanced_msgs[name] = MSG.read(file) #TODO: modular
+        return self.instanced_msgs[name]
+
+
     def get_anm_wrapper(self, names):
         return AnmWrapper(self.get_anm(name) for name in names)