| 
									
										
										
										
											2023-02-25 17:39:39 +08:00
										 |  |  | #include "CKFile.hpp"
 | 
					
						
							| 
									
										
										
										
											2023-08-28 17:04:28 +08:00
										 |  |  | #include "CKContext.hpp"
 | 
					
						
							|  |  |  | #include "CKStateChunk.hpp"
 | 
					
						
							|  |  |  | #include "ObjImpls/CKObject.hpp"
 | 
					
						
							|  |  |  | #include "MgrImpls/CKBaseManager.hpp"
 | 
					
						
							|  |  |  | #include "../VxMath/VxMemoryMappedFile.hpp"
 | 
					
						
							|  |  |  | #include <memory>
 | 
					
						
							| 
									
										
										
										
											2023-02-25 17:39:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-25 17:35:45 +08:00
										 |  |  | namespace LibCmo::CK2 { | 
					
						
							| 
									
										
										
										
											2023-02-25 17:39:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-25 21:57:22 +08:00
										 |  |  | 	CKERROR CKFileWriter::Save(CKSTRING u8_filename) { | 
					
						
							| 
									
										
										
										
											2023-08-28 17:04:28 +08:00
										 |  |  | 		// check document status
 | 
					
						
							|  |  |  | 		if (this->m_Done) CKERROR::CKERR_CANCELLED; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-28 21:21:40 +08:00
										 |  |  | 		// encoding conv helper
 | 
					
						
							|  |  |  | 		std::string name_conv; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-28 17:04:28 +08:00
										 |  |  | 		// try detect filename legality
 | 
					
						
							|  |  |  | 		CKERROR err = PrepareFile(u8_filename); | 
					
						
							|  |  |  | 		if (err != CKERROR::CKERR_OK) return err; | 
					
						
							| 
									
										
										
										
											2023-08-28 21:21:40 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-28 17:04:28 +08:00
										 |  |  | 		// ========== Prepare Stage ==========
 | 
					
						
							|  |  |  | 		// todo: add TOBEDELETED flag for all Referenced objects's m_ObjectFlags
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// MARK: ignore the notification to all CKBehavior based objects.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// ========== StateChunk convertion ==========
 | 
					
						
							| 
									
										
										
										
											2023-08-28 21:21:40 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-28 17:04:28 +08:00
										 |  |  | 		// 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
 | 
					
						
							| 
									
										
										
										
											2023-08-28 21:21:40 +08:00
										 |  |  | 				m_FileObjects[i].FileIndex = m_FileObjects[i - 1].FileIndex + m_FileObjects[i - 1].PackSize + sizeof(CKDWORD); | 
					
						
							| 
									
										
										
										
											2023-08-28 17:04:28 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// ========== Construct File Header ==========
 | 
					
						
							| 
									
										
										
										
											2023-08-28 21:21:40 +08:00
										 |  |  | 		CK_FILE_WRITEMODE fileWriteMode = m_Ctx->GetFileWriteMode(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-28 17:04:28 +08:00
										 |  |  | 		CKRawFileInfo rawHeader; | 
					
						
							|  |  |  | 		std::memcpy(&rawHeader.NeMo, CKNEMOFI, sizeof(CKRawFileInfo::NeMo)); | 
					
						
							|  |  |  | 		rawHeader.Crc = 0; | 
					
						
							|  |  |  | 		rawHeader.Zero = 0; | 
					
						
							|  |  |  | 		rawHeader.CKVersion = CKVERSION; | 
					
						
							|  |  |  | 		rawHeader.FileVersion = 8; | 
					
						
							| 
									
										
										
										
											2023-08-28 21:21:40 +08:00
										 |  |  | 		rawHeader.FileWriteMode = static_cast<CKDWORD>(fileWriteMode); | 
					
						
							| 
									
										
										
										
											2023-08-28 17:04:28 +08:00
										 |  |  | 		rawHeader.ObjectCount = static_cast<CKDWORD>(m_FileObjects.size()); | 
					
						
							|  |  |  | 		rawHeader.ManagerCount = static_cast<CKDWORD>(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
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-28 21:21:40 +08:00
										 |  |  | 		// ========== Write header ==========
 | 
					
						
							| 
									
										
										
										
											2023-08-28 17:04:28 +08:00
										 |  |  | 		// create a buffer
 | 
					
						
							|  |  |  | 		std::unique_ptr<CKBufferParser> 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<CKDWORD>(name_conv.size()); | 
					
						
							|  |  |  | 				hdrparser->Write(&namelen, sizeof(CKDWORD)); | 
					
						
							|  |  |  | 				hdrparser->Write(name_conv.data(), namelen); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// write plugin dep
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			CKDWORD depsize = static_cast<CKDWORD>(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<CKDWORD>(dep.m_Guids.size()); | 
					
						
							|  |  |  | 				hdrparser->Write(&guidsize, sizeof(CKDWORD)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				hdrparser->Write(dep.m_Guids.data(), sizeof(CKGUID) * guidsize); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-28 21:21:40 +08:00
										 |  |  | 		// write included file
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			CKDWORD cache = sizeof(CKDWORD); | 
					
						
							|  |  |  | 			hdrparser->Write(&cache, sizeof(CKDWORD)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			cache = static_cast<CKDWORD>(m_IncludedFiles.size()); | 
					
						
							|  |  |  | 			hdrparser->Write(&cache, sizeof(CKDWORD)); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// compress header if needed
 | 
					
						
							|  |  |  | 		if (EnumsHelper::Has(fileWriteMode, CK_FILE_WRITEMODE::CKFILE_CHUNKCOMPRESSED_OLD) || | 
					
						
							|  |  |  | 			EnumsHelper::Has(fileWriteMode, CK_FILE_WRITEMODE::CKFILE_WHOLECOMPRESSED)) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			CKDWORD comp_buf_size = 0; | 
					
						
							|  |  |  | 			void* comp_buffer = CKPackData(hdrparser->GetBase(), static_cast<CKDWORD>(hdrparser->GetSize()), comp_buf_size, m_Ctx->GetCompressionLevel()); | 
					
						
							|  |  |  | 			if (comp_buffer != nullptr) { | 
					
						
							|  |  |  | 				hdrparser = std::unique_ptr<CKBufferParser>(new CKBufferParser(comp_buffer, comp_buf_size, true)); | 
					
						
							|  |  |  | 				rawHeader.Hdr1PackSize = comp_buf_size; | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				// fallback
 | 
					
						
							|  |  |  | 				rawHeader.Hdr1PackSize = rawHeader.Hdr1UnPackSize; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// ========== Write data ==========
 | 
					
						
							|  |  |  | 		// create a buffer
 | 
					
						
							|  |  |  | 		std::unique_ptr<CKBufferParser> datparser(new CKBufferParser(sumDataSize)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// write manager
 | 
					
						
							|  |  |  | 		for (auto& mgr : m_ManagersData) { | 
					
						
							|  |  |  | 			datparser->Write(&mgr.Manager, sizeof(CKGUID)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			CKDWORD writtenSize = 0; | 
					
						
							|  |  |  | 			if (mgr.Data != nullptr) { | 
					
						
							|  |  |  | 				writtenSize = mgr.Data->ConvertToBuffer(datparser->GetMutablePtr(sizeof(CKDWORD))); | 
					
						
							|  |  |  | 				delete mgr.Data; | 
					
						
							|  |  |  | 				mgr.Data = nullptr; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			datparser->Write(&writtenSize, sizeof(CKDWORD)); | 
					
						
							|  |  |  | 			datparser->MoveCursor(writtenSize); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// write object
 | 
					
						
							|  |  |  | 		for (auto& obj : m_FileObjects) { | 
					
						
							|  |  |  | 			datparser->Write(&obj.PackSize, sizeof(CKDWORD)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (obj.Data != nullptr) { | 
					
						
							|  |  |  | 				obj.Data->ConvertToBuffer(datparser->GetMutablePtr()); | 
					
						
							|  |  |  | 				delete obj.Data; | 
					
						
							|  |  |  | 				obj.Data = nullptr; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			datparser->MoveCursor(obj.PackSize); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		 | 
					
						
							|  |  |  | 		// compress header if needed
 | 
					
						
							|  |  |  | 		if (EnumsHelper::Has(fileWriteMode, CK_FILE_WRITEMODE::CKFILE_CHUNKCOMPRESSED_OLD) || | 
					
						
							|  |  |  | 			EnumsHelper::Has(fileWriteMode, CK_FILE_WRITEMODE::CKFILE_WHOLECOMPRESSED)) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			CKDWORD comp_buf_size = 0; | 
					
						
							|  |  |  | 			void* comp_buffer = CKPackData(datparser->GetBase(), static_cast<CKDWORD>(datparser->GetSize()), comp_buf_size, m_Ctx->GetCompressionLevel()); | 
					
						
							|  |  |  | 			if (comp_buffer != nullptr) { | 
					
						
							|  |  |  | 				datparser = std::unique_ptr<CKBufferParser>(new CKBufferParser(comp_buffer, comp_buf_size, true)); | 
					
						
							|  |  |  | 				rawHeader.DataPackSize = comp_buf_size; | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				// fallback
 | 
					
						
							|  |  |  | 				rawHeader.DataPackSize = rawHeader.DataUnPackSize; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// ========== Construct File Info ==========
 | 
					
						
							|  |  |  | 		// compute crc
 | 
					
						
							|  |  |  | 		CKDWORD computedcrc = CKComputeDataCRC(&rawHeader, sizeof(CKRawFileInfo), 0u); | 
					
						
							|  |  |  | 		computedcrc = CKComputeDataCRC(hdrparser->GetBase(), hdrparser->GetSize(), computedcrc); | 
					
						
							|  |  |  | 		computedcrc = CKComputeDataCRC(datparser->GetBase(), datparser->GetSize(), computedcrc); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// copy to file info
 | 
					
						
							|  |  |  | 		this->m_FileInfo.ProductVersion = rawHeader.ProductVersion; | 
					
						
							|  |  |  | 		this->m_FileInfo.ProductBuild = rawHeader.ProductBuild; | 
					
						
							|  |  |  | 		this->m_FileInfo.FileWriteMode = static_cast<CK_FILE_WRITEMODE>(rawHeader.FileWriteMode); | 
					
						
							|  |  |  | 		this->m_FileInfo.CKVersion = rawHeader.CKVersion; | 
					
						
							|  |  |  | 		this->m_FileInfo.FileVersion = rawHeader.FileVersion; | 
					
						
							|  |  |  | 		this->m_FileInfo.Hdr1PackSize = rawHeader.Hdr1PackSize; | 
					
						
							|  |  |  | 		this->m_FileInfo.Hdr1UnPackSize = rawHeader.Hdr1UnPackSize; | 
					
						
							|  |  |  | 		this->m_FileInfo.DataPackSize = rawHeader.DataPackSize; | 
					
						
							|  |  |  | 		this->m_FileInfo.DataUnPackSize = rawHeader.DataUnPackSize; | 
					
						
							|  |  |  | 		this->m_FileInfo.ManagerCount = rawHeader.ManagerCount; | 
					
						
							|  |  |  | 		this->m_FileInfo.ObjectCount = rawHeader.ObjectCount; | 
					
						
							|  |  |  | 		this->m_FileInfo.MaxIDSaved = rawHeader.MaxIDSaved; | 
					
						
							|  |  |  | 		// fill file size and crc
 | 
					
						
							|  |  |  | 		this->m_FileInfo.FileSize = sizeof(CKRawFileInfo) + rawHeader.DataPackSize + rawHeader.Hdr1PackSize; | 
					
						
							|  |  |  | 		this->m_FileInfo.Crc = computedcrc; | 
					
						
							|  |  |  | 		rawHeader.Crc = computedcrc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// ========== Open File & Write Essential Data ==========
 | 
					
						
							|  |  |  | 		// open file and test
 | 
					
						
							|  |  |  | 		FILE* fs = m_Ctx->OpenFile(u8_filename, "wb"); | 
					
						
							|  |  |  | 		if (fs == nullptr) return CKERROR::CKERR_CANTWRITETOFILE; | 
					
						
							|  |  |  | 		// write small header + header + data
 | 
					
						
							|  |  |  | 		std::fwrite(&rawHeader, sizeof(CKRawFileInfo), 1, fs); | 
					
						
							|  |  |  | 		std::fwrite(hdrparser->GetBase(), sizeof(char), hdrparser->GetSize(), fs); | 
					
						
							|  |  |  | 		std::fwrite(datparser->GetBase(), sizeof(char), datparser->GetSize(), fs); | 
					
						
							|  |  |  | 		// free buffer
 | 
					
						
							|  |  |  | 		hdrparser.reset(); | 
					
						
							|  |  |  | 		datparser.reset(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// ========== Included Files ==========
 | 
					
						
							|  |  |  | 		for (auto& fentry : m_IncludedFiles) { | 
					
						
							|  |  |  | 			// write filename
 | 
					
						
							|  |  |  | 			m_Ctx->GetNativeString(fentry, name_conv); | 
					
						
							|  |  |  | 			CKDWORD filenamelen = static_cast<CKDWORD>(name_conv.size()); | 
					
						
							|  |  |  | 			fwrite(&filenamelen, sizeof(CKDWORD), 1, fs); | 
					
						
							|  |  |  | 			fwrite(name_conv.data(), sizeof(char), filenamelen, fs); | 
					
						
							|  |  |  | 			 | 
					
						
							|  |  |  | 			// try mapping file.
 | 
					
						
							|  |  |  | 			std::unique_ptr<VxMath::VxMemoryMappedFile> mappedFile(new VxMath::VxMemoryMappedFile(fentry.c_str())); | 
					
						
							|  |  |  | 			 | 
					
						
							|  |  |  | 			// write file length
 | 
					
						
							|  |  |  | 			CKDWORD filebodylen = static_cast<CKDWORD>(mappedFile->IsValid() ? mappedFile->GetFileSize() : 0); | 
					
						
							|  |  |  | 			fwrite(&filebodylen, sizeof(CKDWORD), 1, fs); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// write file body
 | 
					
						
							|  |  |  | 			if (mappedFile->IsValid()) { | 
					
						
							|  |  |  | 				fwrite(mappedFile->GetBase(), sizeof(char), filebodylen, fs); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// release mapped file
 | 
					
						
							|  |  |  | 			mappedFile.reset(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// close file
 | 
					
						
							|  |  |  | 		fclose(fs); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return CKERROR::CKERR_OK; | 
					
						
							| 
									
										
										
										
											2023-08-28 17:04:28 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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; | 
					
						
							| 
									
										
										
										
											2023-08-25 21:57:22 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-02-25 17:39:39 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | } |