| Home | Trees | Indices | Help |
|
|---|
|
|
1 """
2 :mod:`pyffi.formats.esp` --- Elder Scrolls plugin/master/save files (.esp, .esm, and .ess)
3 ==========================================================================================
4
5 Implementation
6 --------------
7
8 .. autoclass:: EspFormat
9 :show-inheritance:
10 :members:
11
12 Regression tests
13 ----------------
14
15 Read a ESP file
16 ^^^^^^^^^^^^^^^
17
18 >>> # check and read esp file
19 >>> stream = open('tests/esp/test.esp', 'rb')
20 >>> data = EspFormat.Data()
21 >>> data.inspect(stream)
22 >>> # do some stuff with header?
23 >>> #data.header....
24 >>> data.read(stream)
25 >>> # do some stuff...
26
27 Parse all ESP files in a directory tree
28 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
29
30 >>> for stream, data in EspFormat.walkData('tests/esp'):
31 ... print(stream.name)
32 tests/esp/test.esp
33
34 Create an ESP file from scratch and write to file
35 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
36
37 >>> data = EspFormat.Data()
38 >>> from tempfile import TemporaryFile
39 >>> stream = TemporaryFile()
40 >>> data.write(stream)
41 """
42
43 # ***** BEGIN LICENSE BLOCK *****
44 #
45 # Copyright (c) 2007-2011, Python File Format Interface
46 # All rights reserved.
47 #
48 # Redistribution and use in source and binary forms, with or without
49 # modification, are permitted provided that the following conditions
50 # are met:
51 #
52 # * Redistributions of source code must retain the above copyright
53 # notice, this list of conditions and the following disclaimer.
54 #
55 # * Redistributions in binary form must reproduce the above
56 # copyright notice, this list of conditions and the following
57 # disclaimer in the documentation and/or other materials provided
58 # with the distribution.
59 #
60 # * Neither the name of the Python File Format Interface
61 # project nor the names of its contributors may be used to endorse
62 # or promote products derived from this software without specific
63 # prior written permission.
64 #
65 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
66 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
67 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
68 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
69 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
70 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
71 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
72 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
73 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
74 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
75 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
76 # POSSIBILITY OF SUCH DAMAGE.
77 #
78 # ***** END LICENSE BLOCK *****
79
80 import struct
81 import os
82 import re
83
84 import pyffi.object_models.xml
85 import pyffi.object_models.common
86 from pyffi.object_models.xml.basic import BasicBase
87 import pyffi.object_models
88 from pyffi.utils.graph import EdgeFilter
91 """This class implements the ESP format."""
92 xml_file_name = 'esp.xml'
93 # where to look for esp.xml and in what order:
94 # ESPXMLPATH env var, or EspFormat module directory
95 xml_file_path = [os.getenv('ESPXMLPATH'), os.path.dirname(__file__)]
96 # filter for recognizing esp files by extension
97 # .ess are users save games encoded similarly to esp files
98 # .esm are esp files with an bit set in the header.
99 RE_FILENAME = re.compile(r'^.*\.(esp|ess|esm)$', re.IGNORECASE)
100 # used for comparing floats
101 _EPSILON = 0.0001
102
103 # basic types
104 int = pyffi.object_models.common.Int
105 uint = pyffi.object_models.common.UInt
106 byte = pyffi.object_models.common.Byte
107 ubyte = pyffi.object_models.common.UByte
108 char = pyffi.object_models.common.Char
109 short = pyffi.object_models.common.Short
110 ushort = pyffi.object_models.common.UShort
111 float = pyffi.object_models.common.Float
112 uint64 = pyffi.object_models.common.UInt64
113 ZString = pyffi.object_models.common.ZString
115 _len = 4
116
117 # implementation of esp-specific basic types
118
119 # XXX nothing here yet...
120
121 @staticmethod
123 """Converts version string into an integer.
124
125 :param version_str: The version string.
126 :type version_str: str
127 :return: A version integer.
128
129 >>> hex(EspFormat.version_number('1.2'))
130 '0x102'
131 """
132 high, low = version_str.split(".")
133 return (int(high) << 8) + int(low)
134
135 @classmethod
138 """Read records by data size or by number."""
139 records = []
140 while (size > 0) if size is not None else (num_records > 0):
141 pos = stream.tell()
142 record_type = stream.read(4).decode()
143 if parent:
144 record_type = parent.__class__.__name__ + "_" + record_type
145 stream.seek(pos)
146 try:
147 record = getattr(cls, record_type)()
148 except AttributeError:
149 print("unknown record type %s; aborting" % record_type)
150 break
151 records.append(record)
152 record.read(stream, data)
153 if size is not None:
154 size -= stream.tell() - pos #slower: record.get_size()
155 else:
156 num_records -= 1
157 return records
158
160 """A class to contain the actual esp data."""
164
166 """Quickly checks if stream contains ESP data, and gets the
167 version, by looking at the first 8 bytes.
168
169 :param stream: The stream to inspect.
170 :type stream: file
171 """
172 pos = stream.tell()
173 try:
174 # XXX check that file is ESP
175 if (stream.read(4) != 'TES4'):
176 raise ValueError("Not an ESP file.")
177 finally:
178 stream.seek(pos)
179
180 # overriding pyffi.object_models.FileFormat.Data methods
181
183 """Quickly checks if stream contains ESP data, and reads the
184 header.
185
186 :param stream: The stream to inspect.
187 :type stream: file
188 """
189 pos = stream.tell()
190 try:
191 self.inspect_quick(stream)
192 # XXX read header
193 finally:
194 stream.seek(pos)
195
196
198 """Read a esp file.
199
200 :param stream: The stream from which to read.
201 :type stream: ``file``
202 """
203 self.inspect_quick(stream)
204 # read header record
205 self.tes4.read(stream, self)
206 hedr = self.tes4.get_sub_record("HEDR")
207 if not hedr:
208 print("esp file has no HEDR; aborting")
209 return
210 self.records = EspFormat._read_records(
211 stream, self, num_records=hedr.num_records)
212
213 # check if we are at the end of the file
214 if stream.read(1):
215 #raise ValueError(
216 print(
217 'end of file not reached: corrupt esp file?')
218
220 """Write a esp file.
221
222 :param stream: The stream to which to write.
223 :type stream: ``file``
224 """
225 self.tes4.write(stream, self)
226
227 # DetailNode
228
230 return self.tes4.get_detail_child_nodes(edge_filter=edge_filter)
231
233 return self.tes4.get_detail_child_names(edge_filter=edge_filter)
234
235 # GlobalNode
236
239
244
246 # read all fields
247 pyffi.object_models.xml.struct_.StructBase.read(
248 self, stream, data)
249 # read all subrecords
250 self.sub_records = EspFormat._read_records(
251 stream, data, parent=self, size=self.data_size)
252
255
256 # GlobalNode
257
260
261 # other functions
262
264 """Find first subrecord of given type."""
265 for sub_record in self.sub_records:
266 if sub_record.type == sub_record_type:
267 return sub_record
268 # not found
269 return None
270
275
277 # read all fields
278 pyffi.object_models.xml.struct_.StructBase.read(
279 self, stream, data)
280 # read all subrecords
281 self.records = EspFormat._read_records(
282 stream, data, size=self.data_size - 20)
283
286
287 # GlobalNode
288
291
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Mon Oct 10 19:04:15 2011 | http://epydoc.sourceforge.net |