Example #1
0
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);
}
Example #2
0
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();
}
Example #3
0
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());
}
Example #4
0
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
}
Example #5
0
void ConfigFile::load(SeekableReadStream &stream) {
	UString comment;

	ConfigDomain *domain = 0;

	int lineNumber = 0;
	int domainLineNumber = 0;
	while (!stream.eos() && !stream.err()) {
		lineNumber++;

		// Read a line
		UString line;
		line.readLineUTF8(stream);

		// 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();
			}
		}

	}

	if (stream.err())
		throw Exception(kReadError);

	// 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;
}