Beispiel #1
0
void Decompiler::writeSubRoutine(Common::WriteStream &out, const NWScript::SubRoutine &subRoutine) {
	out.writeString("\n");
	out.writeString(formatSignature(subRoutine, _ncs->getGame(), true));
	out.writeString(" {\n");

	// TODO: local sub routine variables

	if (!subRoutine.blocks.empty())
		writeBlock(out, subRoutine.blocks.front(), 1);

	out.writeString("}\n");
}
Beispiel #2
0
void Decompiler::writeIfControl(Common::WriteStream &out, const ControlStructure &control, size_t indent) {
	writeIndent(out, indent);

	const Variable *cond = control.ifCond->instructions.back()->variables[0];
	out.writeString("if (");
	out.writeString(formatVariableName(cond));
	out.writeString(") {\n");

	if (control.ifTrue)
		writeBlock(out, control.ifTrue, indent + 1);

	writeIndent(out, indent);
	out.writeString("}");

	if (control.ifElse) {
		out.writeString(" else {\n");
		writeBlock(out, control.ifElse, indent + 1);

		writeIndent(out, indent);
		out.writeString("}");
	}
	out.writeString("\n");

	if (control.ifNext)
		writeBlock(out, control.ifNext, indent);
}
Beispiel #3
0
void TwoDAFile::writeCSV(Common::WriteStream &out) const {
	// Write column headers

	for (size_t i = 0; i < _headers.size(); i++) {
		const bool needQuote = _headers[i].contains(',');
		if (needQuote)
			out.writeByte('"');

		out.writeString(_headers[i]);

		if (needQuote)
			out.writeByte('"');

		if (i < (_headers.size() - 1))
			out.writeByte(',');
	}

	out.writeByte('\n');

	// Write array

	for (size_t i = 0; i < _rows.size(); i++) {
		for (size_t j = 0; j < _rows[i]->_data.size(); j++) {
			const bool needQuote = _rows[i]->_data[j].contains(',');

			if (needQuote)
				out.writeByte('"');

			if (_rows[i]->_data[j] != "****")
				out.writeString(_rows[i]->_data[j]);

			if (needQuote)
				out.writeByte('"');

			if (j < (_rows[i]->_data.size() - 1))
				out.writeByte(',');
		}

		out.writeByte('\n');
	}

	out.flush();
}
Beispiel #4
0
void Decompiler::createNSS(Common::WriteStream &out) {
	_ncs->analyzeStack();
	_ncs->analyzeControlFlow();

	out.writeString("// Decompiled using ncsdecomp");

	const Stack &stack = _ncs->getGlobals();

	out.writeString("\n\n");
	for (const auto &global : stack) {
		out.writeString(getVariableTypeName(global.variable->type, _ncs->getGame()));
		out.writeString(" " + formatVariableName(global.variable));
		out.writeString(";\n");
	}

	const SubRoutines &subRoutines = _ncs->getSubRoutines();
	for (const auto &subRoutine : subRoutines) {
		writeSubRoutine(out, subRoutine);
	}
}
Beispiel #5
0
void TwoDAFile::writeASCII(Common::WriteStream &out) const {
	// Write header

	out.writeString("2DA V2.0\n");
	if (!_defaultString.empty())
		out.writeString(Common::UString::format("DEFAULT: %s", _defaultString.c_str()));
	out.writeByte('\n');

	// Calculate column lengths

	std::vector<size_t> colLength;
	colLength.resize(_headers.size() + 1, 0);

	const Common::UString maxRow = Common::UString::format("%d", (int)_rows.size() - 1);
	colLength[0] = maxRow.size();

	for (size_t i = 0; i < _headers.size(); i++)
		colLength[i + 1] = _headers[i].size();

	for (size_t i = 0; i < _rows.size(); i++) {
		for (size_t j = 0; j < _rows[i]->_data.size(); j++) {
			const bool   needQuote = _rows[i]->_data[j].contains(' ');
			const size_t length    = needQuote ? _rows[i]->_data[j].size() + 2 : _rows[i]->_data[j].size();

			colLength[j + 1] = MAX<size_t>(colLength[j + 1], length);
		}
	}

	// Write column headers

	out.writeString(Common::UString::format("%-*s", (int)colLength[0], ""));

	for (size_t i = 0; i < _headers.size(); i++)
		out.writeString(Common::UString::format(" %-*s", (int)colLength[i + 1], _headers[i].c_str()));

	out.writeByte('\n');

	// Write array

	for (size_t i = 0; i < _rows.size(); i++) {
		out.writeString(Common::UString::format("%*u", (int)colLength[0], (uint)i));

		for (size_t j = 0; j < _rows[i]->_data.size(); j++) {
			const bool needQuote = _rows[i]->_data[j].contains(' ');

			Common::UString cellString;
			if (needQuote)
				cellString = Common::UString::format("\"%s\"", _rows[i]->_data[j].c_str());
			else
				cellString = _rows[i]->_data[j];

			out.writeString(Common::UString::format(" %-*s", (int)colLength[j + 1], cellString.c_str()));

		}

		out.writeByte('\n');
	}

	out.flush();
}
Beispiel #6
0
void Testsuite::logPrintf(const char *fmt, ...) {
	// Assuming log message size to be not greater than STRINGBUFLEN i.e 256
	char buffer[STRINGBUFLEN];
	va_list vl;
	va_start(vl, fmt);
	vsnprintf(buffer, STRINGBUFLEN, fmt, vl);
	va_end(vl);
	Common::WriteStream *ws = ConfigParams::instance().getLogWriteStream();

	if (ws) {
		ws->writeString(buffer);
		ws->flush();
		debugCN(kTestbedLogOutput, "%s", buffer);
	} else {
		debugCN(kTestbedLogOutput, "%s", buffer);
	}
}
Beispiel #7
0
void Testsuite::logDetailedPrintf(const char *fmt, ...) {
	// Assuming log message size to be not greater than STRINGBUFLEN i.e 256
	// Messages with this function would only be displayed if -d1 is specified on command line
	char buffer[STRINGBUFLEN];
	va_list vl;
	va_start(vl, fmt);
	vsnprintf(buffer, STRINGBUFLEN, fmt, vl);
	va_end(vl);
	Common::WriteStream *ws = ConfigParams::instance().getLogWriteStream();

	if (ws) {
		ws->writeString(buffer);
		ws->flush();
		debugCN(1, kTestbedLogOutput, "%s", buffer);
	} else {
		debugCN(1, kTestbedLogOutput, "%s", buffer);
	}
}
Beispiel #8
0
bool LureEngine::saveGame(uint8 slotNumber, Common::String &caption) {
	Common::WriteStream *f = this->_saveFileMan->openForSaving(
		generateSaveName(slotNumber));
	if (f == NULL)
		return false;

	f->write("lure", 5);
	f->writeByte(getLanguage());
	f->writeByte(LURE_SAVEGAME_MINOR);
	f->writeString(caption);
	f->writeByte(0); // End of string terminator

	Resources::getReference().saveToStream(f);
	Game::getReference().saveToStream(f);
	Sound.saveToStream(f);
	Fights.saveToStream(f);
	Room::getReference().saveToStream(f);

	delete f;
	return true;
}
Beispiel #9
0
/**
 * This test creates a file testbed.out, writes a sample data and confirms if
 * it is same by reading the file again.
 */
