libcmo21/Unvirt/UnvirtContext.cpp

750 lines
23 KiB
C++
Raw Normal View History

2023-08-27 12:30:12 +08:00
#include "UnvirtContext.hpp"
#include <YYCCommonplace.hpp>
2023-09-12 17:03:06 +08:00
#include <CK2/MgrImpls/CKPathManager.hpp>
2023-08-31 12:37:23 +08:00
#include <cstdarg>
#include <regex>
2023-08-27 12:30:12 +08:00
namespace Unvirt::Context {
#pragma region Specialized for CmdHelper
class EncodingListArgument : public CmdHelper::Nodes::AbstractArgument {
public:
using ArgValue_t = CmdHelper::AMItems::StringArrayItem;
public:
EncodingListArgument(const std::u8string_view& argname) :
AbstractArgument(argname) {}
virtual ~EncodingListArgument() {}
YYCC_DEF_CLS_COPY_MOVE(EncodingListArgument);
protected:
virtual bool BeginConsume(const std::u8string& cur_cmd, CmdHelper::ArgumentsMap& am) override {
// split given argument
std::vector<std::u8string> encs = YYCC::StringHelper::Split(cur_cmd, u8",");
// check each parts is a valid encoding name
for (const auto& item : encs) {
if (!LibCmo::EncodingHelper::IsValidEncodingName(item))
return false;
}
// okey, add into map
am.Add<ArgValue_t>(m_ArgumentName, encs);
return true;
}
};
static YYCC::Constraints::Constraint<size_t> GetOneBasedIndexConstraint() {
return YYCC::Constraints::Constraint<size_t> {
[](const size_t& val) -> bool {
return val > 0u;
}
};
}
#pragma endregion
2023-08-27 12:30:12 +08:00
#pragma region UnvirtContext Misc
UnvirtContext::UnvirtContext() :
m_Splitter(), m_Parser(), m_Help(),
m_PageLen(10u), m_ListStyleIsFull(true), m_OrderExit(false),
m_SearchPart(SearchPart::None), m_SearchIdxResult(),
m_Ctx(nullptr), m_FileReader(nullptr), m_IsShallowRead(true) {
2023-08-27 12:30:12 +08:00
// Setup command parser
// get root node first
CmdHelper::Nodes::RootNode& root = m_Parser.GetRoot();
// setup tree
2023-08-27 16:45:07 +08:00
root
.Then<CmdHelper::Nodes::Literal>(CmdHelper::Nodes::Literal(u8"load")
.Then<CmdHelper::Nodes::Choice>(CmdHelper::Nodes::Choice(u8"stage", { u8"deep", u8"shallow"})
.Comment(u8"The stage of loading. 'deep' will load to CKObject stage. 'shallow' will load to CKStateChunk stage.")
.Then<CmdHelper::Nodes::StringArgument>(CmdHelper::Nodes::StringArgument(u8"filepath")
.Comment(u8"The path to loading file.")
.Executes(
2023-08-27 16:45:07 +08:00
std::bind(&UnvirtContext::ProcLoad, this, std::placeholders::_1),
u8"Load a Virtools composition."
2023-08-27 16:45:07 +08:00
)
)
)
)
.Then<CmdHelper::Nodes::Literal>(CmdHelper::Nodes::Literal(u8"unload")
.Executes(
2023-08-27 16:45:07 +08:00
std::bind(&UnvirtContext::ProcUnLoad, this, std::placeholders::_1),
u8"Release loaded Virtools composition."
2023-08-27 16:45:07 +08:00
)
2023-08-27 12:30:12 +08:00
)
.Then<CmdHelper::Nodes::Literal>(CmdHelper::Nodes::Literal(u8"save")
.Then<CmdHelper::Nodes::StringArgument>(CmdHelper::Nodes::StringArgument(u8"filepath")
.Comment(u8"The path to save file.")
.Executes(
std::bind(&UnvirtContext::ProcSave, this, std::placeholders::_1),
u8"Save the loaded file into a new file."
)
)
)
.Then<CmdHelper::Nodes::Literal>(CmdHelper::Nodes::Literal(u8"info")
.Executes(
2023-08-27 16:45:07 +08:00
std::bind(&UnvirtContext::ProcInfo, this, std::placeholders::_1),
u8"Show the header info of loaded Virtools composition."
2023-08-27 16:45:07 +08:00
)
2023-08-27 12:30:12 +08:00
)
.Then<CmdHelper::Nodes::Literal>(CmdHelper::Nodes::Literal(u8"ls")
.Then<CmdHelper::Nodes::Choice>(CmdHelper::Nodes::Choice(u8"part", { u8"obj", u8"mgr", u8"search"})
.Comment(u8"Which list you want to list.")
.Then<CmdHelper::Nodes::ArithmeticArgument<size_t>>(CmdHelper::Nodes::ArithmeticArgument<size_t>(u8"page", GetOneBasedIndexConstraint())
.Comment(u8"The page index. Start with 1.")
.Executes(
2023-08-27 16:45:07 +08:00
std::bind(&UnvirtContext::ProcLs, this, std::placeholders::_1),
u8"List something about loaded Virtools composition."
2023-08-27 16:45:07 +08:00
)
)
)
2023-08-27 12:30:12 +08:00
)
.Then<CmdHelper::Nodes::Literal>(CmdHelper::Nodes::Literal(u8"data")
.Then<CmdHelper::Nodes::Choice>(CmdHelper::Nodes::Choice(u8"part", { u8"obj", u8"mgr"})
.Comment(u8"Which list you want to show data.")
.Then<CmdHelper::Nodes::ArithmeticArgument<size_t>>(CmdHelper::Nodes::ArithmeticArgument<size_t>(u8"index")
.Comment(u8"The index. Start with 0.")
.Executes(
2023-08-27 16:45:07 +08:00
std::bind(&UnvirtContext::ProcData, this, std::placeholders::_1),
u8"Show the specific CKObject / CKBaseManager data."
2023-08-27 16:45:07 +08:00
)
)
)
2023-08-27 12:30:12 +08:00
)
.Then<CmdHelper::Nodes::Literal>(CmdHelper::Nodes::Literal(u8"chunk")
.Then<CmdHelper::Nodes::Choice>(CmdHelper::Nodes::Choice(u8"part", { u8"obj", u8"mgr"})
.Comment(u8"Which list you want to show chunk.")
.Then<CmdHelper::Nodes::ArithmeticArgument<size_t>>(CmdHelper::Nodes::ArithmeticArgument<size_t>(u8"index")
.Comment(u8"The index. Start with 0.")
.Executes(
2023-08-27 16:45:07 +08:00
std::bind(&UnvirtContext::ProcChunk, this, std::placeholders::_1),
u8"Show the specific CKStateChunk data."
2023-08-27 16:45:07 +08:00
)
)
)
2023-08-27 12:30:12 +08:00
)
.Then<CmdHelper::Nodes::Literal>(CmdHelper::Nodes::Literal(u8"search")
.Then<CmdHelper::Nodes::Choice>(CmdHelper::Nodes::Choice(u8"part", { u8"obj", u8"mgr"})
.Comment(u8"Which list you want to search.")
.Then<CmdHelper::Nodes::Choice>(CmdHelper::Nodes::Choice(u8"mode", { u8"plain", u8"re"})
.Comment(u8"The search mode. `plain` will search by substring and `re` will do regex search.")
.Then<CmdHelper::Nodes::StringArgument>(CmdHelper::Nodes::StringArgument(u8"text")
.Comment(u8"The text or regex to search.")
.Executes(
std::bind(&UnvirtContext::ProcSearch, this, std::placeholders::_1),
u8"Search object or manager by text or regex. Please note that the regex have limited UTF8 support and may cause undefined behavior."
)
)
)
)
)
.Then<CmdHelper::Nodes::Literal>(CmdHelper::Nodes::Literal(u8"items")
.Then<CmdHelper::Nodes::ArithmeticArgument<size_t>>(CmdHelper::Nodes::ArithmeticArgument<size_t>(u8"count", GetOneBasedIndexConstraint())
.Comment(u8"The count of items you want to show in one page.")
.Executes(
2023-08-27 16:45:07 +08:00
std::bind(&UnvirtContext::ProcItems, this, std::placeholders::_1),
u8"Set up how many items should be listed in one page when using 'ls' command."
2023-08-27 16:45:07 +08:00
)
)
2023-08-27 12:30:12 +08:00
)
.Then<CmdHelper::Nodes::Literal>(CmdHelper::Nodes::Literal(u8"style")
.Then<CmdHelper::Nodes::Choice>(CmdHelper::Nodes::Choice(u8"level", { u8"full", u8"simple"})
.Comment(u8"The amount of showen content.")
.Executes(
std::bind(&UnvirtContext::ProcStyle, this, std::placeholders::_1),
u8"Change the detail level of showen data in `ls` command."
)
)
)
.Then<CmdHelper::Nodes::Literal>(CmdHelper::Nodes::Literal(u8"encoding")
.Then<EncodingListArgument>(EncodingListArgument(u8"enc")
.Comment(u8"CKContext used encoding splitted by ','. Support mutiple encoding.")
.Executes(
2023-08-27 16:45:07 +08:00
std::bind(&UnvirtContext::ProcEncoding, this, std::placeholders::_1),
u8"Set the encoding series for CKContext."
2023-08-27 16:45:07 +08:00
)
)
2023-08-27 14:21:44 +08:00
)
.Then<CmdHelper::Nodes::Literal>(CmdHelper::Nodes::Literal(u8"temp")
.Then<CmdHelper::Nodes::StringArgument>(CmdHelper::Nodes::StringArgument(u8"temppath")
.Comment(u8"The path to Temp folder.")
.Executes(
2023-08-27 16:45:07 +08:00
std::bind(&UnvirtContext::ProcTemp, this, std::placeholders::_1),
u8"Set the Temp path for CKContext."
2023-08-27 14:21:44 +08:00
)
2023-08-27 16:45:07 +08:00
)
)
.Then<CmdHelper::Nodes::Literal>(CmdHelper::Nodes::Literal(u8"rsc")
.Then<CmdHelper::Nodes::Literal>(CmdHelper::Nodes::Literal(u8"clear")
.Executes(
2023-09-12 17:03:06 +08:00
std::bind(&UnvirtContext::ProcRsc, this, std::placeholders::_1, true),
u8"Clear all data resources paths."
2023-09-12 17:03:06 +08:00
)
)
.Then<CmdHelper::Nodes::Literal>(CmdHelper::Nodes::Literal(u8"add")
.Then<CmdHelper::Nodes::StringArgument>(CmdHelper::Nodes::StringArgument(u8"datares")
.Comment(u8"The data resources path .")
.Executes(
2023-09-12 17:03:06 +08:00
std::bind(&UnvirtContext::ProcRsc, this, std::placeholders::_1, false),
u8"Add a path to let Virtools find resources."
2023-09-12 17:03:06 +08:00
)
)
)
)
.Then<CmdHelper::Nodes::Literal>(CmdHelper::Nodes::Literal(u8"test")
.Executes(
std::bind(&UnvirtContext::ProcTest, this, std::placeholders::_1),
u8"Call custom debugging function (only available in Debug mode)."
)
)
.Then<CmdHelper::Nodes::Literal>(CmdHelper::Nodes::Literal(u8"help")
.Executes(
2023-08-27 22:14:02 +08:00
std::bind(&UnvirtContext::ProcHelp, this, std::placeholders::_1),
u8"Output this help page."
2023-08-27 22:14:02 +08:00
)
)
.Then<CmdHelper::Nodes::Literal>(CmdHelper::Nodes::Literal(u8"exit")
.Executes(
2023-08-27 16:45:07 +08:00
std::bind(&UnvirtContext::ProcExit, this, std::placeholders::_1),
u8"Exit program."
2023-08-27 16:45:07 +08:00
)
);
2023-08-27 14:21:44 +08:00
// create help document
m_Help = m_Parser.Help();
2023-08-27 16:45:07 +08:00
// initialize CK engine and create context
LibCmo::CK2::CKERROR err = LibCmo::CK2::CKStartUp();
if (err != LibCmo::CK2::CKERROR::CKERR_OK)
throw std::runtime_error("fail to initialize CK2 engine.");
2023-08-27 16:45:07 +08:00
m_Ctx = new LibCmo::CK2::CKContext();
m_Ctx->SetOutputCallback(std::bind(&UnvirtContext::PrintContextMsg, this, std::placeholders::_1));
2023-10-02 17:45:59 +08:00
m_Ctx->SetGlobalImagesSaveOptions(LibCmo::CK2::CK_TEXTURE_SAVEOPTIONS::CKTEXTURE_EXTERNAL);
2023-08-27 12:30:12 +08:00
}
UnvirtContext::~UnvirtContext() {
// free context and shutdown CK engine.
2023-09-01 12:19:06 +08:00
ClearDocument();
delete m_Ctx;
LibCmo::CK2::CKShutdown();
2023-08-27 12:30:12 +08:00
}
bool UnvirtContext::HasOpenedFile() {
return m_FileReader != nullptr;
}
void UnvirtContext::ClearDocument() {
if (m_FileReader == nullptr) return;
// clear search result
m_SearchPart = SearchPart::None;
m_SearchIdxResult.clear();
2023-08-27 22:14:02 +08:00
// delete reader
2023-08-27 12:30:12 +08:00
delete m_FileReader;
m_FileReader = nullptr;
m_IsShallowRead = true;
2023-08-27 22:14:02 +08:00
// clear context
2023-09-12 17:03:06 +08:00
m_Ctx->ClearAll();
2023-08-27 12:30:12 +08:00
}
2023-09-17 10:38:46 +08:00
void UnvirtContext::PrintContextMsg(LibCmo::CKSTRING msg) {
2023-08-27 12:30:12 +08:00
if (msg != nullptr) {
YYCC::ConsoleHelper::FormatLine(YYCC_COLOR_LIGHT_YELLOW(u8"[CKContext] ") "%s", msg);
2023-08-27 12:30:12 +08:00
}
}
void UnvirtContext::PrintCommonInfo(const char8_t* u8_fmt, ...) {
va_list argptr;
va_start(argptr, u8_fmt);
YYCC::ConsoleHelper::WriteLine(
YYCC::StringHelper::VPrintf(u8_fmt, argptr).c_str()
);
va_end(argptr);
}
2023-08-27 12:30:12 +08:00
void UnvirtContext::PrintCommonError(const char8_t* u8_fmt, ...) {
2023-08-27 12:30:12 +08:00
va_list argptr;
va_start(argptr, u8_fmt);
YYCC::ConsoleHelper::FormatLine(YYCC_COLOR_LIGHT_RED(u8"%s"),
YYCC::StringHelper::VPrintf(u8_fmt, argptr).c_str()
);
2023-08-27 12:30:12 +08:00
va_end(argptr);
}
#pragma endregion
#pragma region Main Run
void UnvirtContext::Run() {
// Enable terminal color feature
YYCC::ConsoleHelper::EnableColorfulConsole();
2023-08-27 12:30:12 +08:00
// Show banner
YYCC::ConsoleHelper::WriteLine(YYCC_COLOR_LIGHT_YELLOW(u8"Unvirt"));
YYCC::ConsoleHelper::WriteLine(YYCC_COLOR_LIGHT_YELLOW(u8"Type 'help' for more infomation. Type 'exit' to quit."));
// start process loop
2023-08-27 12:30:12 +08:00
while (true) {
// get command
YYCC::ConsoleHelper::Write(YYCC_COLOR_GREEN(u8"> "));
std::u8string u8cmd = YYCC::ConsoleHelper::ReadLine();
2023-08-27 12:30:12 +08:00
// lex command line first
if (!m_Splitter.Lex(u8cmd)) {
this->PrintCommonError(u8"Lexer error \"%s\".\nType 'help' for usage.", u8cmd.c_str());
continue;
}
// if result is empty, skip to next one
auto cmds = m_Splitter.GetResult();
if (cmds.empty()) continue;
2023-08-27 12:30:12 +08:00
// run command parser
if (!m_Parser.Parse(cmds)) {
this->PrintCommonError(u8"Parser error \"%s\".\nType 'help' for usage.", u8cmd.c_str());
2023-08-27 12:30:12 +08:00
}
// check whether sub processor need exit.
2023-08-27 12:30:12 +08:00
if (m_OrderExit) {
break;
}
}
}
#pragma endregion
#pragma region Proc Detail
void UnvirtContext::ProcLoad(const CmdHelper::ArgumentsMap& amap) {
2023-08-27 12:30:12 +08:00
// check pre-requirement
if (HasOpenedFile()) {
PrintCommonError(u8"Already have a opened file. Close it before calling 'load'.");
return;
}
if (!m_Ctx->IsValidEncoding()) {
PrintCommonError(u8"You have not set encoding properly. Set it before loading by calling 'encoding'.");
2023-08-27 12:30:12 +08:00
return;
}
// get argument
std::u8string filepath = amap.Get<CmdHelper::Nodes::StringArgument::ArgValue_t>(u8"filepath").Get();
bool is_deep = amap.Get<CmdHelper::Nodes::Choice::ArgValue_t>(u8"stage").Get() == 0u;
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());
m_IsShallowRead = false;
2023-08-27 12:30:12 +08:00
} else {
err = m_FileReader->ShallowLoad(filepath.c_str());
m_IsShallowRead = true;
2023-08-27 12:30:12 +08:00
}
if (err != LibCmo::CK2::CKERROR::CKERR_OK) {
// fail to load. release all.
PrintCommonError(u8"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();
}
}
void UnvirtContext::ProcUnLoad(const CmdHelper::ArgumentsMap&) {
2023-08-27 12:30:12 +08:00
// check pre-requirement
if (!HasOpenedFile()) {
this->PrintCommonError(u8"No loaded file.");
2023-08-27 12:30:12 +08:00
return;
}
// free all
ClearDocument();
}
void Unvirt::Context::UnvirtContext::ProcSave(const CmdHelper::ArgumentsMap& amap) {
// check pre-requirement
if (!HasOpenedFile()) {
PrintCommonError(u8"No loaded file.");
return;
}
if (!m_Ctx->IsValidEncoding()) {
PrintCommonError(u8"You have not set encoding properly. Set it before saving by calling 'encoding'.");
return;
}
std::u8string filepath = amap.Get<CmdHelper::Nodes::StringArgument::ArgValue_t>(u8"filepath").Get();
// construct writer from reader
LibCmo::CK2::CKFileWriter writer(m_Ctx, m_FileReader, m_IsShallowRead);
// 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(u8"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);
}
void UnvirtContext::ProcInfo(const CmdHelper::ArgumentsMap&) {
2023-08-27 12:30:12 +08:00
// check pre-requirement
if (!HasOpenedFile()) {
PrintCommonError(u8"No loaded file.");
2023-08-27 12:30:12 +08:00
return;
}
// print
Unvirt::StructFormatter::PrintCKFileInfo(m_FileReader->GetFileInfo());
}
void UnvirtContext::ProcLs(const CmdHelper::ArgumentsMap& amap) {
2023-08-27 12:30:12 +08:00
// check pre-requirement
if (!HasOpenedFile()) {
PrintCommonError(u8"No loaded file.");
2023-08-27 12:30:12 +08:00
return;
}
// get 0 based page (-1)
size_t page = amap.Get<CmdHelper::Nodes::ArithmeticArgument<size_t>::ArgValue_t>(u8"page").Get() - 1u;
2023-08-27 12:30:12 +08:00
// show list
switch (amap.Get<CmdHelper::Nodes::Choice::ArgValue_t>(u8"part").Get()) {
case 0u:
2023-08-27 12:30:12 +08:00
{
// obj list
Unvirt::StructFormatter::PrintObjectList(
m_FileReader->GetFileObjects(),
m_FileReader->GetFileInfo(),
page, this->m_PageLen,
m_ListStyleIsFull
);
2023-08-27 12:30:12 +08:00
break;
}
case 1u:
2023-08-27 12:30:12 +08:00
{
// mgr list
Unvirt::StructFormatter::PrintManagerList(
m_FileReader->GetManagersData(),
page, this->m_PageLen,
m_ListStyleIsFull
);
break;
}
case 2u:
{
// search list
switch (m_SearchPart) {
case SearchPart::None:
{
PrintCommonError(u8"No search result to list.");
break;
}
case SearchPart::Object:
{
Unvirt::StructFormatter::PrintSearchedObjectList(
m_SearchIdxResult,
m_FileReader->GetFileObjects(),
m_FileReader->GetFileInfo(),
page, this->m_PageLen,
m_ListStyleIsFull
);
break;
}
case SearchPart::Manager:
{
Unvirt::StructFormatter::PrintSearchedManagerList(
m_SearchIdxResult,
m_FileReader->GetManagersData(),
page, this->m_PageLen,
m_ListStyleIsFull
);
break;
}
2023-08-27 12:30:12 +08:00
}
break;
}
default:
throw std::runtime_error("unreachable code");
2023-08-27 12:30:12 +08:00
}
}
void UnvirtContext::ProcData(const CmdHelper::ArgumentsMap& amap) {
2023-08-27 16:45:07 +08:00
// check pre-requirement
if (!HasOpenedFile()) {
PrintCommonError(u8"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::Nodes::ArithmeticArgument<size_t>::ArgValue_t>(u8"index").Get();
2023-08-27 16:45:07 +08:00
// show data
switch (amap.Get<CmdHelper::Nodes::Choice::ArgValue_t>(u8"part").Get()) {
case 0u:
2023-08-27 16:45:07 +08:00
{
if (index >= m_FileReader->GetFileObjects().size()) {
PrintCommonError(u8"Index out of range.");
2023-08-27 16:45:07 +08:00
return;
}
Unvirt::StructFormatter::PrintCKObject(m_FileReader->GetFileObjects()[index].ObjPtr);
break;
}
case 1u:
2023-08-27 16:45:07 +08:00
{
if (index >= m_FileReader->GetManagersData().size()) {
PrintCommonError(u8"Index out of range.");
2023-08-27 16:45:07 +08:00
return;
}
// todo: finish manager display
PrintCommonError(u8"Not supported now.");
2023-08-27 16:45:07 +08:00
//Unvirt::StructFormatter::PrintCKBaseManager(m_FileReader->GetManagersData()[index].Data);
break;
}
default:
throw std::runtime_error("unreachable code");
2023-08-27 16:45:07 +08:00
}
2023-08-27 12:30:12 +08:00
}
void UnvirtContext::ProcChunk(const CmdHelper::ArgumentsMap& amap) {
2023-08-27 16:45:07 +08:00
// check pre-requirement
if (!HasOpenedFile()) {
PrintCommonError(u8"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::Nodes::ArithmeticArgument<size_t>::ArgValue_t>(u8"index").Get();
2023-08-27 12:30:12 +08:00
2023-08-27 16:45:07 +08:00
// show data
switch (amap.Get<CmdHelper::Nodes::Choice::ArgValue_t>(u8"part").Get()) {
2023-08-27 16:45:07 +08:00
case 0:
{
if (index >= m_FileReader->GetFileObjects().size()) {
PrintCommonError(u8"Index out of range.");
2023-08-27 16:45:07 +08:00
return;
}
Unvirt::StructFormatter::PrintCKStateChunk(m_FileReader->GetFileObjects()[index].Data);
break;
}
case 1:
{
if (index >= m_FileReader->GetManagersData().size()) {
PrintCommonError(u8"Index out of range.");
2023-08-27 16:45:07 +08:00
return;
}
Unvirt::StructFormatter::PrintCKStateChunk(m_FileReader->GetManagersData()[index].Data);
break;
}
default:
throw std::runtime_error("unreachable code");
2023-08-27 12:30:12 +08:00
}
2023-08-27 16:45:07 +08:00
}
2023-08-27 12:30:12 +08:00
void UnvirtContext::ProcSearch(const CmdHelper::ArgumentsMap& amap) {
// check pre-requirement
if (!HasOpenedFile()) {
PrintCommonError(u8"No loaded file.");
return;
}
// get search text *amap->Get<CmdHelper::StringArgument::vType>("text")
std::u8string search_text = amap.Get<CmdHelper::Nodes::StringArgument::ArgValue_t>(u8"text").Get();
// analyse search mode
std::function<bool(const LibCmo::XContainer::XString&)> search_fct = nullptr;
switch (amap.Get<CmdHelper::Nodes::Choice::ArgValue_t>(u8"mode").Get()) {
case 0u:
{
// plain mode
search_fct = [&search_text](const LibCmo::XContainer::XString& cmp) -> bool {
return cmp.find(search_text) != std::string::npos;
};
break;
}
case 1u:
{
// regex mode
// get ordinary search text
std::string ordinary_search_text = YYCC::EncodingHelper::ToOrdinary(search_text);
// try construct regex
std::regex re;
try {
re = std::regex(ordinary_search_text, std::regex_constants::ECMAScript);
} catch (const std::regex_error& e) {
PrintCommonError(u8"Invalid regular expressions: %s", e.what());
return;
}
// use copy ctor capture to input regex
// because re will be freed when exiting this switch.
search_fct = [re](const LibCmo::XContainer::XString& cmp) -> bool {
// get ordinary name and comapre
std::string ordinary_cmp = YYCC::EncodingHelper::ToOrdinary(cmp);
return std::regex_search(ordinary_cmp, re);
};
break;
}
default:
throw std::runtime_error("unreachable code");
}
// start search
switch (amap.Get<CmdHelper::Nodes::Choice::ArgValue_t>(u8"part").Get()) {
case 0u:
{
// object
m_SearchPart = SearchPart::Object;
m_SearchIdxResult.clear();
size_t counter = 0;
for (const auto& obj : m_FileReader->GetFileObjects()) {
if (search_fct(obj.Name)) {
m_SearchIdxResult.emplace_back(counter);
}
++counter;
}
break;
}
case 1u:
{
// manager
m_SearchPart = SearchPart::Manager;
m_SearchIdxResult.clear();
PrintCommonError(u8"Not supported now.");
// todo: remove this return when fixing manager searching.
return;
break;
}
default:
throw std::runtime_error("unreachable code");
}
// report search result
if (m_SearchIdxResult.empty()) {
PrintCommonInfo(u8"Search done, but no result.");
} else {
PrintCommonInfo(u8"Search done with %" PRIuSIZET " results. Use `ls search` to check them.", m_SearchIdxResult.size());
}
}
void UnvirtContext::ProcItems(const CmdHelper::ArgumentsMap& amap) {
2023-08-27 12:30:12 +08:00
// assign
m_PageLen = amap.Get<CmdHelper::Nodes::ArithmeticArgument<size_t>::ArgValue_t>(u8"count").Get();
2023-08-27 12:30:12 +08:00
}
void UnvirtContext::ProcStyle(const CmdHelper::ArgumentsMap& amap) {
// set list style level
switch (amap.Get<CmdHelper::Nodes::Choice::ArgValue_t>(u8"level").Get()) {
case 0u:
{
// full level
m_ListStyleIsFull = true;
break;
}
case 1u:
{
// simple level
m_ListStyleIsFull = false;
break;
}
default:
throw std::runtime_error("unreachable code");
}
}
void UnvirtContext::ProcEncoding(const CmdHelper::ArgumentsMap& amap) {
const auto& encodings = amap.Get<EncodingListArgument::ArgValue_t>(u8"enc").Get();
2023-08-27 16:45:07 +08:00
m_Ctx->SetEncoding(encodings);
2023-08-27 12:30:12 +08:00
}
void UnvirtContext::ProcTemp(const CmdHelper::ArgumentsMap& amap) {
2023-08-27 12:30:12 +08:00
// assign
std::u8string temp_path = amap.Get<CmdHelper::Nodes::StringArgument::ArgValue_t>(u8"temppath").Get();
if (!m_Ctx->GetPathManager()->SetTempFolder(temp_path.c_str())) {
PrintCommonError(u8"Set temp folder failed. Check your path first.");
2023-09-12 17:03:06 +08:00
}
}
void Unvirt::Context::UnvirtContext::ProcRsc(const CmdHelper::ArgumentsMap& amap, bool is_clear) {
if (is_clear) {
2023-09-12 17:03:06 +08:00
m_Ctx->GetPathManager()->ClearPath();
} else {
std::u8string data_res = amap.Get<CmdHelper::Nodes::StringArgument::ArgValue_t>(u8"datares").Get();
if (!m_Ctx->GetPathManager()->AddPath(data_res.c_str())) {
PrintCommonError(u8"Set data resource folder failed. Check your path first.");
2023-09-12 17:03:06 +08:00
}
}
2023-08-27 12:30:12 +08:00
}
void Unvirt::Context::UnvirtContext::ProcTest(const CmdHelper::ArgumentsMap& amap) {
#if defined(LIBCMO_BUILD_DEBUG)
// MARK: Add the debug code here.
// todo: temporaryly write Transparent Column Fixer code.
// move to independent app in future.
// check pre-requirement
if (!HasOpenedFile()) {
PrintCommonError(u8"No loaded file.");
return;
}
if (!m_IsShallowRead) {
PrintCommonError(u8"Transparent Column Fixer only accept shallow loaded file.");
return;
}
// iterate objects
LibCmo::CKDWORD expcode = static_cast<LibCmo::CKDWORD>(LibCmo::CK2::CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_TEXONLY);
LibCmo::CKDWORD modcode = static_cast<LibCmo::CKDWORD>(LibCmo::CK2::CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_OLDTEXONLY);
for (const auto& cobj : m_FileReader->GetFileObjects()) {
if (cobj.ObjectCid == LibCmo::CK2::CK_CLASSID::CKCID_TEXTURE && cobj.Data != nullptr) {
LibCmo::CK2::CKFileObject& obj = const_cast<LibCmo::CK2::CKFileObject&>(cobj);
obj.Data->StartRead();
auto chunkinfo = obj.Data->GetIdentifiersProfile();
obj.Data->StopRead();
// find CK_STATESAVE_TEXONLY and change it to CK_STATESAVE_OLDTEXONLY
for (const auto& entry : chunkinfo) {
if (entry.m_Identifier == expcode) {
LibCmo::CKDWORD* p = static_cast<LibCmo::CKDWORD*>(entry.m_DataPtr);
p -= 2;
if (*p == expcode) {
*p = modcode;
}
}
}
}
}
#else
PrintCommonError(u8"Test command only available in Debug mode.");
#endif
}
void Unvirt::Context::UnvirtContext::ProcHelp(const CmdHelper::ArgumentsMap&) {
m_Help.Print();
2023-08-27 22:14:02 +08:00
}
void UnvirtContext::ProcExit(const CmdHelper::ArgumentsMap&) {
2023-08-27 12:30:12 +08:00
m_OrderExit = true;
}
#pragma endregion
}