function parseINIString(data) {
	var regex = {
		section: /^\s*\[\s*([^\]]*)\s*\]\s*$/,
		param: /^\s*([^=]+?)\s*=\s*(.*?)\s*$/,
		comment: /^\s*;.*$/
	};
	/** @type {Record<string, string | Record<string, string>>} */
	var value = {};
	var lines = data.split(/[\r\n]+/);
	/** @type {string | null} */
	var section = null;
	lines.forEach(function (line) {
		if (regex.comment.test(line)) {
			return;
		} else if (regex.param.test(line)) {
			var match = line.match(regex.param);
			if (section) {
				// @ts-ignore (could refactor to store section as an object, and use match result instead of test)
				value[section][match[1]] = match[2];
			} else {
				// @ts-ignore (could refactor to use match result instead of test)
				value[match[1]] = match[2];
			}
		} else if (regex.section.test(line)) {
			var match = line.match(regex.section);
			// @ts-ignore (could refactor to use match result instead of test)
			value[match[1]] = {};
			// @ts-ignore (could refactor to use match result instead of test)
			section = match[1];
		} else if (line.length == 0 && section) {
			// REALLY?? An empty line resets the section?? Dubious!
			// What does Windows do?
			section = null;
		};
	});
	return value;
}
@param {string} data 
@returns {Record<string, string | Record<string, string>>}
@private