libcmo21/Unvirt/UnvirtContext.cpp

410 lines
11 KiB
C++
Raw Normal View History

2023-08-27 12:30:12 +08:00
#include "UnvirtContext.hpp"
2023-08-31 12:37:23 +08:00
#include <cstdarg>
2023-08-27 12:30:12 +08:00
namespace Unvirt::Context {
#pragma region UnvirtContext Misc
UnvirtContext::UnvirtContext() :
m_Root(), m_Splitter(), m_Help(nullptr),
2023-08-27 14:21:44 +08:00
m_PageLen(10u), m_OrderExit(false),
m_Ctx(nullptr), m_FileReader(nullptr) {
2023-08-27 12:30:12 +08:00
// create command root
CmdHelper::CommandRoot* root = &m_Root;
2023-08-27 14:21:44 +08:00
2023-08-27 16:45:07 +08:00
root
->Then((new CmdHelper::Literal("load"))
->Then((new CmdHelper::Choice("stage", { "deep", "shallow"}))
->Comment("The stage of loading. 'deep' will load to CKObject stage. 'shallow' will load to CKStateChunk stage.")
->Then((new CmdHelper::StringArgument("filepath"))
->Comment("The path to loading file.")
->Executes(
std::bind(&UnvirtContext::ProcLoad, this, std::placeholders::_1),
"Load a Virtools composition deeply."
)
)
)
)
->Then((new CmdHelper::Literal("unload"))
2023-08-27 14:21:44 +08:00
->Executes(
2023-08-27 16:45:07 +08:00
std::bind(&UnvirtContext::ProcUnLoad, this, std::placeholders::_1),
"Release loaded Virtools composition."
)
2023-08-27 12:30:12 +08:00
)
->Then((new CmdHelper::Literal("save"))
->Then((new CmdHelper::StringArgument("filepath"))
->Comment("The path to save file.")
->Executes(
std::bind(&UnvirtContext::ProcSave, this, std::placeholders::_1),
"Save the loaded file into a new file."
)
)
)
2023-08-27 16:45:07 +08:00
->Then((new CmdHelper::Literal("info"))
->Executes(
std::bind(&UnvirtContext::ProcInfo, this, std::placeholders::_1),
"Show the header info of loaded Virtools composition."
)
2023-08-27 12:30:12 +08:00
)
2023-08-27 16:45:07 +08:00
->Then((new CmdHelper::Literal("ls"))
->Then((new CmdHelper::Choice("part", { "obj", "mgr"}))
->Comment("Which list you want to list.")
->Then((new CmdHelper::IntArgument("page", [](int32_t v) -> bool { return v > 0; }))
->Comment("The page index. Start with 1.")
->Executes(
std::bind(&UnvirtContext::ProcLs, this, std::placeholders::_1),
"List something about loaded Virtools composition."
)
)
)
2023-08-27 12:30:12 +08:00
)
2023-08-27 16:45:07 +08:00
->Then((new CmdHelper::Literal("data"))
->Then((new CmdHelper::Choice("part", { "obj", "mgr"}))
->Comment("Which list you want to show data.")
->Then((new CmdHelper::IntArgument("index", [](int32_t v) -> bool { return v >= 0; }))
->Comment("The index. Start with 0.")
->Executes(
std::bind(&UnvirtContext::ProcData, this, std::placeholders::_1),
"Show the specific CKObject / CKBaseManager data."
)
)
)
2023-08-27 12:30:12 +08:00
)
2023-08-27 16:45:07 +08:00
->Then((new CmdHelper::Literal("chunk"))
->Then((new CmdHelper::Choice("part", { "obj", "mgr"}))
->Comment("Which list you want to show chunk.")
->Then((new CmdHelper::IntArgument("index", [](int32_t v) -> bool { return v >= 0; }))
->Comment("The index. Start with 0.")
->Executes(
std::bind(&UnvirtContext::ProcChunk, this, std::placeholders::_1),
"Show the specific CKStateChunk data."
)
)
)
2023-08-27 12:30:12 +08:00
)
2023-08-27 16:45:07 +08:00
->Then((new CmdHelper::Literal("items"))
->Then((new CmdHelper::IntArgument("count", [](int32_t v) -> bool { return v > 0; }))
->Comment("The count of items you want to show in one page.")
->Executes(
std::bind(&UnvirtContext::ProcItems, this, std::placeholders::_1),
"Set up how many items should be listed in one page when using 'ls' command."
)
)
2023-08-27 12:30:12 +08:00
)
2023-08-27 16:45:07 +08:00
->Then((new CmdHelper::Literal("encoding"))
->Then((new CmdHelper::EncodingArgument("enc"))
->Comment("CKContext used encoding splitted by ','. Support mutiple encoding.")
->Executes(
std::bind(&UnvirtContext::ProcEncoding, this, std::placeholders::_1),
"Set the encoding series for CKContext."
)
)
2023-08-27 14:21:44 +08:00
)
2023-08-27 16:45:07 +08:00
->Then((new CmdHelper::Literal("temp"))
->Then((new CmdHelper::StringArgument("temppath"))
->Comment("The path to Temp folder.")
->Executes(
std::bind(&UnvirtContext::ProcTemp, this, std::placeholders::_1),
"Set the Temp path for CKContext."
2023-08-27 14:21:44 +08:00
)
2023-08-27 16:45:07 +08:00
)
)
2023-08-27 22:14:02 +08:00
->Then((new CmdHelper::Literal("help"))
->Executes(
std::bind(&UnvirtContext::ProcHelp, this, std::placeholders::_1),
"Output this help page."
)
)
2023-08-27 16:51:18 +08:00
->Then((new CmdHelper::Literal("exit"))
2023-08-27 16:45:07 +08:00
->Executes(
std::bind(&UnvirtContext::ProcExit, this, std::placeholders::_1),
"Exit program."
)
);
2023-08-27 14:21:44 +08:00
2023-08-27 16:45:07 +08:00
// 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));
2023-08-27 12:30:12 +08:00
}
UnvirtContext::~UnvirtContext() {
if (m_Help != nullptr)
delete m_Help;
}
bool UnvirtContext::HasOpenedFile() {
return m_FileReader != nullptr;
}
void UnvirtContext::ClearDocument() {
if (m_FileReader == nullptr) return;
2023-08-27 22:14:02 +08:00
// delete reader
2023-08-27 12:30:12 +08:00
delete m_FileReader;
m_FileReader = nullptr;
2023-08-27 22:14:02 +08:00
// clear context
m_Ctx->DestroyAllCKObjects();
2023-08-27 12:30:12 +08:00
}
void UnvirtContext::PrintContextMsg(LibCmo::CK2::CKSTRING msg) {
if (msg != nullptr) {
2023-08-27 22:14:02 +08:00
fprintf(stdout, UNVIRT_TERMCOL_LIGHT_YELLOW(("[CKContext] ")) "%s\n", msg);
2023-08-27 12:30:12 +08:00
}
}
void UnvirtContext::PrintCommonError(const char* u8_fmt, ...) {
va_list argptr;
va_start(argptr, u8_fmt);
2023-08-27 22:14:02 +08:00
std::fputs(UNVIRT_TERMCOLHDR_LIGHT_RED, stdout);
std::vfprintf(stdout, u8_fmt, argptr);
std::fputs(UNVIRT_TERMCOLTAIL, stdout);
2023-08-27 12:30:12 +08:00
va_end(argptr);
2023-08-27 22:14:02 +08:00
std::fputc('\n', stdout);
2023-08-27 12:30:12 +08:00
}
#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)) {
2023-08-27 22:14:02 +08:00
this->PrintCommonError("Syntax error \"%s\".\nType 'help' for usage.", u8cmd.c_str());
2023-08-27 12:30:12 +08:00
}
if (m_OrderExit) {
break;
}
}
}
#pragma endregion
#pragma region Proc Detail
2023-08-27 14:21:44 +08:00
void UnvirtContext::ProcLoad(const CmdHelper::ArgumentsMap* amap) {
2023-08-27 12:30:12 +08:00
// check pre-requirement
if (HasOpenedFile()) {
2023-08-27 22:14:02 +08:00
PrintCommonError("Already have a opened file. Close it before calling 'load'.");
2023-08-27 12:30:12 +08:00
return;
}
2023-08-27 14:21:44 +08:00
std::string filepath = *amap->Get<CmdHelper::StringArgument::vType>("filepath");
bool is_deep = *amap->Get<CmdHelper::Choice::vType>("stage") == 0;
2023-08-27 12:30:12 +08:00
// 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 load file. Function return: %s\n\t%s",
2023-08-27 12:30:12 +08:00
Unvirt::AccessibleValue::GetCkErrorName(err).c_str(),
Unvirt::AccessibleValue::GetCkErrorDescription(err).c_str()
);
ClearDocument();
}
}
2023-08-27 16:45:07 +08:00
void UnvirtContext::ProcUnLoad(const CmdHelper::ArgumentsMap*) {
2023-08-27 12:30:12 +08:00
// check pre-requirement
if (!HasOpenedFile()) {
this->PrintCommonError("No loaded file.");
return;
}
// free all
ClearDocument();
}
void Unvirt::Context::UnvirtContext::ProcSave(const CmdHelper::ArgumentsMap* amap) {
// check pre-requirement
if (!HasOpenedFile()) {
PrintCommonError("No loaded file.");
return;
}
std::string filepath = *amap->Get<CmdHelper::StringArgument::vType>("filepath");
// construct writer from reader
LibCmo::CK2::CKFileWriter writer(m_Ctx, m_FileReader);
// backup current file write mode
// and replace it with reader
LibCmo::CK2::CK_FILE_WRITEMODE backup = m_Ctx->GetFileWriteMode();
m_Ctx->SetFileWriteMode(m_FileReader->GetFileInfo().FileWriteMode);
// run writer
LibCmo::CK2::CKERROR err = writer.Save(filepath.c_str());
if (err != LibCmo::CK2::CKERROR::CKERR_OK) {
// fail to load. release all.
PrintCommonError("Fail to save file. Function return: %s\n\t%s",
Unvirt::AccessibleValue::GetCkErrorName(err).c_str(),
Unvirt::AccessibleValue::GetCkErrorDescription(err).c_str()
);
}
// restore file write mode
m_Ctx->SetFileWriteMode(backup);
}
2023-08-27 16:45:07 +08:00
void UnvirtContext::ProcInfo(const CmdHelper::ArgumentsMap*) {
2023-08-27 12:30:12 +08:00
// check pre-requirement
if (!HasOpenedFile()) {
PrintCommonError("No loaded file.");
return;
}
// print
Unvirt::StructFormatter::PrintCKFileInfo(m_FileReader->GetFileInfo());
}
2023-08-27 14:21:44 +08:00
void UnvirtContext::ProcLs(const CmdHelper::ArgumentsMap* amap) {
2023-08-27 12:30:12 +08:00
// check pre-requirement
if (!HasOpenedFile()) {
2023-08-27 22:14:02 +08:00
PrintCommonError("No loaded file.");
2023-08-27 12:30:12 +08:00
return;
}
2023-08-27 16:45:07 +08:00
// get page
size_t page = *amap->Get<CmdHelper::IntArgument::vType>("page") - 1;
2023-08-27 12:30:12 +08:00
// show list
2023-08-27 16:45:07 +08:00
switch (*amap->Get<CmdHelper::Choice::vType>("part")) {
case 0:
2023-08-27 12:30:12 +08:00
{
// obj list
if (page * m_PageLen >= m_FileReader->GetFileObjects().size()) {
PrintCommonError("Page out of range.");
return;
}
2023-08-29 10:42:13 +08:00
Unvirt::StructFormatter::PrintObjectList(m_FileReader->GetFileObjects(), m_FileReader->GetFileInfo(), page, this->m_PageLen);
2023-08-27 12:30:12 +08:00
break;
}
2023-08-27 16:45:07 +08:00
case 1:
2023-08-27 12:30:12 +08:00
{
// 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;
}
}
}
2023-08-27 14:21:44 +08:00
void UnvirtContext::ProcData( const CmdHelper::ArgumentsMap* amap) {
2023-08-27 16:45:07 +08:00
// check pre-requirement
if (!HasOpenedFile()) {
2023-08-27 22:14:02 +08:00
PrintCommonError("No loaded file.");
2023-08-27 16:45:07 +08:00
return;
}
2023-08-27 12:30:12 +08:00
2023-08-27 16:45:07 +08:00
// get index
size_t index = *amap->Get<CmdHelper::IntArgument::vType>("index");
// show data
switch (*amap->Get<CmdHelper::Choice::vType>("part")) {
case 0:
{
if (index >= m_FileReader->GetFileObjects().size()) {
PrintCommonError("Index out of range.");
return;
}
Unvirt::StructFormatter::PrintCKObject(m_FileReader->GetFileObjects()[index].ObjPtr);
break;
}
case 1:
{
if (index >= m_FileReader->GetManagersData().size()) {
PrintCommonError("Index out of range.");
return;
}
2023-08-27 22:14:02 +08:00
PrintCommonError("WIP function.");
2023-08-27 16:45:07 +08:00
//Unvirt::StructFormatter::PrintCKBaseManager(m_FileReader->GetManagersData()[index].Data);
break;
}
}
2023-08-27 12:30:12 +08:00
}
2023-08-27 14:21:44 +08:00
void UnvirtContext::ProcChunk(const CmdHelper::ArgumentsMap* amap) {
2023-08-27 16:45:07 +08:00
// check pre-requirement
if (!HasOpenedFile()) {
2023-08-27 22:14:02 +08:00
PrintCommonError("No loaded file.");
2023-08-27 16:45:07 +08:00
return;
}
2023-08-27 12:30:12 +08:00
2023-08-27 16:45:07 +08:00
// get index
size_t index = *amap->Get<CmdHelper::IntArgument::vType>("index");
2023-08-27 12:30:12 +08:00
2023-08-27 16:45:07 +08:00
// show data
switch (*amap->Get<CmdHelper::Choice::vType>("part")) {
case 0:
{
if (index >= m_FileReader->GetFileObjects().size()) {
PrintCommonError("Index out of range.");
return;
}
Unvirt::StructFormatter::PrintCKStateChunk(m_FileReader->GetFileObjects()[index].Data);
break;
}
case 1:
{
if (index >= m_FileReader->GetManagersData().size()) {
PrintCommonError("Index out of range.");
return;
}
Unvirt::StructFormatter::PrintCKStateChunk(m_FileReader->GetManagersData()[index].Data);
break;
}
2023-08-27 12:30:12 +08:00
}
2023-08-27 16:45:07 +08:00
}
2023-08-27 12:30:12 +08:00
2023-08-27 16:45:07 +08:00
void UnvirtContext::ProcItems(const CmdHelper::ArgumentsMap* amap) {
2023-08-27 12:30:12 +08:00
// assign
2023-08-27 16:45:07 +08:00
m_PageLen = *amap->Get<CmdHelper::IntArgument::vType>("count");
2023-08-27 12:30:12 +08:00
}
2023-08-27 14:21:44 +08:00
void UnvirtContext::ProcEncoding(const CmdHelper::ArgumentsMap* amap) {
2023-08-27 16:45:07 +08:00
const auto& encodings = *amap->Get<CmdHelper::EncodingArgument::vType>("enc");
m_Ctx->SetEncoding(encodings);
2023-08-27 12:30:12 +08:00
}
2023-08-27 14:21:44 +08:00
void UnvirtContext::ProcTemp(const CmdHelper::ArgumentsMap* amap) {
2023-08-27 12:30:12 +08:00
// assign
2023-08-27 16:45:07 +08:00
m_Ctx->SetTempPath(amap->Get<CmdHelper::StringArgument::vType>("temppath")->c_str());
2023-08-27 12:30:12 +08:00
}
void Unvirt::Context::UnvirtContext::ProcHelp(const CmdHelper::ArgumentsMap*) {
2023-08-27 22:14:02 +08:00
m_Help->Print();
}
2023-08-27 16:45:07 +08:00
void UnvirtContext::ProcExit(const CmdHelper::ArgumentsMap*) {
2023-08-27 12:30:12 +08:00
m_OrderExit = true;
}
#pragma endregion
}