int main(int iArgC, char *cArgV[]) { #ifdef __GLIBCXX__ // Set a better exception handler std::set_terminate(__gnu_cxx::__verbose_terminate_handler); #endif // Disable stdin/printf/etc. sync for a speed boost std::ios_base::sync_with_stdio(false); // Declare the supported options. po::options_description poOptions("Options"); poOptions.add_options() ("type,t", po::value<std::string>(), "specify the output file format") ; po::options_description poHidden("Hidden parameters"); poHidden.add_options() ("in", ".png to read") ("out", "output palette filename") ("help", "produce help message") ; po::options_description poVisible(""); poVisible.add(poOptions); po::options_description poComplete("Parameters"); poComplete.add(poOptions).add(poHidden); po::variables_map mpArgs; std::string srcFile, dstFile; std::string strType; try { po::parsed_options pa = po::parse_command_line(iArgC, cArgV, poComplete); // Parse the global command line options for (std::vector<po::option>::iterator i = pa.options.begin(); i != pa.options.end(); i++) { if (i->string_key.empty()) { // If we've already got an archive filename, complain that a second one // was given (probably a typo.) if (!dstFile.empty()) { std::cerr << "Error: unexpected extra parameter (multiple output " "filenames given?!)" << std::endl; return 1; } assert(i->value.size() > 0); // can't have no values with no name! if (srcFile.empty()) srcFile = i->value[0]; else dstFile = i->value[0]; } else if (i->string_key.compare("help") == 0) { std::cout << "Copyright (C) 2010 Adam Nielsen <*****@*****.**>\n" "This program comes with ABSOLUTELY NO WARRANTY. This is free software,\n" "and you are welcome to change and redistribute it under certain conditions;\n" "see <http://www.gnu.org/licenses/> for details.\n" "\n" "Utility to manipulate image palettes used by games.\n" "Build date " __DATE__ " " __TIME__ << "\n" "\n" "Usage: " PROGNAME " <input>.png -t <output-type> <output-filename>\n" << poVisible << "\n" << std::endl; return 0; } else if ( (i->string_key.compare("t") == 0) || (i->string_key.compare("type") == 0) ) { if (i->value.size() == 0) { std::cerr << PROGNAME ": --type (-t) requires a parameter." << std::endl; return 1; } strType = i->value[0]; } } if (srcFile.empty()) { std::cerr << "Error: no input .png filename given" << std::endl; return 1; } if (dstFile.empty()) { std::cerr << "Error: no output filename given" << std::endl; return 1; } if (strType.empty()) { std::cerr << "Error: no output format type (-t) given" << std::endl; return 1; } // Get the format handler for this file format gg::ManagerPtr pManager(gg::getManager()); gg::ImageTypePtr palType(pManager->getImageTypeByCode(strType)); if (!palType) { std::cerr << "Unknown file type given to -t/--type: " << strType << std::endl; return 1; } std::cout << "Extracting palette from " << srcFile << " to " << dstFile << " as type " << strType << std::endl; png::image<png::index_pixel> png( srcFile, png::require_color_space<png::index_pixel>() ); stream::file_sptr outStream(new stream::file()); try { outStream->create(dstFile.c_str()); } catch (const stream::open_error& e) { std::cerr << PROGNAME ": Unable to create " << dstFile << ": " << e.what() << std::endl; return 2; } camoto::SuppData dummy; gg::ImagePtr palOut = palType->create(outStream, dummy); gg::PaletteTablePtr pal(new gg::PaletteTable()); png::palette pngPal = png.get_palette(); for (png::palette::iterator i = pngPal.begin(); i != pngPal.end(); i++) { gg::PaletteEntry p; p.red = i->red; p.green = i->green; p.blue = i->blue; pal->push_back(p); } palOut->setPalette(pal); } catch (const po::unknown_option& e) { std::cerr << PROGNAME ": " << e.what() << ". Use --help for help." << std::endl; return 1; } catch (const po::invalid_command_line_syntax& e) { std::cerr << PROGNAME ": " << e.what() << ". Use --help for help." << std::endl; return 1; } return 0; }
int main(int iArgC, char *cArgV[]) { // Set a better exception handler std::set_terminate( __gnu_cxx::__verbose_terminate_handler ); // Disable stdin/printf/etc. sync for a speed boost std::ios_base::sync_with_stdio(false); // Declare the supported options. po::options_description poActions("Actions"); poActions.add_options() ("list,l", "list contents of the address book") ("update,u", po::value<std::string>(), "select an address book entry to change by ID") ("add,a", po::value<std::string>(), "add a new address book entry and select it") ("set,s", po::value<std::string>(), "field:value parameter updates entry selected by earlier -u or -a") ; po::options_description poOptions("Options"); poOptions.add_options() ("type,t", po::value<std::string>(), "specify the MFD type (default is autodetect)") ("user,u", po::value<std::string>(), "username to log in as") ("pass,p", po::value<std::string>(), "password") ; po::options_description poHidden("Hidden parameters"); poHidden.add_options() ("host", "MFD hostname") ("help", "produce help message") ; po::options_description poVisible(""); poVisible.add(poActions).add(poOptions); po::options_description poComplete("Parameters"); poComplete.add(poActions).add(poOptions).add(poHidden); po::variables_map mpArgs; std::string host, user, pass, type; try { po::parsed_options pa = po::parse_command_line(iArgC, cArgV, poComplete); // Parse the global command line options for (std::vector<po::option>::iterator i = pa.options.begin(); i != pa.options.end(); i++) { if (i->string_key.empty()) { // If we've already got an archive filename, complain that a second one // was given (probably a typo.) if (!host.empty()) { std::cerr << "Error: unexpected extra parameter (multiple MFDs " "given?!)" << std::endl; return 1; } assert(i->value.size() > 0); // can't have no values with no name! host = i->value[0]; } else if (i->string_key.compare("help") == 0) { std::cout << "Copyright (C) 2010 Adam Nielsen <*****@*****.**>\n" "This program comes with ABSOLUTELY NO WARRANTY. This is free software,\n" "and you are welcome to change and redistribute it under certain conditions;\n" "see <http://www.gnu.org/licenses/> for details.\n" "\n" "Utility to remotely configure multi-function devices (networked photocopiers.)\n" "Build date " __DATE__ " " __TIME__ << "\n" "\n" "Usage: mfdmgr <hostname> <action> [action...]\n" << poVisible << "\n" << std::endl; return RET_OK; } else if ( (i->string_key.compare("t") == 0) || (i->string_key.compare("type") == 0) ) { if (i->value.size() == 0) { std::cerr << PROGNAME ": --type (-t) requires a parameter." << std::endl; return RET_BADARGS; } type = i->value[0]; } else if ( (i->string_key.compare("u") == 0) || (i->string_key.compare("user") == 0) ) { if (i->value.size() == 0) { std::cerr << PROGNAME ": --user (-u) requires a parameter." << std::endl; return RET_BADARGS; } user = i->value[0]; } else if ( (i->string_key.compare("p") == 0) || (i->string_key.compare("pass") == 0) ) { if (i->value.size() == 0) { std::cerr << PROGNAME ": --pass (-p) requires a parameter." << std::endl; return RET_BADARGS; } pass = i->value[0]; } } if (host.empty()) { std::cerr << "Error: no hostname given" << std::endl; return RET_BADARGS; } std::cout << "Opening " << host << " as type " << (type.empty() ? "<autodetect>" : type) << std::endl; // Get the format handler for this file format boost::shared_ptr<mfd::Manager> pManager(mfd::getManager()); mfd::DeviceTypePtr pDeviceType; if (type.empty()) { // Need to autodetect the file format. mfd::DeviceTypePtr pTestType; int i = 0; while ((pTestType = pManager->getDeviceType(i++))) { mfd::E_CERTAINTY cert = pTestType->isInstance(host); switch (cert) { case mfd::EC_DEFINITELY_NO: // Don't print anything (TODO: Maybe unless verbose?) break; case mfd::EC_DEFINITELY_YES: std::cout << "Device is definitely a " << pTestType->getFriendlyName() << " [" << pTestType->getDeviceCode() << "]" << std::endl; pDeviceType = pTestType; // Don't bother checking any other formats if we got a 100% match goto finishTesting; } } finishTesting: if (!pDeviceType) { std::cerr << "Unable to automatically determine the file type. Use " "the --type option to manually specify the file format." << std::endl; return RET_BE_MORE_SPECIFIC; } } else { mfd::DeviceTypePtr pTestType(pManager->getDeviceTypeByCode(type)); if (!pTestType) { std::cerr << "Unknown device type given to -t/--type: " << type << std::endl; return RET_BADARGS; } pDeviceType = pTestType; } assert(pDeviceType != NULL); // Connect to the device boost::shared_ptr<mfd::Device> pDevice(pDeviceType->open(host, user, pass)); assert(pDevice); int iRet = RET_OK; // ID of previously selected address book item std::string idABSelected; std::map<std::string, std::string> fieldSet; // TODO: enum for field type // Run through the actions on the command line for (std::vector<po::option>::iterator i = pa.options.begin(); i != pa.options.end(); i++) { if (i->string_key.compare("list") == 0) { boost::shared_ptr<mfd::AddressBook> ab = pDevice->getAddressBook(); if (!ab) { std::cerr << "This device type does not have an address book." << std::endl; iRet = RET_BADARGS; continue; } const mfd::AddressBook::VC_ENTRYID& entryIds = ab->getEntryIds(); for (mfd::AddressBook::VC_ENTRYID::const_iterator i = entryIds.begin(); i != entryIds.end(); i++ ) { std::cout << "id is " << *i << std::endl; } mfd::AddressBook::VC_FIELDLIST allEntries; ab->getEntries(entryIds, allEntries); for (mfd::AddressBook::VC_FIELDLIST::iterator i = allEntries.begin(); i != allEntries.end(); i++ ) { mfd::AddressBook::FieldList& fl = *i; for (mfd::AddressBook::FieldList::iterator j = fl.begin(); j != fl.end(); j++ ) { std::cout << j->first << "=" << j->second << "; "; } std::cout << std::endl; } } else if (i->string_key.compare("update") == 0) { idABSelected = i->value[0]; std::cout << "Selected ID " << idABSelected << " for update" << std::endl; fieldSet.clear(); // empty any fields previously set } else if (i->string_key.compare("set") == 0) { if (idABSelected.empty()) { std::cerr << "ERROR: You must use -u or -a before --set/-s" << std::endl; iRet = RET_BADARGS; break; } idABSelected = i->value[0]; std::string fieldName, fieldVal; bool bAltDest = split(i->value[0], ':', &fieldName, &fieldVal); if (!bAltDest) { std::cerr << "ERROR: --set/-s requires a parameter of the form " "field:value" << std::endl; iRet = RET_BADARGS; break; } std::cout << "Set field \"" << fieldName << "\" to \"" << fieldVal << "\"" << std::endl; // Ignore --type/-t } else if (i->string_key.compare("type") == 0) { } else if (i->string_key.compare("t") == 0) { } } // for (all command line elements) } catch (po::unknown_option& e) { std::cerr << PROGNAME ": " << e.what() << ". Use --help for help." << std::endl; return RET_BADARGS; } catch (po::invalid_command_line_syntax& e) { std::cerr << PROGNAME ": " << e.what() << ". Use --help for help." << std::endl; return RET_BADARGS; } catch (mfd::ECommFailure& e) { std::cerr << PROGNAME ": Communication failure (" << e.what() << ")" << std::endl; return RET_SHOWSTOPPER; } return RET_OK; }
int main(int iArgC, char *cArgV[]) { #ifdef __GLIBCXX__ // Set a better exception handler std::set_terminate(__gnu_cxx::__verbose_terminate_handler); #endif // Disable stdin/printf/etc. sync for a speed boost std::ios_base::sync_with_stdio(false); // Declare the supported options. po::options_description poActions("Actions"); poActions.add_options() ("list,l", "list files in the archive") ("extract-all,X", "extract all files in the archive") ("extract,x", po::value<std::string>(), "extract a specific file") ("add,a", po::value<std::string>(), "add a file at the end of the archive") ("insert,i", po::value<std::string>(), "add a file at a specific point in the archive") ("metadata,m", "list archive attributes/metadata") ("set-metadata,e", po::value<std::string>(), "change archive attributes/metadata") ("overwrite,o", po::value<std::string>(), "replace a file in the archive with new data") ("rename,r", po::value<std::string>(), "rename a file inside the archive") ("delete,d", po::value<std::string>(), "remove a file from the archive") ("uncompressed-size,z", po::value<int>(), "[with -u only] specify the uncompressed size to use with -i") ; po::options_description poOptions("Options"); poOptions.add_options() ("type,t", po::value<std::string>(), "specify the archive type (default is autodetect)") ("list-types", "list available formats that can be passed to --type") ("filetype,y", po::value<std::string>(), "specify the file type when inserting (default is generic file)") ("attribute,b", po::value<std::string>(), "specify the file attributes when inserting (optional)") ("unfiltered,u", "do not filter files (no encrypt/decrypt/compress/decompress)") ("script,s", "format output suitable for script parsing") ("force,f", "force open even if the archive is not in the given format") ("create,c", "create a new archive file instead of opening an existing one") ; po::options_description poHidden("Hidden parameters"); poHidden.add_options() ("archive", "archive file to manipulate") ("help", "produce help message") ; po::options_description poVisible(""); poVisible.add(poActions).add(poOptions); po::options_description poComplete("Parameters"); poComplete.add(poActions).add(poOptions).add(poHidden); po::variables_map mpArgs; std::string strFilename; std::string strType; bool bScript = false; // show output suitable for script parsing? bool bForceOpen = false; // open anyway even if archive not in given format? bool bCreate = false; // create a new archive? try { po::parsed_options pa = po::parse_command_line(iArgC, cArgV, poComplete); // Parse the global command line options for (std::vector<po::option>::iterator i = pa.options.begin(); i != pa.options.end(); i++) { if (i->string_key.empty()) { // If we've already got an archive filename, complain that a second one // was given (probably a typo.) if (!strFilename.empty()) { std::cerr << "Error: unexpected extra parameter (multiple archive " "filenames given?!)" << std::endl; return RET_BADARGS; } assert(i->value.size() > 0); // can't have no values with no name! strFilename = i->value[0]; } else if (i->string_key.compare("help") == 0) { std::cout << "Copyright (C) 2010-2016 Adam Nielsen <*****@*****.**>\n" "This program comes with ABSOLUTELY NO WARRANTY. This is free software,\n" "and you are welcome to change and redistribute it under certain conditions;\n" "see <http://www.gnu.org/licenses/> for details.\n" "\n" "Utility to manipulate archive files used by games to store data files.\n" "Build date " __DATE__ " " __TIME__ << "\n" "\n" "Usage: gamearch <archive> <action> [action...]\n" << poVisible << "\n" << std::endl; return RET_OK; } else if ( (i->string_key.compare("list-types") == 0) ) { for (const auto& i : ga::ArchiveManager::formats()) { std::string code = i->code(); std::cout << code; int len = code.length(); if (len < 20) std::cout << std::string(20 - len, ' '); std::cout << ' ' << i->friendlyName() << '\n'; } return RET_OK; } else if ( (i->string_key.compare("t") == 0) || (i->string_key.compare("type") == 0) ) { if (i->value.size() == 0) { std::cerr << PROGNAME ": --type (-t) requires a parameter. Use " "--list-types to see valid values." << std::endl; return RET_BADARGS; } strType = i->value[0]; } else if ( (i->string_key.compare("s") == 0) || (i->string_key.compare("script") == 0) ) { bScript = true; } else if ( (i->string_key.compare("f") == 0) || (i->string_key.compare("force") == 0) ) { bForceOpen = true; } else if ( (i->string_key.compare("u") == 0) || (i->string_key.compare("unfiltered") == 0) ) { bUseFilters = false; } else if ( (i->string_key.compare("c") == 0) || (i->string_key.compare("create") == 0) ) { bCreate = true; } } if (strFilename.empty()) { std::cerr << "Error: no game archive filename given" << std::endl; return RET_BADARGS; } std::unique_ptr<stream::file> psArchive; if (bCreate && strType.empty()) { std::cerr << "Error: You must specify the --type of archive to create" << std::endl; return RET_BADARGS; } std::cout << (bCreate ? "Creating " : "Opening ") << strFilename << " as type " << (strType.empty() ? "<autodetect>" : strType) << std::endl; try { psArchive = std::make_unique<stream::file>(strFilename, bCreate); } catch (const stream::open_error& e) { std::cerr << "Error " << (bCreate ? "creating" : "opening") << " archive file " << strFilename << ": " << e.what() << std::endl; return RET_SHOWSTOPPER; } // Get the format handler for this file format ga::ArchiveManager::handler_t pArchType; if (strType.empty()) { // Need to autodetect the file format. for (const auto& i : ga::ArchiveManager::formats()) { ga::ArchiveType::Certainty cert = i->isInstance(*psArchive); switch (cert) { case ga::ArchiveType::Certainty::DefinitelyNo: // Don't print anything (TODO: Maybe unless verbose?) break; case ga::ArchiveType::Certainty::Unsure: std::cout << "File could be a " << i->friendlyName() << " [" << i->code() << "]" << std::endl; // If we haven't found a match already, use this one if (!pArchType) pArchType = i; break; case ga::ArchiveType::Certainty::PossiblyYes: std::cout << "File is likely to be a " << i->friendlyName() << " [" << i->code() << "]" << std::endl; // Take this one as it's better than an uncertain match pArchType = i; break; case ga::ArchiveType::Certainty::DefinitelyYes: std::cout << "File is definitely a " << i->friendlyName() << " [" << i->code() << "]" << std::endl; pArchType = i; // Don't bother checking any other formats if we got a 100% match goto finishTesting; } if (cert != ga::ArchiveType::Certainty::DefinitelyNo) { // We got a possible match, see if it requires any suppdata auto suppList = i->getRequiredSupps(*psArchive, strFilename); if (suppList.size() > 0) { // It has suppdata, see if it's present std::cout << " * This format requires supplemental files..." << std::endl; bool bSuppOK = true; for (const auto& s : suppList) { try { stream::file test_presence(s.second, false); } catch (const stream::open_error&) { bSuppOK = false; std::cout << " * Could not find/open " << s.second << ", archive is probably not " << i->code() << std::endl; break; } } if (bSuppOK) { // All supp files opened ok std::cout << " * All supp files present, archive is likely " << i->code() << std::endl; // Set this as the most likely format pArchType = i; } } } } finishTesting: if (!pArchType) { std::cerr << "Unable to automatically determine the file type. Use " "the --type option to manually specify the file format." << std::endl; return RET_BE_MORE_SPECIFIC; } } else { auto pTestType = ga::ArchiveManager::byCode(strType); if (!pTestType) { std::cerr << "Unknown file type given to -t/--type: " << strType << std::endl; return RET_BADARGS; } pArchType = pTestType; } assert(pArchType != NULL); if (!bCreate) { // Check to see if the file is actually in this format if (pArchType->isInstance(*psArchive) == ga::ArchiveType::Certainty::DefinitelyNo) { if (bForceOpen) { std::cerr << "Warning: " << strFilename << " is not a " << pArchType->friendlyName() << ", open forced." << std::endl; } else { std::cerr << "Invalid format: " << strFilename << " is not a " << pArchType->friendlyName() << "\n" << "Use the -f option to try anyway." << std::endl; return RET_BE_MORE_SPECIFIC; } } } // See if the format requires any supplemental files auto suppList = pArchType->getRequiredSupps(*psArchive, strFilename); camoto::SuppData suppData; for (const auto& s : suppList) { try { std::cout << "Opening supplemental file " << s.second << std::endl; auto suppStream = std::make_unique<stream::file>(s.second, false); suppData[s.first] = std::move(suppStream); } catch (const stream::open_error& e) { std::cerr << "Error opening supplemental file " << s.second << ": " << e.what() << std::endl; return RET_SHOWSTOPPER; } } // Open the archive file std::shared_ptr<ga::Archive> pArchive; try { if (bCreate) { pArchive = pArchType->create(std::move(psArchive), suppData); } else { pArchive = pArchType->open(std::move(psArchive), suppData); } assert(pArchive); } catch (const camoto::error& e) { std::cerr << "Error " << (bCreate ? "creating" : "opening") << " archive file: " << e.what() << std::endl; return RET_SHOWSTOPPER; } // File type of inserted files defaults to empty, which means 'generic file' std::string strLastFiletype; // Last attribute value set with -b auto iLastAttr = ga::Archive::File::Attribute::Default; // Last value set with -z stream::len lenReal = 0; // Run through the actions on the command line for (auto& i : pa.options) { if (i.string_key.compare("list") == 0) { listFiles(std::string(), std::string(), *pArchive, bScript); } else if (i.string_key.compare("extract-all") == 0) { extractAll(pArchive, bScript); } else if (i.string_key.compare("metadata") == 0) { listAttributes(pArchive.get(), bScript); } else if (i.string_key.compare("set-metadata") == 0) { std::string strIndex, strValue; if (!split(i.value[0], '=', &strIndex, &strValue)) { std::cerr << PROGNAME ": -e/--set-metadata requires an index and " "a value (e.g. --set-metadata 0=example)" << std::endl; return RET_BADARGS; } unsigned int index = strtoul(strIndex.c_str(), nullptr, 0); setAttribute(pArchive.get(), bScript, index, strValue); } else if (i.string_key.compare("extract") == 0) { std::string strArchFile, strLocalFile; bool bAltDest = split(i.value[0], '=', &strArchFile, &strLocalFile); if (!bAltDest) sanitisePath(strLocalFile); std::cout << " extracting: " << strArchFile; if (strArchFile.compare(strLocalFile)) std::cout << " (into " << strLocalFile << ")"; std::cout << std::flush; try { // Find the file auto destArch = pArchive; ga::Archive::FileHandle id; findFile(&destArch, &id, strArchFile); if (!id) { std::cout << " [failed; file not found]"; iRet = RET_NONCRITICAL_FAILURE; // one or more files failed } else { // Found it, open on disk auto pfsIn = destArch->open(id, bUseFilters); try { auto fsOut = std::make_shared<stream::output_file>(strLocalFile, true); try { stream::copy(*fsOut, *pfsIn); } catch (const stream::error& e) { std::cout << " [failed; read/write error: " << e.what() << "]"; iRet = RET_UNCOMMON_FAILURE; // some files failed, but not in a usual way } } catch (const stream::error&) { std::cout << " [failed; unable to create output file]"; iRet = RET_UNCOMMON_FAILURE; // some files failed, but not in a usual way } } } catch (const stream::error& e) { std::cout << " [failed; " << e.what() << "]"; iRet = RET_UNCOMMON_FAILURE; // some files failed, but not in a usual way } std::cout << std::endl; } else if (i.string_key.compare("delete") == 0) { std::string& strArchFile = i.value[0]; std::cout << " deleting: " << strArchFile << std::flush; try { auto destArch = pArchive; ga::Archive::FileHandle id; findFile(&destArch, &id, strArchFile); if (!id) { std::cout << " [failed; file not found]"; iRet = RET_NONCRITICAL_FAILURE; // one or more files failed } else { destArch->remove(id); } } catch (const stream::error& e) { std::cout << " [failed; " << e.what() << "]"; iRet = RET_UNCOMMON_FAILURE; // some files failed, but not in a usual way } std::cout << std::endl; } else if (i.string_key.compare("insert") == 0) { std::string strSource, strInsertBefore; if (!split(i.value[0], ':', &strSource, &strInsertBefore)) { std::cerr << PROGNAME ": -i/--insert requires a file to insert " "before (parameter should end with \":beforeme.xyz\")\n" "Or use --add instead." << std::endl; return RET_BADARGS; } std::string strArchFile, strLocalFile; bool bAltDest = split(strSource, '=', &strArchFile, &strLocalFile); std::cout << " inserting: " << strArchFile; if (!strLastFiletype.empty()) std::cout << " as type " << strLastFiletype; std::cout << " (before " << strInsertBefore; if (bAltDest) std::cout << ", from " << strLocalFile; std::cout << ")"; if (lenReal != 0) std::cout << ", with uncompressed size " << lenReal; std::cout << std::flush; // Try to find strInsertBefore auto destArch = pArchive; ga::Archive::FileHandle idBeforeThis; findFile(&destArch, &idBeforeThis, strInsertBefore); if (!idBeforeThis) { std::cout << " [failed; could not find " << strInsertBefore << "]"; iRet = RET_NONCRITICAL_FAILURE; // one or more files failed continue; } try { insertFile(destArch, strLocalFile, strArchFile, idBeforeThis, strLastFiletype, iLastAttr, lenReal); } catch (const stream::error& e) { std::cout << " [failed; " << e.what() << "]"; iRet = RET_UNCOMMON_FAILURE; // some files failed, but not in a usual way } std::cout << std::endl; // Remember --filetype/-y } else if (i.string_key.compare("filetype") == 0) { //} else if (i.string_key.compare("y") == 0) { strLastFiletype = i.value[0]; // Remember --attributes/-b } else if (i.string_key.compare("attribute") == 0) { //} else if (i.string_key.compare("b") == 0) { std::string nextAttr = i.value[0]; bool disable = (nextAttr[0] == '-'); if (disable) nextAttr = nextAttr.substr(1); ga::Archive::File::Attribute next; if (nextAttr.compare("empty") == 0) next = ga::Archive::File::Attribute::Vacant; else if (nextAttr.compare("hidden") == 0) next = ga::Archive::File::Attribute::Hidden; else if (nextAttr.compare("compressed") == 0) next = ga::Archive::File::Attribute::Compressed; else if (nextAttr.compare("encrypted") == 0) next = ga::Archive::File::Attribute::Encrypted; else { std::cerr << "Unknown attribute " << nextAttr << ", valid values are: empty hidden compressed encrypted" << std::endl; iRet = RET_UNCOMMON_FAILURE; next = ga::Archive::File::Attribute::Default; } if (next != ga::Archive::File::Attribute::Default) { auto allowed = pArchive->getSupportedAttributes(); if (allowed & next) { if (disable) iLastAttr &= ~next; else iLastAttr |= next; } else { std::cerr << "Warning: Attribute unsupported by archive format, " "ignoring: " << nextAttr << std::endl; } } // Remember --uncompressed-size/-z } else if (i.string_key.compare("uncompressed-size") == 0) { //} else if (i.string_key.compare("z") == 0) { if (bUseFilters) { std::cerr << PROGNAME ": -z/--uncompressed-size only needs to be " "specified when it can't be determined automatically (i.e. when " "-u/--unfiltered is in use.)" << std::endl; return RET_BADARGS; } lenReal = strtoul(i.value[0].c_str(), NULL, 0); // Ignore --type/-t } else if (i.string_key.compare("type") == 0) { } else if (i.string_key.compare("t") == 0) { // Ignore --script/-s } else if (i.string_key.compare("script") == 0) { } else if (i.string_key.compare("s") == 0) { // Ignore --force/-f } else if (i.string_key.compare("force") == 0) { } else if (i.string_key.compare("f") == 0) { } else if ((!i.string_key.empty()) && (i.value.size() > 0)) { // None of the above (single param) options matched, so it's probably // an option with up to two filenames (with an equal-sign as a // separator.) It could also be the --type option, which we'll ignore. std::string& strParam = i.value[0]; std::string strArchFile, strLocalFile; bool bAltDest = split(strParam, '=', &strArchFile, &strLocalFile); if (i.string_key.compare("add") == 0) { std::cout << " adding: " << strArchFile; if (!strLastFiletype.empty()) std::cout << " as type " << strLastFiletype; if (bAltDest) std::cout << " (from " << strLocalFile << ")"; if (lenReal != 0) std::cout << ", with uncompressed size set to " << lenReal; std::cout << std::endl; try { insertFile(pArchive, strLocalFile, strArchFile, nullptr, strLastFiletype, iLastAttr, lenReal); } catch (const stream::error& e) { std::cout << " [failed; " << e.what() << "]"; iRet = RET_UNCOMMON_FAILURE; // some files failed, but not in a usual way } } else if (i.string_key.compare("rename") == 0) { if ((!bAltDest) || (boost::equals(strArchFile, strLocalFile))) { std::cout << "ignoring attempt to rename " << strArchFile << " into the same name" << std::endl; } else { std::cout << " renaming: " << strArchFile << " to " << strLocalFile << std::flush; try { auto destArch = pArchive; ga::Archive::FileHandle id; findFile(&destArch, &id, strArchFile); if (!id) { std::cout << " [failed; file not found inside archive]"; iRet = RET_NONCRITICAL_FAILURE; // one or more files failed } else { destArch->rename(id, strLocalFile); } } catch (const stream::error& e) { std::cout << " [failed; " << e.what() << "]"; iRet = RET_UNCOMMON_FAILURE; // some files failed, but not in a common way } std::cout << std::endl; } } else if (i.string_key.compare("overwrite") == 0) { std::cout << "overwriting: " << strArchFile; if (bAltDest) std::cout << " (from " << strLocalFile << ")"; if (lenReal != 0) std::cout << ", with uncompressed size set to " << lenReal; std::cout << std::flush; try { // Find the file auto destArch = pArchive; ga::Archive::FileHandle id; findFile(&destArch, &id, strArchFile); if (!id) { std::cout << " [failed; file not found inside archive]"; iRet = RET_NONCRITICAL_FAILURE; // one or more files failed } else { // Found it, open replacement file auto sSrc = std::make_shared<stream::input_file>(strLocalFile); stream::len lenSource = sSrc->size(); // Note that we are opening the file into an output_sptr (instead // of an inout_sptr) as this is more efficient. By foregoing read // access to the file, it means a compressed file won't be // decompressed in case we want to read it. Which we don't, // because we're about to completely overwrite it. auto psDest = destArch->open(id, bUseFilters); // Set the size of the stream within the archive, so it exactly // holds the data we want to write. psDest->truncate(lenSource); if (!bUseFilters) { if (lenReal) { pArchive->resize(id, lenSource, lenReal); } else { // Leave the prefiltered/decompressed size unchanged pArchive->resize(id, lenSource, id->realSize); } } psDest->seekp(0, stream::start); stream::copy(*psDest, *sSrc); psDest->flush(); } } catch (const stream::open_error&) { std::cout << " [failed; unable to open replacement file]"; iRet = RET_NONCRITICAL_FAILURE; // one or more files failed } catch (const stream::error& e) { std::cout << " [failed; " << e.what() << "]"; iRet = RET_UNCOMMON_FAILURE; // some files failed, but not in a common way } std::cout << std::endl; } // else it's the archive filename, but we already have that } } // for (all command line elements) pArchive->flush(); } catch (const po::unknown_option& e) { std::cerr << PROGNAME ": " << e.what() << ". Use --help for help." << std::endl; return RET_BADARGS; } catch (const po::invalid_command_line_syntax& e) { std::cerr << PROGNAME ": " << e.what() << ". Use --help for help." << std::endl; return RET_BADARGS; } return iRet; }
int main(int iArgC, char *cArgV[]) { #ifdef __GLIBCXX__ // Set a better exception handler std::set_terminate(__gnu_cxx::__verbose_terminate_handler); #endif // Disable stdin/printf/etc. sync for a speed boost std::ios_base::sync_with_stdio(false); // Declare the supported options. po::options_description poActions("Actions"); poActions.add_options() ("info,i", "display information about the map, including attributes/metadata") ("print,p", po::value<int>(), "print the given layer in ASCII") ("render,r", po::value<std::string>(), "render the map to the given .png file") ; po::options_description poOptions("Options"); poOptions.add_options() ("type,t", po::value<std::string>(), "specify the map type (default is autodetect)") ("graphics,g", po::value<std::string>(), "filename storing game graphics (required with --render)") ("script,s", "format output suitable for script parsing") ("force,f", "force open even if the map is not in the given format") ("list-types", "list supported file types") ; po::options_description poHidden("Hidden parameters"); poHidden.add_options() ("map", "map file to manipulate") ("help", "produce help message") ; po::options_description poVisible(""); poVisible.add(poActions).add(poOptions); po::options_description poComplete("Parameters"); poComplete.add(poActions).add(poOptions).add(poHidden); po::variables_map mpArgs; std::string strFilename, strType; std::map<gm::ImagePurpose, gm::Map::GraphicsFilename> manualGfx; bool bScript = false; // show output suitable for script parsing? bool bForceOpen = false; // open anyway even if map not in given format? int iRet = RET_OK; try { po::parsed_options pa = po::parse_command_line(iArgC, cArgV, poComplete); // Parse the global command line options for (auto& i : pa.options) { if (i.string_key.empty()) { // If we've already got an map filename, complain that a second one // was given (probably a typo.) if (!strFilename.empty()) { std::cerr << "Error: unexpected extra parameter (multiple map " "filenames given?!)" << std::endl; return 1; } assert(i.value.size() > 0); // can't have no values with no name! strFilename = i.value[0]; } else if (i.string_key.compare("help") == 0) { std::cout << "Copyright (C) 2010-2015 Adam Nielsen <*****@*****.**>\n" "This program comes with ABSOLUTELY NO WARRANTY. This is free software,\n" "and you are welcome to change and redistribute it under certain conditions;\n" "see <http://www.gnu.org/licenses/> for details.\n" "\n" "Utility to manipulate map files used by games to store data files.\n" "Build date " __DATE__ " " __TIME__ << "\n" "\n" "Usage: gamemap <map> <action> [action...]\n" << poVisible << "\n" << std::endl; return RET_OK; } else if ( (i.string_key.compare("t") == 0) || (i.string_key.compare("type") == 0) ) { strType = i.value[0]; } else if ( (i.string_key.compare("g") == 0) || (i.string_key.compare("graphics") == 0) ) { std::string purpose, temp; gm::Map::GraphicsFilename gf; bool a = split(i.value[0], '=', &purpose, &temp); bool b = split(temp, ':', &gf.type, &gf.filename); if (!a || !b) { std::cerr << "Malformed -g/--graphics parameter. Must be of the " "form purpose=type:filename.\n" "Use --help or --list-types for details." << std::endl; return RET_BADARGS; } bool found = false; for (unsigned int i = 0; i < (unsigned int)gm::ImagePurpose::ImagePurposeCount; i++) { gm::ImagePurpose p = (gm::ImagePurpose)i; if (purpose.compare(toString(p)) == 0) { manualGfx[p] = gf; found = true; } } if (!found) { std::cerr << "No match for tileset purpose: " << purpose << "\n" << "Use --list-types for details." << std::endl; return RET_BADARGS; } } else if ( (i.string_key.compare("s") == 0) || (i.string_key.compare("script") == 0) ) { bScript = true; } else if ( (i.string_key.compare("f") == 0) || (i.string_key.compare("force") == 0) ) { bForceOpen = true; } else if ( (i.string_key.compare("list-types") == 0) ) { std::cout << "Tileset purposes: (--graphics purpose=type:file)\n"; for (unsigned int i = 0; i < (unsigned int)gm::ImagePurpose::ImagePurposeCount; i++) { gm::ImagePurpose p = (gm::ImagePurpose)i; std::cout << " " << toString(p) << "\n"; } std::cout << "\nTileset types: (--graphics purpose=type:file)\n"; for (auto& tilesetType : gg::TilesetManager::formats()) { std::string code = tilesetType->code(); std::cout << " " << code; int len = code.length(); if (len < 20) std::cout << std::string(20-code.length(), ' '); std::cout << ' ' << tilesetType->friendlyName(); auto ext = tilesetType->fileExtensions(); if (ext.size()) { auto i = ext.begin(); std::cout << " (*." << *i; for (i++; i != ext.end(); i++) { std::cout << "; *." << *i; } std::cout << ")"; } std::cout << '\n'; } std::cout << "\nMap types: (--type)\n"; for (auto& mapType : gm::MapManager::formats()) { std::string code = mapType->code(); std::cout << " " << code; int len = code.length(); if (len < 20) std::cout << std::string(20 - code.length(), ' '); std::cout << ' ' << mapType->friendlyName(); auto ext = mapType->fileExtensions(); if (ext.size()) { auto i = ext.begin(); std::cout << " (*." << *i; for (i++; i != ext.end(); i++) { std::cout << "; *." << *i; } std::cout << ")"; } std::cout << '\n'; } return RET_OK; } } if (strFilename.empty()) { std::cerr << "Error: no game map filename given" << std::endl; return RET_BADARGS; } std::cout << "Opening " << strFilename << " as type " << (strType.empty() ? "<autodetect>" : strType) << std::endl; std::unique_ptr<stream::inout> content; try { content = std::make_unique<stream::file>(strFilename, false); } catch (const stream::open_error& e) { std::cerr << "Error opening " << strFilename << ": " << e.what() << std::endl; return RET_SHOWSTOPPER; } gm::MapManager::handler_t mapType; if (strType.empty()) { // Need to autodetect the file format. for (auto& mapTestType : gm::MapManager::formats()) { gm::MapType::Certainty cert = mapTestType->isInstance(*content); switch (cert) { case gm::MapType::Certainty::DefinitelyNo: // Don't print anything (TODO: Maybe unless verbose?) break; case gm::MapType::Certainty::Unsure: std::cout << "File could be a " << mapTestType->friendlyName() << " [" << mapTestType->code() << "]" << std::endl; // If we haven't found a match already, use this one if (!mapType) mapType = mapTestType; break; case gm::MapType::Certainty::PossiblyYes: std::cout << "File is likely to be a " << mapTestType->friendlyName() << " [" << mapTestType->code() << "]" << std::endl; // Take this one as it's better than an uncertain match mapType = mapTestType; break; case gm::MapType::Certainty::DefinitelyYes: std::cout << "File is definitely a " << mapTestType->friendlyName() << " [" << mapTestType->code() << "]" << std::endl; mapType = mapTestType; // Don't bother checking any other formats if we got a 100% match goto finishTesting; } if (cert != gm::MapType::Certainty::DefinitelyNo) { // We got a possible match, see if it requires any suppdata auto suppList = mapTestType->getRequiredSupps(*content, strFilename); if (suppList.size() > 0) { // It has suppdata, see if it's present std::cout << " * This format requires supplemental files..." << std::endl; bool bSuppOK = true; for (auto& i : suppList) { try { auto suppStream = std::make_unique<stream::file>(i.second, false); } catch (const stream::open_error&) { bSuppOK = false; std::cout << " * Could not find/open " << i.second << ", map is probably not " << mapTestType->code() << std::endl; break; } } if (bSuppOK) { // All supp files opened ok std::cout << " * All supp files present, map is likely " << mapTestType->code() << std::endl; // Set this as the most likely format mapType = mapTestType; } } } } finishTesting: if (!mapType) { std::cerr << "Unable to automatically determine the file type. Use " "the --type option to manually specify the file format." << std::endl; return RET_BE_MORE_SPECIFIC; } } else { mapType = gm::MapManager::byCode(strType); if (!mapType) { std::cerr << "Unknown file type given to -t/--type: " << strType << std::endl; return RET_BADARGS; } } assert(mapType != NULL); // Check to see if the file is actually in this format if (!mapType->isInstance(*content)) { if (bForceOpen) { std::cerr << "Warning: " << strFilename << " is not a " << mapType->friendlyName() << ", open forced." << std::endl; } else { std::cerr << "Invalid format: " << strFilename << " is not a " << mapType->friendlyName() << "\n" << "Use the -f option to try anyway." << std::endl; return RET_BE_MORE_SPECIFIC; } } // See if the format requires any supplemental files camoto::SuppData suppData; for (auto& i : mapType->getRequiredSupps(*content, strFilename)) { try { std::cerr << "Opening supplemental file " << i.second << std::endl; suppData[i.first] = std::make_unique<stream::file>(i.second, false); } catch (const stream::open_error& e) { std::cerr << "Error opening supplemental file " << i.second << ": " << e.what() << std::endl; // Continue anyway in case the file is optional } } // Open the map file std::shared_ptr<gm::Map> pMap = mapType->open(std::move(content), suppData); assert(pMap); // File type of inserted files defaults to empty, which means 'generic file' std::string strLastFiletype; // Run through the actions on the command line for (auto& i : pa.options) { if (i.string_key.compare("info") == 0) { listAttributes(pMap.get(), bScript); std::cout << (bScript ? "gfx_filename_count=" : "Number of graphics filenames: ") << pMap->graphicsFilenames().size() << "\n"; int fileNum = 0; for (auto& a : pMap->graphicsFilenames()) { if (bScript) { std::cout << "gfx_file" << fileNum << "_name=" << a.second.filename << "\n"; std::cout << "gfx_file" << fileNum << "_type=" << a.second.type << "\n"; std::cout << "gfx_file" << fileNum << "_purpose=" << (unsigned int)a.first << "\n"; } else { std::cout << "Graphics file " << fileNum+1 << ": " << a.second.filename << " ["; switch (a.first) { case gm::ImagePurpose::GenericTileset1: std::cout << "Generic tileset 1"; break; case gm::ImagePurpose::BackgroundImage: std::cout << "Background image"; break; case gm::ImagePurpose::BackgroundTileset1: std::cout << "Background tileset 1"; break; case gm::ImagePurpose::BackgroundTileset2: std::cout << "Background tileset 2"; break; case gm::ImagePurpose::ForegroundTileset1: std::cout << "Foreground tileset 1"; break; case gm::ImagePurpose::ForegroundTileset2: std::cout << "Foreground tileset 2"; break; case gm::ImagePurpose::SpriteTileset1: std::cout << "Sprite tileset 1"; break; case gm::ImagePurpose::FontTileset1: std::cout << "Font tileset 1"; break; case gm::ImagePurpose::FontTileset2: std::cout << "Font tileset 2"; break; default: std::cout << "Unknown purpose <fix this>"; break; } std::cout << " of type " << a.second.type << "]\n"; } fileNum++; } std::cout << (bScript ? "map_type=" : "Map type: "); auto map2d = std::dynamic_pointer_cast<gm::Map2D>(pMap); if (map2d) { std::cout << (bScript ? "2d" : "2D grid-based") << "\n"; #define CAP(o, c, v) " " __STRING(c) << ((v & o::Caps::c) ? '+' : '-') #define MAP2D_CAP(c) CAP(gm::Map2D, c, mapCaps) #define MAP2D_LAYER_CAP(c) CAP(gm::Map2D::Layer, c, layerCaps) auto mapCaps = map2d->caps(); if (bScript) { std::cout << "map_caps=" << (unsigned int)mapCaps << "\n"; } else { std::cout << "Map capabilities:" << MAP2D_CAP(HasViewport) << MAP2D_CAP(HasMapSize) << MAP2D_CAP(SetMapSize) << MAP2D_CAP(HasTileSize) << MAP2D_CAP(SetTileSize) << MAP2D_CAP(AddPaths) << "\n" ; } auto mapTileSize = map2d->tileSize(); std::cout << (bScript ? "tile_width=" : "Tile size: ") << mapTileSize.x << (bScript ? "\ntile_height=" : "x") << mapTileSize.y << "\n"; auto mapSize = map2d->mapSize(); std::cout << (bScript ? "map_width=" : "Map size: ") << mapSize.x << (bScript ? "\nmap_height=" : "x") << mapSize.y << (bScript ? "" : " tiles") << "\n"; if (mapCaps & gm::Map2D::Caps::HasViewport) { auto vp = map2d->viewport(); std::cout << (bScript ? "viewport_width=" : "Viewport size: ") << vp.x << (bScript ? "\nviewport_height=" : "x") << vp.y << (bScript ? "" : " pixels") << "\n"; } unsigned int layerCount = map2d->layers().size(); std::cout << (bScript ? "layercount=" : "Layer count: ") << layerCount << "\n"; unsigned int layerIndex = 0; for (auto& layer : map2d->layers()) { std::string prefix; if (bScript) { std::stringstream ss; ss << "layer" << layerIndex << '_'; prefix = ss.str(); std::cout << prefix << "name=" << layer->title() << "\n"; } else { prefix = " "; std::cout << "Layer " << layerIndex + 1 << ": \"" << layer->title() << "\"\n"; } auto layerCaps = layer->caps(); if (bScript) std::cout << prefix << "caps=" << (unsigned int)layerCaps << "\n"; else std::cout << prefix << "Capabilities:" << MAP2D_LAYER_CAP(HasOwnSize) << MAP2D_LAYER_CAP(SetOwnSize) << MAP2D_LAYER_CAP(HasOwnTileSize) << MAP2D_LAYER_CAP(SetOwnTileSize) << MAP2D_LAYER_CAP(HasPalette) << MAP2D_LAYER_CAP(UseImageDims) << "\n" ; gg::Point layerTileSize; bool layerTileSame; if (layerCaps & gm::Map2D::Layer::Caps::HasOwnTileSize) { layerTileSize = layer->tileSize(); layerTileSame = false; } else { layerTileSize = mapTileSize; layerTileSame = true; } std::cout << prefix << (bScript ? "tile_width=" : "Tile size: ") << layerTileSize.x; if (bScript) std::cout << "\n" << prefix << "tile_height="; else std::cout << "x"; std::cout << layerTileSize.y; if (layerTileSame && (!bScript)) { std::cout << " (same as map)"; } std::cout << "\n"; gg::Point layerSize; bool layerSame; if (layerCaps & gm::Map2D::Layer::Caps::HasOwnSize) { layerSize = layer->layerSize(); layerSame = false; } else { // Convert from map tilesize to layer tilesize, leaving final // pixel dimensions unchanged layerSize.x = mapSize.x * mapTileSize.x / layerTileSize.x; layerSize.y = mapSize.y * mapTileSize.y / layerTileSize.y; layerSame = true; } std::cout << prefix << (bScript ? "width=" : "Layer size: ") << layerSize.x; if (bScript) std::cout << "\n" << prefix << "height="; else std::cout << "x"; std::cout << layerSize.y; if (layerSame && (!bScript)) { std::cout << " (same as map)"; } std::cout << "\n"; layerIndex++; } } else { std::cout << (bScript ? "unknown" : "Unknown! Fix this!") << "\n"; } } else if (i.string_key.compare("print") == 0) { auto map2d = std::dynamic_pointer_cast<gm::Map2D>(pMap); if (map2d) { unsigned int targetLayer = strtoul(i.value[0].c_str(), NULL, 10); if (targetLayer == 0) { std::cerr << "Invalid layer index passed to --print. Use --info " "to list layers in this map." << std::endl; iRet = RET_BADARGS; continue; } if (targetLayer > map2d->layers().size()) { std::cerr << "Invalid layer index passed to --print. Use --info " "to list layers in this map." << std::endl; iRet = RET_BADARGS; continue; } auto layer = map2d->layers().at(targetLayer - 1); // If this fails, the map format returned a null pointer for the layer assert(layer); // Figure out the layer size gg::Point layerSize, tileSize; getLayerDims(*map2d, *layer, &layerSize, &tileSize); auto items = layer->items(); auto t = items.begin(); unsigned int numItems = items.size(); if (t != items.end()) { for (unsigned int y = 0; y < layerSize.y; y++) { for (unsigned int x = 0; x < layerSize.x; x++) { for (unsigned int i = 0; i < numItems; i++) { if ((t->pos.x == x) && (t->pos.y == y)) break; t++; if (t == items.end()) t = items.begin(); } if ((t->pos.x != x) || (t->pos.y != y)) { // Grid position with no tile! std::cout << " "; } else { std::cout << std::hex << std::setw(4) << (unsigned int)t->code << ' '; } } std::cout << "\n"; } } else { std::cout << "Layer is empty!" << std::endl; } } else { std::cerr << "Support for printing this map type has not yet " "been implemented!" << std::endl; } } else if (i.string_key.compare("render") == 0) { // Don't need to check i.value[0], program_options does that for us auto map2d = std::dynamic_pointer_cast<gm::Map2D>(pMap); if (map2d) { gm::TilesetCollection allTilesets; for (auto& a : manualGfx) { if (!bScript) { std::cout << "Loading " << a.second.type << " from " << a.second.filename << std::endl; } allTilesets[a.first] = openTileset(a.second.filename, a.second.type); } for (auto& a : pMap->graphicsFilenames()) { if (allTilesets.find(a.first) == allTilesets.end()) { if (a.second.filename.empty()) { std::cerr << toString(a.first) << " is required, and must " "be specified manually with --graphics." << std::endl; iRet = RET_BADARGS; } else { // This tileset hasn't been specified on the command line, but the // map format handler has given us a filename, so open the file // suggested from the map. allTilesets[a.first] = openTileset(a.second.filename, a.second.type); } } else { if (!a.second.filename.empty()) { std::cout << toString(a.first) << " overridden on command-line\n"; } } } if (allTilesets.empty()) { std::cerr << "No tilesets were loaded, map cannot be rendered. " "Use --graphics to specify a tileset." << std::endl; iRet = RET_BADARGS; } else { map2dToPng(*map2d, allTilesets, i.value[0]); } } else { std::cerr << PROGNAME ": Rendering this type of map is not yet " "implemented." << std::endl; return RET_SHOWSTOPPER; } // Ignore --type/-t } else if (i.string_key.compare("type") == 0) { } else if (i.string_key.compare("t") == 0) { // Ignore --script/-s } else if (i.string_key.compare("script") == 0) { } else if (i.string_key.compare("s") == 0) { // Ignore --force/-f } else if (i.string_key.compare("force") == 0) { } else if (i.string_key.compare("f") == 0) { } } // for (all command line elements) //pMap->flush(); } catch (const po::error& e) { std::cerr << PROGNAME ": " << e.what() << " Use --help for help." << std::endl; return RET_BADARGS; } catch (const stream::error& e) { std::cerr << PROGNAME ": " << e.what() << " Use --help for help." << std::endl; return RET_SHOWSTOPPER; } return iRet; }
int main(int iArgC, char *cArgV[]) { #ifdef __GLIBCXX__ // Set a better exception handler std::set_terminate(__gnu_cxx::__verbose_terminate_handler); #endif #ifdef WIN32 // Change stdout to be binary, so writing \x0A does not get changed to \x0D\x0A _setmode(0, _O_BINARY); _setmode(1, _O_BINARY); _setmode(2, _O_BINARY); #endif // Disable stdin/printf/etc. sync for a speed boost std::ios_base::sync_with_stdio(false); // Declare the supported options. po::options_description poOptions("Options"); poOptions.add_options() ("apply,a", "apply the filter instead (compress/encrypt the input data) rather than " "the default of reversing the filter (to decompress/decrypt)") ("list,l", "list all filters") ("type,t", po::value<std::string>(), "specify the filter type") ; po::options_description poHidden("Hidden parameters"); poHidden.add_options() ("help", "produce help message") ; po::options_description poVisible(""); poVisible.add(poOptions); po::options_description poComplete("Parameters"); poComplete.add(poOptions).add(poHidden); po::variables_map mpArgs; auto pstdout = stream::open_stdout(); ga::FilterManager::handler_t pFilterType; bool bApply = false; // default is to reverse the algorithm (decompress) try { po::parsed_options pa = po::parse_command_line(iArgC, cArgV, poComplete); // Parse the global command line options for (std::vector<po::option>::iterator i = pa.options.begin(); i != pa.options.end(); i++) { if (i->string_key.empty()) { std::cerr << "Error: unexpected extra parameter (you can't list " "filenames on the command line, you must redirect stdin/out)" << std::endl; return RET_BADARGS; } else if (i->string_key.compare("help") == 0) { std::cout << "Copyright (C) 2010-2015 Adam Nielsen <*****@*****.**>\n" "This program comes with ABSOLUTELY NO WARRANTY. This is free software,\n" "and you are welcome to change and redistribute it under certain conditions;\n" "see <http://www.gnu.org/licenses/> for details.\n" "\n" "Utility to apply and reverse compression and encryption algorithms used by\n" "games on their data files.\n" "Build date " __DATE__ " " __TIME__ << "\n" "\n" "Usage: gamecomp -t <type> < infile > outfile\n" << poVisible << "\n" << std::endl; return RET_OK; } else if ( (i->string_key.compare("t") == 0) || (i->string_key.compare("type") == 0) ) { if (i->value.size() == 0) { std::cerr << PROGNAME ": --type (-t) requires a parameter." << std::endl; return RET_BADARGS; } pFilterType = ga::FilterManager::byCode(i->value[0]); if (!pFilterType) { std::cerr << PROGNAME ": Unknown filter code given by --type (-t) - " "use -l for a list." << std::endl; return RET_BADARGS; } } else if ( (i->string_key.compare("a") == 0) || (i->string_key.compare("apply") == 0) ) { bApply = true; } else if ( (i->string_key.compare("l") == 0) || (i->string_key.compare("list") == 0) ) { for (const auto& i : ga::FilterManager::formats()) { std::cout << i->code() << "\n"; } std::cout << std::flush; return RET_OK; } } if (!pFilterType) { std::cerr << PROGNAME ": No filter type given (--type/-t). Use -l for " "a list." << std::endl; return RET_BADARGS; } if (bApply) { // Apply the filter pstdout = pFilterType->apply( std::move(pstdout), camoto::stream::fn_notify_prefiltered_size() ); // Copy filtered data to stdout stream::copy(*pstdout, *stream::open_stdin()); } else { // Apply the filter auto in = pFilterType->apply(stream::open_stdin()); // Copy filtered data to stdout stream::copy(*pstdout, *in); } pstdout->flush(); } catch (const camoto::filter_error& e) { pstdout->flush(); // keep as much data as we could process std::cerr << PROGNAME ": Filtering failed. " << e.what() << std::endl; return RET_SHOWSTOPPER; } catch (const stream::error& e) { std::cerr << PROGNAME ": I/O error - " << e.what() << std::endl; return RET_SHOWSTOPPER; } catch (const po::unknown_option& e) { std::cerr << PROGNAME ": " << e.what() << ". Use --help for help." << std::endl; return RET_BADARGS; } catch (const po::invalid_command_line_syntax& e) { std::cerr << PROGNAME ": " << e.what() << ". Use --help for help." << std::endl; return RET_BADARGS; } return RET_OK; }