2023-08-22 15:30:26 +08:00
|
|
|
#include "CKContext.hpp"
|
2023-08-26 16:37:26 +08:00
|
|
|
#include "ObjImpls/CKObject.hpp"
|
2023-09-01 12:19:06 +08:00
|
|
|
#include "../XContainer/XBitArray.hpp"
|
2023-03-04 14:08:16 +08:00
|
|
|
#include <cstdarg>
|
2023-02-25 22:58:28 +08:00
|
|
|
|
2023-02-26 21:48:03 +08:00
|
|
|
namespace LibCmo::CK2 {
|
2023-02-25 22:58:28 +08:00
|
|
|
|
|
|
|
#if defined(LIBCMO_OS_WIN32)
|
|
|
|
static wchar_t g_UniqueFolder[] = L"LibCmo";
|
|
|
|
#else
|
|
|
|
static char g_UniqueFolder[] = "LibCmo";
|
|
|
|
#endif
|
|
|
|
|
2023-09-01 12:19:06 +08:00
|
|
|
#pragma region Ctor Dtor
|
|
|
|
|
|
|
|
CKContext::CKContext() :
|
|
|
|
m_ObjectsList(), m_ReturnedObjectIds(),
|
|
|
|
m_GroupGlobalIndex(), m_SceneGlobalIndex(),
|
|
|
|
m_CompressionLevel(5), m_FileWriteMode(CK_FILE_WRITEMODE::CKFILE_UNCOMPRESSED),
|
|
|
|
m_NameEncoding(), m_TempFolder(),
|
|
|
|
m_OutputCallback(nullptr)
|
|
|
|
{
|
|
|
|
// preset for temp folder
|
|
|
|
// todo: add current CKContext pointer as the part of temp path.
|
|
|
|
// thus multiple CKContext can work.
|
|
|
|
m_TempFolder = std::filesystem::temp_directory_path();
|
|
|
|
m_TempFolder /= g_UniqueFolder;
|
|
|
|
std::filesystem::create_directory(m_TempFolder);
|
|
|
|
}
|
|
|
|
|
|
|
|
CKContext::~CKContext() {
|
|
|
|
DestroyAllCKObjects();
|
|
|
|
}
|
|
|
|
|
|
|
|
#pragma endregion
|
|
|
|
|
2023-08-23 16:04:58 +08:00
|
|
|
#pragma region Objects Management
|
|
|
|
|
2023-09-01 13:27:46 +08:00
|
|
|
ObjImpls::CKObject* CKContext::CreateObject(CK_CLASSID cls, CKSTRING name,
|
2023-08-25 21:57:22 +08:00
|
|
|
CK_OBJECTCREATION_OPTIONS options, CK_CREATIONMODE* res) {
|
2023-08-23 16:04:58 +08:00
|
|
|
// todo: Process paramter options and res
|
|
|
|
|
|
|
|
// get description first
|
|
|
|
const CKClassDesc* desc = CKGetClassDesc(cls);
|
|
|
|
if (desc == nullptr) return nullptr;
|
|
|
|
|
|
|
|
// allocate a CK_ID first
|
|
|
|
CK_ID decided_id;
|
|
|
|
if (this->m_ReturnedObjectIds.empty()) {
|
|
|
|
// create new CK_ID.
|
2023-08-29 14:00:34 +08:00
|
|
|
decided_id = static_cast<CK_ID>(m_ObjectsList.size());
|
2023-08-23 16:04:58 +08:00
|
|
|
m_ObjectsList.resize(decided_id + 1);
|
|
|
|
} else {
|
|
|
|
// use returned CK_ID.
|
|
|
|
decided_id = m_ReturnedObjectIds.back();
|
|
|
|
m_ReturnedObjectIds.pop_back();
|
|
|
|
}
|
|
|
|
|
|
|
|
// create one
|
2023-09-01 14:55:31 +08:00
|
|
|
ObjImpls::CKObject* obj = desc->CreationFct(this, decided_id + c_ObjectIdOffset, name);
|
2023-08-23 16:04:58 +08:00
|
|
|
|
|
|
|
// put into slot
|
|
|
|
m_ObjectsList[decided_id] = obj;
|
2023-08-25 17:35:45 +08:00
|
|
|
|
|
|
|
// set out variable
|
|
|
|
return obj;
|
2023-08-23 16:04:58 +08:00
|
|
|
}
|
|
|
|
|
2023-09-01 13:27:46 +08:00
|
|
|
ObjImpls::CKObject* CKContext::GetObject(CK_ID id) {
|
2023-09-01 14:55:31 +08:00
|
|
|
id -= c_ObjectIdOffset;
|
2023-08-23 16:04:58 +08:00
|
|
|
if (id >= m_ObjectsList.size()) return nullptr;
|
|
|
|
return m_ObjectsList[id];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-09-01 13:27:46 +08:00
|
|
|
* @brief The real CKObject destroy worker shared by CKContext::DestroyObject and CKContext::~CKContext
|
2023-08-23 16:04:58 +08:00
|
|
|
* @param[in] ctx The CKContext
|
|
|
|
* @param[in] obj The CKObject need to be free.
|
|
|
|
*/
|
2023-08-26 16:37:26 +08:00
|
|
|
static void InternalDestroy(CKContext* ctx, ObjImpls::CKObject* obj) {
|
2023-08-23 16:04:58 +08:00
|
|
|
// find desc by classid
|
|
|
|
// if really we can not find it, we only can delete it directly.
|
|
|
|
const CKClassDesc* desc = CKGetClassDesc(obj->GetClassID());
|
|
|
|
if (desc == nullptr) {
|
|
|
|
delete obj;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// free it and return its value
|
|
|
|
desc->ReleaseFct(ctx, obj);
|
|
|
|
}
|
2023-09-01 13:27:46 +08:00
|
|
|
void CKContext::DestroyObject(CK_ID id) {
|
2023-09-01 14:55:31 +08:00
|
|
|
id -= c_ObjectIdOffset;
|
2023-08-23 16:04:58 +08:00
|
|
|
if (id >= m_ObjectsList.size()) return;
|
|
|
|
|
|
|
|
// get object and free it
|
2023-08-26 16:37:26 +08:00
|
|
|
ObjImpls::CKObject* obj = m_ObjectsList[id];
|
2023-08-23 16:04:58 +08:00
|
|
|
if (obj == nullptr) return;
|
|
|
|
InternalDestroy(this, obj);
|
|
|
|
|
|
|
|
// return its allocated id.
|
|
|
|
m_ObjectsList[id] = nullptr;
|
|
|
|
m_ReturnedObjectIds.emplace_back(id);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-09-01 12:19:06 +08:00
|
|
|
CKDWORD CKContext::AllocateGroupGlobalIndex() {
|
|
|
|
// try find first non-true position
|
|
|
|
CKDWORD index;
|
|
|
|
if (!XContainer::XBitArrayPatch::GetSetBitPosition(m_GroupGlobalIndex, 0, index)) {
|
|
|
|
// failed. distribute new one
|
|
|
|
index = static_cast<CKDWORD>(m_GroupGlobalIndex.size());
|
|
|
|
m_GroupGlobalIndex.resize(m_GroupGlobalIndex.size() + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// set to occupy
|
|
|
|
m_GroupGlobalIndex[index] = true;
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
CKDWORD CKContext::AllocateSceneGlobalIndex() {
|
|
|
|
// same as group
|
|
|
|
CKDWORD index;
|
|
|
|
if (!XContainer::XBitArrayPatch::GetSetBitPosition(m_SceneGlobalIndex, 0, index)) {
|
|
|
|
index = static_cast<CKDWORD>(m_SceneGlobalIndex.size());
|
|
|
|
m_SceneGlobalIndex.resize(m_SceneGlobalIndex.size() + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_SceneGlobalIndex[index] = true;
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CKContext::FreeGroupGlobalIndex(CKDWORD id) {
|
|
|
|
// check position
|
|
|
|
if (id >= m_GroupGlobalIndex.size()) return;
|
|
|
|
// set value
|
|
|
|
m_GroupGlobalIndex[id] = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CKContext::FreeSceneGlobalIndex(CKDWORD id) {
|
|
|
|
// same as group
|
|
|
|
if (id >= m_SceneGlobalIndex.size()) return;
|
|
|
|
m_SceneGlobalIndex[id] = false;
|
|
|
|
}
|
|
|
|
|
2023-08-27 22:14:02 +08:00
|
|
|
void CKContext::DestroyAllCKObjects() {
|
|
|
|
// free all created objects
|
|
|
|
for (auto& ptr : m_ObjectsList) {
|
|
|
|
if (ptr != nullptr) {
|
|
|
|
InternalDestroy(this, ptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// restore returned object list
|
|
|
|
m_ReturnedObjectIds.clear();
|
|
|
|
// empty object list
|
|
|
|
m_ObjectsList.clear();
|
2023-09-01 12:19:06 +08:00
|
|
|
|
|
|
|
// clear group and scene global index at the same time
|
|
|
|
m_SceneGlobalIndex.clear();
|
|
|
|
m_GroupGlobalIndex.clear();
|
2023-08-27 22:14:02 +08:00
|
|
|
}
|
|
|
|
|
2023-08-23 16:04:58 +08:00
|
|
|
#pragma endregion
|
|
|
|
|
2023-08-28 14:18:58 +08:00
|
|
|
#pragma region Common Manager Functions
|
|
|
|
|
|
|
|
CKINT CKContext::GetManagerCount() {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-08-28 17:04:28 +08:00
|
|
|
MgrImpls::CKBaseManager* CKContext::GetManager(CKINT index) {
|
2023-08-28 14:18:58 +08:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
#pragma endregion
|
|
|
|
|
|
|
|
#pragma region File Save/Load Options
|
|
|
|
|
|
|
|
void CKContext::SetCompressionLevel(CKINT level) {
|
|
|
|
if (level > 0 && level < 10) {
|
|
|
|
m_CompressionLevel = level;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CKINT CKContext::GetCompressionLevel() {
|
|
|
|
return m_CompressionLevel;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CKContext::SetFileWriteMode(CK_FILE_WRITEMODE mode) {
|
|
|
|
m_FileWriteMode = mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
CK_FILE_WRITEMODE CKContext::GetFileWriteMode() {
|
|
|
|
return m_FileWriteMode;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#pragma endregion
|
|
|
|
|
2023-08-23 16:04:58 +08:00
|
|
|
#pragma region Output utilities
|
2023-02-28 11:35:54 +08:00
|
|
|
|
2023-08-23 16:04:58 +08:00
|
|
|
void CKContext::OutputToConsole(CKSTRING str) {
|
|
|
|
if (m_OutputCallback == nullptr) return;
|
|
|
|
m_OutputCallback(str);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CKContext::OutputToConsoleEx(CKSTRING fmt, ...) {
|
|
|
|
if (m_OutputCallback == nullptr) return;
|
2023-02-26 21:48:03 +08:00
|
|
|
|
2023-02-25 22:58:28 +08:00
|
|
|
va_list argptr;
|
|
|
|
va_start(argptr, fmt);
|
2023-02-26 21:48:03 +08:00
|
|
|
|
|
|
|
std::string result;
|
|
|
|
int count = std::vsnprintf(nullptr, 0, fmt, argptr);
|
|
|
|
result.resize(count);
|
|
|
|
int write_result = std::vsnprintf(result.data(), count, fmt, argptr);
|
|
|
|
|
|
|
|
if (write_result < 0 || write_result > count) return;
|
|
|
|
|
2023-02-25 22:58:28 +08:00
|
|
|
va_end(argptr);
|
2023-02-26 21:48:03 +08:00
|
|
|
|
2023-08-23 16:04:58 +08:00
|
|
|
m_OutputCallback(result.c_str());
|
2023-02-26 21:48:03 +08:00
|
|
|
}
|
|
|
|
|
2023-08-23 16:04:58 +08:00
|
|
|
void CKContext::SetOutputCallback(OutputCallback cb) {
|
|
|
|
m_OutputCallback = cb;
|
2023-02-25 22:58:28 +08:00
|
|
|
}
|
|
|
|
|
2023-02-28 11:35:54 +08:00
|
|
|
#pragma endregion
|
|
|
|
|
2023-08-23 16:04:58 +08:00
|
|
|
#pragma region Temp IO utilities
|
2023-02-28 11:35:54 +08:00
|
|
|
|
2023-08-23 16:04:58 +08:00
|
|
|
void CKContext::SetTempPath(CKSTRING u8_temp) {
|
2023-08-30 10:03:02 +08:00
|
|
|
EncodingHelper::U8PathToStdPath(this->m_TempFolder, u8_temp);
|
2023-03-04 11:11:36 +08:00
|
|
|
}
|
|
|
|
|
2023-08-30 10:03:02 +08:00
|
|
|
std::string CKContext::GetTempFilePath(CKSTRING u8_filename) {
|
2023-08-23 16:04:58 +08:00
|
|
|
std::filesystem::path stdfilename;
|
2023-08-30 10:03:02 +08:00
|
|
|
EncodingHelper::U8PathToStdPath(stdfilename, u8_filename);
|
2023-08-23 16:04:58 +08:00
|
|
|
auto realfile = this->m_TempFolder / stdfilename;
|
2023-08-28 17:04:28 +08:00
|
|
|
|
2023-08-30 10:03:02 +08:00
|
|
|
std::string result;
|
|
|
|
EncodingHelper::StdPathToU8Path(result, realfile);
|
|
|
|
return result;
|
2023-02-28 11:35:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#pragma endregion
|
|
|
|
|
2023-08-23 16:04:58 +08:00
|
|
|
#pragma region Encoding utilities
|
2023-02-28 11:35:54 +08:00
|
|
|
|
2023-08-23 16:04:58 +08:00
|
|
|
void CKContext::GetUtf8String(const std::string& native_name, std::string& u8_name) {
|
2023-03-04 11:11:36 +08:00
|
|
|
bool success = false;
|
|
|
|
for (const auto& token : this->m_NameEncoding) {
|
|
|
|
success = LibCmo::EncodingHelper::GetUtf8VirtoolsName(native_name, u8_name, token);
|
|
|
|
if (success) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// fallback
|
|
|
|
if (!success) {
|
|
|
|
u8_name = native_name;
|
2023-08-23 16:04:58 +08:00
|
|
|
this->OutputToConsole("Error when converting to UTF8 string.");
|
2023-03-04 11:11:36 +08:00
|
|
|
}
|
2023-02-25 22:58:28 +08:00
|
|
|
}
|
|
|
|
|
2023-08-23 16:04:58 +08:00
|
|
|
void CKContext::GetNativeString(const std::string& u8_name, std::string& native_name) {
|
2023-03-04 11:11:36 +08:00
|
|
|
bool success = false;
|
|
|
|
for (const auto& token : this->m_NameEncoding) {
|
|
|
|
success = LibCmo::EncodingHelper::GetNativeVirtoolsName(u8_name, native_name, token);
|
|
|
|
if (success) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// fallback
|
|
|
|
if (!success) {
|
|
|
|
native_name = u8_name;
|
2023-08-23 16:04:58 +08:00
|
|
|
this->OutputToConsole("Error when converting to native string.");
|
2023-03-04 11:11:36 +08:00
|
|
|
}
|
2023-02-25 22:58:28 +08:00
|
|
|
}
|
|
|
|
|
2023-08-23 16:04:58 +08:00
|
|
|
void CKContext::SetEncoding(const std::vector<std::string> encoding_series) {
|
2023-03-04 11:11:36 +08:00
|
|
|
// free all current series
|
|
|
|
for (const auto& encoding : this->m_NameEncoding) {
|
|
|
|
LibCmo::EncodingHelper::DestroyEncodingToken(encoding);
|
|
|
|
}
|
|
|
|
this->m_NameEncoding.clear();
|
|
|
|
|
|
|
|
// add new encoding
|
|
|
|
for (const auto& encoding_str : encoding_series) {
|
|
|
|
this->m_NameEncoding.push_back(LibCmo::EncodingHelper::CreateEncodingToken(encoding_str));
|
|
|
|
}
|
2023-02-25 22:58:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-02-28 11:35:54 +08:00
|
|
|
#pragma endregion
|
|
|
|
|
2023-02-25 22:58:28 +08:00
|
|
|
}
|