if SERVER then return end --prevents it from running on the server -- Delimiter constants local STRING_START = "<<>>" local STRING_END = "<<>>" -- Encodes a string to be safely stored in XML attributes local function encodeAttributeString(str) if str == nil then return "" end -- First, escape any existing delimiters in the content local escaped = str:gsub(STRING_START, "\\<<>>") :gsub(STRING_END, "\\<<>>") -- Clean the string of control characters escaped = blue_prints.clean_string(escaped) -- Wrap the escaped content with delimiters return STRING_START .. escaped .. STRING_END end -- Decodes a string that was stored in XML attributes local function decodeAttributeString(str) if str == nil then return "" end -- Check if the string has our delimiters local content = str:match(STRING_START .. "(.-)" .. STRING_END) if not content then -- If no delimiters found, return original string (for backward compatibility) return str end -- Unescape any escaped delimiters return content:gsub("\\<<>>", STRING_START) :gsub("\\<<>>", STRING_END) end local function processLabelStrings(xmlString) -- Process header and body attributes in Label tags return xmlString:gsub('(]-)(header="([^"]*)")', function(prefix, full, content) return prefix .. 'header="' .. encodeAttributeString(content) .. '"' end):gsub('(]-)(body="([^"]*)")', function(prefix, full, content) return prefix .. 'body="' .. encodeAttributeString(content) .. '"' end) end -- Function to process input/output node labels local function processNodeLabels(xmlString) -- Pattern to match InputNode or OutputNode sections local function processNodeSection(nodeSection) -- Process ConnectionLabelOverride tags within the node section return nodeSection:gsub('(]-value=)"([^"]*)"', function(prefix, content) -- Don't re-encode if already encoded if content:match("^" .. STRING_START .. ".*" .. STRING_END .. "$") then return prefix .. '"' .. content .. '"' end return prefix .. '"' .. encodeAttributeString(content) .. '"' end) end -- Process InputNode sections xmlString = xmlString:gsub("()", processNodeSection) -- Process OutputNode sections xmlString = xmlString:gsub("()", processNodeSection) return xmlString end -- Modified function to add encoded attributes to components local function add_encoded_attribute_to_component(xmlContent, targetId, attributeName, attributeValue) -- First encode the attribute value local encodedValue = encodeAttributeString(attributeValue) -- Function to add the attribute to the specific Component element local function modifyComponent(componentString) local id = componentString:match('id="(%d+)"') if id and tonumber(id) == targetId then -- Create the full attribute string with the encoded value local attributeStr = string.format('%s=%s', attributeName, encodedValue) return componentString:gsub('/>$', ' ' .. blue_prints.escapePercent(attributeStr) .. ' />') else return componentString end end -- Find the CircuitBox element local circuitBoxStart, circuitBoxEnd = xmlContent:find('') local circuitBoxEndTag = xmlContent:find('', circuitBoxEnd) if not circuitBoxStart or not circuitBoxEndTag then print("CircuitBox element not found") return xmlContent end -- Extract the CircuitBox content local circuitBoxContent = xmlContent:sub(circuitBoxEnd + 1, circuitBoxEndTag - 1) -- Modify the specific Component element local modifiedCircuitBoxContent = circuitBoxContent:gsub('', modifyComponent) -- Replace the original CircuitBox content with the modified content return xmlContent:sub(1, circuitBoxEnd) .. modifiedCircuitBoxContent .. xmlContent:sub(circuitBoxEndTag) end -- Function to extract components and their IDs local function extractComponents(xmlString) local components = {} for id, position in xmlString:gmatch(' tonumber(second_id) then --print("comparing" .. first_id .. " to " .. second_id) xmlContent = swap_lines_in_string(xmlContent, first_line_number, second_line_number) something_in_xml_changed = true end end if something_in_xml_changed then return put_components_in_order(xmlContent) end --print(xmlContent) return xmlContent end local function remove_attribute_from_components(xmlContent, attributeName) -- Function to remove the specified attribute from a component local function removeAttribute(componentString) -- Pattern to match traditional XML attributes local pattern1 = '%s*' .. attributeName .. '="[^"]+"' -- Pattern to match encoded strings with delimiters local pattern2 = '%s*' .. attributeName .. '=<<>>[^<]*<<>>' -- Remove both types of attributes local result = componentString:gsub(pattern1, '') result = result:gsub(pattern2, '') return result end -- Find all Component elements and process them local function processComponents(content) return content:gsub('', removeAttribute) end -- Find the CircuitBox element local circuitBoxStart, circuitBoxEnd = xmlContent:find('') local circuitBoxEndTag = xmlContent:find('', circuitBoxEnd) if not circuitBoxStart or not circuitBoxEndTag then print("CircuitBox element not found") return xmlContent end -- Extract and process the CircuitBox content local circuitBoxContent = xmlContent:sub(circuitBoxEnd + 1, circuitBoxEndTag - 1) local modifiedCircuitBoxContent = processComponents(circuitBoxContent) -- Replace the original CircuitBox content with the modified content return xmlContent:sub(1, circuitBoxEnd) .. modifiedCircuitBoxContent .. xmlContent:sub(circuitBoxEndTag) end local function clean_component_whitespace(xmlContent) local function cleanComponent(componentString) -- Don't touch anything between STRINGSTART and STRINGEND local parts = {} local lastPos = 1 -- Find start and end positions of all encoded strings local startPos = componentString:find('<<>>', lastPos, true) while startPos do local endPos = componentString:find('<<>>', startPos, true) if not endPos then break end -- Add the part before the encoded string local beforePart = componentString:sub(lastPos, startPos - 1) beforePart = beforePart:gsub('%s+', ' ') -- collapse multiple spaces to single space table.insert(parts, beforePart) -- Add the encoded string unchanged table.insert(parts, componentString:sub(startPos, endPos + 13)) lastPos = endPos + 14 startPos = componentString:find('<<>>', lastPos, true) end -- Add any remaining part after the last encoded string if lastPos <= #componentString then local remaining = componentString:sub(lastPos) remaining = remaining:gsub('%s+', ' ') table.insert(parts, remaining) end return table.concat(parts) end -- Find all Component tags and process them local circuitBoxStart, circuitBoxEnd = xmlContent:find('') local circuitBoxEndTag = xmlContent:find('', circuitBoxEnd) if not circuitBoxStart or not circuitBoxEndTag then return xmlContent end local circuitBoxContent = xmlContent:sub(circuitBoxEnd + 1, circuitBoxEndTag - 1) local modifiedContent = circuitBoxContent:gsub('', cleanComponent) return xmlContent:sub(1, circuitBoxEnd) .. modifiedContent .. xmlContent:sub(circuitBoxEndTag) end -- Function to round a number to the nearest integer local function round(num) return math.floor(num + 0.5) end -- Function to round the position and size attributes in a Label element local function round_attributes(label) -- Round the position attribute if label:find('position="') then local pos_x, pos_y = label:match('position="([^,]+),([^"]+)"') local rounded_position = string.format('position="%d,%d"', round(tonumber(pos_x)), round(tonumber(pos_y))) label = label:gsub('position="[^"]+"', rounded_position) end -- Round the size attribute if label:find('size="') then local size_x, size_y = label:match('size="([^,]+),([^"]+)"') local rounded_size = string.format('size="%d,%d"', round(tonumber(size_x)), round(tonumber(size_y))) label = label:gsub('size="[^"]+"', rounded_size) end if label:find('pos="') then local pos_x, pos_y = label:match('pos="([^,]+),([^"]+)"') local rounded_position = string.format('pos="%d,%d"', round(tonumber(pos_x)), round(tonumber(pos_y))) label = label:gsub('pos="[^"]+"', rounded_position) end return label end -- Function to process the entire XML string local function round_position_values(xml_string) local processed_string = "" -- Process each line of the XML string for line in xml_string:gmatch("[^\r\n]+") do -- Find and process Label elements if line:find("