~doctormo/flatr/trunk

« back to all changes in this revision

Viewing changes to bin/extract.py

  • Committer: Martin Owens
  • Date: 2010-08-13 05:59:38 UTC
  • Revision ID: doctormo@gmail.com-20100813055938-z9kh302elionyi70
Very raw initial commit.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
#
 
3
# Copyright 2009 Martin Owens
 
4
#
 
5
# This program is free software: you can redistribute it and/or modify
 
6
#  it under the terms of the GNU General Public License as published by
 
7
#  the Free Software Foundation, either version 3 of the License, or
 
8
#  (at your option) any later version.
 
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
#  You should have received a copy of the GNU General Public License
 
16
#  along with this program.  If not, see <http://www.gnu.org/licenses/>
 
17
#
 
18
"""
 
19
Extraction script for pulling apart the FLA OLE file.
 
20
"""
 
21
import sys
 
22
 
 
23
sys.path.append('../lib/')
 
24
 
 
25
from OleFile import OleFileIO
 
26
import binascii
 
27
import os
 
28
 
 
29
EXT = {
 
30
    'Media' : 'flmed',
 
31
    'Page' : 'flpg',
 
32
    'Symbol' : 'flsym',
 
33
}
 
34
 
 
35
MEDIA = {
 
36
  'CDocumentPage'    : '\x01\x80',
 
37
  'CMediaSound'      : '\x15\x81',
 
38
  'CQTAudioSettings' : 'unknown',
 
39
}
 
40
 
 
41
class stream(object):
 
42
    """String map, allow reading and seeking in a string"""
 
43
    def __init__(self, string):
 
44
        self.c = string
 
45
        self.s = 0
 
46
        self.l = len(string)
 
47
 
 
48
    def __nonzero__(self):
 
49
        return self.l > 0
 
50
 
 
51
    def split(self, chars):
 
52
        """Split up and return further sub streams"""
 
53
        result = []
 
54
        for sub in self.read(-1).split(chars):
 
55
            result.append(stream(sub))
 
56
        return result
 
57
 
 
58
    def seek(self, pos=0):
 
59
        self.s = pos
 
60
 
 
61
    def read(self, size=1):
 
62
        """Returns ASCII or bytes from the stream"""
 
63
        if size == -1:
 
64
            size = self.l - self.s
 
65
        if self.l < size+self.s:
 
66
            raise Exception("Requested size is bigger than string.")
 
67
        start = self.s
 
68
        self.seek(self.s + size)
 
69
        return self.c[start:self.s]
 
70
 
 
71
    def integer(self):
 
72
        """Return a simple integer"""
 
73
        return ord(self.read())
 
74
 
 
75
    def word(self):
 
76
        """Return a word"""
 
77
        return self.integer() + (self.integer() * 256)
 
78
 
 
79
    def string(self, size):
 
80
        """Equiv to asking for a utf-16 string"""
 
81
        if size == -1:
 
82
            size = int(self.l / 2)
 
83
        return unicode(self.read(size*2), 'utf-16')
 
84
 
 
85
    def aschex(self, size):
 
86
        """Dump out the hex of the data"""
 
87
        return binascii.hexlify(self.read(size))
 
88
 
 
89
 
 
90
class FlashSource(OleFileIO):
 
91
    """Decode an FLA flash source file"""
 
92
    def __init__(self, filename, dest_dir=None):
 
93
        self.dest = dest_dir
 
94
        if not self.dest:
 
95
            self.dest = os.path.dirname(filename)
 
96
        self.cont = {}
 
97
        print "Reading in %s" % filename
 
98
        OleFileIO.__init__(self, filename)
 
99
    
 
100
    def decode(self):
 
101
        """Decode an fla and output the files"""
 
102
        for entry in self.listdir():
 
103
            if entry[0] == 'Contents':
 
104
                self.get_content(entry)
 
105
            elif self.cont.has_key(entry[0]):
 
106
                (name, page, data) = self.cont[entry[0]]
 
107
                self.save_file(entry, name, os.path.join(self.dest, page))
 
108
 
 
109
    def _get_stream(self, fh, dest=''):
 
110
        """Read everything in"""
 
111
        r = fh.read(8192)
 
112
        while r:
 
113
            if hasattr(dest, 'write'):
 
114
                dest.write(r)
 
115
            else:
 
116
                dest += r
 
117
            r = fh.read(8192)
 
118
        return dest
 
119
 
 
120
    def get_content(self, entry):
 
121
        """Populate a hash of the content table."""
 
122
        content = stream(self._get_stream(self.openstream(entry)))
 
123
        page = None
 
124
        for c in content.split('\xff\xff\x01\x00'):
 
125
            size = c.word()
 
126
            try:
 
127
                name = c.read(size)
 
128
            except Exception:
 
129
                continue
 
130
            unit = MEDIA[name]+c.read()
 
131
            #sys.stderr.write(name+":\n")
 
132
            for i in c.split(unit):
 
133
                data = []
 
134
                for s in i.split('\xff\xfe\xff'):
 
135
                    if not s:
 
136
                        continue
 
137
                    size = s.integer()
 
138
                    pname = s.string(size)
 
139
                    rest = s.aschex(-1)
 
140
                    data.append(pname)
 
141
                    #sys.stderr.write(pname+",")
 
142
                if 'Page' in data[0]:
 
143
                    page = data[1]
 
144
                self.cont[data[0]] = [ data[1], page, data ]
 
145
 
 
146
 
 
147
    def save_file(self, entry, dest, fln='.'):
 
148
        """Save one of the media sections"""
 
149
        if '.' not in dest:
 
150
            dest += '.resource'
 
151
        dest = os.path.join(fln, dest)
 
152
        ddir = os.path.dirname(dest)
 
153
        if not os.path.exists(ddir):
 
154
            os.makedirs(ddir)
 
155
 
 
156
        print "Extracted: %s" % dest
 
157
        self._get_stream(self.openstream(entry), open(dest, 'wb'))
 
158
 
 
159
 
 
160
if __name__ == '__main__':
 
161
    fla = FlashSource(os.path.expanduser(sys.argv[1]))
 
162
    fla.decode()
 
163