UString readString(SeekableReadStream &stream, Encoding encoding) { std::vector<byte> output; uint32 c; while (((c = readFakeChar(stream, encoding)) != '\0') && !stream.eos()) writeFakeChar(output, c, encoding); return createString(output, encoding); }
bool StreamTokenizer::isChunkEnd(SeekableReadStream &stream) { if (stream.eos()) return true; bool chunkEnd = isIn(stream.readByte(), _chunkEnds); stream.seek(-1, SEEK_CUR); return chunkEnd; }
void StreamTokenizer::skipChunk(SeekableReadStream &stream) { assert(!_chunkEnds.empty()); while (!stream.eos() && !stream.err()) { if (isIn(stream.readByte(), _chunkEnds)) { stream.seek(-1, SEEK_CUR); break; } } if (stream.err()) throw Exception(kReadError); }
void StreamTokenizer::nextChunk(SeekableReadStream &stream) { skipChunk(stream); byte c = stream.readByte(); if (stream.eos() || stream.err()) return; if (!isIn(c, _chunkEnds)) stream.seek(-1, SEEK_CUR); else if (stream.pos() == stream.size()) // This actually the last character, read one more byte to properly set the EOS state stream.readByte(); }
UString readStringLine(SeekableReadStream &stream, Encoding encoding) { std::vector<byte> output; uint32 c; while (((c = readFakeChar(stream, encoding)) != '\0') && !stream.eos()) { if (c == '\n') break; if (c == '\r') continue; writeFakeChar(output, c, encoding); } return createString(output, encoding); }
void FoxPro::loadMemos(SeekableReadStream &fpt) { fpt.skip(4); // Next free block fpt.skip(2); // Unused _memoBlockSize = fpt.readUint16BE(); if (_memoBlockSize < 33) _memoBlockSize *= 1024; fpt.skip(504); // Unused while (!fpt.eos()) { _memos.push_back(new byte[_memoBlockSize]); byte *data = _memos.back(); fpt.read(data, _memoBlockSize); } }
void FoxPro::loadFields(SeekableReadStream &dbf, uint32 recordSize) { // Read all field descriptions, 0x0D is the end marker uint32 fieldsLength = 0; while (!dbf.eos() && (dbf.readByte() != 0x0D)) { Field field; dbf.seek(-1, SeekableReadStream::kOriginCurrent); field.name = readStringFixed(dbf, kEncodingASCII, 11); field.type = (Type) dbf.readByte(); field.offset = dbf.readUint32LE(); field.size = dbf.readByte(); field.decimals = dbf.readByte(); field.flags = dbf.readByte(); field.autoIncNext = dbf.readUint32LE(); field.autoIncStep = dbf.readByte(); dbf.skip(8); // Reserved if (field.offset != (fieldsLength + 1)) throw Exception("Field offset makes no sense (%d != %d)", field.offset, fieldsLength + 1); if (field.type == kTypeMemo) _hasMemo = true; fieldsLength += field.size; _fields.push_back(field); } if (recordSize != (fieldsLength + 1)) throw Exception("Length of all fields does not equal the record size"); }
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()); }
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 }
void ConfigFile::load(SeekableReadStream &stream) { UString comment; ConfigDomain *domain = 0; int lineNumber = 0; int domainLineNumber = 0; while (!stream.eos()) { lineNumber++; // Read a line UString line = readStringLine(stream, kEncodingUTF8); // Parse it UString domainName; UString key, value, lineComment; parseConfigLine(line, domainName, key, value, lineComment, lineNumber); if (!domainName.empty()) { // New domain // Finish up the old domain addDomain(domain, domainLineNumber); // Check that the name is actually valid if (!isValidName(domainName)) throw Exception("\"%s\" isn't a valid domain name (line %d)", domainName.c_str(), lineNumber); // Create the new domain domain = new ConfigDomain(domainName); domain->_prologue = comment; domain->_comment = lineComment; comment.clear(); lineComment.clear(); domainLineNumber = lineNumber; } if (!key.empty()) { // New key if (!domain) throw Exception("Found a key outside a domain (line %d)", lineNumber); if (!isValidName(key)) throw Exception("\"%s\" isn't a valid key name (line %d)", key.c_str(), lineNumber); // Add collected comments to the domain if (!comment.empty()) addDomainKey(*domain, "", "", comment, lineNumber); // Add the key to the domain addDomainKey(*domain, key, value, lineComment, lineNumber); comment.clear(); lineComment.clear(); } // Collect comments, we don't yet know where those belong to. if (!lineComment.empty()) { if (!comment.empty()) comment += '\n'; comment += lineComment; } // Empty line, associate collected comments with the current domain if (domainName.empty() && key.empty() && value.empty() && lineComment.empty()) { if (!comment.empty() && !stream.eos()) { if (!domain) { // We have no domain yet, add it to the file's prologue if (!_prologue.empty()) _prologue += '\n'; _prologue += comment; } else addDomainKey(*domain, "", "", comment, lineNumber); comment.clear(); } } } // Finish up the last domain addDomain(domain, domainLineNumber); // We still have comments, those apparently belong to the bottom of the file if (!comment.empty()) _epilogue = comment; }
UString StreamTokenizer::getToken(SeekableReadStream &stream) { // Init bool chunkEnd = false; bool inQuote = false; bool hasSeparator = false; uint32 separator = 0; UString token; // Run through the stream, character by character while (!stream.eos()) { char c = (char) stream.readByte(); if (isIn(c, _chunkEnds)) { // This is a end character, seek back and break stream.seek(-1, SEEK_CUR); chunkEnd = true; break; } if (isIn(c, _quotes)) { // This is a quote character, set state inQuote = !inQuote; continue; } if (!inQuote && isIn(c, _separators)) { // We're not in a quote and this is a separator if (!token.empty()) { // We have a token hasSeparator = true; separator = c; break; } // We don't yet have a token, let the consecutive separator rule decide what to do if (_conSepRule == kRuleHeed) { // We heed every separator hasSeparator = true; separator = c; break; } if ((_conSepRule == kRuleIgnoreSame) && hasSeparator && (separator != ((byte) c))) { // We ignore only consecutive separators that are the same hasSeparator = true; separator = c; break; } // We ignore all consecutive separators hasSeparator = true; separator = c; continue; } if (isIn(c, _ignores)) // This is a character to be ignored, do so continue; // A normal character, add it to our token token += c; } // Is the string actually empty? if (!token.empty() && (*token.begin() == '\0')) token.clear(); if (!chunkEnd && (_conSepRule != kRuleHeed)) { // We have to look for consecutive separators while (!stream.eos()) { uint32 c = stream.readByte(); // Use the rule to determine when we should abort skipping consecutive separators if (((_conSepRule == kRuleIgnoreSame) && (c != separator)) || ((_conSepRule == kRuleIgnoreAll ) && !isIn(c, _separators))) { stream.seek(-1, SEEK_CUR); break; } } } // And return the token return token; }
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()); }