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 EdgeFilter91 """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.ZString291115 _len = 4116 117 # implementation of esp-specific basic types 118 119 # XXX nothing here yet... 120 121 @staticmethod123 """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 @classmethod138 """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 records158160 """A class to contain the actual esp data.""" 164239166 """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 181183 """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 196198 """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?')218220 """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 228230 return self.tes4.get_detail_child_nodes(edge_filter=edge_filter)231233 return self.tes4.get_detail_child_names(edge_filter=edge_filter)234 235 # GlobalNode 236244270246 # 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 262264 """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 None275277 # 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
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Mon Oct 10 19:04:15 2011 | http://epydoc.sourceforge.net |