diff --git a/LibCmo/CKFile.cpp b/LibCmo/CKFile.cpp index d9fa213..1484bca 100644 --- a/LibCmo/CKFile.cpp +++ b/LibCmo/CKFile.cpp @@ -3,25 +3,6 @@ namespace LibCmo { - -#pragma region CKMinContext - - CKMinContext::CKMinContext() { - - } - CKMinContext::~CKMinContext() { - - } - - void CKMinContext::Printf(CKSTRING fmt, ...) { - va_list argptr; - va_start(argptr, fmt); - vfprintf(stdout, fmt, argptr); - va_end(argptr); - } - -#pragma endregion - #pragma region CKBufferParser CKBufferParser::CKBufferParser(void* ptr, size_t rsize, bool need_manual_free) : @@ -36,6 +17,16 @@ namespace LibCmo { #pragma endregion +#pragma region CKFileInfo + + CKFileInfo::CKFileInfo() { + } + + CKFileInfo::~CKFileInfo() { + } + +#pragma endregion + #pragma region CKFileObject LibCmo::CKFileObject::CKFileObject() : @@ -49,6 +40,25 @@ namespace LibCmo { #pragma endregion +#pragma region CKFileManagerData + + CKFileManagerData::CKFileManagerData() { + } + + CKFileManagerData::~CKFileManagerData() { + } + +#pragma endregion + +#pragma region CKFilePluginDependencies + + CKFilePluginDependencies::CKFilePluginDependencies() { + } + + CKFilePluginDependencies::~CKFilePluginDependencies() { + } + +#pragma endregion #pragma region ShallowDocument @@ -74,12 +84,10 @@ namespace LibCmo { #pragma endregion - #pragma region CKFile Misc CKFile::CKFile(CKMinContext* ctx) : - m_MappedFile(nullptr), m_FileName(), - m_MinCtx(ctx) { + m_FileName(), m_MinCtx(ctx) { ; } @@ -87,7 +95,6 @@ namespace LibCmo { } - #pragma endregion diff --git a/LibCmo/CKFile.hpp b/LibCmo/CKFile.hpp index de7e957..a252ced 100644 --- a/LibCmo/CKFile.hpp +++ b/LibCmo/CKFile.hpp @@ -5,25 +5,10 @@ #include "VxMemoryMappedFile.hpp" #include "CKStateChunk.hpp" -#include "VTObjects.hpp" +#include "CKMinContext.hpp" namespace LibCmo { - class CKMinContext { - public: - CKMinContext(); - CKMinContext(const CKMinContext&) = delete; - CKMinContext& operator=(const CKMinContext&) = delete; - ~CKMinContext(); - - void Printf(CKSTRING fmt, ...); - - private: - - std::string NameEncoding; - std::string TempFolder; - }; - class CKBufferParser { private: char* m_MemBegin; @@ -103,16 +88,14 @@ namespace LibCmo { class CKFileObject { public: CKFileObject(); - CKFileObject(const CKFileObject&) = delete; - CKFileObject& operator=(const CKFileObject&) = delete; + //CKFileObject(const CKFileObject&) = delete; + //CKFileObject& operator=(const CKFileObject&) = delete; ~CKFileObject(); CK_ID ObjectId; CK_CLASSID ObjectCid; std::string Name; CKStateChunk* Data; - CKDWORD PostPackSize; - CKDWORD PrePackSize; CKDWORD FileIndex; private: @@ -121,8 +104,8 @@ namespace LibCmo { class CKFileManagerData { public: CKFileManagerData(); - CKFileManagerData(const CKFileManagerData&) = delete; - CKFileManagerData& operator=(const CKFileManagerData&) = delete; + //CKFileManagerData(const CKFileManagerData&) = delete; + //CKFileManagerData& operator=(const CKFileManagerData&) = delete; ~CKFileManagerData(); CKStateChunk* Data; @@ -134,8 +117,8 @@ namespace LibCmo { class CKFilePluginDependencies { public: CKFilePluginDependencies(); - CKFilePluginDependencies(const CKFilePluginDependencies&) = delete; - CKFilePluginDependencies& operator=(const CKFilePluginDependencies&) = delete; + //CKFilePluginDependencies(const CKFilePluginDependencies&) = delete; + //CKFilePluginDependencies& operator=(const CKFilePluginDependencies&) = delete; ~CKFilePluginDependencies(); CK_PLUGIN_TYPE m_PluginCategory; @@ -239,19 +222,13 @@ namespace LibCmo { private: // reader function and variables - CKERROR PrepareLoad(CKSTRING u8_filename); - CKERROR ReadFileHeader(CKBufferParser* ParserPtr); - CKERROR ReadFileData(CKBufferParser* ParserPtr); - - CKFileData::ShallowDocument* m_ShallowDoc; - CKFileData::DeepDocument* m_DeepDoc; + CKERROR ReadFileHeader(CKBufferParser* ParserPtr, CKFileData::ShallowDocument* doc); + CKERROR ReadFileData(CKBufferParser* ParserPtr, CKFileData::ShallowDocument* doc); // writer function and varibales // shared function and variables std::string m_FileName; - VxMemoryMappedFile* m_MappedFile; - CKMinContext* m_MinCtx; }; diff --git a/LibCmo/CKFileReader.cpp b/LibCmo/CKFileReader.cpp index c0daaad..6ab1291 100644 --- a/LibCmo/CKFileReader.cpp +++ b/LibCmo/CKFileReader.cpp @@ -11,47 +11,45 @@ namespace LibCmo { * No need to support them. */ - CKERROR CKFile::PrepareLoad(CKSTRING u8_filename) { - // assign file name - this->m_FileName = u8_filename; - // create mapped file - this->m_MappedFile = new(std::nothrow) VxMemoryMappedFile(this->m_FileName.c_str()); - if (this->m_MappedFile == nullptr) { + CKERROR CKFile::ShallowLoad(CKSTRING u8_filename, CKFileData::ShallowDocument** out_doc) { + // preset value + *out_doc = nullptr; + + // check file and open memory + if (u8_filename == nullptr) return CKERROR::CKERR_INVALIDPARAMETER; + std::unique_ptr mappedFile(new(std::nothrow) VxMemoryMappedFile(u8_filename)); + if (mappedFile == nullptr) { this->m_MinCtx->Printf("Out of memory when creating Memory File."); return CKERROR::CKERR_OUTOFMEMORY; } - if (!this->m_MappedFile->IsValid()) { + if (!mappedFile->IsValid()) { this->m_MinCtx->Printf("Fail to create Memory File for \"%s\".", this->m_FileName.c_str()); return CKERROR::CKERR_INVALIDFILE; } - return CKERROR::CKERR_OK; - } - - CKERROR CKFile::ShallowLoad(CKSTRING u8_filename, CKFileData::ShallowDocument** out_doc) { - // check file and open memory - if (u8_filename == nullptr) return CKERROR::CKERR_INVALIDPARAMETER; - CKERROR err = PrepareLoad(u8_filename); - if (err != CKERROR::CKERR_OK) return err; - // create document - this->m_ShallowDoc = new(std::nothrow) CKFileData::ShallowDocument(); - if (this->m_ShallowDoc == nullptr) return CKERROR::CKERR_OUTOFMEMORY; + std::unique_ptr doc(new(std::nothrow) CKFileData::ShallowDocument()); + if (doc == nullptr) return CKERROR::CKERR_OUTOFMEMORY; // create buffer and start loading - CKBufferParser parser(this->m_MappedFile->GetBase(), this->m_MappedFile->GetFileSize(), false); - err = this->ReadFileHeader(&parser); + std::unique_ptr parser(new(std::nothrow) CKBufferParser(mappedFile->GetBase(), mappedFile->GetFileSize(), false)); + CKERROR err = this->ReadFileHeader(parser.get(), doc.get()); if (err != CKERROR::CKERR_OK) return err; - err = this->ReadFileData(&parser); + err = this->ReadFileData(parser.get(), doc.get()); if (err != CKERROR::CKERR_OK) return err; - // free all data - this->ClearData(); + // unbind document and assign it + *out_doc = doc.release(); + // other data will be free automatically return CKERROR::CKERR_OK; } - CKERROR CKFile::ReadFileHeader(CKBufferParser* ParserPtr) { + CKERROR CKFile::ReadFileHeader(CKBufferParser* ParserPtr, CKFileData::ShallowDocument* doc) { std::unique_ptr parser(new(std::nothrow) CKBufferParser(ParserPtr->GetBase(), ParserPtr->GetSize(), false)); + if (parser == nullptr) return CKERROR::CKERR_OUTOFMEMORY; + parser->SetCursor(ParserPtr->GetCursor()); + + std::string name_conv; // ========== read header ========== // check header size @@ -73,23 +71,23 @@ namespace LibCmo { } // ========== assign value ========== - this->m_ShallowDoc->m_FileInfo.ProductVersion = rawHeader.ProductVersion; - this->m_ShallowDoc->m_FileInfo.ProductBuild = rawHeader.ProductBuild; - this->m_ShallowDoc->m_FileInfo.FileWriteMode = static_cast(rawHeader.FileWriteMode); - this->m_ShallowDoc->m_FileInfo.CKVersion = rawHeader.CKVersion; - this->m_ShallowDoc->m_FileInfo.FileVersion = rawHeader.FileVersion; - this->m_ShallowDoc->m_FileInfo.FileSize = parser->GetSize(); - this->m_ShallowDoc->m_FileInfo.ManagerCount = rawHeader.ManagerCount; - this->m_ShallowDoc->m_FileInfo.ObjectCount = rawHeader.ObjectCount; - this->m_ShallowDoc->m_FileInfo.MaxIDSaved = rawHeader.MaxIDSaved; - this->m_ShallowDoc->m_FileInfo.Hdr1PackSize = rawHeader.FileVersion >= 8 ? rawHeader.Hdr1PackSize : 0u; - this->m_ShallowDoc->m_FileInfo.Hdr1UnPackSize = rawHeader.FileVersion >= 8 ? rawHeader.Hdr1UnPackSize : 0u; - this->m_ShallowDoc->m_FileInfo.DataPackSize = rawHeader.DataPackSize; - this->m_ShallowDoc->m_FileInfo.DataUnPackSize = rawHeader.DataUnPackSize; - this->m_ShallowDoc->m_FileInfo.Crc = rawHeader.Crc; + doc->m_FileInfo.ProductVersion = rawHeader.ProductVersion; + doc->m_FileInfo.ProductBuild = rawHeader.ProductBuild; + doc->m_FileInfo.FileWriteMode = static_cast(rawHeader.FileWriteMode); + doc->m_FileInfo.CKVersion = rawHeader.CKVersion; + doc->m_FileInfo.FileVersion = rawHeader.FileVersion; + doc->m_FileInfo.FileSize = parser->GetSize(); + doc->m_FileInfo.ManagerCount = rawHeader.ManagerCount; + doc->m_FileInfo.ObjectCount = rawHeader.ObjectCount; + doc->m_FileInfo.MaxIDSaved = rawHeader.MaxIDSaved; + doc->m_FileInfo.Hdr1PackSize = rawHeader.FileVersion >= 8 ? rawHeader.Hdr1PackSize : 0u; + doc->m_FileInfo.Hdr1UnPackSize = rawHeader.FileVersion >= 8 ? rawHeader.Hdr1UnPackSize : 0u; + doc->m_FileInfo.DataPackSize = rawHeader.DataPackSize; + doc->m_FileInfo.DataUnPackSize = rawHeader.DataUnPackSize; + doc->m_FileInfo.Crc = rawHeader.Crc; // ========== crc and body unpacker ========== - if (this->m_ShallowDoc->m_FileInfo.FileVersion >= 8) { + if (doc->m_FileInfo.FileVersion >= 8) { // crc checker for file ver >= 8 // reset crc field of header rawHeader.Crc = 0u; @@ -97,19 +95,22 @@ namespace LibCmo { // compute crc CKDWORD gotten_crc = CKComputeDataCRC(&rawHeader, sizeof(CKRawFileInfo), 0u); parser->SetCursor(sizeof(CKRawFileInfo)); - gotten_crc = CKComputeDataCRC(parser->GetPtr(), this->m_ShallowDoc->m_FileInfo.Hdr1PackSize, gotten_crc); - parser->MoveCursor(this->m_ShallowDoc->m_FileInfo.Hdr1PackSize); - gotten_crc = CKComputeDataCRC(parser->GetPtr(), this->m_ShallowDoc->m_FileInfo.DataPackSize, gotten_crc); + gotten_crc = CKComputeDataCRC(parser->GetPtr(), doc->m_FileInfo.Hdr1PackSize, gotten_crc); + parser->MoveCursor(doc->m_FileInfo.Hdr1PackSize); + gotten_crc = CKComputeDataCRC(parser->GetPtr(), doc->m_FileInfo.DataPackSize, gotten_crc); - if (gotten_crc != this->m_ShallowDoc->m_FileInfo.Crc) return CKERROR::CKERR_FILECRCERROR; + if (gotten_crc != doc->m_FileInfo.Crc) { + this->m_MinCtx->Printf("Virtools file CRC error."); + return CKERROR::CKERR_FILECRCERROR; + } // reset cursor parser->SetCursor(sizeof(CKRawFileInfo)); // compare size to decide wheher use compress feature - void* decomp_buffer = CKUnPackData(this->m_ShallowDoc->m_FileInfo.Hdr1UnPackSize, parser->GetPtr(), this->m_ShallowDoc->m_FileInfo.Hdr1PackSize); + void* decomp_buffer = CKUnPackData(doc->m_FileInfo.Hdr1UnPackSize, parser->GetPtr(), doc->m_FileInfo.Hdr1PackSize); if (decomp_buffer != nullptr) { - parser = std::unique_ptr(new(std::nothrow) CKBufferParser(decomp_buffer, this->m_ShallowDoc->m_FileInfo.Hdr1UnPackSize, true)); + parser = std::unique_ptr(new(std::nothrow) CKBufferParser(decomp_buffer, doc->m_FileInfo.Hdr1UnPackSize, true)); if (parser == nullptr) { delete[] decomp_buffer; return CKERROR::CKERR_OUTOFMEMORY; @@ -119,14 +120,14 @@ namespace LibCmo { // ========== object list read ========== // file ver >= 7 have this features - if (this->m_ShallowDoc->m_FileInfo.FileVersion >= 7) { + { // apply max id saved - this->m_ShallowDoc->m_SaveIDMax = this->m_ShallowDoc->m_FileInfo.MaxIDSaved; + doc->m_SaveIDMax = doc->m_FileInfo.MaxIDSaved; // resize - this->m_ShallowDoc->m_FileObjects.resize(this->m_ShallowDoc->m_FileInfo.ObjectCount); + doc->m_FileObjects.resize(doc->m_FileInfo.ObjectCount); // read data - for (auto& fileobj : this->m_ShallowDoc->m_FileObjects) { + for (auto& fileobj : doc->m_FileObjects) { // read basic fields parser->Read(&(fileobj.ObjectId), sizeof(CK_ID)); parser->Read(&(fileobj.ObjectCid), sizeof(CK_CLASSID)); @@ -135,8 +136,9 @@ namespace LibCmo { CKDWORD namelen; parser->Read(&namelen, sizeof(CKDWORD)); if (namelen != 0) { - fileobj.Name.resize(namelen); - parser->Read(fileobj.Name.data(), namelen); + name_conv.resize(namelen); + parser->Read(name_conv.data(), namelen); + m_MinCtx->GetUtf8ObjectName(name_conv, fileobj.Name); } } } @@ -144,14 +146,14 @@ namespace LibCmo { // ========== dep list read ========== // file ver >= 8 have this feature bool noDepLost = true; - if (this->m_ShallowDoc->m_FileInfo.FileVersion >= 8) { + if (doc->m_FileInfo.FileVersion >= 8) { // get size and resize CKDWORD depSize; parser->Read(&depSize, sizeof(CKDWORD)); - this->m_ShallowDoc->m_PluginDep.resize(depSize); + doc->m_PluginDep.resize(depSize); CKDWORD guid_size; - for (auto& dep : this->m_ShallowDoc->m_PluginDep) { + for (auto& dep : doc->m_PluginDep) { // read category parser->Read(&(dep.m_PluginCategory), sizeof(CK_PLUGIN_TYPE)); // get size and resize @@ -167,7 +169,7 @@ namespace LibCmo { // ========== included file list read ========== // file ver >= 8 have this feature - if (this->m_ShallowDoc->m_FileInfo.FileVersion >= 8) { + if (doc->m_FileInfo.FileVersion >= 8) { // MARK: i don't knwo what is this! int32_t hasIncludedFile; parser->Read(&hasIncludedFile, sizeof(int32_t)); @@ -176,20 +178,173 @@ namespace LibCmo { // read included file size and resize CKDWORD includedFileCount; parser->Read(&includedFileCount, sizeof(CKDWORD)); - this->m_ShallowDoc->m_IncludedFiles.resize(includedFileCount); + doc->m_IncludedFiles.resize(includedFileCount); hasIncludedFile -= 4; } - // backward pos - parser->SetCursor(hasIncludedFile); + // MARK: backward pos + // backward with 0? + parser->MoveCursor(hasIncludedFile); + } + + // ========== sync main parser ========== + if (doc->m_FileInfo.FileVersion >= 8) { + // file ver >= 8, use header offset + // because it have compress feature + ParserPtr->SetCursor(doc->m_FileInfo.Hdr1PackSize + sizeof(CKRawFileInfo)); + } else { + // otherwise, sync with current parser. + ParserPtr->SetCursor(parser->GetCursor()); } return CKERROR::CKERR_OK; } - CKERROR CKFile::ReadFileData(CKBufferParser* ParserPtr) { - return CKERROR(); + CKERROR CKFile::ReadFileData(CKBufferParser* ParserPtr, CKFileData::ShallowDocument* doc) { + std::unique_ptr parser(new(std::nothrow) CKBufferParser(ParserPtr->GetBase(), ParserPtr->GetSize(), false)); + if (parser == nullptr) return CKERROR::CKERR_OUTOFMEMORY; + parser->SetCursor(ParserPtr->GetCursor()); + + std::string name_conv; + + // ========== compress feature process ========== + if (EnumsHelper::FlagEnumHas(doc->m_FileInfo.FileWriteMode, CK_FILE_WRITEMODE::CKFILE_CHUNKCOMPRESSED_OLD) || + EnumsHelper::FlagEnumHas(doc->m_FileInfo.FileWriteMode, CK_FILE_WRITEMODE::CKFILE_WHOLECOMPRESSED)) { + + void* decomp_buffer = CKUnPackData(doc->m_FileInfo.DataUnPackSize, parser->GetPtr(), doc->m_FileInfo.DataPackSize); + if (decomp_buffer != nullptr) { + parser = std::unique_ptr(new(std::nothrow) CKBufferParser(decomp_buffer, doc->m_FileInfo.DataUnPackSize, true)); + + if (parser == nullptr) { + delete[] decomp_buffer; + return CKERROR::CKERR_OUTOFMEMORY; + } + } + } + + // ========== old file crc and obj list read ========== + // only file ver < 8 run this + if (doc->m_FileInfo.FileVersion < 8) { + // check crc + CKDWORD gotten_crc = CKComputeDataCRC( + parser->GetPtr(), + parser->GetSize() - parser->GetCursor(), + 0u + ); + if (gotten_crc != doc->m_FileInfo.Crc) { + this->m_MinCtx->Printf("Virtools file CRC error."); + return CKERROR::CKERR_FILECRCERROR; + } + + // MARK: why read again? especially for file ver == 7. + // get save id max + parser->Read(&doc->m_SaveIDMax, sizeof(int32_t)); + // get object count and resize + parser->Read(&doc->m_FileInfo.ObjectCount, sizeof(CKDWORD)); + if (doc->m_FileObjects.empty()) { + doc->m_FileObjects.resize(doc->m_FileInfo.ObjectCount); + } + } + + // ========== manager read ========== + // only file ver >= 6 have this + if (doc->m_FileInfo.ManagerCount != 0) { + doc->m_FileManagersData.resize(doc->m_FileInfo.ManagerCount); + CKDWORD stateChunkLen = 0u; + bool stateChkParseSuccess = false; + + for (auto& mgr : doc->m_FileManagersData) { + // read guid + parser->Read(&(mgr.Manager), sizeof(CKGUID)); + + // read statechunk len + parser->Read(&stateChunkLen, sizeof(CKDWORD)); + // check len + if (stateChunkLen == 0) { + mgr.Data = nullptr; + continue; + } + + // read statechunk + mgr.Data = new(std::nothrow) CKStateChunk(); + if (mgr.Data != nullptr) { + stateChkParseSuccess = mgr.Data->ConvertFromBuffer(parser->GetPtr()); + if (!stateChkParseSuccess) { + delete mgr.Data; + mgr.Data = nullptr; + } + } + parser->MoveCursor(stateChunkLen); + } + } + + // ========== object read ========== + // only works file version >= 4. < 4 section has been removed. + if (doc->m_FileInfo.ObjectCount != 0) { + // new file reader section + CKDWORD stateChunkLen = 0u; + bool stateChkParseSuccess = false; + for (auto& obj : doc->m_FileObjects) { + // get statechunk len + parser->Read(&stateChunkLen, sizeof(CKDWORD)); + // check state chunk len + if (stateChunkLen == 0) { + obj.Data = nullptr; + continue; + } + + // read state chunk + obj.Data = new(std::nothrow) CKStateChunk(); + if (obj.Data != nullptr) { + stateChkParseSuccess = obj.Data->ConvertFromBuffer(parser->GetPtr()); + if (!stateChkParseSuccess) { + delete obj.Data; + obj.Data = nullptr; + } + } + parser->MoveCursor(stateChunkLen); + + } + } + + // ========== included file get ========== + // before reading, we need switch back to original parser. + // and skip data chunk size + parser = std::unique_ptr(new(std::nothrow) CKBufferParser(ParserPtr->GetBase(), ParserPtr->GetSize(), false)); + parser->MoveCursor(doc->m_FileInfo.DataPackSize); + + // then we can read it. + if (doc->m_IncludedFiles.size() != 0) { + for (auto& file : doc->m_IncludedFiles) { + // get file name length and resize it + CKDWORD filenamelen = 0u; + parser->Read(&filenamelen, sizeof(CKDWORD)); + name_conv.resize(filenamelen); + + // read filename + if (filenamelen != 0) { + parser->Read(name_conv.data(), filenamelen); + m_MinCtx->GetUtf8ObjectName(name_conv, file); + } + + // read file body length + CKDWORD filebodylen = 0u; + parser->Read(&filebodylen, sizeof(CKDWORD)); + + // read file body + FILE* fp = m_MinCtx->OpenTempFile(file.c_str(), false); + if (fp != nullptr) { + Utils::CopyStream(parser->GetPtr(), fp, filebodylen); + fclose(fp); + } + + // move to next + parser->MoveCursor(filebodylen); + } + } + + return CKERROR::CKERR_OK; } //CKERROR CKFile::Load(CKSTRING u8_filename, /*CKObjectArray list, */ CK_LOAD_FLAGS flags) { diff --git a/LibCmo/CKMinContext.cpp b/LibCmo/CKMinContext.cpp new file mode 100644 index 0000000..d3e758c --- /dev/null +++ b/LibCmo/CKMinContext.cpp @@ -0,0 +1,63 @@ +#include "VTUtils.hpp" +#include "CKMinContext.hpp" + +namespace LibCmo { + +#if defined(LIBCMO_OS_WIN32) + static wchar_t g_UniqueFolder[] = L"LibCmo"; +#else + static char g_UniqueFolder[] = "LibCmo"; +#endif + + + CKMinContext::CKMinContext() : + NameEncoding(), NameEncodingToken(Encoding::ENCODING_TOKEN_DEFAULT), + TempFolder() + { + // preset for temp folder + TempFolder = std::filesystem::temp_directory_path(); + TempFolder /= g_UniqueFolder; + std::filesystem::create_directory(TempFolder); + } + CKMinContext::~CKMinContext() { + + } + + void CKMinContext::Printf(CKSTRING fmt, ...) { + va_list argptr; + va_start(argptr, fmt); + vfprintf(stdout, fmt, argptr); + va_end(argptr); + } + + void CKMinContext::GetUtf8ObjectName(std::string& native_name, std::string& u8_name) { + Encoding::GetUtf8VirtoolsName(native_name, u8_name, this->NameEncodingToken); + } + + void CKMinContext::GetNativeObjectName(std::string& u8_name, std::string& native_name) { + Encoding::GetNativeVirtoolsName(u8_name, native_name, this->NameEncodingToken); + } + + void CKMinContext::SetEncoding(CKSTRING encoding) { + this->NameEncoding = encoding; + this->RefetchEncodingToken(); + } + + void CKMinContext::SetTempPath(CKSTRING u8_temp) { + Encoding::SetStdPathFromU8Path(this->TempFolder, u8_temp); + } + + FILE* CKMinContext::OpenTempFile(CKSTRING u8_filename, bool is_read) { + std::filesystem::path stdfilename; + Encoding::SetStdPathFromU8Path(stdfilename, u8_filename); + + auto realfile = this->TempFolder / stdfilename; + return Encoding::OpenStdPathFile(realfile, is_read); + } + + void CKMinContext::RefetchEncodingToken(void) { + Encoding::DestroyEncodingToken(this->NameEncodingToken); + this->NameEncodingToken = Encoding::CreateEncodingToken(this->NameEncoding); + } + +} \ No newline at end of file diff --git a/LibCmo/CKMinContext.hpp b/LibCmo/CKMinContext.hpp new file mode 100644 index 0000000..baf6781 --- /dev/null +++ b/LibCmo/CKMinContext.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include "CKDefines.hpp" +#include "CKEnums.hpp" +#include "VTEncoding.hpp" +#include + +namespace LibCmo { + + // forward decl to rm recursive reference + namespace ObjsImpl { class CKObject; } + + class CKMinContext { + public: + CKMinContext(); + CKMinContext(const CKMinContext&) = delete; + CKMinContext& operator=(const CKMinContext&) = delete; + ~CKMinContext(); + + void Printf(CKSTRING fmt, ...); + + ObjsImpl::CKObject* CreateObject(CK_ID id, CKSTRING name); + void DestroyObject(ObjsImpl::CKObject* obj); + + void GetUtf8ObjectName(std::string& native_name, std::string& u8_name); + void GetNativeObjectName(std::string& u8_name, std::string& native_name); + + void SetEncoding(CKSTRING encoding); + void SetTempPath(CKSTRING u8_temp); + + FILE* OpenTempFile(CKSTRING u8_filename, bool is_read); + + private: + void RefetchEncodingToken(void); + + std::string NameEncoding; + Encoding::ENCODING_TOKEN NameEncodingToken; + std::filesystem::path TempFolder; + }; + +} diff --git a/LibCmo/LibCmo.vcxproj b/LibCmo/LibCmo.vcxproj index 62863ee..1258b51 100644 --- a/LibCmo/LibCmo.vcxproj +++ b/LibCmo/LibCmo.vcxproj @@ -174,10 +174,12 @@ + + @@ -185,6 +187,7 @@ + diff --git a/LibCmo/LibCmo.vcxproj.filters b/LibCmo/LibCmo.vcxproj.filters index 126597a..6ef7c45 100644 --- a/LibCmo/LibCmo.vcxproj.filters +++ b/LibCmo/LibCmo.vcxproj.filters @@ -42,6 +42,12 @@ Sources + + Sources + + + Sources + @@ -71,5 +77,8 @@ Headers + + Headers + \ No newline at end of file diff --git a/LibCmo/VTEncoding.cpp b/LibCmo/VTEncoding.cpp index 0564363..f0319d5 100644 --- a/LibCmo/VTEncoding.cpp +++ b/LibCmo/VTEncoding.cpp @@ -60,6 +60,16 @@ namespace LibCmo { return CharToWchar(src.c_str(), dest, codepage); } + bool CharToChar(const char* src, std::string& dest, UINT src_codepage, UINT dest_codepage) { + std::wstring intermediary; + if (!CharToWchar(src, intermediary, src_codepage)) return false; + if (!WcharToChar(intermediary, dest, dest_codepage)) return false; + return true; + } + bool CharToChar(std::string& src, std::string& dest, UINT src_codepage, UINT dest_codepage) { + return CharToChar(src.c_str(), dest, src_codepage, dest_codepage); + } + #else //todo: linux implementation @@ -72,35 +82,66 @@ namespace LibCmo { #if defined(LIBCMO_OS_WIN32) - void GetUtf8VirtoolsName(std::string& native_name, std::string& u8_name, const char* u8_encoding_spec) { - // switch encoding spec - UINT codepage = CP_ACP; - if (!GetWindowsCodePage(u8_encoding_spec, &codepage)) { - u8_name = native_name.c_str(); - return; - } + const ENCODING_TOKEN ENCODING_TOKEN_DEFAULT = nullptr; - // do convert - std::wstring intermediary; - if (!CharToWchar(native_name, intermediary, codepage)) { - u8_name = native_name.c_str(); - return; - } - if (!WcharToChar(intermediary, u8_name, CP_UTF8)) { - u8_name = native_name.c_str(); - return; - } + ENCODING_TOKEN CreateEncodingToken(std::string& token_string) { + ENCODING_TOKEN token = new(std::nothrow) UINT(); + if (token == nullptr) return ENCODING_TOKEN_DEFAULT; - return; + if (!GetWindowsCodePage(token_string.c_str(), token)) { + *token = CP_ACP; + } + return token; + } + void DestroyEncodingToken(ENCODING_TOKEN token) { + if (token != ENCODING_TOKEN_DEFAULT) { + delete token; + } } - void GetWcharStdin(std::string& u8_input, std::wstring& wc_input) { - // just redirect to prev func - CharToWchar(u8_input, wc_input, CP_UTF8); + void GetUtf8VirtoolsName(std::string& native_name, std::string& u8_name, ENCODING_TOKEN token) { + if (token == ENCODING_TOKEN_DEFAULT) { + u8_name = native_name.c_str(); + return; + } + + // convert with fallback + if (!CharToChar(native_name, u8_name, *token, CP_UTF8)) { + u8_name = native_name.c_str(); + } + } + + void GetNativeVirtoolsName(std::string& u8_name, std::string& native_name, ENCODING_TOKEN token) { + if (token == ENCODING_TOKEN_DEFAULT) { + native_name = u8_name.c_str(); + return; + } + + // convert with fallback + if (!CharToChar(u8_name, native_name, CP_UTF8, *token)) { + native_name = u8_name.c_str(); + } + } + + void SetStdPathFromU8Path(std::filesystem::path& stdpath, const char* u8_path) { + std::wstring intermediary; + if (CharToWchar(u8_path, intermediary, CP_UTF8)) { + stdpath = intermediary.c_str(); + } else { + // fallback + 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"); } #else - //todo: linux implementation + + const ENCODING_TOKEN ENCODING_TOKEN_DEFAULT = nullptr; + + //todo: linux implementation #endif diff --git a/LibCmo/VTEncoding.hpp b/LibCmo/VTEncoding.hpp index 16ef964..9665cd3 100644 --- a/LibCmo/VTEncoding.hpp +++ b/LibCmo/VTEncoding.hpp @@ -2,6 +2,7 @@ #include "VTUtils.hpp" #include +#include #if defined(LIBCMO_OS_WIN32) #include @@ -25,6 +26,9 @@ namespace LibCmo { bool CharToWchar(const char* src, std::wstring& dest, UINT codepage); bool CharToWchar(std::string& src, std::wstring& dest, UINT codepage); + bool CharToChar(const char* src, std::string& dest, UINT src_codepage, UINT dest_codepage); + bool CharToChar(std::string& src, std::string& dest, UINT src_codepage, UINT dest_codepage); + #else #error NO IMPLEMENTATION FOR LINUX ENCODING! #endif @@ -35,9 +39,31 @@ namespace LibCmo { #if defined(LIBCMO_OS_WIN32) - void GetUtf8VirtoolsName(std::string& native_name, std::string& u8_name, const char* u8_encoding_spec); + /// + /// Token is the ticket for using encoding functions. + /// It should be created by "GenerateEncodingToken" and free by "DestroyEncodingToken". + /// + using ENCODING_TOKEN = UINT*; + extern const ENCODING_TOKEN ENCODING_TOKEN_DEFAULT; + + ENCODING_TOKEN CreateEncodingToken(std::string& token_string); + void DestroyEncodingToken(ENCODING_TOKEN token); + + void GetUtf8VirtoolsName(std::string& native_name, std::string& u8_name, ENCODING_TOKEN token); + void GetNativeVirtoolsName(std::string& u8_name, std::string& native_name, ENCODING_TOKEN token); + + void SetStdPathFromU8Path(std::filesystem::path& stdpath, const char* u8_path); + FILE* OpenStdPathFile(std::filesystem::path& u8_filepath, bool is_read); #else + + /// + /// Token is the ticket for using encoding functions. + /// It should be created by "GenerateEncodingToken" and free by "DestroyEncodingToken". + /// + using ENCODING_TOKEN = char*; + extern const ENCODING_TOKEN ENCODING_TOKEN_DEFAULT; + #error NO IMPLEMENTATION FOR LINUX ENCODING! #endif diff --git a/LibCmo/VTUtils.cpp b/LibCmo/VTUtils.cpp new file mode 100644 index 0000000..0ed8ddf --- /dev/null +++ b/LibCmo/VTUtils.cpp @@ -0,0 +1,24 @@ +#include "VTUtils.hpp" + +namespace LibCmo { + namespace Utils { + + + static constexpr const size_t CHUNK_SIZE = 10240; + void CopyStream(const void* src, FILE* dest, size_t len) { + fwrite(src, sizeof(char), len, dest); + } + void CopyStream(FILE* src, void* dest, size_t len) { + size_t expected_size = 0u; + char* p = reinterpret_cast(dest); + + while (len != 0) { + expected_size = len < CHUNK_SIZE ? len : CHUNK_SIZE; + fread(p, sizeof(char), expected_size, src); + p += expected_size; + len -= expected_size; + } + } + + } +} diff --git a/LibCmo/VTUtils.hpp b/LibCmo/VTUtils.hpp index efa5282..3097924 100644 --- a/LibCmo/VTUtils.hpp +++ b/LibCmo/VTUtils.hpp @@ -41,10 +41,13 @@ #define LIBCMO_OS_WIN32 #endif +#include + namespace LibCmo { namespace Utils { - + void CopyStream(const void* src, FILE* dest, size_t len); + void CopyStream(FILE* src, void* dest, size_t len); } } diff --git a/LibCmo/VxMemoryMappedFile.cpp b/LibCmo/VxMemoryMappedFile.cpp index f06ff33..7c3a34b 100644 --- a/LibCmo/VxMemoryMappedFile.cpp +++ b/LibCmo/VxMemoryMappedFile.cpp @@ -16,7 +16,7 @@ namespace LibCmo { // save file path #if defined(LIBCMO_OS_WIN32) - Encoding::CharToWchar(u8_filepath, this->m_szFilePath, CP_UTF8); + Encoding::SetStdPathFromU8Path(m_szFilePath, u8_filepath); #else this->m_szFilePath = u8_filepath; #endif @@ -26,7 +26,7 @@ namespace LibCmo { // open file this->m_hFile = CreateFileW( - this->m_szFilePath.c_str(), + this->m_szFilePath.wstring().c_str(), GENERIC_READ, 0, // do not share NULL, // no security diff --git a/LibCmo/VxMemoryMappedFile.hpp b/LibCmo/VxMemoryMappedFile.hpp index d7cbaa4..29437c7 100644 --- a/LibCmo/VxMemoryMappedFile.hpp +++ b/LibCmo/VxMemoryMappedFile.hpp @@ -8,23 +8,25 @@ #endif #include +#include namespace LibCmo { class VxMemoryMappedFile { private: + #if defined(LIBCMO_OS_WIN32) - std::wstring m_szFilePath; HANDLE m_hFile; DWORD m_dwFileSizeLow, m_dwFileSizeHigh; HANDLE m_hFileMapping; LPVOID m_hFileMapView; #else - std::string m_szFilePath; + #error NO IMPLEMENTATION FOR LINUX MMAP! #endif + std::filesystem::path m_szFilePath; void* m_pMemoryMappedFileBase; size_t m_cbFile; bool m_bIsValid; diff --git a/Unvirt/StructFormatter.cpp b/Unvirt/StructFormatter.cpp index 775adab..098cf23 100644 --- a/Unvirt/StructFormatter.cpp +++ b/Unvirt/StructFormatter.cpp @@ -11,8 +11,11 @@ namespace Unvirt { std::string container; fputs(UNVIRT_TERMCOL_LIGHT_YELLOW(("CKFileInfo\n")), fout); - fprintf(fout, "Version (File / CK): %" PRIu32 " / 0x%08" PRIX32 "\n", - fileinfo.FileVersion, fileinfo.CKVersion + fprintf(fout, "Version (File / CK): %" PRIu32 " / %02" PRIX32 "/%02" PRIX32 "/%04" PRIX32 "\n", + fileinfo.FileVersion, + (fileinfo.CKVersion >> 24) & 0xFF, + (fileinfo.CKVersion >> 16) & 0xFF, + (fileinfo.CKVersion >> 0) & 0xFFFF ); LibCmo::CKDWORD product_series[4]{ diff --git a/Unvirt/Unvirt.cpp b/Unvirt/Unvirt.cpp index eb4cb99..16f7361 100644 --- a/Unvirt/Unvirt.cpp +++ b/Unvirt/Unvirt.cpp @@ -10,11 +10,15 @@ int main(int argc, char* argv[]) { Unvirt::TerminalHelper::EnsureTerminalEncoding(); LibCmo::CKMinContext vtctx; + vtctx.SetTempPath("Temp"); + vtctx.SetEncoding("850"); + LibCmo::CKFile vtfile(&vtctx); LibCmo::CKFileData::ShallowDocument* doc; - LibCmo::CKERROR err = vtfile.ShallowLoad("Language.old.nmo", &doc); + LibCmo::CKERROR err = vtfile.ShallowLoad("combining behaviors.cmo", &doc); - Unvirt::StructFormatter::PrintCKFileInfo(doc->m_FileInfo); + if (doc) + Unvirt::StructFormatter::PrintCKFileInfo(doc->m_FileInfo); return 0; }