gruesome 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,430 @@
1
+ require_relative 'header'
2
+ require_relative 'memory'
3
+ require_relative 'abbreviation_table'
4
+
5
+ module Gruesome
6
+ module Z
7
+ class ObjectTable
8
+ def initialize(memory)
9
+ @memory = memory
10
+ @header = Header.new(@memory.contents)
11
+ @abbreviation_table = AbbreviationTable.new(@memory)
12
+
13
+ @address = @header.object_tbl_addr
14
+
15
+ if @header.version <= 3
16
+ # versions 1-3 have 32 entries
17
+ @num_properties = 31
18
+ else
19
+ # versions 4+ have 32 entries
20
+ @num_properties = 63
21
+ end
22
+
23
+ @object_tree_address = @address + @num_properties*2
24
+
25
+ if @header.version <= 3
26
+ @attributes_size = 4
27
+ @object_id_size = 1
28
+
29
+ # Entry:
30
+ #
31
+ # Attributes (4 bytes)
32
+ # Parent Object ID (1 byte)
33
+ # Sibling Object ID (1 byte)
34
+ # Child Object ID (1 byte)
35
+ # Properties Address (2 bytes)
36
+ else
37
+ @attributes_size = 6
38
+ @object_id_size = 2
39
+
40
+ # Entry: (Object IDs are 2 bytes)
41
+ #
42
+ # Attributes (6 bytes)
43
+ # Parent Object ID (2 byte)
44
+ # Sibling Object ID (2 byte)
45
+ # Child Object ID (2 byte)
46
+ # Properties Address (2 bytes)
47
+ end
48
+
49
+ @obj_entry_size = @attributes_size + (@object_id_size * 3) + 2
50
+
51
+ # Check machine endianess
52
+ @endian = [1].pack('S')[0] == 1 ? 'little' : 'big'
53
+ end
54
+
55
+ def property_default(index)
56
+ index -= 1
57
+ index %= @num_properties
58
+
59
+ # The first thing in the table is the property defaults list
60
+ # So, simply lookup the 16-bit word at the entry given by index
61
+ prop_default_addr = @address + index*2
62
+ @memory.force_readw(prop_default_addr)
63
+ end
64
+
65
+ def object_entry(index)
66
+ index -= 1
67
+ obj_entry_addr = @object_tree_address + (index * @obj_entry_size)
68
+
69
+ attributes_address = obj_entry_addr
70
+ cur_addr = attributes_address
71
+
72
+ # read the attributes
73
+ attribute_bytes = []
74
+ @attributes_size.times do
75
+ attribute_bytes << @memory.force_readb(cur_addr)
76
+ cur_addr += 1
77
+ end
78
+
79
+ # read the object ids of associated objects
80
+ ids = (0..2).map do |i|
81
+ if @object_id_size == 2
82
+ id = @memory.force_readw(cur_addr)
83
+ cur_addr += 2
84
+ else
85
+ id = @memory.force_readb(cur_addr)
86
+ cur_addr += 1
87
+ end
88
+ id
89
+ end
90
+
91
+ properties_address = @memory.force_readw(cur_addr)
92
+
93
+ # return a hash with the information of the entry
94
+ { :attributes_address => attributes_address,
95
+ :attributes => attribute_bytes,
96
+ :parent_id => ids[0],
97
+ :sibling_id => ids[1],
98
+ :child_id => ids[2],
99
+ :properties_address => properties_address }
100
+ end
101
+
102
+ def object_get_child(index)
103
+ object_entry(index)[:child_id]
104
+ end
105
+
106
+ def object_get_sibling(index)
107
+ object_entry(index)[:sibling_id]
108
+ end
109
+
110
+ def object_get_parent(index)
111
+ object_entry(index)[:parent_id]
112
+ end
113
+
114
+ def object_set_child(index, child_id)
115
+ entry = object_entry(index)
116
+ addr = entry[:attributes_address] + @attributes_size + @object_id_size*2
117
+
118
+ if @object_id_size == 1
119
+ @memory.force_writeb(addr, child_id)
120
+ else
121
+ @memory.force_writew(addr, child_id)
122
+ end
123
+ end
124
+
125
+ def object_set_sibling(index, sibling_id)
126
+ entry = object_entry(index)
127
+ addr = entry[:attributes_address] + @attributes_size + @object_id_size
128
+
129
+ if @object_id_size == 1
130
+ @memory.force_writeb(addr, sibling_id)
131
+ else
132
+ @memory.force_writew(addr, sibling_id)
133
+ end
134
+ end
135
+
136
+ def object_set_parent(index, parent_id)
137
+ entry = object_entry(index)
138
+ addr = entry[:attributes_address] + @attributes_size
139
+
140
+ if @object_id_size == 1
141
+ @memory.force_writeb(addr, parent_id)
142
+ else
143
+ @memory.force_writew(addr, parent_id)
144
+ end
145
+ end
146
+
147
+ def object_insert_object(index, new_child)
148
+ # remove child from old parent
149
+ if object_get_parent(new_child) != 0
150
+ object_remove_object new_child
151
+ end
152
+
153
+ # add to new parent
154
+ object_set_sibling(new_child, object_get_child(index))
155
+ object_set_child(index, new_child)
156
+ object_set_parent(new_child, index)
157
+ end
158
+
159
+ def object_remove_object(index)
160
+ old_parent = object_get_parent(index)
161
+ object_set_parent(index, 0)
162
+
163
+ if old_parent != 0
164
+ current_child = object_get_child(old_parent)
165
+ if current_child == index
166
+ # if we are the primary child of our parent, we removed
167
+ # ourselves... so we have to set a new primary child
168
+ # to be my sibling
169
+ object_set_child(old_parent, object_get_sibling(index))
170
+ else
171
+ # if not, we are within some list of siblings
172
+ # we need to remove ourself from this linked list
173
+ # whose head is current_child
174
+
175
+ last = current_child
176
+ sibling = object_get_sibling(current_child)
177
+ while sibling != 0
178
+ if sibling == index
179
+ # found me!
180
+ # now, set the sibling of the previous one
181
+ # to my sibling (removing me from the chain)
182
+ object_set_sibling(last, object_get_sibling(index))
183
+
184
+ # break out of the loop
185
+ break
186
+ end
187
+ last = sibling
188
+ sibling = object_get_sibling(sibling)
189
+ end
190
+ end
191
+ end
192
+ end
193
+
194
+ def object_short_text(index)
195
+ entry = object_entry(index)
196
+ prop_address = entry[:properties_address]
197
+
198
+ # text_len is the maximum number of bytes the string can have
199
+ text_len = @memory.force_readb(prop_address) * 2
200
+ chrs = @memory.force_readzstr(prop_address+1, text_len)[1]
201
+
202
+ ZSCII.translate(0, @header.version, chrs, @abbreviation_table)
203
+ end
204
+
205
+ def attribute_number_to_byte_bit_pair(attribute_number)
206
+ # get the byte and bit number
207
+ #
208
+ # Note: it counts from the MSB, so it has to
209
+ # reverse the bit_number by subtracting from 7
210
+ #
211
+ # That is, attribute 7 is byte 0, bit 0
212
+ # attribute 17 is byte 2, bit 6
213
+ # etc
214
+
215
+ byte_number = (attribute_number / 8).to_i
216
+ bit_number = attribute_number % 8
217
+ bit_number = 7 - bit_number
218
+
219
+ { :byte => byte_number, :bit => bit_number }
220
+ end
221
+ private :attribute_number_to_byte_bit_pair
222
+
223
+ def object_has_attribute?(index, attribute_number)
224
+ entry = object_entry(index)
225
+
226
+ location = attribute_number_to_byte_bit_pair(attribute_number)
227
+ attribute_byte = entry[:attributes][location[:byte]]
228
+ mask = 1 << location[:bit]
229
+
230
+ (attribute_byte & mask) > 0
231
+ end
232
+
233
+ def object_set_attribute(index, attribute_number)
234
+ entry = object_entry(index)
235
+
236
+ location = attribute_number_to_byte_bit_pair(attribute_number)
237
+ attribute_byte = entry[:attributes][location[:byte]]
238
+ mask = 1 << location[:bit]
239
+
240
+ attribute_byte |= mask
241
+
242
+ byte_addr = entry[:attributes_address] + location[:byte]
243
+ @memory.force_writeb(byte_addr, attribute_byte)
244
+ end
245
+
246
+ def object_clear_attribute(index, attribute_number)
247
+ entry = object_entry(index)
248
+
249
+ location = attribute_number_to_byte_bit_pair(attribute_number)
250
+ attribute_byte = entry[:attributes][location[:byte]]
251
+ mask = 1 << location[:bit]
252
+
253
+ attribute_byte &= ~mask
254
+
255
+ byte_addr = entry[:attributes_address] + location[:byte]
256
+ @memory.force_writeb(byte_addr, attribute_byte)
257
+ end
258
+
259
+ def object_get_size_and_number(prop_address)
260
+ size = 0
261
+ property_number = 0
262
+ if @header.version <= 3
263
+ size = @memory.force_readb(prop_address)
264
+ prop_address += 1
265
+
266
+ # when size is 0, this is the end of the list
267
+ if size == 0
268
+ return nil
269
+ end
270
+
271
+ # size is considered 32 times the data size minus one
272
+ # property number is the first 5 bits
273
+ property_number = size & 0b11111
274
+
275
+ size = size >> 5
276
+ size += 1
277
+ else
278
+ # in versions 4+, the size and property number are given
279
+ # as two bytes.
280
+ #
281
+ # if bit 7 is set in the first byte:
282
+ # The second byte is read where first 6 bits
283
+ # indicate size, bit 6 is ignored, bit 7 is set
284
+ # if bit 7 is clear in the first byte:
285
+ # bit 6 is clear = size of 1
286
+ # bit 6 is set = size of 2
287
+ #
288
+ # property number is first 6 bits of first byte
289
+
290
+ first_byte = @memory.force_readb(prop_address)
291
+ prop_address += 1
292
+
293
+ property_number = first_byte & 0b111111
294
+ if first_byte & 0b10000000 > 0
295
+ # bit 7 is set
296
+ second_byte = @memory.force_readb(prop_address)
297
+ prop_address += 1
298
+
299
+ size = second_byte & 0b111111
300
+
301
+ # a size of 0 is allowed, and will indicate a size of 64
302
+ if size == 0
303
+ size = 64
304
+ end
305
+ else
306
+ # bit 7 is clear
307
+ if first_byte & 0b1000000 > 0
308
+ # bit 6 is set
309
+ size = 2
310
+ else
311
+ # bit 6 is clear
312
+ size = 1
313
+ end
314
+ end
315
+ end
316
+
317
+ { :size => size, :property_number => property_number, :property_data_address => prop_address }
318
+ end
319
+
320
+ def object_properties(index)
321
+ entry = object_entry(index)
322
+ prop_address = entry[:properties_address]
323
+
324
+ # get the length of the string in bytes
325
+ text_len = @memory.force_readb(prop_address) * 2
326
+ prop_address += 1
327
+
328
+ prop_address += text_len
329
+
330
+ properties = {}
331
+
332
+ while true do
333
+ entry_info = object_get_size_and_number(prop_address)
334
+
335
+ if entry_info == nil
336
+ break
337
+ end
338
+
339
+ # regardless of version, we now have the property size and the number
340
+
341
+ properties[entry_info[:property_number]] = {:size => entry_info[:size], :property_data_address => entry_info[:property_data_address]}
342
+ prop_address = entry_info[:property_data_address] + entry_info[:size]
343
+ end
344
+
345
+ properties
346
+ end
347
+
348
+ def object_get_property(index, property_number)
349
+ properties = object_properties(index)
350
+
351
+ property_data = []
352
+
353
+ property_info = properties[property_number]
354
+
355
+ if property_info == nil
356
+ property_data = nil
357
+ else
358
+ address = property_info[:property_data_address]
359
+ properties[property_number][:size].times do
360
+ property_data << @memory.force_readb(address)
361
+ address += 1
362
+ end
363
+ end
364
+
365
+ property_data
366
+ end
367
+
368
+ def object_get_property_addr(index, property_number)
369
+ properties = object_properties(index)
370
+
371
+ property_info = properties[property_number]
372
+
373
+ if property_info == nil
374
+ 0
375
+ else
376
+ property_info[:property_data_address]
377
+ end
378
+ end
379
+
380
+ def object_get_property_data_size_from_address(address)
381
+ if @header.version <= 3
382
+ prop_address = address - 1
383
+ else
384
+ first_byte = @memory.force_readb(address-1)
385
+
386
+ if first_byte & 0b10000000 > 0
387
+ prop_address = address - 2
388
+ else
389
+ prop_address = address - 1
390
+ end
391
+ end
392
+ object_get_size_and_number(prop_address)[:size]
393
+ end
394
+
395
+ def object_get_property_word(index, property_number)
396
+ property_data = object_get_property(index, property_number)
397
+ if property_data == nil
398
+ property_default(property_number)
399
+ elsif property_data.size > 1
400
+ if @endian == 'little'
401
+ (property_data[1] << 8) | property_data[0]
402
+ else
403
+ (property_data[0] << 8) | property_data[1]
404
+ end
405
+ else
406
+ property_data[0]
407
+ end
408
+ end
409
+
410
+ def object_set_property_word(index, property_number, value)
411
+ properties = object_properties(index)
412
+
413
+ property_info = properties[property_number]
414
+
415
+ if property_info == nil
416
+ raise "put_prop attempted to set a property value for a property that did not exist"
417
+ else
418
+ if property_info[:size] == 1
419
+ value &= 0xff
420
+ @memory.force_writeb(property_info[:property_data_address], value)
421
+ elsif property_info[:size] == 2
422
+ @memory.force_writew(property_info[:property_data_address], value)
423
+ else
424
+ raise "put_prop attempted to set a property whose data size is larger than a word"
425
+ end
426
+ end
427
+ end
428
+ end
429
+ end
430
+ end