Example #1
0
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);
	}
}
Example #2
0
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);
}
Example #3
0
UString readStringFixed(SeekableReadStream &stream, Encoding encoding, size_t length) {
	std::vector<byte> output;
	output.resize(length);

	length = stream.read(&output[0], length);
	output.resize(length);

	return createString(output, encoding);
}
Example #4
0
size_t ZipFile::findCentralDirectoryEnd(SeekableReadStream &zip) {
	size_t uSizeFile = zip.size();

	size_t uMaxBack = MIN<size_t>(0xFFFF, uSizeFile); // Maximum size of global comment

	byte *buf = new byte[BUFREADCOMMENT + 4];

	size_t uPosFound = 0;
	size_t uBackRead = 4;
	while ((uPosFound == 0) && (uBackRead < uMaxBack)) {
		size_t uReadSize, uReadPos;

		uBackRead = MIN<size_t>(uMaxBack, uBackRead + BUFREADCOMMENT);

		uReadPos  = uSizeFile - uBackRead;

		uReadSize = MIN<size_t>(BUFREADCOMMENT + 4, uSizeFile - uReadPos);

		try {
			zip.seek(uReadPos);
		} catch (...) {
			delete[] buf;
			return 0;
		}

		if (zip.read(buf, uReadSize) != uReadSize) {
			uPosFound = 0;
			break;
		}

		for (size_t i = (uReadSize - 3); i-- > 0; )
			if (READ_LE_UINT32(buf + i) == 0x06054B50) {
				uPosFound = uReadPos + i;
				break;
			}

	}

	delete[] buf;
	return uPosFound;
}
Example #5
0
size_t searchBackwards(SeekableReadStream &haystack, const byte *needle, size_t needleSize,
                       size_t maxReadBack) {

	if (needleSize == 0 || maxReadBack == 0)
		return SIZE_MAX;

	assert(maxReadBack >= needleSize);

	static const size_t kReadBufferSize = 0x400;

	const size_t sizeFile = haystack.size();
	const size_t maxBack  = MIN<size_t>(maxReadBack, sizeFile);

	ScopedArray<byte> buf(new byte[kReadBufferSize + needleSize]);

	size_t backRead = needleSize;
	while (backRead < maxBack) {
		backRead = MIN<size_t>(maxBack, backRead + kReadBufferSize);

		const size_t readPos  = sizeFile - backRead;
		const size_t readSize = MIN<size_t>(kReadBufferSize + needleSize, sizeFile - readPos);

		try {
			haystack.seek(readPos);
		} catch (...) {
			break;
		}

		if (haystack.read(buf.get(), readSize) != readSize)
			break;

		for (size_t i = (readSize - (needleSize - 1)); i-- > 0; )
			if (!memcmp(buf.get() + i, needle, needleSize))
				return readPos + i;
	}

	return SIZE_MAX;
}
Example #6
0
static uint32 readFakeChar(SeekableReadStream &stream, Encoding encoding) {
	byte data[2];

	switch (encoding) {
		case kEncodingASCII:
		case kEncodingLatin9:
		case kEncodingUTF8:
		case kEncodingCP1250:
		case kEncodingCP1251:
		case kEncodingCP1252:
		case kEncodingCP932:
		case kEncodingCP936:
		case kEncodingCP949:
		case kEncodingCP950:
			if (stream.read(data, 1) != 1)
				return 0;

			return data[0];

		case kEncodingUTF16LE:
			if (stream.read(data, 2) != 2)
				return 0;

			return READ_LE_UINT16(data);

		case kEncodingUTF16BE:
			if (stream.read(data, 2) != 2)
				return 0;

			return READ_BE_UINT16(data);

		default:
			break;
	}

	return 0;
}
Example #7
0
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");
}
Example #8
0
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);
}
Example #9
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 #10
0
void ZipFile::getFileProperties(SeekableReadStream &zip, const IFile &file,
		uint16 &compMethod, uint32 &compSize, uint32 &realSize) const {

	zip.seek(file.offset);

	uint32 tag = zip.readUint32LE();
	if (tag != 0x04034B50)
		throw Exception("Unknown ZIP record %08X", tag);

	zip.skip(4);

	compMethod = zip.readUint16LE();

	zip.skip(8);

	compSize = zip.readUint32LE();
	realSize = zip.readUint32LE();

	uint16 nameLength  = zip.readUint16LE();
	uint16 extraLength = zip.readUint16LE();

	zip.skip(nameLength);
	zip.skip(extraLength);
}
Example #11
0
String NEResources::getResourceString(SeekableReadStream &exe, uint32 offset) {
    uint32 curPos = exe.pos();

    if (!exe.seek(offset)) {
        exe.seek(curPos);
        return "";
    }

    uint8 length = exe.readByte();

    String string;
    for (uint16 i = 0; i < length; i++)
        string += (char)exe.readByte();

    exe.seek(curPos);
    return string;
}
Example #12
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
}
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());
}
Example #14
0
UString StreamTokenizer::getToken(SeekableReadStream &stream) {
	// Init
	bool   chunkEnd     = false;
	bool   inQuote      = false;
	uint32 separator    = 0xFFFFFFFF;

	UString token;

	// Run through the stream, character by character
	uint32 c;
	while ((c = stream.readChar()) != ReadStream::kEOF) {

		if (isIn(c, _chunkEnds)) {
			// This is a end character, seek back and break
			stream.seek(-1, SeekableReadStream::kOriginCurrent);
			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

				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

				separator = c;
				break;
			}

			if ((_conSepRule == kRuleIgnoreSame) && (separator != 0xFFFFFFFF) && (separator != c)) {
				// We ignore only consecutive separators that are the same
				separator = c;
				break;
			}

			// We ignore all consecutive separators
			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 ((c = stream.readChar()) != ReadStream::kEOF) {

			// 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, SeekableReadStream::kOriginCurrent);
				break;
			}
		}

	}

	// And return the token
	return token;
}
Example #15
0
void ZipFile::load(SeekableReadStream &zip) {
	size_t endPos = findCentralDirectoryEnd(zip);
	if (endPos == 0)
		throw Exception("End of central directory record not found");

	zip.seek(endPos);

	zip.skip(4); // Header, already checked

	uint16 curDisk        = zip.readUint16LE();
	uint16 centralDirDisk = zip.readUint16LE();

	uint16 curDiskDirs = zip.readUint16LE();
	uint16 totalDirs   = zip.readUint16LE();

	if ((curDisk != 0) || (curDisk != centralDirDisk) || (curDiskDirs != totalDirs))
		throw Exception("Unsupported multi-disk ZIP file");

	zip.skip(4); // Size of central directory

	uint32 centralDirPos = zip.readUint32LE();
	zip.seek(centralDirPos);

	uint32 tag = zip.readUint32LE();
	if (tag != 0x02014B50)
		throw Exception("Unknown ZIP record %08X", tag);

	_iFiles.reserve(totalDirs);
	while (tag == 0x02014B50) {
		 File  file;
		IFile iFile;

		zip.skip(20);

		iFile.size = zip.readUint32LE();

		uint16 nameLength    = zip.readUint16LE();
		uint16 extraLength   = zip.readUint16LE();
		uint16 commentLength = zip.readUint16LE();
		uint16 diskNum       = zip.readUint16LE();

		if (diskNum != 0)
			throw Exception("Unsupported multi-disk ZIP file");

		zip.skip(6); // File attributes

		iFile.offset = zip.readUint32LE();

		file.name = readStringFixed(zip, kEncodingASCII, nameLength).toLower();

		zip.skip(extraLength);
		zip.skip(commentLength);

		tag = zip.readUint32LE();
		if ((tag != 0x02014B50) && (tag != 0x06054B50))
			throw Exception("Unknown ZIP record %08X", tag);

		// Ignore empty file names
		if (!file.name.empty()) {
			// HACK: Skip any filename with a trailing slash because it's
			// a directory. The proper solution would be to interpret the
			// file attributes.

			if (*(--file.name.end()) != '/') {
				file.index = _iFiles.size();

				_files.push_back(file);
				_iFiles.push_back(iFile);
			}
		}
	}
}
Example #16
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 #17
0
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;
}