summaryrefslogtreecommitdiff
path: root/libmat/hachoir_editor
diff options
context:
space:
mode:
authorjvoisin2014-06-08 13:39:18 +0200
committerjvoisin2014-06-08 13:39:18 +0200
commitaf36529554c39a2eefcc2c8723715e2d25b401b8 (patch)
treef54b964520bab44d1dfac725086211eaf22d3763 /libmat/hachoir_editor
parentef5a32cfd3c0555ffe5ddf413eeaae61622ebb4b (diff)
Rename the MAT folder to libmat.
This commit fixes some issues for dump operating systems who doesn't handle capitalization.
Diffstat (limited to 'libmat/hachoir_editor')
-rw-r--r--libmat/hachoir_editor/__init__.py8
-rw-r--r--libmat/hachoir_editor/field.py69
-rw-r--r--libmat/hachoir_editor/fieldset.py352
-rw-r--r--libmat/hachoir_editor/typed_field.py253
4 files changed, 682 insertions, 0 deletions
diff --git a/libmat/hachoir_editor/__init__.py b/libmat/hachoir_editor/__init__.py
new file mode 100644
index 0000000..1835676
--- /dev/null
+++ b/libmat/hachoir_editor/__init__.py
@@ -0,0 +1,8 @@
1from field import (
2 EditorError, FakeField)
3from typed_field import (
4 EditableField, EditableBits, EditableBytes,
5 EditableInteger, EditableString,
6 createEditableField)
7from fieldset import EditableFieldSet, NewFieldSet, createEditor
8
diff --git a/libmat/hachoir_editor/field.py b/libmat/hachoir_editor/field.py
new file mode 100644
index 0000000..6b1efe3
--- /dev/null
+++ b/libmat/hachoir_editor/field.py
@@ -0,0 +1,69 @@
1from hachoir_core.error import HachoirError
2from hachoir_core.field import joinPath, MissingField
3
4class EditorError(HachoirError):
5 pass
6
7class FakeField(object):
8 """
9 This class have API looks similar to Field API, but objects don't contain
10 any value: all values are _computed_ by parent methods.
11
12 Example: FakeField(editor, "abc").size calls editor._getFieldSize("abc").
13 """
14 is_field_set = False
15
16 def __init__(self, parent, name):
17 self._parent = parent
18 self._name = name
19
20 def _getPath(self):
21 return joinPath(self._parent.path, self._name)
22 path = property(_getPath)
23
24 def _getName(self):
25 return self._name
26 name = property(_getName)
27
28 def _getAddress(self):
29 return self._parent._getFieldAddress(self._name)
30 address = property(_getAddress)
31
32 def _getSize(self):
33 return self._parent.input[self._name].size
34 size = property(_getSize)
35
36 def _getValue(self):
37 return self._parent.input[self._name].value
38 value = property(_getValue)
39
40 def createDisplay(self):
41 # TODO: Returns new value if field is altered
42 return self._parent.input[self._name].display
43 display = property(createDisplay)
44
45 def _getParent(self):
46 return self._parent
47 parent = property(_getParent)
48
49 def hasValue(self):
50 return self._parent.input[self._name].hasValue()
51
52 def __getitem__(self, key):
53 # TODO: Implement this function!
54 raise MissingField(self, key)
55
56 def _isAltered(self):
57 return False
58 is_altered = property(_isAltered)
59
60 def writeInto(self, output):
61 size = self.size
62 addr = self._parent._getFieldInputAddress(self._name)
63 input = self._parent.input
64 stream = input.stream
65 if size % 8:
66 output.copyBitsFrom(stream, addr, size, input.endian)
67 else:
68 output.copyBytesFrom(stream, addr, size//8)
69
diff --git a/libmat/hachoir_editor/fieldset.py b/libmat/hachoir_editor/fieldset.py
new file mode 100644
index 0000000..b7c9b07
--- /dev/null
+++ b/libmat/hachoir_editor/fieldset.py
@@ -0,0 +1,352 @@
1from hachoir_core.dict import UniqKeyError
2from hachoir_core.field import MissingField, Float32, Float64, FakeArray
3from hachoir_core.compatibility import any
4from hachoir_core.i18n import _
5from typed_field import createEditableField
6from field import EditorError
7from collections import deque # Python 2.4
8import weakref # Python 2.1
9import struct
10
11class EditableFieldSet(object):
12 MAX_SIZE = (1 << 40) # Arbitrary limit to catch errors
13 is_field_set = True
14
15 def __init__(self, parent, fieldset):
16 self._parent = parent
17 self.input = fieldset # original FieldSet
18 self._fields = {} # cache of editable fields
19 self._deleted = set() # Names of deleted fields
20 self._inserted = {} # Inserted field (name => list of field,
21 # where name is the name after)
22
23 def array(self, key):
24 # FIXME: Use cache?
25 return FakeArray(self, key)
26
27 def _getParent(self):
28 return self._parent
29 parent = property(_getParent)
30
31 def _isAltered(self):
32 if self._inserted:
33 return True
34 if self._deleted:
35 return True
36 return any(field.is_altered for field in self._fields.itervalues())
37 is_altered = property(_isAltered)
38
39 def reset(self):
40 """
41 Reset the field set and the input field set.
42 """
43 for key, field in self._fields.iteritems():
44 if not field.is_altered:
45 del self._fields[key]
46 self.input.reset()
47
48 def __len__(self):
49 return len(self.input) \
50 - len(self._deleted) \
51 + sum( len(new) for new in self._inserted.itervalues() )
52
53 def __iter__(self):
54 for field in self.input:
55 name = field.name
56 if name in self._inserted:
57 for newfield in self._inserted[name]:
58 yield weakref.proxy(newfield)
59 if name not in self._deleted:
60 yield self[name]
61 if None in self._inserted:
62 for newfield in self._inserted[None]:
63 yield weakref.proxy(newfield)
64
65 def insertBefore(self, name, *new_fields):
66 self._insert(name, new_fields, False)
67
68 def insertAfter(self, name, *new_fields):
69 self._insert(name, new_fields, True)
70
71 def insert(self, *new_fields):
72 self._insert(None, new_fields, True)
73
74 def _insert(self, key, new_fields, next):
75 """
76 key is the name of the field before which new_fields
77 will be inserted. If next is True, the fields will be inserted
78 _after_ this field.
79 """
80 # Set unique field name
81 for field in new_fields:
82 if field._name.endswith("[]"):
83 self.input.setUniqueFieldName(field)
84
85 # Check that there is no duplicate in inserted fields
86 new_names = list(field.name for field in new_fields)
87 names_set = set(new_names)
88 if len(names_set) != len(new_fields):
89 duplicates = (name for name in names_set if 1 < new_names.count(name))
90 raise UniqKeyError(_("Duplicates in inserted fields: %s") % ", ".join(duplicates))
91
92 # Check that field names are not in input
93 if self.input: # Write special version for NewFieldSet?
94 for name in new_names:
95 if name in self.input and name not in self._deleted:
96 raise UniqKeyError(_("Field name '%s' already exists") % name)
97
98 # Check that field names are not in inserted fields
99 for fields in self._inserted.itervalues():
100 for field in fields:
101 if field.name in new_names:
102 raise UniqKeyError(_("Field name '%s' already exists") % field.name)
103
104 # Input have already inserted field?
105 if key in self._inserted:
106 if next:
107 self._inserted[key].extend( reversed(new_fields) )
108 else:
109 self._inserted[key].extendleft( reversed(new_fields) )
110 return
111
112 # Whould like to insert in inserted fields?
113 if key:
114 for fields in self._inserted.itervalues():
115 names = [item.name for item in fields]
116 try:
117 pos = names.index(key)
118 except ValueError:
119 continue
120 if 0 <= pos:
121 if next:
122 pos += 1
123 fields.rotate(-pos)
124 fields.extendleft( reversed(new_fields) )
125 fields.rotate(pos)
126 return
127
128 # Get next field. Use None if we are at the end.
129 if next:
130 index = self.input[key].index + 1
131 try:
132 key = self.input[index].name
133 except IndexError:
134 key = None
135
136 # Check that field names are not in input
137 if key not in self.input:
138 raise MissingField(self, key)
139
140 # Insert in original input
141 self._inserted[key]= deque(new_fields)
142
143 def _getDescription(self):
144 return self.input.description
145 description = property(_getDescription)
146
147 def _getStream(self):
148 # FIXME: This property is maybe a bad idea since address may be differents
149 return self.input.stream
150 stream = property(_getStream)
151
152 def _getName(self):
153 return self.input.name
154 name = property(_getName)
155
156 def _getEndian(self):
157 return self.input.endian
158 endian = property(_getEndian)
159
160 def _getAddress(self):
161 if self._parent:
162 return self._parent._getFieldAddress(self.name)
163 else:
164 return 0
165 address = property(_getAddress)
166
167 def _getAbsoluteAddress(self):
168 address = self.address
169 current = self._parent
170 while current:
171 address += current.address
172 current = current._parent
173 return address
174 absolute_address = property(_getAbsoluteAddress)
175
176 def hasValue(self):
177 return False
178# return self._parent.input[self.name].hasValue()
179
180 def _getSize(self):
181 if self.is_altered:
182 return sum(field.size for field in self)
183 else:
184 return self.input.size
185 size = property(_getSize)
186
187 def _getPath(self):
188 return self.input.path
189 path = property(_getPath)
190
191 def _getOriginalField(self, name):
192 assert name in self.input
193 return self.input[name]
194
195 def _getFieldInputAddress(self, name):
196 """
197 Absolute address of a field from the input field set.
198 """
199 assert name in self.input
200 return self.input[name].absolute_address
201
202 def _getFieldAddress(self, name):
203 """
204 Compute relative address of a field. The operation takes care of
205 deleted and resized fields.
206 """
207 #assert name not in self._deleted
208 addr = 0
209 for field in self:
210 if field.name == name:
211 return addr
212 addr += field.size
213 raise MissingField(self, name)
214
215 def _getItemByPath(self, path):
216 if not path[0]:
217 path = path[1:]
218 field = self
219 for name in path:
220 field = field[name]
221 return field
222
223 def __contains__(self, name):
224 try:
225 field = self[name]
226 return (field is not None)
227 except MissingField:
228 return False
229
230 def __getitem__(self, key):
231 """
232 Create a weak reference to an editable field (EditableField) for the
233 field with specified name. If the field is removed later, using the
234 editable field will raise a weakref.ReferenceError exception.
235
236 May raise a MissingField error if the field doesn't exist in original
237 field set or it has been deleted.
238 """
239 if "/" in key:
240 return self._getItemByPath(key.split("/"))
241 if isinstance(key, (int, long)):
242 raise EditorError("Integer index are not supported")
243
244 if (key in self._deleted) or (key not in self.input):
245 raise MissingField(self, key)
246 if key not in self._fields:
247 field = self.input[key]
248 if field.is_field_set:
249 self._fields[key] = createEditableFieldSet(self, field)
250 else:
251 self._fields[key] = createEditableField(self, field)
252 return weakref.proxy(self._fields[key])
253
254 def __delitem__(self, name):
255 """
256 Remove a field from the field set. May raise an MissingField exception
257 if the field has already been deleted.
258 """
259 parts = name.partition('/')
260 if parts[2]:
261 fieldset = self[parts[0]]
262 del fieldset[parts[2]]
263 return
264 if name in self._deleted:
265 raise MissingField(self, name)
266 self._deleted.add(name)
267 if name in self._fields:
268 del self._fields[name]
269
270 def writeInto(self, output):
271 """
272 Write the content if this field set into the output stream
273 (OutputStream).
274 """
275 if not self.is_altered:
276 # Not altered: just copy bits/bytes
277 input = self.input
278 if input.size % 8:
279 output.copyBitsFrom(input.stream,
280 input.absolute_address, input.size, input.endian)
281 else:
282 output.copyBytesFrom(input.stream,
283 input.absolute_address, input.size//8)
284 else:
285 # Altered: call writeInto() method of each field
286 realaddr = 0
287 for field in self:
288 field.writeInto(output)
289 realaddr += field.size
290
291 def _getValue(self):
292 raise EditorError('Field set "%s" has no value' % self.path)
293 def _setValue(self, value):
294 raise EditorError('Field set "%s" value is read only' % self.path)
295 value = property(_getValue, _setValue, "Value of field")
296
297class EditableFloat(EditableFieldSet):
298 _value = None
299
300 def _isAltered(self):
301 return (self._value is not None)
302 is_altered = property(_isAltered)
303
304 def writeInto(self, output):
305 if self._value is not None:
306 self._write(output)
307 else:
308 EditableFieldSet.writeInto(self, output)
309
310 def _write(self, output):
311 format = self.input.struct_format
312 raw = struct.pack(format, self._value)
313 output.writeBytes(raw)
314
315 def _setValue(self, value):
316 self.parent._is_altered = True
317 self._value = value
318 value = property(EditableFieldSet._getValue, _setValue)
319
320def createEditableFieldSet(parent, field):
321 cls = field.__class__
322 # FIXME: Support Float80
323 if cls in (Float32, Float64):
324 return EditableFloat(parent, field)
325 else:
326 return EditableFieldSet(parent, field)
327
328class NewFieldSet(EditableFieldSet):
329 def __init__(self, parent, name):
330 EditableFieldSet.__init__(self, parent, None)
331 self._name = name
332 self._endian = parent.endian
333
334 def __iter__(self):
335 if None in self._inserted:
336 return iter(self._inserted[None])
337 else:
338 raise StopIteration()
339
340 def _getName(self):
341 return self._name
342 name = property(_getName)
343
344 def _getEndian(self):
345 return self._endian
346 endian = property(_getEndian)
347
348 is_altered = property(lambda self: True)
349
350def createEditor(fieldset):
351 return EditableFieldSet(None, fieldset)
352
diff --git a/libmat/hachoir_editor/typed_field.py b/libmat/hachoir_editor/typed_field.py
new file mode 100644
index 0000000..0f0427b
--- /dev/null
+++ b/libmat/hachoir_editor/typed_field.py
@@ -0,0 +1,253 @@
1from hachoir_core.field import (
2 RawBits, Bit, Bits, PaddingBits,
3 RawBytes, Bytes, PaddingBytes,
4 GenericString, Character,
5 isInteger, isString)
6from field import FakeField
7
8class EditableField(FakeField):
9 """
10 Pure virtual class used to write editable field class.
11 """
12
13 _is_altered = False
14 def __init__(self, parent, name, value=None):
15 FakeField.__init__(self, parent, name)
16 self._value = value
17
18 def _isAltered(self):
19 return self._is_altered
20 is_altered = property(_isAltered)
21
22 def hasValue(self):
23 return True
24
25 def _computeSize(self):
26 raise NotImplementedError()
27 def _getValue(self):
28 return self._value
29 def _setValue(self, value):
30 self._value = value
31
32 def _propGetValue(self):
33 if self._value is not None:
34 return self._getValue()
35 else:
36 return FakeField._getValue(self)
37 def _propSetValue(self, value):
38 self._setValue(value)
39 self._is_altered = True
40 value = property(_propGetValue, _propSetValue)
41
42 def _getSize(self):
43 if self._value is not None:
44 return self._computeSize()
45 else:
46 return FakeField._getSize(self)
47 size = property(_getSize)
48
49 def _write(self, output):
50 raise NotImplementedError()
51
52 def writeInto(self, output):
53 if self._is_altered:
54 self._write(output)
55 else:
56 return FakeField.writeInto(self, output)
57
58class EditableFixedField(EditableField):
59 """
60 Editable field with fixed size.
61 """
62
63 def __init__(self, parent, name, value=None, size=None):
64 EditableField.__init__(self, parent, name, value)
65 if size is not None:
66 self._size = size
67 else:
68 self._size = self._parent._getOriginalField(self._name).size
69
70 def _getSize(self):
71 return self._size
72 size = property(_getSize)
73
74class EditableBits(EditableFixedField):
75 def __init__(self, parent, name, *args):
76 if args:
77 if len(args) != 2:
78 raise TypeError(
79 "Wrong argument count, EditableBits constructor prototype is: "
80 "(parent, name, [size, value])")
81 size = args[0]
82 value = args[1]
83 assert isinstance(value, (int, long))
84 else:
85 size = None
86 value = None
87 EditableFixedField.__init__(self, parent, name, value, size)
88 if args:
89 self._setValue(args[1])
90 self._is_altered = True
91
92 def _setValue(self, value):
93 if not(0 <= value < (1 << self._size)):
94 raise ValueError("Invalid value, must be in range %s..%s"
95 % (0, (1 << self._size) - 1))
96 self._value = value
97
98 def _write(self, output):
99 output.writeBits(self._size, self._value, self._parent.endian)
100
101class EditableBytes(EditableField):
102 def _setValue(self, value):
103 if not value: raise ValueError(
104 "Unable to set empty string to a EditableBytes field")
105 self._value = value
106
107 def _computeSize(self):
108 return len(self._value) * 8
109
110 def _write(self, output):
111 output.writeBytes(self._value)
112
113class EditableString(EditableField):
114 MAX_SIZE = {
115 "Pascal8": (1 << 8)-1,
116 "Pascal16": (1 << 16)-1,
117 "Pascal32": (1 << 32)-1,
118 }
119
120 def __init__(self, parent, name, *args, **kw):
121 if len(args) == 2:
122 value = args[1]
123 assert isinstance(value, str) # TODO: support Unicode
124 elif not args:
125 value = None
126 else:
127 raise TypeError(
128 "Wrong argument count, EditableString constructor prototype is:"
129 "(parent, name, [format, value])")
130 EditableField.__init__(self, parent, name, value)
131 if len(args) == 2:
132 self._charset = kw.get('charset', None)
133 self._format = args[0]
134 if self._format in GenericString.PASCAL_FORMATS:
135 self._prefix_size = GenericString.PASCAL_FORMATS[self._format]
136 else:
137 self._prefix_size = 0
138 self._suffix_str = GenericString.staticSuffixStr(
139 self._format, self._charset, self._parent.endian)
140 self._is_altered = True
141 else:
142 orig = self._parent._getOriginalField(name)
143 self._charset = orig.charset
144 self._format = orig.format
145 self._prefix_size = orig.content_offset
146 self._suffix_str = orig.suffix_str
147
148 def _setValue(self, value):
149 size = len(value)
150 if self._format in self.MAX_SIZE and self.MAX_SIZE[self._format] < size:
151 raise ValueError("String is too big")
152 self._value = value
153
154 def _computeSize(self):
155 return (self._prefix_size + len(self._value) + len(self._suffix_str))*8
156
157 def _write(self, output):
158 if self._format in GenericString.SUFFIX_FORMAT:
159 output.writeBytes(self._value)
160 output.writeBytes(self._suffix_str)
161 elif self._format == "fixed":
162 output.writeBytes(self._value)
163 else:
164 assert self._format in GenericString.PASCAL_FORMATS
165 size = GenericString.PASCAL_FORMATS[self._format]
166 output.writeInteger(len(self._value), False, size, self._parent.endian)
167 output.writeBytes(self._value)
168
169class EditableCharacter(EditableFixedField):
170 def __init__(self, parent, name, *args):
171 if args:
172 if len(args) != 3:
173 raise TypeError(
174 "Wrong argument count, EditableCharacter "
175 "constructor prototype is: (parent, name, [value])")
176 value = args[0]
177 if not isinstance(value, str) or len(value) != 1:
178 raise TypeError("EditableCharacter needs a character")
179 else:
180 value = None
181 EditableFixedField.__init__(self, parent, name, value, 8)
182 if args:
183 self._is_altered = True
184
185 def _setValue(self, value):
186 if not isinstance(value, str) or len(value) != 1:
187 raise TypeError("EditableCharacter needs a character")
188 self._value = value
189
190 def _write(self, output):
191 output.writeBytes(self._value)
192
193class EditableInteger(EditableFixedField):
194 VALID_VALUE_SIGNED = {
195 8: (-(1 << 8), (1 << 8)-1),
196 16: (-(1 << 15), (1 << 15)-1),
197 32: (-(1 << 31), (1 << 31)-1),
198 }
199 VALID_VALUE_UNSIGNED = {
200 8: (0, (1 << 8)-1),
201 16: (0, (1 << 16)-1),
202 32: (0, (1 << 32)-1)
203 }
204
205 def __init__(self, parent, name, *args):
206 if args:
207 if len(args) != 3:
208 raise TypeError(
209 "Wrong argument count, EditableInteger constructor prototype is: "
210 "(parent, name, [signed, size, value])")
211 size = args[1]
212 value = args[2]
213 assert isinstance(value, (int, long))
214 else:
215 size = None
216 value = None
217 EditableFixedField.__init__(self, parent, name, value, size)
218 if args:
219 self._signed = args[0]
220 self._is_altered = True
221 else:
222 self._signed = self._parent._getOriginalField(self._name).signed
223
224 def _setValue(self, value):
225 if self._signed:
226 valid = self.VALID_VALUE_SIGNED
227 else:
228 valid = self.VALID_VALUE_UNSIGNED
229 minval, maxval = valid[self._size]
230 if not(minval <= value <= maxval):
231 raise ValueError("Invalid value, must be in range %s..%s"
232 % (minval, maxval))
233 self._value = value
234
235 def _write(self, output):
236 output.writeInteger(
237 self.value, self._signed, self._size//8, self._parent.endian)
238
239def createEditableField(fieldset, field):
240 if isInteger(field):
241 cls = EditableInteger
242 elif isString(field):
243 cls = EditableString
244 elif field.__class__ in (RawBytes, Bytes, PaddingBytes):
245 cls = EditableBytes
246 elif field.__class__ in (RawBits, Bits, Bit, PaddingBits):
247 cls = EditableBits
248 elif field.__class__ == Character:
249 cls = EditableCharacter
250 else:
251 cls = FakeField
252 return cls(fieldset, field.name)
253