gruesome 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,428 +3,428 @@ require_relative 'memory'
3
3
  require_relative 'abbreviation_table'
4
4
 
5
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
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
430
  end