fix: fix issues

- restore some CKGlobals behavior because it will cause runtime exception.
- move some classes from CmdHelper to UnvirtContext to make CmdHelper more common to use.
- output warning string when fail to get utf8 or ordinary string.
This commit is contained in:
yyc12345 2024-08-27 11:25:53 +08:00
parent 3735a202f3
commit 65861143bf
14 changed files with 91 additions and 70 deletions

View File

@ -281,7 +281,7 @@ namespace LibCmo::CK2 {
#pragma region Encoding utilities
void CKContext::GetUTF8String(const std::string& native_name, XContainer::XString& u8_name) {
bool CKContext::GetUTF8String(const std::string& native_name, XContainer::XString& u8_name) {
bool conv_success = false, has_valid_token = false;
for (const auto& token : this->m_NameEncoding) {
if (token == EncodingHelper::INVALID_ENCODING_TOKEN) continue;
@ -298,9 +298,11 @@ namespace LibCmo::CK2 {
this->OutputToConsole(u8"Error when converting to UTF8 string from ordinary string. The string will leave to blank.");
}
}
// return value
return conv_success;
}
void CKContext::GetOrdinaryString(const XContainer::XString& u8_name, std::string& native_name) {
bool CKContext::GetOrdinaryString(const XContainer::XString& u8_name, std::string& native_name) {
bool conv_success = false, has_valid_token = false;
for (const auto& token : this->m_NameEncoding) {
if (token == EncodingHelper::INVALID_ENCODING_TOKEN) continue;
@ -317,6 +319,8 @@ namespace LibCmo::CK2 {
this->OutputToConsole(u8"Error when converting to ordinary string from UTF8 string. The string will leave to blank.");
}
}
// return value
return conv_success;
}
void CKContext::SetEncoding(const XContainer::XArray<XContainer::XString>& encoding_seq) {

View File

@ -104,6 +104,7 @@ namespace LibCmo::CK2 {
* @brief Convert given ordinary string to UTF8 string.
* @param[in] native_name The input ordinary string.
* @param[out] u8_name The output UTF8 string.
* @return True if convertion is success, otherwise false.
* @exception RuntimeException Raised when perform this operation with a blank encoding sequence.
* @remarks
* The encoding of ordinary is specified by encoding sequence.
@ -111,11 +112,12 @@ namespace LibCmo::CK2 {
* However, if you use this function with blank encoding sequence, it will raise exception.
* So becore using this function, please make sure that you have checked by calling IsValidEncoding().
*/
void GetUTF8String(const std::string& native_name, XContainer::XString& u8_name);
bool GetUTF8String(const std::string& native_name, XContainer::XString& u8_name);
/**
* @brief Convert given UTF8 string to ordinary string.
* @param[in] u8_name The input UTF8 string.
* @param[out] native_name The output ordinary string.
* @return True if convertion is success, otherwise false.
* @exception RuntimeException Raised when perform this operation with a blank encoding sequence.
* @remarks
* The encoding of ordinary is specified by encoding sequence.
@ -123,7 +125,7 @@ namespace LibCmo::CK2 {
* However, if you use this function with blank encoding sequence, it will raise exception.
* So becore using this function, please make sure that you have checked by calling IsValidEncoding().
*/
void GetOrdinaryString(const XContainer::XString& u8_name, std::string& native_name);
bool GetOrdinaryString(const XContainer::XString& u8_name, std::string& native_name);
/**
* @brief Set the encoding sequence.
* @param[in] encoding_series The encoding name in this sequence.

View File

@ -133,7 +133,8 @@ namespace LibCmo::CK2 {
if (namelen != 0) {
name_conv.resize(namelen);
parser->Read(name_conv.data(), namelen);
m_Ctx->GetUTF8String(name_conv, fileobj.Name);
if (!m_Ctx->GetUTF8String(name_conv, fileobj.Name))
m_Ctx->OutputToConsole(u8"Fail to get UTF8 name for CKObject when reading file header. Some objects name will leave to blank.");
} else {
XContainer::NSXString::FromCKSTRING(fileobj.Name, nullptr);
}
@ -311,7 +312,8 @@ namespace LibCmo::CK2 {
// read filename
if (filenamelen != 0) {
parser->Read(name_conv.data(), filenamelen);
m_Ctx->GetUTF8String(name_conv, file);
if (!m_Ctx->GetUTF8String(name_conv, file))
m_Ctx->OutputToConsole(u8"Fail to get UTF8 name for included file when reading file body. Some included files may be stripped.");
}
// read file body length

View File

@ -91,7 +91,8 @@ namespace LibCmo::CK2 {
sumHdrObjSize += 4 * CKSizeof(CKDWORD);
if (XContainer::NSXString::ToCKSTRING(obj.Name) != nullptr) {
// += Name size
m_Ctx->GetOrdinaryString(obj.Name, name_conv);
if (!m_Ctx->GetOrdinaryString(obj.Name, name_conv))
m_Ctx->OutputToConsole(u8"Fail to get ordinary string for CKObject name when computing the size of saved file. It may cause application crash or saved file has blank object name.");
sumHdrObjSize += static_cast<CKDWORD>(name_conv.size());
}
@ -178,7 +179,8 @@ namespace LibCmo::CK2 {
if (XContainer::NSXString::ToCKSTRING(obj.Name) != nullptr) {
// if have name, write it
m_Ctx->GetOrdinaryString(obj.Name, name_conv);
if (!m_Ctx->GetOrdinaryString(obj.Name, name_conv))
m_Ctx->OutputToConsole(u8"Fail to get ordinary string for CKObject name when saving file. Some objects may be saved with blank name.");
CKDWORD namelen = static_cast<CKDWORD>(name_conv.size());
hdrparser->Write(&namelen);
hdrparser->Write(name_conv.data(), namelen);
@ -318,7 +320,8 @@ namespace LibCmo::CK2 {
m_Ctx->GetPathManager()->GetFileName(filename);
// write filename
m_Ctx->GetOrdinaryString(filename, name_conv);
if (!m_Ctx->GetOrdinaryString(filename, name_conv))
m_Ctx->OutputToConsole(u8"Fail to get ordinary string for included file when saving file. Some included files may not be saved correctly.");
CKDWORD filenamelen = static_cast<CKDWORD>(name_conv.size());
std::fwrite(&filenamelen, sizeof(CKDWORD), 1, fs);
std::fwrite(name_conv.data(), sizeof(CKBYTE), filenamelen, fs);

View File

@ -145,8 +145,7 @@ namespace LibCmo::CK2 {
void CKClassNeedNotificationFrom(CK_CLASSID listener, CK_CLASSID listenTo) {
size_t idxListener, idxListenTo;
if (!GetClassIdIndex(listener, idxListener) || !GetClassIdIndex(listenTo, idxListenTo))
throw LogicException("Invalid CK_CLASSID in argument.");
if (!GetClassIdIndex(listener, idxListener) || !GetClassIdIndex(listenTo, idxListenTo)) return;
XContainer::NSXBitArray::Set(g_CKClassInfo[idxListener].ToBeNotify, static_cast<CKDWORD>(idxListenTo));
}
@ -191,25 +190,25 @@ namespace LibCmo::CK2 {
const CKClassDesc* CKGetClassDesc(CK_CLASSID cid) {
size_t intcid;
if (!GetClassIdIndex(cid, intcid))
throw LogicException("Invalid CK_CLASSID.");
if (!GetClassIdIndex(cid, intcid)) return nullptr;
return &g_CKClassInfo[intcid];
}
CKSTRING CKClassIDToString(CK_CLASSID cid) {
const CKClassDesc* desc = CKGetClassDesc(cid);
if (desc == nullptr) return u8"Undefined Type";
return desc->NameFct();
}
bool CKIsChildClassOf(CK_CLASSID child, CK_CLASSID parent) {
size_t intchild, intparent;
if (!GetClassIdIndex(child, intchild) || !GetClassIdIndex(parent, intparent))
throw LogicException("Invalid CK_CLASSID.");
if (!GetClassIdIndex(child, intchild) || !GetClassIdIndex(parent, intparent)) return false;
return g_CKClassInfo[intchild].Parents[intparent];
}
CK_CLASSID CKGetParentClassID(CK_CLASSID child) {
const CKClassDesc* desc = CKGetClassDesc(child);
if (desc == nullptr) return CK_CLASSID::CKCID_OBJECT;
return desc->Parent;
}
@ -228,6 +227,7 @@ namespace LibCmo::CK2 {
bool CKIsNeedNotify(CK_CLASSID listener, CK_CLASSID deletedObjCid) {
const CKClassDesc* desc = CKGetClassDesc(listener);
if (desc == nullptr) return false;
return XContainer::NSXBitArray::IsSet(desc->CommonToBeNotify, static_cast<CKDWORD>(deletedObjCid));
}
@ -238,6 +238,8 @@ namespace LibCmo::CK2 {
if (!XContainer::NSXBitArray::IsSet(delObjCids, static_cast<CKDWORD>(i))) continue;
const CKClassDesc* desc = CKGetClassDesc(static_cast<CK_CLASSID>(i));
if (desc == nullptr) continue;
XContainer::NSXBitArray::Or(result, desc->ToNotify);
}

View File

@ -155,7 +155,7 @@ namespace LibCmo::CK2 {
* @brief Order that first class id will be notified when deleting object whose class id is second argument
* @param[in] listener The id of class will be notified.
* @param[in] listenTo The id of class which first argument interested in.
* @exception LogicException Raised if given CK_CLASSID is invalid.
* @remarks If one of given class ids is invalid, this function simply return and do nothing.
*/
void CKClassNeedNotificationFrom(CK_CLASSID listener, CK_CLASSID listenTo);
/**
@ -188,15 +188,15 @@ namespace LibCmo::CK2 {
* @brief Get the class description struct by given class id.
* @param[in] cid Class id for fetching.
* @return The pointer to corresponding class description.
* We guaranteen that this pointer must not be nullptr.
* @exception LogicException Raised if given CK_CLASSID is invalid.
* If given class id is invalid, this function will return nullptr.
* According to this, caller can utilize this function to validate class id.
*/
const CKClassDesc* CKGetClassDesc(CK_CLASSID cid);
/**
* @brief Get the name representation of given class id.
* @param[in] cid Class id for fetching.
* @return The name of given class id.
* @exception LogicException Raised if given CK_CLASSID is invalid.
* If given class id is invalid, it return a predefined name.
*/
CKSTRING CKClassIDToString(CK_CLASSID cid);
@ -205,14 +205,14 @@ namespace LibCmo::CK2 {
* @param[in] child The id of first class assumed as child class.
* @param[in] parent The id of second class assumed as parent class.
* @return True if relation is satisfied, otherwise false.
* @exception LogicException Raised if given CK_CLASSID is invalid.
* If one of given class ids is invalid, this function always return false.
*/
bool CKIsChildClassOf(CK_CLASSID child, CK_CLASSID parent);
/**
* @brief Get the parent class id of given class id.
* @param[in] child The id to class which need to find parent class.
* @return The parent class id.
* @exception LogicException Raised if given CK_CLASSID is invalid.
* If given class id is invalid, this function always return the class id of CKObject.
*/
CK_CLASSID CKGetParentClassID(CK_CLASSID child);
/**
@ -220,7 +220,6 @@ namespace LibCmo::CK2 {
* @param[in] cid1 The id of first class finding common parent.
* @param[in] cid2 The id of second class finding common parent.
* @return The cloest common parent class id.
* @exception LogicException Raised if given CK_CLASSID is invalid.
*/
CK_CLASSID CKGetCommonParent(CK_CLASSID cid1, CK_CLASSID cid2);
@ -229,14 +228,13 @@ namespace LibCmo::CK2 {
* @param[in] listener The class id of checking whether need to be notified.
* @param[in] deletedObjCid The class id of deleting object.
* @return True if it need to be notified, otherwise false.
* @exception LogicException Raised if given CK_CLASSID is invalid.
* If the class id of checking is invalid, this function always return false.
*/
bool CKIsNeedNotify(CK_CLASSID listener, CK_CLASSID deletedObjCid); /**
/**
* @brief Get all class ids need to be notified when objects whose class id included in \c delObjCids are deleting.
* @param[in] delObjCids The bit array representing class ids which need to be queried.
* @return The queried bit array representing class ids need to be notified.
* @exception LogicException Raised if given CK_CLASSID is invalid.
* @see CKIsNeedNotify()
*/
XContainer::XBitArray CKGetAllNotifyClassID(const XContainer::XBitArray& delObjCids);

View File

@ -148,7 +148,13 @@ namespace LibCmo::CK2 {
}
// convert encoding
m_BindContext->GetUTF8String(cache, *strl);
if (!m_BindContext->GetUTF8String(cache, *strl)) {
m_BindContext->OutputToConsole(u8"Fail to get UTF8 string when reading CKStateChunk. Some objects may be loaded incorrectly.");
strl->clear();
return false;
}
// okey
return true;
}

View File

@ -138,7 +138,8 @@ namespace LibCmo::CK2 {
// convert encoding
std::string cache;
m_BindContext->GetOrdinaryString(*strl, cache);
if (!m_BindContext->GetOrdinaryString(*strl, cache))
m_BindContext->OutputToConsole(u8"Fail to get ordinary string when saving CKStateChunk. Some objects may be saved incorrectly.");
if (cache.empty()) {
// write zero string

View File

@ -20,6 +20,8 @@ namespace LibCmo::CK2::MgrImpls {
// get description first
const CKClassDesc* desc = CKGetClassDesc(cls);
// if no description, return directly to reject creating object.
if (desc == nullptr) return nullptr;
// allocate a CK_ID first
CKDWORD decided_off;
@ -60,7 +62,9 @@ namespace LibCmo::CK2::MgrImpls {
void CKObjectManager::InternalDestroy(ObjImpls::CKObject* obj) {
// find desc by classid
const CKClassDesc* desc = CKGetClassDesc(obj->GetClassID());
// a create CKObject instance definitely can find corresponding desc.
// if not, throw exception.
if (desc == nullptr) throw LogicException("Invalid CK_CLASSID");
// free it
desc->ReleaseFct(m_Context, obj);
}

View File

@ -187,7 +187,8 @@ namespace LibCmo::CK2::ObjImpls {
props.m_ReaderGuid.d1 = realprops.m_ReaderGuid.d1;
props.m_ReaderGuid.d2 = realprops.m_ReaderGuid.d2;
std::string ext;
m_Context->GetOrdinaryString(realprops.m_Ext.GetExt(), ext);
if (!m_Context->GetOrdinaryString(realprops.m_Ext.GetExt(), ext))
m_Context->OutputToConsole(u8"Fail to get ordinary string for the extension of bitmap properties when saving CKTexture. Some textures may be saved with blank extension.");
std::memcpy(
props.m_Ext.m_Data,
ext.c_str(),
@ -314,7 +315,8 @@ namespace LibCmo::CK2::ObjImpls {
// get utf8 extension
XContainer::XString ext;
m_Context->GetUTF8String(props->m_Ext.m_Data, ext);
if (!m_Context->GetUTF8String(props->m_Ext.m_Data, ext))
m_Context->OutputToConsole(u8"Fail to get UTF8 extension when loading CKTexture. Some textures may have blank extension in bitmap properties.");
// get my bitmap prop
CKBitmapProperties myprops(

View File

@ -522,6 +522,11 @@ if (!this->IsRootNode()) { \
#pragma region String Argument
StringArgument::StringArgument(const std::u8string_view& argname, Constraint_t constraint) :
AbstractArgument(argname), m_Constraint(constraint) {}
StringArgument::~StringArgument() {}
bool StringArgument::BeginConsume(const std::u8string& cur_cmd, ArgumentsMap& am) {
// check constraint
if (m_Constraint.IsValid() && !m_Constraint.m_CheckFct(cur_cmd))
@ -531,23 +536,6 @@ if (!this->IsRootNode()) { \
return true;
}
#pragma endregion
#pragma region Encoding List Argument
bool EncodingListArgument::BeginConsume(const std::u8string& cur_cmd, ArgumentsMap& am) {
// 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;
}
#pragma endregion
}

View File

@ -498,9 +498,8 @@ namespace Unvirt::CmdHelper {
using ArgValue_t = AMItems::StringItem;
using Constraint_t = YYCC::Constraints::Constraint<std::u8string>;
public:
StringArgument(const std::u8string_view& argname, Constraint_t constraint = Constraint_t {}) :
AbstractArgument(argname), m_Constraint(constraint) {}
virtual ~StringArgument() {}
StringArgument(const std::u8string_view& argname, Constraint_t constraint = Constraint_t {});
virtual ~StringArgument();
YYCC_DEF_CLS_COPY_MOVE(StringArgument);
protected:
@ -508,19 +507,6 @@ namespace Unvirt::CmdHelper {
Constraint_t m_Constraint;
};
class EncodingListArgument : public AbstractArgument {
public:
using ArgValue_t = 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, ArgumentsMap& am) override;
};
}
class CommandParser {

View File

@ -204,7 +204,7 @@ namespace Unvirt::StructFormatter {
for (LibCmo::CKDWORD i = 0; i < slot_count; ++i) {
auto desc = bd.GetImageDesc(i);
Console::FormatLine(u8"#%" PRIuCKDWORD "\t%" PRIuCKDWORD "\t%" PRIuCKDWORD "\t0x%" PRIxCKDWORD " bytes\t",
Console::FormatLine(u8"#%" PRIuCKDWORD "\t%" PRIuCKDWORD "\t%" PRIuCKDWORD "\t%s\t0x%" PRIxCKDWORD " bytes\t%s",
i,
desc->GetWidth(),
desc->GetHeight(),

View File

@ -7,7 +7,31 @@
namespace Unvirt::Context {
#pragma region Constraint Help Function
#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> {
@ -19,7 +43,6 @@ namespace Unvirt::Context {
#pragma endregion
#pragma region UnvirtContext Misc
UnvirtContext::UnvirtContext() :
@ -136,7 +159,7 @@ namespace Unvirt::Context {
)
)
.Then<CmdHelper::Nodes::Literal>(CmdHelper::Nodes::Literal(u8"encoding")
.Then<CmdHelper::Nodes::EncodingListArgument>(CmdHelper::Nodes::EncodingListArgument(u8"enc")
.Then<EncodingListArgument>(EncodingListArgument(u8"enc")
.Comment(u8"CKContext used encoding splitted by ','. Support mutiple encoding.")
.Executes(
std::bind(&UnvirtContext::ProcEncoding, this, std::placeholders::_1),
@ -644,7 +667,7 @@ namespace Unvirt::Context {
}
void UnvirtContext::ProcEncoding(const CmdHelper::ArgumentsMap& amap) {
const auto& encodings = amap.Get<CmdHelper::Nodes::EncodingListArgument::ArgValue_t>(u8"enc").Get();
const auto& encodings = amap.Get<EncodingListArgument::ArgValue_t>(u8"enc").Get();
m_Ctx->SetEncoding(encodings);
}
@ -723,4 +746,4 @@ namespace Unvirt::Context {
#pragma endregion
}
}