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"); }
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); }
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(); }
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); } }
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(); }
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); } }
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); } }
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; }
/** * 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; }
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 } }
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'); } }
void Decompiler::writeIndent(Common::WriteStream &out, size_t indent) { for (size_t i = 0; i < indent; ++i) out.writeString("\t"); }
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; } }