| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 |
- /* ScummVM Tools
- *
- * ScummVM Tools is the legal property of its developers, whose
- * names are too numerous to list here. Please refer to the
- * COPYRIGHT file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
- #include "objectFactory.h"
- #include "decompiler_disassembler.h"
- #include "decompiler_engine.h"
- #include "instruction.h"
- #include "control_flow.h"
- //#include "scummv6/engine.h"
- #include "decompiler_engine.h"
- #include <fstream>
- #include <iostream>
- #include <map>
- #include <string>
- #include <vector>
- #ifdef _MSC_VER
- #pragma warning (push)
- #pragma warning(disable:4512)
- #pragma warning(disable:4100)
- #endif
- #include <boost/program_options.hpp>
- #include <boost/graph/graphviz.hpp>
- #ifdef _MSC_VER
- #pragma warning (pop)
- #endif
- namespace po = boost::program_options;
- #define ENGINE(id, description, engineClass) engines[std::string(id)] = description; engineFactory.addEntry<engineClass>(std::string(id));
- int main(int argc, char** argv) {
- try {
- std::map<std::string, std::string> engines;
- ObjectFactory<std::string, Engine> engineFactory;
- ENGINE("scummv6", "SCUMM v6", Engine);//Scumm::v6::Scummv6Engine);
- po::options_description visible("Options", 1024);
- visible.add_options()
- ("help,h", "Produce this help message.")
- ("engine,e", po::value<std::string>(), "Engine the script originates from.")
- ("list,l", "List the supported engines.")
- ("dump-disassembly,d", po::value<std::string>()->implicit_value(""), "Dump the disassembly to a file. Leave out filename to output to stdout.")
- ("dump-graph,g", po::value<std::string>()->implicit_value(""), "Output the control flow graph in dot format to a file. Leave out filename to output to stdout.")
- ("only-disassembly,D", "Stops after disassembly. Implies -d.")
- ("only-graph,G", "Stops after control flow graph has been generated. Implies -g.")
- ("show-unreachable,u", "Show the address and contents of unreachable groups in the script.")
- ("variant,v", po::value<std::string>()->default_value(""), "Tell the engine that the script is from a specific variant. To see a list of variants supported by a specific engine, use the -h option and the -e option together.")
- ("no-stack-effect,s", "Leave out the stack effect when printing raw instructions.");
- po::options_description args("", 1024);
- args.add(visible).add_options()
- ("input-file", po::value<std::string>(), "Input file");
- po::positional_options_description fileArg;
- fileArg.add("input-file", -1);
- po::variables_map vm;
- try {
- // FIXME: If specified as the last parameter before the input file name, -d currently requires a filename to specified. -d must be specified earlier than that if outputting to stdout.
- po::store(po::command_line_parser(argc, argv).options(args).positional(fileArg).run(), vm);
- po::notify(vm);
- } catch (std::exception& e) {
- std::cout << e.what() << std::endl;
- }
- if (vm.count("list")) {
- std::cout << "Available engines:" << "\n";
- std::map<std::string, std::string>::iterator it;
- for (it = engines.begin(); it != engines.end(); it++)
- std::cout << (*it).first << " " << (*it).second << "\n";
- return 0;
- }
- if (vm.count("help") || !vm.count("input-file")) {
- std::cout << "Usage: " << argv[0] << " [option...] file" << "\n";
- std::cout << visible << "\n";
- if (vm.count("engine") && engines.find(vm["engine"].as<std::string>()) != engines.end()) {
- Engine *engine = engineFactory.create(vm["engine"].as<std::string>());
- std::vector<std::string> variants;
- engine->getVariants(variants);
- if (variants.empty()) {
- std::cout << engines[vm["engine"].as<std::string>()] << " does not use variants.\n";
- } else {
- std::cout << "Supported variants for " << engines[vm["engine"].as<std::string>()] << ":\n";
- for (std::vector<std::string>::iterator i = variants.begin(); i != variants.end(); ++i) {
- std::cout << " " << *i << "\n";
- }
- }
- delete engine;
- std::cout << "\n";
- }
- std::cout << "Note: If outputting to stdout, -d or -g must NOT be specified immediately before the input file.\n";
- return 1;
- }
- if (!vm.count("engine")) {
- std::cout << "Engine must be specified.\n";
- return 2;
- } else if (engines.find(vm["engine"].as<std::string>()) == engines.end()) {
- std::cout << "Unknown engine.\n";
- return 2;
- }
- if (vm.count("no-stack-effect")) {
- setOutputStackEffect(false);
- }
- std::unique_ptr<Engine> engine(engineFactory.create(vm["engine"].as<std::string>()));
- engine->_variant = vm["variant"].as<std::string>();
- std::string inputFile = vm["input-file"].as<std::string>();
- // Disassembly
- InstVec insts;
- auto disassembler = engine->GetDisassembler(insts);
- disassembler->open(inputFile.c_str());
- disassembler->disassemble();
- if (vm.count("dump-disassembly")) {
- std::streambuf *buf;
- std::ofstream of;
- if (vm["dump-disassembly"].as<std::string>() != "") {
- of.open(vm["dump-disassembly"].as<std::string>().c_str());
- buf = of.rdbuf();
- } else {
- buf = std::cout.rdbuf();
- }
- std::ostream out(buf);
- disassembler->dumpDisassembly(out);
- }
- if (!engine->supportsCodeFlow() || vm.count("only-disassembly") || insts.empty()) {
- if (!vm.count("dump-disassembly")) {
- disassembler->dumpDisassembly(std::cout);
- }
- return 0;
- }
- // Control flow analysis
- auto cf = std::make_unique<ControlFlow>(insts, *engine);
- cf->createGroups();
- Graph g = cf->analyze();
- if (vm.count("dump-graph")) {
- std::streambuf *buf;
- std::ofstream of;
- if (vm["dump-graph"].as<std::string>() != "") {
- of.open(vm["dump-graph"].as<std::string>().c_str());
- buf = of.rdbuf();
- } else {
- buf = std::cout.rdbuf();
- }
- std::ostream out(buf);
- boost::write_graphviz(out, g, boost::make_label_writer(get(boost::vertex_name, g)), boost::makeArrowheadWriter(get(boost::edge_attribute, g)), GraphProperties(engine.get(), g));
- }
- if (!engine->supportsCodeGen() || vm.count("only-graph")) {
- if (!vm.count("dump-graph")) {
- boost::write_graphviz(std::cout, g, boost::make_label_writer(get(boost::vertex_name, g)), boost::makeArrowheadWriter(get(boost::edge_attribute, g)), GraphProperties(engine.get(), g));
- }
- return 0;
- }
- // Post-processing of CFG
- engine->PostCFG(insts, g);
- // Code generation
- auto cg = engine->GetCodeGenerator(insts, std::cout);
- cg->generate(insts, g);
- if (vm.count("show-unreachable")) {
- std::vector<GroupPtr> unreachable;
- VertexRange vr = boost::vertices(g);
- for (VertexIterator v = vr.first; v != vr.second; ++v)
- {
- GroupPtr gr = boost::get(boost::vertex_name, g, *v);
- if (gr->_stackLevel == -1)
- unreachable.push_back(gr);
- }
- if (!unreachable.empty()) {
- for (size_t i = 0; i < unreachable.size(); i++) {
- if (i == 0) {
- if (unreachable.size() == 1)
- std::cout << boost::format("\n%d unreachable group detected.\n") % unreachable.size();
- else
- std::cout << boost::format("\n%d unreachable groups detected.\n") % unreachable.size();
- }
- std::cout << "Group " << (i + 1) << ":\n";
- ConstInstIterator inst = unreachable[i]->_start;
- do {
- std::cout << *inst;
- } while (inst++ != unreachable[i]->_end);
- std::cout << "----------\n";
- }
- }
- }
- } catch (UnknownOpcodeException &e) {
- std::cerr << "ERROR: " << e.what() << "\n";
- return 3;
- } catch (std::exception &e) {
- std::cerr << "ERROR: " << e.what() << "\n";
- return 4;
- }
- return 0;
- }
|