void ConfigManager::loadFromStream(SeekableReadStream &stream) { String domainName; String comment; Domain domain; int lineno = 0; _appDomain.clear(); _gameDomains.clear(); _miscDomains.clear(); _transientDomain.clear(); _domainSaveOrder.clear(); #ifdef ENABLE_KEYMAPPER _keymapperDomain.clear(); #endif // TODO: Detect if a domain occurs multiple times (or likewise, if // a key occurs multiple times inside one domain). while (!stream.eos() && !stream.err()) { lineno++; // Read a line String line = stream.readLine(); if (line.size() == 0) { // Do nothing } else if (line[0] == '#') { // Accumulate comments here. Once we encounter either the start // of a new domain, or a key-value-pair, we associate the value // of the 'comment' variable with that entity. comment += line; #ifdef _WIN32 comment += "\r\n"; #else comment += "\n"; #endif } else if (line[0] == '[') { // It's a new domain which begins here. // Determine where the previously accumulated domain goes, if we accumulated anything. addDomain(domainName, domain); domain.clear(); const char *p = line.c_str() + 1; // Get the domain name, and check whether it's valid (that // is, verify that it only consists of alphanumerics, // dashes and underscores). while (*p && (isalnum(*p) || *p == '-' || *p == '_')) p++; if (*p == '\0') error("Config file buggy: missing ] in line %d", lineno); else if (*p != ']') error("Config file buggy: Invalid character '%c' occurred in section name in line %d", *p, lineno); domainName = String(line.c_str() + 1, p); domain.setDomainComment(comment); comment.clear(); } else { // This line should be a line with a 'key=value' pair, or an empty one. // Skip leading whitespaces const char *t = line.c_str(); while (isspace(*t)) t++; // Skip empty lines / lines with only whitespace if (*t == 0) continue; // If no domain has been set, this config file is invalid! if (domainName.empty()) { error("Config file buggy: Key/value pair found outside a domain in line %d", lineno); } // Split string at '=' into 'key' and 'value'. First, find the "=" delimeter. const char *p = strchr(t, '='); if (!p) error("Config file buggy: Junk found in line line %d: '%s'", lineno, t); // Extract the key/value pair String key(t, p); String value(p + 1); // Trim of spaces key.trim(); value.trim(); // Finally, store the key/value pair in the active domain domain[key] = value; // Store comment domain.setKVComment(key, comment); comment.clear(); } } addDomain(domainName, domain); // Add the last domain found }
bool ConfigFile::loadFromStream(SeekableReadStream &stream) { Section section; KeyValue kv; String comment; int lineno = 0; // TODO: Detect if a section occurs multiple times (or likewise, if // a key occurs multiple times inside one section). while (!stream.eos() && !stream.err()) { lineno++; // Read a line String line = stream.readLine(); if (line.size() == 0) { // Do nothing } else if (line[0] == '#' || line[0] == ';' || line.hasPrefix("//")) { // Accumulate comments here. Once we encounter either the start // of a new section, or a key-value-pair, we associate the value // of the 'comment' variable with that entity. The semicolon and // C++-style comments are used for Living Books games in Mohawk. comment += line; comment += "\n"; } else if (line[0] == '(') { // HACK: The following is a hack added by Kirben to support the // "map.ini" used in the HE SCUMM game "SPY Fox in Hold the Mustard". // // It would be nice if this hack could be restricted to that game, // but the current design of this class doesn't allow to do that // in a nice fashion (a "isMustard" parameter is *not* a nice // solution). comment += line; comment += "\n"; } else if (line[0] == '[') { // It's a new section which begins here. const char *p = line.c_str() + 1; // Get the section name, and check whether it's valid (that // is, verify that it only consists of alphanumerics, // periods, dashes and underscores). Mohawk Living Books games // can have periods in their section names. while (*p && (isalnum(static_cast<unsigned char>(*p)) || *p == '-' || *p == '_' || *p == '.')) p++; if (*p == '\0') error("ConfigFile::loadFromStream: missing ] in line %d", lineno); else if (*p != ']') error("ConfigFile::loadFromStream: Invalid character '%c' occurred in section name in line %d", *p, lineno); // Previous section is finished now, store it. if (!section.name.empty()) _sections.push_back(section); section.name = String(line.c_str() + 1, p); section.keys.clear(); section.comment = comment; comment.clear(); assert(isValidName(section.name)); } else { // This line should be a line with a 'key=value' pair, or an empty one. // Skip leading whitespaces const char *t = line.c_str(); while (isspace(static_cast<unsigned char>(*t))) t++; // Skip empty lines / lines with only whitespace if (*t == 0) continue; // If no section has been set, this config file is invalid! if (section.name.empty()) { error("ConfigFile::loadFromStream: Key/value pair found outside a section in line %d", lineno); } // Split string at '=' into 'key' and 'value'. First, find the "=" delimeter. const char *p = strchr(t, '='); if (!p) error("Config file buggy: Junk found in line line %d: '%s'", lineno, t); // Extract the key/value pair kv.key = String(t, p); kv.value = String(p + 1); // Trim of spaces kv.key.trim(); kv.value.trim(); // Store comment kv.comment = comment; comment.clear(); assert(isValidName(kv.key)); section.keys.push_back(kv); } } // Save last section if (!section.name.empty()) _sections.push_back(section); return (!stream.err() || stream.eos()); }
bool ConfigFile::loadFromStream(SeekableReadStream &stream) { char buf[MAXLINELEN]; Section section; KeyValue kv; String comment; int lineno = 0; // TODO: Detect if a section occurs multiple times (or likewise, if // a key occurs multiple times inside one section). while (!stream.eos()) { lineno++; if (!stream.readLine(buf, MAXLINELEN)) break; if (buf[0] == '#') { // Accumulate comments here. Once we encounter either the start // of a new section, or a key-value-pair, we associate the value // of the 'comment' variable with that entity. comment += buf; comment += "\n"; } else if (buf[0] == '(') { // HACK: The following is a hack added by Kirben to support the // "map.ini" used in the HE SCUMM game "SPY Fox in Hold the Mustard". // // It would be nice if this hack could be restricted to that game, // but the current design of this class doesn't allow to do that // in a nice fashion (a "isMustard" parameter is *not* a nice // solution). comment += buf; comment += "\n"; } else if (buf[0] == '[') { // It's a new section which begins here. char *p = buf + 1; // Get the section name, and check whether it's valid (that // is, verify that it only consists of alphanumerics, // dashes and underscores). while (*p && (isalnum(*p) || *p == '-' || *p == '_')) p++; if (*p == '\0') error("ConfigFile::loadFromStream: missing ] in line %d", lineno); else if (*p != ']') error("ConfigFile::loadFromStream: Invalid character '%c' occured in section name in line %d", *p, lineno); *p = 0; // Previous section is finished now, store it. if (!section.name.empty()) _sections.push_back(section); section.name = buf + 1; section.keys.clear(); section.comment = comment; comment.clear(); assert(isValidName(section.name)); } else { // Skip leading & trailing whitespaces char *t = rtrim(ltrim(buf)); // Skip empty lines if (*t == 0) continue; // If no section has been set, this config file is invalid! if (section.name.empty()) { error("ConfigFile::loadFromStream: Key/value pair found outside a section in line %d", lineno); } // Split string at '=' into 'key' and 'value'. char *p = strchr(t, '='); if (!p) error("ConfigFile::loadFromStream: Junk found in line line %d: '%s'", lineno, t); *p = 0; kv.key = rtrim(t); kv.value = ltrim(p + 1); kv.comment = comment; comment.clear(); assert(isValidName(kv.key)); section.keys.push_back(kv); } } // Save last section if (!section.name.empty()) _sections.push_back(section); return (!stream.ioFailed() || stream.eos()); }