now can read CKMesh without memory issue

This commit is contained in:
yyc12345 2023-09-18 23:11:33 +08:00
parent 678529a664
commit d381369ed6
7 changed files with 149 additions and 89 deletions

View File

@ -17,7 +17,7 @@ namespace LibCmo::CK2 {
// get ext and guid to find correct guid // get ext and guid to find correct guid
CKCHAR filerawext[4]; CKCHAR filerawext[4];
CKGUID fileguid; CKGUID fileguid;
chk->ReadNoSizeBuffer(CKSizeof(filerawext), filerawext); chk->ReadAndFillBuffer(filerawext, CKSizeof(filerawext));
chk->ReadStruct(fileguid); chk->ReadStruct(fileguid);
CKFileExtension fileext(filerawext); CKFileExtension fileext(filerawext);
auto reader = DataHandlers::CKBitmapHandler::GetBitmapHandlerWrapper(fileext, fileguid); auto reader = DataHandlers::CKBitmapHandler::GetBitmapHandlerWrapper(fileext, fileguid);
@ -56,7 +56,7 @@ namespace LibCmo::CK2 {
VxMath::VxDoAlphaBlit(slot, static_cast<CKBYTE>(globalalpha)); VxMath::VxDoAlphaBlit(slot, static_cast<CKBYTE>(globalalpha));
} else { } else {
auto alphabuf = chk->ReadBufferWrapper(); auto alphabuf = chk->ReadBufferWrapper();
VxMath::VxDoAlphaBlit(slot, reinterpret_cast<CKBYTE*>(alphabuf.get())); VxMath::VxDoAlphaBlit(slot, reinterpret_cast<const CKBYTE*>(alphabuf.get()));
} }
} }
@ -79,7 +79,6 @@ namespace LibCmo::CK2 {
// read RGBA buffer // read RGBA buffer
CKStateChunk::Buffer_t redBuffer, greenBuffer, blueBuffer, alphaBuffer; CKStateChunk::Buffer_t redBuffer, greenBuffer, blueBuffer, alphaBuffer;
CKDWORD bufsize;
CKDWORD bufopt; CKDWORD bufopt;
chk->ReadStruct(bufopt); chk->ReadStruct(bufopt);
bufopt &= 0xFu; bufopt &= 0xFu;

View File

@ -19,6 +19,7 @@
#include "ObjImpls/CK3dObject.hpp" #include "ObjImpls/CK3dObject.hpp"
#include "ObjImpls/CKTexture.hpp" #include "ObjImpls/CKTexture.hpp"
#include "ObjImpls/CKMaterial.hpp" #include "ObjImpls/CKMaterial.hpp"
#include "ObjImpls/CKMesh.hpp"
namespace LibCmo::CK2 { namespace LibCmo::CK2 {
@ -414,6 +415,7 @@ CKClassRegister(cid, parentCid, \
EasyClassReg(ObjImpls::CK3dObject, CK_CLASSID::CKCID_3DOBJECT, CK_CLASSID::CKCID_3DENTITY, "3D Object"); EasyClassReg(ObjImpls::CK3dObject, CK_CLASSID::CKCID_3DOBJECT, CK_CLASSID::CKCID_3DENTITY, "3D Object");
EasyClassReg(ObjImpls::CKTexture, CK_CLASSID::CKCID_TEXTURE, CK_CLASSID::CKCID_BEOBJECT, "Texture"); EasyClassReg(ObjImpls::CKTexture, CK_CLASSID::CKCID_TEXTURE, CK_CLASSID::CKCID_BEOBJECT, "Texture");
EasyClassReg(ObjImpls::CKMaterial, CK_CLASSID::CKCID_MATERIAL, CK_CLASSID::CKCID_BEOBJECT, "Material"); EasyClassReg(ObjImpls::CKMaterial, CK_CLASSID::CKCID_MATERIAL, CK_CLASSID::CKCID_BEOBJECT, "Material");
EasyClassReg(ObjImpls::CKMesh, CK_CLASSID::CKCID_MESH, CK_CLASSID::CKCID_BEOBJECT, "Mesh");
#undef EasyClassReg #undef EasyClassReg
#undef EasyClassRegWithNotify #undef EasyClassRegWithNotify

View File

@ -65,7 +65,7 @@ namespace LibCmo::CK2 {
CKStateChunk* m_Host; CKStateChunk* m_Host;
CKDWORD m_ConsumedSize; CKDWORD m_ConsumedSize;
}; };
class BufferDeleter { class BufferDeleter {
public: public:
BufferDeleter() : m_Host(nullptr), m_BufSize(0) {} BufferDeleter() : m_Host(nullptr), m_BufSize(0) {}
@ -333,73 +333,89 @@ namespace LibCmo::CK2 {
/* /*
Buffer related function implements: Buffer related function implements:
ReadBuffer(void**) Read Byte based size. -> ReadBuffer ReadBuffer(void**) Read Byte based size. -> ReadAndCopyBuffer(void**, CKDWORD*)
ReadAndFillBuffer(int, void*) User give Byte based size. -> ReadNoSizeBuffer ReadAndFillBuffer(int, void*) User give Byte based size. -> ReadBuffer(const void**, CKDWORD)
ReadAndFillBuffer(void*) Read Byte based size. -> ReadBuffer ReadAndFillBuffer(void*) Read Byte based size. -> ReadBuffer(const void**, CKDWORD*)
ReadAndFillBuffer_LEndian(int, void*) User give Byte based size. -> ReadNoSizeBuffer ReadAndFillBuffer_LEndian(int, void*) User give Byte based size. -> ReadBuffer(const void**, CKDWORD)
ReadAndFillBuffer_LEndian(void*) Read Byte based size. -> ReadBuffer ReadAndFillBuffer_LEndian(void*) Read Byte based size. -> ReadBuffer(const void**, CKDWORD*)
ReadAndFillBuffer_LEndian16(int, void*) User give Byte based size. -> ReadNoSizeBuffer ReadAndFillBuffer_LEndian16(int, void*) User give Byte based size. -> ReadBuffer(const void**, CKDWORD)
ReadAndFillBuffer_LEndian16(void*) Read Byte based size. -> ReadBuffer ReadAndFillBuffer_LEndian16(void*) Read Byte based size. -> ReadBuffer(const void**, CKDWORD*)
*/ */
/** /**
* @brief Read a buffer with unknow size (order user specific it). * @brief Read buffer and copy it.
* @param size_in_byte[in] The size of buffer. * The copied buffer and the size of buffer will be returned to caller.
* @param allocatedBuf[out] Buffer for filling. * Caller should free the buffer by calling CKStateChunk::DeleteBuffer(void*).
* @return true if success. * @param ppData[out] The pointer to pointer holding the new copied data.
* @remark * @param size_in_byte[out] Set to the size of buffer when success.
* + Following original Virtools functions can use this function to implement: * @return True if success.
* - ReadAndFillBuffer(int, void*)
* - ReadAndFillBuffer_LEndian(int, void*)
* - ReadAndFillBuffer_LEndian16(int, void*)
*/
bool ReadNoSizeBuffer(CKDWORD size_in_byte, void* allocatedBuf);
/**
* @brief Read a buffer with knowen size stored in chunk.
* @param buf[out] a pointer to the pointer receiving data start address.
* @param len_in_byte[out] a pointer to the variable receiving the length of gotten buffer.
* @return true if success.
* @remark * @remark
* + Following original Virtools functions can use this function to implement: * + Following original Virtools functions can use this function to implement:
* - ReadBuffer(void**) * - ReadBuffer(void**)
* - ReadAndFillBuffer(void*)
* - ReadAndFillBuffer_LEndian(void*)
* - ReadAndFillBuffer_LEndian16(void*)
* + The created buffer should be freed by DeleteBuffer().
*/ */
bool ReadBuffer(void** buf, CKDWORD* len_in_byte); bool ReadBuffer(void** ppData, CKDWORD* size_in_byte);
/** /**
* @brief Free the buffer allocated by CKStateChunk reading functions. * @brief Free the buffer allocated by CKStateChunk reading functions.
* @param buf[in] The buffer need to be free. * @param buf[in] The buffer need to be free.
* @see ReadBuffer * @see ReadBuffer
*/ */
void DeleteBuffer(const void* buf); void DeleteBuffer(const void* buf);
/** /**
* @brief A RAII wrapper for ReadBuffer and DeleteBuffer * @brief A wrapper for ReadAndCopyBuffer(void**, CKDWORD*)
* @param uptr The pointer to unique_ptr receiving data. * @return
* @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(); Buffer_t ReadBufferWrapper();
/**
* @brief Read buffer and fill user struct.
* The size of buffer will be read from CKStateChunk internally and return to caller.
* @param pData[out] The pointer holding the data.
* @return True if success.
* @remark
* + Following original Virtools functions can use this function to implement:
* - ReadAndFillBuffer(void*)
* - ReadAndFillBuffer_LEndian(void*)
* - ReadAndFillBuffer_LEndian16(void*)
*/
bool ReadAndFillBuffer(void* pData);
/**
* @brief Read buffer and fill user struct.
* The size of buffer is provided by user.
* @param pData[out] The pointer holding the data.
* @param size_in_byte[in] The size of data which you want to read in byte unit
* @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*)
*/
bool ReadAndFillBuffer(void* pData, CKDWORD size_in_byte);
///**
// * @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 ==========*/ /* ========== Sequence Functions ==========*/
/// <summary> /// <summary>

View File

@ -289,57 +289,87 @@ namespace LibCmo::CK2 {
/* ========== Buffer Functions ==========*/ /* ========== Buffer Functions ==========*/
bool CKStateChunk::ReadNoSizeBuffer(CKDWORD size_in_byte, void* allocatedBuf) { bool CKStateChunk::ReadBuffer(void** ppData, CKDWORD* size_in_byte) {
if (allocatedBuf == nullptr) return false; if (ppData == nullptr || size_in_byte == nullptr) return false;
return this->ReadByteData(allocatedBuf, size_in_byte); *ppData = nullptr;
} *size_in_byte = 0;
bool CKStateChunk::ReadBuffer(void** buf, CKDWORD* len_in_byte) { // read size first
if (buf == nullptr || len_in_byte == nullptr) return false; if (!this->ReadStruct(size_in_byte)) {
// get buffer size.
CKDWORD bufByteSize = 0u;
if (!this->ReadStruct(bufByteSize)) {
*buf = nullptr;
*len_in_byte = 0;
return false; return false;
} }
*len_in_byte = bufByteSize;
// special treat for zero length buffer // if it is empty buffer, create a fake buffer
if (bufByteSize == 0) { // and simply return it.
*buf = nullptr; if (*size_in_byte == 0) {
*ppData = new CKBYTE[1];
return true; return true;
} }
// create buffer // use LockReadBuffer to get pointer
*buf = new CKBYTE[bufByteSize]; auto locker = LockReadBufferWrapper(*size_in_byte);
if (locker == nullptr) {
// read data *size_in_byte = 0;
if (!this->ReadByteData(*buf, bufByteSize)) {
this->DeleteBuffer(*buf);
*buf = nullptr;
*len_in_byte = 0;
return false; return false;
} }
// copy data and unlock buffer
*ppData = new CKBYTE[*size_in_byte];
std::memcpy(*ppData, locker.get(), *size_in_byte);
locker.reset();
return true; return true;
} }
void CKStateChunk::DeleteBuffer(const void* buf) { void CKStateChunk::DeleteBuffer(const void* buf) {
if (buf == nullptr) return; if (buf == nullptr) return;
delete[] reinterpret_cast<const CKBYTE*>(buf); delete[] reinterpret_cast<const CKBYTE*>(buf);
} }
CKStateChunk::Buffer_t CKStateChunk::ReadBufferWrapper() { CKStateChunk::Buffer_t CKStateChunk::ReadBufferWrapper() {
void* bufcache = nullptr; void* cache;
CKDWORD len_in_byte; CKDWORD size;
bool ret = ReadBuffer(&bufcache, &len_in_byte); if (!ReadBuffer(&cache, &size)) {
if (ret) {
return Buffer_t(bufcache, BufferDeleter(this, len_in_byte));
} else {
return Buffer_t(); return Buffer_t();
} }
return Buffer_t(cache, BufferDeleter(this, size));
}
bool CKStateChunk::ReadAndFillBuffer(void* pData) {
if (pData == nullptr) return false;
// get buffer size.
CKDWORD size_in_byte;
if (!this->ReadStruct(size_in_byte)) {
return false;
}
// use LockReadBuffer to get pointer
// this step can process zero length buffer
// so we do not treat it specially
auto locker = LockReadBufferWrapper(size_in_byte);
if (locker == nullptr) {
return false;
}
// then copy the data
std::memcpy(pData, locker.get(), size_in_byte);
locker.reset();
return true;
}
bool CKStateChunk::ReadAndFillBuffer(void* pData, CKDWORD size_in_byte) {
if (pData == nullptr) return false;
// directly use LockReadBuffer to get pointer
auto locker = LockReadBufferWrapper(size_in_byte);
if (locker == nullptr) {
return false;
}
// then copy the data
std::memcpy(pData, locker.get(), size_in_byte);
locker.reset();
return true;
} }
/* ========== Sequence Functions ==========*/ /* ========== Sequence Functions ==========*/

View File

@ -79,14 +79,16 @@ namespace LibCmo::CK2::ObjImpls {
// read and set vertex count // read and set vertex count
CKDWORD vertexCount; CKDWORD vertexCount;
chunk->ReadStruct(vertexCount); chunk->ReadStruct(vertexCount);
SetVertexCount(vertexCount);
if (vertexCount != 0) { if (vertexCount != 0) {
// read save flags // read save flags
chunk->ReadStruct(saveflags); chunk->ReadStruct(saveflags);
// read size in dword // read size in dword (including it self)
CKDWORD sizeInDword; CKDWORD sizeInDword;
chunk->ReadStruct(sizeInDword); chunk->ReadStruct(sizeInDword);
--sizeInDword; // remove self.
// lock read buffer // lock read buffer
auto buf = chunk->LockReadBufferWrapper(sizeInDword * CKSizeof(CKDWORD)); auto buf = chunk->LockReadBufferWrapper(sizeInDword * CKSizeof(CKDWORD));
@ -211,7 +213,14 @@ namespace LibCmo::CK2::ObjImpls {
chunk->ReadStruct(lineCount); chunk->ReadStruct(lineCount);
SetLineCount(lineCount); SetLineCount(lineCount);
chunk->ReadNoSizeBuffer(lineCount * 2 ) chunk->ReadAndFillBuffer(m_LineIndices.data(), CKSizeof(CKWORD) * lineCount * 2);
}
// build normals
if (EnumsHelper::Has(saveflags, VertexSaveFlags::NoNormal)) {
BuildNormals();
} else {
BuildFaceNormals();
} }
return true; return true;
@ -227,6 +236,10 @@ namespace LibCmo::CK2::ObjImpls {
SetLineCount(0); SetLineCount(0);
} }
void CKMesh::BuildNormals() {}
void CKMesh::BuildFaceNormals() {}
#pragma region Vertex Section #pragma region Vertex Section
CKDWORD CKMesh::GetVertexCount() { CKDWORD CKMesh::GetVertexCount() {

View File

@ -109,7 +109,7 @@ namespace LibCmo::VxMath {
} }
} }
void VxDoAlphaBlit(VxImageDescEx* dst_desc, CKBYTE* AlphaValues) { void VxDoAlphaBlit(VxImageDescEx* dst_desc, const CKBYTE* AlphaValues) {
if (dst_desc == nullptr) return; if (dst_desc == nullptr) return;
CKDWORD* pixels = dst_desc->GetMutablePixels(); CKDWORD* pixels = dst_desc->GetMutablePixels();

View File

@ -81,7 +81,7 @@ namespace LibCmo::VxMath {
* @param AlphaValues[in] A BYTE array containing the alpha values for each pixel. This array should be allocated to Width*Height bytes. * @param AlphaValues[in] A BYTE array containing the alpha values for each pixel. This array should be allocated to Width*Height bytes.
* @remark If the destination image does not have alpha information the function returns immediatly. * @remark If the destination image does not have alpha information the function returns immediatly.
*/ */
void VxDoAlphaBlit(VxImageDescEx* dst_desc, CKBYTE* AlphaValues); void VxDoAlphaBlit(VxImageDescEx* dst_desc, const CKBYTE* AlphaValues);
} }