#include #include #include #include #include "utils.h" #include "config_file.h" #include #include #include #include #include int PrintUsage(const char *aExecName, const char *ErrorStr = nullptr) { if (ErrorStr != nullptr) std::cout << "Error: " << ErrorStr << std::endl; std::cout << "Usage: " << aExecName << " []" << std::endl; std::cout << " The following options are available:" << std::endl; std::cout << " -m: allow multiple matches" << std::endl; return 1; } std::regex CommentLine("[ \t]*#.*"); std::regex MatchLine("[ \t]*(0x[0-9a-fA-F]+):(0x[0-9a-fA-F]+)[ \t]*(#.*)?"); std::regex PatchLine("[ \t]*(0x[0-9a-fA-F]+):(0x[0-9a-fA-F]+)[ \t]*(0x[0-9a-fA-F]+):(0x[0-9a-fA-F]+)[ \t]*(#.*)?"); struct ValMask_s { uint16_t mValue; uint16_t mMask; }; struct Patch_s { ValMask_s mMatch; ValMask_s mPatch; }; std::string Trim(const std::string& aStr, const std::string& aWhitespace = " \t") { const auto FirstNonSpace = aStr.find_first_not_of(aWhitespace); if (FirstNonSpace == std::string::npos) return ""; const auto LastNonSpace = aStr.find_last_not_of(aWhitespace); const auto TrimRange = LastNonSpace - FirstNonSpace + 1; return aStr.substr(FirstNonSpace, TrimRange); } boost::optional ParseLine(std::string &aLine, size_t aLineNo) { std::string Line = Trim(aLine); if (Line.length() == 0) return boost::optional(); // Empty lines are skipped std::smatch Matches; if (std::regex_match(Line, Matches, PatchLine)) { CRAY_ASSERT(Matches.size() > 5); Patch_s Patch; Patch.mMatch.mValue = FromString(Matches[1]); Patch.mMatch.mMask = FromString(Matches[2]); Patch.mPatch.mValue = FromString(Matches[3]); Patch.mPatch.mMask = FromString(Matches[4]); return Patch; } if (std::regex_match(Line, Matches, MatchLine)) { CRAY_ASSERT(Matches.size() > 3); Patch_s Patch; Patch.mMatch.mValue = FromString(Matches[1]); Patch.mMatch.mMask = FromString(Matches[2]); Patch.mPatch.mValue = 0; Patch.mPatch.mMask = 0x0000; // Patch is not applied return Patch; } if (std::regex_match(Line, CommentLine)) return boost::optional(); // Comment lines are skipped throw Generic_x() << "Can't parse line " << DecPrinter(aLineNo); } class PatchFile_c { public: PatchFile_c() {} void Read(std::ifstream &aStrm) { size_t LineNo = 1; while (!aStrm.eof()) { std::string Line; std::getline(aStrm, Line); boost::optional Patch = ParseLine(Line, LineNo); if (Patch.is_initialized()) { mPatch.push_back(Patch.get()); } } } std::vector mPatch; unsigned int Apply(std::vector &aArray) { unsigned int MatchCount = 0; size_t Start = 0; size_t LastStart = aArray.size() - mPatch.size(); while (Start < LastStart) { bool Found = true; for (size_t Idx = 0; Idx < mPatch.size(); ++Idx) { if (((aArray[Start + Idx] ^ mPatch[Idx].mMatch.mValue) & mPatch[Idx].mMatch.mMask) != 0) { Found = false; break; } } if (Found) { for (size_t Idx = 0; Idx < mPatch.size(); ++Idx) { aArray[Start + Idx] = (aArray[Start + Idx] & ~mPatch[Idx].mPatch.mMask) | (mPatch[Idx].mPatch.mValue & mPatch[Idx].mPatch.mMask); } ++MatchCount; } ++Start; } return MatchCount; } }; int main(int argc, const char* argv[]) { CommandLine_c CommandLine(argc, argv); std::string InFileName; std::string PatchFileName; std::string OutFileName; bool UniqueMatch = true; try { while (CommandLine.HasMoreParams()) { std::string CurParam = CommandLine.GetNextParam(); if (CurParam.length() == 0) continue; if (CurParam == "-m") { UniqueMatch = false; } else if (InFileName.empty()) { InFileName = CurParam; } else if (PatchFileName.empty()) { PatchFileName = CurParam; } else if (OutFileName.empty()) { OutFileName = CurParam; } else { throw Generic_x("Unkown command line parameter"); } } if (InFileName.length() == 0) throw Generic_x() << "Input file must be specified"; if (!boost::filesystem::exists(InFileName)) throw Generic_x() << "File: " << InFileName << " doesn't exist"; if (PatchFileName.length() == 0) throw Generic_x() << "Patch file must be specified"; if (!boost::filesystem::exists(PatchFileName)) throw Generic_x() << "File: " << PatchFileName << " doesn't exist"; if (OutFileName.length() == 0) throw Generic_x() << "Output file must be specified"; size_t FileSize = boost::filesystem::file_size(InFileName); if ((FileSize % 2) != 0) throw Generic_x() << "This prorgam only supports binary files with even number of bytes"; std::vector FileContent(FileSize / 2); std::ifstream InFile(InFileName, std::ios::binary | std::ios::in); InFile.read((char*)&(FileContent[0]), FileSize); if (InFile.bad()) throw Generic_x() << "Can't read input file: " << InFileName; InFile.close(); for (auto &Word : FileContent) { Word = SwapBytes(Word); } std::ifstream PatchStrm(PatchFileName, std::ios::in); if (PatchStrm.bad()) throw Generic_x() << "Can't open patch file: " << PatchFileName; PatchFile_c PatchFile; PatchFile.Read(PatchStrm); PatchStrm.close(); unsigned int MatchCnt = PatchFile.Apply(FileContent); if (MatchCnt == 0) { std::cout << "Patch wasn't applied: no match was found" << std::endl; return 2; } if (MatchCnt > 1 && UniqueMatch) { std::cout << "Patch wasn't applied: multiple matches were found" << std::endl; return 3; } std::cout << "Patch applied " << MatchCnt << " times" << std::endl; std::ofstream OutFile(OutFileName, std::ios::binary | std::ios::out); if (OutFile.bad()) throw Generic_x() << "Can't open output file " << OutFileName; for (auto &Word : FileContent) { Word = SwapBytes(Word); } OutFile.write((char*)&(FileContent[0]), FileSize); if (OutFile.bad()) throw Generic_x() << "Can't write output file: " << OutFileName; OutFile.close(); return 0; } catch(std::exception &Ex) { return PrintUsage(argv[0], Ex.what()); } }