--context is always -eq to the highest possible subfolder in the mods dir, this can be ether the mod or modpack root folder. local DIR_DELIM = debug.getinfo(1, "S").source:match("[\\/]") local context = debug and debug.getinfo(1, "S").source:match("mods[/\\][/\\]?([^/]*)") or "context" local cwdpath = minetest and (minetest.get_modpath(minetest.get_current_modname()) .. DIR_DELIM .. "lua5_1") or debug.getinfo(1, "S").source:match("^(.+)[/\\]"):sub(2) --setup lib env local print = _G[context]["log"] --some string function moved here, for more info see: string.lua function string:trim() return self:match("^%s*(.-)%s*$") end --[[ function string:split (p2Sep, p3Patern) if p2Sep == nil then p2Sep = "%s" end if p3Patern == nil then p3Patern = table.concat({"([^", p2Sep, "]+)"}, "") end local returns = {} for _ in string.gmatch(self, p3Patern) do table.insert(returns, _) end return returns end --]] function string:startsWith(p2Needle) -- self == haystack return self:sub(1, #p2Needle) == p2Needle end function string:endsWith(p2Needle) -- self == haystack return p2Needle == "" or self:sub(-#p2Needle) == p2Needle end function string:charCount(p2Char) local count = 0 for _ in string.gmatch(self, p2Char) do count = count + 1 end return count end --end string.lua snipet _G[context].colors ={ reset = "\27[0m", red = "\27[31m", green = "\27[32m", yellow = "\27[33m", blue = "\27[34m", magenta = "\27[35m", cyan = "\27[36m", white= "\27[37m" } _G[context].forFileAsLines=function(filePath, funcHandle, ignoreReturn) local file = io.open(filePath, "r") --if file is context:open then local returns = {} if not file then error(_G[context].colors.red.."access error: reading non-existant file, @" .._G[context].colors.cyan ..filePath.._G[context].colors.reset) end if file then --itarate over each line and pass to funkyFunc for line in file:lines() do local linedata=line:trim()--:sub(1,-2) --remove spooky char at the end of a line. If file uses \r\n, it may require (1,-3) linedata=funcHandle and (funcHandle(linedata)) or linedata --buggy:TODO --this data subject to brakeages, test for errors here: --print("<"..linedata..">") if not ignoreReturn then --used for large files, to avoid memory bloat. table.insert(returns, linedata) end end file:close() return returns else return {} end end -- _G[context].forFileAsIniObj = function(filePath, ignoreHeaders) local debug_print=false local returns = {} --object to store data about ini file as a hashmap table returns[""]={} --default object to store global values before a header tag('[' or ']') is found in the file stream local rpointer = "" -- by default pointer set to "" by default local _ = debug_print and print("<"..filePath..">") local _ = _G[context].forFileAsLines(filePath, function (line) --in ini, ';' may be used as a comment by default. --in the future this may be abstracted into a larger function with more customization if not line:startsWith(";") and string.charCount(line, " ") ~= #line and #line ~= 0 then --discard comments or empty lines --if is header "[*]" local _ = debug_print and print(line) if line:match("^%[.*%]$") then if not ignoreHeaders then rpointer = line:sub(2,-2):trim() --remove [ and ] from the header and use as str:returnPointer returns[rpointer]= returns[rpointer] or {} end --else if is key value pair "*=*" elseif line:match("^.+=.+$") and line:charCount("=") == 1 then local spointerarray = line:split("=") spointerarray[1]=spointerarray[1]:trim() spointerarray[2]=spointerarray[2]:trim() --If parser fails due to missing '"', use raw string as a fall back. --minetest.parse_json create an unmutable error with no way to prevent it, --just return as string ill deal with this later. :( --Upon reflection this function should always return a string, --since it may be used in multiple use case and so should leave parsing decisions to the caller --_G[context].parse(spointerarray[2]) or returns[rpointer][spointerarray[1]] = spointerarray[2] else --else is error error(table.concat({"error reading: ", filePath, "@^", line, "$"},"")) end end end, true) --if the global array table was never used, delete it. if #returns[""] == 0 and true then returns[""]=nil end return returns end --Why the fuck is "true" true in the context of a conditional, is lua just as bad as javascript? assert(true == _G[context].parse(_G[context].forFileAsIniObj(cwdpath..DIR_DELIM.."test.ini", false)["testdata"]["success"]))