Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- Сохранение настроек в 1
- --]]
- -- Загрузка всех нужных функций
- local string = require "string"
- local global = require "_G"
- local table = require "table"
- local table = require "table"
- local coroutine = require "coroutine"
- local open = require "io".open
- local floor = require "math".floor
- local error = global.error
- local type = global.type
- local load = global.load
- local tonumber = global.tonumber
- local setmetatable = global.setmetatable
- local next = global.next
- local ipairs = global.ipairs
- local pcall = global.pcall
- local rawget, rawset = global.rawget, global.rawset
- local format, match, sub, gsub, find, gmatch, lower, dump = string.format, string.match, string.sub, string.gsub, string.find, string.gmatch, string.lower, string.dump
- local wrap, yield = coroutine.wrap, coroutine.yield
- local insert, concat, remove = table.insert, table.concat, table.remove
- string = nil
- coroutine = nil
- table = nil
- local unserialize
- function unserialize(value, n) -- Принимает значения ключей в файле в виде строки и возвращает значения Lua
- local t = sub(value, #value)
- if t == 'x' then -- Определение типа шестнадцатиричное число
- local a = tonumber("0x"..sub(value, 1, #value-1))
- if not a then
- return a, "#"..n..": not a hex number format"
- end
- return true, a/1000
- elseif find(value, "^{(.-)}$") then -- Определение типа массив
- value = sub(value, 2, #value-1)
- local k = 1
- local a = { }
- local c, b = 0, ""
- for value in gmatch(value, "([^;,]+)") do
- c = c + select(2, gsub(value, "{", ""))
- c = c - select(2, gsub(value, "}", ""))
- if c == 0 then
- b = b..value
- b = match(b, "^%s*(.-)%s*$")
- local ok, err = unserialize(b, k)
- a[k] = err
- k = k+1
- b = ""
- else
- b = b..value..","
- end
- end
- return true, a
- elseif value == "" then -- Определение типа ничего
- return true, nil
- elseif find(t, "%d") then
- local a = tonumber(value)
- if not a then
- return a, "#"..n..": not a hex number format"
- end
- return true, a
- elseif lower(value) == "true" then -- Определение типа boolean true
- return true, true
- elseif lower(value) == "false" then -- Определение типа boolean false
- return true, false
- elseif find(value, "^0x%x+") then -- Определение шестнадцатиричного целого числа
- return true, tonumber(value)
- elseif t == 'b' then -- Определение типа boolean
- return true, value ~= "0b"
- elseif match(value, "^[\"'](.+)[\"']$") then -- Определение типа строки
- local f = load("return "..value)
- if not (f and pcall(f)) then
- return f, "#"..n..": not a string format"
- end
- return true, f()
- elseif t == 'n' then -- Определение типа число
- local a = tonumber(sub(value, 1, #value-1))
- if not a then
- return a, "#"..n..": not a number format"
- end
- return true, a
- end
- return nil, "#"..n..": couldn't read unregistered type"
- end
- local serialize
- function serialize(value, n, hex) -- Делает обратную работу функции unserialize. Аргумент hex определяет в какую СС будет кодироваться число.
- local t = type(value)
- if t == "boolean" then -- Определение типа boolean
- return true, value == true and "true" or "false" --"1b" or "0b"
- elseif t == "string" then -- Определение типа строки
- -- Из-за криво работающего string.format("%q", value), приходится так коряво поступать
- return true, '"'..gsub(gsub(gsub(gsub(gsub(value, "\\", "\\\\"), "\t", "\\t"), "\b", "\\b"), "\n", "\\n"), "\"", "\\\"")..'"'
- elseif t == "number" then -- Определение типа число
- if hex then
- return true, format("%xx", floor(value*1000))
- else
- return true, value == floor(value) and format("%d", value) or format("%.3f", value)
- end
- elseif t == "table" then -- Определение типа массив
- local s = { }
- for key, value in ipairs(value) do
- local ok, err = serialize(value, key, hex)
- if not ok then
- return ok, err
- end
- s[#s+1] = err
- end
- s = "{"..concat(s, ", ").."}"
- return true, s
- elseif t == "nil" then -- Определение типа ничего
- return true, ""
- end
- return nil, "#"..n..": couldn't code unregistered type"
- end
- local function decode(String, n) -- Расшифровывает одну строку из файла
- local key, value, comment
- do -- Ниже можно увидеть адские шаблоны для строк
- local a, b = match(String, ".+=.-[\"'].+[\"'].-#()(.+)$")
- if not a then
- a, b = match(String, "#()(.+)")
- end
- if a then
- String = sub(String, 1, a-2)
- comment = b
- end
- end
- key, value = match(String, "^%s*(.-)%s*=%s*(.-)%s*$")
- if not key then
- key, value = match(String, "^%s*(.-)%s*="), ""
- end
- local ok = true
- if key then
- ok, value = unserialize(value, n)
- else value = nil
- end
- if not ok then
- return ok, value, nil, comment
- end
- return true, key, value, comment
- end
- local function code(key, value, comment, hex, n) -- Конвертирует значения Lua в строку с настройкой
- if key then
- local ok, a = serialize(value, n, hex)
- if not ok then
- return ok, a
- end
- return true, key.." = "..a..(comment and " #"..comment or "")
- elseif comment then
- return true, "#"..comment
- end
- end
- flush = function(self, hex) -- Сохраняет данные в файл
- local file, err = open(rawget(self, "_path"), "w")
- if not file then
- return file, err
- end
- local keys, values, comments, n = self.keys, self.values, self.comments, rawget(self, "_n")
- for i = 1, n do
- local ok, value = code(keys[i], values[i], comments[i], hex, i)
- if ok then
- file:write(value)
- end
- if i ~= n then
- file:write("\n")
- end
- file:flush()
- end
- file:close()
- return true
- end
- local function get(self, key)
- local keys, values, n = self.keys, self.values, rawget(self, "_n")
- for i = 1, n do
- if key == keys[i] then
- return values[i]
- end
- end
- return nil
- end
- local function set(self, key, value)
- local keys, values, n = self.keys, self.values, rawget(self, "_n")
- for i = 1, n do
- if key == keys[i] then
- values[i] = value
- return true
- end
- end
- n = n+1
- keys[n] = key
- values[n] = value
- rawset(self, "_n", n)
- return false
- end
- --Таблица с методами
- methods = {
- object = "settings"; -- Для моих будующих программ может быть полезно
- save = flush;
- totable = function(self) -- Трансформирует объект в обычную таблицу Lua
- local keys, values, t = self.keys, self.values, { }
- for i = 1, rawget(self, "_n") do
- if keys[i] then
- t[keys[i]] = values[i]
- end
- end
- return t
- end;
- setLine = function(self, key, value, i) -- Устанавливает строку со значением
- local keys, values, n = self.keys, self.values, rawget(self, "_n")
- i = i or rawget(self, "_n")+1
- if type(i) ~= "number" then
- error("bad argument #3 to method 'setLine' (number expected, got "..type(i)..")", 2)
- end
- rawset(self, "_n", n < i and i or n)
- if key == nil then
- keys[i] = nil
- values[i] = nil
- return
- end
- if type(key) == "string" then
- keys[i] = key
- values[i] = value
- return
- end
- error("bad argument #1 to method 'setLine' (string expected, got "..type(key)..")", 2)
- end;
- getLine = function(self, i) -- Возвращает всё, что храница в строке
- if type(i) ~= "number" then
- error("bad argument to method 'setLine' (number expected, got "..type(i)..")", 2)
- end
- return self.keys[i], self.values[i], self.comments[i]
- end;
- set = function(self, key, value) -- Устанавливает ключу значение
- if type(key) ~= "string" then
- error("bad argument #1 to method 'set' (string expected, got "..type(key)..")", 2)
- end
- return set(self, key, value)
- end;
- get = function(self, key) -- Возвращает значение ключа
- if type(key) ~= "string" then
- error("bad argument to method 'get' (string expected, got "..type(key)..")", 2)
- end
- return get(self, key)
- end;
- setComment = function(self, i, comment) -- Устанавливает комментарий строки
- if type(i) ~= "number" then
- error("bad argument #1 to method 'setLine' (number expected, got "..type(i)..")", 2)
- end
- if type(comment) ~= "string" and comment ~= nil then
- error("bad argument #2 to method 'setLine' (string expected, got "..type(comment)..")", 2)
- end
- self.comments[i] = comment
- local n = rawget(self, "_n")
- rawset(self, "_n", n < i and i or n)
- end;
- getComment = function(self, i) -- Возвращает комментарий строки
- if type(i) ~= "number" then
- error("bad argument to method 'setLine' (number expected, got "..type(i)..")", 2)
- end
- return self.comments[i]
- end;
- }
- -- Метатаблица для объекта с настройками
- local meta = {
- __index = function(self, key)
- if key == "path" then
- return rawget(self, "_path")
- elseif key == "n" then
- return rawget(self, "_n")
- end
- local r = methods[key]
- if r then return r end
- if type(key) ~= "string" then
- error("bad key word (string expected, got "..type(key)..")", 2)
- end
- return get(self, key)
- end;
- __newindex = function(self, key, value)
- if key == "path" then
- if type(value) ~= "string" then
- error("bad 'path' value (string expected, got "..type(value)..")", 2)
- end
- return rawset(self, "_path", value)
- elseif key == "n" then
- error("parametr 'n' is read only", 2)
- end
- if type(key) ~= "string" then
- error("bad key word (string expected, got "..type(key)..")", 2)
- end
- return set(self, key, value)
- end;
- __pairs = function(self) -- Возвращает итератор, который проходит по всем элементам
- local keys, values, comments = self.keys, self.values, self.comments
- return wrap(function()
- for i = 1, rawget(self, "_n") do
- if keys[i] then
- yield(keys[i], values[i], comments[i])
- end
- end
- end)
- end;
- __ipairs = function(self) -- Возвращает итератор, который проходит по всем строкам
- local keys, values, comments = self.keys, self.values, self.comments
- return wrap(function()
- for i = 1, rawget(self, "_n") do
- yield(i, keys[i], values[i], comments[i])
- end
- end)
- end;
- __len = function(self) -- Возвращает количество строк, и обнавляет поле 'n' в объекте
- local n = 0
- for key, value in next, self.comments do
- n = n < key and key or n
- end
- for key, value in next, self.keys do
- n = n < key and key or n
- end
- for key, value in next, self.values do
- n = n < key and key or n
- end
- rawset(self, "_n", n)
- return n
- end;
- __tostring = function(self) -- Возвращает тип объекта и путь до файла
- return "settings: ("..rawget(self, "_path")..")"
- end;
- }
- return {
- open = function(path) -- Возвращает объект, полученный из просканированного файла
- do
- local path = type(path)
- if path ~= "string" then
- error("bad argument (string expected, got "..path..")", 2)
- end
- end
- local file, err = open(path)
- if not file then
- return nil, err
- end
- local object = {
- _path = path;
- keys = { };
- values = { };
- comments = { };
- }
- local keys, values, comments = object.keys, object.values, object.comments
- local n = 0
- for line in file:lines() do
- n = n + 1
- local ok, key, value, comment = decode(line, n)
- comments[n] = comment
- if key then
- keys[n] = key
- values[n] = value
- end
- end
- object._n = n
- file:close()
- return setmetatable(object, meta)
- end;
- convert = function(path, t) -- Конвертирует обычную таблицу Lua в объект настроек
- t = t or { }
- do
- local _t = type(path)
- if _t ~= "string" then
- error("bad argument #1 (string expected, got ".._t..")", 2)
- end
- _t = type(t)
- if _t ~= "table" then
- error("bad argument #2 (table/nil expected, got ".._t..")", 2)
- end
- end
- local i, keys, values = 0, { }, { }
- for key, value in next, t do
- i = i + 1
- if type(key) == "string" then
- keys[i] = key
- values[i] = value
- end
- end
- return setmetatable({
- _n = i;
- _path = path;
- keys = keys;
- values = values;
- comments = { };
- }, meta)
- end;
- decode = function(string, i) -- Декодер для пользователя
- if type(string) ~= "string" then
- error("bad argument (string expected, got "..type(string)..")", 2)
- end
- return decode(string, i or 1)
- end;
- code = function(key, value, comment, hex, i)
- if type(key) ~= "string" then
- error("bad argument #1 (string expected, got "..type(key)..")", 2)
- end
- if type(comment) ~= "string" then
- error("bad argument #3 (string expected, got "..type(comment)..")", 2)
- end
- return code(key, value, comment, hex, i or 1)
- end;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement