diff --git a/src/VmaReplay/Common.cpp b/src/VmaReplay/Common.cpp index f04aece..4c1a6b7 100644 --- a/src/VmaReplay/Common.cpp +++ b/src/VmaReplay/Common.cpp @@ -1,5 +1,436 @@ #include "Common.h" +//////////////////////////////////////////////////////////////////////////////// +// class CmdLineParser + +bool CmdLineParser::ReadNextArg(std::string *OutArg) +{ + if (m_argv != NULL) + { + if (m_ArgIndex >= (size_t)m_argc) return false; + + *OutArg = m_argv[m_ArgIndex]; + m_ArgIndex++; + return true; + } + else + { + if (m_ArgIndex >= m_CmdLineLength) return false; + + OutArg->clear(); + bool InsideQuotes = false; + while (m_ArgIndex < m_CmdLineLength) + { + char Ch = m_CmdLine[m_ArgIndex]; + if (Ch == '\\') + { + bool FollowedByQuote = false; + size_t BackslashCount = 1; + size_t TmpIndex = m_ArgIndex + 1; + while (TmpIndex < m_CmdLineLength) + { + char TmpCh = m_CmdLine[TmpIndex]; + if (TmpCh == '\\') + { + BackslashCount++; + TmpIndex++; + } + else if (TmpCh == '"') + { + FollowedByQuote = true; + break; + } + else + break; + } + + if (FollowedByQuote) + { + if (BackslashCount % 2 == 0) + { + for (size_t i = 0; i < BackslashCount / 2; i++) + *OutArg += '\\'; + m_ArgIndex += BackslashCount + 1; + InsideQuotes = !InsideQuotes; + } + else + { + for (size_t i = 0; i < BackslashCount / 2; i++) + *OutArg += '\\'; + *OutArg += '"'; + m_ArgIndex += BackslashCount + 1; + } + } + else + { + for (size_t i = 0; i < BackslashCount; i++) + *OutArg += '\\'; + m_ArgIndex += BackslashCount; + } + } + else if (Ch == '"') + { + InsideQuotes = !InsideQuotes; + m_ArgIndex++; + } + else if (isspace(Ch)) + { + if (InsideQuotes) + { + *OutArg += Ch; + m_ArgIndex++; + } + else + { + m_ArgIndex++; + break; + } + } + else + { + *OutArg += Ch; + m_ArgIndex++; + } + } + + while (m_ArgIndex < m_CmdLineLength && isspace(m_CmdLine[m_ArgIndex])) + m_ArgIndex++; + + return true; + } +} + +CmdLineParser::SHORT_OPT * CmdLineParser::FindShortOpt(char Opt) +{ + for (size_t i = 0; i < m_ShortOpts.size(); i++) + if (m_ShortOpts[i].Opt == Opt) + return &m_ShortOpts[i]; + return NULL; +} + +CmdLineParser::LONG_OPT * CmdLineParser::FindLongOpt(const std::string &Opt) +{ + for (size_t i = 0; i < m_LongOpts.size(); i++) + if (m_LongOpts[i].Opt == Opt) + return &m_LongOpts[i]; + return NULL; +} + +CmdLineParser::CmdLineParser(int argc, char **argv) : + m_argv(argv), + m_CmdLine(NULL), + m_argc(argc), + m_CmdLineLength(0), + m_ArgIndex(1), + m_InsideMultioption(false), + m_LastArgIndex(0), + m_LastOptId(0) +{ + assert(argc > 0); + assert(argv != NULL); +} + +CmdLineParser::CmdLineParser(const char *CmdLine) : + m_argv(NULL), + m_CmdLine(CmdLine), + m_argc(0), + m_ArgIndex(0), + m_InsideMultioption(false), + m_LastArgIndex(0), + m_LastOptId(0) +{ + assert(CmdLine != NULL); + + m_CmdLineLength = strlen(m_CmdLine); + + while (m_ArgIndex < m_CmdLineLength && isspace(m_CmdLine[m_ArgIndex])) + m_ArgIndex++; +} + +void CmdLineParser::RegisterOpt(uint32_t Id, char Opt, bool Parameter) +{ + assert(Opt != '\0'); + + m_ShortOpts.push_back(SHORT_OPT(Id, Opt, Parameter)); +} + +void CmdLineParser::RegisterOpt(uint32_t Id, const std::string &Opt, bool Parameter) +{ + assert(!Opt.empty()); + + m_LongOpts.push_back(LONG_OPT(Id, Opt, Parameter)); +} + +CmdLineParser::RESULT CmdLineParser::ReadNext() +{ + if (m_InsideMultioption) + { + assert(m_LastArgIndex < m_LastArg.length()); + SHORT_OPT *so = FindShortOpt(m_LastArg[m_LastArgIndex]); + if (so == NULL) + { + m_LastOptId = 0; + m_LastParameter.clear(); + return CmdLineParser::RESULT_ERROR; + } + if (so->Parameter) + { + if (m_LastArg.length() == m_LastArgIndex+1) + { + if (!ReadNextArg(&m_LastParameter)) + { + m_LastOptId = 0; + m_LastParameter.clear(); + return CmdLineParser::RESULT_ERROR; + } + m_InsideMultioption = false; + m_LastOptId = so->Id; + return CmdLineParser::RESULT_OPT; + } + else if (m_LastArg[m_LastArgIndex+1] == '=') + { + m_InsideMultioption = false; + m_LastParameter = m_LastArg.substr(m_LastArgIndex+2); + m_LastOptId = so->Id; + return CmdLineParser::RESULT_OPT; + } + else + { + m_InsideMultioption = false; + m_LastParameter = m_LastArg.substr(m_LastArgIndex+1); + m_LastOptId = so->Id; + return CmdLineParser::RESULT_OPT; + } + } + else + { + if (m_LastArg.length() == m_LastArgIndex+1) + { + m_InsideMultioption = false; + m_LastParameter.clear(); + m_LastOptId = so->Id; + return CmdLineParser::RESULT_OPT; + } + else + { + m_LastArgIndex++; + + m_LastParameter.clear(); + m_LastOptId = so->Id; + return CmdLineParser::RESULT_OPT; + } + } + } + else + { + if (!ReadNextArg(&m_LastArg)) + { + m_LastParameter.clear(); + m_LastOptId = 0; + return CmdLineParser::RESULT_END; + } + + if (!m_LastArg.empty() && m_LastArg[0] == '-') + { + if (m_LastArg.length() > 1 && m_LastArg[1] == '-') + { + size_t EqualIndex = m_LastArg.find('=', 2); + if (EqualIndex != std::string::npos) + { + LONG_OPT *lo = FindLongOpt(m_LastArg.substr(2, EqualIndex-2)); + if (lo == NULL || lo->Parameter == false) + { + m_LastOptId = 0; + m_LastParameter.clear(); + return CmdLineParser::RESULT_ERROR; + } + m_LastParameter = m_LastArg.substr(EqualIndex+1); + m_LastOptId = lo->Id; + return CmdLineParser::RESULT_OPT; + } + else + { + LONG_OPT *lo = FindLongOpt(m_LastArg.substr(2)); + if (lo == NULL) + { + m_LastOptId = 0; + m_LastParameter.clear(); + return CmdLineParser::RESULT_ERROR; + } + if (lo->Parameter) + { + if (!ReadNextArg(&m_LastParameter)) + { + m_LastOptId = 0; + m_LastParameter.clear(); + return CmdLineParser::RESULT_ERROR; + } + } + else + m_LastParameter.clear(); + m_LastOptId = lo->Id; + return CmdLineParser::RESULT_OPT; + } + } + else + { + if (m_LastArg.length() < 2) + { + m_LastOptId = 0; + m_LastParameter.clear(); + return CmdLineParser::RESULT_ERROR; + } + SHORT_OPT *so = FindShortOpt(m_LastArg[1]); + if (so == NULL) + { + m_LastOptId = 0; + m_LastParameter.clear(); + return CmdLineParser::RESULT_ERROR; + } + if (so->Parameter) + { + if (m_LastArg.length() == 2) + { + if (!ReadNextArg(&m_LastParameter)) + { + m_LastOptId = 0; + m_LastParameter.clear(); + return CmdLineParser::RESULT_ERROR; + } + m_LastOptId = so->Id; + return CmdLineParser::RESULT_OPT; + } + else if (m_LastArg[2] == '=') + { + m_LastParameter = m_LastArg.substr(3); + m_LastOptId = so->Id; + return CmdLineParser::RESULT_OPT; + } + else + { + m_LastParameter = m_LastArg.substr(2); + m_LastOptId = so->Id; + return CmdLineParser::RESULT_OPT; + } + } + else + { + if (m_LastArg.length() == 2) + { + m_LastParameter.clear(); + m_LastOptId = so->Id; + return CmdLineParser::RESULT_OPT; + } + else + { + m_InsideMultioption = true; + m_LastArgIndex = 2; + + m_LastParameter.clear(); + m_LastOptId = so->Id; + return CmdLineParser::RESULT_OPT; + } + } + } + } + else if (!m_LastArg.empty() && m_LastArg[0] == '/') + { + size_t EqualIndex = m_LastArg.find('=', 1); + if (EqualIndex != std::string::npos) + { + if (EqualIndex == 2) + { + SHORT_OPT *so = FindShortOpt(m_LastArg[1]); + if (so != NULL) + { + if (so->Parameter == false) + { + m_LastOptId = 0; + m_LastParameter.clear(); + return CmdLineParser::RESULT_ERROR; + } + m_LastParameter = m_LastArg.substr(EqualIndex+1); + m_LastOptId = so->Id; + return CmdLineParser::RESULT_OPT; + } + } + LONG_OPT *lo = FindLongOpt(m_LastArg.substr(1, EqualIndex-1)); + if (lo == NULL || lo->Parameter == false) + { + m_LastOptId = 0; + m_LastParameter.clear(); + return CmdLineParser::RESULT_ERROR; + } + m_LastParameter = m_LastArg.substr(EqualIndex+1); + m_LastOptId = lo->Id; + return CmdLineParser::RESULT_OPT; + } + else + { + if (m_LastArg.length() == 2) + { + SHORT_OPT *so = FindShortOpt(m_LastArg[1]); + if (so != NULL) + { + if (so->Parameter) + { + if (!ReadNextArg(&m_LastParameter)) + { + m_LastOptId = 0; + m_LastParameter.clear(); + return CmdLineParser::RESULT_ERROR; + } + } + else + m_LastParameter.clear(); + m_LastOptId = so->Id; + return CmdLineParser::RESULT_OPT; + } + } + LONG_OPT *lo = FindLongOpt(m_LastArg.substr(1)); + if (lo == NULL) + { + m_LastOptId = 0; + m_LastParameter.clear(); + return CmdLineParser::RESULT_ERROR; + } + if (lo->Parameter) + { + if (!ReadNextArg(&m_LastParameter)) + { + m_LastOptId = 0; + m_LastParameter.clear(); + return CmdLineParser::RESULT_ERROR; + } + } + else + m_LastParameter.clear(); + m_LastOptId = lo->Id; + return CmdLineParser::RESULT_OPT; + } + } + else + { + m_LastOptId = 0; + m_LastParameter = m_LastArg; + return CmdLineParser::RESULT_PARAMETER; + } + } +} + +uint32_t CmdLineParser::GetOptId() +{ + return m_LastOptId; +} + +const std::string & CmdLineParser::GetParameter() +{ + return m_LastParameter; +} + +//////////////////////////////////////////////////////////////////////////////// +// Glolals + /* void SetConsoleColor(CONSOLE_COLOR color) diff --git a/src/VmaReplay/Common.h b/src/VmaReplay/Common.h index d3ac260..afd52c1 100644 --- a/src/VmaReplay/Common.h +++ b/src/VmaReplay/Common.h @@ -39,6 +39,67 @@ static inline T align_up(T val, T align) return (val + align - 1) / align * align; } +class CmdLineParser +{ +public: + enum RESULT + { + RESULT_OPT, + RESULT_PARAMETER, + RESULT_END, + RESULT_ERROR, + }; + + CmdLineParser(int argc, char **argv); + CmdLineParser(const char *CmdLine); + + void RegisterOpt(uint32_t Id, char Opt, bool Parameter); + void RegisterOpt(uint32_t Id, const std::string &Opt, bool Parameter); + + RESULT ReadNext(); + uint32_t GetOptId(); + const std::string & GetParameter(); + +private: + struct SHORT_OPT + { + uint32_t Id; + char Opt; + bool Parameter; + + SHORT_OPT(uint32_t Id, char Opt, bool Parameter) : Id(Id), Opt(Opt), Parameter(Parameter) { } + }; + + struct LONG_OPT + { + uint32_t Id; + std::string Opt; + bool Parameter; + + LONG_OPT(uint32_t Id, std::string Opt, bool Parameter) : Id(Id), Opt(Opt), Parameter(Parameter) { } + }; + + char **m_argv; + const char *m_CmdLine; + int m_argc; + size_t m_CmdLineLength; + size_t m_ArgIndex; + + bool ReadNextArg(std::string *OutArg); + + std::vector m_ShortOpts; + std::vector m_LongOpts; + + SHORT_OPT * FindShortOpt(char Opt); + LONG_OPT * FindLongOpt(const std::string &Opt); + + bool m_InsideMultioption; + std::string m_LastArg; + size_t m_LastArgIndex; + uint32_t m_LastOptId; + std::string m_LastParameter; +}; + /* class RandomNumberGenerator { diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp index eaac603..dd88f42 100644 --- a/src/VmaReplay/VmaReplay.cpp +++ b/src/VmaReplay/VmaReplay.cpp @@ -30,11 +30,31 @@ static const int RESULT_ERROR_SOURCE_FILE = -2; static const int RESULT_ERROR_FORMAT = -3; static const int RESULT_ERROR_VULKAN = -4; +enum CMD_LINE_OPT +{ + CMD_LINE_OPT_VERBOSITY, +}; + +static enum class VERBOSITY +{ + MINIMUM = 0, + DEFAULT, + MAXIMUM, + COUNT, +} g_Verbosity = VERBOSITY::DEFAULT; + +static std::string g_FilePath; + struct StrRange { const char* beg; const char* end; + StrRange() { } + StrRange(const char* beg, const char* end) : beg(beg), end(end) { } + explicit StrRange(const char* sz) : beg(sz), end(sz + strlen(sz)) { } + explicit StrRange(const std::string& s) : beg(s.data()), end(s.data() + s.length()) { } + size_t length() const { return end - beg; } }; @@ -286,7 +306,7 @@ private: void Destroy(const Allocation& alloc); // Increments warning counter. Returns true if warning message should be printed. - bool IssueWarning() { return m_WarningCount++ < MAX_WARNINGS_TO_SHOW; } + bool IssueWarning(); int InitVulkan(); void FinalizeVulkan(); @@ -318,11 +338,14 @@ int Player::Init() Player::~Player() { - PrintStats(); + if(g_Verbosity > VERBOSITY::MINIMUM) + { + PrintStats(); + } FinalizeVulkan(); - if(m_WarningCount > MAX_WARNINGS_TO_SHOW) + if(g_Verbosity < VERBOSITY::MAXIMUM && m_WarningCount > MAX_WARNINGS_TO_SHOW) printf("WARNING: %zu more warnings not shown.\n", m_WarningCount - MAX_WARNINGS_TO_SHOW); } @@ -421,9 +444,25 @@ void Player::Destroy(const Allocation& alloc) vmaFreeMemory(m_Allocator, alloc.allocation); } +bool Player::IssueWarning() +{ + if(g_Verbosity < VERBOSITY::MAXIMUM) + { + return m_WarningCount++ < MAX_WARNINGS_TO_SHOW; + } + else + { + ++m_WarningCount; + return true; + } +} + int Player::InitVulkan() { - printf("Initializing Vulkan...\n"); + if(g_Verbosity > VERBOSITY::MINIMUM) + { + printf("Initializing Vulkan...\n"); + } uint32_t instanceLayerPropCount = 0; VkResult res = vkEnumerateInstanceLayerProperties(&instanceLayerPropCount, nullptr); @@ -984,14 +1023,24 @@ void Player::ExecuteCreateImage(size_t lineNumber, const CsvSplit& csvSplit) static void PrintCommandLineSyntax() { - printf("Command line syntax:\n" - " VmaReplay \n"); + printf( + "Command line syntax:\n" + " VmaReplay [Options] \n" + "Available options:\n" + " -v - Verbosity level:\n" + " 0 - Minimum verbosity. Prints only warnings and errors.\n" + " 1 - Default verbosity. Prints important messages and statistics.\n" + " 2 - Maximum verbosity. Prints a lot of information.\n" + ); } static int ProcessFile(const char* data, size_t numBytes) { // Begin stats. - printf("File size: %zu B\n", numBytes); + if(g_Verbosity > VERBOSITY::MINIMUM) + { + printf("File size: %zu B\n", numBytes); + } LineSplit lineSplit(data, numBytes); StrRange line; @@ -1014,27 +1063,36 @@ static int ProcessFile(const char* data, size_t numBytes) int result = player.Init(); if(result == 0) { - printf("Playing...\n"); + if(g_Verbosity > VERBOSITY::MINIMUM) + { + printf("Playing...\n"); + } while(lineSplit.GetNextLine(line)) { player.ExecuteLine(lineSplit.GetNextLineIndex(), line); } // End stats. - printf("Done.\n"); - printf("File lines: %zu\n", lineSplit.GetNextLineIndex()); + if(g_Verbosity > VERBOSITY::MINIMUM) + { + printf("Done.\n"); + printf("File lines: %zu\n", lineSplit.GetNextLineIndex()); + } } return result; } -static int ProcessFile(const char* filePath) +static int ProcessFile() { - printf("Loading file \"%s\"...\n", filePath); + if(g_Verbosity > VERBOSITY::MINIMUM) + { + printf("Loading file \"%s\"...\n", g_FilePath.c_str()); + } int result = 0; FILE* file = nullptr; - const errno_t err = fopen_s(&file, filePath, "rb"); + const errno_t err = fopen_s(&file, g_FilePath.c_str(), "rb"); if(err == 0) { _fseeki64(file, 0, SEEK_END); @@ -1066,13 +1124,64 @@ static int ProcessFile(const char* filePath) static int main2(int argc, char** argv) { - if(argc != 2) + CmdLineParser cmdLineParser(argc, argv); + + cmdLineParser.RegisterOpt(CMD_LINE_OPT_VERBOSITY, 'v', true); + + CmdLineParser::RESULT res; + while((res = cmdLineParser.ReadNext()) != CmdLineParser::RESULT_END) + { + switch(res) + { + case CmdLineParser::RESULT_OPT: + switch(cmdLineParser.GetOptId()) + { + case CMD_LINE_OPT_VERBOSITY: + { + uint32_t verbosityVal = UINT32_MAX; + if(StrRangeToUint(StrRange(cmdLineParser.GetParameter()), verbosityVal) && + verbosityVal < (uint32_t)VERBOSITY::COUNT) + { + g_Verbosity = (VERBOSITY)verbosityVal; + } + else + { + PrintCommandLineSyntax(); + return RESULT_ERROR_COMMAND_LINE; + } + } + break; + default: + assert(0); + } + break; + case CmdLineParser::RESULT_PARAMETER: + if(g_FilePath.empty()) + { + g_FilePath = cmdLineParser.GetParameter(); + } + else + { + PrintCommandLineSyntax(); + return RESULT_ERROR_COMMAND_LINE; + } + break; + case CmdLineParser::RESULT_ERROR: + PrintCommandLineSyntax(); + return RESULT_ERROR_COMMAND_LINE; + break; + default: + assert(0); + } + } + + if(g_FilePath.empty()) { PrintCommandLineSyntax(); return RESULT_ERROR_COMMAND_LINE; } - return ProcessFile(argv[1]); + return ProcessFile(); } int main(int argc, char** argv)