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
CKCHAR filerawext[4];
CKGUID fileguid;
chk->ReadNoSizeBuffer(CKSizeof(filerawext), filerawext);
chk->ReadAndFillBuffer(filerawext, CKSizeof(filerawext));
chk->ReadStruct(fileguid);
CKFileExtension fileext(filerawext);
auto reader = DataHandlers::CKBitmapHandler::GetBitmapHandlerWrapper(fileext, fileguid);
@ -56,7 +56,7 @@ namespace LibCmo::CK2 {
VxMath::VxDoAlphaBlit(slot, static_cast<CKBYTE>(globalalpha));
} else {
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
CKStateChunk::Buffer_t redBuffer, greenBuffer, blueBuffer, alphaBuffer;
CKDWORD bufsize;
CKDWORD bufopt;
chk->ReadStruct(bufopt);
bufopt &= 0xFu;

View File

@ -19,6 +19,7 @@
#include "ObjImpls/CK3dObject.hpp"
#include "ObjImpls/CKTexture.hpp"
#include "ObjImpls/CKMaterial.hpp"
#include "ObjImpls/CKMesh.hpp"
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::CKTexture, CK_CLASSID::CKCID_TEXTURE, CK_CLASSID::CKCID_BEOBJECT, "Texture");
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 EasyClassRegWithNotify

View File

@ -65,7 +65,7 @@ namespace LibCmo::CK2 {
CKStateChunk* m_Host;
CKDWORD m_ConsumedSize;
};
class BufferDeleter {
public:
BufferDeleter() : m_Host(nullptr), m_BufSize(0) {}
@ -333,73 +333,89 @@ namespace LibCmo::CK2 {
/*
Buffer related function implements:
ReadBuffer(void**) Read Byte based size. -> ReadBuffer
ReadAndFillBuffer(int, void*) User give Byte based size. -> ReadNoSizeBuffer
ReadAndFillBuffer(void*) Read Byte based size. -> ReadBuffer
ReadAndFillBuffer_LEndian(int, void*) User give Byte based size. -> ReadNoSizeBuffer
ReadAndFillBuffer_LEndian(void*) Read Byte based size. -> ReadBuffer
ReadAndFillBuffer_LEndian16(int, void*) User give Byte based size. -> ReadNoSizeBuffer
ReadAndFillBuffer_LEndian16(void*) Read Byte based size. -> ReadBuffer
ReadBuffer(void**) Read Byte based size. -> ReadAndCopyBuffer(void**, CKDWORD*)
ReadAndFillBuffer(int, void*) User give Byte based size. -> ReadBuffer(const void**, CKDWORD)
ReadAndFillBuffer(void*) Read Byte based size. -> ReadBuffer(const void**, CKDWORD*)
ReadAndFillBuffer_LEndian(int, void*) User give Byte based size. -> ReadBuffer(const void**, CKDWORD)
ReadAndFillBuffer_LEndian(void*) Read Byte based size. -> ReadBuffer(const void**, CKDWORD*)
ReadAndFillBuffer_LEndian16(int, void*) User give Byte based size. -> ReadBuffer(const void**, CKDWORD)
ReadAndFillBuffer_LEndian16(void*) Read Byte based size. -> ReadBuffer(const void**, CKDWORD*)
*/
/**
* @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*)
*/
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.
* @brief Read buffer and copy it.
* The copied buffer and the size of buffer will be returned to caller.
* Caller should free the buffer by calling CKStateChunk::DeleteBuffer(void*).
* @param ppData[out] The pointer to pointer holding the new copied data.
* @param size_in_byte[out] Set to the size of buffer when success.
* @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 ReadBuffer(void** buf, CKDWORD* len_in_byte);
bool ReadBuffer(void** ppData, CKDWORD* size_in_byte);
/**
* @brief Free the buffer allocated by CKStateChunk reading functions.
* @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.
* }
* ```
* @brief A wrapper for ReadAndCopyBuffer(void**, CKDWORD*)
* @return
*/
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 ==========*/
/// <summary>

View File

@ -289,57 +289,87 @@ namespace LibCmo::CK2 {
/* ========== 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** ppData, CKDWORD* size_in_byte) {
if (ppData == nullptr || size_in_byte == nullptr) return false;
*ppData = nullptr;
*size_in_byte = 0;
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;
// read size first
if (!this->ReadStruct(size_in_byte)) {
return false;
}
*len_in_byte = bufByteSize;
// special treat for zero length buffer
if (bufByteSize == 0) {
*buf = nullptr;
// if it is empty buffer, create a fake buffer
// and simply return it.
if (*size_in_byte == 0) {
*ppData = new CKBYTE[1];
return true;
}
// create buffer
*buf = new CKBYTE[bufByteSize];
// read data
if (!this->ReadByteData(*buf, bufByteSize)) {
this->DeleteBuffer(*buf);
*buf = nullptr;
*len_in_byte = 0;
// use LockReadBuffer to get pointer
auto locker = LockReadBufferWrapper(*size_in_byte);
if (locker == nullptr) {
*size_in_byte = 0;
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;
}
void CKStateChunk::DeleteBuffer(const void* buf) {
if (buf == nullptr) return;
delete[] reinterpret_cast<const CKBYTE*>(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 {
void* cache;
CKDWORD size;
if (!ReadBuffer(&cache, &size)) {
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 ==========*/

View File

@ -79,14 +79,16 @@ namespace LibCmo::CK2::ObjImpls {
// read and set vertex count
CKDWORD vertexCount;
chunk->ReadStruct(vertexCount);
SetVertexCount(vertexCount);
if (vertexCount != 0) {
// read save flags
chunk->ReadStruct(saveflags);
// read size in dword
// read size in dword (including it self)
CKDWORD sizeInDword;
chunk->ReadStruct(sizeInDword);
--sizeInDword; // remove self.
// lock read buffer
auto buf = chunk->LockReadBufferWrapper(sizeInDword * CKSizeof(CKDWORD));
@ -211,7 +213,14 @@ namespace LibCmo::CK2::ObjImpls {
chunk->ReadStruct(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;
@ -227,6 +236,10 @@ namespace LibCmo::CK2::ObjImpls {
SetLineCount(0);
}
void CKMesh::BuildNormals() {}
void CKMesh::BuildFaceNormals() {}
#pragma region Vertex Section
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;
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.
* @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);
}