The settings the game saves itself, the quiz settings and now fullscreen, move to settings.cfg (settings_debug.cfg in debug mode). config.ini in the same folder is edited by hand: the server to start with, the API password and debug mode. The game writes it with everything commented out when it is missing and never changes it after that. IniFile reads it: plain key = value lines, whole-line # or ; comments and optional quotes.
74 lines
2.6 KiB
GDScript
74 lines
2.6 KiB
GDScript
class_name IniFile
|
|
extends RefCounted
|
|
## A small INI file for settings people edit by hand: [code][section][/code] headers and
|
|
## [code]key = value[/code] lines.
|
|
##
|
|
## Lines starting with [code];[/code] or [code]#[/code] are comments. Comments take whole
|
|
## lines, so a value such as a password can hold any character. Quotes around a value are
|
|
## optional and removed. Section and key names are not case-sensitive.
|
|
|
|
const TRUE_WORDS: Array[String] = ["true", "yes", "on", "1"]
|
|
const FALSE_WORDS: Array[String] = ["false", "no", "off", "0"]
|
|
|
|
var _sections: Dictionary[String, Dictionary] = {}
|
|
|
|
|
|
## Reads INI [param text]. Lines that are neither a section, a setting nor a comment are
|
|
## skipped with a warning.
|
|
static func parse(text: String) -> IniFile:
|
|
var ini: IniFile = IniFile.new()
|
|
var section: String = ""
|
|
var line_number: int = 0
|
|
for raw_line: String in text.split("\n"):
|
|
line_number += 1
|
|
var line: String = raw_line.strip_edges()
|
|
if line.is_empty() or line.begins_with(";") or line.begins_with("#"):
|
|
continue
|
|
if line.begins_with("[") and line.ends_with("]"):
|
|
section = line.substr(1, line.length() - 2).strip_edges().to_lower()
|
|
continue
|
|
var equals: int = line.find("=")
|
|
if equals <= 0:
|
|
push_warning("Line %d is not a setting: %s" % [line_number, line])
|
|
continue
|
|
var key: String = line.substr(0, equals).strip_edges().to_lower()
|
|
var value: String = line.substr(equals + 1).strip_edges()
|
|
if value.length() >= 2 and value[0] in ["\"", "'"] and value[-1] == value[0]:
|
|
value = value.substr(1, value.length() - 2)
|
|
if !ini._sections.has(section):
|
|
ini._sections[section] = {}
|
|
ini._sections[section][key] = value
|
|
return ini
|
|
|
|
|
|
## Reads the INI file at [param path]. A missing file gives an empty [IniFile].
|
|
static func load_file(path: String) -> IniFile:
|
|
return parse(FileAccess.get_file_as_string(path))
|
|
|
|
|
|
func has_value(section: String, key: String) -> bool:
|
|
return _sections.get(section.to_lower(), {}).has(key.to_lower())
|
|
|
|
|
|
## The sections that have at least one setting.
|
|
func get_sections() -> Array[String]:
|
|
return _sections.keys()
|
|
|
|
|
|
func get_string(section: String, key: String, default: String = "") -> String:
|
|
return _sections.get(section.to_lower(), {}).get(key.to_lower(), default)
|
|
|
|
|
|
## Reads true/false, yes/no, on/off or 1/0. Anything else gives [param default] and a
|
|
## warning.
|
|
func get_bool(section: String, key: String, default: bool) -> bool:
|
|
if !has_value(section, key):
|
|
return default
|
|
var value: String = get_string(section, key).to_lower()
|
|
if value in TRUE_WORDS:
|
|
return true
|
|
if value in FALSE_WORDS:
|
|
return false
|
|
push_warning("%s in [%s] should be true or false, not %s" % [key, section, value])
|
|
return default
|