diff --git a/LibCmo/CK2/CKContext.cpp b/LibCmo/CK2/CKContext.cpp index ad9751c..b414d43 100644 --- a/LibCmo/CK2/CKContext.cpp +++ b/LibCmo/CK2/CKContext.cpp @@ -99,7 +99,7 @@ namespace LibCmo::CK2 { return 0; } - MgrImpls::CKBaseManager* CKContext::GetManager(int index) { + MgrImpls::CKBaseManager* CKContext::GetManager(CKINT index) { return nullptr; } @@ -188,12 +188,18 @@ namespace LibCmo::CK2 { EncodingHelper::SetStdPathFromU8Path(this->m_TempFolder, u8_temp); } - FILE* CKContext::OpenTempFile(CKSTRING u8_filename, CKBOOL is_read) { + FILE* CKContext::OpenTempFile(CKSTRING u8_filename, CKSTRING u8_mode) { std::filesystem::path stdfilename; EncodingHelper::SetStdPathFromU8Path(stdfilename, u8_filename); auto realfile = this->m_TempFolder / stdfilename; - return EncodingHelper::OpenStdPathFile(realfile, is_read); + return EncodingHelper::StdPathFOpen(realfile, u8_mode); + } + + FILE* CKContext::OpenFile(CKSTRING u8_filename, CKSTRING u8_mode) { + std::filesystem::path stdfilename; + EncodingHelper::SetStdPathFromU8Path(stdfilename, u8_filename); + return EncodingHelper::StdPathFOpen(stdfilename, u8_mode); } #pragma endregion diff --git a/LibCmo/CK2/CKContext.hpp b/LibCmo/CK2/CKContext.hpp index af932a9..e941c54 100644 --- a/LibCmo/CK2/CKContext.hpp +++ b/LibCmo/CK2/CKContext.hpp @@ -60,7 +60,7 @@ namespace LibCmo::CK2 { // ========== Common Managers Functions ========== CKINT GetManagerCount(); - MgrImpls::CKBaseManager* GetManager(int index); + MgrImpls::CKBaseManager* GetManager(CKINT index); // ========== File Save/Load Options ========== @@ -79,7 +79,8 @@ namespace LibCmo::CK2 { // ========== Temp IO utilities ========== void SetTempPath(CKSTRING u8_temp); - FILE* OpenTempFile(CKSTRING u8_filename, CKBOOL is_read); + FILE* OpenTempFile(CKSTRING u8_filename, CKSTRING u8_mode); + FILE* OpenFile(CKSTRING u8_filename, CKSTRING u8_mode); // ========== Print utilities ========== diff --git a/LibCmo/CK2/CKDefines.hpp b/LibCmo/CK2/CKDefines.hpp index 56986a2..3950544 100644 --- a/LibCmo/CK2/CKDefines.hpp +++ b/LibCmo/CK2/CKDefines.hpp @@ -9,14 +9,19 @@ #include namespace LibCmo::CK2 { + /** + * @brief The identifier of Virtools file. + */ + constexpr const char CKNEMOFI[] = "Nemo Fi"; /** * @brief Current Version of CK Engine (Day/Month/Year) */ - constexpr const CKDWORD CKVERSION = 0x05082002u; + constexpr const CKDWORD CKVERSION = 0x13022002u; /** * @brief Current Version of Dev */ - constexpr const CKDWORD DEVVERSION = 0x02050000u; + constexpr const CKDWORD DEVVERSION = 0x02010001u; + constexpr const CKDWORD DEVBUILD = 0u; constexpr const CKGUID VIRTOOLS_GUID = CKGUID(0x56495254u, 0x4f4f4c53u); // ========== Class registration utilities ========== diff --git a/LibCmo/CK2/CKFile.hpp b/LibCmo/CK2/CKFile.hpp index d8ea959..98f88f1 100644 --- a/LibCmo/CK2/CKFile.hpp +++ b/LibCmo/CK2/CKFile.hpp @@ -121,6 +121,7 @@ namespace LibCmo::CK2 { ObjImpls::CKObject* ObjPtr; /**< A pointer to the object itself (as CreatedObject when loading) */ TypeHelper::MKString Name; /**< Name of the Object */ CKStateChunk* Data; /**< A CKStateChunk that contains object information */ + CKDWORD PackSize; /**< The CKStateChunk data size */ //CKINT PostPackSize; /**< When compressed chunk by chunk : size of Data after compression */ //CKINT PrePackSize; /**< When compressed chunk by chunk : size of Data before compression */ CK_FO_OPTIONS Options; /**< When loading an object it may be renamed , use to replace another object */ @@ -233,11 +234,14 @@ namespace LibCmo::CK2 { */ CKBOOL m_IsCopyFromReader; + CKINT m_SaveIDMax; /**< Maximum CK_ID found when saving or loading objects */ XContainer::XArray m_FileObjects; /**< List of objects being saved / loaded */ XContainer::XArray m_ManagersData; /**< Manager Data loaded */ XContainer::XArray m_PluginsDep; /**< Plugins dependencies for this file */ XContainer::XArray m_IncludedFiles; /**< List of files that should be inserted in the CMO file. */ - CKFileInfo m_FileInfo; /**< Headers summary */ + //CKFileInfo m_FileInfo; /**< Headers summary */ + + CKERROR PrepareFile(CKSTRING filename); CKContext* m_Ctx; CKFileVisitor m_Visitor; diff --git a/LibCmo/CK2/CKFileOthers.cpp b/LibCmo/CK2/CKFileOthers.cpp index b0bcc22..3d3e8ca 100644 --- a/LibCmo/CK2/CKFileOthers.cpp +++ b/LibCmo/CK2/CKFileOthers.cpp @@ -1,5 +1,6 @@ #include "CKFile.hpp" #include "CKStateChunk.hpp" +#include "ObjImpls/CKObject.hpp" #include namespace LibCmo::CK2 { @@ -13,12 +14,12 @@ namespace LibCmo::CK2 { CKFileObject::CKFileObject() : ObjectId(0u), CreatedObjectId(0u), ObjectCid(CK_CLASSID::CKCID_OBJECT), ObjPtr(nullptr), Name(), Data(nullptr), Options(CK_FO_OPTIONS::CK_FO_DEFAULT), - FileIndex(0u), SaveFlags(CK_STATESAVE_ALL) {} + FileIndex(0u), SaveFlags(CK_STATESAVE_ALL), PackSize(0u) {} CKFileObject::CKFileObject(const CKFileObject& rhs) : ObjectId(rhs.ObjectId), CreatedObjectId(rhs.CreatedObjectId), ObjectCid(rhs.ObjectCid), ObjPtr(rhs.ObjPtr), Name(rhs.Name), Data(rhs.Data), Options(rhs.Options), - FileIndex(rhs.FileIndex), SaveFlags(rhs.SaveFlags) { + FileIndex(rhs.FileIndex), SaveFlags(rhs.SaveFlags), PackSize(rhs.PackSize) { if (this->Data != nullptr) { this->Data = new CKStateChunk(*(rhs.Data)); } @@ -27,7 +28,7 @@ namespace LibCmo::CK2 { CKFileObject::CKFileObject(CKFileObject&& rhs) : ObjectId(rhs.ObjectId), CreatedObjectId(rhs.CreatedObjectId), ObjectCid(rhs.ObjectCid), ObjPtr(rhs.ObjPtr), Name(rhs.Name), Data(rhs.Data), Options(rhs.Options), - FileIndex(rhs.FileIndex), SaveFlags(rhs.SaveFlags) { + FileIndex(rhs.FileIndex), SaveFlags(rhs.SaveFlags), PackSize(rhs.PackSize) { rhs.Data = nullptr; } @@ -43,6 +44,7 @@ namespace LibCmo::CK2 { this->Data = new CKStateChunk(*(rhs.Data)); } + this->PackSize = rhs.PackSize; this->Options = rhs.Options; this->FileIndex = rhs.FileIndex; this->SaveFlags = rhs.SaveFlags; @@ -59,7 +61,8 @@ namespace LibCmo::CK2 { this->Data = rhs.Data; rhs.Data = nullptr; - + + this->PackSize = rhs.PackSize; this->Options = rhs.Options; this->FileIndex = rhs.FileIndex; this->SaveFlags = rhs.SaveFlags; @@ -160,16 +163,19 @@ namespace LibCmo::CK2 { CKFileWriter::CKFileWriter(CKContext* ctx) : m_Ctx(ctx), m_Visitor(this), m_Done(false), m_IsCopyFromReader(false), - m_FileObjects(), m_ManagersData(), m_PluginsDep(), m_IncludedFiles(), - m_FileInfo() + m_SaveIDMax(0), + m_FileObjects(), m_ManagersData(), m_PluginsDep(), m_IncludedFiles() {} CKFileWriter::CKFileWriter(CKContext* ctx, CKFileReader* reader) : m_Ctx(ctx), m_Visitor(this), m_Done(false), m_IsCopyFromReader(true), - m_FileObjects(), m_ManagersData(), m_PluginsDep(), m_IncludedFiles(), - m_FileInfo() + m_SaveIDMax(0), + m_FileObjects(), m_ManagersData(), m_PluginsDep(), m_IncludedFiles() { + // sync save id max + this->m_SaveIDMax = reader->GetSaveIdMax(); + // copy objects for (const auto& item : reader->GetFileObjects()) { CKFileObject obj; @@ -182,8 +188,11 @@ namespace LibCmo::CK2 { obj.Data = new CKStateChunk(*item.Data); } - // copy save flag + // copy other data + obj.ObjectId = item.ObjectId; + obj.ObjectCid = item.ObjectCid; obj.SaveFlags = item.SaveFlags; + obj.Name = item.Name; // insert m_FileObjects.emplace_back(std::move(obj)); diff --git a/LibCmo/CK2/CKFileReader.cpp b/LibCmo/CK2/CKFileReader.cpp index f3fbd1a..6e984a7 100644 --- a/LibCmo/CK2/CKFileReader.cpp +++ b/LibCmo/CK2/CKFileReader.cpp @@ -16,7 +16,7 @@ namespace LibCmo::CK2 { CKERROR CKFileReader::ShallowLoad(CKSTRING u8_filename) { // check document status - if (this->m_Done) LIBPANIC("Can not load multiple times for single CKFileReader.") + if (this->m_Done) CKERROR::CKERR_CANCELLED; // check file and open memory if (u8_filename == nullptr) return CKERROR::CKERR_INVALIDPARAMETER; @@ -46,7 +46,7 @@ namespace LibCmo::CK2 { // ========== read header ========== // check header size if (parser->GetSize() < sizeof(CKRawFileInfo)) return CKERROR::CKERR_INVALIDFILE; - if (std::memcmp(parser->GetPtr(), "Nemo Fi", sizeof(CKRawFileInfo::NeMo))) return CKERROR::CKERR_INVALIDFILE; + if (std::memcmp(parser->GetPtr(), CKNEMOFI, sizeof(CKRawFileInfo::NeMo))) return CKERROR::CKERR_INVALIDFILE; // read header CKRawFileInfo rawHeader; parser->Read(&rawHeader, sizeof(CKRawFileInfo)); @@ -168,7 +168,7 @@ namespace LibCmo::CK2 { parser->Read(&includedFileCount, sizeof(CKDWORD)); this->m_IncludedFiles.resize(includedFileCount); - hasIncludedFile -= 4; + hasIncludedFile -= sizeof(CKDWORD); } // MARK: backward pos @@ -263,13 +263,12 @@ namespace LibCmo::CK2 { // only works file version >= 4. < 4 section has been removed. if (this->m_FileInfo.ObjectCount != 0) { // new file reader section - CKDWORD stateChunkLen = 0u; bool stateChkParseSuccess = false; for (auto& obj : this->m_FileObjects) { // get statechunk len - parser->Read(&stateChunkLen, sizeof(CKDWORD)); + parser->Read(&obj.PackSize, sizeof(CKDWORD)); // check state chunk len - if (stateChunkLen == 0) { + if (obj.PackSize == 0) { obj.Data = nullptr; continue; } @@ -281,7 +280,7 @@ namespace LibCmo::CK2 { delete obj.Data; obj.Data = nullptr; } - parser->MoveCursor(stateChunkLen); + parser->MoveCursor(obj.PackSize); } } @@ -311,7 +310,7 @@ namespace LibCmo::CK2 { parser->Read(&filebodylen, sizeof(CKDWORD)); // read file body - FILE* fp = m_Ctx->OpenTempFile(file.c_str(), false); + FILE* fp = m_Ctx->OpenTempFile(file.c_str(), "wb"); if (fp != nullptr) { StreamHelper::CopyStream(parser->GetPtr(), fp, filebodylen); fclose(fp); @@ -326,6 +325,9 @@ namespace LibCmo::CK2 { } CKERROR CKFileReader::DeepLoad(CKSTRING u8_filename) { + // check document status + if (this->m_Done) CKERROR::CKERR_CANCELLED; + // ========== prepare work ========== CKERROR err = CKERROR::CKERR_OK; diff --git a/LibCmo/CK2/CKFileWriter.cpp b/LibCmo/CK2/CKFileWriter.cpp index c2dd28a..0211429 100644 --- a/LibCmo/CK2/CKFileWriter.cpp +++ b/LibCmo/CK2/CKFileWriter.cpp @@ -1,9 +1,212 @@ #include "CKFile.hpp" +#include "CKContext.hpp" +#include "CKStateChunk.hpp" +#include "ObjImpls/CKObject.hpp" +#include "MgrImpls/CKBaseManager.hpp" +#include "../VxMath/VxMemoryMappedFile.hpp" +#include namespace LibCmo::CK2 { CKERROR CKFileWriter::Save(CKSTRING u8_filename) { - return CKERROR::CKERR_OK; + // check document status + if (this->m_Done) CKERROR::CKERR_CANCELLED; + + // try detect filename legality + CKERROR err = PrepareFile(u8_filename); + if (err != CKERROR::CKERR_OK) return err; + + // ========== Prepare Stage ========== + // todo: add TOBEDELETED flag for all Referenced objects's m_ObjectFlags + + // MARK: ignore the notification to all CKBehavior based objects. + + // ========== StateChunk convertion ========== + + // iterate all objects and transform it into CKStateChunk + // MARK: Drop the support of collecting the sum of InterfaceChunk's size. + // because it is useless. + for (auto& obj : m_FileObjects) { + // if there is a chunk, skip + if (obj.Data != nullptr) continue; + + obj.Data = new CKStateChunk(&m_Visitor, m_Ctx); + obj.Data->StartWrite(); + bool suc = obj.ObjPtr->Save(obj.Data, &m_Visitor, obj.SaveFlags); + obj.Data->StopWrite(); + if (!suc) { + // fail to parse + delete obj.Data; + obj.Data = nullptr; + } + } + + // iterate manager + // if copied from reader. skip + if (!m_IsCopyFromReader) { + CKINT mgrcount = m_Ctx->GetManagerCount(); + CKINT availablemgr = 0; + + // place manager + // if no data, skip it + m_ManagersData.resize(mgrcount); + for (CKINT i = 0; i < mgrcount; ++i) { + MgrImpls::CKBaseManager* mgr = m_Ctx->GetManager(i); + + m_ManagersData[availablemgr].Manager = mgr->GetGuid(); + + m_ManagersData[availablemgr].Data = new CKStateChunk(&m_Visitor, m_Ctx); + m_ManagersData[availablemgr].Data->StartWrite(); + bool suc = mgr->SaveData(m_ManagersData[availablemgr].Data, &m_Visitor); + m_ManagersData[availablemgr].Data->StopWrite(); + if (!suc) { + delete m_ManagersData[availablemgr].Data; + m_ManagersData[availablemgr].Data = nullptr; + } else { + ++availablemgr; + } + } + // resize to the new size which erase all skipped manager + m_ManagersData.resize(availablemgr); + + } + + // if copied from reader, skip plugin dep + if (!m_IsCopyFromReader) { + // todo: finish plugin dep filling + } + + // MARK: skip include file filling. + // we order user manually fill it. + + // ========== Size Calc ========== + // iterate 3 list to get each parts' size + CKDWORD sumDataObjSize = 0, + sumHdrObjSize = 0; + for (auto& obj : m_FileObjects) { + // += 4DWORD(ObjId, ObjCid, FileIndex, NameLen) + Name size + sumHdrObjSize += 4 * sizeof(CKDWORD) + obj.Name.size(); + + if (obj.Data == nullptr) { + obj.PackSize = 0; + } else { + obj.PackSize = obj.Data->ConvertToBuffer(nullptr); + } + // += chunk size + chunk + sumDataObjSize += obj.PackSize + sizeof(CKDWORD); + } + + CKDWORD sumDataMgrSize = 0; + for (auto& mgr : m_ManagersData) { + CKDWORD chunksize; + if (mgr.Data == nullptr) { + chunksize = 0; + } else { + chunksize = mgr.Data->ConvertToBuffer(nullptr); + } + // += GUID(2 DWORD) + chunk size + chunk + sumDataMgrSize += chunksize + 3 * sizeof(CKDWORD); + } + + // += Plugin Dep list size + CKDWORD sumHdrPlgSize = sizeof(CKDWORD); + for (auto& plg : m_PluginsDep) { + // += GUID list + (dep category + GUID list size) + sumHdrPlgSize += sizeof(CKGUID) * plg.m_Guids.size() + 2 * sizeof(CKDWORD); + } + + CKDWORD sumHdrIncludedFiles = sizeof(int32_t) + sizeof(CKDWORD); + + // calc the whole size + CKDWORD sumHdrSize = sumHdrObjSize + sumHdrPlgSize + sumHdrIncludedFiles; + CKDWORD sumDataSize = sumDataObjSize + sumDataMgrSize; + + // compute file index for all object + if (!m_FileObjects.empty()) { + // set base for first one + m_FileObjects[0].FileIndex = sumHdrSize + sumDataMgrSize + sizeof(CKRawFileInfo); + // calc the remains + for (size_t i = 1; i < m_FileObjects.size(); ++i) { + // prev obj PackSize + prev obj FileIndex + prev obj chunk size + m_FileObjects[i].FileIndex = m_FileObjects[i - 1].FileIndex + m_FileObjects[i - 1].PackSize + sizeof(CKDWORD); + } + } + + // ========== Construct File Header ========== + CKRawFileInfo rawHeader; + std::memcpy(&rawHeader.NeMo, CKNEMOFI, sizeof(CKRawFileInfo::NeMo)); + rawHeader.Crc = 0; + rawHeader.Zero = 0; + rawHeader.CKVersion = CKVERSION; + rawHeader.FileVersion = 8; + rawHeader.FileWriteMode = static_cast(m_Ctx->GetFileWriteMode()); + rawHeader.ObjectCount = static_cast(m_FileObjects.size()); + rawHeader.ManagerCount = static_cast(m_ManagersData.size()); + rawHeader.Hdr1UnPackSize = sumHdrSize; + rawHeader.DataUnPackSize = sumDataSize; + rawHeader.Hdr1PackSize = sumHdrSize; + rawHeader.DataPackSize = sumDataSize; + rawHeader.ProductVersion = DEVVERSION; + rawHeader.ProductBuild = DEVBUILD; + rawHeader.MaxIDSaved = m_SaveIDMax; + // crc will filled later + + // create a encoding conversion helper string + std::string name_conv; + + // ========== Writing header ========== + // create a buffer + std::unique_ptr hdrparser(new CKBufferParser(sumHdrSize)); + + // write obj + for (auto& obj : m_FileObjects) { + + // todo: remove TOBEDELETED for referenced objects' m_ObjectFlags + + hdrparser->Write(&obj.ObjectId, sizeof(CK_ID)); + hdrparser->Write(&obj.ObjectCid, sizeof(CK_CLASSID)); + hdrparser->Write(&obj.FileIndex, sizeof(CKDWORD)); + + if (obj.Name.c_str() != nullptr) { + m_Ctx->GetNativeString(obj.Name.string(), name_conv); + CKDWORD namelen = static_cast(name_conv.size()); + hdrparser->Write(&namelen, sizeof(CKDWORD)); + hdrparser->Write(name_conv.data(), namelen); + } + } + + // write plugin dep + { + CKDWORD depsize = static_cast(m_PluginsDep.size()); + hdrparser->Write(&depsize, sizeof(CKDWORD)); + + for (auto& dep : m_PluginsDep) { + hdrparser->Write(&dep.m_PluginCategory, sizeof(CK_PLUGIN_TYPE)); + + CKDWORD guidsize = static_cast(dep.m_Guids.size()); + hdrparser->Write(&guidsize, sizeof(CKDWORD)); + + hdrparser->Write(dep.m_Guids.data(), sizeof(CKGUID) * guidsize); + } + } + + } + + CKERROR CKFileWriter::PrepareFile(CKSTRING filename) { + // check nullptr + if (filename == nullptr) return CKERROR::CKERR_INVALIDFILE; + + // try open file to check whether we can write it. + CKERROR err; + FILE* tryfile = m_Ctx->OpenFile(filename, "ab"); + if (tryfile == nullptr) { + err = CKERROR::CKERR_CANTWRITETOFILE; + } else { + err = CKERROR::CKERR_OK; + std::fclose(tryfile); + } + + return err; } } \ No newline at end of file diff --git a/LibCmo/CK2/CKGlobals.hpp b/LibCmo/CK2/CKGlobals.hpp index 6a2aae3..de31d93 100644 --- a/LibCmo/CK2/CKGlobals.hpp +++ b/LibCmo/CK2/CKGlobals.hpp @@ -46,10 +46,12 @@ namespace LibCmo::CK2 { CKDWORD CKComputeDataCRC(const void* data, CKINT size, CKDWORD PreviousCRC = 0); // ========== CKClass Registration ========== + void CKClassRegister(CK_CLASSID cid, CK_CLASSID parentCid, CKClassCreationFct createFct, CKClassReleaseFct relFct, CKClassNameFct nameFct); // ========== Class Hierarchy Management ========== + CKINT CKGetClassCount(); const CKClassDesc* CKGetClassDesc(CK_CLASSID cid); CKSTRING CKClassIDToString(CK_CLASSID cid); diff --git a/LibCmo/CK2/MgrImpls/CKBaseManager.hpp b/LibCmo/CK2/MgrImpls/CKBaseManager.hpp index 7592ba5..46fe30a 100644 --- a/LibCmo/CK2/MgrImpls/CKBaseManager.hpp +++ b/LibCmo/CK2/MgrImpls/CKBaseManager.hpp @@ -69,8 +69,8 @@ namespace LibCmo::CK2::MgrImpls { data for your manager. @see CKStateChunk, LoadData */ - virtual CKStateChunk* SaveData(CKFileVisitor* SavedFile) { - return nullptr; + virtual bool SaveData(CKStateChunk* chunk, CKFileVisitor* SavedFile) { + return true; } /** @brief Called to load manager data. diff --git a/LibCmo/CK2/ObjImpls/CKObject.cpp b/LibCmo/CK2/ObjImpls/CKObject.cpp index d5751aa..41d78b0 100644 --- a/LibCmo/CK2/ObjImpls/CKObject.cpp +++ b/LibCmo/CK2/ObjImpls/CKObject.cpp @@ -6,8 +6,8 @@ namespace LibCmo::CK2::ObjImpls { void CKObject::PreSave(CKFileVisitor* file, CKDWORD flags) {} - CKStateChunk* CKObject::Save(CKFileVisitor* file, CKDWORD flags) { - return nullptr; + bool CKObject::Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) { + return true; } bool CKObject::Load(CKStateChunk* chunk, CKFileVisitor* file) { diff --git a/LibCmo/CK2/ObjImpls/CKObject.hpp b/LibCmo/CK2/ObjImpls/CKObject.hpp index 4b3985d..e42282f 100644 --- a/LibCmo/CK2/ObjImpls/CKObject.hpp +++ b/LibCmo/CK2/ObjImpls/CKObject.hpp @@ -40,7 +40,7 @@ namespace LibCmo::CK2::ObjImpls { } virtual void PreSave(CKFileVisitor* file, CKDWORD flags); - virtual CKStateChunk* Save(CKFileVisitor* file, CKDWORD flags); + virtual bool Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags); virtual bool Load(CKStateChunk* chunk, CKFileVisitor* file); virtual void PostLoad(); diff --git a/LibCmo/VTEncoding.cpp b/LibCmo/VTEncoding.cpp index 10e7b97..69f3c6c 100644 --- a/LibCmo/VTEncoding.cpp +++ b/LibCmo/VTEncoding.cpp @@ -187,9 +187,15 @@ namespace LibCmo::EncodingHelper { stdpath = u8_path; } } - - FILE* OpenStdPathFile(std::filesystem::path& u8_filepath, bool is_read) { - return _wfopen(u8_filepath.wstring().c_str(), is_read ? L"rb" : L"wb"); + + FILE* StdPathFOpen(std::filesystem::path& std_filepath, const char* u8_mode) { + std::wstring wmode; + if (CharToWchar(u8_mode, wmode, CP_UTF8)) { + return _wfopen(std_filepath.wstring().c_str(), wmode.c_str()); + } else { + // fallback + return std::fopen(std_filepath.string().c_str(), u8_mode); + } } #else @@ -237,8 +243,8 @@ namespace LibCmo::EncodingHelper { stdpath = u8_path; } - FILE* OpenStdPathFile(std::filesystem::path& u8_filepath, bool is_read) { - return fopen(u8_filepath.string().c_str(), is_read ? "rb" : "wb"); + FILE* StdPathFOpen(std::filesystem::path& std_filepath, const char* u8_mode) { + return std::fopen(u8_filepath.string().c_str(), u8_mode); } diff --git a/LibCmo/VTEncoding.hpp b/LibCmo/VTEncoding.hpp index 173a104..193217e 100644 --- a/LibCmo/VTEncoding.hpp +++ b/LibCmo/VTEncoding.hpp @@ -72,7 +72,7 @@ namespace LibCmo::EncodingHelper { bool GetNativeVirtoolsName(const std::string& u8_name, std::string& native_name, const ENCODING_TOKEN& token); void SetStdPathFromU8Path(std::filesystem::path& stdpath, const char* u8_path); - FILE* OpenStdPathFile(std::filesystem::path& u8_filepath, bool is_read); + FILE* StdPathFOpen(std::filesystem::path& std_filepath, const char* u8_mode); #pragma endregion diff --git a/LibCmo/VTUtils.hpp b/LibCmo/VTUtils.hpp index d750cbf..8c27bb9 100644 --- a/LibCmo/VTUtils.hpp +++ b/LibCmo/VTUtils.hpp @@ -119,7 +119,9 @@ namespace LibCmo { const char* c_str() const { return m_HasStr ? m_Str.c_str() : nullptr; } - + const std::string& string() const { + return m_Str; + } const size_t size() const { return m_HasStr ? m_Str.size() : 0u; }