2023-02-11 15:29:51 +08:00
|
|
|
#include "VTUtils.hpp"
|
|
|
|
#if defined(LIBCMO_OS_WIN32)
|
|
|
|
#define ZLIB_WINAPI
|
|
|
|
#include "zconf.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "VTStruct.hpp"
|
|
|
|
#include <zlib.h>
|
2023-02-08 22:57:31 +08:00
|
|
|
|
|
|
|
namespace LibCmo {
|
|
|
|
|
2023-02-11 15:29:51 +08:00
|
|
|
CKERROR CKFile::Load(CKSTRING u8_filename, /*CKObjectArray list, */ CK_LOAD_FLAGS flags) {
|
|
|
|
CKERROR result = this->OpenFile(u8_filename, flags);
|
|
|
|
if (result == CKERROR::CKERR_OK || result == CKERROR::CKERR_PLUGINSMISSING) {
|
|
|
|
result = this->LoadFileData();
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
CKERROR CKFile::OpenFile(CKSTRING u8_filename, CK_LOAD_FLAGS flags) {
|
|
|
|
this->ClearData();
|
|
|
|
if (u8_filename == nullptr) return CKERROR::CKERR_INVALIDPARAMETER;
|
|
|
|
|
|
|
|
this->m_FileName = u8_filename;
|
2023-02-14 21:31:18 +08:00
|
|
|
this->m_MappedFile = new(std::nothrow) VxMemoryMappedFile(this->m_FileName.c_str());
|
|
|
|
if (this->m_MappedFile == nullptr) return CKERROR::CKERR_OUTOFMEMORY;
|
2023-02-11 15:29:51 +08:00
|
|
|
if (!this->m_MappedFile->IsValid()) return CKERROR::CKERR_INVALIDFILE;
|
|
|
|
|
2023-02-14 21:31:18 +08:00
|
|
|
// MARK: CKContext::SetLastCmoLoaded
|
2023-02-11 15:29:51 +08:00
|
|
|
return this->OpenMemory(this->m_MappedFile->GetBase(), this->m_MappedFile->GetFileSize(), flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
CKERROR CKFile::OpenMemory(void* MemoryBuffer, size_t BufferSize, CK_LOAD_FLAGS Flags) {
|
|
|
|
if (MemoryBuffer == nullptr) return CKERROR::CKERR_INVALIDPARAMETER;
|
|
|
|
// compare magic words
|
|
|
|
if (BufferSize < 0x20 || memcmp(MemoryBuffer, "Nemo", 4u)) return CKERROR::CKERR_INVALIDFILE;
|
|
|
|
|
2023-02-14 21:31:18 +08:00
|
|
|
this->m_Parser = new(std::nothrow) CKBufferParser(MemoryBuffer, BufferSize, false);
|
|
|
|
if (this->m_Parser == nullptr) return CKERROR::CKERR_OUTOFMEMORY;
|
|
|
|
// MARK: eliminate check of m_Parser->m_ReaderBegin
|
2023-02-11 15:29:51 +08:00
|
|
|
|
2023-02-14 21:31:18 +08:00
|
|
|
// MARK: dword_2405F6C0 = 0;
|
2023-02-11 15:29:51 +08:00
|
|
|
this->m_Flags = Flags;
|
|
|
|
this->m_IndexByClassId.resize(static_cast<size_t>(CK_CLASSID::CKCID_MAXCLASSID));
|
|
|
|
return this->ReadFileHeaders(&(this->m_Parser));
|
|
|
|
}
|
|
|
|
|
|
|
|
CKERROR CKFile::ReadFileHeaders(CKBufferParser** ParserPtr) {
|
|
|
|
CKBufferParser* parser = *ParserPtr;
|
|
|
|
this->m_IncludedFiles.clear();
|
|
|
|
|
|
|
|
// ========== read header1 ==========
|
|
|
|
CKDWORD fhdr1[8];
|
|
|
|
CKDWORD fhdr2[8];
|
|
|
|
if (parser->GetSize() < sizeof(fhdr1)) return CKERROR::CKERR_INVALIDFILE;
|
2023-02-15 21:03:26 +08:00
|
|
|
parser->Read(fhdr1, sizeof(fhdr1));
|
2023-02-11 15:29:51 +08:00
|
|
|
|
|
|
|
if (fhdr1[5]) { // it seems that there is a ZERO checker?
|
|
|
|
memset(fhdr1, 0, sizeof(fhdr1));
|
|
|
|
}
|
|
|
|
// check outdated
|
|
|
|
if (fhdr1[4] > 9) return CKERROR::CKERR_OBSOLETEVIRTOOLS;
|
|
|
|
|
|
|
|
// ========== read header2 ==========
|
|
|
|
// file ver < 5 do not have second header
|
|
|
|
if (fhdr1[4] < 5) {
|
|
|
|
memset(fhdr2, 0, sizeof(fhdr2));
|
|
|
|
} else {
|
|
|
|
if (parser->GetSize() < sizeof(fhdr1) + sizeof(fhdr2)) return CKERROR::CKERR_INVALIDFILE;
|
2023-02-15 21:03:26 +08:00
|
|
|
parser->Read(fhdr2, sizeof(fhdr2));
|
2023-02-11 15:29:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// forcely reset too big product ver
|
|
|
|
if (fhdr2[5] >= 12) {
|
|
|
|
fhdr2[5] = 0;
|
|
|
|
fhdr2[6] = 0x1010000;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ========== assign value ==========
|
|
|
|
this->m_FileInfo.ProductVersion = fhdr2[5];
|
|
|
|
this->m_FileInfo.ProductBuild = fhdr2[6];
|
|
|
|
this->m_FileInfo.FileWriteMode = static_cast<CK_FILE_WRITEMODE>(fhdr1[6]);
|
|
|
|
this->m_FileInfo.CKVersion = fhdr1[3];
|
|
|
|
this->m_FileInfo.FileVersion = fhdr1[4];
|
|
|
|
this->m_FileInfo.FileSize = parser->GetSize();
|
|
|
|
this->m_FileInfo.ManagerCount = fhdr2[2];
|
|
|
|
this->m_FileInfo.ObjectCount = fhdr2[3];
|
|
|
|
this->m_FileInfo.MaxIDSaved = fhdr2[4];
|
|
|
|
this->m_FileInfo.Hdr1PackSize = fhdr1[7];
|
|
|
|
this->m_FileInfo.Hdr1UnPackSize = fhdr2[7];
|
|
|
|
this->m_FileInfo.DataPackSize = fhdr2[0];
|
|
|
|
this->m_FileInfo.DataUnPackSize = fhdr2[1];
|
2023-02-11 15:46:02 +08:00
|
|
|
this->m_FileInfo.Crc = fhdr1[2];
|
2023-02-11 15:29:51 +08:00
|
|
|
|
|
|
|
// ========== crc and body unpacker ==========
|
|
|
|
if (this->m_FileInfo.FileVersion >= 8) {
|
|
|
|
// crc checker for file ver >= 8
|
|
|
|
// reset crc field of header
|
|
|
|
fhdr1[2] = 0;
|
|
|
|
|
|
|
|
// compute crc
|
2023-02-15 21:03:26 +08:00
|
|
|
CKDWORD gotten_crc = CKComputeDataCRC(&fhdr1, sizeof(fhdr1), 0u);
|
2023-02-11 15:29:51 +08:00
|
|
|
parser->SetCursor(sizeof(fhdr1));
|
2023-02-15 21:03:26 +08:00
|
|
|
gotten_crc = CKComputeDataCRC(parser->GetPtr(), sizeof(fhdr2), gotten_crc);
|
2023-02-11 15:29:51 +08:00
|
|
|
parser->MoveCursor(sizeof(fhdr2));
|
2023-02-15 21:03:26 +08:00
|
|
|
gotten_crc = CKComputeDataCRC(parser->GetPtr(), this->m_FileInfo.Hdr1PackSize, gotten_crc);
|
2023-02-11 15:29:51 +08:00
|
|
|
parser->MoveCursor(this->m_FileInfo.Hdr1PackSize);
|
2023-02-15 21:03:26 +08:00
|
|
|
gotten_crc = CKComputeDataCRC(parser->GetPtr(), this->m_FileInfo.DataPackSize, gotten_crc);
|
2023-02-11 15:29:51 +08:00
|
|
|
parser->SetCursor(sizeof(fhdr1) + sizeof(fhdr2));
|
|
|
|
|
2023-02-15 21:03:26 +08:00
|
|
|
if (gotten_crc != this->m_FileInfo.Crc) return CKERROR::CKERR_FILECRCERROR;
|
2023-02-11 15:29:51 +08:00
|
|
|
|
|
|
|
// compare size to decide wheher use compress feature
|
|
|
|
void* decomp_buffer = CKUnPackData(this->m_FileInfo.Hdr1UnPackSize, parser->GetPtr(), this->m_FileInfo.Hdr1PackSize);
|
|
|
|
if (decomp_buffer != nullptr) {
|
2023-02-14 21:31:18 +08:00
|
|
|
parser = new(std::nothrow) CKBufferParser(decomp_buffer, this->m_FileInfo.Hdr1UnPackSize, true);
|
|
|
|
if (parser == nullptr) {
|
2023-02-15 21:03:26 +08:00
|
|
|
delete[] decomp_buffer;
|
2023-02-14 21:31:18 +08:00
|
|
|
return CKERROR::CKERR_OUTOFMEMORY;
|
|
|
|
}
|
2023-02-11 15:29:51 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ========== object list read ==========
|
|
|
|
// file ver >= 7 have this features
|
|
|
|
if (this->m_FileInfo.FileVersion >= 7) {
|
|
|
|
// apply max id saved
|
|
|
|
this->m_SaveIDMax = this->m_FileInfo.MaxIDSaved;
|
|
|
|
// resize
|
|
|
|
this->m_FileObject.resize(this->m_FileInfo.ObjectCount);
|
|
|
|
|
|
|
|
// read data
|
2023-02-14 21:31:18 +08:00
|
|
|
for (auto& fileobj : this->m_FileObject) {
|
2023-02-11 15:29:51 +08:00
|
|
|
// setup useless fields
|
2023-02-14 21:31:18 +08:00
|
|
|
fileobj.ObjPtr = nullptr;
|
|
|
|
fileobj.Data = nullptr;
|
2023-02-11 15:29:51 +08:00
|
|
|
|
|
|
|
// read basic fields
|
2023-02-15 21:03:26 +08:00
|
|
|
parser->Read(&(fileobj.Object), sizeof(CK_ID));
|
|
|
|
parser->Read(&(fileobj.ObjectCid), sizeof(CK_CLASSID));
|
|
|
|
parser->Read(&(fileobj.FileIndex), sizeof(CKDWORD));
|
2023-02-11 15:29:51 +08:00
|
|
|
|
|
|
|
CKDWORD namelen;
|
2023-02-15 21:03:26 +08:00
|
|
|
parser->Read(&namelen, sizeof(CKDWORD));
|
2023-02-11 15:29:51 +08:00
|
|
|
if (namelen != 0) {
|
2023-02-14 21:31:18 +08:00
|
|
|
fileobj.Name.resize(namelen);
|
2023-02-15 21:03:26 +08:00
|
|
|
parser->Read(fileobj.Name.data(), namelen);
|
2023-02-11 15:29:51 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-14 21:31:18 +08:00
|
|
|
// ========== dep list read ==========
|
|
|
|
// file ver >= 8 have this feature
|
|
|
|
bool noDepLost = true;
|
|
|
|
if (this->m_FileInfo.FileVersion >= 8) {
|
|
|
|
// get size and resize
|
|
|
|
CKDWORD depSize;
|
2023-02-15 21:03:26 +08:00
|
|
|
parser->Read(&depSize, sizeof(CKDWORD));
|
2023-02-14 21:31:18 +08:00
|
|
|
this->m_PluginDep.resize(depSize);
|
|
|
|
|
|
|
|
CKDWORD guid_size;
|
|
|
|
for (auto& dep : this->m_PluginDep) {
|
|
|
|
// read category
|
2023-02-15 21:03:26 +08:00
|
|
|
parser->Read(&(dep.m_PluginCategory), sizeof(CK_PLUGIN_TYPE));
|
2023-02-14 21:31:18 +08:00
|
|
|
// get size and resize
|
2023-02-15 21:03:26 +08:00
|
|
|
parser->Read(&guid_size, sizeof(CKDWORD));
|
2023-02-14 21:31:18 +08:00
|
|
|
dep.m_Guids.resize(guid_size);
|
|
|
|
dep.ValidGuids.resize(guid_size);
|
|
|
|
// read data
|
|
|
|
if (guid_size != 0) {
|
2023-02-15 21:03:26 +08:00
|
|
|
parser->Read(dep.m_Guids.data(), sizeof(CKGUID)* guid_size);
|
2023-02-14 21:31:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// extra load flag
|
|
|
|
if (EnumHelper::FlagEnumHas(this->m_Flags, CK_LOAD_FLAGS::CK_LOAD_CHECKDEPENDENCIES)) {
|
|
|
|
for (size_t i = 0u; i < dep.m_Guids.size(); ++i) {
|
|
|
|
// MARK: CKPluginManager::FindComponent
|
|
|
|
bool plgEntryIsNull = true;
|
|
|
|
if (plgEntryIsNull) {
|
|
|
|
noDepLost = false;
|
|
|
|
dep.ValidGuids[i] = false;
|
|
|
|
} else {
|
|
|
|
dep.ValidGuids[i] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ========== included file list read ==========
|
|
|
|
// file ver >= 8 have this feature
|
|
|
|
if (this->m_FileInfo.FileVersion >= 8) {
|
|
|
|
// MARK: i don't knwo what is this!
|
2023-02-15 21:03:26 +08:00
|
|
|
int32_t hasIncludedFile;
|
|
|
|
parser->Read(&hasIncludedFile, sizeof(int32_t));
|
2023-02-14 21:31:18 +08:00
|
|
|
|
2023-02-15 21:03:26 +08:00
|
|
|
if (hasIncludedFile > 0) {
|
2023-02-14 21:31:18 +08:00
|
|
|
// read included file size and resize
|
|
|
|
CKDWORD includedFileCount;
|
2023-02-15 21:03:26 +08:00
|
|
|
parser->Read(&includedFileCount, sizeof(CKDWORD));
|
2023-02-14 21:31:18 +08:00
|
|
|
this->m_IncludedFiles.resize(includedFileCount);
|
|
|
|
|
2023-02-15 21:03:26 +08:00
|
|
|
hasIncludedFile -= 4;
|
2023-02-14 21:31:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// backward pos
|
2023-02-15 21:03:26 +08:00
|
|
|
parser->SetCursor(hasIncludedFile);
|
2023-02-14 21:31:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// ========== read data ==========
|
|
|
|
// restore old parser if needed
|
|
|
|
if (parser != *ParserPtr) {
|
|
|
|
delete parser;
|
|
|
|
parser = *ParserPtr;
|
|
|
|
parser->MoveCursor(this->m_FileInfo.Hdr1PackSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: dword_2405F6BC, dword_2405F6B8 set
|
|
|
|
|
|
|
|
if (!(EnumHelper::FlagEnumHas(this->m_Flags, CK_LOAD_FLAGS::CK_LOAD_CHECKDEPENDENCIES) &&
|
|
|
|
this->m_FileInfo.FileVersion < 8)) {
|
|
|
|
// we have read all header. return result
|
|
|
|
return noDepLost ? CKERROR::CKERR_OK : CKERROR::CKERR_PLUGINSMISSING;
|
|
|
|
} // however, if we need check dep, and, we have not read dep list. continue read them.
|
|
|
|
|
2023-02-08 22:57:31 +08:00
|
|
|
return CKERROR::CKERR_OK;
|
|
|
|
}
|
|
|
|
|
2023-02-11 15:29:51 +08:00
|
|
|
CKERROR CKFile::ReadFileData(CKBufferParser** ParserPtr) {
|
2023-02-15 21:03:26 +08:00
|
|
|
CKBufferParser* parser = *ParserPtr;
|
|
|
|
|
|
|
|
// ========== compress feature process ==========
|
|
|
|
if (EnumHelper::FlagEnumHas(this->m_FileInfo.FileWriteMode, CK_FILE_WRITEMODE::CKFILE_CHUNKCOMPRESSED_OLD) ||
|
|
|
|
EnumHelper::FlagEnumHas(this->m_FileInfo.FileWriteMode, CK_FILE_WRITEMODE::CKFILE_WHOLECOMPRESSED)) {
|
|
|
|
void* decomp_buffer = CKUnPackData(this->m_FileInfo.DataUnPackSize, parser->GetPtr(), this->m_FileInfo.DataPackSize);
|
|
|
|
parser->MoveCursor(this->m_FileInfo.DataPackSize);
|
|
|
|
|
|
|
|
if (decomp_buffer != nullptr) {
|
|
|
|
parser = new(std::nothrow) CKBufferParser(decomp_buffer, this->m_FileInfo.Hdr1UnPackSize, 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 (this->m_FileInfo.FileVersion < 8) {
|
|
|
|
// very very old flag
|
|
|
|
if (this->m_FileInfo.FileVersion < 2) {
|
|
|
|
;
|
|
|
|
// MARK: dword_2405F6C0 setter
|
|
|
|
}
|
|
|
|
|
|
|
|
// check crc
|
|
|
|
CKDWORD gotten_crc = CKComputeDataCRC(
|
|
|
|
parser->GetPtr(),
|
|
|
|
parser->GetSize() - parser->GetCursor(),
|
|
|
|
0u
|
|
|
|
);
|
|
|
|
if (gotten_crc != this->m_FileInfo.Crc) {
|
|
|
|
// MARK: report crc error
|
|
|
|
return CKERROR::CKERR_FILECRCERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
// get save id max
|
|
|
|
parser->Read(&this->m_SaveIDMax, sizeof(int32_t));
|
|
|
|
// get object count and resize
|
|
|
|
parser->Read(&this->m_FileInfo.ObjectCount, sizeof(CKDWORD));
|
|
|
|
if (this->m_FileObject.empty()) {
|
|
|
|
this->m_FileObject.resize(this->m_FileInfo.ObjectCount);
|
|
|
|
// MARK: ZeroMemory removed. i think it is not necessary.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ========== manager read ==========
|
|
|
|
|
|
|
|
|
2023-02-08 22:57:31 +08:00
|
|
|
return CKERROR::CKERR_OK;
|
|
|
|
}
|
|
|
|
|
2023-02-11 15:29:51 +08:00
|
|
|
CKERROR CKFile::LoadFileData(void/*CKObjectArray list*/) {
|
2023-02-15 21:03:26 +08:00
|
|
|
if (!this->m_Parser && !this->m_ReadFileDataDone) {
|
|
|
|
return CKERROR::CKERR_INVALIDFILE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: sub_240372EA, ctx + 193 = 1
|
|
|
|
|
|
|
|
// if file data has not been read. read it
|
|
|
|
CKERROR err = CKERROR::CKERR_OK;
|
|
|
|
CKBufferParser** ParserPtr = &this->m_Parser;
|
|
|
|
if (!this->m_ReadFileDataDone) {
|
|
|
|
err = this->ReadFileData(ParserPtr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: i assume FinishLoading do not calling mapped file anymore
|
|
|
|
// free file data
|
|
|
|
if (this->m_Parser != nullptr) {
|
|
|
|
delete this->m_Parser;
|
|
|
|
this->m_Parser = nullptr;
|
|
|
|
}
|
|
|
|
if (this->m_MappedFile != nullptr) {
|
|
|
|
delete this->m_MappedFile;
|
|
|
|
this->m_MappedFile = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if no error, do finish loading
|
|
|
|
if (err == CKERROR::CKERR_OK) {
|
|
|
|
this->FinishLoading(this->m_Flags);
|
|
|
|
|
|
|
|
// MARK: dword_2405F6C0 old vt ver output
|
|
|
|
}
|
|
|
|
// MARK: CKContext::SetAutomaticLoadMode
|
|
|
|
// MARK:CKContext::SetUserLoadCallback
|
|
|
|
|
|
|
|
// MARK: sub_24037360
|
|
|
|
// MARK: ctx + 193 = 0
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
CKERROR CKFile::FinishLoading(/*CKObjectArray list, */CK_LOAD_FLAGS flags) {
|
2023-02-11 15:29:51 +08:00
|
|
|
return CKERROR::CKERR_OK;
|
|
|
|
}
|
2023-02-15 21:03:26 +08:00
|
|
|
|
2023-02-08 22:57:31 +08:00
|
|
|
}
|