Package pyffi :: Package formats :: Package rockstar :: Package dir_
[hide private]
[frames] | no frames]

Source Code for Package pyffi.formats.rockstar.dir_

  1  """ 
  2  :mod:`pyffi.formats.rockstar.dir_` --- DIR (.dir) 
  3  ================================================= 
  4   
  5  A .dir file simply contains a list of files. 
  6   
  7  Implementation 
  8  -------------- 
  9   
 10  .. autoclass:: DirFormat 
 11     :show-inheritance: 
 12     :members: 
 13   
 14  Regression tests 
 15  ---------------- 
 16   
 17  Read a DIR file 
 18  ^^^^^^^^^^^^^^^ 
 19   
 20  >>> # check and read dir file 
 21  >>> stream = open('tests/rockstar/dir/test.dir', 'rb') 
 22  >>> data = DirFormat.Data() 
 23  >>> data.inspect(stream) 
 24  >>> # do some stuff with header? 
 25  >>> # XXX nothing for now 
 26  >>> # read directory 
 27  >>> data.read(stream) 
 28  >>> len(data.files) 
 29  2 
 30  >>> data.files[0].offset 
 31  0 
 32  >>> data.files[0].size 
 33  1 
 34  >>> data.files[0].name 
 35  'hello.txt' 
 36   
 37  Parse all DIR files in a directory tree 
 38  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
 39   
 40  >>> for stream, data in DirFormat.walkData('tests/rockstar/dir'): 
 41  ...     print(stream.name) 
 42  tests/rockstar/dir/test.dir 
 43   
 44  Create an DIR file from scratch and write to file 
 45  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
 46   
 47  >>> data = DirFormat.Data() 
 48  >>> from tempfile import TemporaryFile 
 49  >>> stream = TemporaryFile() 
 50  >>> data.write(stream) 
 51  """ 
 52   
 53  # ***** BEGIN LICENSE BLOCK ***** 
 54  # 
 55  # Copyright (c) 2007-2011, Python File Format Interface 
 56  # All rights reserved. 
 57  # 
 58  # Redisdirbution and use in source and binary forms, with or without 
 59  # modification, are permitted provided that the following conditions 
 60  # are met: 
 61  # 
 62  #    * Redisdirbutions of source code must retain the above copyright 
 63  #      notice, this list of conditions and the following disclaimer. 
 64  # 
 65  #    * Redisdirbutions in binary form must reproduce the above 
 66  #      copyright notice, this list of conditions and the following 
 67  #      disclaimer in the documentation and/or other materials provided 
 68  #      with the disdirbution. 
 69  # 
 70  #    * Neither the name of the Python File Format Interface 
 71  #      project nor the names of its condirbutors may be used to endorse 
 72  #      or promote products derived from this software without specific 
 73  #      prior written permission. 
 74  # 
 75  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONDIRBUTORS 
 76  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 77  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 78  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 79  # COPYRIGHT OWNER OR CONDIRBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 80  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 81  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 82  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 83  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, SDIRCT 
 84  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 85  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 86  # POSSIBILITY OF SUCH DAMAGE. 
 87  # 
 88  # ***** END LICENSE BLOCK ***** 
 89   
 90  from itertools import chain, izip 
 91  import struct 
 92  import os 
 93  import re 
 94   
 95  import pyffi.object_models.xml 
 96  import pyffi.object_models.common 
 97  from pyffi.object_models.xml.basic import BasicBase 
 98  import pyffi.object_models 
 99  from pyffi.utils.graph import EdgeFilter 
