update cmd dispatcher
This commit is contained in:
parent
8a1f71e855
commit
3252e61c0d
12
README.md
12
README.md
|
@ -4,7 +4,7 @@ The Library for CMO File Read/Write. Also the Minimalist Virtools Environment.
|
|||
|
||||
## Status
|
||||
|
||||
This is a long time project and I am stucked at the analyze of CKBeObject. I don't know its class layout, And I don't know its complex `CKBeObject::Save()`.
|
||||
This is a long time project.
|
||||
This project welcome everyone's contribution, except the employee of Dassault, which created Virtools.
|
||||
|
||||
## Introduction
|
||||
|
@ -35,9 +35,8 @@ There are 3 lists which indicate our accept guideline.
|
|||
|
||||
These features will be accepted as soon as possible.
|
||||
|
||||
* The save steps of Virtools file.
|
||||
* The bug fix of Virtools file reader.
|
||||
* Class layout, `Save()` and `Load()` functions of following `CKObject` based classes.
|
||||
* Class layout, `Load()` functions of following `CKObject` based classes.
|
||||
- `CKBeObject`
|
||||
- `CKGroup`
|
||||
- `CKMaterial`
|
||||
|
@ -46,7 +45,7 @@ These features will be accepted as soon as possible.
|
|||
- `CKRenderObject`
|
||||
- `CK3dEntity`
|
||||
- `CK3dObject`
|
||||
* Class layout, `SaveData()` and `LoadData()` functions of following `CKBaseManager` based classes.
|
||||
* Class layout, and `LoadData()` functions of following `CKBaseManager` based classes.
|
||||
- `CKAttributeManager`
|
||||
- `CKBehaviorManager`
|
||||
|
||||
|
@ -55,6 +54,8 @@ These features will be accepted as soon as possible.
|
|||
These features are in plan, but not urge to merge.
|
||||
|
||||
* The `CK_ID` remap system of Reader & Writer.
|
||||
* Any Save functions.
|
||||
* The save steps of Virtools file.
|
||||
* Other CK classes implementations.
|
||||
* Non-Virtools 2.1 implementations.
|
||||
|
||||
|
@ -74,4 +75,5 @@ This project require:
|
|||
* zlib
|
||||
* iconv (non-Windows system required)
|
||||
|
||||
It can be compiled on Windows via sln file, or on Linux platform via CMake file.
|
||||
It can be compiled on Windows via sln file, or on Linux platform via CMake file.
|
||||
However CMake may not be updated in time because I develop this project on Windows frequently.
|
||||
|
|
|
@ -1,32 +1,10 @@
|
|||
#include "CmdHelper.hpp"
|
||||
#include "TerminalHelper.hpp"
|
||||
#include "StructFormatter.hpp"
|
||||
#include "AccessibleValue.hpp"
|
||||
|
||||
#include <CKMinContext.hpp>
|
||||
#include <CKFile.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <functional>
|
||||
|
||||
namespace Unvirt::CmdHelper {
|
||||
|
||||
static FILE* fout = stdout;
|
||||
|
||||
#pragma region CmdSplitter
|
||||
|
||||
CmdSplitter::CmdSplitter() :
|
||||
mCmdChar(0), mBuffer(nullptr), mResult(nullptr),
|
||||
mState(StateType::NORMAL), mPreState(StateType::NORMAL) {
|
||||
;
|
||||
}
|
||||
CmdSplitter::~CmdSplitter() {
|
||||
;
|
||||
}
|
||||
|
||||
const std::deque<std::string> CmdSplitter::Convert(const std::string& u8cmd) {
|
||||
std::deque<std::string> CmdSplitter::Convert(const std::string& u8cmd) {
|
||||
// set up variables
|
||||
std::deque<std::string> result;
|
||||
std::string buffer;
|
||||
|
@ -35,8 +13,8 @@ namespace Unvirt::CmdHelper {
|
|||
mState = mPreState = StateType::SPACE;
|
||||
|
||||
// split
|
||||
for (auto it = u8cmd.begin(); it != u8cmd.end(); ++it) {
|
||||
mCmdChar = (*it);
|
||||
for (auto& c : u8cmd) {
|
||||
mCmdChar = c;
|
||||
|
||||
// skip all invalid characters, \0 and etc.
|
||||
// mCmdChar >= 0 to ensure all non-ASCII UTF8 char can be accepted directly.
|
||||
|
@ -82,367 +60,292 @@ namespace Unvirt::CmdHelper {
|
|||
return result;
|
||||
}
|
||||
|
||||
void CmdSplitter::ProcSpace(void) {
|
||||
switch (mCmdChar) {
|
||||
case '\'':
|
||||
mState = StateType::SINGLE;
|
||||
break;
|
||||
case '"':
|
||||
mState = StateType::DOUBLE;
|
||||
break;
|
||||
case '\\':
|
||||
mState = StateType::ESCAPE;
|
||||
mPreState = StateType::NORMAL;
|
||||
break;
|
||||
case ' ':
|
||||
break; // skip blank
|
||||
default:
|
||||
mBuffer->push_back(mCmdChar);
|
||||
mState = StateType::NORMAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
void CmdSplitter::ProcSingle(void) {
|
||||
switch (mCmdChar) {
|
||||
case '\'':
|
||||
mState = StateType::NORMAL;
|
||||
break;
|
||||
case '"':
|
||||
mBuffer->push_back('"');
|
||||
break;
|
||||
case '\\':
|
||||
mState = StateType::ESCAPE;
|
||||
mPreState = StateType::SINGLE;
|
||||
break;
|
||||
case ' ':
|
||||
mBuffer->push_back(' ');
|
||||
break;
|
||||
default:
|
||||
mBuffer->push_back(mCmdChar);
|
||||
break;
|
||||
}
|
||||
}
|
||||
void CmdSplitter::ProcDouble(void) {
|
||||
switch (mCmdChar) {
|
||||
case '\'':
|
||||
mBuffer->push_back('\'');
|
||||
break;
|
||||
case '"':
|
||||
mState = StateType::NORMAL;
|
||||
break;
|
||||
case '\\':
|
||||
mState = StateType::ESCAPE;
|
||||
mPreState = StateType::DOUBLE;
|
||||
break;
|
||||
case ' ':
|
||||
mBuffer->push_back(' ');
|
||||
break;
|
||||
default:
|
||||
mBuffer->push_back(mCmdChar);
|
||||
break;
|
||||
}
|
||||
}
|
||||
void CmdSplitter::ProcEscape(void) {
|
||||
// add itself
|
||||
mBuffer->push_back(mCmdChar);
|
||||
// restore state
|
||||
mState = mPreState;
|
||||
}
|
||||
void CmdSplitter::ProcNormal(void) {
|
||||
switch (mCmdChar) {
|
||||
case '\'':
|
||||
mBuffer->push_back('\'');
|
||||
break;
|
||||
case '"':
|
||||
mBuffer->push_back('"');
|
||||
break;
|
||||
case '\\':
|
||||
mState = StateType::ESCAPE;
|
||||
mPreState = StateType::NORMAL;
|
||||
break;
|
||||
case ' ':
|
||||
mResult->push_back(*mBuffer);
|
||||
mBuffer->clear();
|
||||
mState = StateType::SPACE;
|
||||
break;
|
||||
default:
|
||||
mBuffer->push_back(mCmdChar);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
#pragma region ArgParser
|
||||
#pragma region Abstract Node
|
||||
|
||||
bool ArgParser::ParseInt(const std::deque<std::string>& cmd, const size_t expected_index, int32_t& result) {
|
||||
if (expected_index >= cmd.size()) {
|
||||
result = 0;
|
||||
AbstractNode* AbstractNode::Then(AbstractNode* node) {
|
||||
// check literal duplication
|
||||
for (auto& pnode : m_Nodes) {
|
||||
if (pnode->IsConflictWith(node))
|
||||
throw std::invalid_argument("conflict node.");
|
||||
}
|
||||
// add into list
|
||||
m_Nodes.emplace_back(node);
|
||||
return this;
|
||||
}
|
||||
|
||||
AbstractNode* AbstractNode::Executes(ExecutionFct fct, const char* cmt) {
|
||||
if (m_Execution != nullptr)
|
||||
throw std::invalid_argument("duplicated executions.");
|
||||
m_Execution = fct;
|
||||
m_Comment = cmt;
|
||||
return this;
|
||||
}
|
||||
|
||||
void AbstractNode::Help(HelpDocument& doc) {
|
||||
// add self
|
||||
BeginHelp(doc);
|
||||
|
||||
// check terminal
|
||||
if (m_Execution != nullptr) {
|
||||
doc.Terminate(m_Comment);
|
||||
}
|
||||
|
||||
// iterate children
|
||||
for (auto& pnode : m_Nodes) {
|
||||
pnode->Help(doc);
|
||||
}
|
||||
|
||||
// pop self
|
||||
EndHelp(doc);
|
||||
}
|
||||
|
||||
bool AbstractNode::Consume(std::deque<std::string>& arglist, ArgumentsMap& argmap) {
|
||||
// if no data can consume, return
|
||||
if (arglist.empty()) return false;
|
||||
|
||||
// backup current value
|
||||
std::string cur = arglist.front();
|
||||
// consume self
|
||||
if (!BeginAccept(cur, argmap)) {
|
||||
// fail to consume self. not matched. return
|
||||
return false;
|
||||
}
|
||||
|
||||
// pop front for following code
|
||||
arglist.pop_front();
|
||||
|
||||
#define CONSUME_DEFER \
|
||||
arglist.push_front(cur); \
|
||||
EndAccept(argmap);
|
||||
|
||||
if (arglist.empty()) {
|
||||
// this is must be a terminal.
|
||||
// check whether we have execution.
|
||||
if (m_Execution == nullptr) {
|
||||
CONSUME_DEFER;
|
||||
return false;
|
||||
} else {
|
||||
m_Execution(argmap);
|
||||
CONSUME_DEFER;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// have following command, try match them
|
||||
// iterate literal and argument to check terminal
|
||||
for (auto& pnode : m_Nodes) {
|
||||
if (pnode->Consume(arglist, argmap)) {
|
||||
CONSUME_DEFER;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// if still nothing to match, return false
|
||||
CONSUME_DEFER;
|
||||
return false;
|
||||
}
|
||||
|
||||
#undef CONSUME_DEFER
|
||||
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Argument Impl
|
||||
|
||||
bool IntArgument::BeginParse(const std::string& val) {
|
||||
char* pend = nullptr;
|
||||
errno = 0;
|
||||
int64_t v = std::strtoll(cmd[expected_index].c_str(), &pend, 10);
|
||||
int64_t v = std::strtoll(val.c_str(), &pend, 10);
|
||||
|
||||
if (pend == cmd[expected_index].c_str() || errno == ERANGE) {
|
||||
result = 0;
|
||||
if (pend == val.c_str() || errno == ERANGE) return false;
|
||||
|
||||
// check limit
|
||||
int32_t value = static_cast<int32_t>(v);
|
||||
if (m_IntLimit != nullptr && !m_IntLimit(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
result = static_cast<int>(v);
|
||||
m_ParsedData = new int32_t(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ArgParser::ParseString(const std::deque<std::string>& cmd, const size_t expected_index, std::string& result) {
|
||||
if (expected_index >= cmd.size()) {
|
||||
result.clear();
|
||||
return false;
|
||||
} else {
|
||||
result = cmd[expected_index];
|
||||
return true;
|
||||
}
|
||||
void IntArgument::EndParse() {
|
||||
delete reinterpret_cast<int32_t*>(m_ParsedData);
|
||||
m_ParsedData = nullptr;
|
||||
}
|
||||
|
||||
bool ArgParser::ParseSwitch(const std::deque<std::string>& cmd, const size_t expected_index, const std::vector<std::string>& switches, std::string& gotten) {
|
||||
if (expected_index >= cmd.size()) {
|
||||
gotten.clear();
|
||||
return false;
|
||||
}
|
||||
bool StringArgument::BeginParse(const std::string& strl) {
|
||||
// string always accept every text
|
||||
m_ParsedData = new std::string(strl);
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const auto& sw : switches) {
|
||||
if (cmd[expected_index] == sw) {
|
||||
gotten = cmd[expected_index];
|
||||
void StringArgument::EndParse() {
|
||||
delete reinterpret_cast<std::string*>(m_ParsedData);
|
||||
m_ParsedData = nullptr;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Command Root
|
||||
|
||||
bool CommandRoot::RootConsume(std::deque<std::string>& arglist) {
|
||||
// if no data can consume, return
|
||||
if (arglist.empty()) return false;
|
||||
|
||||
// create a argument map
|
||||
ArgumentsMap amap;
|
||||
|
||||
// and we only just need iterate all children
|
||||
for (auto& pnode : m_Nodes) {
|
||||
if (pnode->Consume(arglist, amap)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
gotten.clear();
|
||||
// no matched
|
||||
return false;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
HelpDocument* CommandRoot::RootHelp() {
|
||||
HelpDocument* doc = new HelpDocument();
|
||||
|
||||
#pragma region InteractiveCmd Misc
|
||||
// we only just need iterate all children
|
||||
for (auto& pnode : m_Nodes) {
|
||||
pnode->Help(*doc);
|
||||
}
|
||||
|
||||
InteractiveCmd::InteractiveCmd() :
|
||||
m_CmdSplitter(), m_PageLen(10),
|
||||
m_Ctx(nullptr), m_File(nullptr), m_Doc(nullptr) {
|
||||
|
||||
// create context and file
|
||||
m_Ctx = new LibCmo::CK2::CKMinContext();
|
||||
m_File = new LibCmo::CK2::CKFile(m_Ctx);
|
||||
|
||||
// bind callback
|
||||
m_Ctx->SetPrintCallback(std::bind(&InteractiveCmd::PrintMinContextMsg, this, std::placeholders::_1));
|
||||
|
||||
}
|
||||
|
||||
InteractiveCmd::~InteractiveCmd() {
|
||||
// delete doc if necessary
|
||||
if (m_Doc != nullptr) delete m_Doc;
|
||||
// delete file and ctx
|
||||
delete m_File;
|
||||
delete m_Ctx;
|
||||
}
|
||||
|
||||
bool InteractiveCmd::HasOpenedFile(void) {
|
||||
return m_Doc != nullptr;
|
||||
}
|
||||
|
||||
void InteractiveCmd::ClearDocument(void) {
|
||||
if (m_Doc == nullptr) return;
|
||||
|
||||
// clear doc
|
||||
delete m_Doc;
|
||||
m_Doc = nullptr;
|
||||
|
||||
// clear all loaded objects
|
||||
m_Ctx->ClearCKObject();
|
||||
}
|
||||
|
||||
void InteractiveCmd::PrintMinContextMsg(const std::string& msg) {
|
||||
fputs(UNVIRT_TERMCOL_LIGHT_YELLOW(("[CKMinContext] ")), fout);
|
||||
fputs(msg.c_str(), fout);
|
||||
fputc('\n', fout);
|
||||
return doc;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region InteractiveCmd Dispatch
|
||||
#pragma region Help Document
|
||||
|
||||
void InteractiveCmd::Run(void) {
|
||||
std::string u8cmd;
|
||||
void HelpDocument::Terminate(std::string& command_desc) {
|
||||
// create new result and copy stack
|
||||
ResultItem result(command_desc);
|
||||
result.m_ArgDesc.insert(result.m_ArgDesc.end(), m_Stack.begin(), m_Stack.end());
|
||||
// add into result
|
||||
m_Results.emplace_back(std::move(result));
|
||||
}
|
||||
|
||||
while (true) {
|
||||
// get command
|
||||
GetCmdLine(u8cmd);
|
||||
void HelpDocument::Print() {
|
||||
fputs("Command Help:\n", stdout);
|
||||
|
||||
// split cmd and parse it
|
||||
auto cmds = m_CmdSplitter.Convert(u8cmd);
|
||||
|
||||
// get sub command
|
||||
if (cmds.size() < 1u) {
|
||||
this->PrintCommonError("No command specified!");
|
||||
this->PrintHelp();
|
||||
continue;
|
||||
for (auto& item : m_Results) {
|
||||
for (auto& cmd : item.m_ArgDesc) {
|
||||
fputs(cmd.m_Name.c_str(), stdout);
|
||||
fputc(' ', stdout);
|
||||
}
|
||||
std::string subcmd(cmds.front());
|
||||
cmds.pop_front();
|
||||
fputc('\n', stdout);
|
||||
|
||||
// dispatch command
|
||||
if (subcmd == "load") this->ProcLoad(cmds);
|
||||
else if (subcmd == "unload") this->ProcUnLoad(cmds);
|
||||
else if (subcmd == "info") this->ProcInfo(cmds);
|
||||
else if (subcmd == "ls") this->ProcLs(cmds);
|
||||
else if (subcmd == "items") this->ProcItems(cmds);
|
||||
else if (subcmd == "encoding") this->ProcEncoding(cmds);
|
||||
else if (subcmd == "temp") this->ProcTemp(cmds);
|
||||
else if (subcmd == "help") this->PrintHelp();
|
||||
else if (subcmd == "exit") break;
|
||||
else {
|
||||
this->PrintCommonError("No such command \"%s\".", subcmd.c_str());
|
||||
this->PrintHelp();
|
||||
if (!item.m_CmdDesc.empty()) {
|
||||
fputs(item.m_CmdDesc.c_str(), stdout);
|
||||
fputc('\n', stdout);
|
||||
}
|
||||
|
||||
|
||||
for (auto& cmd : item.m_ArgDesc) {
|
||||
if (!cmd.m_Desc.empty()) {
|
||||
fprintf(stdout, "\t%s: %s\n", cmd.m_Name.c_str(), cmd.m_Desc.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
fputc('\n', stdout);
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveCmd::GetCmdLine(std::string& u8cmd) {
|
||||
fputs("Unvirt> ", fout);
|
||||
#if defined(LIBCMO_OS_WIN32)
|
||||
std::wstring wcmd;
|
||||
std::getline(std::wcin, wcmd);
|
||||
LibCmo::EncodingHelper::WcharToChar(wcmd, u8cmd, CP_UTF8);
|
||||
#else
|
||||
std::getline(std::cin, u8cmd);
|
||||
#endif
|
||||
}
|
||||
|
||||
void InteractiveCmd::PrintHelp(void) {
|
||||
fputs(UNVIRT_TERMCOL_LIGHT_YELLOW(("Allowed Subcommands: \n")), fout);
|
||||
|
||||
fputs("load\n", fout);
|
||||
fputs("\tDescription: Load a Virtools composition.\n", fout);
|
||||
fputs("\tSyntax: load <file path>\n", fout);
|
||||
|
||||
fputs("unload\n", fout);
|
||||
fputs("\tDescription: Release loaded Virtools composition.\n", fout);
|
||||
fputs("\tSyntax: unload\n", fout);
|
||||
|
||||
fputs("info\n", fout);
|
||||
fputs("\tDescription: Show the header info of loaded Virtools composition.\n", fout);
|
||||
fputs("\tSyntax: info\n", fout);
|
||||
|
||||
fputs("ls\n", fout);
|
||||
fputs("\tDescription: List something about loaded Virtools composition.\n", fout);
|
||||
fputs("\tSyntax: ls <obj | mgr> [page]\n", fout);
|
||||
|
||||
fputs("items\n", fout);
|
||||
fputs("\tDescription: Set up how many items should be listed in one page when using \"ls\" command.\n", fout);
|
||||
fputs("\tSyntax: items <num>\n", fout);
|
||||
|
||||
fputs("encoding\n", fout);
|
||||
fputs("\tDescription: Set the encoding series for CKMinContext.\n", fout);
|
||||
fputs("\tSyntax: encoding [encoding name1] [encoding name2] [encoding name3] ...\n", fout);
|
||||
|
||||
fputs("temp\n", fout);
|
||||
fputs("\tDescription: Set the Temp path for CKMinContext.\n", fout);
|
||||
fputs("\tSyntax: temp <temp path>\n", fout);
|
||||
|
||||
fputs("exit\n", fout);
|
||||
fputs("\tDescription: Exit program\n", fout);
|
||||
fputs("\tSyntax: exit\n", fout);
|
||||
|
||||
}
|
||||
|
||||
void InteractiveCmd::PrintArgParseError(const std::deque<std::string>& cmd, size_t pos) {
|
||||
if (pos >= cmd.size()) {
|
||||
fprintf(fout, UNVIRT_TERMCOL_LIGHT_RED(("Lost argument at position %zu.\n")), pos);
|
||||
} else {
|
||||
fprintf(fout, UNVIRT_TERMCOL_LIGHT_RED(("Unexpected argument \"%s\".\n")), cmd[pos].c_str());
|
||||
}
|
||||
|
||||
// arg error always print help
|
||||
this->PrintHelp();
|
||||
}
|
||||
|
||||
void InteractiveCmd::PrintCommonError(const char* u8_fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, u8_fmt);
|
||||
std::fputs(UNVIRT_TERMCOLHDR_LIGHT_RED, fout);
|
||||
std::vfprintf(fout, u8_fmt, argptr);
|
||||
std::fputs(UNVIRT_TERMCOLTAIL, fout);
|
||||
va_end(argptr);
|
||||
std::fputc('\n', fout);
|
||||
}
|
||||
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region InteractiveCmd Processors
|
||||
|
||||
void InteractiveCmd::ProcLoad(const std::deque<std::string>& cmd) {
|
||||
// check pre-requirement
|
||||
if (HasOpenedFile()) {
|
||||
this->PrintCommonError("Already have a opened file. Close it before calling \"load\".");
|
||||
return;
|
||||
}
|
||||
|
||||
// check requirement
|
||||
size_t pos = 0u;
|
||||
std::string filepath;
|
||||
if (!ArgParser::ParseString(cmd, pos, filepath)) {
|
||||
this->PrintArgParseError(cmd, pos);
|
||||
return;
|
||||
}
|
||||
|
||||
// proc
|
||||
m_Doc = new LibCmo::CK2::CKFileDocument();
|
||||
LibCmo::CK2::CKERROR err = m_File->DeepLoad(filepath.c_str(), &m_Doc);
|
||||
if (err != LibCmo::CK2::CKERROR::CKERR_OK) {
|
||||
// fail to load. release all.
|
||||
this->PrintCommonError("Fail to open file. Function return: %s\n%s",
|
||||
Unvirt::AccessibleValue::GetCkErrorName(err).c_str(),
|
||||
Unvirt::AccessibleValue::GetCkErrorDescription(err).c_str()
|
||||
);
|
||||
|
||||
this->ClearDocument();
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveCmd::ProcUnLoad(const std::deque<std::string>& cmd) {
|
||||
// check pre-requirement
|
||||
if (!HasOpenedFile()) {
|
||||
this->PrintCommonError("No loaded file.");
|
||||
return;
|
||||
}
|
||||
|
||||
// free all
|
||||
this->ClearDocument();
|
||||
}
|
||||
|
||||
void InteractiveCmd::ProcInfo(const std::deque<std::string>& cmd) {
|
||||
// check pre-requirement
|
||||
if (!HasOpenedFile()) {
|
||||
this->PrintCommonError("No loaded file.");
|
||||
return;
|
||||
}
|
||||
|
||||
// print
|
||||
Unvirt::StructFormatter::PrintCKFileInfo(m_Doc->m_FileInfo);
|
||||
}
|
||||
|
||||
void InteractiveCmd::ProcLs(const std::deque<std::string>& cmd) {
|
||||
// static values of switches
|
||||
static const std::vector<std::string> c_AllowedSwitches{
|
||||
"obj", "mgr"
|
||||
};
|
||||
|
||||
// check pre-requirement
|
||||
if (!HasOpenedFile()) {
|
||||
this->PrintCommonError("No loaded file.");
|
||||
return;
|
||||
}
|
||||
|
||||
// check requirement
|
||||
size_t pos = 0u;
|
||||
std::string sw;
|
||||
if (!ArgParser::ParseSwitch(cmd, pos, c_AllowedSwitches, sw)) {
|
||||
this->PrintArgParseError(cmd, pos);
|
||||
return;
|
||||
}
|
||||
++pos;
|
||||
int32_t gotten_page;
|
||||
if (!ArgParser::ParseInt(cmd, pos, gotten_page)) {
|
||||
gotten_page = 1; // asssume as 1
|
||||
}
|
||||
if (gotten_page <= 0) {
|
||||
this->PrintCommonError("Page out of range.");
|
||||
return;
|
||||
}
|
||||
size_t page = static_cast<size_t>(gotten_page) - 1;
|
||||
|
||||
// show list
|
||||
if (sw == c_AllowedSwitches[0]) {
|
||||
// obj list
|
||||
if (page * this->m_PageLen >= m_Doc->m_FileObjects.size()) {
|
||||
this->PrintCommonError("Page out of range.");
|
||||
return;
|
||||
}
|
||||
|
||||
Unvirt::StructFormatter::PrintObjectList(this->m_Doc->m_FileObjects, page, this->m_PageLen);
|
||||
|
||||
} else {
|
||||
// mgr list
|
||||
if (page * this->m_PageLen >= m_Doc->m_FileManagersData.size()) {
|
||||
this->PrintCommonError("Page out of range.");
|
||||
return;
|
||||
}
|
||||
|
||||
Unvirt::StructFormatter::PrintManagerList(this->m_Doc->m_FileManagersData, page, this->m_PageLen);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void InteractiveCmd::ProcItems(const std::deque<std::string>& cmd) {
|
||||
// check requirement
|
||||
size_t pos = 0u;
|
||||
int32_t count;
|
||||
if (!ArgParser::ParseInt(cmd, pos, count) || count <= 0) {
|
||||
this->PrintArgParseError(cmd, pos);
|
||||
return;
|
||||
}
|
||||
|
||||
// assign
|
||||
m_PageLen = static_cast<size_t>(count);
|
||||
}
|
||||
|
||||
void InteractiveCmd::ProcEncoding(const std::deque<std::string>& cmd) {
|
||||
// create list first
|
||||
std::vector<std::string> encoding_list;
|
||||
|
||||
// get list item
|
||||
size_t pos = 0u;
|
||||
while (true) {
|
||||
std::string pending;
|
||||
if (!ArgParser::ParseString(cmd, pos, pending)) {
|
||||
break; // no more encoding, break
|
||||
}
|
||||
|
||||
// add and move to next
|
||||
++pos;
|
||||
encoding_list.push_back(std::move(pending));
|
||||
}
|
||||
|
||||
// apply list
|
||||
this->m_Ctx->SetEncoding(encoding_list);
|
||||
}
|
||||
|
||||
void InteractiveCmd::ProcTemp(const std::deque<std::string>& cmd) {
|
||||
// check requirement
|
||||
size_t pos = 0u;
|
||||
std::string temppath;
|
||||
if (!ArgParser::ParseString(cmd, pos, temppath)) {
|
||||
this->PrintArgParseError(cmd, pos);
|
||||
return;
|
||||
}
|
||||
|
||||
// assign
|
||||
m_Ctx->SetTempPath(temppath.c_str());
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
}
|
||||
|
|
|
@ -1,26 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include <VTUtils.hpp>
|
||||
#include <CKDefines.hpp>
|
||||
#include <VTAll.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <deque>
|
||||
#include <unordered_map>
|
||||
#include <stdexcept>
|
||||
#include <cinttypes>
|
||||
#include <initializer_list>
|
||||
|
||||
namespace Unvirt::CmdHelper {
|
||||
|
||||
class CmdSplitter {
|
||||
public:
|
||||
CmdSplitter();
|
||||
CmdSplitter(const CmdSplitter&) = delete;
|
||||
CmdSplitter& operator=(const CmdSplitter&) = delete;
|
||||
~CmdSplitter();
|
||||
|
||||
const std::deque<std::string> Convert(const std::string& u8cmd);
|
||||
private:
|
||||
char mCmdChar;
|
||||
std::string* mBuffer;
|
||||
std::deque<std::string>* mResult;
|
||||
|
||||
enum class StateType : int {
|
||||
SPACE,
|
||||
SINGLE,
|
||||
|
@ -28,142 +21,312 @@ namespace Unvirt::CmdHelper {
|
|||
ESCAPE,
|
||||
NORMAL
|
||||
};
|
||||
public:
|
||||
CmdSplitter() :
|
||||
mCmdChar(0), mBuffer(nullptr), mResult(nullptr),
|
||||
mState(StateType::NORMAL), mPreState(StateType::NORMAL) {}
|
||||
~CmdSplitter() {}
|
||||
LIBCMO_DISABLE_COPY_MOVE(CmdSplitter);
|
||||
|
||||
std::deque<std::string> Convert(const std::string& u8cmd);
|
||||
protected:
|
||||
char mCmdChar;
|
||||
std::string* mBuffer;
|
||||
std::deque<std::string>* mResult;
|
||||
|
||||
StateType mState, mPreState;
|
||||
|
||||
inline void ProcSpace(void) {
|
||||
switch (mCmdChar) {
|
||||
case '\'':
|
||||
mState = StateType::SINGLE;
|
||||
break;
|
||||
case '"':
|
||||
mState = StateType::DOUBLE;
|
||||
break;
|
||||
case '\\':
|
||||
mState = StateType::ESCAPE;
|
||||
mPreState = StateType::NORMAL;
|
||||
break;
|
||||
case ' ':
|
||||
break; // skip blank
|
||||
default:
|
||||
mBuffer->push_back(mCmdChar);
|
||||
mState = StateType::NORMAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
inline void ProcSingle(void) {
|
||||
switch (mCmdChar) {
|
||||
case '\'':
|
||||
mState = StateType::NORMAL;
|
||||
break;
|
||||
case '"':
|
||||
mBuffer->push_back('"');
|
||||
break;
|
||||
case '\\':
|
||||
mState = StateType::ESCAPE;
|
||||
mPreState = StateType::SINGLE;
|
||||
break;
|
||||
case ' ':
|
||||
mBuffer->push_back(' ');
|
||||
break;
|
||||
default:
|
||||
mBuffer->push_back(mCmdChar);
|
||||
break;
|
||||
}
|
||||
}
|
||||
inline void ProcDouble(void) {
|
||||
switch (mCmdChar) {
|
||||
case '\'':
|
||||
mBuffer->push_back('\'');
|
||||
break;
|
||||
case '"':
|
||||
mState = StateType::NORMAL;
|
||||
break;
|
||||
case '\\':
|
||||
mState = StateType::ESCAPE;
|
||||
mPreState = StateType::DOUBLE;
|
||||
break;
|
||||
case ' ':
|
||||
mBuffer->push_back(' ');
|
||||
break;
|
||||
default:
|
||||
mBuffer->push_back(mCmdChar);
|
||||
break;
|
||||
}
|
||||
}
|
||||
inline void ProcEscape(void) {
|
||||
// add itself
|
||||
mBuffer->push_back(mCmdChar);
|
||||
// restore state
|
||||
mState = mPreState;
|
||||
}
|
||||
inline void ProcNormal(void) {
|
||||
switch (mCmdChar) {
|
||||
case '\'':
|
||||
mBuffer->push_back('\'');
|
||||
break;
|
||||
case '"':
|
||||
mBuffer->push_back('"');
|
||||
break;
|
||||
case '\\':
|
||||
mState = StateType::ESCAPE;
|
||||
mPreState = StateType::NORMAL;
|
||||
break;
|
||||
case ' ':
|
||||
mResult->push_back(*mBuffer);
|
||||
mBuffer->clear();
|
||||
mState = StateType::SPACE;
|
||||
break;
|
||||
default:
|
||||
mBuffer->push_back(mCmdChar);
|
||||
break;
|
||||
}
|
||||
}
|
||||
void ProcSpace(void);
|
||||
void ProcSingle(void);
|
||||
void ProcDouble(void);
|
||||
void ProcEscape(void);
|
||||
void ProcNormal(void);
|
||||
};
|
||||
|
||||
class ArgParser {
|
||||
class HelpDocument {
|
||||
public:
|
||||
ArgParser() {}
|
||||
ArgParser(const ArgParser&) = delete;
|
||||
ArgParser& operator=(const ArgParser&) = delete;
|
||||
~ArgParser() {}
|
||||
HelpDocument() : m_Stack(), m_Results() {}
|
||||
~HelpDocument() {}
|
||||
LIBCMO_DISABLE_COPY_MOVE(HelpDocument);
|
||||
|
||||
static bool ParseInt(const std::deque<std::string>& cmd, const size_t expected_index, int32_t& result);
|
||||
static bool ParseString(const std::deque<std::string>& cmd, const size_t expected_index, std::string& result);
|
||||
static bool ParseSwitch(const std::deque<std::string>& cmd, const size_t expected_index, const std::vector<std::string>& switches, std::string& gotten);
|
||||
void PushLiteral(const std::string& literal_name) {
|
||||
m_Stack.emplace_back(StackItem(literal_name, ""));
|
||||
}
|
||||
void PushArgument(const std::string& arg_name, const std::string& arg_desc) {
|
||||
m_Stack.emplace_back(StackItem(arg_name, arg_desc));
|
||||
}
|
||||
void Pop() {
|
||||
m_Stack.pop_back();
|
||||
}
|
||||
void Terminate(std::string& command_desc);
|
||||
void Print();
|
||||
|
||||
protected:
|
||||
struct StackItem {
|
||||
StackItem() : m_Name(), m_Desc() {}
|
||||
StackItem(const std::string& name, const std::string& desc) : m_Name(name), m_Desc(desc) {}
|
||||
LIBCMO_DEFAULT_COPY_MOVE(StackItem);
|
||||
std::string m_Name;
|
||||
std::string m_Desc;
|
||||
};
|
||||
std::deque<StackItem> m_Stack;
|
||||
struct ResultItem {
|
||||
ResultItem() : m_CmdDesc(), m_ArgDesc() {}
|
||||
ResultItem(const std::string& desc) : m_CmdDesc(desc), m_ArgDesc() {}
|
||||
LIBCMO_DEFAULT_COPY_MOVE(ResultItem);
|
||||
std::string m_CmdDesc;
|
||||
std::vector<StackItem> m_ArgDesc;
|
||||
};
|
||||
std::vector<ResultItem> m_Results;
|
||||
};
|
||||
|
||||
class InteractiveCmd {
|
||||
class AbstractArgument;
|
||||
class ArgumentsMap {
|
||||
public:
|
||||
InteractiveCmd();
|
||||
InteractiveCmd(const InteractiveCmd&) = delete;
|
||||
InteractiveCmd& operator=(const InteractiveCmd&) = delete;
|
||||
~InteractiveCmd();
|
||||
ArgumentsMap() : m_Data() {}
|
||||
~ArgumentsMap() {}
|
||||
LIBCMO_DISABLE_COPY_MOVE(ArgumentsMap);
|
||||
|
||||
void Run(void);
|
||||
void Add(const std::string& k, AbstractArgument* v) {
|
||||
m_Data.emplace(std::make_pair(k, v));
|
||||
}
|
||||
void Remove(const std::string& k) {
|
||||
m_Data.erase(k);
|
||||
}
|
||||
AbstractArgument* Get(const char* k) const {
|
||||
auto finder = m_Data.find(k);
|
||||
if (finder == m_Data.end()) throw std::invalid_argument("No such argument name.");
|
||||
return finder->second;
|
||||
}
|
||||
|
||||
private:
|
||||
void GetCmdLine(std::string& u8cmd);
|
||||
void PrintHelp(void);
|
||||
void PrintArgParseError(const std::deque<std::string>& cmd, size_t pos);
|
||||
void PrintCommonError(const char* u8_fmt, ...);
|
||||
|
||||
void ProcLoad(const std::deque<std::string>& cmd);
|
||||
void ProcUnLoad(const std::deque<std::string>& cmd);
|
||||
void ProcInfo(const std::deque<std::string>& cmd);
|
||||
void ProcLs(const std::deque<std::string>& cmd);
|
||||
void ProcItems(const std::deque<std::string>& cmd);
|
||||
void ProcEncoding(const std::deque<std::string>& cmd);
|
||||
void ProcTemp(const std::deque<std::string>& cmd);
|
||||
|
||||
bool HasOpenedFile(void);
|
||||
void ClearDocument(void);
|
||||
void PrintMinContextMsg(const std::string& msg);
|
||||
|
||||
CmdSplitter m_CmdSplitter;
|
||||
size_t m_PageLen;
|
||||
LibCmo::CK2::CKMinContext* m_Ctx;
|
||||
LibCmo::CK2::CKFile* m_File;
|
||||
LibCmo::CK2::CKFileDocument* m_Doc;
|
||||
protected:
|
||||
std::unordered_map<std::string, AbstractArgument*> m_Data;
|
||||
};
|
||||
|
||||
}
|
||||
class CommandRoot;
|
||||
using ExecutionFct = std::function<void(const ArgumentsMap&)>;
|
||||
class AbstractNode {
|
||||
friend class CommandRoot;
|
||||
public:
|
||||
AbstractNode() : m_Execution(nullptr), m_Nodes(), m_Comment() {}
|
||||
virtual ~AbstractNode() {
|
||||
for (auto& ptr : m_Nodes) {
|
||||
delete ptr;
|
||||
}
|
||||
}
|
||||
LIBCMO_DISABLE_COPY_MOVE(AbstractNode);
|
||||
|
||||
AbstractNode* Then(AbstractNode*);
|
||||
AbstractNode* Executes(ExecutionFct, const char*);
|
||||
|
||||
protected:
|
||||
void Help(HelpDocument&);
|
||||
bool Consume(std::deque<std::string>&, ArgumentsMap&);
|
||||
virtual bool IsLiteral() = 0;
|
||||
virtual bool IsConflictWith(AbstractNode*) = 0;
|
||||
virtual void BeginHelp(HelpDocument&) = 0;
|
||||
virtual void EndHelp(HelpDocument&) = 0;
|
||||
virtual bool BeginAccept(const std::string&, ArgumentsMap&) = 0;
|
||||
virtual void EndAccept(ArgumentsMap&) = 0;
|
||||
|
||||
std::vector<AbstractNode*> m_Nodes;
|
||||
ExecutionFct m_Execution;
|
||||
std::string m_Comment;
|
||||
};
|
||||
|
||||
class CommandRoot : public AbstractNode {
|
||||
public:
|
||||
CommandRoot() : AbstractNode() {}
|
||||
virtual ~CommandRoot() {}
|
||||
LIBCMO_DISABLE_COPY_MOVE(CommandRoot);
|
||||
|
||||
// Root use special consume and help functions.
|
||||
bool RootConsume(std::deque<std::string>&);
|
||||
HelpDocument* RootHelp();
|
||||
|
||||
protected:
|
||||
virtual bool IsLiteral() override { throw std::logic_error("Root can not be called."); }
|
||||
virtual bool IsConflictWith(AbstractNode*) override { throw std::logic_error("Root can not be called."); }
|
||||
virtual void BeginHelp(HelpDocument&) override { throw std::logic_error("Root can not be called."); }
|
||||
virtual void EndHelp(HelpDocument&) override { throw std::logic_error("Root can not be called."); }
|
||||
virtual bool BeginAccept(const std::string&, ArgumentsMap&) override { throw std::logic_error("Root can not be called."); }
|
||||
virtual void EndAccept(ArgumentsMap&) override { throw std::logic_error("Root can not be called."); }
|
||||
};
|
||||
|
||||
class Literal : public AbstractNode {
|
||||
friend class Choice;
|
||||
public:
|
||||
Literal(const char* words) : AbstractNode(), m_Literal(words) {
|
||||
if (m_Literal.empty() || words == nullptr) throw std::invalid_argument("Invalid literal.");
|
||||
}
|
||||
virtual ~Literal() {}
|
||||
LIBCMO_DISABLE_COPY_MOVE(Literal);
|
||||
|
||||
protected:
|
||||
virtual bool IsLiteral() override { return true; }
|
||||
virtual bool IsConflictWith(AbstractNode* node) override {
|
||||
Literal* pliteral = dynamic_cast<Literal*>(node);
|
||||
if (pliteral == nullptr) return false;
|
||||
return pliteral->m_Literal == this->m_Literal;
|
||||
}
|
||||
virtual void BeginHelp(HelpDocument& doc) override {
|
||||
doc.PushLiteral(m_Literal);
|
||||
}
|
||||
virtual void EndHelp(HelpDocument& doc) override {
|
||||
doc.Pop();
|
||||
}
|
||||
virtual bool BeginAccept(const std::string& strl, ArgumentsMap&) override { return strl == m_Literal; }
|
||||
virtual void EndAccept(ArgumentsMap&) override {}
|
||||
|
||||
std::string m_Literal;
|
||||
};
|
||||
|
||||
class Choice : public AbstractNode {
|
||||
public:
|
||||
Choice(const char* argname, const std::initializer_list<std::string>& vocabulary) :
|
||||
AbstractNode(), m_ChoiceName(argname == nullptr ? "" : argname), m_Vocabulary(vocabulary) {
|
||||
if (m_ChoiceName.empty() || argname == nullptr) throw std::invalid_argument("Invalid choice name.");
|
||||
if (m_Vocabulary.size() < 2) throw std::invalid_argument("Too less vocabulary. At least 2 items.");
|
||||
}
|
||||
virtual ~Choice() {}
|
||||
LIBCMO_DISABLE_COPY_MOVE(Choice)
|
||||
|
||||
protected:
|
||||
virtual bool IsLiteral() override { return true; }
|
||||
virtual bool IsConflictWith(AbstractNode* node) override {
|
||||
Literal* pliteral = dynamic_cast<Literal*>(node);
|
||||
if (pliteral != nullptr) {
|
||||
for (const auto& word : m_Vocabulary) {
|
||||
if (word == pliteral->m_Literal)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Choice* pchoice = dynamic_cast<Choice*>(node);
|
||||
if (pchoice != nullptr) {
|
||||
for (const auto& thisword : m_Vocabulary) {
|
||||
for (const auto& thatword : pchoice->m_Vocabulary) {
|
||||
if (thisword == thatword)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
virtual void BeginHelp(HelpDocument& doc) override {
|
||||
std::string switches;
|
||||
for (const auto& item : m_Vocabulary) {
|
||||
if (!switches.empty()) switches += " | ";
|
||||
switches += item;
|
||||
}
|
||||
doc.PushLiteral("[" + switches + "]");
|
||||
}
|
||||
virtual void EndHelp(HelpDocument& doc) override {
|
||||
doc.Pop();
|
||||
}
|
||||
virtual bool BeginAccept(const std::string& strl, ArgumentsMap& amap) override {
|
||||
for (const auto& item : m_Vocabulary) {
|
||||
if (strl == item) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void EndAccept(ArgumentsMap&) override {}
|
||||
|
||||
std::string m_ChoiceName;
|
||||
std::vector<std::string> m_Vocabulary;
|
||||
};
|
||||
|
||||
class AbstractArgument : public AbstractNode {
|
||||
public:
|
||||
AbstractArgument(const char* argname, const char* argdesc) : AbstractNode(),
|
||||
m_ArgName(argname == nullptr ? "" : argname), m_ArgDesc(argdesc), m_Accepted(false), m_ParsedData(nullptr) {
|
||||
if (m_ArgName.empty() || argname == nullptr) throw std::invalid_argument("Invalid argument name.");
|
||||
}
|
||||
virtual ~AbstractArgument() {}
|
||||
LIBCMO_DISABLE_COPY_MOVE(AbstractArgument);
|
||||
|
||||
template<class T>
|
||||
T* GetData() { return reinterpret_cast<T*>(m_ParsedData); }
|
||||
|
||||
protected:
|
||||
virtual bool BeginParse(const std::string&) = 0;
|
||||
virtual void EndParse() = 0;
|
||||
|
||||
virtual bool IsLiteral() override { return false; }
|
||||
virtual bool IsConflictWith(AbstractNode* node) override {
|
||||
return false;
|
||||
}
|
||||
virtual void BeginHelp(HelpDocument& doc) override {
|
||||
std::string newargname = "<";
|
||||
newargname.append(m_ArgName);
|
||||
newargname.append(">");
|
||||
doc.PushArgument(newargname, m_ArgDesc);
|
||||
}
|
||||
virtual void EndHelp(HelpDocument& doc) override {
|
||||
doc.Pop();
|
||||
}
|
||||
virtual bool BeginAccept(const std::string& strl, ArgumentsMap& amap) override {
|
||||
m_Accepted = BeginParse(strl);
|
||||
if (m_Accepted) amap.Add(m_ArgName, this);
|
||||
return m_Accepted;
|
||||
}
|
||||
virtual void EndAccept(ArgumentsMap& amap) override {
|
||||
if (m_Accepted) {
|
||||
amap.Remove(m_ArgName);
|
||||
EndParse();
|
||||
m_Accepted = false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string m_ArgName;
|
||||
std::string m_ArgDesc;
|
||||
bool m_Accepted;
|
||||
void* m_ParsedData;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Return true mean this value can accept.
|
||||
*/
|
||||
using IntLimit = std::function<bool(int32_t)>;
|
||||
class IntArgument : public AbstractArgument {
|
||||
public:
|
||||
IntArgument(const char* argname, const char* argdesc = nullptr, IntLimit limit = nullptr) :
|
||||
AbstractArgument(argname, argdesc), m_IntLimit(limit) {}
|
||||
virtual ~IntArgument() {}
|
||||
LIBCMO_DISABLE_COPY_MOVE(IntArgument);
|
||||
|
||||
protected:
|
||||
virtual bool BeginParse(const std::string&) override;
|
||||
virtual void EndParse() override;
|
||||
|
||||
IntLimit m_IntLimit;
|
||||
};
|
||||
|
||||
class StringArgument : public AbstractArgument {
|
||||
public:
|
||||
StringArgument(const char* argname, const char* argdesc = nullptr) : AbstractArgument(argname, argdesc) {}
|
||||
virtual ~StringArgument() {}
|
||||
LIBCMO_DISABLE_COPY_MOVE(StringArgument);
|
||||
|
||||
protected:
|
||||
virtual bool BeginParse(const std::string&) override;
|
||||
virtual void EndParse() override;
|
||||
};
|
||||
|
||||
class EncodingArgument : public AbstractArgument {
|
||||
public:
|
||||
EncodingArgument(const char* argname, const char* argdesc = nullptr) : AbstractArgument(argname, argdesc) {}
|
||||
virtual ~EncodingArgument() {}
|
||||
LIBCMO_DISABLE_COPY_MOVE(EncodingArgument);
|
||||
|
||||
protected:
|
||||
virtual bool BeginParse(const std::string&) override;
|
||||
virtual void EndParse() override;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,32 +1,4 @@
|
|||
#include "AccessibleValue.hpp"
|
||||
#include "TerminalHelper.hpp"
|
||||
#include "StructFormatter.hpp"
|
||||
#include "CmdHelper.hpp"
|
||||
|
||||
#include <VTAll.hpp>
|
||||
#include <CK2/CKContext.hpp>
|
||||
#include <CK2/CKFile.hpp>
|
||||
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
|
||||
namespace Unvirt {
|
||||
|
||||
class UnvirtContext {
|
||||
public:
|
||||
UnvirtContext() {}
|
||||
~UnvirtContext() {}
|
||||
|
||||
void Run() {
|
||||
Unvirt::TerminalHelper::EnsureTerminalColor();
|
||||
Unvirt::TerminalHelper::EnsureTerminalEncoding();
|
||||
|
||||
}
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
#include "UnvirtContext.hpp"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
|
||||
|
|
|
@ -181,6 +181,7 @@
|
|||
<ClCompile Include="TerminalHelper.cpp" />
|
||||
<ClCompile Include="StringHelper.cpp" />
|
||||
<ClCompile Include="Unvirt.cpp" />
|
||||
<ClCompile Include="UnvirtContext.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="AccessibleValue.hpp" />
|
||||
|
@ -188,6 +189,7 @@
|
|||
<ClInclude Include="StructFormatter.hpp" />
|
||||
<ClInclude Include="TerminalHelper.hpp" />
|
||||
<ClInclude Include="StringHelper.hpp" />
|
||||
<ClInclude Include="UnvirtContext.hpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
|
|
@ -33,6 +33,9 @@
|
|||
<ClCompile Include="StructFormatter.cpp">
|
||||
<Filter>Sources</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="UnvirtContext.cpp">
|
||||
<Filter>Sources</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="AccessibleValue.hpp">
|
||||
|
@ -50,5 +53,8 @@
|
|||
<ClInclude Include="StructFormatter.hpp">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="UnvirtContext.hpp">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
298
Unvirt/UnvirtContext.cpp
Normal file
298
Unvirt/UnvirtContext.cpp
Normal file
|
@ -0,0 +1,298 @@
|
|||
#include "UnvirtContext.hpp"
|
||||
|
||||
namespace Unvirt::Context {
|
||||
|
||||
static FILE* fout = stdout;
|
||||
|
||||
#pragma region Encoding Arg
|
||||
|
||||
// Copy from Gamepiaynmo/BallanceModLoader
|
||||
std::vector<std::string> SplitString(const std::string& str, const std::string& de) {
|
||||
size_t lpos, pos = 0;
|
||||
std::vector<std::string> res;
|
||||
|
||||
lpos = str.find_first_not_of(de, pos);
|
||||
while (lpos != std::string::npos) {
|
||||
pos = str.find_first_of(de, lpos);
|
||||
res.push_back(str.substr(lpos, pos - lpos));
|
||||
if (pos == std::string::npos) break;
|
||||
|
||||
lpos = str.find_first_not_of(de, pos);
|
||||
}
|
||||
|
||||
if (pos != std::string::npos)
|
||||
res.push_back("");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool EncodingArgument::BeginParse(const std::string& strl) {
|
||||
// encoding always accept every text
|
||||
m_ParsedData = new std::vector<std::string>(SplitString(strl, ","));
|
||||
return true;
|
||||
}
|
||||
|
||||
void EncodingArgument::EndParse() {
|
||||
delete reinterpret_cast<std::vector<std::string>*>(m_ParsedData);
|
||||
m_ParsedData = nullptr;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region UnvirtContext Misc
|
||||
|
||||
UnvirtContext::UnvirtContext() :
|
||||
m_Root(), m_Splitter(), m_Help(nullptr),
|
||||
m_PageLen(10u), m_Ctx(nullptr), m_FileReader(nullptr) {
|
||||
|
||||
// create command root
|
||||
CmdHelper::CommandRoot* root = &m_Root;
|
||||
root->Then(
|
||||
(new CmdHelper::Literal("deepload"))->Then(
|
||||
(new CmdHelper::StringArgument("filepath", "The path to loading file."))->Executes(
|
||||
std::bind(&UnvirtContext::ProcLoad, this, true, std::placeholders::_1),
|
||||
"Load a Virtools composition deeply. Load file to CKObject stage."
|
||||
)
|
||||
)
|
||||
);
|
||||
root->Then(
|
||||
(new CmdHelper::Literal("shallowload"))->Then(
|
||||
(new CmdHelper::StringArgument("filepath", "The path to loading file."))->Executes(
|
||||
std::bind(&UnvirtContext::ProcLoad, this, false, std::placeholders::_1),
|
||||
"Load a Virtools composition shallowly. Load file to CKStateChunk stage."
|
||||
)
|
||||
)
|
||||
);
|
||||
root->Then(
|
||||
(new CmdHelper::Literal("unload"))->Executes(
|
||||
std::bind(&UnvirtContext::ProcUnLoad, this, std::placeholders::_1),
|
||||
"Release loaded Virtools composition."
|
||||
)
|
||||
);
|
||||
root->Then(
|
||||
(new CmdHelper::Literal("info"))->Executes(
|
||||
std::bind(&UnvirtContext::ProcInfo, this, std::placeholders::_1),
|
||||
"Show the header info of loaded Virtools composition."
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
root->Then(
|
||||
(new CmdHelper::Literal("foo"))->Then(
|
||||
(new CmdHelper::Literal("bar"))->Executes([](const CmdHelper::ArgumentsMap& amap) -> void {
|
||||
fprintf(stdout, "foobar!\n");
|
||||
}, "simple foobar")
|
||||
)->Then(
|
||||
(new CmdHelper::IntArgument("bar", "the calling target 1"))->Executes([](const CmdHelper::ArgumentsMap& amap) -> void {
|
||||
fprintf(stdout, "foo%" PRIi32 "!\n", *(amap.Get("bar")->GetData<int32_t>()));
|
||||
}, "specialized foo bar")
|
||||
)
|
||||
)->Then(
|
||||
(new CmdHelper::Literal("call"))->Then(
|
||||
(new CmdHelper::IntArgument("bar", "the calling taget 2"))->Executes([](const CmdHelper::ArgumentsMap& amap) -> void {
|
||||
fprintf(stdout, "call%" PRIi32 "!\n", *(amap.Get("bar")->GetData<int32_t>()));
|
||||
}, "calling someone")
|
||||
)
|
||||
);
|
||||
// create help
|
||||
m_Help = root->RootHelp();
|
||||
|
||||
// create context
|
||||
m_Ctx = new LibCmo::CK2::CKContext();
|
||||
m_Ctx->SetOutputCallback(std::bind(&UnvirtContext::PrintContextMsg, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
UnvirtContext::~UnvirtContext() {
|
||||
if (m_Help != nullptr)
|
||||
delete m_Help;
|
||||
}
|
||||
|
||||
bool UnvirtContext::HasOpenedFile() {
|
||||
return m_FileReader != nullptr;
|
||||
}
|
||||
|
||||
void UnvirtContext::ClearDocument() {
|
||||
if (m_FileReader == nullptr) return;
|
||||
delete m_FileReader;
|
||||
m_FileReader = nullptr;
|
||||
}
|
||||
|
||||
void UnvirtContext::PrintContextMsg(LibCmo::CK2::CKSTRING msg) {
|
||||
if (msg != nullptr) {
|
||||
fprintf(fout, UNVIRT_TERMCOL_LIGHT_YELLOW(("[CKContext] ")) "%s\n", msg);
|
||||
}
|
||||
}
|
||||
|
||||
void UnvirtContext::PrintCommonError(const char* u8_fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, u8_fmt);
|
||||
std::fputs(UNVIRT_TERMCOLHDR_LIGHT_RED, fout);
|
||||
std::vfprintf(fout, u8_fmt, argptr);
|
||||
std::fputs(UNVIRT_TERMCOLTAIL, fout);
|
||||
va_end(argptr);
|
||||
std::fputc('\n', fout);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Main Run
|
||||
|
||||
void UnvirtContext::Run() {
|
||||
Unvirt::TerminalHelper::EnsureTerminalColor();
|
||||
Unvirt::TerminalHelper::EnsureTerminalEncoding();
|
||||
std::string u8cmd;
|
||||
|
||||
while (true) {
|
||||
// get command
|
||||
TerminalHelper::GetCmdLine(u8cmd);
|
||||
|
||||
// split cmd and parse it
|
||||
auto cmds = m_Splitter.Convert(u8cmd);
|
||||
|
||||
// get sub command
|
||||
if (!m_Root.RootConsume(cmds)) {
|
||||
this->PrintCommonError("Command syntax error \"%s\".", u8cmd.c_str());
|
||||
m_Help->Print();
|
||||
}
|
||||
|
||||
if (m_OrderExit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Proc Detail
|
||||
|
||||
void UnvirtContext::ProcLoad(bool is_deep, const CmdHelper::ArgumentsMap& amap) {
|
||||
// check pre-requirement
|
||||
if (HasOpenedFile()) {
|
||||
PrintCommonError("Already have a opened file. Close it before calling \"load\".");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string filepath = *amap.Get("filepath")->GetData<std::string>();
|
||||
|
||||
// proc
|
||||
m_FileReader = new LibCmo::CK2::CKFileReader(m_Ctx);
|
||||
LibCmo::CK2::CKERROR err;
|
||||
if (is_deep) {
|
||||
err = m_FileReader->DeepLoad(filepath.c_str());
|
||||
} else {
|
||||
err = m_FileReader->ShallowLoad(filepath.c_str());
|
||||
}
|
||||
if (err != LibCmo::CK2::CKERROR::CKERR_OK) {
|
||||
// fail to load. release all.
|
||||
PrintCommonError("Fail to open file. Function return: %s\n\t%s",
|
||||
Unvirt::AccessibleValue::GetCkErrorName(err).c_str(),
|
||||
Unvirt::AccessibleValue::GetCkErrorDescription(err).c_str()
|
||||
);
|
||||
|
||||
ClearDocument();
|
||||
}
|
||||
}
|
||||
|
||||
void UnvirtContext::ProcUnLoad(const CmdHelper::ArgumentsMap& amap) {
|
||||
// check pre-requirement
|
||||
if (!HasOpenedFile()) {
|
||||
this->PrintCommonError("No loaded file.");
|
||||
return;
|
||||
}
|
||||
|
||||
// free all
|
||||
ClearDocument();
|
||||
}
|
||||
|
||||
void UnvirtContext::ProcInfo(const CmdHelper::ArgumentsMap& amap) {
|
||||
// check pre-requirement
|
||||
if (!HasOpenedFile()) {
|
||||
PrintCommonError("No loaded file.");
|
||||
return;
|
||||
}
|
||||
|
||||
// print
|
||||
Unvirt::StructFormatter::PrintCKFileInfo(m_FileReader->GetFileInfo());
|
||||
}
|
||||
|
||||
void UnvirtContext::ProcLs(ViewPart parts, const CmdHelper::ArgumentsMap& amap) {
|
||||
// check pre-requirement
|
||||
if (!HasOpenedFile()) {
|
||||
this->PrintCommonError("No loaded file.");
|
||||
return;
|
||||
}
|
||||
|
||||
// check requirement
|
||||
int32_t gotten_page = *amap.Get("page")->GetData<int32_t>();
|
||||
if (gotten_page <= 0) {
|
||||
PrintCommonError("Page out of range.");
|
||||
return;
|
||||
}
|
||||
size_t page = static_cast<size_t>(gotten_page) - 1;
|
||||
|
||||
// show list
|
||||
switch (parts) {
|
||||
case ViewPart::Objects:
|
||||
{
|
||||
// obj list
|
||||
if (page * m_PageLen >= m_FileReader->GetFileObjects().size()) {
|
||||
PrintCommonError("Page out of range.");
|
||||
return;
|
||||
}
|
||||
|
||||
Unvirt::StructFormatter::PrintObjectList(m_FileReader->GetFileObjects(), page, this->m_PageLen);
|
||||
break;
|
||||
}
|
||||
case ViewPart::Managers:
|
||||
{
|
||||
// mgr list
|
||||
if (page * m_PageLen >= m_FileReader->GetManagersData().size()) {
|
||||
PrintCommonError("Page out of range.");
|
||||
return;
|
||||
}
|
||||
|
||||
Unvirt::StructFormatter::PrintManagerList(m_FileReader->GetManagersData(), page, this->m_PageLen);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UnvirtContext::ProcData(ViewPart parts, const CmdHelper::ArgumentsMap& amap) {
|
||||
|
||||
}
|
||||
|
||||
void UnvirtContext::ProcChunk(ViewPart parts, const CmdHelper::ArgumentsMap& amap) {
|
||||
|
||||
}
|
||||
|
||||
void UnvirtContext::ProcItems(const CmdHelper::ArgumentsMap& amap) {
|
||||
// check requirement
|
||||
int32_t count = *amap.Get("count")->GetData<int32_t>();
|
||||
if (count <= 0) {
|
||||
PrintCommonError("Value out of range.");
|
||||
return;
|
||||
}
|
||||
|
||||
// assign
|
||||
m_PageLen = static_cast<size_t>(count);
|
||||
}
|
||||
|
||||
void UnvirtContext::ProcEncoding(const CmdHelper::ArgumentsMap& amap) {
|
||||
|
||||
}
|
||||
|
||||
void UnvirtContext::ProcTemp(const CmdHelper::ArgumentsMap& amap) {
|
||||
// check requirement
|
||||
std::string temppath = *amap.Get("temppath")->GetData<std::string>();
|
||||
|
||||
// assign
|
||||
m_Ctx->SetTempPath(temppath.c_str());
|
||||
}
|
||||
|
||||
void UnvirtContext::ProcExit(const CmdHelper::ArgumentsMap& amap) {
|
||||
m_OrderExit = true;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
}
|
57
Unvirt/UnvirtContext.hpp
Normal file
57
Unvirt/UnvirtContext.hpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
#pragma once
|
||||
#include "AccessibleValue.hpp"
|
||||
#include "TerminalHelper.hpp"
|
||||
#include "StructFormatter.hpp"
|
||||
#include "CmdHelper.hpp"
|
||||
|
||||
#include <VTAll.hpp>
|
||||
#include <CK2/CKContext.hpp>
|
||||
#include <CK2/CKFile.hpp>
|
||||
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
|
||||
namespace Unvirt::Context {
|
||||
|
||||
class UnvirtContext {
|
||||
public:
|
||||
UnvirtContext();
|
||||
~UnvirtContext();
|
||||
LIBCMO_DISABLE_COPY_MOVE(UnvirtContext);
|
||||
|
||||
void Run();
|
||||
|
||||
protected:
|
||||
enum class ViewPart {
|
||||
Objects, Managers
|
||||
};
|
||||
void PrintCommonError(const char* u8_fmt, ...);
|
||||
|
||||
void ProcLoad(bool is_deep, const CmdHelper::ArgumentsMap& amap);
|
||||
void ProcUnLoad(const CmdHelper::ArgumentsMap& amap);
|
||||
void ProcInfo(const CmdHelper::ArgumentsMap& amap);
|
||||
void ProcLs(ViewPart parts, const CmdHelper::ArgumentsMap& amap);
|
||||
void ProcData(ViewPart parts, const CmdHelper::ArgumentsMap& amap);
|
||||
void ProcChunk(ViewPart parts, const CmdHelper::ArgumentsMap& amap);
|
||||
void ProcItems(const CmdHelper::ArgumentsMap& amap);
|
||||
void ProcEncoding(const CmdHelper::ArgumentsMap& amap);
|
||||
void ProcTemp(const CmdHelper::ArgumentsMap& amap);
|
||||
void ProcExit(const CmdHelper::ArgumentsMap& amap);
|
||||
|
||||
protected:
|
||||
bool HasOpenedFile();
|
||||
void ClearDocument();
|
||||
void PrintContextMsg(LibCmo::CK2::CKSTRING msg);
|
||||
|
||||
CmdHelper::CommandRoot m_Root;
|
||||
CmdHelper::HelpDocument* m_Help;
|
||||
CmdHelper::CmdSplitter m_Splitter;
|
||||
|
||||
size_t m_PageLen;
|
||||
bool m_OrderExit;
|
||||
LibCmo::CK2::CKContext* m_Ctx;
|
||||
LibCmo::CK2::CKFileReader* m_FileReader;
|
||||
};
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user