demontools.lua
local DemonTools = {}
local cheatConsole = Geyser.MiniConsole:new({name = "DemonnicCheatConsole", width = 4000, wrapWidth = 10000, color = "black"})
cheatConsole:hide()
local function exists(path)
path = path:gsub([[\$]], "")
path = path:gsub([[/$]], "")
return io.exists(path)
end
local function isWindows()
return package.config:sub(1, 1) == [[\]]
end
local function isDir(path)
if not exists(path) then return false end
path = path:gsub([[\]], "/")
if path:ends("/") then
path = path:sub(1,-2)
end
local ok, err, code = lfs.attributes(path, "mode")
if ok then
if ok == "directory" then
return true
else
return false
end
end
return ok, err, code
end
local function mkdir_p(path)
path = path:gsub("\\", "/")
local pathTbl = path:split("/")
local cwd = "/"
if isWindows() then
cwd = ""
end
for index, dirName in ipairs(pathTbl) do
if index == 1 then
cwd = cwd .. dirName
else
cwd = cwd .. "/" .. dirName
cwd = cwd:gsub("//", "/")
end
if not table.contains({"/", "C:"}, cwd) and not exists(cwd) then
local ok, err = lfs.mkdir(cwd)
if not ok then
return ok, err
end
end
end
return true
end
local htmlHeader = [=[ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" >
<link href='http://fonts.googleapis.com/css?family=Droid+Sans+Mono' rel='stylesheet' type='text/css'>
<style type="text/css">
body {
background-color: black;
font-family: 'Droid Sans Mono';
white-space: pre;
font-size: 12px;
}
</style>
</head>
<body><span>
]=]
local htmlHeaderPattern = [=[ <!DOCTYPE HTML PUBLIC "%-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http%-equiv="Content%-Type" content="text/html;charset=utf%-8" >
<link href='http://fonts.googleapis.com/css%?family=Droid%+Sans%+Mono' rel='stylesheet' type='text/css'>
<style type="text/css">
body {
background%-color: black;
font%-family: 'Droid Sans Mono';
white%-space: pre;
font%-size: 12px;
}
</style>
</head>
<body><span>
]=]
-- Internal function, used to turn a string variable name into a value
local function getValueAt(accessString)
local ok, err = pcall(loadstring("return " .. tostring(accessString)))
if ok then return err end
return nil, err
end
-- internal sorting function, sorts first by hue, then luminosity, then value
local function sortColorsByHue(lhs, rhs)
local lh, ll, lv = unpack(lhs.sort)
local rh, rl, rv = unpack(rhs.sort)
if lh < rh then
return true
elseif lh > rh then
return false
elseif ll < rl then
return true
elseif ll > rl then
return false
else
return lv < rv
end
end
-- internal sorting function, removes _ from snake_case and compares to camelCase
local function sortColorsByName(a, b)
local aname = string.gsub(string.lower(a.name), "_", "")
local bname = string.gsub(string.lower(b.name), "_", "")
return aname < bname
end
-- internal function used to turn sorted colors table into columns
local function chunkify(tbl, num_chunks)
local pop = function(t)
return table.remove(t, 1)
end
tbl = table.deepcopy(tbl)
local tblsize = #tbl
local base_chunk_size = tblsize / num_chunks
local chunky_chunks = tblsize % num_chunks
local chunks = {}
for i = 1, num_chunks do
local chunk_size = base_chunk_size
if i <= chunky_chunks then
chunk_size = chunk_size + 1
end
local chunk = {}
for j = 1, chunk_size do
chunk[j] = pop(tbl)
end
chunks[i] = chunk
end
return chunks
end
-- internal function, converts rgb to hsv
-- found at https://github.com/EmmanuelOga/columns/blob/master/utils/color.lua#L89
local function rgbToHsv(r, g, b)
r, g, b = r / 255, g / 255, b / 255
local max, min = math.max(r, g, b), math.min(r, g, b)
local h, s, v
v = max
local d = max - min
if max == 0 then
s = 0
else
s = d / max
end
if max == min then
h = 0
-- achromatic
else
if max == r then
h = (g - b) / d
if g < b then
h = h + 6
end
elseif max == g then
h = (b - r) / d + 2
elseif max == b then
h = (r - g) / d + 4
end
h = h / 6
end
return h, s, v
end
-- internal stepping function, removes some of the noise for a more pleasing sort
-- cribbed from the python on https://www.alanzucconi.com/2015/09/30/colour-sorting/
local function step(r, g, b)
local lum = math.sqrt(.241 * r + .691 * g + .068 * b)
local reps = 8
local h, s, v = rgbToHsv(r, g, b)
local h2 = math.floor(h * reps)
local lum2 = math.floor(lum * reps)
local v2 = math.floor(v * reps)
if h2 % 2 == 1 then
v2 = reps - v2
lum2 = reps - lum2
end
return h2, lum2, v2
end
local function calc_luminosity(r, g, b)
r = r < 11 and r / (255 * 12.92) or ((0.055 + r / 255) / 1.055) ^ 2.4
g = g < 11 and g / (255 * 12.92) or ((0.055 + g / 255) / 1.055) ^ 2.4
b = b < 11 and b / (255 * 12.92) or ((0.055 + b / 255) / 1.055) ^ 2.4
return (0.2126 * r) + (0.7152 * g) + (0.0722 * b)
end
local function include(color, options)
if options.removeDupes and (string.find(color, "_") and not color:starts("ansi")) or string.find(color:lower(), 'gray') then
return false
end
if options.removeAnsi255 and string.find(color, "ansi_%d%d%d") then
return false
end
end
local function echoColor(color, options)
local rgb = color.rgb
local fgc = "white"
if calc_luminosity(unpack(rgb)) > 0.5 then
fgc = "black"
end
local colorString
if options.justText then
colorString = string.format('<%s:%s> %-23s<reset> ', color.name, 'black', color.name)
else
colorString = string.format('<%s:%s> %-23s<reset> ', fgc, color.name, color.name)
end
if options.window == "main" then
if options.echoOnly then
cecho(colorString)
else
cechoLink(colorString, [[appendCmdLine("]] .. color.name .. [[")]], table.concat(rgb, ", "), true)
end
else
if options.echoOnly then
cecho(options.window, colorString)
else
cechoLink(options.window, colorString, [[appendCmdLine("]] .. color.name .. [[")]], table.concat(rgb, ", "), true)
end
end
end
local cnames = {}
local function _color_name(rgb)
if cnames[rgb] then
return cnames[rgb]
end
local least_distance = math.huge
local cname = ""
for name, color in pairs(color_table) do
local color_distance = math.sqrt((color[1] - rgb[1]) ^ 2 + (color[2] - rgb[2]) ^ 2 + (color[3] - rgb[3]) ^ 2)
if color_distance < least_distance then
least_distance = color_distance
cname = name
end
end
cnames[rgb] = cname
return cname
end
local function rgbToAnsi(rgb)
local result = ""
local cols = rgb:split(":")
local fore = cols[1]
local back = cols[2]
if fore ~= "" then
local components = fore:split(",")
result = string.format("%s\27[38:2::%s:%s:%sm", result, components[1] or "0", components[2] or "0", components[3] or "0")
end
if back then
local components = back:split(",")
result = string.format("%s\27[48:2::%s:%s:%sm", result, components[1] or "0", components[2] or "0", components[3] or "0")
end
return result
end
local function hexToAnsi(hexcode)
local result = ""
local cols = hexcode:split(",")
local fore = cols[1]
local back = cols[2]
if fore ~= "" then
local components = {tonumber(fore:sub(1, 2), 16), tonumber(fore:sub(3, 4), 16), tonumber(fore:sub(5, 6), 16)}
result = string.format("%s\27[38:2::%s:%s:%sm", result, components[1] or "0", components[2] or "0", components[3] or "0")
end
if back then
local components = {tonumber(back:sub(1, 2), 16), tonumber(back:sub(3, 4), 16), tonumber(back:sub(5, 6), 16)}
result = string.format("%s\27[48:2::%s:%s:%sm", result, components[1] or "0", components[2] or "0", components[3] or "0")
end
return result
end
local function hexToRgb(hexcode)
local result = "<"
local cols = hexcode:split(",")
local fore = cols[1]
local back = cols[2]
if fore ~= "" then
local r, g, b = Geyser.Color.parse("#" .. fore)
result = string.format("%s%s,%s,%s", result, r, g, b)
end
if back then
local r, g, b = Geyser.Color.parse("#" .. back)
result = string.format("%s:%s,%s,%s", result, r, g, b)
end
return string.format("%s>", result)
end
local function rgbToHex(rgb)
local result = "#"
local cols = rgb:split(":")
local fore = cols[1]
local back = cols[2]
if fore ~= "" then
local r, g, b = unpack(string.split(fore, ","))
result = string.format("%s%02x%02x%02x", result, r, g, b)
end
if back then
local r, g, b = unpack(string.split(back, ","))
result = string.format("%s,%02x%02x%02x", result, r, g, b)
end
return result
end
local function rgbToCname(rgb)
local result = "<"
local cols = rgb:split(":")
local fore = cols[1]
local back = cols[2]
if fore ~= "" then
result = string.format("%s%s", result, _color_name(fore:split(",")))
end
if back then
result = string.format("%s:%s", result, _color_name(back:split(",")))
end
return string.format("%s>", result)
end
local function cnameToRgb(cname)
local result = "<"
local cols = cname:split(":")
local fore = cols[1]
local back = cols[2]
if fore ~= "" then
local rgb = color_table[fore] or {0, 0, 0}
result = string.format("%s%s", result, table.concat(rgb, ","))
end
if back then
local rgb = color_table[back] or {0, 0, 0}
result = string.format("%s:%s", result, table.concat(rgb, ","))
end
return string.format("%s>", result)
end
local function toFromDecho(from, to, text)
local patterns = {d = _Echos.Patterns.Decimal[1], c = _Echos.Patterns.Color[1], h = _Echos.Patterns.Hex[1]}
local funcs = {d = {c = rgbToCname, h = rgbToHex, a = rgbToAnsi}, c = {d = cnameToRgb}, h = {d = hexToRgb}}
local resetCodes = {d = "<r>", h = "#r", c = "<reset>", a = "\27[39;49m"}
local colorPattern = patterns[from]
local func = funcs[from][to]
local reset = resetCodes[to]
local result = ""
for str, color, res in rex.split(text, colorPattern) do
result = result .. str
if color then
if color:sub(1, 1) == "|" then
color = color:gsub("|c", "#")
end
if from == "h" then
result = result .. func(color:sub(2, -1))
else
result = result .. func(color:match("<(.+)>"))
end
end
if res then
result = result .. reset
end
end
return result
end
local function decho2cecho(text)
return toFromDecho("d", "c", text)
end
local function cecho2decho(text)
return toFromDecho("c", "d", text)
end
local function decho2hecho(text)
return toFromDecho("d", "h", text)
end
local function hecho2decho(text)
return toFromDecho("h", "d", text)
end
local function cecho2ansi(text)
local dtext = cecho2decho(text)
return decho2ansi(dtext)
end
local function cecho2hecho(text)
local dtext = cecho2decho(text)
return decho2hecho(dtext)
end
local function hecho2cecho(text)
local dtext = hecho2decho(text)
return decho2cecho(dtext)
end
local function ansi2decho(tstring)
local cpattern = [=[\e\[([0-9;:]+)m]=]
local result = ""
local resets = {"39;49", "00", "0"}
local colours = {
[0] = color_table.ansiBlack,
[1] = color_table.ansiRed,
[2] = color_table.ansiGreen,
[3] = color_table.ansiYellow,
[4] = color_table.ansiBlue,
[5] = color_table.ansiMagenta,
[6] = color_table.ansiCyan,
[7] = color_table.ansiWhite,
}
local lightColours = {
[0] = color_table.ansiLightBlack,
[1] = color_table.ansiLightRed,
[2] = color_table.ansiLightGreen,
[3] = color_table.ansiLightYellow,
[4] = color_table.ansiLightBlue,
[5] = color_table.ansiLightMagenta,
[6] = color_table.ansiLightCyan,
[7] = color_table.ansiLightWhite,
}
local function colorCodeToRGB(color, parts)
local rgb
if color ~= 8 then
rgb = colours[color]
else
if parts[2] == "5" then
local color_number = tonumber(parts[3])
if color_number < 8 then
rgb = colours[color_number]
elseif color_number > 7 and color_number < 16 then
rgb = lightColours[color_number - 8]
else
rgb = color_table["ansi_" .. color_number]
end
elseif parts[2] == "2" then
local r = parts[4] or 0
local g = parts[5] or 0
local b = parts[6] or 0
if r == "" then
r = 0
end
if g == "" then
g = 0
end
if b == "" then
b = 0
end
rgb = {r, g, b}
end
end
return rgb
end
for str, color in rex.split(tstring, cpattern) do
result = result .. str
if color then
if table.contains(resets, color) then
result = result .. "<r>"
else
local parts
if color:find(";") then
parts = color:split(";")
else
parts = color:split(":")
end
local code = parts[1]
if code:starts("3") then
color = tonumber(code:sub(2, 2))
local rgb = colorCodeToRGB(color, parts)
result = string.format("%s<%s,%s,%s>", result, rgb[1], rgb[2], rgb[3])
elseif code:starts("4") then
color = tonumber(code:sub(2, 2))
local rgb = colorCodeToRGB(color, parts)
result = string.format("%s<:%s,%s,%s>", result, rgb[1], rgb[2], rgb[3])
elseif tonumber(code) >= 90 and tonumber(code) <= 97 then
local rgb = colours[tonumber(code) - 90]
result = string.format("%s<%s,%s,%s>", result, rgb[1], rgb[2], rgb[3])
elseif tonumber(code) >= 100 and tonumber(code) <= 107 then
local rgb = colours[tonumber(code) - 100]
result = string.format("%s<:%s,%s,%s>", result, rgb[1], rgb[2], rgb[3])
end
end
end
end
return result
end
local function decho2ansi(text)
local colorPattern = _Echos.Patterns.Decimal[1]
local result = ""
for str, color, res in rex.split(text, colorPattern) do
result = result .. str
if color then
result = result .. rgbToAnsi(color:match("<(.+)>"))
end
if res then
result = result .. "\27[39;49m"
end
end
return result
end
local function hecho2ansi(text)
local colorPattern = _Echos.Patterns.Hex[1]
local result = ""
for str, color, res in rex.split(text, colorPattern) do
result = result .. str
if color then
if color:sub(1, 1) == "|" then
color = color:gsub("|c", "#")
end
result = result .. hexToAnsi(color:sub(2, -1))
end
if res then
result = result .. "\27[39;49m"
end
end
return result
end
local function ansi2hecho(text)
local dtext = ansi2decho(text)
return decho2hecho(dtext)
end
local function displayColors(options)
options = options or {}
local optionsType = type(options)
assert(optionsType == "table", "displayColors(options) argument error: options as table expects, got " .. optionsType)
options.cols = options.cols or 4
options.search = options.search or ""
options.sort = options.sort or false
if options.removeDupes == nil then
options.removeDupes = true
end
if options.removeAnsi255 == nil then
options.removeAnsi255 = true
end
if options.columnSort == nil then
options.columnSort = true
end
if type(options.window) == "table" then
options.window = options.window.name
end
options.window = options.window or "main"
local color_table = options.color_table or color_table
local cols, search, sort = options.cols, options.search, options.sort
local colors = {}
for k, v in pairs(color_table) do
local color = {}
color.rgb = v
color.name = k
color.sort = {step(unpack(v))}
if include(k, options) and k:lower():find(search) then
table.insert(colors, color)
end
end
if sort then
table.sort(colors, sortColorsByName)
else
table.sort(colors, sortColorsByHue)
end
if options.columnSort then
local columns_table = chunkify(colors, cols)
local lines = #columns_table[1]
for i = 1, lines do
for j = 1, cols do
local color = columns_table[j][i]
if color then
echoColor(color, options)
end
end
echo(options.window, "\n")
end
else
local i = 1
for _, k in ipairs(colors) do
echoColor(k, options)
if i == cols then
echo(options.window, "\n")
i = 1
else
i = i + 1
end
end
if i ~= 1 then
echo(options.window, "\n")
end
end
end
local function cecho2string(text)
local pattern = _Echos.Patterns.Color[2]
local result = rex.gsub(text, pattern, "")
return result
end
local function decho2string(text)
local pattern = _Echos.Patterns.Decimal[2]
local result = rex.gsub(text, pattern, "")
return result
end
local function hecho2string(text)
local pattern = _Echos.Patterns.Hex[2]
local result = rex.gsub(text, pattern, "")
return result
end
local function append2decho()
cheatConsole:clear()
cheatConsole:appendBuffer()
local str = copy2decho(cheatConsole.name)
cheatConsole:clear()
return str
end
local function html2decho(text)
text = text:gsub(htmlHeaderPattern, "")
text = text:gsub("<span style='color: rgb%((%d+,%d+,%d+)%);background: rgb%((%d+,%d+,%d+)%);'>", "<%1:%2>")
text = text:gsub("<br>", "\n")
text = text:gsub("</span>", "")
return text
end
local function html2cecho(text)
local dtext = html2decho(text)
return decho2cecho(dtext)
end
local function html2hecho(text)
local dtext = html2decho(text)
return decho2hecho(dtext)
end
local function html2ansi(text)
local dtext = html2decho(text)
return decho2ansi(dtext)
end
local function html2string(text)
local dtext = html2decho(text)
return decho2string(text)
end
local function consoleToString(options)
options = options or {}
options.win = options.win or "main"
options.format = options.format or "d"
options.start_line = options.start_line or 0
if options.includeHtmlWrapper == nil then
options.includeHtmlWrapper = true
end
local console_line_count = options.win == "main" and getLineCount() or getLineCount(options.win)
if not options.end_line then
options.end_line = console_line_count
end
if options.end_line > console_line_count then
options.end_line = console_line_count
end
local start, finish, format = options.start_line, options.end_line, options.format
local current_x, current_y
if options.win == "main" then
current_x = getColumnNumber()
current_y = getLineNumber()
else
current_x = getColumnNumber(options.win)
current_y = getLineNumber(options.win)
end
local function move(x, y)
if options.win == "main" then
return moveCursor(x, y)
else
return moveCursor(options.win, x, y)
end
end
local function gcl()
local win, raw
if options.win ~= "main" then
win = options.win
raw = getCurrentLine(win)
else
win = nil
raw = getCurrentLine()
end
if raw == "" then
return ""
end
if format == "h" then
return copy2html(win)
elseif format == "d" then
return copy2decho(win)
elseif format == "a" then
return decho2ansi(copy2decho(win))
elseif format == "c" then
return decho2cecho(copy2decho(win))
elseif format == "x" then
return decho2hecho(copy2decho(win))
elseif format == "r" then
return raw
end
end
local lines = {}
if format == "h" and options.includeHtmlWrapper then
lines[#lines + 1] = htmlHeader
end
for line_number = start, finish do
move(0, line_number)
lines[#lines + 1] = gcl()
end
if format == "h" and options.includeHtmlWrapper then
lines[#lines + 1] = "</span></body></html>"
end
moveCursor(current_x, current_y)
return table.concat(lines, "\n")
end
local function decho2html(text)
cheatConsole:clear()
text = text:gsub("\n", "<br>")
cheatConsole:decho(text)
local html = copy2html(cheatConsole.name)
cheatConsole:clear()
return html
end
local function cecho2html(text)
local dtext = cecho2decho(text)
return decho2html(dtext)
end
local function hecho2html(text)
local dtext = hecho2decho(text)
return decho2html(dtext)
end
local function ansi2html(text)
local dtext = ansi2decho(text)
return decho2html(dtext)
end
local function scientific_round(number, sigDigits)
local decimalPlace = string.find(number, "%.")
if not decimalPlace or (sigDigits < decimalPlace) then
local numberTable = {}
local count = 1
for digit in string.gmatch(number, "%d") do
table.insert(numberTable, digit)
end
local endNumber = ""
for i, digit in ipairs(numberTable) do
if i < sigDigits then
endNumber = endNumber .. digit
end
if i == sigDigits then
if tonumber(numberTable[i + 1]) >= 5 then
endNumber = endNumber .. digit + 1
else
endNumber = endNumber .. digit
end
end
if i > sigDigits and (not decimalPlace or (i < decimalPlace)) then
endNumber = endNumber .. "0"
end
end
return tonumber(endNumber)
else
local decimalDigits = sigDigits - decimalPlace + 1
return tonumber(string.format("%" .. decimalPlace - 1 .. "." .. decimalDigits .. "f", number))
end
end
local function roundInt(number)
return math.floor(number + 0.5)
end
function string.tobyte(self)
return (self:gsub('.', function(c)
return string.byte(c)
end))
end
function string.tocolor(self)
local strTable = {}
local part1 = {}
local part2 = {}
_ = self:gsub(".", function(c)
table.insert(strTable, c)
end)
for index, value in ipairs(strTable) do
if (index % 2 == 0) then
table.insert(part1, value)
else
table.insert(part2, value)
end
end
local newStr = string.reverse(table.concat(part1)) .. table.concat(part2)
math.randomseed(string.cut(newStr:tobyte(), 18))
local r = math.random(0, 255)
local g = math.random(0, 255)
local b = math.random(0, 255)
math.randomseed(os.time())
return {r, g, b}
end
local function colorMunge(strForColor, strToEcho, format)
format = format or 'd'
local rgb = strForColor:tocolor()
local color
if format == "d" then
color = string.format("<%s>", table.concat(rgb, ","))
elseif format == "c" then
color = string.format("<%s>", _color_name(rgb))
elseif format == "h" then
color = string.format("#%02x%02x%02x", rgb[1], rgb[2], rgb[3])
end
return color .. strToEcho
end
local function colorMungeEcho(strForColor, strToEcho, format, win)
format = format or "d"
win = win or "main"
local str = colorMunge(strForColor, strToEcho, format)
local func
if format == "d" then
func = decho
end
if format == "c" then
func = cecho
end
if format == "h" then
func = hecho
end
if win == "main" then
func(str)
else
func(win, str)
end
end
local function milliToHuman(milliseconds)
local totalseconds = math.floor(milliseconds / 1000)
milliseconds = milliseconds % 1000
local seconds = totalseconds % 60
local minutes = math.floor(totalseconds / 60)
local hours = math.floor(minutes / 60)
minutes = minutes % 60
return string.format("%02d:%02d:%02d:%03d", hours, minutes, seconds, milliseconds)
end
function DemonTools.chunkify(tbl, num_chunks)
return chunkify(tbl, num_chunks)
end
function DemonTools.ansi2cecho(text)
local dtext = ansi2decho(text)
return decho2cecho(dtext)
end
function DemonTools.ansi2decho(text)
return ansi2decho(text)
end
function DemonTools.ansi2hecho(text)
return ansi2hecho(text)
end
function DemonTools.cecho2decho(text)
return cecho2decho(text)
end
function DemonTools.cecho2ansi(text)
return cecho2ansi(text)
end
function DemonTools.cecho2hecho(text)
return cecho2hecho(text)
end
function DemonTools.decho2cecho(text)
return decho2cecho(text)
end
function DemonTools.decho2ansi(text)
return decho2ansi(text)
end
function DemonTools.decho2hecho(text)
return decho2hecho(text)
end
function DemonTools.decho2html(text)
return decho2html(text)
end
function DemonTools.cecho2html(text)
return cecho2html(text)
end
function DemonTools.hecho2html(text)
return hecho2html(text)
end
function DemonTools.ansi2html(text)
return ansi2html(text)
end
function DemonTools.html2cecho(text)
return html2cecho(text)
end
function DemonTools.html2decho(text)
return html2decho(text)
end
function DemonTools.html2ansi(text)
return html2ansi(text)
end
function DemonTools.html2hecho(text)
return html2hecho(text)
end
function DemonTools.cecho2string(text)
return cecho2string(text)
end
function DemonTools.decho2string(text)
return decho2string(text)
end
function DemonTools.hecho2string(text)
return hecho2string(text)
end
function DemonTools.html2string(text)
return html2string(text)
end
function DemonTools.hecho2ansi(text)
return hecho2ansi(text)
end
function DemonTools.hecho2cecho(text)
return hecho2cecho(text)
end
function DemonTools.hecho2decho(text)
return hecho2decho(text)
end
function DemonTools.append2decho()
return append2decho()
end
function DemonTools.consoleToString(options)
return consoleToString(options)
end
function DemonTools.displayColors(options)
return displayColors(options)
end
function DemonTools.roundInt(number)
local num = tonumber(number)
local numType = type(num)
assert(numType == "number", string.format("DemonTools.roundInt(number): number as number expected, got %s", type(number)))
return roundInt(num)
end
function DemonTools.scientific_round(number, sig_digits)
return scientific_round(number, sig_digits)
end
function DemonTools.string2color(str)
return string.tocolor(str)
end
function DemonTools.colorMunge(strForColor, strToColor, format)
return colorMunge(strForColor, strToColor, format)
end
function DemonTools.colorMungeEcho(strForColor, strToEcho, format, win)
colorMungeEcho(strForColor, strToEcho, format, win)
end
function DemonTools.milliToHuman(milliseconds, tbl)
local human = milliToHuman(milliseconds)
local output
if tbl then
local timetbl = human:split(":")
output = {
hours = tonumber(timetbl[1]),
minutes = tonumber(timetbl[2]),
seconds = tonumber(timetbl[3]),
milliseconds = tonumber(timetbl[4]),
original = milliseconds,
}
else
output = human
end
return output
end
function DemonTools.getValueAt(variableString)
return getValueAt(variableString)
end
function DemonTools.exists(path)
return exists(path)
end
function DemonTools.isDir(path)
return isDir(path)
end
function DemonTools.isWindows()
return isWindows()
end
function DemonTools.mkdir_p(path)
return mkdir_p(path)
end
DemonTools.htmlHeader = htmlHeader
DemonTools.htmlHeaderPattern = htmlHeaderPattern
local echoOutputs = {
Color = {
["\27reset"] = "<reset>",
["\27bold"] = "<b>",
["\27boldoff"] = "</b>",
["\27italics"] = "<i>",
["\27italicsoff"] = "</i>",
["\27underline"] = "<u>",
["\27underlineoff"] = "</u>",
["\27strikethrough"] = "<s>",
["\27strikethroughoff"] = "</s>",
["\27overline"] = "<o>",
["\27overlineoff"] = "</o>",
},
Decimal = {
["\27reset"] = "<r>",
["\27bold"] = "<b>",
["\27boldoff"] = "</b>",
["\27italics"] = "<i>",
["\27italicsoff"] = "</i>",
["\27underline"] = "<u>",
["\27underlineoff"] = "</u>",
["\27strikethrough"] = "<s>",
["\27strikethroughoff"] = "</s>",
["\27overline"] = "<o>",
["\27overlineoff"] = "</o>",
},
Hex = {
["\27reset"] = "#r",
["\27bold"] = "#b",
["\27boldoff"] = "#/b",
["\27italics"] = "#i",
["\27italicsoff"] = "#/i",
["\27underline"] = "#u",
["\27underlineoff"] = "#/u",
["\27strikethrough"] = "#s",
["\27strikethroughoff"] = "#/s",
["\27overline"] = "#o",
["\27overlineoff"] = "#/o",
}
}
local echoPatterns = _Echos.Patterns
local echoProcess = _Echos.Process
function DemonTools.toHTML(t, reset)
reset = reset or {
background = { 0, 0, 0 },
bold = false,
foreground = { 255, 255, 255 },
italic = false,
overline = false,
reverse = false,
strikeout = false,
underline = false
}
local format = table.deepcopy(reset)
local result = getHTMLformat(format)
for _,v in ipairs(t) do
local formatChanged = false
if type(v) == "table" then
if v.fg then
format.foreground = {v.fg[1], v.fg[2], v.fg[3]}
formatChanged = true
end
if v.bg then
format.background = {v.bg[1], v.bg[2], v.bg[3]}
formatChanged = true
end
elseif v == "\27bold" then
format.bold = true
formatChanged = true
elseif v == "\27boldoff" then
format.bold = false
formatChanged = true
elseif v == "\27italics" then
format.italic = true
formatChanged = true
elseif v == "\27italicsoff" then
format.italic = false
formatChanged = true
elseif v == "\27underline" then
format.underline = true
formatChanged = true
elseif v == "\27underlineoff" then
format.underline = false
formatChanged = true
elseif v == "\27strikethrough" then
format.strikeout = true
formatChanged = true
elseif v == "\27strikethroughoff" then
format.strikeout = false
formatChanged = true
elseif v == "\27overline" then
format.overline = true
formatChanged = true
elseif v == "\27overlineoff" then
format.overline = false
formatChanged = true
elseif v == "\27reset" then
format = table.deepcopy(reset)
formatChanged = true
end
v = formatChanged and getHTMLformat(format) or v
result = result .. v
end
return result
end
local function toEcho(colorType, colors)
colorType = colorType:lower()
local result
if colorType == "hex" then
local fg,bg = "", ""
if colors.fg then
fg = string.format("%02x%02x%02x", unpack(colors.fg))
end
if colors.bg then
bg = string.format(",%02x%02x%02x", unpack(colors.bg))
end
result = string.format("#%s%s", fg, bg)
elseif colorType == "color" then
local fg,bg = "",""
if colors.fg then
fg = closestColor(colors.fg)
end
if colors.bg then
bg = ":" .. closestColor(colors.bg[1], colors.bg[2], colors.bg[3])
end
result = string.format("<%s%s>", fg, bg)
elseif colorType == "decimal" then
local fg,bg = "", ""
if colors.fg then
fg = string.format("%d,%d,%d", unpack(colors.fg))
end
if colors.bg then
bg = string.format(":%d,%d,%d", unpack(colors.bg))
end
result = string.format("<%s%s>", fg, bg)
end
return result
end
function DemonTools.echoConverter(str, from, to, resetFormat)
local strType, fromType, toType, resetType = type(str), type(from), type(to), type(resetFormat)
local errTemplate = "bad argument #{argNum} type ({argName} as string expected, got {argType})"
local argNum, argName, argType
local err = false
if strType ~= "string" then
argNum = 1
argName = "str"
argType = strType
err = true
elseif fromType ~= "string" then
argNum = 2
argName = "from"
argType = fromType
err = true
elseif toType ~= "string" then
argNum = 3
argName = "to"
argType = toType
err = true
elseif resetFormat and resetType ~= "table" then
argType = resetType
errTemplate = "bad argument #4 type (optional resetFormat as table of formatting options expected, got {argType})"
err = true
end
if err then
printError(f(errTemplate), true, true)
end
from = from:title()
local t = echoProcess(str, from)
if not echoPatterns[from] then
local msg = "argument #4 (from) must be a valid echo type. Valid types are: " .. table.concat(table.keys(echoPatterns), ",")
end
local processed = echoProcess(str, from)
if to:lower() == "html" then
return DemonTools.toHTML(processed, resetFormat)
end
local outputs = echoOutputs[to]
if not outputs then
local msg = "argument #3 (to) must be a valid echo type. Valid types are: " .. table.concat(table.keys(echoOutputs), ",")
printError(msg, true, true)
end
local result = ""
for _, token in ipairs(processed) do
local formatter = outputs[token]
if formatter and token:find("\27") then
result = result .. formatter
elseif type(token) == "table" then
result = result .. toEcho(to, token)
else
result = result .. token
end
end
return result
end
return DemonTools