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