100   
101 -class DirFormat(pyffi.object_models.xml.FileFormat):
102 """This class implements the DIR format.""" 103 xml_file_name = 'dir.xml' 104 # where to look for dir.xml 105 xml_file_path = [os.path.dirname(__file__)] 106 # file name regular expression match 107 RE_FILENAME = re.compile(r'^.*\.dir$', re.IGNORECASE) 108 109 # basic types 110 UInt = pyffi.object_models.common.UInt
111 - class String(pyffi.object_models.common.FixedString):
112 _len = 24
113
114 - class Data(pyffi.object_models.FileFormat.Data):
115 """A class to contain the actual dir data.""" 116
117 - def __init__(self, folder=None):
118 """Initialize empty file list, or take list of files from 119 a folder. 120 """ 121 self.files = [] 122 offset = 0 123 if folder: 124 for filename in sorted(os.listdir(folder)): 125 if not os.path.isfile(os.path.join(folder, filename)): 126 continue 127 fileinfo = os.stat(os.path.join(folder, filename)) 128 file_record = DirFormat.File() 129 file_record.offset = offset 130 file_record.size = (fileinfo.st_size + 2047) // 2048 131 file_record.name = filename 132 self.files.append(file_record) 133 offset += file_record.size
134
135 - def inspect_quick(self, stream):
136 """Quickly checks if stream contains DIR data, by looking at 137 the first 36 bytes. 138 139 :param stream: The stream to inspect. 140 :type stream: file 141 """ 142 pos = stream.tell() 143 try: 144 off1, size1, file1 = struct.unpack( 145 "<II24s", stream.read(32)) 146 try: 147 off2, = struct.unpack( 148 "<I", stream.read(4)) 149 except struct.error: 150 # this happens if .dir only contains one file record 151 off2 = size1 152 if not(off1 == 0 153 #and size1 < 1000 # heuristic 154 and off2 == size1 155 and file1[-1] == '\x00'): 156 raise ValueError('Not a Rockstar DIR file.') 157 finally: 158 stream.seek(pos)
159 160 # overriding pyffi.object_models.FileFormat.Data methods 161
162 - def inspect(self, stream):
163 """Quickly checks if stream contains DIR data. 164 165 :param stream: The stream to inspect. 166 :type stream: file 167 """ 168 pos = stream.tell() 169 try: 170 self.inspect_quick(stream) 171 finally: 172 stream.seek(pos)
173 174
175 - def read(self, stream):
176 """Read a dir file. 177 178 :param stream: The stream from which to read. 179 :type stream: ``file`` 180 """ 181 self.inspect_quick(stream) 182 self.files = [] 183 pos = stream.tell() 184 while stream.read(1): 185 stream.seek(pos) 186 file_record = DirFormat.File() 187 file_record.read(stream, self) 188 self.files.append(file_record) 189 pos = stream.tell()
190
191 - def write(self, stream):
192 """Write a dir file. 193 194 :param stream: The stream to which to write. 195 :type stream: ``file`` 196 """ 197 for file_record in self.files: 198 file_record.write(stream, self)
199 200 # GlobalNode 201
202 - def get_global_child_nodes(self, edge_filter=EdgeFilter()):
203 return self.files
204
205 - def unpack(self, image, folder):
206 """Unpack all files, whose data resides in the given 207 image, into the given folder. 208 """ 209 for file_record in self.files: 210 image.seek(file_record.offset * 2048) 211 with open(os.path.join(folder, file_record.name), 'wb') as data: 212 data.write(image.read(file_record.size * 2048))
213
214 - def pack(self, image, folder):
215 """Pack all files, whose data resides in the given folder, 216 into the given image. 217 """ 218 for file_record in self.files: 219 if image.tell() != file_record.offset * 2048: 220 raise ValueError('file offset mismatch') 221 with open(os.path.join(folder, file_record.name), 'rb') as data: 222 allbytes = data.read() 223 size = file_record.size * 2048 224 if len(allbytes) > size: 225 raise ValueError('file larger than record size') 226 image.write(allbytes) 227 if len(allbytes) < size: 228 image.write('\x00' * (size - len(allbytes)))
229 230 if __name__=='__main__': 231 import doctest 232 doctest.testmod() 233