135 lines
3.5 KiB
Lua
135 lines
3.5 KiB
Lua
--[[
|
|
IniFile: basically a Lua rewrite of SM's IniFile class that serves as the
|
|
basis for the sm-ssc UserPrefs and ThemePrefs configuration systems.
|
|
Note that this is a namespace, not a class per se.
|
|
--]]
|
|
|
|
-- TODO: move this into a more general section
|
|
-- func takes a key and a value
|
|
function foreach_by_sorted_keys(tbl, keys, func)
|
|
table.sort(keys)
|
|
for _, key in ipairs(keys) do func(key, tbl[key]) end
|
|
end
|
|
|
|
function foreach_ordered( tbl, func )
|
|
local string_keys= {}
|
|
local number_keys= {}
|
|
-- First person to to use this on a table that uses something else as keys
|
|
-- gets to extend this function to cover more types. And a beating. -Kyz
|
|
for k,_ in pairs(tbl) do
|
|
if type(k) == "string" then
|
|
table.insert(string_keys, k)
|
|
elseif type(k) == "number" then
|
|
table.insert(number_keys, k)
|
|
end
|
|
end
|
|
-- iterate in sorted order
|
|
foreach_by_sorted_keys(tbl, number_keys, func)
|
|
foreach_by_sorted_keys(tbl, string_keys, func)
|
|
end
|
|
|
|
-- redeclared here for my sanity's sake
|
|
-- TODO: declare these as global variables
|
|
local RageFile =
|
|
{
|
|
READ = 1,
|
|
WRITE = 2,
|
|
STREAMED = 4,
|
|
SLOW_FLUSH = 8
|
|
}
|
|
|
|
-- IniFile namespace
|
|
IniFile =
|
|
{
|
|
StrToKeyVal = function( str )
|
|
local key, value = str:match( "(.+)=(.*)" )
|
|
|
|
-- key is always a string, but value may be num, bool, or nil.
|
|
-- do a few quick checks to see which one it is.
|
|
|
|
-- if it's a nil, convert it to an empty string and return
|
|
if value == nil then value = ""; return key, value; end
|
|
|
|
-- if it's a number, convert it in place and return
|
|
if tonumber(value) ~= nil then value = tonumber(value); return key, value; end
|
|
|
|
-- not a number, so let's try a boolean value
|
|
if value == "true" then value = true;
|
|
elseif value == "false" then value = false;
|
|
end
|
|
|
|
return key, value
|
|
end,
|
|
|
|
ReadFile = function( file_path )
|
|
Trace( "IniFile.ReadFile( " .. file_path .. " )" )
|
|
local file = RageFileUtil.CreateRageFile()
|
|
|
|
if not file:Open(file_path, RageFile.READ) then
|
|
Warn( string.format("ReadFile(%s): %s",file_path,file:GetError()) )
|
|
file:destroy()
|
|
return { } -- return a blank table
|
|
end
|
|
|
|
local tbl = { }
|
|
local current = tbl
|
|
|
|
while not file:AtEOF() do
|
|
local str = file:GetLine()
|
|
|
|
--ignore comments.
|
|
if not str:find("^%s*#") then
|
|
-- is this a section?
|
|
local sec = str:match( "^%[(.+)%]" )
|
|
|
|
-- if so, set focus there; otherwise, try to
|
|
-- read a key/value pair (ignore blank lines)
|
|
if sec then
|
|
-- if this section doesn't exist, create it
|
|
tbl[sec] = tbl[sec] and tbl[sec] or { }
|
|
current = tbl[sec]
|
|
--Warn( "Switching section to " .. sec )
|
|
else
|
|
local k, v = IniFile.StrToKeyVal( str )
|
|
if k and v ~= nil then current[k] = v end
|
|
end
|
|
end
|
|
end
|
|
|
|
file:Close()
|
|
file:destroy()
|
|
|
|
return tbl
|
|
end,
|
|
|
|
WriteFile = function( file_path, tbl )
|
|
Trace( "IniFile.WriteFile( " .. file_path .. " )" )
|
|
local file = RageFileUtil.CreateRageFile()
|
|
|
|
if not file:Open(file_path, RageFile.WRITE) then
|
|
Warn( string.format("WriteFile(%s): %s",file_path,file:GetError()) )
|
|
file:destroy()
|
|
return false
|
|
end
|
|
|
|
-- declare functions so we can write with foreach_ordered
|
|
local function put_pair( k, v )
|
|
file:PutLine( string.format("%s=%s", k, tostring(v)) )
|
|
end
|
|
|
|
local function put_section( section, pair )
|
|
file:PutLine( "[" .. section .. "]" )
|
|
foreach_ordered( pair, put_pair )
|
|
file:PutLine("") -- put a blank line between sections
|
|
end
|
|
|
|
-- each base key is a section and its value is a
|
|
-- table of key-value pairs under that section.
|
|
foreach_ordered( tbl, put_section )
|
|
|
|
file:Close()
|
|
file:destroy()
|
|
return true
|
|
end
|
|
};
|