diff --git a/LibCmo/CK2/CKStateChunk.cpp b/LibCmo/CK2/CKStateChunkOthers.cpp similarity index 52% rename from LibCmo/CK2/CKStateChunk.cpp rename to LibCmo/CK2/CKStateChunkOthers.cpp index e476cc3..b9fa05d 100644 --- a/LibCmo/CK2/CKStateChunk.cpp +++ b/LibCmo/CK2/CKStateChunkOthers.cpp @@ -502,539 +502,4 @@ namespace LibCmo::CK2 { #pragma endregion -#pragma region Read Functions - - void CKStateChunk::StartRead(void) { - if (this->m_Parser.m_Status != CKStateChunkStatus::IDLE) return; - - this->m_Parser.m_CurrentPos = 0u; - this->m_Parser.m_DataSize = this->m_DataDwSize; - this->m_Parser.m_PrevIdentifierPos = 0u; - this->m_Parser.m_Status = CKStateChunkStatus::READ; - } - - void CKStateChunk::StopRead(void) { - if (this->m_Parser.m_Status != CKStateChunkStatus::READ) return; - - this->m_Parser.m_CurrentPos = 0u; - this->m_Parser.m_DataSize = this->m_DataDwSize; - this->m_Parser.m_PrevIdentifierPos = 0u; - this->m_Parser.m_Status = CKStateChunkStatus::IDLE; - } - - /* ========== Identifier Functions ==========*/ - - bool CKStateChunk::SeekIdentifierDword(CKDWORD identifier) { - CKDWORD cache; - return SeekIdentifierDwordAndReturnSize(identifier, &cache); - } - - bool CKStateChunk::SeekIdentifierDwordAndReturnSize(CKDWORD identifier, CKDWORD* out_size) { - if (this->m_Parser.m_Status != CKStateChunkStatus::READ) return false; - - CKDWORD pos = 0u; - if (this->m_DataDwSize < 2u) return false; // impossible to have a identifier - - // search identifier - while (this->m_pData[pos] != identifier) { - pos = this->m_pData[pos + 1]; - if (pos == 0u) return false; // got tail. no more identifier - if (pos + 1 >= this->m_DataDwSize) return false; // out of buffer - } - - // got identifier - this->m_Parser.m_PrevIdentifierPos = pos; - this->m_Parser.m_CurrentPos = pos + 2; - - // calc size - CKDWORD nextptr = this->m_pData[pos + 1]; - if (nextptr == 0) { - // the last identifier, use chunk size instead - nextptr = this->m_DataDwSize; - } - *out_size = CKSizeof(CKDWORD) * (nextptr - pos - 2u); - return true; - } - - bool CKStateChunk::LockReadBuffer(const void** ppData, CKDWORD size_in_byte) { - // check arguments - if (*ppData == nullptr) return false; - *ppData = nullptr; - // check self status - if (this->m_Parser.m_Status != CKStateChunkStatus::READ) return false; - - // get corresponding size - CKDWORD size_in_dword = this->GetCeilDwordSize(size_in_byte); - // ensure space - if (this->EnsureReadSpace(size_in_dword)) { - *ppData = this->m_pData + this->m_Parser.m_CurrentPos; - return true; - } else { - // failed, report to context - m_BindContext->OutputToConsoleEx("CKStateChunk::LockReadBuffer at buffer pos %" PRIuCKDWORD ".", this->m_Parser.m_CurrentPos); - return false; - } - } - - bool CKStateChunk::UnLockReadBuffer(CKDWORD size_in_byte) { - // check self status - if (this->m_Parser.m_Status != CKStateChunkStatus::READ) return false; - - // get corresponding size - CKDWORD size_in_dword = this->GetCeilDwordSize(size_in_byte); - // ensure space - if (this->EnsureReadSpace(size_in_dword)) { - this->m_Parser.m_CurrentPos += size_in_dword; - return true; - } else { - // failed, report to context - m_BindContext->OutputToConsoleEx("CKStateChunk::UnLockReadBuffer at buffer pos %" PRIuCKDWORD ".", this->m_Parser.m_CurrentPos); - return false; - } - } - - CKStateChunk::LockedReadBuffer_t CKStateChunk::LockReadBufferWrapper(CKDWORD size_in_byte) { - const void* pData; - bool ret = LockReadBuffer(&pData, size_in_byte); - if (ret) { - return LockedReadBuffer_t(pData, LockedReadBufferDeleter(this, size_in_byte)); - } else { - return LockedReadBuffer_t(); - } - } - - /* ========== Basic Data Read Functions ==========*/ - - - bool CKStateChunk::ReadByteData(void* data_ptr, CKDWORD size_in_byte) { - if (data_ptr == nullptr) return false; - - const void* pData; - bool ret = LockReadBuffer(&pData, size_in_byte); - if (ret) { - std::memcpy(data_ptr, pData, size_in_byte); - UnLockReadBuffer(size_in_byte); - return true; - } else { - return false; - } - } - - bool CKStateChunk::ReadString(XContainer::XString* strl) { - if (strl == nullptr) return false; - - // get byte based size - CKDWORD strByteSize = 0u; - if (!this->ReadStruct(strByteSize)) { - strl->clear(); - return false; - } - - // read data - XContainer::XString cache; - cache.resize(strByteSize); - if (!this->ReadByteData(cache.data(), strByteSize)) { - strl->clear(); - return false; - } - - // convert encoding - m_BindContext->GetUtf8String(cache, *strl); - return true; - } - - /* ========== Complex Data Read Functions ==========*/ - - bool CKStateChunk::ReadObjectID(CK_ID* id) { - if (id == nullptr) return false; - - // get basic value - CKINT gotten_id = 0; - if (!this->ReadStruct(gotten_id)) return false; - - // different strategy according to chunk ver - if (this->m_ChunkVersion >= CK_STATECHUNK_CHUNKVERSION::CHUNK_VERSION1) { - // new file - - // if no doc associated, return directly - if (this->m_BindFile == nullptr) { - *id = static_cast(gotten_id); - return true; - } - // if it is positive, return corresponding value - if (gotten_id >= 0) { - *id = this->m_BindFile->GetFileObjectByIndex(gotten_id)->CreatedObjectId; - return true; - } - - } else { - // old file - // i don't know why I need skip 2 DWORD - // just copy IDA code. - - if (gotten_id) { - this->Skip(2); - return this->ReadStruct(id); - } - } - - // all failed - *id = 0u; - return false; - } - - bool CKStateChunk::ReadManagerInt(CKGUID* guid, CKINT* intval) { - if (guid == nullptr || intval == nullptr) return false; - - // read guid first - if (!this->ReadStruct(guid)) return false; - // then read int value - if (!this->ReadStruct(intval)) return false; - - return true; - } - - CKStateChunk* CKStateChunk::ReadSubChunk(void) { - CKStateChunk* subchunk = nullptr; - - // get size and do a enough space check - CKDWORD subChunkSize; - if (!this->ReadStruct(subChunkSize)) goto subchunk_defer; - if (!this->EnsureReadSpace(subChunkSize)) goto subchunk_defer; - - // create statechunk - subchunk = new CKStateChunk(this->m_BindFile, this->m_BindContext); - - // start read data - // read class id - if (!this->ReadStruct(subchunk->m_ClassId)) goto subchunk_defer; - - // different read strategy by chunk version - if (this->m_ChunkVersion >= CK_STATECHUNK_CHUNKVERSION::CHUNK_VERSION1) { - // new file - - // read combined version - CKDWORD versionInfo; - if (!this->ReadStruct(versionInfo)) goto subchunk_defer; - subchunk->m_DataVersion = static_cast(versionInfo & 0xffff); - subchunk->m_ChunkVersion = static_cast((versionInfo >> 16) & 0xffff); - - // read data size and create it - if (!this->ReadStruct(subchunk->m_DataDwSize)) goto subchunk_defer; - subchunk->m_pData = new CKDWORD[subchunk->m_DataDwSize]; - - // has bind file? - CKDWORD hasBindFile; - if (!this->ReadStruct(hasBindFile)) goto subchunk_defer; - if (hasBindFile == 1) subchunk->m_BindFile = nullptr; - - // 3 list size - // manager only existed when ver > 4 - CKDWORD lssize; - if (!this->ReadStruct(lssize)) goto subchunk_defer; - subchunk->m_ObjectList.resize(lssize); - if (!this->ReadStruct(lssize)) goto subchunk_defer; - subchunk->m_ChunkList.resize(lssize); - if (this->m_ChunkVersion > CK_STATECHUNK_CHUNKVERSION::CHUNK_VERSION1) { - if (!this->ReadStruct(lssize)) goto subchunk_defer; - subchunk->m_ManagerList.resize(lssize); - } - - // core data - if (subchunk->m_DataDwSize != 0) { - if (!this->ReadByteData(subchunk->m_pData, subchunk->m_DataDwSize * CKSizeof(CKDWORD))) goto subchunk_defer; - } - - // 3 list data - if (!subchunk->m_ObjectList.empty()) { - if (!this->ReadByteData( - subchunk->m_ObjectList.data(), - static_cast(subchunk->m_ObjectList.size()) * CKSizeof(CKDWORD) - )) goto subchunk_defer; - } - if (!subchunk->m_ChunkList.empty()) { - if (!this->ReadByteData( - subchunk->m_ChunkList.data(), - static_cast(subchunk->m_ChunkList.size()) * CKSizeof(CKDWORD) - )) goto subchunk_defer; - } - if (!subchunk->m_ManagerList.empty()) { - if (!this->ReadByteData( - subchunk->m_ManagerList.data(), - static_cast(subchunk->m_ManagerList.size()) * CKSizeof(CKDWORD) - )) goto subchunk_defer; - } - - } else { - // old file - - // read data size and create it - if (!this->ReadStruct(subchunk->m_DataDwSize)) goto subchunk_defer; - subchunk->m_pData = new CKDWORD[subchunk->m_DataDwSize]; - - // skip one? - // I don't know why - this->Skip(1u); - - // read core buf - if (!this->ReadByteData(subchunk->m_pData, subchunk->m_DataDwSize * CKSizeof(CKDWORD))) goto subchunk_defer; - - } - - return subchunk; - subchunk_defer: - if (subchunk != nullptr) delete subchunk; - return nullptr; - } - - /* ========== Buffer Functions ==========*/ - - bool CKStateChunk::ReadNoSizeBuffer(CKDWORD size_in_byte, void* allocatedBuf) { - if (allocatedBuf == nullptr) return false; - return this->ReadByteData(allocatedBuf, size_in_byte); - } - - bool CKStateChunk::ReadBuffer(void** buf, CKDWORD* len_in_byte) { - if (buf == nullptr || len_in_byte == nullptr) return false; - - // get buffer size. - CKDWORD bufByteSize = 0u; - if (!this->ReadStruct(bufByteSize)) { - *buf = nullptr; - *len_in_byte = 0; - return false; - } - *len_in_byte = bufByteSize; - - // special treat for zero length buffer - if (bufByteSize == 0) { - *buf = nullptr; - return true; - } - - // create buffer - *buf = new CKBYTE[bufByteSize]; - - // read data - if (!this->ReadByteData(*buf, bufByteSize)) { - this->DeleteBuffer(*buf); - *buf = nullptr; - *len_in_byte = 0; - return false; - } - - return true; - } - - void CKStateChunk::DeleteBuffer(const void* buf) { - if (buf == nullptr) return; - delete[] reinterpret_cast(buf); - } - - CKStateChunk::Buffer_t CKStateChunk::ReadBufferWrapper() { - void* bufcache = nullptr; - CKDWORD len_in_byte; - bool ret = ReadBuffer(&bufcache, &len_in_byte); - if (ret) { - return Buffer_t(bufcache, BufferDeleter(this, len_in_byte)); - } else { - return Buffer_t(); - } - } - - /* ========== Sequence Functions ==========*/ - - bool CKStateChunk::ReadObjectIDSequence(XContainer::XArray* ls) { - if (ls == nullptr) return false; - ls->clear(); - - // read count - CKDWORD count; - if (!this->ReadStruct(count)) return false; - - // resize list and read it - ls->resize(count); - for (size_t i = 0; i < count; ++i) { - if (!this->ReadObjectID(ls->data() + i)) { - ls->clear(); - return false; - } - } - - return true; - } - - bool CKStateChunk::ReadManagerIntSequence(CKGUID* guid, XContainer::XArray* ls) { - if (guid == nullptr || ls == nullptr) return false; - - // read count - CKDWORD count; - if (!this->ReadStruct(count)) return false; - - // read guid - if (!this->ReadStruct(guid)) return false; - - // resize list and read it - ls->resize(count); - for (size_t i = 0; i < count; ++i) { - if (!this->ReadStruct(ls->data() + i)) { - ls->clear(); - return false; - } - } - - return true; - } - - bool CKStateChunk::ReadSubChunkSequence(XContainer::XArray* ls) { - if (ls == nullptr) return false; - - // clear first - for (auto& item : *ls) { - if (item != nullptr) - delete (item); - } - ls->clear(); - - // read count - CKDWORD count; - if (!this->ReadStruct(count)) return false; - - // resize list and read it - ls->resize(count, nullptr); - for (size_t i = 0; i < count; ++i) { - (*ls)[i] = this->ReadSubChunk(); - if ((*ls)[i] == nullptr) { - // fail. remove all created statechunk and clear it - for (auto& item : *ls) { - if (item != nullptr) - delete (item); - } - ls->clear(); - // return - return false; - } - } - - return true; - } - - bool CKStateChunk::ReadXObjectArray(XContainer::XObjectArray* ls) { - if (ls == nullptr) return false; - ls->clear(); - - // read count - CKDWORD count; - if (!this->ReadStruct(count)) return false; - if (count == 0) return true; // 0 size array - - // old file size correction - bool old_file = this->m_ChunkVersion < CK_STATECHUNK_CHUNKVERSION::CHUNK_VERSION1; - if (old_file) { - // skip 4. but I don't know why!!! - this->Skip(4); - if (!this->ReadStruct(count)) return false; - } - - // resize list and read - ls->resize(count); - for (auto& id : *ls) { - // read ID first - CKINT cache; - if (!this->ReadStruct(cache)) { - ls->clear(); - return false; - } - - // in old file or no bind file, the read data directly is CK_ID. - // in new file or has bind file, the read data is the index in FileObjects - if (old_file || this->m_BindFile == nullptr) { - id = static_cast(cache); - } else { - if (cache < 0) id = 0; - else id = this->m_BindFile->GetFileObjectByIndex(cache)->CreatedObjectId; - } - } - - return true; - } - - bool CKStateChunk::ReadXObjectPointerArray(XContainer::XObjectPointerArray* ls) { - if (ls == nullptr) return false; - - // very very similar to ReadXObjectArray - // we execute it first. - XContainer::XObjectArray idarr; - if (!ReadXObjectArray(idarr)) return false; - - // then convert it to pointer list - ls->resize(idarr.size()); - for (size_t i = 0; i < idarr.size(); ++i) { - (*ls)[i] = m_BindContext->GetObject(idarr[i]); - } - - return true; - } - -#pragma endregion - - -#pragma region Write Functions - - void CKStateChunk::StartWrite() { - if (this->m_Parser.m_Status != CKStateChunkStatus::IDLE) return; - - // delete all current buffer - if (this->m_pData != nullptr) { - delete[] this->m_pData; - this->m_pData = nullptr; - } - this->m_DataDwSize = 0u; - - // reset parser - this->m_Parser.m_CurrentPos = 0u; - this->m_Parser.m_DataSize = this->m_DataDwSize; - this->m_Parser.m_PrevIdentifierPos = 0u; - - // force chunk version - this->m_ChunkVersion = CK_STATECHUNK_CHUNKVERSION::CHUNK_VERSION4; - - // switch status - this->m_Parser.m_Status = CKStateChunkStatus::WRITE; - } - - void CKStateChunk::StopWrite(void) { - if (this->m_Parser.m_Status != CKStateChunkStatus::WRITE) return; - - // update buffer size - this->m_DataDwSize = this->m_Parser.m_CurrentPos; - // shrink it - ResizeBuffer(this->m_DataDwSize); - - // shrink 3 vector also - this->m_ObjectList.shrink_to_fit(); - this->m_ManagerList.shrink_to_fit(); - this->m_ChunkList.shrink_to_fit(); - - // reset parser - this->m_Parser.m_CurrentPos = 0u; - this->m_Parser.m_DataSize = this->m_DataDwSize; - this->m_Parser.m_PrevIdentifierPos = 0u; - this->m_Parser.m_Status = CKStateChunkStatus::IDLE; - } - - bool CKStateChunk::LockWriteBuffer(const void** ppData, CKDWORD size_in_byte) { - return false; - } - - bool CKStateChunk::UnLockWriteBuffer(CKDWORD size_in_byte) { - return false; - } - - CKStateChunk::LockedWriteBuffer_t CKStateChunk::LockWriteBufferWrapper(CKDWORD size_in_byte) { - return LockedWriteBuffer_t(); - } - -#pragma endregion - } diff --git a/LibCmo/CK2/CKStateChunkReader.cpp b/LibCmo/CK2/CKStateChunkReader.cpp new file mode 100644 index 0000000..012e54d --- /dev/null +++ b/LibCmo/CK2/CKStateChunkReader.cpp @@ -0,0 +1,479 @@ +#include "CKStateChunk.hpp" +#include "CKFile.hpp" +#include "CKContext.hpp" + +namespace LibCmo::CK2 { + + void CKStateChunk::StartRead(void) { + if (this->m_Parser.m_Status != CKStateChunkStatus::IDLE) return; + + this->m_Parser.m_CurrentPos = 0u; + this->m_Parser.m_DataSize = this->m_DataDwSize; + this->m_Parser.m_PrevIdentifierPos = 0u; + this->m_Parser.m_Status = CKStateChunkStatus::READ; + } + + void CKStateChunk::StopRead(void) { + if (this->m_Parser.m_Status != CKStateChunkStatus::READ) return; + + this->m_Parser.m_CurrentPos = 0u; + this->m_Parser.m_DataSize = this->m_DataDwSize; + this->m_Parser.m_PrevIdentifierPos = 0u; + this->m_Parser.m_Status = CKStateChunkStatus::IDLE; + } + + /* ========== Identifier Functions ==========*/ + + bool CKStateChunk::SeekIdentifierDword(CKDWORD identifier) { + CKDWORD cache; + return SeekIdentifierDwordAndReturnSize(identifier, &cache); + } + + bool CKStateChunk::SeekIdentifierDwordAndReturnSize(CKDWORD identifier, CKDWORD* out_size) { + if (this->m_Parser.m_Status != CKStateChunkStatus::READ) return false; + + CKDWORD pos = 0u; + if (this->m_DataDwSize < 2u) return false; // impossible to have a identifier + + // search identifier + while (this->m_pData[pos] != identifier) { + pos = this->m_pData[pos + 1]; + if (pos == 0u) return false; // got tail. no more identifier + if (pos + 1 >= this->m_DataDwSize) return false; // out of buffer + } + + // got identifier + this->m_Parser.m_PrevIdentifierPos = pos; + this->m_Parser.m_CurrentPos = pos + 2; + + // calc size + CKDWORD nextptr = this->m_pData[pos + 1]; + if (nextptr == 0) { + // the last identifier, use chunk size instead + nextptr = this->m_DataDwSize; + } + *out_size = CKSizeof(CKDWORD) * (nextptr - pos - 2u); + return true; + } + + bool CKStateChunk::LockReadBuffer(const void** ppData, CKDWORD size_in_byte) { + // check arguments + if (*ppData == nullptr) return false; + *ppData = nullptr; + // check self status + if (this->m_Parser.m_Status != CKStateChunkStatus::READ) return false; + + // get corresponding size + CKDWORD size_in_dword = this->GetCeilDwordSize(size_in_byte); + // ensure space + if (this->EnsureReadSpace(size_in_dword)) { + *ppData = this->m_pData + this->m_Parser.m_CurrentPos; + return true; + } else { + // failed, report to context + m_BindContext->OutputToConsoleEx("CKStateChunk::LockReadBuffer at buffer pos %" PRIuCKDWORD ".", this->m_Parser.m_CurrentPos); + return false; + } + } + + bool CKStateChunk::UnLockReadBuffer(CKDWORD size_in_byte) { + // check self status + if (this->m_Parser.m_Status != CKStateChunkStatus::READ) return false; + + // get corresponding size + CKDWORD size_in_dword = this->GetCeilDwordSize(size_in_byte); + // ensure space + if (this->EnsureReadSpace(size_in_dword)) { + this->m_Parser.m_CurrentPos += size_in_dword; + return true; + } else { + // failed, report to context + m_BindContext->OutputToConsoleEx("CKStateChunk::UnLockReadBuffer at buffer pos %" PRIuCKDWORD ".", this->m_Parser.m_CurrentPos); + return false; + } + } + + CKStateChunk::LockedReadBuffer_t CKStateChunk::LockReadBufferWrapper(CKDWORD size_in_byte) { + const void* pData; + bool ret = LockReadBuffer(&pData, size_in_byte); + if (ret) { + return LockedReadBuffer_t(pData, LockedReadBufferDeleter(this, size_in_byte)); + } else { + return LockedReadBuffer_t(); + } + } + + /* ========== Basic Data Read Functions ==========*/ + + + bool CKStateChunk::ReadByteData(void* data_ptr, CKDWORD size_in_byte) { + if (data_ptr == nullptr) return false; + + const void* pData; + bool ret = LockReadBuffer(&pData, size_in_byte); + if (ret) { + std::memcpy(data_ptr, pData, size_in_byte); + UnLockReadBuffer(size_in_byte); + return true; + } else { + return false; + } + } + + bool CKStateChunk::ReadString(XContainer::XString* strl) { + if (strl == nullptr) return false; + + // get byte based size + CKDWORD strByteSize = 0u; + if (!this->ReadStruct(strByteSize)) { + strl->clear(); + return false; + } + + // read data + XContainer::XString cache; + cache.resize(strByteSize); + if (!this->ReadByteData(cache.data(), strByteSize)) { + strl->clear(); + return false; + } + + // convert encoding + m_BindContext->GetUtf8String(cache, *strl); + return true; + } + + /* ========== Complex Data Read Functions ==========*/ + + bool CKStateChunk::ReadObjectID(CK_ID* id) { + if (id == nullptr) return false; + + // get basic value + CKINT gotten_id = 0; + if (!this->ReadStruct(gotten_id)) return false; + + // different strategy according to chunk ver + if (this->m_ChunkVersion >= CK_STATECHUNK_CHUNKVERSION::CHUNK_VERSION1) { + // new file + + // if no doc associated, return directly + if (this->m_BindFile == nullptr) { + *id = static_cast(gotten_id); + return true; + } + // if it is positive, return corresponding value + if (gotten_id >= 0) { + *id = this->m_BindFile->GetFileObjectByIndex(gotten_id)->CreatedObjectId; + return true; + } + + } else { + // old file + // i don't know why I need skip 2 DWORD + // just copy IDA code. + + if (gotten_id) { + this->Skip(2); + return this->ReadStruct(id); + } + } + + // all failed + *id = 0u; + return false; + } + + bool CKStateChunk::ReadManagerInt(CKGUID* guid, CKINT* intval) { + if (guid == nullptr || intval == nullptr) return false; + + // read guid first + if (!this->ReadStruct(guid)) return false; + // then read int value + if (!this->ReadStruct(intval)) return false; + + return true; + } + + CKStateChunk* CKStateChunk::ReadSubChunk(void) { + CKStateChunk* subchunk = nullptr; + + // get size and do a enough space check + CKDWORD subChunkSize; + if (!this->ReadStruct(subChunkSize)) goto subchunk_defer; + if (!this->EnsureReadSpace(subChunkSize)) goto subchunk_defer; + + // create statechunk + subchunk = new CKStateChunk(this->m_BindFile, this->m_BindContext); + + // start read data + // read class id + if (!this->ReadStruct(subchunk->m_ClassId)) goto subchunk_defer; + + // different read strategy by chunk version + if (this->m_ChunkVersion >= CK_STATECHUNK_CHUNKVERSION::CHUNK_VERSION1) { + // new file + + // read combined version + CKDWORD versionInfo; + if (!this->ReadStruct(versionInfo)) goto subchunk_defer; + subchunk->m_DataVersion = static_cast(versionInfo & 0xffff); + subchunk->m_ChunkVersion = static_cast((versionInfo >> 16) & 0xffff); + + // read data size and create it + if (!this->ReadStruct(subchunk->m_DataDwSize)) goto subchunk_defer; + subchunk->m_pData = new CKDWORD[subchunk->m_DataDwSize]; + + // has bind file? + CKDWORD hasBindFile; + if (!this->ReadStruct(hasBindFile)) goto subchunk_defer; + if (hasBindFile == 1) subchunk->m_BindFile = nullptr; + + // 3 list size + // manager only existed when ver > 4 + CKDWORD lssize; + if (!this->ReadStruct(lssize)) goto subchunk_defer; + subchunk->m_ObjectList.resize(lssize); + if (!this->ReadStruct(lssize)) goto subchunk_defer; + subchunk->m_ChunkList.resize(lssize); + if (this->m_ChunkVersion > CK_STATECHUNK_CHUNKVERSION::CHUNK_VERSION1) { + if (!this->ReadStruct(lssize)) goto subchunk_defer; + subchunk->m_ManagerList.resize(lssize); + } + + // core data + if (subchunk->m_DataDwSize != 0) { + if (!this->ReadByteData(subchunk->m_pData, subchunk->m_DataDwSize * CKSizeof(CKDWORD))) goto subchunk_defer; + } + + // 3 list data + if (!subchunk->m_ObjectList.empty()) { + if (!this->ReadByteData( + subchunk->m_ObjectList.data(), + static_cast(subchunk->m_ObjectList.size()) * CKSizeof(CKDWORD) + )) goto subchunk_defer; + } + if (!subchunk->m_ChunkList.empty()) { + if (!this->ReadByteData( + subchunk->m_ChunkList.data(), + static_cast(subchunk->m_ChunkList.size()) * CKSizeof(CKDWORD) + )) goto subchunk_defer; + } + if (!subchunk->m_ManagerList.empty()) { + if (!this->ReadByteData( + subchunk->m_ManagerList.data(), + static_cast(subchunk->m_ManagerList.size()) * CKSizeof(CKDWORD) + )) goto subchunk_defer; + } + + } else { + // old file + + // read data size and create it + if (!this->ReadStruct(subchunk->m_DataDwSize)) goto subchunk_defer; + subchunk->m_pData = new CKDWORD[subchunk->m_DataDwSize]; + + // skip one? + // I don't know why + this->Skip(1u); + + // read core buf + if (!this->ReadByteData(subchunk->m_pData, subchunk->m_DataDwSize * CKSizeof(CKDWORD))) goto subchunk_defer; + + } + + return subchunk; + subchunk_defer: + if (subchunk != nullptr) delete subchunk; + return nullptr; + } + + /* ========== Buffer Functions ==========*/ + + bool CKStateChunk::ReadNoSizeBuffer(CKDWORD size_in_byte, void* allocatedBuf) { + if (allocatedBuf == nullptr) return false; + return this->ReadByteData(allocatedBuf, size_in_byte); + } + + bool CKStateChunk::ReadBuffer(void** buf, CKDWORD* len_in_byte) { + if (buf == nullptr || len_in_byte == nullptr) return false; + + // get buffer size. + CKDWORD bufByteSize = 0u; + if (!this->ReadStruct(bufByteSize)) { + *buf = nullptr; + *len_in_byte = 0; + return false; + } + *len_in_byte = bufByteSize; + + // special treat for zero length buffer + if (bufByteSize == 0) { + *buf = nullptr; + return true; + } + + // create buffer + *buf = new CKBYTE[bufByteSize]; + + // read data + if (!this->ReadByteData(*buf, bufByteSize)) { + this->DeleteBuffer(*buf); + *buf = nullptr; + *len_in_byte = 0; + return false; + } + + return true; + } + + void CKStateChunk::DeleteBuffer(const void* buf) { + if (buf == nullptr) return; + delete[] reinterpret_cast(buf); + } + + CKStateChunk::Buffer_t CKStateChunk::ReadBufferWrapper() { + void* bufcache = nullptr; + CKDWORD len_in_byte; + bool ret = ReadBuffer(&bufcache, &len_in_byte); + if (ret) { + return Buffer_t(bufcache, BufferDeleter(this, len_in_byte)); + } else { + return Buffer_t(); + } + } + + /* ========== Sequence Functions ==========*/ + + bool CKStateChunk::ReadObjectIDSequence(XContainer::XArray* ls) { + if (ls == nullptr) return false; + ls->clear(); + + // read count + CKDWORD count; + if (!this->ReadStruct(count)) return false; + + // resize list and read it + ls->resize(count); + for (size_t i = 0; i < count; ++i) { + if (!this->ReadObjectID(ls->data() + i)) { + ls->clear(); + return false; + } + } + + return true; + } + + bool CKStateChunk::ReadManagerIntSequence(CKGUID* guid, XContainer::XArray* ls) { + if (guid == nullptr || ls == nullptr) return false; + + // read count + CKDWORD count; + if (!this->ReadStruct(count)) return false; + + // read guid + if (!this->ReadStruct(guid)) return false; + + // resize list and read it + ls->resize(count); + for (size_t i = 0; i < count; ++i) { + if (!this->ReadStruct(ls->data() + i)) { + ls->clear(); + return false; + } + } + + return true; + } + + bool CKStateChunk::ReadSubChunkSequence(XContainer::XArray* ls) { + if (ls == nullptr) return false; + + // clear first + for (auto& item : *ls) { + if (item != nullptr) + delete (item); + } + ls->clear(); + + // read count + CKDWORD count; + if (!this->ReadStruct(count)) return false; + + // resize list and read it + ls->resize(count, nullptr); + for (size_t i = 0; i < count; ++i) { + (*ls)[i] = this->ReadSubChunk(); + if ((*ls)[i] == nullptr) { + // fail. remove all created statechunk and clear it + for (auto& item : *ls) { + if (item != nullptr) + delete (item); + } + ls->clear(); + // return + return false; + } + } + + return true; + } + + bool CKStateChunk::ReadXObjectArray(XContainer::XObjectArray* ls) { + if (ls == nullptr) return false; + ls->clear(); + + // read count + CKDWORD count; + if (!this->ReadStruct(count)) return false; + if (count == 0) return true; // 0 size array + + // old file size correction + bool old_file = this->m_ChunkVersion < CK_STATECHUNK_CHUNKVERSION::CHUNK_VERSION1; + if (old_file) { + // skip 4. but I don't know why!!! + this->Skip(4); + if (!this->ReadStruct(count)) return false; + } + + // resize list and read + ls->resize(count); + for (auto& id : *ls) { + // read ID first + CKINT cache; + if (!this->ReadStruct(cache)) { + ls->clear(); + return false; + } + + // in old file or no bind file, the read data directly is CK_ID. + // in new file or has bind file, the read data is the index in FileObjects + if (old_file || this->m_BindFile == nullptr) { + id = static_cast(cache); + } else { + if (cache < 0) id = 0; + else id = this->m_BindFile->GetFileObjectByIndex(cache)->CreatedObjectId; + } + } + + return true; + } + + bool CKStateChunk::ReadXObjectPointerArray(XContainer::XObjectPointerArray* ls) { + if (ls == nullptr) return false; + + // very very similar to ReadXObjectArray + // we execute it first. + XContainer::XObjectArray idarr; + if (!ReadXObjectArray(idarr)) return false; + + // then convert it to pointer list + ls->resize(idarr.size()); + for (size_t i = 0; i < idarr.size(); ++i) { + (*ls)[i] = m_BindContext->GetObject(idarr[i]); + } + + return true; + } + +} diff --git a/LibCmo/CK2/CKStateChunkWriter.cpp b/LibCmo/CK2/CKStateChunkWriter.cpp new file mode 100644 index 0000000..143462e --- /dev/null +++ b/LibCmo/CK2/CKStateChunkWriter.cpp @@ -0,0 +1,61 @@ +#include "CKStateChunk.hpp" +#include "CKFile.hpp" +#include "CKContext.hpp" + +namespace LibCmo::CK2 { + + void CKStateChunk::StartWrite() { + if (this->m_Parser.m_Status != CKStateChunkStatus::IDLE) return; + + // delete all current buffer + if (this->m_pData != nullptr) { + delete[] this->m_pData; + this->m_pData = nullptr; + } + this->m_DataDwSize = 0u; + + // reset parser + this->m_Parser.m_CurrentPos = 0u; + this->m_Parser.m_DataSize = this->m_DataDwSize; + this->m_Parser.m_PrevIdentifierPos = 0u; + + // force chunk version + this->m_ChunkVersion = CK_STATECHUNK_CHUNKVERSION::CHUNK_VERSION4; + + // switch status + this->m_Parser.m_Status = CKStateChunkStatus::WRITE; + } + + void CKStateChunk::StopWrite(void) { + if (this->m_Parser.m_Status != CKStateChunkStatus::WRITE) return; + + // update buffer size + this->m_DataDwSize = this->m_Parser.m_CurrentPos; + // shrink it + ResizeBuffer(this->m_DataDwSize); + + // shrink 3 vector also + this->m_ObjectList.shrink_to_fit(); + this->m_ManagerList.shrink_to_fit(); + this->m_ChunkList.shrink_to_fit(); + + // reset parser + this->m_Parser.m_CurrentPos = 0u; + this->m_Parser.m_DataSize = this->m_DataDwSize; + this->m_Parser.m_PrevIdentifierPos = 0u; + this->m_Parser.m_Status = CKStateChunkStatus::IDLE; + } + + bool CKStateChunk::LockWriteBuffer(const void** ppData, CKDWORD size_in_byte) { + return false; + } + + bool CKStateChunk::UnLockWriteBuffer(CKDWORD size_in_byte) { + return false; + } + + CKStateChunk::LockedWriteBuffer_t CKStateChunk::LockWriteBufferWrapper(CKDWORD size_in_byte) { + return LockedWriteBuffer_t(); + } + +} diff --git a/LibCmo/LibCmo.vcxproj b/LibCmo/LibCmo.vcxproj index 51ca96e..b93b09a 100644 --- a/LibCmo/LibCmo.vcxproj +++ b/LibCmo/LibCmo.vcxproj @@ -179,6 +179,8 @@ + + @@ -195,7 +197,7 @@ - + diff --git a/LibCmo/LibCmo.vcxproj.filters b/LibCmo/LibCmo.vcxproj.filters index b15688b..e3362aa 100644 --- a/LibCmo/LibCmo.vcxproj.filters +++ b/LibCmo/LibCmo.vcxproj.filters @@ -81,7 +81,7 @@ Sources\CK2 - + Sources\CK2 @@ -129,6 +129,12 @@ Sources\XContainer + + Sources\CK2 + + + Sources\CK2 +