# # This module should be mixed-in by classes that contain microchunk data, e.g. # Element, ChangePage, Level. # module MicroChunkUser # TODO: mc(Get|Set)Value are probably low-level and maybe should be private # Note: the above was written when they had four lines, now most of the code from MicroChunkLoader#_(get|set)val was moved here # TODO: I'm not sure if they can be private, because they are called from MicroChunkUser#mcCopyValuesTo... maybe protected? def mcGetValue(valueId) return valueId if !valueId.is_a? Array return nil if valueId.length == 0 valueId = [TYPE_INTEGER] + valueId if valueId[0].is_a? Symbol dataType, attribute, index = valueId dataType = TYPE_INTEGER if !dataType return self.send("mcGet_"+attribute, index) if self.respond_to?("mcGet_"+attribute) return mcGetValue([attribute])[index] if index self.send(attribute) end def mcSetValue(valueId, val) return if !valueId.is_a? Array return if valueId.length == 0 valueId = [TYPE_INTEGER] + valueId if valueId[0].is_a? Symbol dataType, attribute, index = valueId dataType = TYPE_INTEGER if !dataType val = Misc.toBool(val) if data_type == TYPE_BOOLEAN val = val.to_s if data_type == TYPE_STRING && !val.is_a? String return self.send("mcSet_"+attribute, val, index) if self.respond_to?("mcSet_"+attribute) return (mcGetValue([attribute])[index] = val) if index self.send(attribute.to_s+"=", val) val end def mcSetToDefaults(ruleset) ruleset.each do |rule| propertyId, defaultValue, confId, element, saveType, maxNumEntities, defaultNumEntities = rule byteMask = confId & CONF_MASK_BYTES defaultValue = obj.mcGetValue(defaultValue) # if it's inside an array that's not yet initialized yet, initialize it if propertyId[-1].is_a? Integer && !mcGetValue([propertyId[-2]]) mcSetValue([propertyId[-2]], []) end if byteMask == CONF_MASK_MULTI_BYTES plainDefaultValue = defaultValue case dataType when TYPE_STRING defaultValue = plainDefaultValue[0, maxNumEntities] when TYPE_ELEMENT_LIST defaultValue = [] defaultValue.setDefault { plainDefaultValue } defaultValue.length = defaultNumEntities when TYPE_CONTENT_LIST defaultValue = [] defaultValue.setDefault { Misc::array(3, 3, plainDefaultValue) } defaultValue.length = defaultNumEntities end end mcSetValue(propertyId, defaultValue) end end def mcCopyValuesTo(dest,ruleset) ruleset.each do |rule| propertyId, defaultValue, confId, element, saveType, maxNumEntities, defaultNumEntities = rule byteMask = confId & CONF_MASK_BYTES if byteMask == CONF_MASK_MULTI_BYTES if dataType == TYPE_STRING dest.mcSetValue(propertyId, mcGetValue(propertyId)[0,maxNumEntities]) elsif data_type == TYPE_ELEMENT_LIST src_elements = mcGetValue(propertyId) dest_elements = dest.mcGetValue(propertyId) src_elements.slice!(maxNumEntities...src_elements.length) if src_elements.length > maxNumEntities dest_elements.slice!(src_elements.length...dest_elements.length) if dest_elements.length > src_elements.length src_elements.length.times do |j| dest_elements[j] = src_elements[j] end elsif data_type == TYPE_CONTENT_LIST src_content = mcGetValue(propertyId) dest_content = dest.mcGetValue(propertyId) # shorten the arrays if they're too big src_content.slice!(maxNumEntities...src_content.length) if src_content.length > maxNumEntities dest_content.slice!(src_content.length...dest_content.length) if dest_content.length > src_content.length Misc.loop3d(src_elements.length, 3, 3) do |x, y, c| dest_content[c][x][y] = src_content[c][x][y] end end else # constant size configuration data (1, 2 or 4 bytes) obj.mcSetValue(propertyId, mcGetValue(propertyId)) end end end def mcLoad(file, ruleset, zelement, real_element) micro_chunk_size = 0 zconf_type = file.get8 zbyte_mask = zconf_type & CONF_MASK_BYTES element_found = false micro_chunk_size += 1 if byte_mask == CONF_MASK_MULTI_BYTES num_bytes = file.get16be buffer = file.getCharacters(num_bytes) ruleset.each do |rule| propertyId, defaultValue, confId, element, saveType, maxNumEntities, defaultNumEntities = rule next if element != zelement || confId != zconf_type data_type = propertyId[0].is_a? Symbol ? TYPE_INTEGER : propertyId[0] num_entities = num_bytes / case data_type when TYPE_ELEMENT, TYPE_ELEMENT_LIST then 2 when TYPE_CONTENT, TYPE_CONTENT_LIST then 3*3 else 1 end if num_entities > maxNumEntities error(ERR_WARN, "truncating number of entities for element #{element} from #{num_entities} to #{maxNumEntities}") num_entities = maxNumEntities end element_found = true case data_type when TYPE_STRING mcSetValue(propertyId, buffer[0, num_entities]) when TYPE_ELEMENT_LIST element_array = mcGetValue(propertyId) num_entities.times do |j| element_array[j] = getMappedElement(buffer[i*2] << 8 | b[i*2+1]) end element_array.length = num_entities when TYPE_CONTENT_LIST content = mcGetValue(propertyId) Misc::loop3d(num_entities, 3, 3) do |c, x, y| byte_pos = (c*3*3 + y*3 + x) * 2 getMappedElement( b[byte_pos] << 8 | b[byte_pos + 1] ) end content.length = num_entities else element_found = false end break end micro_chunk_size += 2 + num_bytes else # constant size configuration data (1, 2 or 4 bytes) value = case byte_mask when CONF_MASK_1_BYTE then file.get8 when CONF_MASK_2_BYTE then file.get16be when CONF_MASK_4_BYTE then file.get32be else 0 end ruleset.each do |rule| propertyId, defaultValue, confId, element, saveType, maxNumEntities, defaultNumEntities = rule next if element != zelement || confId != zconf_type # it isn't the rule we're searching for data_type = propertyId[0].is_a? Symbol ? TYPE_INTEGER : propertyId[0] value = getMappedElement(value) if data_type == TYPE_ELEMENT mcSetValue(propertyId, value) element_found = true break end micro_chunk_size += case byte_mask when CONF_MASK_1_BYTE then 1 when CONF_MASK_2_BYTE then 2 when CONF_MASK_4_BYTE then 4 else 0 end end if !element_found type = case byte_mask when CONF_MASK_1_BYTE then "8_BIT" when CONF_MASK_2_BYTE then "16_BIT" when CONF_MASK_4_BYTE then "32_BIT" else "BYTES" end token = conf_type & CONF_MASK_TOKEN error(ERR_WARN, "cannot load micro chunk 'CONF_VALUE_#{type}|#{token}' value for element #{real_element} ['#{EL_NAME(real_element)}']") end micro_chunk_size end end