TestExitStatus FStests::testWriteFile() {
	const Common::String &path = ConfMan.get("path");
	Common::FSNode gameRoot(path);
	if (!gameRoot.exists()) {
		Testsuite::logPrintf("Couldn't open the game data directory %s", path.c_str());
		 return kTestFailed;
	}

	Common::FSNode fileToWrite = gameRoot.getChild("testbed.out");

	Common::WriteStream *ws = fileToWrite.createWriteStream();

	if (!ws) {
		Testsuite::logDetailedPrintf("Can't open writable file in game data dir\n");
		 return kTestFailed;
	}

	ws->writeString("ScummVM Rocks!");
	ws->flush();
	delete ws;

	Common::SeekableReadStream *rs = fileToWrite.createReadStream();
	if (!rs) {
		Testsuite::logDetailedPrintf("Can't open recently written file testbed.out in game data dir\n");
		 return kTestFailed;
	}
	Common::String readFromFile = rs->readLine();
	delete rs;

	if (readFromFile.equals("ScummVM Rocks!")) {
		// All good
		Testsuite::logDetailedPrintf("Data written and read correctly\n");
		 return kTestPassed;
	}

	 return kTestFailed;
}
Beispiel #10
0
void Decompiler::writeBlock(Common::WriteStream &out, const Block *block, size_t indent) {
	for (const auto instruction : block->instructions) {
		writeInstruction(out, instruction, indent);
	}

	for (const auto &childType : block->childrenTypes) {
		if (isSubRoutineCall(childType)) {
			writeIndent(out, indent);
			const Instruction *instruction = block->instructions.back();


			out.writeString(formatJumpLabelName(*instruction->branches[0]));
			out.writeString("(");

			for (size_t i = 0; i < instruction->variables.size(); ++i) {
				out.writeString(formatVariableName(instruction->variables[i]));
				if (i < instruction->variables.size() - 1)
					out.writeString(", ");
			}

			out.writeString(");\n");

			writeBlock(out, block->children[1], indent);
		}
	}

	for (const auto &control : block->controls) {
		if (control.type == kControlTypeReturn) {
			writeIndent(out, indent);
			out.writeString("return;\n");
		} else if (control.type == kControlTypeIfCond) {
			writeIfControl(out, control, indent);
		}

		// TODO: while
		// TODO: break
		// TODO: continue
	}
}
Beispiel #11
0
void TwoDAFile::writeBinary(Common::WriteStream &out) const {
	const size_t columnCount = _headers.size();
	const size_t rowCount    = _rows.size();
	const size_t cellCount   = columnCount * rowCount;

	out.writeString("2DA V2.b\n");

	// Write the column headers

	for (std::vector<Common::UString>::const_iterator h = _headers.begin(); h != _headers.end(); ++h) {
		out.writeString(*h);
		out.writeByte('\t');
	}
	out.writeByte('\0');

	// Write the row indices

	out.writeUint32LE((uint32) rowCount);
	for (size_t i = 0; i < rowCount; i++) {
		out.writeString(Common::composeString(i));
		out.writeByte('\t');
	}

	/* Deduplicate cell data strings. Binary 2DA files don't store the
	 * data for each cell directly: instead, each cell contains an offset
	 * into a data array. This way, cells with the same data only need to
	 * to store this data once.
	 *
	 * The original binary 2DA files in KotOR/KotOR2 make extensive use
	 * of that, and we should do this as well.
	 *
	 * Basically, this involves going through each cell, and looking up
	 * if we already saved this particular piece of data. If not, save
	 * it, otherwise only remember the offset. There's no need to be
	 * particularly smart about it, so we're just doing it the naive
	 * O(n^2) way.
	 */

	std::vector<Common::UString> data;
	std::vector<size_t> offsets;

	data.reserve(cellCount);
	offsets.reserve(cellCount);

	size_t dataSize = 0;

	std::vector<size_t> cells;
	cells.reserve(cellCount);

	for (size_t i = 0; i < rowCount; i++) {
		assert(_rows[i]);

		for (size_t j = 0; j < columnCount; j++) {
			const Common::UString cell = _rows[i]->getString(j);

			// Do we already know about this cell data string?
			size_t foundCell = SIZE_MAX;
			for (size_t k = 0; k < data.size(); k++) {
				if (data[k] == cell) {
					foundCell = k;
					break;
				}
			}

			// If not, add it to the cell data array
			if (foundCell == SIZE_MAX) {
				foundCell = data.size();

				data.push_back(cell);
				offsets.push_back(dataSize);

				dataSize += data.back().size() + 1;

				if (dataSize > 65535)
					throw Common::Exception("TwoDAFile::writeBinary(): Cell data size overflow");
			}

			// Remember the offset to the cell data array
			cells.push_back(offsets[foundCell]);
		}
	}

	// Write cell data offsets
	for (std::vector<size_t>::const_iterator c = cells.begin(); c != cells.end(); ++c)
		out.writeUint16LE((uint16) *c);

	// Size of the all cell data strings
	out.writeUint16LE((uint16) dataSize);

	// Write cell data strings
	for (std::vector<Common::UString>::const_iterator d = data.begin(); d != data.end(); ++d) {
		out.writeString(*d);
		out.writeByte('\0');
	}
}
Beispiel #12
0
void Decompiler::writeIndent(Common::WriteStream &out, size_t indent) {
	for (size_t i = 0; i < indent; ++i)
		out.writeString("\t");
}
Beispiel #13
0
void Decompiler::writeInstruction(Common::WriteStream &out, const Instruction* instruction, size_t indent) {
	switch (instruction->opcode) {
		case kOpcodeCONST: {
			const Variable *v = instruction->variables[0];
			writeIndent(out, indent);
			out.writeString(getVariableTypeName(v->type) + " " + formatVariableName(v) + " = " + formatInstructionData(*instruction) + ";\n");

			break;
		}

		case kOpcodeACTION: {
			unsigned int paramCount = instruction->args[1];

			writeIndent(out, indent);

			if (instruction->variables.size() > paramCount) {
				const Variable *ret = instruction->variables.back();
				out.writeString(getVariableTypeName(ret->type, _ncs->getGame()) + " " + formatVariableName(ret) + " = ");
			}

			out.writeString(getFunctionName(_ncs->getGame(), instruction->args[0]));
			out.writeString("(");
			for (unsigned int i = 0; i < paramCount; ++i) {
				out.writeString(formatVariableName(instruction->variables[i]));
				if (i < paramCount - 1)
					out.writeString(", ");
			}
			out.writeString(");\n");

			break;
		}

		case kOpcodeCPDOWNBP:
		case kOpcodeCPDOWNSP:
		case kOpcodeCPTOPBP:
		case kOpcodeCPTOPSP: {
			const Variable *v1 = instruction->variables[0];
			const Variable *v2 = instruction->variables[1];

			writeIndent(out, indent);
			out.writeString(getVariableTypeName(v2->type, _ncs->getGame()) + " " + formatVariableName(v2) + " = " + formatVariableName(v1) + ";\n");

			break;
		}

		case kOpcodeLOGAND: {
			const Variable *v1 = instruction->variables[0];
			const Variable *v2 = instruction->variables[1];
			const Variable *result = instruction->variables[2];

			writeIndent(out, indent);
			out.writeString(
					getVariableTypeName(result->type, _ncs->getGame()) + " " +
					formatVariableName(result) + " = " +
					formatVariableName(v1) + " && " + formatVariableName(v2) + ";\n"
			);

			break;
		}

		case kOpcodeLOGOR: {
			const Variable *v1 = instruction->variables[0];
			const Variable *v2 = instruction->variables[1];
			const Variable *result = instruction->variables[2];

			writeIndent(out, indent);
			out.writeString(
					getVariableTypeName(result->type, _ncs->getGame()) + " " +
					formatVariableName(result) + " = " +
					formatVariableName(v1) + " || " + formatVariableName(v2) + ";\n"
			);

			break;
		}

		case kOpcodeEQ: {
			const Variable *v1 = instruction->variables[0];
			const Variable *v2 = instruction->variables[1];
			const Variable *result = instruction->variables[2];

			writeIndent(out, indent);
			out.writeString(
					getVariableTypeName(result->type, _ncs->getGame()) + " " +
					formatVariableName(result) + " = " +
					formatVariableName(v1) + " == " + formatVariableName(v2) + ";\n"
			);


			break;
		}

		case kOpcodeLEQ: {
			const Variable *v1 = instruction->variables[0];
			const Variable *v2 = instruction->variables[1];
			const Variable *result = instruction->variables[2];

			writeIndent(out, indent);
			out.writeString(
					getVariableTypeName(result->type, _ncs->getGame()) + " " +
					formatVariableName(result) + " = " +
					formatVariableName(v1) + " <= " + formatVariableName(v2) + ";\n"
			);

			break;
		}

		case kOpcodeLT: {
			const Variable *v1 = instruction->variables[0];
			const Variable *v2 = instruction->variables[1];
			const Variable *result = instruction->variables[2];

			writeIndent(out, indent);
			out.writeString(
					getVariableTypeName(result->type, _ncs->getGame()) + " " +
					formatVariableName(result) + " = " +
					formatVariableName(v1) + " < " + formatVariableName(v2) + ";\n"
			);

			break;
		}

		case kOpcodeGEQ: {
			const Variable *v1 = instruction->variables[0];
			const Variable *v2 = instruction->variables[1];
			const Variable *result = instruction->variables[2];

			writeIndent(out, indent);
			out.writeString(
					getVariableTypeName(result->type, _ncs->getGame()) + " " +
					formatVariableName(result) + " = " +
					formatVariableName(v1) + " >= " + formatVariableName(v2) + ";\n"
			);

			break;
		}

		case kOpcodeGT: {
			const Variable *v1 = instruction->variables[0];
			const Variable *v2 = instruction->variables[1];
			const Variable *result = instruction->variables[2];

			writeIndent(out, indent);
			out.writeString(
					getVariableTypeName(result->type, _ncs->getGame()) + " " +
					formatVariableName(result) + " = " +
					formatVariableName(v1) + " > " + formatVariableName(v2) + ";\n"
			);

			break;
		}

		case kOpcodeNOT: {
			const Variable *v = instruction->variables[0];
			const Variable *result = instruction->variables[1];

			writeIndent(out, indent);
			out.writeString(
					getVariableTypeName(result->type, _ncs->getGame()) + " " +
					formatVariableName(result) + " = " +
					"!" + formatVariableName(v) + ";\n"
			);

			break;
		}

		case kOpcodeRSADD: {
			const Variable *v = instruction->variables[0];

			writeIndent(out, indent);
			out.writeString(
					getVariableTypeName(v->type, _ncs->getGame()) + " " +
					formatVariableName(v) + " = "
			);

			switch (v->type) {
				case kTypeString:
					out.writeString("\"\"");
					break;
				case kTypeInt:
					out.writeString("0");
					break;
				case kTypeFloat:
					out.writeString("0.0");
					break;

				default:
					// TODO: No idea how empty objects or engine types are intialized.
					out.writeString("0");
					break;
			}

			out.writeString(";\n");

			break;
		}

		// TODO: Not all necessary instruction are implemented here

		default:
			break;
	}
}