diff --git a/LibCmo/CKDefines.hpp b/LibCmo/CKDefines.hpp index 3d688a4..2462f1e 100644 --- a/LibCmo/CKDefines.hpp +++ b/LibCmo/CKDefines.hpp @@ -26,6 +26,7 @@ namespace LibCmo { using XIntArray = std::vector; template using XClassArray = std::vector; + using XObjectArray = std::vector; //using CKObjectArray = std::vector; @@ -131,6 +132,39 @@ namespace LibCmo { VxMatrix(float m[4][4]) : m_Data() { std::memcpy(m_Data, m, sizeof(m_Data)); } }; + struct VxImageDescEx { + CK2::CKINT Size; + CK2::CKDWORD Flags; + + CK2::CKINT Width; + CK2::CKINT Height; + union { + CK2::CKINT BytesPerLine; + CK2::CKINT TotalImageSize; + }; + CK2::CKINT BitsPerPixel; + union { + CK2::CKDWORD RedMask; + CK2::CKDWORD BumpDuMask; + }; + union { + CK2::CKDWORD GreenMask; + CK2::CKDWORD BumpDvMask; + }; + union { + CK2::CKDWORD BlueMask; + CK2::CKDWORD BumpLumMask; + + }; + CK2::CKDWORD AlphaMask; + + CK2::CKWORD BytesPerColorEntry; + CK2::CKWORD ColorMapEntries; + + CK2::CKBYTE* ColorMap; + CK2::CKBYTE* Image; + }; + } } diff --git a/LibCmo/CKStateChunk.cpp b/LibCmo/CKStateChunk.cpp index d354d9d..5430990 100644 --- a/LibCmo/CKStateChunk.cpp +++ b/LibCmo/CKStateChunk.cpp @@ -17,8 +17,7 @@ namespace LibCmo::CK2 { m_ClassId(rhs.m_ClassId), m_DataVersion(rhs.m_DataVersion), m_ChunkVersion(rhs.m_ChunkVersion), m_Parser(rhs.m_Parser), m_ObjectList(rhs.m_ObjectList), m_ManagerList(rhs.m_ManagerList), m_ChunkList(rhs.m_ChunkList), - m_pData(nullptr), m_DataDwSize(rhs.m_DataDwSize) - { + m_pData(nullptr), m_DataDwSize(rhs.m_DataDwSize) { // copy buffer if (rhs.m_pData != nullptr) { this->m_pData = new(std::nothrow) CKDWORD[rhs.m_DataDwSize]; @@ -62,8 +61,8 @@ namespace LibCmo::CK2 { void CKStateChunk::Clear(void) { this->m_ClassId = CK_CLASSID::CKCID_OBJECT; - //this->m_DataVersion = CK_STATECHUNK_DATAVERSION::CHUNK_DEV_2_1; - //this->m_ChunkVersion = CK_STATECHUNK_CHUNKVERSION::CHUNK_VERSION4; + this->m_DataVersion = CK_STATECHUNK_DATAVERSION::CHUNK_DEV_2_1; + this->m_ChunkVersion = CK_STATECHUNK_CHUNKVERSION::CHUNK_VERSION4; this->m_Parser.m_CurrentPos = 0; this->m_Parser.m_DataSize = 0; @@ -84,8 +83,82 @@ namespace LibCmo::CK2 { return sizeof(CKDWORD) * this->m_DataDwSize; } - void CKStateChunk::_EnsureWriteSpace(CKDWORD size) { - ; + CK_STATECHUNK_DATAVERSION CKStateChunk::GetDataVersion() { + return this->m_DataVersion; + } + + void CKStateChunk::SetDataVersion(CK_STATECHUNK_DATAVERSION version) { + this->m_DataVersion = version; + } + + bool CKStateChunk::EnsureReadSpace(CKDWORD dword_required) { + return (m_Parser.m_Status == CKStateChunkStatus::READ) && + (this->m_Parser.m_CurrentPos + dword_required <= this->m_Parser.m_DataSize); + } + + bool CKStateChunk::ResizeBuffer(CKDWORD new_dwsize) { + if (new_dwsize == 0u) { + // if reuqired size is zero, we just delete it + if (this->m_pData != nullptr) { + delete[] this->m_pData; + this->m_pData = nullptr; + } + } else { + // otherwise, we create a new buffer instead it + CKDWORD* newbuf = new(std::nothrow) CKDWORD[new_dwsize]; + if (newbuf == nullptr) return false; // if fail to create, return + + // if no original data, we do not need copy it and free it + if (this->m_pData != nullptr) { + std::memcpy(newbuf, this->m_pData, sizeof(CKDWORD) * new_dwsize); + delete[] this->m_pData; + } + + // assign new buffer + this->m_pData = newbuf; + } + + return true; + } + + bool CKStateChunk::EnsureWriteSpace(CKDWORD dwsize) { + if (this->m_Parser.m_Status != CKStateChunkStatus::WRITE) return false; + + // check whether need enlarge + CKDWORD needed = dwsize + this->m_Parser.m_CurrentPos; + if (needed > this->m_Parser.m_DataSize) { + // add a very enough space to buffer + if (dwsize < 512) dwsize = 512; + needed = dwsize + this->m_Parser.m_CurrentPos; + + // try resizing it + this->ResizeBuffer(needed); + + // update size + this->m_Parser.m_DataSize = needed; + } + } + + bool CKStateChunk::Skip(CKDWORD DwordCount) { + bool result; + switch (this->m_Parser.m_Status) { + case CKStateChunkStatus::READ: + result = EnsureReadSpace(DwordCount); + break; + case CKStateChunkStatus::WRITE: + result = EnsureWriteSpace(DwordCount); + break; + case CKStateChunkStatus::IDLE: + default: + result = false; + break; + } + + // if success, move cursor + if (result) { + this->m_Parser.m_CurrentPos += DwordCount; + } + return result; } @@ -258,9 +331,7 @@ namespace LibCmo::CK2 { // return true; //} - bool CKStateChunk::SeekIdentifier(CKDWORD identifier) { - return false; - } +#pragma region Read Functions void CKStateChunk::StartRead(void) { if (this->m_Parser.m_Status != CKStateChunkStatus::IDLE) return; @@ -271,8 +342,116 @@ namespace LibCmo::CK2 { this->m_Parser.m_Status = CKStateChunkStatus::READ; } - void CKStateChunk::ReadString(std::string& strl) { - ; + bool CKStateChunk::SeekIdentifier(CKDWORD identifier) { + CKDWORD cache; + return SeekIdentifierAndReturnSize(identifier, &cache); } + bool CKStateChunk::SeekIdentifierAndReturnSize(CKDWORD identifier, CKDWORD* out_size) { + if (this->m_Parser.m_Status != CKStateChunkStatus::READ) return false; + + CKDWORD pos = 0u; + if (this->m_DataDwSize < 2) 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 = sizeof(CKDWORD) * (nextptr - pos); + return true; + } + + CKERROR CKStateChunk::ReadString(std::string& strl) { + // get byte based size + CKDWORD strByteSize = 0u; + CKERROR err = this->ReadStructPtr(&strByteSize); + if (err != CKERROR::CKERR_OK) { + strl.clear(); + return err; + } + + // convert to DWORD based + CKDWORD strDwordSize = this->GetCeilDwordSize(strByteSize); + + // cp + if (this->EnsureReadSpace(strDwordSize)) { + strl.resize(strByteSize); + std::memcpy(strl.data(), this->m_pData + this->m_Parser.m_CurrentPos, strByteSize); + this->m_Parser.m_CurrentPos += strDwordSize; + } else strl.clear(); + } + + + 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; + } + +#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; + } + +#pragma endregion + + } diff --git a/LibCmo/CKStateChunk.hpp b/LibCmo/CKStateChunk.hpp index 1cc08bd..6b81185 100644 --- a/LibCmo/CKStateChunk.hpp +++ b/LibCmo/CKStateChunk.hpp @@ -2,6 +2,8 @@ #include "CKDefines.hpp" #include "CKEnums.hpp" +#include +#include namespace LibCmo::CK2 { @@ -12,19 +14,6 @@ namespace LibCmo::CK2 { CKStateChunk& operator=(const CKStateChunk&); ~CKStateChunk(); - void Clear(void); - - bool ConvertFromBuffer(const void* buf); - CKDWORD ConvertToBuffer(void* buf); - - //bool UnPack(CKDWORD DestSize); - CKDWORD GetDataSize(void); - - bool SeekIdentifier(CKDWORD identifier); - - void StartRead(void); - void ReadString(std::string& strl); - private: enum class CKStateChunkStatus : int32_t { IDLE, @@ -51,10 +40,120 @@ namespace LibCmo::CK2 { std::vector m_ManagerList; private: - void _EnsureWriteSpace(CKDWORD size); - inline bool _EnsureReadSpace(CKDWORD required) { - return (this->m_Parser.m_CurrentPos <= this->m_Parser.m_DataSize) && (required <= (this->m_Parser.m_DataSize - this->m_Parser.m_CurrentPos)); + inline size_t GetCeilDwordSize(size_t char_size) { + return (char_size + 3) >> 3; } + bool ResizeBuffer(CKDWORD new_dwsize); + bool EnsureWriteSpace(CKDWORD dwsize); + bool EnsureReadSpace(CKDWORD dword_required); + + public: + bool ConvertFromBuffer(const void* buf); + CKDWORD ConvertToBuffer(void* buf); + + //bool UnPack(CKDWORD DestSize); + void Clear(void); + CKDWORD GetDataSize(void); + CK_STATECHUNK_DATAVERSION GetDataVersion(); + void SetDataVersion(CK_STATECHUNK_DATAVERSION version); + bool Skip(CKDWORD DwordCount); + +#pragma region Read Function + + public: + void StartRead(void); + bool SeekIdentifier(CKDWORD identifier); + bool SeekIdentifierAndReturnSize(CKDWORD identifier, CKDWORD* out_size); + CKERROR ReadString(std::string& strl); + /* + * Read Struct + Primitive type: ReadInt, ReadByte, ReadWord, ReadDword, ReadFloat, etc... + Struct type: ReadGuid, ReadVector, ReadMatrix, etc... + Both of them are redirected to this. + */ + template + CKERROR ReadStructPtr(T* data) { + size_t size = GetCeilDwordSize(sizeof(T)); + if (EnsureReadSpace(static_cast(size))) { + std::memcpy(data, this->m_pData + this->m_Parser.m_CurrentPos, size); + + } return CKERROR::CKERR_OUTOFMEMORY; + return CKERROR::CKERR_OK; + } + /* + * Read Struct + A wrapper for ReadStructPtr. + Use reference, not pointer. + */ + template + inline CKERROR ReadStructRef(T& data) { + return ReadStructPtr(&data); + } + /* + * Read Enum Data + A wrapper for ReadStructPtr. + All Enum read redirect to this. + */ + template + inline CKERROR ReadEnum(T& data) { + return ReadStructPtr(reinterpret_cast*>(&data)); + } + + CKERROR ReadBuffer(void* allocatedBuf); + CKERROR ReadNoSizeBuffer(CKDWORD size, void* allocatedBuf); + + //int ReadInt(); + //int StartReadSequence(); + //CK_ID ReadObjectID(); + //CKStateChunk* ReadSubChunk(); + //int StartManagerReadSequence(CKGUID* guid); + //CKGUID ReadGuid(); + //void ReadAndFillBuffer_LEndian(void* buffer); + //void ReadAndFillBuffer_LEndian16(void* buffer); + //float ReadFloat(); + //CKWORD ReadWord(); + //CKDWORD ReadDword(); + //CKDWORD ReadDwordAsWords(); + //void ReadVector(VxMath::VxVector* v); + //void ReadMatrix(VxMath::VxMatrix& mat); + //CKObjectImplements::CKObject* ReadObject(CKMinContext*); + //void ReadAndFillBuffer(void* buffer); + //CKBYTE* ReadRawBitmap(VxMath::VxImageDescEx& desc); + //XObjectArray ReadXObjectArray(void); + void StopRead(void); + +#pragma endregion + +#pragma region Write Function + + public: + void StartWrite(); + //void WriteIdentifier(CKDWORD id); + //void AddChunkAndDelete(CKStateChunk*); + //void StartObjectIDSequence(int count); + //void WriteObjectSequence(CKObjectImplements::CKObject* obj); + //void WriteInt(int data); + //void WriteFloat(float data); + //void WriteDword(CKDWORD data); + //void WriteDwordAsWords(CKDWORD data); + //void WriteVector(const VxMath::VxVector* v); + //void WriteMatrix(const VxMath::VxMatrix& mat); + //void WriteObject(CKObjectImplements::CKObject* obj); + //void WriteBuffer_LEndian(int size, void* buf); + //void WriteBuffer_LEndian16(int size, void* buf); + //void WriteBufferNoSize_LEndian(int size, void* buf); + ///*void UpdateDataSize();*/ + //void* LockWriteBuffer(int DwordCount); + + /* + * Old Name: CloseChunk(); + */ + void StopWrite(void); + +#pragma endregion + + + }; }