diff --git a/LibCmo/CK2/CKBitmapData.cpp b/LibCmo/CK2/CKBitmapData.cpp index d2cf160..02e37e8 100644 --- a/LibCmo/CK2/CKBitmapData.cpp +++ b/LibCmo/CK2/CKBitmapData.cpp @@ -30,16 +30,18 @@ namespace LibCmo::CK2 { chk->ReadStruct(imgbytesize); if (imgbytesize != 0) { // get image data ptr - const void* imgdata = nullptr; - if (!chk->ReadDryBuffer(&imgdata, imgbytesize)) { + auto imgdata = chk->LockReadBufferWrapper(imgbytesize); + if (imgdata == nullptr) { return false; } // parse image VxMath::VxImageDescEx cache; - if (!reader->ReadMemory(imgdata, imgbytesize, &cache)) { + if (!reader->ReadMemory(imgdata.get(), imgbytesize, &cache)) { return false; } + // unlock buffer + imgdata.reset(); // post proc image (copy to slot) VxMath::VxDoBlit(&cache, slot); @@ -53,9 +55,7 @@ namespace LibCmo::CK2 { chk->ReadStruct(globalalpha); VxMath::VxDoAlphaBlit(slot, static_cast(globalalpha)); } else { - CKStateChunk::TBuffer alphabuf; - CKDWORD buflen; - chk->ReadBufferWrapper(&alphabuf, &buflen); + auto alphabuf = chk->ReadBufferWrapper(); VxMath::VxDoAlphaBlit(slot, reinterpret_cast(alphabuf.get())); } } @@ -78,7 +78,7 @@ namespace LibCmo::CK2 { chk->ReadStruct(blueMask); // read RGBA buffer - CKStateChunk::TBuffer redBuffer, greenBuffer, blueBuffer, alphaBuffer; + CKStateChunk::Buffer_t redBuffer, greenBuffer, blueBuffer, alphaBuffer; CKDWORD bufsize; CKDWORD bufopt; chk->ReadStruct(bufopt); @@ -90,11 +90,11 @@ namespace LibCmo::CK2 { // so return false simply return false; } else { - chk->ReadBufferWrapper(&blueBuffer, &bufsize); - chk->ReadBufferWrapper(&greenBuffer, &bufsize); - chk->ReadBufferWrapper(&redBuffer, &bufsize); + blueBuffer = chk->ReadBufferWrapper(); + greenBuffer = chk->ReadBufferWrapper(); + redBuffer = chk->ReadBufferWrapper(); } - chk->ReadBufferWrapper(&alphaBuffer, &bufsize); + alphaBuffer = chk->ReadBufferWrapper(); // write into file if (redBuffer != nullptr && greenBuffer != nullptr && blueBuffer != nullptr) { diff --git a/LibCmo/CK2/CKStateChunk.cpp b/LibCmo/CK2/CKStateChunk.cpp index 560aa2c..e476cc3 100644 --- a/LibCmo/CK2/CKStateChunk.cpp +++ b/LibCmo/CK2/CKStateChunk.cpp @@ -99,6 +99,37 @@ namespace LibCmo::CK2 { #pragma endregion +#pragma region Self Used Data Struct + + void CKStateChunk::LockedReadBufferDeleter::operator()(LIBCMO_UNUSED const void* buf) { + if (m_Host == nullptr) return; + m_Host->UnLockReadBuffer(m_ConsumedSize); + } + + void CKStateChunk::LockedReadBufferDeleter::SetConsumedSize(CKDWORD newsize) { + m_ConsumedSize = newsize; + } + + void CKStateChunk::LockedWriteBufferDeleter::operator()(LIBCMO_UNUSED const void* buf) { + if (m_Host == nullptr) return; + m_Host->UnLockWriteBuffer(m_ConsumedSize); + } + + void CKStateChunk::LockedWriteBufferDeleter::SetConsumedSize(CKDWORD newsize) { + m_ConsumedSize = newsize; + } + + void CKStateChunk::BufferDeleter::operator()(const void* buf) { + if (m_Host == nullptr) return; + m_Host->DeleteBuffer(buf); + } + + CKDWORD CKStateChunk::BufferDeleter::GetBufferSize() const { + return m_BufSize; + } + +#pragma endregion + #pragma region Misc Funcs // ========== Public Funcs ========== @@ -525,24 +556,66 @@ namespace LibCmo::CK2 { return true; } - - /* ========== Basic Data Read Functions ==========*/ - - bool CKStateChunk::ReadByteData(void* data_ptr, CKDWORD size_in_byte) { + 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)) { - // only copy when data_ptr is not nullptr - // do dry run if there are no dest to copy for. - if (data_ptr != nullptr) { - std::memcpy(data_ptr, this->m_pData + this->m_Parser.m_CurrentPos, size_in_byte); - } this->m_Parser.m_CurrentPos += size_in_dword; return true; } else { // failed, report to context - m_BindContext->OutputToConsoleEx("CKStateChunk read length error at %" PRIuCKDWORD ".", this->m_Parser.m_CurrentPos); + 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; } } @@ -753,38 +826,22 @@ namespace LibCmo::CK2 { return true; } - bool CKStateChunk::ReadBufferWrapper(TBuffer* uptr, CKDWORD* len_in_byte) { - if (uptr == nullptr || len_in_byte == nullptr) return false; - - void* bufcache = nullptr; - bool ret = ReadBuffer(&bufcache, len_in_byte); - uptr->reset(bufcache); - - return ret; - } - - bool CKStateChunk::ReadDryBuffer(const void** buf, CKDWORD ordered_size) { - if (buf == nullptr) return false; - - // backup current pos - *buf = GetCurrentPointer(); - if (!this->ReadByteData(nullptr, ordered_size)) { - *buf = nullptr; - return false; - } - return true; - } - - void CKStateChunk::BufferDeleter::operator()(void* buf) { - if (buf == nullptr) return; - delete[] reinterpret_cast(buf); - } - 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) { @@ -966,6 +1023,18 @@ namespace LibCmo::CK2 { 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/CKStateChunk.hpp b/LibCmo/CK2/CKStateChunk.hpp index b6352a4..7481a8d 100644 --- a/LibCmo/CK2/CKStateChunk.hpp +++ b/LibCmo/CK2/CKStateChunk.hpp @@ -37,69 +37,52 @@ namespace LibCmo::CK2 { void* m_DataPtr; CKDWORD m_AreaSize; }; - template - class LockedBuffer_t { + + class LockedReadBufferDeleter { public: - /** - * @brief The type of free function called by this class. - * It must have 2 argument, first one is buffer need to be free, second is consumed size. - */ - using FreeFct = std::function; + LockedReadBufferDeleter() : m_Host(nullptr), m_ConsumedSize(0) {} + LockedReadBufferDeleter(CKStateChunk* host, CKDWORD init_size) : + m_Host(host), m_ConsumedSize(init_size) {} + LIBCMO_DEFAULT_COPY_MOVE(LockedReadBufferDeleter); - LockedBuffer_t() : m_Ptr(nullptr), m_ConsumedByteSize(0), m_FreeFct(nullptr) {} - LockedBuffer_t(const void* ptr, CKDWORD expectedByteSize, FreeFct fct) : - m_Ptr(const_cast(static_cast(ptr))), m_ConsumedByteSize(expectedByteSize), m_FreeFct(fct) {} - LockedBuffer_t(const LockedBuffer_t&) = delete; - LockedBuffer_t& operator=(const LockedBuffer_t&) = delete; - LockedBuffer_t(LockedBuffer_t&& rhs) : - m_Ptr(rhs.m_Ptr), m_ConsumedByteSize(rhs.m_ConsumedByteSize), m_FreeFct(rhs.m_FreeFct) { - rhs.m_Ptr = nullptr; - rhs.m_ConsumedByteSize = 0; - rhs.m_FreeFct = nullptr; - } - LockedBuffer_t& operator=(LockedBuffer_t&& rhs) { - // reset self - Reset(); - // copy data - m_Ptr = rhs.m_Ptr; - m_ConsumedByteSize = rhs.m_ConsumedByteSize; - m_FreeFct = rhs.m_FreeFct; - // remove rhs data. - rhs.m_Ptr = nullptr; - rhs.m_ConsumedByteSize = 0; - rhs.m_FreeFct = nullptr; - } - ~LockedBuffer_t() { - Reset(); - } - - const void* GetPtr() const { - return m_Ptr; - } - // References: - // https://stackoverflow.com/questions/43051882/how-to-disable-a-class-member-function-for-certain-template-types - // https://stackoverflow.com/questions/13964447/why-compile-error-with-enable-if - template = 0> - void* GetMutablePtr() { - return m_Ptr; - } - void SetRealConsumedSize(CKDWORD sizeInByte) { - m_ConsumedByteSize = sizeInByte; - } - void Reset() { - if (m_Ptr == nullptr) return; - if (m_FreeFct == nullptr) return; - m_FreeFct(m_Ptr, m_ConsumedByteSize); - - m_Ptr = nullptr; - m_ConsumedByteSize = 0; - m_FreeFct = nullptr; - } + void operator()(LIBCMO_UNUSED const void* buf); + void SetConsumedSize(CKDWORD newsize); private: - CKBYTE* m_Ptr; - CKDWORD m_ConsumedByteSize; - FreeFct m_FreeFct; + CKStateChunk* m_Host; + CKDWORD m_ConsumedSize; }; + + class LockedWriteBufferDeleter { + public: + LockedWriteBufferDeleter() : m_Host(nullptr), m_ConsumedSize(0) {} + LockedWriteBufferDeleter(CKStateChunk* host, CKDWORD init_size) : + m_Host(host), m_ConsumedSize(init_size) {} + LIBCMO_DEFAULT_COPY_MOVE(LockedWriteBufferDeleter); + + void operator()(LIBCMO_UNUSED const void* buf); + void SetConsumedSize(CKDWORD newsize); + private: + CKStateChunk* m_Host; + CKDWORD m_ConsumedSize; + }; + + class BufferDeleter { + public: + BufferDeleter() : m_Host(nullptr), m_BufSize(0) {} + BufferDeleter(CKStateChunk* host, CKDWORD bufsize) : + m_Host(host), m_BufSize(bufsize) {} + LIBCMO_DEFAULT_COPY_MOVE(BufferDeleter); + + void operator()(const void* buf); + CKDWORD GetBufferSize() const; + private: + CKStateChunk* m_Host; + CKDWORD m_BufSize; + }; + + using LockedReadBuffer_t = std::unique_ptr; + using LockedWriteBuffer_t = std::unique_ptr; + using Buffer_t = std::unique_ptr; private: enum class CKStateChunkStatus : CKDWORD { @@ -226,44 +209,91 @@ namespace LibCmo::CK2 { return SeekIdentifierDwordAndReturnSize(static_cast(enum_v), out_size); } + /* ========== Read Buffer Controller ==========*/ + + public: + /** + * @brief Lock read buffer and make sure it has at least some bytes to read. + * @param ppData[out] The pointer to pointer receiving data address. + * @param size_in_byte[in] The number of bytes present in the read buffer needs to be ensured. + * @return Treu if success (can read) + * @remark + * + It actually not lock the buffer, just make sure that the read area is okey. + * + Do not forget calling UnLockReadBuffer after all read work done. + * @see UnLockReadBuffer, ReadBufferLocker + */ + bool LockReadBuffer(const void** ppData, CKDWORD size_in_byte); + /** + * @brief Unlock read buffer and move forward with specified bytes. + * @param size_in_byte[in] The number of bytes you wish to move forward. + * This value can be different with the value passed to LockReadBuffer but should not greater than it. + * @return True if success (have enough space to move forward) + * @remark + * + It actually not unlock the buffer, just move forward reading pointer. + * + Used together with LockReadBuffer. + * @see LockReadBuffer + */ + bool UnLockReadBuffer(CKDWORD size_in_byte); + /** + * @brief A RAII wrapper for LockReadBuffer and UnLockReadBuffer. + * @param size_in_byte[in] The value passed to LockReadBuffer. + * @return A read-only buffer with RAII feature (more like std::unique_ptr). + * @remark + * + The return value is more like std::unique_ptr but it have more features. + * + If GeneralBuffer_t::GetPtr return nullptr, it mean this function is failed. + * + If you only use the pointer-getter provided by the return value, the final moving forward byte count is the value passed in this function. + * + You also can use GeneralBuffer_t::SetSize to set the final moving forward byte count before the return value free itself. + * + The value passed to GeneralBuffer_t::SetSize will finally be passed to UnLockReadBuffer. + * + You can use GeneralBuffer_t::Reset to force free the return value. + * @example + * ``` + * LockedReadBuffer_t buf = ReadBufferLocker(1919810); + * if (buf.GetPtr() == nullptr) { + * // failed + * } else { + * stuff(buf); // do some operation... + * buf.SetSize(114514); // i only consume these bytes. + * buf.Reset(); // immediately free it. + * } + * ``` + * @see LockReadBuffer, UnLockReadBuffer, LockedReadBuffer_t + */ + LockedReadBuffer_t LockReadBufferWrapper(CKDWORD size_in_byte); + /* ========== Basic Data Read Functions ==========*/ private: - - bool LockReadBuffer(void** ppData, CKDWORD expectedByteSize); - bool UnLockReadBuffer(CKDWORD realConsumedByteSize); - /** - * @brief The base read function for all data. - * This function will check all read requirements. - * If you have use this function or functions calling this function. You do not need check any reading requirements anymore. + * @brief A struct-read-friendly wrapper for LockReadBuffer and UnLockReadBuffer. + * All following struct reading function use this function as a underlaying calling. * @param data_ptr[out] the pointer to data. must be allocated first. * @param size_in_byte[in] the size of data in byte. * @return True if success. */ bool ReadByteData(void* data_ptr, CKDWORD size_in_byte); public: - /// - /// Read Struct - /// Primitive type: ReadInt, ReadByte, ReadWord, ReadDword, ReadFloat, etc... - /// Struct type: ReadGuid, ReadVector, ReadMatrix, etc... - /// Both of them are redirected to this. - /// - /// - /// - /// + /** + * @brief Read Struct + * @tparam T The reading type. + * @param data Data pointer for reading. Can not be bullptr + * @return True if reading success. + * @remark + * + This function is a reinterpter of a bunch of original Virtools SDK reading functions, including: + * - Primitive type: ReadInt, ReadByte, ReadWord, ReadDword, ReadFloat, etc... + * - Struct type: ReadGuid, ReadVector, ReadMatrix, etc... + * @see ReadStruct(T&) + */ template bool ReadStruct(T* data) { return ReadByteData(data, CKSizeof(T)); } - /// - /// Read Struct - /// A wrapper for ReadStructPtr. - /// Use reference, not pointer. - /// - /// - /// - /// + /** + * @brief Read Struct (Reference Type) + * @tparam T The reading type. + * @param data Data reference for reading. + * @return True if reading success. + * @see ReadStruct(T*) + */ template inline bool ReadStruct(T& data) { return ReadByteData(&data, CKSizeof(T)); @@ -291,14 +321,12 @@ namespace LibCmo::CK2 { return ReadManagerInt(&guid, &intval); } - /// - /// Read sub chunk - /// Return nullptr if failed. - /// Returned CKStateChunk should be manually released! - /// - /// - /// - CKStateChunk* ReadSubChunk(void); + /** + * @brief Read sub chunk + * @return Returned a new created of CKStateChunk if success, otherwise nullptr. + * Returned CKStateChunk should be manually released! + */ + CKStateChunk* ReadSubChunk(); /* ========== Buffer Functions ==========*/ @@ -315,61 +343,63 @@ namespace LibCmo::CK2 { */ /** - * @brief The deleter for std::unique_ptr of CKStateChunk created buffer. + * @brief Read a buffer with unknow size (order user specific it). + * @param size_in_byte[in] The size of buffer. + * @param allocatedBuf[out] Buffer for filling. + * @return true if success. + * @remark + * + Following original Virtools functions can use this function to implement: + * - ReadAndFillBuffer(int, void*) + * - ReadAndFillBuffer_LEndian(int, void*) + * - ReadAndFillBuffer_LEndian16(int, void*) */ - struct BufferDeleter { - BufferDeleter() = default; - BufferDeleter(const BufferDeleter&) noexcept {} - void operator()(void* buf); - }; - /** - * @brief The type of CKStateChunk auto free buffer. - */ - using TBuffer = std::unique_ptr; - - /// - /// Read a buffer with unknow size (order user specific it). - /// ReadAndFillBuffer(int, void*), ReadAndFillBuffer_LEndian(int, void*), ReadAndFillBuffer_LEndian16(int, void*) are redirected to this. - /// The buffer should be allocated by caller first. - /// - /// - /// - /// bool ReadNoSizeBuffer(CKDWORD size_in_byte, void* allocatedBuf); - /// - /// Read a buffer with knowen size stored in chunk. - /// ReadBuffer(void**), ReadAndFillBuffer(void*), ReadAndFillBuffer_LEndian(void*), ReadAndFillBuffer_LEndian16(void*) are redirected to this. - /// The buffer will be allocated by function. - /// Use CKStateChunk::DeleteBuffer() to delete it. - /// - /// a pointer to the pointer receiving data start address. - /// a pointer to the variable receiving the length of gotten buffer. - /// - bool ReadBuffer(void** buf, CKDWORD* len_in_byte); /** - * @brief A auto free wrapper for ReadBuffer - * @param uptr The pointer to unique_ptr receiving data. - * @param len_in_byte The size of gotten buffer. - * @return - */ - bool ReadBufferWrapper(TBuffer* uptr, CKDWORD* len_in_byte); - /** - * @brief Perform a dry buffer reading. - * This function will only make sure there is enough space for your reading. - * And return the start memory address to you. - * And will not create any extra memory like ReadBuffer. + * @brief Read a buffer with knowen size stored in chunk. * @param buf[out] a pointer to the pointer receiving data start address. - * @param ordered_sizepin] your expected length of this buffer. - * @return + * @param len_in_byte[out] a pointer to the variable receiving the length of gotten buffer. + * @return true if success. + * @remark + * + Following original Virtools functions can use this function to implement: + * - ReadBuffer(void**) + * - ReadAndFillBuffer(void*) + * - ReadAndFillBuffer_LEndian(void*) + * - ReadAndFillBuffer_LEndian16(void*) + * + The created buffer should be freed by DeleteBuffer(). */ - bool ReadDryBuffer(const void** buf, CKDWORD ordered_size); - + bool ReadBuffer(void** buf, CKDWORD* len_in_byte); + /** * @brief Free the buffer allocated by CKStateChunk reading functions. - * @param buf The buffer need to be free. + * @param buf[in] The buffer need to be free. + * @see ReadBuffer */ void DeleteBuffer(const void* buf); + /** + * @brief A RAII wrapper for ReadBuffer and DeleteBuffer + * @param uptr The pointer to unique_ptr receiving data. + * @param len_in_byte The size of gotten buffer. + * @return A buffer with RAII feature (more like std::unique_ptr). + * @remark + * + The return value is more like std::unique_ptr but it have more features. + * + If Buffer_t::GetPtr return nullptr, it mean this function is failed. + * + Use Buffer_t::GetSize to get the size of buffer. + * + You can use Buffer_t::Reset to force free the return value. + * @example + * ``` + * Buffer_t buf = ReadBufferWrapper(1919810); + * if (buf.GetPtr() == nullptr) { + * // failed + * } else { + * stuff(buf); // do some operation... + * buf.SetSize(114514); // i only consume these bytes. + * buf.Reset(); // immediately free it. + * } + * ``` + */ + Buffer_t ReadBufferWrapper(); + /* ========== Sequence Functions ==========*/ /// @@ -476,6 +506,10 @@ namespace LibCmo::CK2 { */ void StopWrite(void); + bool LockWriteBuffer(const void** ppData, CKDWORD size_in_byte); + bool UnLockWriteBuffer(CKDWORD size_in_byte); + LockedWriteBuffer_t LockWriteBufferWrapper(CKDWORD size_in_byte); + #pragma endregion diff --git a/LibCmo/CK2/ObjImpls/CKTexture.cpp b/LibCmo/CK2/ObjImpls/CKTexture.cpp index eee2acb..d519bff 100644 --- a/LibCmo/CK2/ObjImpls/CKTexture.cpp +++ b/LibCmo/CK2/ObjImpls/CKTexture.cpp @@ -168,9 +168,7 @@ namespace LibCmo::CK2::ObjImpls { // save properties if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_TEXSAVEFORMAT)) { - CKDWORD bufsize; - CKStateChunk::TBuffer buf; - chunk->ReadBufferWrapper(&buf, &bufsize); + auto buf = chunk->ReadBufferWrapper(); if (buf != nullptr) { FakeBitmapProperties* props = reinterpret_cast(buf.get()); diff --git a/Unvirt/StructFormatter.cpp b/Unvirt/StructFormatter.cpp index 0a2bc9f..472bee0 100644 --- a/Unvirt/StructFormatter.cpp +++ b/Unvirt/StructFormatter.cpp @@ -222,7 +222,7 @@ namespace Unvirt::StructFormatter { // write identifiers operchunk->StartRead(); - const auto collection = operchunk->GetIdentifierProfile(); + const auto collection = operchunk->GetIdentifiersProfile(); operchunk->StopRead(); fputs(UNVIRT_TERMCOL_LIGHT_YELLOW(("Identifiers\n")), stdout); fputs("Identifier\tData Pointer\tData Size\n", stdout);