chore: update build script
- change project layout for better understanding. - update build script for more close to standard cmake way.
This commit is contained in:
@@ -1,732 +0,0 @@
|
||||
#include "CKBitmapData.hpp"
|
||||
#include "CKContext.hpp"
|
||||
#include "CKStateChunk.hpp"
|
||||
#include "CKFile.hpp"
|
||||
#include "DataHandlers/CKBitmapHandler.hpp"
|
||||
#include "MgrImpls/CKPathManager.hpp"
|
||||
#include <memory>
|
||||
|
||||
namespace LibCmo::CK2 {
|
||||
|
||||
#pragma region Assist RW Functions
|
||||
|
||||
constexpr const CKDWORD c_SpecificFmtHasTransparent = 2;
|
||||
constexpr const CKDWORD c_SpecificFmtNoTransparent = 1;
|
||||
|
||||
bool CKBitmapData::ReadSpecificFormatBitmap(CKStateChunk* chk, VxMath::VxImageDescEx* slot) {
|
||||
// read transparent prop
|
||||
CKDWORD transprop;
|
||||
chk->ReadStruct(transprop);
|
||||
|
||||
// get ext and guid to find correct guid
|
||||
CKCHAR filerawext[4];
|
||||
CKGUID fileguid;
|
||||
chk->ReadAndFillBuffer(filerawext, CKSizeof(filerawext));
|
||||
chk->ReadStruct(fileguid);
|
||||
CKFileExtension fileext(filerawext);
|
||||
auto reader = DataHandlers::CKBitmapHandler::GetBitmapHandlerWrapper(fileext, fileguid);
|
||||
if (reader == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// read image size
|
||||
CKDWORD imgbytesize;
|
||||
chk->ReadStruct(imgbytesize);
|
||||
if (imgbytesize != 0) {
|
||||
// get image data ptr
|
||||
auto imgdata = chk->LockReadBufferWrapper(imgbytesize);
|
||||
if (imgdata == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// parse image
|
||||
VxMath::VxImageDescEx cache;
|
||||
if (!reader->ReadMemory(imgdata.get(), imgbytesize, &cache)) {
|
||||
return false;
|
||||
}
|
||||
// unlock buffer
|
||||
imgdata.reset();
|
||||
|
||||
// post proc image (copy to slot)
|
||||
VxMath::VxDoBlit(&cache, slot);
|
||||
|
||||
// proc image alpha
|
||||
if (transprop == c_SpecificFmtHasTransparent) {
|
||||
CKDWORD alphacount;
|
||||
chk->ReadStruct(alphacount);
|
||||
if (alphacount == 1) {
|
||||
CKDWORD globalalpha;
|
||||
chk->ReadStruct(globalalpha);
|
||||
VxMath::VxDoAlphaBlit(slot, static_cast<CKBYTE>(globalalpha));
|
||||
} else {
|
||||
auto alphabuf = chk->ReadBufferWrapper();
|
||||
VxMath::VxDoAlphaBlit(slot, static_cast<const CKBYTE*>(alphabuf.get()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CKBitmapData::ReadRawBitmap(CKStateChunk* chk, VxMath::VxImageDescEx* slot) {
|
||||
CKDWORD bytePerPixel, width, height, redMask, greenMask, blueMask, alphaMask;
|
||||
chk->ReadStruct(bytePerPixel); // not used
|
||||
if (bytePerPixel == 0) return false;
|
||||
|
||||
chk->ReadStruct(width);
|
||||
chk->ReadStruct(height);
|
||||
chk->ReadStruct(alphaMask);
|
||||
chk->ReadStruct(redMask);
|
||||
chk->ReadStruct(greenMask);
|
||||
chk->ReadStruct(blueMask);
|
||||
|
||||
// read RGBA buffer
|
||||
CKStateChunk::Buffer_t redBuffer, greenBuffer, blueBuffer, alphaBuffer;
|
||||
CKDWORD bufopt;
|
||||
chk->ReadStruct(bufopt);
|
||||
bufopt &= 0xFu;
|
||||
if (bufopt != 0) {
|
||||
// MARK: not supported CCompressionTools::jpegDecode()
|
||||
// There are some shitty jpeg decode function.
|
||||
// I do not want to touch them because all of my work do not related to them
|
||||
// so return false simply
|
||||
return false;
|
||||
} else {
|
||||
blueBuffer = chk->ReadBufferWrapper();
|
||||
greenBuffer = chk->ReadBufferWrapper();
|
||||
redBuffer = chk->ReadBufferWrapper();
|
||||
}
|
||||
alphaBuffer = chk->ReadBufferWrapper();
|
||||
|
||||
// write into file
|
||||
if (redBuffer != nullptr && greenBuffer != nullptr && blueBuffer != nullptr) {
|
||||
// create image
|
||||
slot->CreateImage(width, height);
|
||||
// get essential data
|
||||
CKDWORD pixelcount = slot->GetPixelCount();
|
||||
CKBYTE* dst = slot->GetMutableImage(),
|
||||
* redSrc = static_cast<CKBYTE*>(redBuffer.get()),
|
||||
* greenSrc = static_cast<CKBYTE*>(greenBuffer.get()),
|
||||
* blueSrc = static_cast<CKBYTE*>(blueBuffer.get()),
|
||||
* alphaSrc = static_cast<CKBYTE*>(alphaBuffer.get());
|
||||
for (CKDWORD p = 0; p < pixelcount; ++p) {
|
||||
*(dst++) = *(blueSrc++);
|
||||
*(dst++) = *(greenSrc++);
|
||||
*(dst++) = *(redSrc++);
|
||||
// if no alpha data, set to 0xFF
|
||||
*(dst++) = (alphaBuffer != nullptr ? (*(alphaSrc++)) : 0xFFu);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CKBitmapData::ReadOldRawBitmap(CKStateChunk* chk, VxMath::VxImageDescEx* slot) {
|
||||
// MARK: not supported because all of my work do not involve this function.
|
||||
return false;
|
||||
}
|
||||
|
||||
void CKBitmapData::WriteSpecificFormatBitmap(CKStateChunk* chk, const VxMath::VxImageDescEx* slot, const CKBitmapProperties* savefmt) {
|
||||
// check image validation
|
||||
if (slot->IsValid()) {
|
||||
// get reader
|
||||
auto reader = DataHandlers::CKBitmapHandler::GetBitmapHandlerWrapper(savefmt->m_Ext, savefmt->m_ReaderGuid);
|
||||
if (reader != nullptr) {
|
||||
|
||||
// prepare file data
|
||||
CKDWORD expectedSize = reader->SaveMemory(nullptr, slot, *savefmt);
|
||||
std::unique_ptr<CKBYTE[]> filebuf(new CKBYTE[expectedSize]);
|
||||
reader->SaveMemory(filebuf.get(), slot, *savefmt);
|
||||
|
||||
// in original Virtools design, only save alpha data when raw data can not represent alpha data
|
||||
bool canSaveAlpha = reader->CanSaveAlpha();
|
||||
|
||||
// write basic data
|
||||
// whether has transparent
|
||||
chk->WriteStruct(canSaveAlpha ? c_SpecificFmtNoTransparent : c_SpecificFmtHasTransparent);
|
||||
// write ext and guid
|
||||
chk->WriteBufferNoSize(savefmt->m_Ext.GetExt(), savefmt->m_Ext.GetSize());
|
||||
chk->WriteStruct(savefmt->m_ReaderGuid);
|
||||
|
||||
// write file data len and self
|
||||
chk->WriteStruct(expectedSize);
|
||||
chk->WriteBufferNoSize(filebuf.get(), expectedSize);
|
||||
// free filebuf
|
||||
filebuf.reset();
|
||||
|
||||
// write alpha if necessary
|
||||
if (!canSaveAlpha) {
|
||||
// prepare alpha list
|
||||
CKDWORD pixelCount = slot->GetPixelCount();
|
||||
std::unique_ptr<CKBYTE[]> alphabuf(new CKBYTE[pixelCount * VxMath::VxImageDescEx::ColorFactorSize]);
|
||||
VxMath::VxCopyStructure(
|
||||
pixelCount,
|
||||
alphabuf.get(),
|
||||
VxMath::VxImageDescEx::ColorFactorSize,
|
||||
VxMath::VxImageDescEx::ColorFactorSize,
|
||||
slot->GetImage() + 3 * VxMath::VxImageDescEx::ColorFactorSize, // move to A factor
|
||||
VxMath::VxImageDescEx::PixelSize
|
||||
);
|
||||
|
||||
// check whether alpha are the same value
|
||||
bool isSameAlpha = true;
|
||||
CKDWORD sameAlpha = 0;
|
||||
for (CKDWORD i = 0; i < pixelCount; ++i) {
|
||||
if (i == 0) {
|
||||
sameAlpha = static_cast<CKDWORD>(alphabuf[i * VxMath::VxImageDescEx::ColorFactorSize]);
|
||||
} else {
|
||||
if (sameAlpha != static_cast<CKDWORD>(alphabuf[i * VxMath::VxImageDescEx::ColorFactorSize])) {
|
||||
isSameAlpha = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// write alpha count
|
||||
// MARK: originally, the written value is how many different alpha value in this image.
|
||||
// so obviously its range is from 1 - 255 and 1 mean all alpha are the same value.
|
||||
// thus we write 2 here because Virtools do not depend this value in reader,
|
||||
// we only just let virtools to know there is a alpha list.
|
||||
chk->WriteStruct(isSameAlpha ? 1 : 2);
|
||||
if (isSameAlpha) {
|
||||
chk->WriteStruct(sameAlpha);
|
||||
} else {
|
||||
chk->WriteBuffer(alphabuf.get(), pixelCount * VxMath::VxImageDescEx::ColorFactorSize);
|
||||
}
|
||||
|
||||
// free alphabuf
|
||||
alphabuf.reset();
|
||||
|
||||
}
|
||||
|
||||
// free reader
|
||||
reader.reset();
|
||||
|
||||
// explicitly return to skip fallback
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// fallback
|
||||
// if image is invalid, or get reader failed.
|
||||
// write a zero
|
||||
chk->WriteStruct(0);
|
||||
|
||||
}
|
||||
|
||||
void CKBitmapData::WriteRawBitmap(CKStateChunk* chk, const VxMath::VxImageDescEx* slot) {
|
||||
// check image validation
|
||||
if (slot->IsValid()) {
|
||||
// write basic data
|
||||
chk->WriteStruct(32); // write bytePerPixel. always is 32 bpp.
|
||||
chk->WriteStruct(slot->GetWidth());
|
||||
chk->WriteStruct(slot->GetHeight());
|
||||
// alpha, red, green, blue mask, we always use ARGB8888 fmt.
|
||||
chk->WriteStruct(0xFF000000);
|
||||
chk->WriteStruct(0x00FF0000);
|
||||
chk->WriteStruct(0x0000FF00);
|
||||
chk->WriteStruct(0x000000FF);
|
||||
|
||||
// write bufopt
|
||||
// write 0 to indicate there is no shitty jpeg compression
|
||||
// see ReadRawBitmap for more info.
|
||||
chk->WriteStruct(0);
|
||||
|
||||
// MARK: originally there is a bunch of code to convert non-ARGB data into ARGB fmt.
|
||||
// but in my project design, all data is ARGB fmt, so we can simply write them.
|
||||
// all convertion code removed.
|
||||
|
||||
// create 4 channel buf
|
||||
// we always write alpha channel data.
|
||||
CKDWORD pixelCount = slot->GetPixelCount();
|
||||
CKDWORD bufSize = pixelCount * VxMath::VxImageDescEx::ColorFactorSize;
|
||||
std::unique_ptr<CKBYTE[]> redbuf(new CKBYTE[bufSize]);
|
||||
std::unique_ptr<CKBYTE[]> greenbuf(new CKBYTE[bufSize]);
|
||||
std::unique_ptr<CKBYTE[]> bluebuf(new CKBYTE[bufSize]);
|
||||
std::unique_ptr<CKBYTE[]> alphabuf(new CKBYTE[bufSize]);
|
||||
|
||||
// copy channel data
|
||||
// copy a
|
||||
VxMath::VxCopyStructure(
|
||||
pixelCount,
|
||||
alphabuf.get(),
|
||||
VxMath::VxImageDescEx::ColorFactorSize,
|
||||
VxMath::VxImageDescEx::ColorFactorSize,
|
||||
slot->GetImage() + (3 * VxMath::VxImageDescEx::ColorFactorSize),
|
||||
VxMath::VxImageDescEx::PixelSize
|
||||
);
|
||||
// copy r
|
||||
VxMath::VxCopyStructure(
|
||||
pixelCount,
|
||||
redbuf.get(),
|
||||
VxMath::VxImageDescEx::ColorFactorSize,
|
||||
VxMath::VxImageDescEx::ColorFactorSize,
|
||||
slot->GetImage() + (2 * VxMath::VxImageDescEx::ColorFactorSize),
|
||||
VxMath::VxImageDescEx::PixelSize
|
||||
);
|
||||
// copy g
|
||||
VxMath::VxCopyStructure(
|
||||
pixelCount,
|
||||
greenbuf.get(),
|
||||
VxMath::VxImageDescEx::ColorFactorSize,
|
||||
VxMath::VxImageDescEx::ColorFactorSize,
|
||||
slot->GetImage() + (1 * VxMath::VxImageDescEx::ColorFactorSize),
|
||||
VxMath::VxImageDescEx::PixelSize
|
||||
);
|
||||
// copy b
|
||||
VxMath::VxCopyStructure(
|
||||
pixelCount,
|
||||
bluebuf.get(),
|
||||
VxMath::VxImageDescEx::ColorFactorSize,
|
||||
VxMath::VxImageDescEx::ColorFactorSize,
|
||||
slot->GetImage() + (0 * VxMath::VxImageDescEx::ColorFactorSize),
|
||||
VxMath::VxImageDescEx::PixelSize
|
||||
);
|
||||
|
||||
// write 4 buf
|
||||
chk->WriteBuffer(bluebuf.get(), bufSize);
|
||||
chk->WriteBuffer(greenbuf.get(), bufSize);
|
||||
chk->WriteBuffer(redbuf.get(), bufSize);
|
||||
chk->WriteBuffer(alphabuf.get(), bufSize);
|
||||
|
||||
// free 4 buf
|
||||
redbuf.reset();
|
||||
greenbuf.reset();
|
||||
bluebuf.reset();
|
||||
alphabuf.reset();
|
||||
|
||||
// explicitly return to skip fallback
|
||||
return;
|
||||
}
|
||||
|
||||
// fallback
|
||||
// if image is invalid, write a zero
|
||||
chk->WriteStruct(0);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Core Read / Write
|
||||
|
||||
bool CKBitmapData::ReadFromChunk(CKStateChunk* chunk, CKFileVisitor* file, const CKBitmapDataReadIdentifiers& identifiers) {
|
||||
XContainer::XBitArray hasReadSlot;
|
||||
|
||||
// check 3 types enbedded image
|
||||
// MARK: i think there is a potential vulnerable issue.
|
||||
// if a slot failed, all following slot will read data from a wrong position.
|
||||
// thus the program will crash or allocated massive garbage data.
|
||||
if (chunk->SeekIdentifierDword(identifiers.m_SpecificFormat)) {
|
||||
// specific format
|
||||
CKDWORD slotcount, width, height, bpp;
|
||||
chunk->ReadStruct(slotcount);
|
||||
chunk->ReadStruct(width);
|
||||
chunk->ReadStruct(height);
|
||||
chunk->ReadStruct(bpp);
|
||||
|
||||
SetSlotCount(slotcount);
|
||||
hasReadSlot.resize(slotcount, false);
|
||||
|
||||
// the height and width is written outside of specific format
|
||||
// so we create image first for it.
|
||||
// and let reader to read data.
|
||||
// and free image if is is failed.
|
||||
if (width > 0 && height > 0) {
|
||||
for (CKDWORD i = 0; i < slotcount; ++i) {
|
||||
CreateImage(width, height, i);
|
||||
if (ReadSpecificFormatBitmap(chunk, GetImageDesc(i))) {
|
||||
XContainer::NSXBitArray::Set(hasReadSlot, i);
|
||||
} else {
|
||||
ReleaseImage(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if (chunk->SeekIdentifierDword(identifiers.m_RawData)) {
|
||||
// raw data
|
||||
CKDWORD slotcount;
|
||||
chunk->ReadStruct(slotcount);
|
||||
|
||||
SetSlotCount(slotcount);
|
||||
hasReadSlot.resize(slotcount, false);
|
||||
|
||||
// the height and width is read by raw data function self.
|
||||
// so we pass a cache variable to reader and do some modification
|
||||
// if it is success.
|
||||
for (CKDWORD i = 0; i < slotcount; ++i) {
|
||||
VxMath::VxImageDescEx rawcache;
|
||||
if (ReadRawBitmap(chunk, &rawcache)) {
|
||||
XContainer::NSXBitArray::Set(hasReadSlot, i);
|
||||
|
||||
// do upside down blit
|
||||
CreateImage(rawcache.GetWidth(), rawcache.GetHeight(), i);
|
||||
VxMath::VxDoBlitUpsideDown(&rawcache, GetImageDesc(i));
|
||||
}
|
||||
}
|
||||
|
||||
} else if (chunk->SeekIdentifierDword(identifiers.m_OldRawData)) {
|
||||
// raw data (old format)
|
||||
CKDWORD slotcount;
|
||||
chunk->ReadStruct(slotcount);
|
||||
|
||||
SetSlotCount(slotcount);
|
||||
hasReadSlot.resize(slotcount, false);
|
||||
|
||||
// MARK: a rough implement because we do not support this identifier
|
||||
for (CKDWORD i = 0; i < slotcount; ++i) {
|
||||
if (ReadOldRawBitmap(chunk, GetImageDesc(i))) {
|
||||
XContainer::NSXBitArray::Set(hasReadSlot, i);
|
||||
} else {
|
||||
ReleaseImage(i);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// file name section
|
||||
if (chunk->SeekIdentifierDword(identifiers.m_FileNames)) {
|
||||
// read slot count
|
||||
CKDWORD slotcount;
|
||||
chunk->ReadStruct(slotcount);
|
||||
SetSlotCount(slotcount);
|
||||
|
||||
// read string in detail
|
||||
// and try load not loaded image.
|
||||
for (CKDWORD i = 0; i < slotcount; ++i) {
|
||||
XContainer::XString filename;
|
||||
chunk->ReadString(filename);
|
||||
if (filename.empty()) continue;
|
||||
|
||||
bool isNotLoaded = (i >= hasReadSlot.size()) || (!XContainer::NSXBitArray::IsSet(hasReadSlot, i));
|
||||
if (isNotLoaded) {
|
||||
// if this image is not loaded.
|
||||
// try resolve its file name and load it.
|
||||
// and set resolved filename for it.
|
||||
if (m_Context->GetPathManager()->ResolveFileName(filename)) {
|
||||
if (LoadImage(filename.c_str(), i)) {
|
||||
SetSlotFileName(i, filename.c_str());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// otherwise, set filename simply
|
||||
SetSlotFileName(i, filename.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// movie info
|
||||
// MARK: movie is not implemented here.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CKBitmapData::DumpToChunk(CKStateChunk* chunk, CKFileVisitor* file, const CKBitmapDataWriteIdentifiers& identifiers) {
|
||||
// MARK: movie is not supported in this project in plan.
|
||||
|
||||
// resolve save fmt and save opt
|
||||
CK_TEXTURE_SAVEOPTIONS saveopt = GetSaveOptions();
|
||||
if (saveopt == CK_TEXTURE_SAVEOPTIONS::CKTEXTURE_USEGLOBAL) {
|
||||
saveopt = m_Context->GetGlobalImagesSaveOptions();
|
||||
}
|
||||
CKBitmapProperties savefmt(GetSaveFormat());
|
||||
if (GetSaveOptions() == CK_TEXTURE_SAVEOPTIONS::CKTEXTURE_USEGLOBAL) {
|
||||
savefmt = m_Context->GetGlobalImagesSaveFormat();
|
||||
}
|
||||
|
||||
// filter external / original files
|
||||
// the slot can not fulfill extrnal saving requirement will be written in raw data
|
||||
CKDWORD slotcount = GetSlotCount();
|
||||
XContainer::XBitArray validExternalSavingSlot;
|
||||
if (saveopt == CK_TEXTURE_SAVEOPTIONS::CKTEXTURE_EXTERNAL) {
|
||||
for (CKDWORD i = 0; i < slotcount; ++i) {
|
||||
if (GetSlotFileName(i) == nullptr) {
|
||||
saveopt = CK_TEXTURE_SAVEOPTIONS::CKTEXTURE_RAWDATA;
|
||||
} else {
|
||||
XContainer::NSXBitArray::Set(validExternalSavingSlot, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (saveopt == CK_TEXTURE_SAVEOPTIONS::CKTEXTURE_INCLUDEORIGINALFILE) {
|
||||
for (CKDWORD i = 0; i < slotcount; ++i) {
|
||||
if (file->AddSavedFile(GetSlotFileName(i))) {
|
||||
XContainer::NSXBitArray::Set(validExternalSavingSlot, i);
|
||||
} else {
|
||||
saveopt = CK_TEXTURE_SAVEOPTIONS::CKTEXTURE_RAWDATA;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// save data
|
||||
if (saveopt == CK_TEXTURE_SAVEOPTIONS::CKTEXTURE_RAWDATA) {
|
||||
// save as raw data
|
||||
chunk->WriteIdentifierDword(identifiers.m_RawData);
|
||||
chunk->WriteStruct(slotcount);
|
||||
|
||||
VxMath::VxImageDescEx invalidDesc;
|
||||
for (CKDWORD i = 0; i < slotcount; ++i) {
|
||||
VxMath::VxImageDescEx* thisimg = GetImageDesc(i);
|
||||
if (XContainer::NSXBitArray::IsSet(validExternalSavingSlot, i) || !thisimg->IsValid()) {
|
||||
// if this slot can save as external, pass a invalid desc to writer
|
||||
// or image is invalid, simply write it as invalid one.
|
||||
WriteRawBitmap(chunk, &invalidDesc);
|
||||
} else {
|
||||
// otherwise, pass the real slot data
|
||||
// do upside down first as reader done
|
||||
VxMath::VxImageDescEx upsidedown(thisimg->GetWidth(), thisimg->GetHeight());
|
||||
VxMath::VxDoBlitUpsideDown(thisimg, &upsidedown);
|
||||
WriteRawBitmap(chunk, &upsidedown);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (saveopt == CK_TEXTURE_SAVEOPTIONS::CKTEXTURE_IMAGEFORMAT) {
|
||||
// save as special format
|
||||
chunk->WriteIdentifierDword(identifiers.m_SpecificFormat);
|
||||
chunk->WriteStruct(slotcount);
|
||||
|
||||
// write width, height and bpp
|
||||
chunk->WriteStruct(GetWidth());
|
||||
chunk->WriteStruct(GetHeight());
|
||||
chunk->WriteStruct(32);
|
||||
|
||||
// write slot one by one
|
||||
for (CKDWORD i = 0; i < slotcount; ++i) {
|
||||
WriteSpecificFormatBitmap(chunk, GetImageDesc(i), &savefmt);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// write filename
|
||||
{
|
||||
chunk->WriteIdentifierDword(identifiers.m_FileNames);
|
||||
chunk->WriteStruct(slotcount);
|
||||
|
||||
XContainer::XString filename;
|
||||
for (CKDWORD i = 0; i < slotcount; ++i) {
|
||||
XContainer::NSXString::FromCKSTRING(filename, GetSlotFileName(i));
|
||||
m_Context->GetPathManager()->GetFileName(filename);
|
||||
chunk->WriteString(filename);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Slot Functions
|
||||
|
||||
void CKBitmapData::SetSlotCount(CKDWORD count) {
|
||||
m_Slots.resize(count);
|
||||
|
||||
if (count == 0) {
|
||||
YYCC::EnumHelper::Add(m_BitmapFlags, CK_BITMAPDATA_FLAGS::CKBITMAPDATA_INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
CKDWORD CKBitmapData::GetSlotCount() const {
|
||||
return static_cast<CKDWORD>(m_Slots.size());
|
||||
}
|
||||
|
||||
void CKBitmapData::SetCurrentSlot(CKDWORD slot) {
|
||||
if (slot >= m_Slots.size()) return;
|
||||
|
||||
m_CurrentSlot = slot;
|
||||
|
||||
// NOTE: idk what the fuck this is. just interpter the IDA decompiled code.
|
||||
if (YYCC::EnumHelper::Has(m_BitmapFlags, CK_BITMAPDATA_FLAGS::CKBITMAPDATA_CUBEMAP)) {
|
||||
YYCC::EnumHelper::Add(m_BitmapFlags, CK_BITMAPDATA_FLAGS::CKBITMAPDATA_FORCERESTORE);
|
||||
}
|
||||
}
|
||||
|
||||
CKDWORD CKBitmapData::GetCurrentSlot() const {
|
||||
return m_CurrentSlot;
|
||||
}
|
||||
|
||||
bool CKBitmapData::CreateImage(CKDWORD Width, CKDWORD Height, CKDWORD Slot) {
|
||||
if (Slot >= m_Slots.size()) return false;
|
||||
|
||||
CKBitmapSlot& slotdata = m_Slots[Slot];
|
||||
slotdata.m_ImageData.CreateImage(Width, Height);
|
||||
VxMath::VxDoAlphaBlit(&slotdata.m_ImageData, 0xFFu);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CKBitmapData::LoadImage(CKSTRING filename, CKDWORD slot) {
|
||||
if (filename == nullptr) return false;
|
||||
if (slot >= m_Slots.size()) return false;
|
||||
|
||||
// get extension of file. then get corresponding reader
|
||||
XContainer::XString ext(filename);
|
||||
m_Context->GetPathManager()->GetExtension(ext);
|
||||
auto reader = DataHandlers::CKBitmapHandler::GetBitmapHandlerWrapper(
|
||||
CKFileExtension(ext.c_str()),
|
||||
CKGUID()
|
||||
);
|
||||
if (reader == nullptr) return false;
|
||||
|
||||
// get desc and read data
|
||||
if (!reader->ReadFile(filename, GetImageDesc(slot))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CKBitmapData::SaveImage(CKSTRING filename, CKDWORD slot, bool isForceThisFmt) {
|
||||
if (filename == nullptr) return false;
|
||||
if (slot >= m_Slots.size()) return false;
|
||||
|
||||
// prepare save format
|
||||
CKBitmapProperties savefmt;
|
||||
if (isForceThisFmt) {
|
||||
savefmt = this->m_SaveProperties;
|
||||
} else {
|
||||
XContainer::XString ext(filename);
|
||||
m_Context->GetPathManager()->GetExtension(ext);
|
||||
if (ext.empty()) {
|
||||
// fallback to this fmt
|
||||
savefmt = this->m_SaveProperties;
|
||||
} else {
|
||||
savefmt.m_Ext.SetExt(ext.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// get reader by format
|
||||
auto reader = DataHandlers::CKBitmapHandler::GetBitmapHandlerWrapper(savefmt.m_Ext, savefmt.m_ReaderGuid);
|
||||
if (reader == nullptr) return false;
|
||||
|
||||
// save file
|
||||
if (!reader->SaveFile(filename, GetImageDesc(slot), savefmt)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
VxMath::VxImageDescEx* CKBitmapData::GetImageDesc(CKDWORD slot) {
|
||||
if (slot >= m_Slots.size()) return nullptr;
|
||||
return &m_Slots[slot].m_ImageData;
|
||||
}
|
||||
|
||||
void CKBitmapData::ReleaseImage(CKDWORD slot) {
|
||||
if (slot >= m_Slots.size()) return;
|
||||
m_Slots[slot].m_ImageData.FreeImage();
|
||||
}
|
||||
|
||||
bool CKBitmapData::SetSlotFileName(CKDWORD slot, CKSTRING filename) {
|
||||
if (slot >= m_Slots.size()) return false;
|
||||
if (filename == nullptr) return false;
|
||||
m_Slots[slot].m_FileName = filename;
|
||||
return true;
|
||||
}
|
||||
|
||||
CKSTRING CKBitmapData::GetSlotFileName(CKDWORD slot) const {
|
||||
if (slot >= m_Slots.size()) return nullptr;
|
||||
|
||||
// return nullptr if corresponding filename is empty to indicate there is no binding filename
|
||||
return XContainer::NSXString::ToCKSTRING(m_Slots[slot].m_FileName);
|
||||
}
|
||||
|
||||
CKDWORD CKBitmapData::GetWidth() const {
|
||||
for (auto& slot : m_Slots) {
|
||||
if (slot.m_ImageData.IsValid()) {
|
||||
return slot.m_ImageData.GetWidth();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
CKDWORD CKBitmapData::GetHeight() const {
|
||||
for (auto& slot : m_Slots) {
|
||||
if (slot.m_ImageData.IsValid()) {
|
||||
return slot.m_ImageData.GetHeight();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Not important variable visitor
|
||||
|
||||
CK_BITMAPDATA_FLAGS CKBitmapData::GetBitmapFlags() const {
|
||||
return m_BitmapFlags;
|
||||
}
|
||||
|
||||
void CKBitmapData::SetCubeMap(bool is_cube) {
|
||||
// MARK: originally we should resize solot to 6 exactly.
|
||||
// but we decide split the flag settings and slot.
|
||||
// User should set slot count manually.
|
||||
if (is_cube) {
|
||||
YYCC::EnumHelper::Add(m_BitmapFlags, CK_BITMAPDATA_FLAGS::CKBITMAPDATA_CUBEMAP);
|
||||
} else {
|
||||
YYCC::EnumHelper::Remove(m_BitmapFlags, CK_BITMAPDATA_FLAGS::CKBITMAPDATA_CUBEMAP);
|
||||
}
|
||||
}
|
||||
|
||||
bool CKBitmapData::IsCubeMap() const {
|
||||
return YYCC::EnumHelper::Has(m_BitmapFlags, CK_BITMAPDATA_FLAGS::CKBITMAPDATA_CUBEMAP);
|
||||
}
|
||||
|
||||
const CKBitmapProperties& CKBitmapData::GetSaveFormat() const {
|
||||
return m_SaveProperties;
|
||||
}
|
||||
|
||||
void CKBitmapData::SetSaveFormat(const CKBitmapProperties& props) {
|
||||
m_SaveProperties = props;
|
||||
}
|
||||
|
||||
CK_TEXTURE_SAVEOPTIONS CKBitmapData::GetSaveOptions() const {
|
||||
return m_SaveOptions;
|
||||
}
|
||||
|
||||
void CKBitmapData::SetSaveOptions(CK_TEXTURE_SAVEOPTIONS opts) {
|
||||
m_SaveOptions = opts;
|
||||
}
|
||||
|
||||
void CKBitmapData::SetTransparent(bool Transparency) {
|
||||
if (Transparency) {
|
||||
YYCC::EnumHelper::Add(m_BitmapFlags, CK_BITMAPDATA_FLAGS::CKBITMAPDATA_TRANSPARENT);
|
||||
} else {
|
||||
YYCC::EnumHelper::Remove(m_BitmapFlags, CK_BITMAPDATA_FLAGS::CKBITMAPDATA_TRANSPARENT);
|
||||
}
|
||||
}
|
||||
|
||||
bool CKBitmapData::IsTransparent() const {
|
||||
return YYCC::EnumHelper::Has(m_BitmapFlags, CK_BITMAPDATA_FLAGS::CKBITMAPDATA_TRANSPARENT);
|
||||
}
|
||||
|
||||
void CKBitmapData::SetTransparentColor(CKDWORD col) {
|
||||
// MARK: originally, we should set CK_BITMAPDATA_FLAGS::CKBITMAPDATA_TRANSPARENT for m_BitmapFlags.
|
||||
// but in CKTexture::Read(), I don't know why Virtools use this fucking design. Virtools split the enable and color data self,
|
||||
// and always write Transparent Color no matter whether Transparent enabled.
|
||||
// so i split transparent enable function and transparent color. User should manually enable transparent if they needed.
|
||||
// Just set transparent color will not enable transparent automatically.
|
||||
m_TransColor = col;
|
||||
}
|
||||
|
||||
CKDWORD CKBitmapData::GetTransparentColor() const {
|
||||
return m_TransColor;
|
||||
}
|
||||
|
||||
void CKBitmapData::SetPickThreshold(CKDWORD threshold) {
|
||||
m_PickThreshold = threshold;
|
||||
}
|
||||
|
||||
CKDWORD CKBitmapData::GetPickThreshold() const {
|
||||
return m_PickThreshold;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
CKBitmapData::CKBitmapData(CKContext* ctx) :
|
||||
m_Context(ctx),
|
||||
m_Slots(), m_CurrentSlot(0),
|
||||
m_PickThreshold(0), m_BitmapFlags(CK_BITMAPDATA_FLAGS::CKBITMAPDATA_INVALID), m_TransColor(0),
|
||||
m_SaveProperties(ctx->GetGlobalImagesSaveFormat()), m_SaveOptions(CK_TEXTURE_SAVEOPTIONS::CKTEXTURE_USEGLOBAL) {}
|
||||
|
||||
CKBitmapData::~CKBitmapData() {}
|
||||
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../VTInternal.hpp"
|
||||
|
||||
namespace LibCmo::CK2 {
|
||||
|
||||
struct CKBitmapDataReadIdentifiers {
|
||||
CKDWORD m_SpecificFormat; /**< CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_TEXREADER(0x100000) in default. */
|
||||
CKDWORD m_RawData; /**< CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_TEXCOMPRESSED(0x20000) in default. */
|
||||
CKDWORD m_OldRawData; /**< CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_TEXBITMAPS(0x4000) in default. */
|
||||
|
||||
CKDWORD m_FileNames; /**< CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_TEXFILENAMES(0x10000) in default. */
|
||||
CKDWORD m_MovieFileName; /**< CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_TEXAVIFILENAME(0x1000) in default. */
|
||||
};
|
||||
|
||||
struct CKBitmapDataWriteIdentifiers {
|
||||
CKDWORD m_SpecificFormat; /**< CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_TEXREADER(0x100000) in default. */
|
||||
CKDWORD m_RawData; /**< CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_TEXCOMPRESSED(0x20000) in default. */
|
||||
|
||||
CKDWORD m_FileNames; /**< CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_TEXFILENAMES(0x10000) in default. */
|
||||
CKDWORD m_MovieFileName; /**< CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_TEXAVIFILENAME(0x1000) in default. */
|
||||
};
|
||||
|
||||
class CKBitmapSlot {
|
||||
public:
|
||||
CKBitmapSlot() :
|
||||
m_ImageData(), m_FileName() {}
|
||||
~CKBitmapSlot() {}
|
||||
YYCC_DEF_CLS_COPY_MOVE(CKBitmapSlot);
|
||||
|
||||
VxMath::VxImageDescEx m_ImageData;
|
||||
XContainer::XString m_FileName;
|
||||
};
|
||||
|
||||
class CKBitmapData {
|
||||
public:
|
||||
CKBitmapData(CKContext* ctx);
|
||||
~CKBitmapData();
|
||||
YYCC_DEL_CLS_COPY_MOVE(CKBitmapData);
|
||||
|
||||
#pragma region RW Funcs
|
||||
|
||||
static bool ReadSpecificFormatBitmap(CKStateChunk* chk, VxMath::VxImageDescEx* slot);
|
||||
static bool ReadRawBitmap(CKStateChunk* chk, VxMath::VxImageDescEx* slot);
|
||||
static bool ReadOldRawBitmap(CKStateChunk* chk, VxMath::VxImageDescEx* slot);
|
||||
static void WriteSpecificFormatBitmap(CKStateChunk* chk, const VxMath::VxImageDescEx* slot, const CKBitmapProperties* savefmt);
|
||||
static void WriteRawBitmap(CKStateChunk* chk, const VxMath::VxImageDescEx* slot);
|
||||
|
||||
bool ReadFromChunk(CKStateChunk* chunk, CKFileVisitor* file, const CKBitmapDataReadIdentifiers& identifiers);
|
||||
bool DumpToChunk(CKStateChunk* chunk, CKFileVisitor* file, const CKBitmapDataWriteIdentifiers& identifiers);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Slot funcs
|
||||
|
||||
void SetSlotCount(CKDWORD count);
|
||||
CKDWORD GetSlotCount() const;
|
||||
void SetCurrentSlot(CKDWORD slot);
|
||||
CKDWORD GetCurrentSlot() const;
|
||||
|
||||
/**
|
||||
* @brief Create a black image with full alpha in specified slot.
|
||||
* @param[in] Width Image width
|
||||
* @param[in] Height Image height
|
||||
* @param[in] Slot The slot placing image.
|
||||
* @return True if creating success.
|
||||
*/
|
||||
bool CreateImage(CKDWORD Width, CKDWORD Height, CKDWORD Slot);
|
||||
/**
|
||||
* @brief Load image into specified slot.
|
||||
* @param[in] filename The file name of loading image.
|
||||
* @param[in] slot The slot placing loaded image.
|
||||
* @return True if load successfully.
|
||||
*/
|
||||
bool LoadImage(CKSTRING filename, CKDWORD slot);
|
||||
/**
|
||||
* @brief Save image for specified slot.
|
||||
* @param[in] filename The file name of saving image.
|
||||
* @param[in] slot The slot will be saved.
|
||||
* @param[in] isForceThisFmt True to use this class specified format to save image. Otherwise use the format evaluated by the image file name.
|
||||
* @return True if success.
|
||||
*/
|
||||
bool SaveImage(CKSTRING filename, CKDWORD slot, bool isForceThisFmt = false);
|
||||
/**
|
||||
* @brief Get specified slot image descriptor.
|
||||
* @param[in] slot The slot to get.
|
||||
* @return The descriptor. nullptr if failed.
|
||||
*/
|
||||
VxMath::VxImageDescEx* GetImageDesc(CKDWORD slot);
|
||||
/**
|
||||
* @brief Release specified slot image.
|
||||
* @param[in] slot The slot to free.
|
||||
*/
|
||||
void ReleaseImage(CKDWORD slot);
|
||||
|
||||
/**
|
||||
* @brief Set associated file name for specified slot.
|
||||
* @param[in] slot The slot to set.
|
||||
* @param[in] filename The associated file name.
|
||||
*/
|
||||
bool SetSlotFileName(CKDWORD slot, CKSTRING filename);
|
||||
/**
|
||||
* @brief Get associated file name for specified slot.
|
||||
* @param[in] slot The slot to get.
|
||||
* @return The file name. nullptr if failed.
|
||||
*/
|
||||
CKSTRING GetSlotFileName(CKDWORD slot) const;
|
||||
|
||||
/**
|
||||
* @brief Get first valid image's width.
|
||||
* @return 0 if no valid image.
|
||||
*/
|
||||
CKDWORD GetWidth() const;
|
||||
/**
|
||||
* @brief Get first valid image's height.
|
||||
* @return 0 if no valid image
|
||||
*/
|
||||
CKDWORD GetHeight() const;
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Not important funcs
|
||||
|
||||
CK_BITMAPDATA_FLAGS GetBitmapFlags() const;
|
||||
|
||||
void SetCubeMap(bool is_cube);
|
||||
bool IsCubeMap() const;
|
||||
|
||||
const CKBitmapProperties& GetSaveFormat() const;
|
||||
void SetSaveFormat(const CKBitmapProperties& props);
|
||||
CK_TEXTURE_SAVEOPTIONS GetSaveOptions() const;
|
||||
void SetSaveOptions(CK_TEXTURE_SAVEOPTIONS opts);
|
||||
/**
|
||||
@brief Enables or disables the color key transparency.
|
||||
@param[in] Transparency TRUE activates transparency, FALSE disables it.
|
||||
@remark
|
||||
+ 0x00000000 (black) is the default transparent color.
|
||||
+ Setting on the transparency and a transparent color automatically
|
||||
updates the alpha channel so that pixel with the transparent color have
|
||||
a 0 alpha value.
|
||||
@see IsTransparent, SetTranparentColor
|
||||
*/
|
||||
void SetTransparent(bool Transparency);
|
||||
/**
|
||||
@brief Returns whether color keyed transparency is enabled.
|
||||
@return TRUE if color keying is enabled.
|
||||
@see SetTransparent
|
||||
*/
|
||||
bool IsTransparent() const;
|
||||
/**
|
||||
@brief Sets the transparent color.
|
||||
@param[in] Color A 32 bit ARGB transparent color.
|
||||
@remark
|
||||
+ 0x00000000 (black) is the default transparent color.
|
||||
+ Setting on the transparency and a transparent color automatically
|
||||
updates the alpha channel so that pixel with the transparent color have
|
||||
a 0 alpha value.
|
||||
@see GetTranparentColor, SetTransparent
|
||||
*/
|
||||
void SetTransparentColor(CKDWORD col);
|
||||
/**
|
||||
@brief Returns the transparent color.
|
||||
@return A 32 bit ARGB transparent color.
|
||||
@remark
|
||||
+ 0x00000000 (black) is the default transparent color.
|
||||
@see SetTranparentColor, SetTransparent
|
||||
*/
|
||||
CKDWORD GetTransparentColor() const;
|
||||
/**
|
||||
@brief Sets pick threshold value.
|
||||
@param[in] pt Pick threshold value to be set.
|
||||
@remark
|
||||
+ The pick threshold is used when picking object with
|
||||
transparent textures.
|
||||
+ It is the minimum value for alpha component
|
||||
below which picking is not valid.So this value is supposed to be in the range 0..255
|
||||
and the default value 0 means the picking is always valid.
|
||||
+ But if a value >0 is used and the texture use transparency (some pixels of the bitmap will have
|
||||
alpha component of 0) an object will not be picked on its transparent part.
|
||||
@see CKRenderContext::Pick
|
||||
*/
|
||||
void SetPickThreshold(CKDWORD threshold);
|
||||
/**
|
||||
@brief Gets pick threshold value.
|
||||
@return Pick threshold value
|
||||
*/
|
||||
CKDWORD GetPickThreshold() const;
|
||||
|
||||
#pragma endregion
|
||||
|
||||
protected:
|
||||
CKContext* m_Context;
|
||||
XContainer::XArray<CKBitmapSlot> m_Slots;
|
||||
CKDWORD m_CurrentSlot;
|
||||
CKDWORD m_PickThreshold;
|
||||
CK_BITMAPDATA_FLAGS m_BitmapFlags;
|
||||
CKDWORD m_TransColor;
|
||||
|
||||
CKBitmapProperties m_SaveProperties;
|
||||
CK_TEXTURE_SAVEOPTIONS m_SaveOptions;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,351 +0,0 @@
|
||||
#include "CKContext.hpp"
|
||||
#include "ObjImpls/CKObject.hpp"
|
||||
#include "MgrImpls/CKBaseManager.hpp"
|
||||
#include "MgrImpls/CKObjectManager.hpp"
|
||||
#include "MgrImpls/CKPathManager.hpp"
|
||||
#include <cstdarg>
|
||||
|
||||
namespace LibCmo::CK2 {
|
||||
|
||||
#pragma region Ctor Dtor
|
||||
|
||||
CKContext::CKContext() :
|
||||
// setup manager
|
||||
m_ManagerList(),
|
||||
m_ObjectManager(nullptr), m_PathManager(nullptr),
|
||||
// setup object cache
|
||||
m_ObjectCache(), m_ObjectPointerCache(),
|
||||
// setup file save/load options
|
||||
m_CompressionLevel(5),
|
||||
m_FileWriteMode(CK_FILE_WRITEMODE::CKFILE_UNCOMPRESSED),
|
||||
m_GlobalImagesSaveOptions(CK_TEXTURE_SAVEOPTIONS::CKTEXTURE_RAWDATA),
|
||||
m_GlobalSoundsSaveOptions(CK_SOUND_SAVEOPTIONS::CKSOUND_EXTERNAL),
|
||||
m_GlobalImagesSaveFormat(),
|
||||
// misc init
|
||||
m_NameEncoding(),
|
||||
m_OutputCallback(nullptr) {
|
||||
|
||||
// setup save format
|
||||
m_GlobalImagesSaveFormat.m_Ext.SetExt(u8"bmp");
|
||||
|
||||
// setup managers
|
||||
m_ObjectManager = new MgrImpls::CKObjectManager(this);
|
||||
m_ManagerList.emplace_back(m_ObjectManager);
|
||||
m_PathManager = new MgrImpls::CKPathManager(this);
|
||||
m_ManagerList.emplace_back(m_PathManager);
|
||||
}
|
||||
|
||||
CKContext::~CKContext() {
|
||||
// reset context
|
||||
ClearAll();
|
||||
// free all manager
|
||||
for (auto& mgrptr : m_ManagerList) {
|
||||
delete mgrptr;
|
||||
}
|
||||
// free encoding
|
||||
this->ClearEncoding();
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Engine runtime
|
||||
|
||||
void CKContext::ClearAll() {
|
||||
// pre clear all
|
||||
ExecuteManagersOnPreClearAll();
|
||||
|
||||
// order object manager clear all objects
|
||||
m_ObjectManager->DestroyAllObjects();
|
||||
|
||||
// post clear all
|
||||
ExecuteManagersOnPostClearAll();
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Objects Management / Access
|
||||
|
||||
ObjImpls::CKObject* CKContext::CreateObject(CK_CLASSID cls, CKSTRING name, CK_OBJECTCREATION_OPTIONS options, CK_CREATIONMODE* res) {
|
||||
return m_ObjectManager->CreateObject(cls, name, options, res);
|
||||
}
|
||||
|
||||
ObjImpls::CKObject* CKContext::GetObject(CK_ID ObjID) {
|
||||
return m_ObjectManager->GetObject(ObjID);
|
||||
}
|
||||
|
||||
CKDWORD CKContext::GetObjectCount() {
|
||||
return m_ObjectManager->GetObjectCount();
|
||||
}
|
||||
|
||||
void CKContext::DestroyObject(ObjImpls::CKObject* obj) {
|
||||
CK_ID id = obj->GetID();
|
||||
return m_ObjectManager->DestroyObjects(&id, 1);
|
||||
}
|
||||
|
||||
void CKContext::DestroyObject(CK_ID id) {
|
||||
return m_ObjectManager->DestroyObjects(&id, 1);
|
||||
}
|
||||
|
||||
void CKContext::DestroyObjects(CK_ID* obj_ids, CKDWORD Count) {
|
||||
return m_ObjectManager->DestroyObjects(obj_ids, Count);
|
||||
}
|
||||
|
||||
ObjImpls::CKObject* GeneralPrevFinder(XContainer::XObjectPointerArray& objptrs, ObjImpls::CKObject* previous) {
|
||||
if (objptrs.empty()) return nullptr;
|
||||
|
||||
if (previous == nullptr) {
|
||||
return objptrs.front();
|
||||
} else {
|
||||
auto finder = std::find(objptrs.begin(), objptrs.end(), previous);
|
||||
if (finder == objptrs.end()) return nullptr;
|
||||
++finder;
|
||||
if (finder == objptrs.end()) return nullptr;
|
||||
return *finder;
|
||||
}
|
||||
}
|
||||
ObjImpls::CKObject* CKContext::GetObjectByName(CKSTRING name, ObjImpls::CKObject* previous) {
|
||||
if (name == nullptr) return nullptr;
|
||||
if (previous == nullptr) {
|
||||
m_ObjectPointerCache = m_ObjectManager->GetObjectByNameAndClass(name, CK_CLASSID::CKCID_OBJECT, true);
|
||||
}
|
||||
return GeneralPrevFinder(m_ObjectPointerCache, previous);
|
||||
}
|
||||
|
||||
ObjImpls::CKObject* CKContext::GetObjectByNameAndClass(CKSTRING name, CK_CLASSID cid, ObjImpls::CKObject* previous) {
|
||||
if (name == nullptr) return nullptr;
|
||||
if (previous == nullptr) {
|
||||
m_ObjectPointerCache = m_ObjectManager->GetObjectByNameAndClass(name, cid, false);
|
||||
}
|
||||
return GeneralPrevFinder(m_ObjectPointerCache, previous);
|
||||
}
|
||||
|
||||
ObjImpls::CKObject* CKContext::GetObjectByNameAndParentClass(CKSTRING name, CK_CLASSID pcid, ObjImpls::CKObject* previous) {
|
||||
if (name == nullptr) return nullptr;
|
||||
if (previous == nullptr) {
|
||||
m_ObjectPointerCache = m_ObjectManager->GetObjectByNameAndClass(name, pcid, true);
|
||||
}
|
||||
return GeneralPrevFinder(m_ObjectPointerCache, previous);
|
||||
}
|
||||
|
||||
const XContainer::XObjectPointerArray CKContext::GetObjectListByType(CK_CLASSID cid, bool derived) {
|
||||
return m_ObjectManager->GetObjectByNameAndClass(nullptr, cid, derived);
|
||||
}
|
||||
|
||||
CKDWORD CKContext::GetObjectsCountByClassID(CK_CLASSID cid) {
|
||||
auto result = m_ObjectManager->GetObjectByNameAndClass(nullptr, cid, false);
|
||||
|
||||
m_ObjectCache.clear();
|
||||
for (auto& obj : result) {
|
||||
m_ObjectCache.emplace_back(obj->GetID());
|
||||
}
|
||||
|
||||
return static_cast<CKDWORD>(m_ObjectCache.size());
|
||||
}
|
||||
|
||||
CK_ID* CKContext::GetObjectsListByClassID(CK_CLASSID cid) {
|
||||
return m_ObjectCache.data();
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Common Managers
|
||||
|
||||
MgrImpls::CKObjectManager* CKContext::GetObjectManager() {
|
||||
return m_ObjectManager;
|
||||
}
|
||||
|
||||
MgrImpls::CKPathManager* CKContext::GetPathManager() {
|
||||
return m_PathManager;
|
||||
}
|
||||
|
||||
CKDWORD CKContext::GetManagerCount() {
|
||||
return static_cast<CKDWORD>(m_ManagerList.size());
|
||||
}
|
||||
|
||||
MgrImpls::CKBaseManager* CKContext::GetManager(CKDWORD index) {
|
||||
if (index >= m_ManagerList.size()) return nullptr;
|
||||
return m_ManagerList[index];
|
||||
}
|
||||
|
||||
void CKContext::ExecuteManagersOnPreClearAll() {
|
||||
ExecuteManagersGeneral([](MgrImpls::CKBaseManager* mgr) -> void {
|
||||
mgr->PreClearAll();
|
||||
});
|
||||
}
|
||||
|
||||
void CKContext::ExecuteManagersOnPostClearAll() {
|
||||
ExecuteManagersGeneral([](MgrImpls::CKBaseManager* mgr) -> void {
|
||||
mgr->PostClearAll();
|
||||
});
|
||||
}
|
||||
|
||||
void CKContext::ExecuteManagersOnSequenceToBeDeleted(const CK_ID* objids, CKDWORD count) {
|
||||
ExecuteManagersGeneral([objids, count](MgrImpls::CKBaseManager* mgr) -> void {
|
||||
mgr->SequenceToBeDeleted(objids, count);
|
||||
});
|
||||
}
|
||||
|
||||
void CKContext::ExecuteManagersOnSequenceDeleted(const CK_ID* objids, CKDWORD count) {
|
||||
ExecuteManagersGeneral([objids, count](MgrImpls::CKBaseManager* mgr) -> void {
|
||||
mgr->SequenceDeleted(objids, count);
|
||||
});
|
||||
}
|
||||
|
||||
void CKContext::ExecuteManagersGeneral(std::function<void(MgrImpls::CKBaseManager*)> fct) {
|
||||
for (auto& mgrptr : m_ManagerList) {
|
||||
fct(mgrptr);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region File Save/Load Options
|
||||
|
||||
void CKContext::SetCompressionLevel(CKINT level) {
|
||||
if (level > 0 && level < 10) {
|
||||
m_CompressionLevel = level;
|
||||
}
|
||||
}
|
||||
|
||||
CKINT CKContext::GetCompressionLevel() {
|
||||
return m_CompressionLevel;
|
||||
}
|
||||
|
||||
void CKContext::SetFileWriteMode(CK_FILE_WRITEMODE mode) {
|
||||
m_FileWriteMode = mode;
|
||||
}
|
||||
|
||||
CK_FILE_WRITEMODE CKContext::GetFileWriteMode() {
|
||||
return m_FileWriteMode;
|
||||
}
|
||||
|
||||
CK_TEXTURE_SAVEOPTIONS CKContext::GetGlobalImagesSaveOptions() {
|
||||
return m_GlobalImagesSaveOptions;
|
||||
}
|
||||
|
||||
void CKContext::SetGlobalImagesSaveOptions(CK_TEXTURE_SAVEOPTIONS Options) {
|
||||
if (Options != CK_TEXTURE_SAVEOPTIONS::CKTEXTURE_USEGLOBAL) {
|
||||
m_GlobalImagesSaveOptions = Options;
|
||||
}
|
||||
}
|
||||
|
||||
const CKBitmapProperties& CKContext::GetGlobalImagesSaveFormat() {
|
||||
return m_GlobalImagesSaveFormat;
|
||||
}
|
||||
|
||||
void CKContext::SetGlobalImagesSaveFormat(const CKBitmapProperties& Format) {
|
||||
m_GlobalImagesSaveFormat = Format;
|
||||
}
|
||||
|
||||
CK_SOUND_SAVEOPTIONS CKContext::GetGlobalSoundsSaveOptions() {
|
||||
return m_GlobalSoundsSaveOptions;
|
||||
}
|
||||
|
||||
void CKContext::SetGlobalSoundsSaveOptions(CK_SOUND_SAVEOPTIONS Options) {
|
||||
if (Options != CK_SOUND_SAVEOPTIONS::CKSOUND_USEGLOBAL) {
|
||||
m_GlobalSoundsSaveOptions = Options;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Output utilities
|
||||
|
||||
void CKContext::OutputToConsole(CKSTRING str) {
|
||||
if (m_OutputCallback == nullptr) return;
|
||||
if (str == nullptr) return;
|
||||
m_OutputCallback(str);
|
||||
}
|
||||
|
||||
void CKContext::OutputToConsoleEx(CKSTRING fmt, ...) {
|
||||
if (m_OutputCallback == nullptr) return;
|
||||
if (fmt == nullptr) return;
|
||||
|
||||
va_list argptr;
|
||||
va_start(argptr, fmt);
|
||||
XContainer::XString result(YYCC::StringHelper::VPrintf(fmt, argptr));
|
||||
va_end(argptr);
|
||||
|
||||
// use c_str(), not XContainer::NSXString::ToCKSTRING because we want make sure this paramter is not nullptr.
|
||||
// we always output a valid C style string, even if no chars need to write.
|
||||
m_OutputCallback(result.c_str());
|
||||
}
|
||||
|
||||
void CKContext::SetOutputCallback(OutputCallback cb) {
|
||||
m_OutputCallback = cb;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Encoding utilities
|
||||
|
||||
bool CKContext::GetUTF8String(const std::string& native_name, XContainer::XString& u8_name) {
|
||||
bool conv_success = false, has_valid_token = false;
|
||||
for (const auto& token : this->m_NameEncoding) {
|
||||
if (token == EncodingHelper::INVALID_ENCODING_TOKEN) continue;
|
||||
has_valid_token = true;
|
||||
conv_success = EncodingHelper::ToUTF8(native_name, u8_name, token);
|
||||
if (conv_success) break;
|
||||
}
|
||||
// fallback if failed.
|
||||
if (!conv_success) {
|
||||
if (!has_valid_token) {
|
||||
throw RuntimeException("Try to get UTF8 string from ordinary string in CKContext but giving empty encoding candidate.");
|
||||
} else {
|
||||
u8_name.clear();
|
||||
this->OutputToConsole(u8"Error when converting to UTF8 string from ordinary string. The string will leave to blank.");
|
||||
}
|
||||
}
|
||||
// return value
|
||||
return conv_success;
|
||||
}
|
||||
|
||||
bool CKContext::GetOrdinaryString(const XContainer::XString& u8_name, std::string& native_name) {
|
||||
bool conv_success = false, has_valid_token = false;
|
||||
for (const auto& token : this->m_NameEncoding) {
|
||||
if (token == EncodingHelper::INVALID_ENCODING_TOKEN) continue;
|
||||
has_valid_token = true;
|
||||
conv_success = EncodingHelper::ToOrdinary(u8_name, native_name, token);
|
||||
if (conv_success) break;
|
||||
}
|
||||
// fallback if failed.
|
||||
if (!conv_success) {
|
||||
if (!has_valid_token) {
|
||||
throw RuntimeException("Try to get ordinary string from UTF8 string in CKContext but giving empty encoding candidate.");
|
||||
} else {
|
||||
native_name.clear();
|
||||
this->OutputToConsole(u8"Error when converting to ordinary string from UTF8 string. The string will leave to blank.");
|
||||
}
|
||||
}
|
||||
// return value
|
||||
return conv_success;
|
||||
}
|
||||
|
||||
void CKContext::SetEncoding(const XContainer::XArray<XContainer::XString>& encoding_seq) {
|
||||
// free all current series
|
||||
this->ClearEncoding();
|
||||
// add new encoding
|
||||
for (const auto& encoding_str : encoding_seq) {
|
||||
this->m_NameEncoding.emplace_back(LibCmo::EncodingHelper::CreateEncodingToken(encoding_str));
|
||||
}
|
||||
}
|
||||
|
||||
void CKContext::ClearEncoding() {
|
||||
for (const auto& token : this->m_NameEncoding) {
|
||||
if (token == EncodingHelper::INVALID_ENCODING_TOKEN) continue;
|
||||
LibCmo::EncodingHelper::DestroyEncodingToken(token);
|
||||
}
|
||||
this->m_NameEncoding.clear();
|
||||
}
|
||||
|
||||
bool CKContext::IsValidEncoding() {
|
||||
for (const auto& token : this->m_NameEncoding) {
|
||||
if (token != EncodingHelper::INVALID_ENCODING_TOKEN) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../VTInternal.hpp"
|
||||
#include <map>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
|
||||
namespace LibCmo::CK2 {
|
||||
|
||||
/**
|
||||
@brief Main Interface Object
|
||||
@details
|
||||
+ The CKContext object is the heart of all Virtools based applications, It is the first object that should be created in order to
|
||||
use Virtools SDK. A CKContext can be simply created by calling its constructor.
|
||||
+ The CKContext object act as the central interface to create/destroy objects,to access managers, to load/save files.
|
||||
+ Several CKContext can be created inside a same process (in multiple threads for example) but objects created
|
||||
by a specific CKContext must not be used in other contextes.
|
||||
@see CKContext::CreateObject, CKContext::GetObject, CKContext::DestroyObject
|
||||
*/
|
||||
class CKContext {
|
||||
public:
|
||||
CKContext();
|
||||
~CKContext();
|
||||
YYCC_DEL_CLS_COPY_MOVE(CKContext);
|
||||
|
||||
// ========== Engine runtime ==========
|
||||
public:
|
||||
/**
|
||||
* @brief Simply clear all CKContext to restore its status.
|
||||
*/
|
||||
void ClearAll();
|
||||
|
||||
// ========== Objects Management / Access ==========
|
||||
// These functions is a simply redirect to CKObjectManager
|
||||
|
||||
ObjImpls::CKObject* CreateObject(CK_CLASSID cls, CKSTRING name,
|
||||
CK_OBJECTCREATION_OPTIONS options = CK_OBJECTCREATION_OPTIONS::CK_OBJECTCREATION_NONAMECHECK,
|
||||
CK_CREATIONMODE* res = nullptr);
|
||||
|
||||
ObjImpls::CKObject* GetObject(CK_ID ObjID);
|
||||
CKDWORD GetObjectCount();
|
||||
void DestroyObject(ObjImpls::CKObject *obj);
|
||||
void DestroyObject(CK_ID id);
|
||||
void DestroyObjects(CK_ID* obj_ids, CKDWORD Count);
|
||||
|
||||
ObjImpls::CKObject* GetObjectByName(CKSTRING name, ObjImpls::CKObject *previous = nullptr);
|
||||
ObjImpls::CKObject* GetObjectByNameAndClass(CKSTRING name, CK_CLASSID cid, ObjImpls::CKObject *previous = nullptr);
|
||||
ObjImpls::CKObject* GetObjectByNameAndParentClass(CKSTRING name, CK_CLASSID pcid, ObjImpls::CKObject* previous = nullptr);
|
||||
const XContainer::XObjectPointerArray GetObjectListByType(CK_CLASSID cid, bool derived);
|
||||
CKDWORD GetObjectsCountByClassID(CK_CLASSID cid);
|
||||
CK_ID* GetObjectsListByClassID(CK_CLASSID cid);
|
||||
|
||||
protected:
|
||||
XContainer::XObjectPointerArray m_ObjectPointerCache;
|
||||
XContainer::XObjectArray m_ObjectCache;
|
||||
|
||||
// ========== Common Managers ==========
|
||||
public:
|
||||
MgrImpls::CKObjectManager* GetObjectManager();
|
||||
MgrImpls::CKPathManager* GetPathManager();
|
||||
|
||||
CKDWORD GetManagerCount();
|
||||
MgrImpls::CKBaseManager* GetManager(CKDWORD index);
|
||||
|
||||
void ExecuteManagersOnPreClearAll();
|
||||
void ExecuteManagersOnPostClearAll();
|
||||
void ExecuteManagersOnSequenceToBeDeleted(const CK_ID* objids, CKDWORD count);
|
||||
void ExecuteManagersOnSequenceDeleted(const CK_ID* objids, CKDWORD count);
|
||||
|
||||
protected:
|
||||
void ExecuteManagersGeneral(std::function<void(MgrImpls::CKBaseManager*)> fct);
|
||||
XContainer::XArray<MgrImpls::CKBaseManager*> m_ManagerList;
|
||||
|
||||
MgrImpls::CKObjectManager* m_ObjectManager;
|
||||
MgrImpls::CKPathManager* m_PathManager;
|
||||
|
||||
// ========== File Save/Load Options ==========
|
||||
public:
|
||||
void SetCompressionLevel(CKINT level);
|
||||
CKINT GetCompressionLevel();
|
||||
|
||||
void SetFileWriteMode(CK_FILE_WRITEMODE mode);
|
||||
CK_FILE_WRITEMODE GetFileWriteMode();
|
||||
|
||||
CK_TEXTURE_SAVEOPTIONS GetGlobalImagesSaveOptions();
|
||||
void SetGlobalImagesSaveOptions(CK_TEXTURE_SAVEOPTIONS Options);
|
||||
|
||||
const CKBitmapProperties& GetGlobalImagesSaveFormat();
|
||||
void SetGlobalImagesSaveFormat(const CKBitmapProperties& Format);
|
||||
|
||||
CK_SOUND_SAVEOPTIONS GetGlobalSoundsSaveOptions();
|
||||
void SetGlobalSoundsSaveOptions(CK_SOUND_SAVEOPTIONS Options);
|
||||
|
||||
protected:
|
||||
CKINT m_CompressionLevel;
|
||||
CK_FILE_WRITEMODE m_FileWriteMode;
|
||||
CK_TEXTURE_SAVEOPTIONS m_GlobalImagesSaveOptions;
|
||||
CK_SOUND_SAVEOPTIONS m_GlobalSoundsSaveOptions;
|
||||
CKBitmapProperties m_GlobalImagesSaveFormat;
|
||||
|
||||
// ========== Encoding utilities ==========
|
||||
public:
|
||||
/**
|
||||
* @brief Convert given ordinary string to UTF8 string.
|
||||
* @param[in] native_name The input ordinary string.
|
||||
* @param[out] u8_name The output UTF8 string.
|
||||
* @return True if convertion is success, otherwise false.
|
||||
* @exception RuntimeException Raised when perform this operation with a blank encoding sequence.
|
||||
* @remarks
|
||||
* The encoding of ordinary is specified by encoding sequence.
|
||||
* If we fail to do convertion, the result will leave to blank and output a message to CKContext.
|
||||
* However, if you use this function with blank encoding sequence, it will raise exception.
|
||||
* So becore using this function, please make sure that you have checked by calling IsValidEncoding().
|
||||
*/
|
||||
bool GetUTF8String(const std::string& native_name, XContainer::XString& u8_name);
|
||||
/**
|
||||
* @brief Convert given UTF8 string to ordinary string.
|
||||
* @param[in] u8_name The input UTF8 string.
|
||||
* @param[out] native_name The output ordinary string.
|
||||
* @return True if convertion is success, otherwise false.
|
||||
* @exception RuntimeException Raised when perform this operation with a blank encoding sequence.
|
||||
* @remarks
|
||||
* The encoding of ordinary is specified by encoding sequence.
|
||||
* If we fail to do convertion, the result will leave to blank and output a message to CKContext.
|
||||
* However, if you use this function with blank encoding sequence, it will raise exception.
|
||||
* So becore using this function, please make sure that you have checked by calling IsValidEncoding().
|
||||
*/
|
||||
bool GetOrdinaryString(const XContainer::XString& u8_name, std::string& native_name);
|
||||
/**
|
||||
* @brief Set the encoding sequence.
|
||||
* @param[in] encoding_series The encoding name in this sequence.
|
||||
* @remarks
|
||||
* \li The order in encoding sequence is important. The encoding name with lower index will be used for convertion first.
|
||||
* \li Encoding sequence will be used for performing GetUTF8String() and GetOrdinaryString().
|
||||
* We will try using it to do convertion from top to bottom (if one failed we will continue trying to use next one to do convertion).
|
||||
*/
|
||||
void SetEncoding(const XContainer::XArray<XContainer::XString>& encoding_series);
|
||||
/**
|
||||
* @brief Clear specified encoding sequence.
|
||||
*/
|
||||
void ClearEncoding();
|
||||
/**
|
||||
* @brief Check whether current encoding sequence at least has one valid encoding for convertion.
|
||||
* @return True if it is, otherwise false.
|
||||
*/
|
||||
bool IsValidEncoding();
|
||||
|
||||
protected:
|
||||
XContainer::XArray<EncodingHelper::EncodingToken> m_NameEncoding;
|
||||
|
||||
// ========== Print utilities ==========
|
||||
public:
|
||||
/**
|
||||
* @brief The callback prototype.
|
||||
* @details It accept a CKSTRING representing the string need to be printed.
|
||||
* The passed CKSTRING is guaranteen that it can not be nullptr.
|
||||
*/
|
||||
using OutputCallback = std::function<void(CKSTRING)>;
|
||||
/**
|
||||
* @brief Output plain message.
|
||||
* @param[in] str Plain message. nullptr is allowed but not suggested.
|
||||
*/
|
||||
void OutputToConsole(CKSTRING str);
|
||||
/**
|
||||
* @brief Output message with given format.
|
||||
* @param[in] fmt The format string. nullptr is allowed but not suggested.
|
||||
* @param[in] ... The arguments of format string.
|
||||
*/
|
||||
void OutputToConsoleEx(CKSTRING fmt, ...);
|
||||
/**
|
||||
* @brief Set the callback for message printing.
|
||||
* @param[in] cb The function pointer to callback. nullptr to remove callback.
|
||||
*/
|
||||
void SetOutputCallback(OutputCallback cb);
|
||||
|
||||
protected:
|
||||
OutputCallback m_OutputCallback;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "CKTypes.hpp"
|
||||
#include "CKGlobals.hpp"
|
||||
|
||||
namespace LibCmo::CK2 {
|
||||
|
||||
#pragma region Preregistred Manager GUIDs
|
||||
|
||||
// Virtools Managers GUID second DWORD must be 0
|
||||
|
||||
constexpr const CKDWORD OBJECT_MANAGER_GUID1 = 0x7cbb3b91;
|
||||
constexpr const CKDWORD ATTRIBUTE_MANAGER_GUID1 = 0x3d242466;
|
||||
constexpr const CKDWORD MESSAGE_MANAGER_GUID1 = 0x466a0fac;
|
||||
constexpr const CKDWORD FLOOR_MANAGER_GUID1 = 0x420936f9;
|
||||
constexpr const CKDWORD COLLISION_MANAGER_GUID1 = 0x38244712;
|
||||
constexpr const CKDWORD GRID_MANAGER_GUID1 = 0x7f004791;
|
||||
constexpr const CKDWORD TIME_MANAGER_GUID1 = 0x89ce7b32;
|
||||
constexpr const CKDWORD BEHAVIOR_MANAGER_GUID1 = 0x58d621ae;
|
||||
constexpr const CKDWORD INPUT_MANAGER_GUID1 = 0xf787c904;
|
||||
constexpr const CKDWORD SOUND_MANAGER_GUID1 = 0xdce135f6;
|
||||
constexpr const CKDWORD MIDI_MANAGER_GUID1 = 0x594154a6;
|
||||
constexpr const CKDWORD INTERFACE_MANAGER_GUID1 = 0x9a4b8e3d;
|
||||
constexpr const CKDWORD RENDER_MANAGER_GUID1 = 0xa213c8d5;
|
||||
constexpr const CKDWORD PARAMETER_MANAGER_GUID1 = 0x9ce57ab6;
|
||||
constexpr const CKDWORD PATH_MANAGER_GUID1 = 0x15fd54b9;
|
||||
constexpr const CKDWORD VARIABLE_MANAGER_GUID1 = 0x98cc3cc9;
|
||||
|
||||
constexpr const CKGUID OBJECT_MANAGER_GUID { OBJECT_MANAGER_GUID1, 0 };
|
||||
constexpr const CKGUID ATTRIBUTE_MANAGER_GUID { ATTRIBUTE_MANAGER_GUID1, 0 };
|
||||
constexpr const CKGUID MESSAGE_MANAGER_GUID { MESSAGE_MANAGER_GUID1, 0 };
|
||||
constexpr const CKGUID TIME_MANAGER_GUID { TIME_MANAGER_GUID1, 0 };
|
||||
constexpr const CKGUID SOUND_MANAGER_GUID { SOUND_MANAGER_GUID1, 0 };
|
||||
constexpr const CKGUID MIDI_MANAGER_GUID { MIDI_MANAGER_GUID1, 0 };
|
||||
constexpr const CKGUID INPUT_MANAGER_GUID { INPUT_MANAGER_GUID1, 0 };
|
||||
constexpr const CKGUID BEHAVIOR_MANAGER_GUID { BEHAVIOR_MANAGER_GUID1, 0 };
|
||||
constexpr const CKGUID FLOOR_MANAGER_GUID { FLOOR_MANAGER_GUID1, 0 };
|
||||
constexpr const CKGUID COLLISION_MANAGER_GUID { COLLISION_MANAGER_GUID1, 0 };
|
||||
constexpr const CKGUID GRID_MANAGER_GUID { GRID_MANAGER_GUID1, 0 };
|
||||
constexpr const CKGUID INTERFACE_MANAGER_GUID { INTERFACE_MANAGER_GUID1, 0 };
|
||||
constexpr const CKGUID RENDER_MANAGER_GUID { RENDER_MANAGER_GUID1, 0 };
|
||||
constexpr const CKGUID PARAMETER_MANAGER_GUID { PARAMETER_MANAGER_GUID1, 0 };
|
||||
constexpr const CKGUID PATH_MANAGER_GUID { PATH_MANAGER_GUID1, 0 };
|
||||
constexpr const CKGUID VARIABLE_MANAGER_GUID { VARIABLE_MANAGER_GUID1, 0 };
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Misc Constant Variables
|
||||
|
||||
/**
|
||||
* @brief The identifier of Virtools file.
|
||||
*/
|
||||
constexpr const CKCHAR CKNEMOFI[] = u8"Nemo Fi";
|
||||
/**
|
||||
* @brief Current Version of CK Engine (Day/Month/Year)
|
||||
*/
|
||||
constexpr const CKDWORD CKVERSION = 0x13022002u;
|
||||
/**
|
||||
* @brief Current Version of Dev
|
||||
*/
|
||||
constexpr const CKDWORD DEVBUILD = 0x02010001u;
|
||||
constexpr const CKDWORD DEVVERSION = 0u;
|
||||
constexpr const CKGUID VIRTOOLS_GUID = CKGUID(0x56495254u, 0x4f4f4c53u);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Common Used Struct
|
||||
|
||||
/**
|
||||
* @brief Storage class for filename extensions
|
||||
*/
|
||||
class CKFileExtension {
|
||||
public:
|
||||
CKFileExtension() : m_Data() {
|
||||
std::memset(m_Data, 0, c_DataLen);
|
||||
}
|
||||
CKFileExtension(CKSTRING s) : CKFileExtension() {
|
||||
SetExt(s);
|
||||
}
|
||||
CKFileExtension(const CKFileExtension& rhs) : CKFileExtension() {
|
||||
std::memcpy(m_Data, rhs.m_Data, c_DataLen);
|
||||
}
|
||||
CKFileExtension(CKFileExtension&& rhs) : CKFileExtension() {
|
||||
std::memmove(m_Data, rhs.m_Data, c_DataLen);
|
||||
std::memset(rhs.m_Data, 0, c_DataLen);
|
||||
}
|
||||
CKFileExtension& operator=(const CKFileExtension& rhs) {
|
||||
std::memcpy(m_Data, rhs.m_Data, c_DataLen);
|
||||
return *this;
|
||||
}
|
||||
CKFileExtension& operator=(CKFileExtension&& rhs) {
|
||||
std::memmove(m_Data, rhs.m_Data, c_DataLen);
|
||||
std::memset(rhs.m_Data, 0, c_DataLen);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void SetExt(CKSTRING s) {
|
||||
if (s == nullptr) {
|
||||
m_Data[0] = u8'\0';
|
||||
} else {
|
||||
if (s[0] == u8'.') ++s; // skip dot
|
||||
CKDWORD len = CKStrLen(s);
|
||||
if (len > (c_DataLen - 1)) len = c_DataLen - 1;
|
||||
std::memcpy(m_Data, s, len);
|
||||
}
|
||||
}
|
||||
|
||||
CKSTRING GetExt() const {
|
||||
return m_Data;
|
||||
}
|
||||
|
||||
CKDWORD GetSize() const {
|
||||
return c_DataLen;
|
||||
}
|
||||
|
||||
bool operator==(const CKFileExtension& rhs) const {
|
||||
return CKStrEqualI(m_Data, rhs.m_Data);
|
||||
}
|
||||
|
||||
protected:
|
||||
static constexpr size_t c_DataLen = 4u;
|
||||
CKCHAR m_Data[c_DataLen];
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Bitmap readers image description
|
||||
* @details
|
||||
* The struct describe the bitmap handler's infomation, including its GUID and supported file extension.
|
||||
* This struct also will store some parameters related to bitmap handler, such as jpeg compress level and etc.
|
||||
* But currently there are no such parameters.
|
||||
*/
|
||||
class CKBitmapProperties {
|
||||
public:
|
||||
CKBitmapProperties() :
|
||||
m_ReaderGuid(), m_Ext() {}
|
||||
CKBitmapProperties(const CKGUID& guid, CKSTRING ext) :
|
||||
m_ReaderGuid(guid), m_Ext(ext) {}
|
||||
YYCC_DEF_CLS_COPY_MOVE(CKBitmapProperties);
|
||||
|
||||
CKGUID m_ReaderGuid; /**< CKGUID that uniquely identifies the reader that created this properties structure */
|
||||
CKFileExtension m_Ext; /**< File Extension of the image being described by this structure */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light Structure passed to CKRasterizerContext::SetLight()
|
||||
* @details
|
||||
* This struct is a part of CKRasterizer.
|
||||
* I put it in there just for the convenience of CKLight.
|
||||
*/
|
||||
struct CKLightData {
|
||||
VxMath::VXLIGHT_TYPE m_Type; /**< Point, Spot, Directionnal */
|
||||
VxMath::VxColor m_Diffuse; /**< Diffuse Color */
|
||||
VxMath::VxColor m_Specular; /**< Specular Color (Unused...) */
|
||||
VxMath::VxColor m_Ambient; /**< Ambient Color (Unused...) */
|
||||
VxMath::VxVector3 m_Position; /**< World Position */
|
||||
VxMath::VxVector3 m_Direction; /**< Direction */
|
||||
CKFLOAT m_Range; /**< Range */
|
||||
CKFLOAT m_Falloff;
|
||||
CKFLOAT m_Attenuation0;
|
||||
CKFLOAT m_Attenuation1;
|
||||
CKFLOAT m_Attenuation2;
|
||||
CKFLOAT m_InnerSpotCone; /**< Only for spot lights */
|
||||
CKFLOAT m_OuterSpotCone; /**< Only for spot lights */
|
||||
};
|
||||
|
||||
#pragma endregion
|
||||
|
||||
|
||||
}
|
||||
@@ -1,264 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../VTUtils.hpp"
|
||||
#include "CKTypes.hpp"
|
||||
#include <cinttypes>
|
||||
#include <cstdint>
|
||||
|
||||
namespace LibCmo::CK2 {
|
||||
|
||||
/**
|
||||
Specify the way files are saved to disk (compression)
|
||||
@remark
|
||||
+ File write mode controls the format of a Virtools file when saved. More specifically it
|
||||
controls whether compression is enabled and also if the Virtools Dev Interface specific data
|
||||
should be stored in the file (if CKFILE_FORVIEWER flag is set , no interface data is saved)
|
||||
|
||||
@see CKContext::SetFileWriteMode, CKContext::GetFileWriteMode, CKContext::SetCompressionLevel,
|
||||
CKContext::SetGlobalImagesSaveOptions, CKContext::SetGlobalSoundsSaveOptions
|
||||
*/
|
||||
enum class CK_FILE_WRITEMODE : CKDWORD {
|
||||
CKFILE_UNCOMPRESSED = 0, /**< Save data uncompressed */
|
||||
CKFILE_CHUNKCOMPRESSED_OLD = 1, /**< Obsolete */
|
||||
CKFILE_EXTERNALTEXTURES_OLD = 2, /**< Obsolete : use CKContext::SetGlobalImagesSaveOptions instead. */
|
||||
CKFILE_FORVIEWER = 4, /**< Don't save Interface Data within the file, the level won't be editable anymore in the interface */
|
||||
CKFILE_WHOLECOMPRESSED = 8, /**< Compress the whole file */
|
||||
};
|
||||
/**
|
||||
Load Options.
|
||||
@remark
|
||||
+ This options apply when loading a Virtools file
|
||||
or a importing a 3D Model file.
|
||||
+ They defines whether object geometry,only animations
|
||||
or only behaviors should be loaded.
|
||||
+ One can specify (using the CK_LOAD_AS_DYNAMIC_OBJECT) if
|
||||
created CKObjects should be created as dynamic (See also Dynamic Objects)
|
||||
See also : CKContext::Load, CKContext::CKSave
|
||||
*/
|
||||
enum class CK_LOAD_FLAGS : CKDWORD {
|
||||
CK_LOAD_ANIMATION = 1 << 0, /**< Load animations */
|
||||
CK_LOAD_GEOMETRY = 1 << 1, /**< Load geometry. */
|
||||
CK_LOAD_DEFAULT = CK_LOAD_GEOMETRY | CK_LOAD_ANIMATION, /**< Load animations & geometry */
|
||||
CK_LOAD_ASCHARACTER = 1 << 2, /**< Load all the objects and create a character that contains them all . */
|
||||
CK_LOAD_DODIALOG = 1 << 3, /**< Check object name unicity and warns the user with a dialog box when duplicate names are found. */
|
||||
CK_LOAD_AS_DYNAMIC_OBJECT = 1 << 4, /**< Objects loaded from this file may be deleted at run-time or are temporary */
|
||||
CK_LOAD_AUTOMATICMODE = 1 << 5, /**< Check object name unicity and automatically rename or replace according to the options specified in CKContext::SetAutomaticLoadMode */
|
||||
CK_LOAD_CHECKDUPLICATES = 1 << 6, /**< Check object name unicity (The list of duplicates is stored in the CKFile class after a OpenFile call */
|
||||
CK_LOAD_CHECKDEPENDENCIES = 1 << 7, /**< Check if every plugins needed are availables */
|
||||
CK_LOAD_ONLYBEHAVIORS = 1 << 8, /**< */
|
||||
};
|
||||
/**
|
||||
Options that will be used to create this object...
|
||||
*/
|
||||
enum class CK_FO_OPTIONS : CKDWORD {
|
||||
CK_FO_DEFAULT = 0, /**< Default behavior : a new object will be created with the name stored in CKFileObject */
|
||||
CK_FO_RENAMEOBJECT, /**< Renaming : a new object will be created with the name stored in CKFileObject + a integer value XXX to ensure its uniqueness */
|
||||
CK_FO_REPLACEOBJECT, /**< Do not create a new object, instead use an existing one which CK_ID is given by CreatedObject to load the chunk on */
|
||||
CK_FO_DONTLOADOBJECT, /**< Object chunk will not be read either because it is a reference or because the loaded object already exist in the current level and the user choose to keep the existing one. */
|
||||
};
|
||||
/**
|
||||
@brief Specify the way an object just loaded should be handled when it already exists in the level.
|
||||
*/
|
||||
enum class CK_LOADMODE : CKINT {
|
||||
CKLOAD_INVALID = -1, /**< Use the existing object instead of loading */
|
||||
CKLOAD_OK = 0, /**< Ignore ( Name unicity is broken ) */
|
||||
CKLOAD_REPLACE = 1, /**< Replace the existing object (Not yet implemented) */
|
||||
CKLOAD_RENAME = 2, /**< Rename the loaded object */
|
||||
CKLOAD_USECURRENT = 3,/**< Use the existing object instead of loading */
|
||||
};
|
||||
using CK_CREATIONMODE = CK_LOADMODE;
|
||||
/**
|
||||
Specify the way an object is created through CKCreateObject.
|
||||
@remark
|
||||
+ These flag controls the way an object is created, the most important of these flags
|
||||
being CK_OBJECTCREATION_DYNAMIC which, if set in CKCreateObject, make the newly created object
|
||||
dynamic.
|
||||
@see CKContext::CreateObject
|
||||
*/
|
||||
enum class CK_OBJECTCREATION_OPTIONS : CKDWORD {
|
||||
CK_OBJECTCREATION_NONAMECHECK = 0, /**< Do not test for name unicity (may be overriden in special case) */
|
||||
CK_OBJECTCREATION_REPLACE = 1, /**< Replace the current object by the object being loaded */
|
||||
CK_OBJECTCREATION_RENAME = 2, /**< Rename the created object to ensure its uniqueness */
|
||||
CK_OBJECTCREATION_USECURRENT = 3, /**< Do not create a new object, use the one with the same name instead */
|
||||
CK_OBJECTCREATION_ASK = 4, /**< If a duplicate name if found, opens a dialog box to ask the useror use automatic load mode if any. */
|
||||
CK_OBJECTCREATION_FLAGSMASK = 0x0000000F, /**< Mask for previous values */
|
||||
CK_OBJECTCREATION_DYNAMIC = 0x00000010, /**< The object must be created dynamic */
|
||||
CK_OBJECTCREATION_ACTIVATE = 0x00000020, /**< The object will be copied/created active */
|
||||
CK_OBJECTCREATION_NONAMECOPY = 0x00000040 /**< The object will take control of the string given to it directly, without copying it */
|
||||
};
|
||||
/**
|
||||
Type identifier for a Virtools plugin.
|
||||
@remark
|
||||
+ Each plugin must be given a type.
|
||||
+ This enumeration is used to identify a specific catagory
|
||||
of plugin when using the CKPluginManager.
|
||||
|
||||
@see CKPluginManager
|
||||
*/
|
||||
enum class CK_PLUGIN_TYPE : CKDWORD {
|
||||
CKPLUGIN_BITMAP_READER = 0, /**< The plugin is bitmap (textures,sprites) loader */
|
||||
CKPLUGIN_SOUND_READER = 1, /**< Sound Reader Plugin */
|
||||
CKPLUGIN_MODEL_READER = 2, /**< 3D Model Reader */
|
||||
CKPLUGIN_MANAGER_DLL = 3, /**< The plugin implements a Manager */
|
||||
CKPLUGIN_BEHAVIOR_DLL = 4, /**< The plugin implements one or more behaviors */
|
||||
CKPLUGIN_RENDERENGINE_DLL = 5, /**< Render Engine plugin */
|
||||
CKPLUGIN_MOVIE_READER = 6, /**< Movie (AVI,Mpeg) reader */
|
||||
CKPLUGIN_EXTENSION_DLL = 7, /**< Generic extension (definition of new parameter types or operations for ex.) */
|
||||
};
|
||||
/**
|
||||
@remark CHUNK_OPTIONS in original Virtools header.
|
||||
*/
|
||||
enum class CK_STATECHUNK_CHUNKOPTIONS : CKDWORD {
|
||||
CHNK_OPTION_IDS = 0x01, /**< IDS are stored inside chunk */
|
||||
CHNK_OPTION_MAN = 0x02, /**< Managers ints are store inside chunk */
|
||||
CHNK_OPTION_CHN = 0x04, /**< Sub chunk are stored inside chunk */
|
||||
CHNK_OPTION_FILE = 0x08, /**< Chunk was written with indices relative to a file.... */
|
||||
CHNK_OPTION_ALLOWDYN = 0x10, /**< Dynamic object can be written in the chunk */
|
||||
CHNK_OPTION_LISTBIG = 0x20, /**< List are store in big Endian ? */
|
||||
CHNK_DONTDELETE_PTR = 0x40, /**< Data buffer stored in m_Buffer is not owned by CKStateChunk , it must not be deleted... */
|
||||
CHNK_DONTDELETE_PARSER = 0x80, /**< m_Parser Ptr is not owned by CKStateChunk , it must not be deleted... */
|
||||
};
|
||||
enum class CK_STATECHUNK_CHUNKVERSION : CKDWORD {
|
||||
CHUNK_VERSIONBASE = 0,
|
||||
CHUNK_VERSION1 = 4, /**< equal to file version : WriteObjectID => table */
|
||||
CHUNK_VERSION2 = 5, /**< add Manager Data */
|
||||
CHUNK_VERSION3 = 6, /**< New ConvertToBuffer / ReadFromBuffer (file system changed to reflect this ) */
|
||||
CHUNK_VERSION4 = 7, /**< New WriteObjectID when saving to a file */
|
||||
};
|
||||
enum class CK_STATECHUNK_DATAVERSION : CKDWORD {
|
||||
CHUNKDATA_OLDVERSION = 0, /**< Before any version was saved */
|
||||
CHUNKDATA_BASEVERSION = 1, /**< First version */
|
||||
CHUNK_WAVESOUND_VERSION2 = 2, /**< Changes in wavesound format */
|
||||
CHUNK_WAVESOUND_VERSION3 = 3, /**< Changes in wavesound format */
|
||||
CHUNK_MATERIAL_VERSION_ZTEST = 4, /**< Change in material save format */
|
||||
CHUNK_MAJORCHANGE_VERSION = 5, /**< Optimisations on many save functions */
|
||||
CHUNK_MACCHANGE_VERSION = 6, /**< Misc new Statechunk functions for macintosh (Big-Endian <-> Little Endian conversion ) */
|
||||
CHUNK_WAVESOUND_VERSION4 = 7, /**< Changes in wavesound format (Added sound length) */
|
||||
CHUNK_SCENECHANGE_VERSION = 8, /**< Changes in sceneObjectDesc format (Remove lasttimevalue) */
|
||||
CHUNK_MESHCHANGE_VERSION = 9, /**< Changes in Mesh save format (primitives) */
|
||||
CHUNK_DEV_2_1 = 10, /**< Changes in wavesound reading of inside, outside angles */
|
||||
CHUNKDATA_CURRENTVERSION = CHUNK_DEV_2_1,
|
||||
};
|
||||
enum class CK_OBJECT_SHOWOPTION : CKDWORD {
|
||||
CKHIDE = 0x0,
|
||||
CKSHOW = 0x1,
|
||||
CKHIERARCHICALHIDE = 0x2,
|
||||
};
|
||||
/**
|
||||
CKObject Flags
|
||||
@remark
|
||||
+ Flags specifying special settings for basic objects.
|
||||
+ Some of this flags are shared with sub-classes such as CKParameterIn,CKParameterOut and CKBehaviorIO.
|
||||
+ You rarely need to modify directly this flags through CKObject::SetFlags or CKObject::ModifyObjectFlags instead
|
||||
you should always use the specific acces function (given between ()) which may need to perform additionnal operations.
|
||||
@see CKObject, CKObject::GetObjectFlags, CKObject::ModifyObjectFlags
|
||||
*/
|
||||
enum class CK_OBJECT_FLAGS : CKDWORD {
|
||||
CK_OBJECT_INTERFACEOBJ = 0x00000001, /**< Reserved for Inteface Use */
|
||||
CK_OBJECT_PRIVATE = 0x00000002, /**< The object must not be displayed in interface (Lists,Level view,etc...),nor should it be saved. (CKObject::IsPrivate() */
|
||||
CK_OBJECT_INTERFACEMARK = 0x00000004,
|
||||
CK_OBJECT_FREEID = 0x00000008, /**< ID of this object can be released safely and is free to be reused by future CKobjects. */
|
||||
CK_OBJECT_TOBEDELETED = 0x00000010, /**< This object is being deleted */
|
||||
CK_OBJECT_NOTTOBESAVED = 0x00000020, /**< This object must not be saved */
|
||||
CK_OBJECT_VISIBLE = 0x00000040, /**< This object is visible (CKObject::Show) */
|
||||
CK_OBJECT_NAMESHARED = 0x00000080, /**< This object has its name from another entity */
|
||||
CK_OBJECT_DYNAMIC = 0x00000108, /**< This object may be created or deleted at run-time, it also contails CK_OBJECT_FREEID. (CKObject::IsDynamic,CKContext::CreateObject) */
|
||||
CK_OBJECT_HIERACHICALHIDE = 0x00000200, /**< This object hides all its hierarchy (CKObject::Show) */
|
||||
CK_OBJECT_UPTODATE = 0x00000400, /**< (Camera,etc..) */
|
||||
CK_OBJECT_TEMPMARKER = 0x00000800,
|
||||
CK_OBJECT_ONLYFORFILEREFERENCE = 0x00001000,
|
||||
CK_OBJECT_NOTTOBEDELETED = 0x00002000, /**< This object must not be deleted in a clear all */
|
||||
CK_OBJECT_APPDATA = 0x00004000, /**< This object has app data */
|
||||
CK_OBJECT_SINGLEACTIVITY = 0x00008000, /**< this object has an information of single activity */
|
||||
CK_OBJECT_LOADSKIPBEOBJECT = 0x00010000, /**< When loading this object the CKBeObject part should be skipped */
|
||||
CK_OBJECT_NOTTOBELISTEDANDSAVED = 0x00000023, /**< Combination of Private and Not To Be Saved The following flags are specific to parameters (they are stored here for object's size purposes ) */
|
||||
CK_PARAMETEROUT_SETTINGS = 0x00400000,
|
||||
CK_PARAMETEROUT_PARAMOP = 0x00800000, /**< This parameter is the output of a CKParameterOperation (Automatically set by Engine) */
|
||||
CK_PARAMETERIN_DISABLED = 0x01000000, /**< Parameter In or Out is disabled (CKBehavior::EnableInputParameter,CKBehavior::DisableInputParameter) */
|
||||
CK_PARAMETERIN_THIS = 0x02000000, /**< Special parameter type : its value and type are always equal to its owner (CKParameter::SetAsMyselfParameter) */
|
||||
CK_PARAMETERIN_SHARED = 0x04000000,
|
||||
CK_PARAMETEROUT_DELETEAFTERUSE = 0x08000000, /**< When adding parameters to CKMessage, they can be automatically deleted when message is released (CKMessage::AddParameter) */
|
||||
CK_OBJECT_PARAMMASK = 0x0FC00000, /**< Mask for options specific to parameters The Following flags are specific for Behavior ios (CKBehaviorIO) */
|
||||
CK_BEHAVIORIO_IN = 0x10000000, /**< This BehaviorIO is a behavior input (CKBehaviorIO::SetType} */
|
||||
CK_BEHAVIORIO_OUT = 0x20000000, /**< This BehaviorIO is a behavior output (CKBehaviorIO::SetType) */
|
||||
CK_BEHAVIORIO_ACTIVE = 0x40000000, /**< This BehaviorIO is a currently active (CKBehaviorIO::Activate} */
|
||||
CK_OBJECT_IOTYPEMASK = 0x30000000,
|
||||
CK_OBJECT_IOMASK = 0xF0000000, /**< The Following flags are specific for Behavior ios (CKBehaviorIO) */
|
||||
CKBEHAVIORLINK_RESERVED = 0x10000000, /**< This BehaviorIO is a behavior input (CKBehaviorIO::SetType} */
|
||||
CKBEHAVIORLINK_ACTIVATEDLASTFRAME = 0x20000000, /**< This link had been activated last frame */
|
||||
CK_OBJECT_BEHAVIORLINKMASK = 0x30000000,
|
||||
};
|
||||
|
||||
/**
|
||||
@brief 3dEntity Flags
|
||||
@remark
|
||||
+ Flags give user and engine more information about the 3dEntity.
|
||||
@see CK3dEntity::SetFlags,CK3dEntity::GetFlags
|
||||
*/
|
||||
enum class CK_3DENTITY_FLAGS : CKDWORD {
|
||||
CK_3DENTITY_DUMMY = 0x00000001, /**< Entity is a dummy used to represent a position */
|
||||
CK_3DENTITY_FRAME = 0x00000002, /**< Entity is a frame used to represent an orientation */
|
||||
CK_3DENTITY_RESERVED0 = 0x00000020, /**< Obsolete Flag */
|
||||
CK_3DENTITY_TARGETLIGHT = 0x00000100, /**< Entity is a target of a light */
|
||||
CK_3DENTITY_TARGETCAMERA = 0x00000200, /**< Entity is a target of a camera */
|
||||
CK_3DENTITY_IGNOREANIMATION = 0x00000400, /**< Animation using this entity can't modify it */
|
||||
CK_3DENTITY_HIERARCHICALOBSTACLE = 0x00000800, /**< Used by the Collision Manager */
|
||||
CK_3DENTITY_UPDATELASTFRAME = 0x00001000, /**< Store the last world matrix for this Entity after each render */
|
||||
CK_3DENTITY_CAMERAIGNOREASPECT = 0x00002000, /**< Ignore aspect ratio setting for cameras */
|
||||
CK_3DENTITY_DISABLESKINPROCESS = 0x00004000, /**< Force skin processing to be disabled */
|
||||
CK_3DENTITY_ENABLESKINOFFSET = 0x00008000, /**< If not set the skin stay attached to the bones the vertices are linked to, otherwise the skin can be freely rotated,translated or scaled according to its owner entity matrix. */
|
||||
CK_3DENTITY_PLACEVALID = 0x00010000, /**< Used internally when saving */
|
||||
CK_3DENTITY_PARENTVALID = 0x00020000, /**< Used internally when saving */
|
||||
CK_3DENTITY_IKJOINTVALID = 0x00040000, /**< Special flag for Bodyparts : IK Joint data is valid */
|
||||
CK_3DENTITY_PORTAL = 0x00080000, /**< The 3dEntity is a portal */
|
||||
CK_3DENTITY_ZORDERVALID = 0x00100000, /**< The 3dEntity has a non-zero ZOrder */
|
||||
CK_3DENTITY_CHARACTERDOPROCESS = 0x80000000, /**< Special flag for Characters : Automatic process of animation */
|
||||
};
|
||||
/**
|
||||
{filename:CK_TEXTURE_SAVEOPTIONS}
|
||||
Summary: Specify the way textures or sprites will be saved
|
||||
|
||||
Remarks :
|
||||
+ Textures can be stored inside Virtools files or kept as references to external files.
|
||||
+ These options can be used for a specific texture (or sprite) or as a global setting.
|
||||
See also: CKBitmapData::SetSaveOptions,CKSprite::SetSaveOptions,CKContext::SetGlobalImagesSaveOptions
|
||||
*/
|
||||
enum class CK_TEXTURE_SAVEOPTIONS : CKDWORD {
|
||||
CKTEXTURE_RAWDATA = 0, /**< Save raw data inside file. The bitmap is saved in a raw 32 bit per pixel format. */
|
||||
CKTEXTURE_EXTERNAL = 1, /**< Store only the file name for the texture. The bitmap file must be present in the bitmap paths when loading the composition. */
|
||||
CKTEXTURE_IMAGEFORMAT = 2, /**< Save using format specified. The bitmap data will be converted to the specified format by the correspondant bitmap plugin and saved inside file. */
|
||||
CKTEXTURE_USEGLOBAL = 3, /**< Use Global settings, that is the settings given with CKContext::SetGlobalImagesSaveOptions. (Not valid when using CKContext::SetImagesSaveOptions). */
|
||||
CKTEXTURE_INCLUDEORIGINALFILE = 4, /**< Insert original image file inside CMO file. The bitmap file that was used originally for the texture or sprite will be append to the composition file and extracted when the file is loaded. */
|
||||
};
|
||||
/**
|
||||
{filename:CK_SOUND_SAVEOPTIONS}
|
||||
Summary: Specify the way sounds will be saved
|
||||
|
||||
Remarks :
|
||||
+ Sounds can kept as references to external files or the original sound file can
|
||||
be appended to the composition file.
|
||||
+ These options can be used for a specific sound or as a global setting.
|
||||
See also: CKSound::SetSaveOptions,CKContext::SetGlobalSoundSaveOptions
|
||||
*/
|
||||
enum class CK_SOUND_SAVEOPTIONS : CKDWORD {
|
||||
CKSOUND_EXTERNAL = 0, /**< Store only the file name for the sound. The sound file must be present in one of the sound paths when the composition is loaded. */
|
||||
CKSOUND_INCLUDEORIGINALFILE = 1, /**< Insert original sound file inside the CMO file. The sound file that was used originally will be append to the composition file and extracted when the file is loaded. */
|
||||
CKSOUND_USEGLOBAL = 2, /**< Use Global settings. This flag is only valid for the CKSound::SetSaveOptions method. */
|
||||
};
|
||||
|
||||
enum class CK_BITMAPDATA_FLAGS : CKDWORD {
|
||||
CKBITMAPDATA_INVALID = 1,
|
||||
CKBITMAPDATA_TRANSPARENT = 2,
|
||||
CKBITMAPDATA_FORCERESTORE = 4,
|
||||
CKBITMAPDATA_CLAMPUPTODATE = 8,
|
||||
CKBITMAPDATA_CUBEMAP = 16,
|
||||
CKBITMAPDATA_FREEVIDEOMEMORY = 32,
|
||||
CKBITMAPDATA_DYNAMIC = 64,
|
||||
};
|
||||
|
||||
enum class CK_CAMERA_PROJECTION : CKDWORD {
|
||||
CK_PERSPECTIVEPROJECTION = 1,
|
||||
CK_ORTHOGRAPHICPROJECTION = 2,
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,294 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../VTInternal.hpp"
|
||||
|
||||
namespace LibCmo::XContainer {
|
||||
using XIntArray = XArray<CKINT>;
|
||||
using XFileObjectsTable = XHashTable<CK2::CK_ID, CKINT>;
|
||||
}
|
||||
|
||||
namespace LibCmo::CK2 {
|
||||
|
||||
class CKBufferParser {
|
||||
private:
|
||||
CKBYTE* m_MemBegin;
|
||||
CKDWORD m_MemPos;
|
||||
bool m_NeedManualFree;
|
||||
CKDWORD m_MemSize;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Create CKBufferParser from a existed buffer.
|
||||
* @param ptr The start pointer of buffer. This buffer should be allocated by 'new[]', not 'new' or 'malloc()'.
|
||||
* @param rsize The size of buffer.
|
||||
* @param need_manual_free True if provided buffer need freed by CKBufferParser automatically.
|
||||
*/
|
||||
CKBufferParser(const void* ptr, CKDWORD rsize, bool need_manual_free) :
|
||||
m_MemBegin(const_cast<CKBYTE*>(static_cast<const CKBYTE*>(ptr))),
|
||||
m_MemPos(0u), m_MemSize(rsize),
|
||||
m_NeedManualFree(need_manual_free)
|
||||
{}
|
||||
/**
|
||||
* @brief Create CKBufferParser from a new created buffer.
|
||||
* @param newsize The size of new buffer.
|
||||
*/
|
||||
CKBufferParser(CKDWORD newsize) :
|
||||
m_MemBegin(new CKBYTE[newsize]),
|
||||
m_MemPos(0u), m_MemSize(newsize),
|
||||
m_NeedManualFree(true)
|
||||
{}
|
||||
~CKBufferParser() {
|
||||
if (this->m_NeedManualFree) delete[](this->m_MemBegin);
|
||||
}
|
||||
YYCC_DEL_CLS_COPY_MOVE(CKBufferParser);
|
||||
|
||||
const void* GetPtr(CKINT extraoff = 0) { return (this->m_MemBegin + m_MemPos + extraoff); }
|
||||
void* GetMutablePtr(CKINT extraoff = 0) { return (this->m_MemBegin + m_MemPos + extraoff); }
|
||||
void* GetBase() { return this->m_MemBegin; }
|
||||
CKDWORD GetSize() { return this->m_MemSize; }
|
||||
CKDWORD GetCursor() { return this->m_MemPos; }
|
||||
void MoveCursor(CKINT off) { this->m_MemPos += off; }
|
||||
void SetCursor(CKDWORD off) { this->m_MemPos = off; }
|
||||
void Read(void* data, CKDWORD data_size) {
|
||||
std::memcpy(data, (this->m_MemBegin + m_MemPos), data_size);
|
||||
this->m_MemPos += data_size;
|
||||
}
|
||||
template<class _Ty>
|
||||
void Read(_Ty* data) {
|
||||
Read(data, CKSizeof(_Ty));
|
||||
}
|
||||
void Write(const void* data, CKDWORD data_size) {
|
||||
std::memcpy((this->m_MemBegin + m_MemPos), data, data_size);
|
||||
this->m_MemPos += data_size;
|
||||
}
|
||||
template<class _Ty>
|
||||
void Write(const _Ty* data) {
|
||||
Write(data, CKSizeof(_Ty));
|
||||
}
|
||||
};
|
||||
|
||||
#pragma pack(1)
|
||||
struct CKRawFileInfo {
|
||||
CKBYTE NeMo[8];
|
||||
CKDWORD Crc;
|
||||
CKDWORD CKVersion;
|
||||
CKDWORD FileVersion;
|
||||
CKDWORD Zero;
|
||||
CKDWORD FileWriteMode;
|
||||
CKDWORD Hdr1PackSize;
|
||||
|
||||
CKDWORD DataPackSize;
|
||||
CKDWORD DataUnPackSize;
|
||||
CKDWORD ManagerCount;
|
||||
CKDWORD ObjectCount;
|
||||
CKDWORD MaxIDSaved;
|
||||
CKDWORD ProductVersion;
|
||||
CKDWORD ProductBuild;
|
||||
CKDWORD Hdr1UnPackSize;
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
class CKFileInfo {
|
||||
public:
|
||||
CKFileInfo() :
|
||||
ProductVersion(0u), ProductBuild(0x01010000u), FileWriteMode(CK_FILE_WRITEMODE::CKFILE_UNCOMPRESSED),
|
||||
FileVersion(8u), CKVersion(CKVERSION), FileSize(0u),
|
||||
ObjectCount(0u), ManagerCount(0u), MaxIDSaved(0u), Crc(0u),
|
||||
Hdr1PackSize(0u), Hdr1UnPackSize(0u), DataPackSize(0u), DataUnPackSize(0u) {}
|
||||
~CKFileInfo() {}
|
||||
YYCC_DEF_CLS_COPY_MOVE(CKFileInfo);
|
||||
|
||||
CKDWORD ProductVersion; /**< Virtools Version (Dev/Creation). (CK_VIRTOOLS_VERSION) */
|
||||
CKDWORD ProductBuild; /**< Virtools Build Number. */
|
||||
CK_FILE_WRITEMODE FileWriteMode; /**< Options used to save this file. (CK_FILE_WRITEMODE) */
|
||||
CKDWORD FileVersion; /**< Version of file format when file was saved. */
|
||||
CKDWORD CKVersion; /**< Version of CK when file was saved. */
|
||||
CKDWORD FileSize; /**< Size of file in bytes. */
|
||||
CKDWORD ObjectCount; /**< Number of objects stored in the file. */
|
||||
CKDWORD ManagerCount; /**< Number of managers which saved data in the file. */
|
||||
CK_ID MaxIDSaved; /**< Maximum Object identifier saved */
|
||||
CKDWORD Crc; /**< Crc of data */
|
||||
CKDWORD Hdr1PackSize; /**< The compressed size of Header section. */
|
||||
CKDWORD Hdr1UnPackSize; /**< The uncompressed size of Header section. */
|
||||
CKDWORD DataPackSize; /**< The compressed size of Data section. */
|
||||
CKDWORD DataUnPackSize; /**< The uncompressed size of Data section. */
|
||||
};
|
||||
|
||||
class CKFileObject {
|
||||
public:
|
||||
CKFileObject();
|
||||
CKFileObject(const CKFileObject&);
|
||||
CKFileObject(CKFileObject&&);
|
||||
CKFileObject& operator=(const CKFileObject&);
|
||||
CKFileObject& operator=(CKFileObject&&);
|
||||
~CKFileObject();
|
||||
|
||||
CK_ID ObjectId; /**< ID of the object being load/saved (as it will be/was saved in the file) */
|
||||
CK_ID CreatedObjectId; /**< ID of the object being created */
|
||||
CK_CLASSID ObjectCid; /**< Class Identifier of the object */
|
||||
ObjImpls::CKObject* ObjPtr; /**< A pointer to the object itself (as CreatedObject when loading) */
|
||||
XContainer::XString Name; /**< Name of the Object */
|
||||
CKStateChunk* Data; /**< A CKStateChunk that contains object information */
|
||||
CKDWORD PackSize; /**< The CKStateChunk data size */
|
||||
//CKINT PostPackSize; /**< When compressed chunk by chunk : size of Data after compression */
|
||||
//CKINT PrePackSize; /**< When compressed chunk by chunk : size of Data before compression */
|
||||
CK_FO_OPTIONS Options; /**< When loading an object it may be renamed , use to replace another object */
|
||||
CKDWORD FileIndex; /**< Position of the object data inside uncompressed file buffer */
|
||||
CKDWORD SaveFlags; /**< Flags used when this object was saved. */
|
||||
};
|
||||
|
||||
class CKFileManagerData {
|
||||
public:
|
||||
CKFileManagerData();
|
||||
CKFileManagerData(const CKFileManagerData&);
|
||||
CKFileManagerData(CKFileManagerData&&);
|
||||
CKFileManagerData& operator=(const CKFileManagerData&);
|
||||
CKFileManagerData& operator=(CKFileManagerData&&);
|
||||
~CKFileManagerData();
|
||||
|
||||
CKStateChunk* Data;
|
||||
CKGUID Manager;
|
||||
};
|
||||
|
||||
class CKFilePluginDependencies {
|
||||
public:
|
||||
CKFilePluginDependencies() :
|
||||
m_PluginCategory(CK_PLUGIN_TYPE::CKPLUGIN_MANAGER_DLL), m_Guids() {}
|
||||
~CKFilePluginDependencies() {}
|
||||
YYCC_DEF_CLS_COPY_MOVE(CKFilePluginDependencies);
|
||||
|
||||
CK_PLUGIN_TYPE m_PluginCategory;
|
||||
XContainer::XArray<CKGUID> m_Guids;
|
||||
//XContainer::XBitArray ValidGuids;
|
||||
};
|
||||
|
||||
class CKFileVisitor {
|
||||
public:
|
||||
CKFileVisitor(CKFileReader* reader);
|
||||
CKFileVisitor(CKFileWriter* writer);
|
||||
CKFileVisitor(const CKFileVisitor&);
|
||||
CKFileVisitor(CKFileVisitor&&);
|
||||
CKFileVisitor& operator=(const CKFileVisitor&);
|
||||
CKFileVisitor& operator=(CKFileVisitor&&);
|
||||
|
||||
const CKFileObject* GetFileObjectByIndex(CKDWORD index);
|
||||
CKDWORD GetIndexByObjectID(CK_ID objid);
|
||||
bool AddSavedFile(CKSTRING u8FileName);
|
||||
|
||||
protected:
|
||||
bool m_IsReader;
|
||||
CKFileReader* m_Reader;
|
||||
CKFileWriter* m_Writer;
|
||||
CKContext* m_Ctx;
|
||||
};
|
||||
|
||||
class CKFileReader {
|
||||
friend class CKFileVisitor;
|
||||
public:
|
||||
CKFileReader(CKContext* ctx);
|
||||
~CKFileReader();
|
||||
YYCC_DEL_CLS_COPY_MOVE(CKFileReader);
|
||||
|
||||
// ========== Loading ==========
|
||||
CKERROR ShallowLoad(CKSTRING u8_filename);
|
||||
CKERROR DeepLoad(CKSTRING u8_filename);
|
||||
|
||||
// ========== Loading Result ==========
|
||||
CK_ID GetSaveIdMax();
|
||||
const XContainer::XArray<CKFileObject>& GetFileObjects();
|
||||
const XContainer::XArray<CKFileManagerData>& GetManagersData();
|
||||
const XContainer::XArray<CKFilePluginDependencies>& GetPluginsDep();
|
||||
const XContainer::XArray<XContainer::XString>& GetIncludedFiles();
|
||||
const CKFileInfo GetFileInfo();
|
||||
|
||||
protected:
|
||||
bool m_Done;
|
||||
CK_ID m_SaveIDMax; /**< Maximum CK_ID found when saving or loading objects */
|
||||
XContainer::XArray<CKFileObject> m_FileObjects; /**< List of objects being saved / loaded */
|
||||
XContainer::XArray<CKFileManagerData> m_ManagersData; /**< Manager Data loaded */
|
||||
XContainer::XArray<CKFilePluginDependencies> m_PluginsDep; /**< Plugins dependencies for this file */
|
||||
// XContainer::XClassArray<XContainer::XIntArray> m_IndexByClassId; /**< List of index in the m_FileObjects table sorted by ClassID */
|
||||
/**
|
||||
* @brief List of files that should be inserted in the CMO file.
|
||||
* @remarks Each item is just file name, not the full path to file.
|
||||
*/
|
||||
XContainer::XArray<XContainer::XString> m_IncludedFiles;
|
||||
CKFileInfo m_FileInfo; /**< Headers summary */
|
||||
|
||||
CKERROR ReadFileHeader(CKBufferParser* ParserPtr);
|
||||
CKERROR ReadFileData(CKBufferParser* ParserPtr);
|
||||
|
||||
CKContext* m_Ctx;
|
||||
CKFileVisitor m_Visitor;
|
||||
};
|
||||
|
||||
class CKFileWriter {
|
||||
friend class CKFileVisitor;
|
||||
public:
|
||||
CKFileWriter(CKContext* ctx);
|
||||
CKFileWriter(CKContext* ctx, CKFileReader* reader, bool is_shallow);
|
||||
~CKFileWriter();
|
||||
YYCC_DEL_CLS_COPY_MOVE(CKFileWriter);
|
||||
|
||||
// ========== Saving Preparing ==========
|
||||
bool AddSavedObject(ObjImpls::CKObject* obj, CKDWORD flags = CK_STATESAVE_ALL);
|
||||
bool AddSavedObjects(const XContainer::XObjectPointerArray& objarray, CKDWORD flags = CK_STATESAVE_ALL);
|
||||
bool AddSavedFile(CKSTRING u8FileName);
|
||||
|
||||
// ========== Saving ==========
|
||||
CKERROR Save(CKSTRING u8_filename);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief A helper function to check whether given file can be written.
|
||||
* @param[in] filename The name of file to be wriiten
|
||||
* @return CKERROR::CK_OK if can write.
|
||||
*/
|
||||
CKERROR PrepareFile(CKSTRING filename);
|
||||
/**
|
||||
* @brief Internal used Object Adder.
|
||||
* @details
|
||||
* This function is used by AddSavedObject() and Copy from reader constructor.
|
||||
* This function accept an object pointer, and try adding them.
|
||||
* And set m_IncludedFiles and m_ObjectsHashTable properly.
|
||||
* @param[in] obj The pointer to added object.
|
||||
* @param[in] flags The flag used when saving this object.
|
||||
* @return True if success.
|
||||
*/
|
||||
bool InternalObjectAdder(ObjImpls::CKObject* obj, CKDWORD flags = CK_STATESAVE_ALL);
|
||||
|
||||
protected:
|
||||
bool m_Done; /**< True if this writer is already written into file. A written CKFileWriter can no be written again. */
|
||||
/**
|
||||
* @brief True if this writer is not allowed to add objects.
|
||||
* @remarks
|
||||
* \li This field should be false in default.
|
||||
* \li This field usually be set when importing from reader.
|
||||
*/
|
||||
bool m_DisableAddingObject;
|
||||
/**
|
||||
* @brief True if this writer is not allowed to add files.
|
||||
* @remarks
|
||||
* \li This field should be false in default.
|
||||
* \li This field usually be set when importing from reader.
|
||||
*/
|
||||
bool m_DisableAddingFile;
|
||||
|
||||
CK_ID m_SaveIDMax; /**< Maximum CK_ID found when saving or loading objects */
|
||||
XContainer::XArray<CKFileObject> m_FileObjects; /**< List of objects being saved / loaded */
|
||||
XContainer::XArray<CKFileManagerData> m_ManagersData; /**< Manager Data loaded */
|
||||
XContainer::XArray<CKFilePluginDependencies> m_PluginsDep; /**< Plugins dependencies for this file */
|
||||
/**
|
||||
* @brief List of files that should be inserted in the CMO file.
|
||||
* @remarks Each item is the full path to file.
|
||||
*/
|
||||
XContainer::XArray<XContainer::XString> m_IncludedFiles;
|
||||
XContainer::XHashTable<CK_ID, CKDWORD> m_ObjectsHashTable; /**< A Object ID to save index hash table. */
|
||||
XContainer::XBitArray m_AlreadySavedMask; /**< Field recording saved object id. If this object is saved, set m_AlreadySavedMask[id] to true. Also used to check whether object already is in save list. */
|
||||
CKFileInfo m_FileInfo; /**< Headers summary */
|
||||
|
||||
CKContext* m_Ctx;
|
||||
CKFileVisitor m_Visitor;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,386 +0,0 @@
|
||||
#include "CKFile.hpp"
|
||||
#include "CKStateChunk.hpp"
|
||||
#include "CKContext.hpp"
|
||||
#include "MgrImpls/CKPathManager.hpp"
|
||||
#include "ObjImpls/CKObject.hpp"
|
||||
#include <cstdarg>
|
||||
|
||||
namespace LibCmo::CK2 {
|
||||
|
||||
#pragma region CKFileObject
|
||||
|
||||
// CKObject is managed by CKMinContext,
|
||||
// so we do not considering its memory leak
|
||||
// however, we need process CKStateChunk.
|
||||
|
||||
CKFileObject::CKFileObject() :
|
||||
ObjectId(0u), CreatedObjectId(0u), ObjectCid(CK_CLASSID::CKCID_OBJECT),
|
||||
ObjPtr(nullptr), Name(), Data(nullptr), Options(CK_FO_OPTIONS::CK_FO_DEFAULT),
|
||||
FileIndex(0u), SaveFlags(CK_STATESAVE_ALL), PackSize(0u) {}
|
||||
|
||||
CKFileObject::CKFileObject(const CKFileObject& rhs) :
|
||||
ObjectId(rhs.ObjectId), CreatedObjectId(rhs.CreatedObjectId), ObjectCid(rhs.ObjectCid),
|
||||
ObjPtr(rhs.ObjPtr), Name(rhs.Name), Data(rhs.Data), Options(rhs.Options),
|
||||
FileIndex(rhs.FileIndex), SaveFlags(rhs.SaveFlags), PackSize(rhs.PackSize) {
|
||||
if (this->Data != nullptr) {
|
||||
this->Data = new CKStateChunk(*(rhs.Data));
|
||||
}
|
||||
}
|
||||
|
||||
CKFileObject::CKFileObject(CKFileObject&& rhs) :
|
||||
ObjectId(rhs.ObjectId), CreatedObjectId(rhs.CreatedObjectId), ObjectCid(rhs.ObjectCid),
|
||||
ObjPtr(rhs.ObjPtr), Name(rhs.Name), Data(rhs.Data), Options(rhs.Options),
|
||||
FileIndex(rhs.FileIndex), SaveFlags(rhs.SaveFlags), PackSize(rhs.PackSize) {
|
||||
rhs.Data = nullptr;
|
||||
}
|
||||
|
||||
CKFileObject& CKFileObject::operator=(const CKFileObject& rhs) {
|
||||
this->ObjectId = rhs.ObjectId;
|
||||
this->CreatedObjectId = rhs.CreatedObjectId;
|
||||
this->ObjectCid = rhs.ObjectCid;
|
||||
this->ObjPtr = rhs.ObjPtr;
|
||||
this->Name = rhs.Name;
|
||||
|
||||
this->Data = rhs.Data;
|
||||
if (this->Data != nullptr) {
|
||||
this->Data = new CKStateChunk(*(rhs.Data));
|
||||
}
|
||||
|
||||
this->PackSize = rhs.PackSize;
|
||||
this->Options = rhs.Options;
|
||||
this->FileIndex = rhs.FileIndex;
|
||||
this->SaveFlags = rhs.SaveFlags;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CKFileObject& CKFileObject::operator=(CKFileObject&& rhs) {
|
||||
this->ObjectId = rhs.ObjectId;
|
||||
this->CreatedObjectId = rhs.CreatedObjectId;
|
||||
this->ObjectCid = rhs.ObjectCid;
|
||||
this->ObjPtr = rhs.ObjPtr;
|
||||
this->Name = rhs.Name;
|
||||
|
||||
this->Data = rhs.Data;
|
||||
rhs.Data = nullptr;
|
||||
|
||||
this->PackSize = rhs.PackSize;
|
||||
this->Options = rhs.Options;
|
||||
this->FileIndex = rhs.FileIndex;
|
||||
this->SaveFlags = rhs.SaveFlags;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CKFileObject::~CKFileObject() {
|
||||
if (Data != nullptr) delete Data;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region CKFileManagerData
|
||||
|
||||
CKFileManagerData::CKFileManagerData() :
|
||||
Data(nullptr), Manager(0u, 0u) {}
|
||||
|
||||
CKFileManagerData::CKFileManagerData(const CKFileManagerData& rhs) :
|
||||
Data(rhs.Data), Manager(rhs.Manager) {
|
||||
|
||||
if (this->Data != nullptr) {
|
||||
this->Data = new CKStateChunk(*(rhs.Data));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
CKFileManagerData::CKFileManagerData(CKFileManagerData&& rhs) :
|
||||
Data(rhs.Data), Manager(rhs.Manager) {
|
||||
rhs.Data = nullptr;
|
||||
}
|
||||
|
||||
CKFileManagerData& CKFileManagerData::operator=(const CKFileManagerData& rhs) {
|
||||
this->Manager = rhs.Manager;
|
||||
|
||||
this->Data = rhs.Data;
|
||||
if (this->Data != nullptr) {
|
||||
this->Data = new CKStateChunk(*(rhs.Data));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CKFileManagerData& CKFileManagerData::operator=(CKFileManagerData&& rhs) {
|
||||
this->Manager = rhs.Manager;
|
||||
|
||||
this->Data = rhs.Data;
|
||||
rhs.Data = nullptr;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CKFileManagerData::~CKFileManagerData() {
|
||||
if (Data != nullptr) delete Data;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region CKFileReader
|
||||
|
||||
CKFileReader::CKFileReader(CKContext* ctx) :
|
||||
m_Ctx(ctx), m_Visitor(this),
|
||||
m_Done(false),
|
||||
m_SaveIDMax(0),
|
||||
m_FileObjects(), m_ManagersData(), m_PluginsDep(), m_IncludedFiles(),
|
||||
m_FileInfo() {}
|
||||
|
||||
CKFileReader::~CKFileReader() {}
|
||||
|
||||
CK_ID CKFileReader::GetSaveIdMax() {
|
||||
return m_SaveIDMax;
|
||||
}
|
||||
|
||||
const XContainer::XArray<CKFileObject>& CKFileReader::GetFileObjects() {
|
||||
return m_FileObjects;
|
||||
}
|
||||
|
||||
const XContainer::XArray<CKFileManagerData>& CKFileReader::GetManagersData() {
|
||||
return m_ManagersData;
|
||||
}
|
||||
|
||||
const XContainer::XArray<CKFilePluginDependencies>& CKFileReader::GetPluginsDep() {
|
||||
return m_PluginsDep;
|
||||
}
|
||||
|
||||
const XContainer::XArray<XContainer::XString>& CKFileReader::GetIncludedFiles() {
|
||||
return m_IncludedFiles;
|
||||
}
|
||||
|
||||
const CKFileInfo CKFileReader::GetFileInfo() {
|
||||
return m_FileInfo;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region CKFileWriter
|
||||
|
||||
CKFileWriter::CKFileWriter(CKContext* ctx) :
|
||||
m_Ctx(ctx), m_Visitor(this),
|
||||
m_Done(false),
|
||||
m_DisableAddingObject(false), m_DisableAddingFile(false),
|
||||
m_SaveIDMax(0),
|
||||
m_FileObjects(), m_ManagersData(), m_PluginsDep(), m_IncludedFiles(),
|
||||
m_FileInfo()
|
||||
{}
|
||||
|
||||
CKFileWriter::CKFileWriter(CKContext* ctx, CKFileReader* reader, bool is_shallow) :
|
||||
m_Ctx(ctx), m_Visitor(this),
|
||||
m_Done(false),
|
||||
m_DisableAddingObject(true), m_DisableAddingFile(is_shallow), // only disable adding file in shallow mode. but disable adding object in all mode.
|
||||
m_SaveIDMax(0),
|
||||
m_FileObjects(), m_ManagersData(), m_PluginsDep(), m_IncludedFiles(),
|
||||
m_FileInfo()
|
||||
{
|
||||
if (is_shallow) {
|
||||
|
||||
#pragma region Shallow Assign
|
||||
|
||||
// sync save id max
|
||||
this->m_SaveIDMax = reader->GetSaveIdMax();
|
||||
|
||||
// copy statechunk
|
||||
for (const auto& item : reader->GetFileObjects()) {
|
||||
CKFileObject obj;
|
||||
|
||||
// use ctor to copy CKStateChunk
|
||||
if (item.Data == nullptr) {
|
||||
obj.Data = nullptr;
|
||||
} else {
|
||||
obj.Data = new CKStateChunk(*item.Data);
|
||||
}
|
||||
|
||||
// set other data
|
||||
obj.ObjectId = item.ObjectId;
|
||||
obj.CreatedObjectId = 0;
|
||||
obj.ObjectCid = item.ObjectCid;
|
||||
obj.ObjPtr = nullptr; // set zero for obj
|
||||
obj.Name = item.Name;
|
||||
obj.SaveFlags = item.SaveFlags;
|
||||
|
||||
// insert
|
||||
m_FileObjects.emplace_back(std::move(obj));
|
||||
}
|
||||
|
||||
// copy managers
|
||||
for (const auto& item : reader->GetManagersData()) {
|
||||
CKFileManagerData mgr;
|
||||
// copy guid
|
||||
mgr.Manager = item.Manager;
|
||||
// copy chunk
|
||||
if (item.Data == nullptr) {
|
||||
mgr.Data = nullptr;
|
||||
} else {
|
||||
mgr.Data = new CKStateChunk(*item.Data);
|
||||
}
|
||||
|
||||
// insert
|
||||
m_ManagersData.emplace_back(std::move(mgr));
|
||||
}
|
||||
|
||||
// copy plugin dep
|
||||
for (const auto& item : reader->GetPluginsDep()) {
|
||||
// direct copy
|
||||
m_PluginsDep.emplace_back(item);
|
||||
}
|
||||
|
||||
// copy included file
|
||||
for (const auto& item : reader->GetIncludedFiles()) {
|
||||
// resolve it to temp folder
|
||||
// and add it
|
||||
m_IncludedFiles.emplace_back(m_Ctx->GetPathManager()->GetTempFilePath(item.c_str()));
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} else {
|
||||
|
||||
#pragma region Deep Assign
|
||||
|
||||
// call internal object adder one by one
|
||||
for (const auto& item : reader->GetFileObjects()) {
|
||||
CKFileObject obj;
|
||||
|
||||
// skip if invalid
|
||||
if (item.ObjPtr == nullptr) continue;
|
||||
|
||||
// try add
|
||||
InternalObjectAdder(item.ObjPtr);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
CKFileWriter::~CKFileWriter() {}
|
||||
|
||||
bool CKFileWriter::InternalObjectAdder(ObjImpls::CKObject * obj, CKDWORD flags) {
|
||||
if (obj == nullptr) return false;
|
||||
|
||||
// check whether is saved.
|
||||
CK_ID objid = obj->GetID();
|
||||
if (XContainer::NSXBitArray::IsSet(m_AlreadySavedMask, static_cast<CKDWORD>(objid))) return false;
|
||||
|
||||
// ok, insert this value
|
||||
m_ObjectsHashTable.try_emplace(objid, static_cast<CKDWORD>(m_FileObjects.size()));
|
||||
XContainer::NSXBitArray::Set(m_AlreadySavedMask, static_cast<CKDWORD>(objid));
|
||||
// update max id
|
||||
m_SaveIDMax = std::max(m_SaveIDMax, objid);
|
||||
|
||||
// add entry
|
||||
CKFileObject fobj;
|
||||
fobj.ObjectId = objid;
|
||||
fobj.ObjPtr = obj;
|
||||
fobj.ObjectCid = obj->GetClassID();
|
||||
fobj.SaveFlags = flags;
|
||||
XContainer::NSXString::FromCKSTRING(fobj.Name, obj->GetName());
|
||||
m_FileObjects.emplace_back(std::move(fobj));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CKFileWriter::AddSavedObject(ObjImpls::CKObject* obj, CKDWORD flags) {
|
||||
if (m_Done || m_DisableAddingObject) return false;
|
||||
|
||||
// call internal adder
|
||||
return InternalObjectAdder(obj, flags);
|
||||
}
|
||||
|
||||
bool CKFileWriter::AddSavedObjects(const XContainer::XObjectPointerArray& objarray, CKDWORD flags) {
|
||||
if (m_Done || m_DisableAddingObject) return false;
|
||||
|
||||
bool ret = true;
|
||||
for (auto obj : objarray) {
|
||||
// call AddSavedObject one by one
|
||||
if (!AddSavedObject(obj, flags)) {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool CKFileWriter::AddSavedFile(CKSTRING u8FileName) {
|
||||
if (m_Done || m_DisableAddingFile) return false;
|
||||
if (u8FileName == nullptr) return false;
|
||||
|
||||
m_IncludedFiles.emplace_back(u8FileName);
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
|
||||
#pragma region CKFileVisitor
|
||||
|
||||
CKFileVisitor::CKFileVisitor(CKFileReader* reader) :
|
||||
m_IsReader(true), m_Reader(reader), m_Writer(nullptr), m_Ctx(reader->m_Ctx) {
|
||||
if (reader == nullptr) throw LogicException("Reader is nullptr.");
|
||||
}
|
||||
|
||||
CKFileVisitor::CKFileVisitor(CKFileWriter* writer) :
|
||||
m_IsReader(false), m_Reader(nullptr), m_Writer(writer), m_Ctx(writer->m_Ctx) {
|
||||
if (writer == nullptr) throw LogicException("Writer is nullptr.");
|
||||
}
|
||||
|
||||
CKFileVisitor::CKFileVisitor(const CKFileVisitor& rhs) :
|
||||
m_IsReader(rhs.m_IsReader), m_Reader(rhs.m_Reader), m_Writer(rhs.m_Writer), m_Ctx(rhs.m_Ctx) {}
|
||||
|
||||
CKFileVisitor::CKFileVisitor(CKFileVisitor&& rhs) :
|
||||
m_IsReader(rhs.m_IsReader), m_Reader(rhs.m_Reader), m_Writer(rhs.m_Writer), m_Ctx(rhs.m_Ctx) {}
|
||||
|
||||
CKFileVisitor& CKFileVisitor::operator=(const CKFileVisitor& rhs) {
|
||||
this->m_IsReader = rhs.m_IsReader;
|
||||
this->m_Reader = rhs.m_Reader;
|
||||
this->m_Writer = rhs.m_Writer;
|
||||
this->m_Ctx = rhs.m_Ctx;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CKFileVisitor& CKFileVisitor::operator=(CKFileVisitor&& rhs) {
|
||||
this->m_IsReader = rhs.m_IsReader;
|
||||
this->m_Reader = rhs.m_Reader;
|
||||
this->m_Writer = rhs.m_Writer;
|
||||
this->m_Ctx = rhs.m_Ctx;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
const CKFileObject* CKFileVisitor::GetFileObjectByIndex(CKDWORD index) {
|
||||
if (m_IsReader) {
|
||||
return &m_Reader->m_FileObjects[index];
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
CKDWORD CKFileVisitor::GetIndexByObjectID(CK_ID objid) {
|
||||
// see CKFile::SaveFindObjectIndex in IDA
|
||||
CKDWORD idx = static_cast<CKDWORD>(-1);
|
||||
if (m_IsReader) return idx;
|
||||
|
||||
auto finder = m_Writer->m_ObjectsHashTable.find(objid);
|
||||
if (finder == m_Writer->m_ObjectsHashTable.end()) return idx;
|
||||
return finder->second;
|
||||
}
|
||||
|
||||
bool CKFileVisitor::AddSavedFile(CKSTRING u8FileName) {
|
||||
if (m_IsReader) {
|
||||
return false;
|
||||
} else {
|
||||
return m_Writer->AddSavedFile(u8FileName);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
}
|
||||
@@ -1,436 +0,0 @@
|
||||
#include "CKFile.hpp"
|
||||
#include "CKStateChunk.hpp"
|
||||
#include "ObjImpls/CKObject.hpp"
|
||||
#include "MgrImpls/CKPathManager.hpp"
|
||||
#include "../VxMath/VxMemoryMappedFile.hpp"
|
||||
#include "CKContext.hpp"
|
||||
#include <memory>
|
||||
|
||||
namespace LibCmo::CK2 {
|
||||
|
||||
/*
|
||||
* NOTE:
|
||||
* We onlt support read Virtools file with FileVersion >= 7.
|
||||
* The file with FileVersion < 7 is older than NeMo 1.0 (CK 1.1).
|
||||
* No need to support them.
|
||||
*/
|
||||
|
||||
CKERROR CKFileReader::ShallowLoad(CKSTRING u8_filename) {
|
||||
// check document status
|
||||
if (this->m_Done) return CKERROR::CKERR_CANCELLED;
|
||||
// check CKContext encoding sequence
|
||||
if (!this->m_Ctx->IsValidEncoding()) return CKERROR::CKERR_CANCELLED;
|
||||
|
||||
// check file and open memory
|
||||
if (u8_filename == nullptr) return CKERROR::CKERR_INVALIDPARAMETER;
|
||||
std::unique_ptr<VxMath::VxMemoryMappedFile> mappedFile(new VxMath::VxMemoryMappedFile(u8_filename));
|
||||
if (!mappedFile->IsValid()) {
|
||||
this->m_Ctx->OutputToConsoleEx(u8"Fail to create Memory File for \"%s\".", u8_filename);
|
||||
return CKERROR::CKERR_INVALIDFILE;
|
||||
}
|
||||
|
||||
// create buffer and start loading
|
||||
std::unique_ptr<CKBufferParser> parser(new CKBufferParser(mappedFile->GetBase(), mappedFile->GetFileSize(), false));
|
||||
CKERROR err = this->ReadFileHeader(parser.get());
|
||||
if (err != CKERROR::CKERR_OK) return err;
|
||||
err = this->ReadFileData(parser.get());
|
||||
if (err != CKERROR::CKERR_OK) return err;
|
||||
|
||||
// other data will be free automatically
|
||||
// set done flag and return
|
||||
this->m_Done = true;
|
||||
return CKERROR::CKERR_OK;
|
||||
}
|
||||
|
||||
CKERROR CKFileReader::ReadFileHeader(CKBufferParser* ParserPtr) {
|
||||
std::unique_ptr<CKBufferParser> parser(new CKBufferParser(ParserPtr->GetBase(), ParserPtr->GetSize(), false));
|
||||
parser->SetCursor(ParserPtr->GetCursor());
|
||||
|
||||
std::string name_conv;
|
||||
|
||||
// ========== read header ==========
|
||||
// check header size
|
||||
if (parser->GetSize() < CKSizeof(CKRawFileInfo)) return CKERROR::CKERR_INVALIDFILE;
|
||||
if (std::memcmp(parser->GetPtr(), CKNEMOFI, sizeof(CKRawFileInfo::NeMo))) return CKERROR::CKERR_INVALIDFILE;
|
||||
// read header
|
||||
CKRawFileInfo rawHeader;
|
||||
parser->Read(&rawHeader);
|
||||
|
||||
// ========== header checker ==========
|
||||
// check zero flag?
|
||||
if (rawHeader.Zero) return CKERROR::CKERR_INVALIDFILE;
|
||||
// check file version
|
||||
if (rawHeader.FileVersion > 9 || rawHeader.FileVersion < 7) return CKERROR::CKERR_OBSOLETEVIRTOOLS;
|
||||
// force reset too big product ver?
|
||||
if (rawHeader.ProductVersion >= 12u) {
|
||||
rawHeader.ProductVersion = 0u;
|
||||
rawHeader.ProductBuild = 0x01010000u;
|
||||
}
|
||||
|
||||
// ========== assign value ==========
|
||||
this->m_FileInfo.ProductVersion = rawHeader.ProductVersion;
|
||||
this->m_FileInfo.ProductBuild = rawHeader.ProductBuild;
|
||||
this->m_FileInfo.FileWriteMode = static_cast<CK_FILE_WRITEMODE>(rawHeader.FileWriteMode);
|
||||
this->m_FileInfo.CKVersion = rawHeader.CKVersion;
|
||||
this->m_FileInfo.FileVersion = rawHeader.FileVersion;
|
||||
this->m_FileInfo.FileSize = parser->GetSize();
|
||||
this->m_FileInfo.ManagerCount = rawHeader.ManagerCount;
|
||||
this->m_FileInfo.ObjectCount = rawHeader.ObjectCount;
|
||||
this->m_FileInfo.MaxIDSaved = static_cast<CK_ID>(rawHeader.MaxIDSaved);
|
||||
this->m_FileInfo.Hdr1PackSize = rawHeader.FileVersion >= 8 ? rawHeader.Hdr1PackSize : 0u;
|
||||
this->m_FileInfo.Hdr1UnPackSize = rawHeader.FileVersion >= 8 ? rawHeader.Hdr1UnPackSize : 0u;
|
||||
this->m_FileInfo.DataPackSize = rawHeader.DataPackSize;
|
||||
this->m_FileInfo.DataUnPackSize = rawHeader.DataUnPackSize;
|
||||
this->m_FileInfo.Crc = rawHeader.Crc;
|
||||
|
||||
// ========== crc and body unpacker ==========
|
||||
if (this->m_FileInfo.FileVersion >= 8) {
|
||||
// crc checker for file ver >= 8
|
||||
// reset crc field of header
|
||||
rawHeader.Crc = 0u;
|
||||
|
||||
// Compute and check CRC in theory (< Virtools 4.0)
|
||||
CKDWORD gotten_crc = CKComputeDataCRC(&rawHeader, CKSizeof(CKRawFileInfo), 0u);
|
||||
parser->SetCursor(CKSizeof(CKRawFileInfo));
|
||||
gotten_crc = CKComputeDataCRC(parser->GetPtr(), this->m_FileInfo.Hdr1PackSize, gotten_crc);
|
||||
parser->MoveCursor(this->m_FileInfo.Hdr1PackSize);
|
||||
gotten_crc = CKComputeDataCRC(parser->GetPtr(), this->m_FileInfo.DataPackSize, gotten_crc);
|
||||
|
||||
if (gotten_crc != this->m_FileInfo.Crc) {
|
||||
// MARK:
|
||||
// If the CRC check failed, there is another way to compute CRC. (>= Virtools 4.0)
|
||||
// This is a patch for Dassault stupid programmer.
|
||||
//
|
||||
// After Virtools 4.0, Dassault use a new way to compute the CRC of file.
|
||||
// Dassault introduces a new class called CKMemoryBufferWriter which use file and memory map to handle big file properly.
|
||||
// This algorithm splits the whole data body into 8 MB chunks and calculate them one by one to avoid instantaneous memory occupation.
|
||||
// However, there is a bug in virtual function CKMemoryBufferWriter::ComputeCRC.
|
||||
// It takes `PreviousCRC` as argument but never use it in function.
|
||||
// In this function, the start value of CRC compution is hardcoded 0.
|
||||
// So, although Dassault programmer try to compute CRC for file header, header part and daat part in code, it actually only compute CRC for data part!
|
||||
// I 100% sure this is the mistake of Dassault stupid programmer and this bug cause more horrible result.
|
||||
//
|
||||
// In Virtools 2.1, engine will check CRC of file first. If no matched CRC, engine will reject loading file.
|
||||
// So the obvious result is that we can not load file saved by Virtools 4.0 in Virtools 2.1.
|
||||
// But this is not the point which makes me indignant.
|
||||
// The real weird point is that we can use Virtools 3.5 to open file saved by Virtools 4.0, but why?
|
||||
// After some researches, I found that the programmer of Dassault totally removed CRC check when loading file, since some version which I don't know, to suppress this bug!
|
||||
// This is totally cheat and commercial-oriented behavior!
|
||||
// I guess Dassault programmer also found that they can not load new created file in old Virtools.
|
||||
// But they didn't find out what cause this bug, and just directly remove the whole of CRC checker to resolve this bug!
|
||||
// I can't believe that this thing happens on such official software.
|
||||
// This is the point which makes me indignant.
|
||||
gotten_crc = CKComputeDataCRC(parser->GetPtr(), this->m_FileInfo.DataPackSize, 0u);
|
||||
|
||||
// Both CRC compute methods are failed. This file may be really broken.
|
||||
// Report exception directly.
|
||||
if (gotten_crc != this->m_FileInfo.Crc) {
|
||||
this->m_Ctx->OutputToConsole(u8"Virtools file CRC error.");
|
||||
return CKERROR::CKERR_FILECRCERROR;
|
||||
}
|
||||
}
|
||||
|
||||
// reset cursor
|
||||
parser->SetCursor(CKSizeof(CKRawFileInfo));
|
||||
|
||||
// compare size to decide wheher use compress feature
|
||||
if (this->m_FileInfo.Hdr1PackSize != this->m_FileInfo.Hdr1UnPackSize) {
|
||||
void* decomp_buffer = CKUnPackData(this->m_FileInfo.Hdr1UnPackSize, parser->GetPtr(), this->m_FileInfo.Hdr1PackSize);
|
||||
if (decomp_buffer != nullptr) {
|
||||
parser = std::unique_ptr<CKBufferParser>(new CKBufferParser(decomp_buffer, this->m_FileInfo.Hdr1UnPackSize, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========== object list read ==========
|
||||
// file ver >= 7 have this features
|
||||
{
|
||||
// apply max id saved
|
||||
this->m_SaveIDMax = this->m_FileInfo.MaxIDSaved;
|
||||
// resize
|
||||
this->m_FileObjects.resize(this->m_FileInfo.ObjectCount);
|
||||
|
||||
// read data
|
||||
for (auto& fileobj : this->m_FileObjects) {
|
||||
// read basic fields
|
||||
parser->Read(&fileobj.ObjectId);
|
||||
parser->Read(&fileobj.ObjectCid);
|
||||
parser->Read(&fileobj.FileIndex);
|
||||
|
||||
CKDWORD namelen;
|
||||
parser->Read(&namelen);
|
||||
if (namelen != 0) {
|
||||
name_conv.resize(namelen);
|
||||
parser->Read(name_conv.data(), namelen);
|
||||
if (!m_Ctx->GetUTF8String(name_conv, fileobj.Name))
|
||||
m_Ctx->OutputToConsole(u8"Fail to get UTF8 name for CKObject when reading file header. Some objects name will leave to blank.");
|
||||
} else {
|
||||
XContainer::NSXString::FromCKSTRING(fileobj.Name, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========== dep list read ==========
|
||||
// file ver >= 8 have this feature
|
||||
if (this->m_FileInfo.FileVersion >= 8) {
|
||||
// get size and resize
|
||||
CKDWORD depSize;
|
||||
parser->Read(&depSize);
|
||||
this->m_PluginsDep.resize(depSize);
|
||||
|
||||
CKDWORD guid_size;
|
||||
for (auto& dep : this->m_PluginsDep) {
|
||||
// read category
|
||||
parser->Read(&dep.m_PluginCategory);
|
||||
// get size and resize
|
||||
parser->Read(&guid_size);
|
||||
dep.m_Guids.resize(guid_size);
|
||||
// read data
|
||||
if (guid_size != 0) {
|
||||
parser->Read(dep.m_Guids.data(), sizeof(CKGUID) * guid_size);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// ========== included file list read ==========
|
||||
// file ver >= 8 have this feature
|
||||
if (this->m_FileInfo.FileVersion >= 8) {
|
||||
// MARK: i don't knwo what is this!
|
||||
CKINT hasIncludedFile;
|
||||
parser->Read(&hasIncludedFile);
|
||||
|
||||
if (hasIncludedFile > 0) {
|
||||
// read included file size and resize
|
||||
CKDWORD includedFileCount;
|
||||
parser->Read(&includedFileCount);
|
||||
this->m_IncludedFiles.resize(includedFileCount);
|
||||
|
||||
hasIncludedFile -= static_cast<CKINT>(sizeof(CKDWORD));
|
||||
}
|
||||
|
||||
// MARK: backward pos
|
||||
// backward with 0?
|
||||
parser->MoveCursor(hasIncludedFile);
|
||||
}
|
||||
|
||||
// ========== sync main parser ==========
|
||||
if (this->m_FileInfo.FileVersion >= 8) {
|
||||
// file ver >= 8, use header offset
|
||||
// because it have compress feature
|
||||
ParserPtr->SetCursor(this->m_FileInfo.Hdr1PackSize + CKSizeof(CKRawFileInfo));
|
||||
} else {
|
||||
// otherwise, sync with current parser.
|
||||
ParserPtr->SetCursor(parser->GetCursor());
|
||||
}
|
||||
|
||||
return CKERROR::CKERR_OK;
|
||||
}
|
||||
|
||||
CKERROR CKFileReader::ReadFileData(CKBufferParser* ParserPtr) {
|
||||
std::unique_ptr<CKBufferParser> parser(new CKBufferParser(ParserPtr->GetBase(), ParserPtr->GetSize(), false));
|
||||
parser->SetCursor(ParserPtr->GetCursor());
|
||||
|
||||
std::string name_conv;
|
||||
|
||||
// ========== compress feature process ==========
|
||||
if (YYCC::EnumHelper::Has(this->m_FileInfo.FileWriteMode, CK_FILE_WRITEMODE::CKFILE_CHUNKCOMPRESSED_OLD) ||
|
||||
YYCC::EnumHelper::Has(this->m_FileInfo.FileWriteMode, CK_FILE_WRITEMODE::CKFILE_WHOLECOMPRESSED)) {
|
||||
|
||||
void* decomp_buffer = CKUnPackData(this->m_FileInfo.DataUnPackSize, parser->GetPtr(), this->m_FileInfo.DataPackSize);
|
||||
if (decomp_buffer != nullptr) {
|
||||
parser = std::unique_ptr<CKBufferParser>(new CKBufferParser(decomp_buffer, this->m_FileInfo.DataUnPackSize, true));
|
||||
}
|
||||
}
|
||||
|
||||
// ========== old file crc and obj list read ==========
|
||||
// only file ver < 8 run this
|
||||
if (this->m_FileInfo.FileVersion < 8) {
|
||||
// check crc
|
||||
CKDWORD gotten_crc = CKComputeDataCRC(
|
||||
parser->GetPtr(),
|
||||
parser->GetSize() - parser->GetCursor(),
|
||||
0u
|
||||
);
|
||||
if (gotten_crc != this->m_FileInfo.Crc) {
|
||||
this->m_Ctx->OutputToConsole(u8"Virtools file CRC error.");
|
||||
return CKERROR::CKERR_FILECRCERROR;
|
||||
}
|
||||
|
||||
// MARK: why read again? especially for file ver == 7.
|
||||
// get save id max
|
||||
parser->Read(&this->m_SaveIDMax);
|
||||
// get object count and resize
|
||||
parser->Read(&this->m_FileInfo.ObjectCount);
|
||||
if (this->m_FileObjects.empty()) {
|
||||
this->m_FileObjects.resize(this->m_FileInfo.ObjectCount);
|
||||
}
|
||||
}
|
||||
|
||||
// ========== manager read ==========
|
||||
// only file ver >= 6 have this
|
||||
if (this->m_FileInfo.ManagerCount != 0) {
|
||||
this->m_ManagersData.resize(this->m_FileInfo.ManagerCount);
|
||||
CKDWORD stateChunkLen = 0u;
|
||||
bool stateChkParseSuccess = false;
|
||||
|
||||
for (auto& mgr : this->m_ManagersData) {
|
||||
// read guid
|
||||
parser->Read(&mgr.Manager);
|
||||
|
||||
// read statechunk len
|
||||
parser->Read(&stateChunkLen);
|
||||
// check len
|
||||
if (stateChunkLen == 0) {
|
||||
mgr.Data = nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
// read statechunk
|
||||
mgr.Data = new CKStateChunk(&this->m_Visitor, this->m_Ctx);
|
||||
stateChkParseSuccess = mgr.Data->ConvertFromBuffer(parser->GetPtr());
|
||||
if (!stateChkParseSuccess) {
|
||||
delete mgr.Data;
|
||||
mgr.Data = nullptr;
|
||||
}
|
||||
parser->MoveCursor(stateChunkLen);
|
||||
}
|
||||
}
|
||||
|
||||
// ========== object read ==========
|
||||
// only works file version >= 4. < 4 section has been removed.
|
||||
if (this->m_FileInfo.ObjectCount != 0) {
|
||||
// new file reader section
|
||||
bool stateChkParseSuccess = false;
|
||||
for (auto& obj : this->m_FileObjects) {
|
||||
// get statechunk len
|
||||
parser->Read(&obj.PackSize);
|
||||
// check state chunk len
|
||||
if (obj.PackSize == 0) {
|
||||
obj.Data = nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
// read state chunk
|
||||
obj.Data = new CKStateChunk(&this->m_Visitor, this->m_Ctx);
|
||||
stateChkParseSuccess = obj.Data->ConvertFromBuffer(parser->GetPtr());
|
||||
if (!stateChkParseSuccess) {
|
||||
delete obj.Data;
|
||||
obj.Data = nullptr;
|
||||
}
|
||||
parser->MoveCursor(obj.PackSize);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// ========== included file get ==========
|
||||
// before reading, we need switch back to original parser.
|
||||
// and skip data chunk size
|
||||
parser = std::unique_ptr<CKBufferParser>(new CKBufferParser(ParserPtr->GetBase(), ParserPtr->GetSize(), false));
|
||||
parser->SetCursor(ParserPtr->GetCursor());
|
||||
parser->MoveCursor(this->m_FileInfo.DataPackSize);
|
||||
|
||||
// then we can read it.
|
||||
if (this->m_IncludedFiles.size() != 0) {
|
||||
for (auto& file : this->m_IncludedFiles) {
|
||||
// get file name length and resize it
|
||||
CKDWORD filenamelen = 0u;
|
||||
parser->Read(&filenamelen);
|
||||
name_conv.resize(filenamelen);
|
||||
|
||||
// read filename
|
||||
if (filenamelen != 0) {
|
||||
parser->Read(name_conv.data(), filenamelen);
|
||||
if (!m_Ctx->GetUTF8String(name_conv, file))
|
||||
m_Ctx->OutputToConsole(u8"Fail to get UTF8 name for included file when reading file body. Some included files may be stripped.");
|
||||
}
|
||||
|
||||
// read file body length
|
||||
CKDWORD filebodylen = 0u;
|
||||
parser->Read(&filebodylen);
|
||||
|
||||
// read file body
|
||||
XContainer::XString tempfilename = m_Ctx->GetPathManager()->GetTempFilePath(file.c_str());
|
||||
FILE* fp = YYCC::IOHelper::UTF8FOpen(tempfilename.c_str(), u8"wb");
|
||||
if (fp != nullptr) {
|
||||
std::fwrite(parser->GetPtr(), sizeof(CKBYTE), filebodylen, fp);
|
||||
std::fclose(fp);
|
||||
} else {
|
||||
m_Ctx->OutputToConsoleEx(u8"Fail to open temp file: %s", tempfilename.c_str());
|
||||
}
|
||||
|
||||
// move to next
|
||||
parser->MoveCursor(filebodylen);
|
||||
}
|
||||
}
|
||||
|
||||
return CKERROR::CKERR_OK;
|
||||
}
|
||||
|
||||
CKERROR CKFileReader::DeepLoad(CKSTRING u8_filename) {
|
||||
// check document status
|
||||
if (this->m_Done) return CKERROR::CKERR_CANCELLED;
|
||||
// check CKContext encoding sequence
|
||||
if (!this->m_Ctx->IsValidEncoding()) return CKERROR::CKERR_CANCELLED;
|
||||
|
||||
// ========== prepare work ==========
|
||||
CKERROR err = CKERROR::CKERR_OK;
|
||||
|
||||
// get shallow document first
|
||||
err = this->ShallowLoad(u8_filename);
|
||||
if (err != CKERROR::CKERR_OK) return err;
|
||||
// reset done flag because we need further processing
|
||||
this->m_Done = false;
|
||||
|
||||
// ========== create object first ==========
|
||||
for (auto& obj : this->m_FileObjects) {
|
||||
// todo: skip CK_LEVEL
|
||||
// todo: resolve references
|
||||
if (obj.Data == nullptr) continue;
|
||||
|
||||
// create object and assign created obj ckid
|
||||
obj.ObjPtr = m_Ctx->CreateObject(obj.ObjectCid, XContainer::NSXString::ToCKSTRING(obj.Name));
|
||||
if (obj.ObjPtr == nullptr) {
|
||||
obj.CreatedObjectId = 0u;
|
||||
} else {
|
||||
obj.CreatedObjectId = obj.ObjPtr->GetID();
|
||||
}
|
||||
}
|
||||
|
||||
// ========== CKStateChunk remap ==========
|
||||
// todo: remap
|
||||
// todo: CK_LEVEL special proc
|
||||
|
||||
// ========== consume Managers ==========
|
||||
// todo...
|
||||
|
||||
// ========== analyze objects CKStateChunk ==========
|
||||
for (auto& obj : this->m_FileObjects) {
|
||||
if (obj.Data == nullptr || obj.ObjPtr == nullptr) continue;
|
||||
|
||||
// todo: special treat for CK_LEVEL
|
||||
// try parsing data
|
||||
obj.Data->StartRead();
|
||||
bool success = obj.ObjPtr->Load(obj.Data, &this->m_Visitor);
|
||||
obj.Data->StopRead();
|
||||
if (success) {
|
||||
// if success, clear CKStateChunk*
|
||||
delete obj.Data;
|
||||
obj.Data = nullptr;
|
||||
} else {
|
||||
// if failed, delete it
|
||||
m_Ctx->DestroyObject(obj.ObjectId);
|
||||
obj.ObjPtr = nullptr;
|
||||
obj.CreatedObjectId = 0u;
|
||||
}
|
||||
}
|
||||
|
||||
// ========== finalize work ==========
|
||||
|
||||
|
||||
// set done flag and return
|
||||
this->m_Done = true;
|
||||
return CKERROR::CKERR_OK;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,377 +0,0 @@
|
||||
#include "CKFile.hpp"
|
||||
#include "CKContext.hpp"
|
||||
#include "CKStateChunk.hpp"
|
||||
#include "ObjImpls/CKObject.hpp"
|
||||
#include "MgrImpls/CKBaseManager.hpp"
|
||||
#include "MgrImpls/CKPathManager.hpp"
|
||||
#include "../VxMath/VxMemoryMappedFile.hpp"
|
||||
#include <memory>
|
||||
|
||||
namespace LibCmo::CK2 {
|
||||
|
||||
CKERROR CKFileWriter::Save(CKSTRING u8_filename) {
|
||||
// check document status
|
||||
if (this->m_Done) return CKERROR::CKERR_CANCELLED;
|
||||
// check CKContext encoding sequence
|
||||
if (!this->m_Ctx->IsValidEncoding()) return CKERROR::CKERR_CANCELLED;
|
||||
|
||||
// encoding conv helper
|
||||
std::string name_conv;
|
||||
|
||||
// try detect filename legality
|
||||
CKERROR err = PrepareFile(u8_filename);
|
||||
if (err != CKERROR::CKERR_OK) return err;
|
||||
|
||||
// ========== Prepare Stage ==========
|
||||
// todo: add TOBEDELETED flag for all Referenced objects's m_ObjectFlags
|
||||
|
||||
// MARK: ignore the notification to all CKBehavior based objects.
|
||||
|
||||
// ========== StateChunk convertion ==========
|
||||
|
||||
// iterate all objects and transform it into CKStateChunk
|
||||
// MARK: Drop the support of collecting the sum of InterfaceChunk's size.
|
||||
// because it is useless.
|
||||
for (auto& obj : m_FileObjects) {
|
||||
// if there is a chunk, skip
|
||||
if (obj.Data != nullptr) continue;
|
||||
|
||||
obj.Data = new CKStateChunk(&m_Visitor, m_Ctx);
|
||||
obj.Data->StartWrite();
|
||||
bool suc = obj.ObjPtr->Save(obj.Data, &m_Visitor, obj.SaveFlags);
|
||||
obj.Data->StopWrite();
|
||||
if (!suc) {
|
||||
// fail to parse
|
||||
delete obj.Data;
|
||||
obj.Data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// iterate manager
|
||||
// if object adding is disabled, skip. because in that mode, no need to query manager data.
|
||||
if (!m_DisableAddingObject) {
|
||||
CKINT mgrcount = m_Ctx->GetManagerCount();
|
||||
CKINT availablemgr = 0;
|
||||
|
||||
// place manager
|
||||
// if no data, skip it
|
||||
m_ManagersData.resize(mgrcount);
|
||||
for (CKINT i = 0; i < mgrcount; ++i) {
|
||||
MgrImpls::CKBaseManager* mgr = m_Ctx->GetManager(i);
|
||||
|
||||
m_ManagersData[availablemgr].Manager = mgr->GetGuid();
|
||||
|
||||
m_ManagersData[availablemgr].Data = new CKStateChunk(&m_Visitor, m_Ctx);
|
||||
m_ManagersData[availablemgr].Data->StartWrite();
|
||||
bool suc = mgr->SaveData(m_ManagersData[availablemgr].Data, &m_Visitor);
|
||||
m_ManagersData[availablemgr].Data->StopWrite();
|
||||
if (!suc) {
|
||||
delete m_ManagersData[availablemgr].Data;
|
||||
m_ManagersData[availablemgr].Data = nullptr;
|
||||
} else {
|
||||
++availablemgr;
|
||||
}
|
||||
}
|
||||
// resize to the new size which erase all skipped manager
|
||||
m_ManagersData.resize(availablemgr);
|
||||
|
||||
}
|
||||
|
||||
// if object adding is disabled, skip plugin dep. same reason with manager iteration.
|
||||
if (!m_DisableAddingObject) {
|
||||
// todo: finish plugin dep filling
|
||||
}
|
||||
|
||||
// ========== Size Calc ==========
|
||||
// iterate 3 list to get each parts' size
|
||||
CKDWORD sumDataObjSize = 0,
|
||||
sumHdrObjSize = 0;
|
||||
for (auto& obj : m_FileObjects) {
|
||||
// += 4DWORD(ObjId, ObjCid, FileIndex, NameLen)
|
||||
sumHdrObjSize += 4 * CKSizeof(CKDWORD);
|
||||
if (XContainer::NSXString::ToCKSTRING(obj.Name) != nullptr) {
|
||||
// += Name size
|
||||
if (!m_Ctx->GetOrdinaryString(obj.Name, name_conv))
|
||||
m_Ctx->OutputToConsole(u8"Fail to get ordinary string for CKObject name when computing the size of saved file. It may cause application crash or saved file has blank object name.");
|
||||
sumHdrObjSize += static_cast<CKDWORD>(name_conv.size());
|
||||
}
|
||||
|
||||
if (obj.Data == nullptr) {
|
||||
obj.PackSize = 0;
|
||||
} else {
|
||||
obj.PackSize = obj.Data->ConvertToBuffer(nullptr);
|
||||
}
|
||||
// += chunk size + chunk
|
||||
sumDataObjSize += obj.PackSize + CKSizeof(CKDWORD);
|
||||
}
|
||||
|
||||
CKDWORD sumDataMgrSize = 0;
|
||||
for (auto& mgr : m_ManagersData) {
|
||||
CKDWORD chunksize;
|
||||
if (mgr.Data == nullptr) {
|
||||
chunksize = 0;
|
||||
} else {
|
||||
chunksize = mgr.Data->ConvertToBuffer(nullptr);
|
||||
}
|
||||
// += GUID(2 DWORD) + chunk size + chunk
|
||||
sumDataMgrSize += chunksize + 3 * CKSizeof(CKDWORD);
|
||||
}
|
||||
|
||||
// += Plugin Dep list size
|
||||
CKDWORD sumHdrPlgSize = CKSizeof(CKDWORD);
|
||||
for (auto& plg : m_PluginsDep) {
|
||||
// += GUID list + (dep category + GUID list size)
|
||||
sumHdrPlgSize +=
|
||||
CKSizeof(CKGUID) * static_cast<CKDWORD>(plg.m_Guids.size())
|
||||
+ 2 * CKSizeof(CKDWORD);
|
||||
}
|
||||
|
||||
CKDWORD sumHdrIncludedFiles = CKSizeof(CKINT) + CKSizeof(CKDWORD);
|
||||
|
||||
// calc the whole size
|
||||
CKDWORD sumHdrSize = sumHdrObjSize + sumHdrPlgSize + sumHdrIncludedFiles;
|
||||
CKDWORD sumDataSize = sumDataObjSize + sumDataMgrSize;
|
||||
|
||||
// compute file index for all object
|
||||
if (!m_FileObjects.empty()) {
|
||||
// set base for first one
|
||||
m_FileObjects[0].FileIndex = sumHdrSize + sumDataMgrSize + CKSizeof(CKRawFileInfo);
|
||||
// calc the remains
|
||||
for (size_t i = 1; i < m_FileObjects.size(); ++i) {
|
||||
// prev obj PackSize + prev obj FileIndex + prev obj chunk size
|
||||
m_FileObjects[i].FileIndex = m_FileObjects[i - 1].FileIndex + m_FileObjects[i - 1].PackSize + CKSizeof(CKDWORD);
|
||||
}
|
||||
}
|
||||
|
||||
// ========== Construct File Header ==========
|
||||
CK_FILE_WRITEMODE fileWriteMode = m_Ctx->GetFileWriteMode();
|
||||
|
||||
CKRawFileInfo rawHeader;
|
||||
std::memcpy(&rawHeader.NeMo, CKNEMOFI, sizeof(CKRawFileInfo::NeMo));
|
||||
rawHeader.Crc = 0;
|
||||
rawHeader.Zero = 0;
|
||||
rawHeader.CKVersion = CKVERSION;
|
||||
rawHeader.FileVersion = 8;
|
||||
rawHeader.FileWriteMode = static_cast<CKDWORD>(fileWriteMode);
|
||||
rawHeader.ObjectCount = static_cast<CKDWORD>(m_FileObjects.size());
|
||||
rawHeader.ManagerCount = static_cast<CKDWORD>(m_ManagersData.size());
|
||||
rawHeader.Hdr1UnPackSize = sumHdrSize;
|
||||
rawHeader.DataUnPackSize = sumDataSize;
|
||||
rawHeader.Hdr1PackSize = sumHdrSize;
|
||||
rawHeader.DataPackSize = sumDataSize;
|
||||
rawHeader.ProductVersion = DEVVERSION;
|
||||
rawHeader.ProductBuild = DEVBUILD;
|
||||
rawHeader.MaxIDSaved = static_cast<CKDWORD>(m_SaveIDMax);
|
||||
// crc will filled later
|
||||
|
||||
// ========== Write header ==========
|
||||
// create a buffer
|
||||
std::unique_ptr<CKBufferParser> hdrparser(new CKBufferParser(sumHdrSize));
|
||||
|
||||
// write obj
|
||||
for (auto& obj : m_FileObjects) {
|
||||
|
||||
// todo: remove TOBEDELETED for referenced objects' m_ObjectFlags
|
||||
|
||||
hdrparser->Write(&obj.ObjectId);
|
||||
hdrparser->Write(&obj.ObjectCid);
|
||||
hdrparser->Write(&obj.FileIndex);
|
||||
|
||||
if (XContainer::NSXString::ToCKSTRING(obj.Name) != nullptr) {
|
||||
// if have name, write it
|
||||
if (!m_Ctx->GetOrdinaryString(obj.Name, name_conv))
|
||||
m_Ctx->OutputToConsole(u8"Fail to get ordinary string for CKObject name when saving file. Some objects may be saved with blank name.");
|
||||
CKDWORD namelen = static_cast<CKDWORD>(name_conv.size());
|
||||
hdrparser->Write(&namelen);
|
||||
hdrparser->Write(name_conv.data(), namelen);
|
||||
} else {
|
||||
// otherwise, write 0 to indicate no name
|
||||
CKDWORD namelen = 0;
|
||||
hdrparser->Write(&namelen);
|
||||
}
|
||||
}
|
||||
|
||||
// write plugin dep
|
||||
{
|
||||
CKDWORD depsize = static_cast<CKDWORD>(m_PluginsDep.size());
|
||||
hdrparser->Write(&depsize);
|
||||
|
||||
for (auto& dep : m_PluginsDep) {
|
||||
hdrparser->Write(&dep.m_PluginCategory, sizeof(CK_PLUGIN_TYPE));
|
||||
|
||||
CKDWORD guidsize = static_cast<CKDWORD>(dep.m_Guids.size());
|
||||
hdrparser->Write(&guidsize);
|
||||
|
||||
hdrparser->Write(dep.m_Guids.data(), sizeof(CKGUID) * guidsize);
|
||||
}
|
||||
}
|
||||
|
||||
// write included file
|
||||
{
|
||||
CKDWORD cache = CKSizeof(CKDWORD);
|
||||
hdrparser->Write(&cache);
|
||||
|
||||
cache = static_cast<CKDWORD>(m_IncludedFiles.size());
|
||||
hdrparser->Write(&cache);
|
||||
}
|
||||
|
||||
// compress header if needed
|
||||
if (YYCC::EnumHelper::Has(fileWriteMode, CK_FILE_WRITEMODE::CKFILE_CHUNKCOMPRESSED_OLD) ||
|
||||
YYCC::EnumHelper::Has(fileWriteMode, CK_FILE_WRITEMODE::CKFILE_WHOLECOMPRESSED)) {
|
||||
|
||||
CKDWORD comp_buf_size = 0;
|
||||
void* comp_buffer = CKPackData(hdrparser->GetBase(), hdrparser->GetSize(), comp_buf_size, m_Ctx->GetCompressionLevel());
|
||||
if (comp_buffer != nullptr) {
|
||||
hdrparser = std::unique_ptr<CKBufferParser>(new CKBufferParser(comp_buffer, comp_buf_size, true));
|
||||
rawHeader.Hdr1PackSize = comp_buf_size;
|
||||
} else {
|
||||
// fallback
|
||||
rawHeader.Hdr1PackSize = rawHeader.Hdr1UnPackSize;
|
||||
}
|
||||
}
|
||||
|
||||
// ========== Write data ==========
|
||||
// create a buffer
|
||||
std::unique_ptr<CKBufferParser> datparser(new CKBufferParser(sumDataSize));
|
||||
|
||||
// write manager
|
||||
for (auto& mgr : m_ManagersData) {
|
||||
datparser->Write(&mgr.Manager);
|
||||
|
||||
CKDWORD writtenSize = 0;
|
||||
if (mgr.Data != nullptr) {
|
||||
writtenSize = mgr.Data->ConvertToBuffer(datparser->GetMutablePtr(CKSizeof(CKDWORD)));
|
||||
delete mgr.Data;
|
||||
mgr.Data = nullptr;
|
||||
}
|
||||
|
||||
datparser->Write(&writtenSize);
|
||||
datparser->MoveCursor(writtenSize);
|
||||
}
|
||||
|
||||
// write object
|
||||
for (auto& obj : m_FileObjects) {
|
||||
datparser->Write(&obj.PackSize);
|
||||
|
||||
if (obj.Data != nullptr) {
|
||||
obj.Data->ConvertToBuffer(datparser->GetMutablePtr());
|
||||
delete obj.Data;
|
||||
obj.Data = nullptr;
|
||||
}
|
||||
|
||||
datparser->MoveCursor(obj.PackSize);
|
||||
}
|
||||
|
||||
// compress header if needed
|
||||
if (YYCC::EnumHelper::Has(fileWriteMode, CK_FILE_WRITEMODE::CKFILE_CHUNKCOMPRESSED_OLD) ||
|
||||
YYCC::EnumHelper::Has(fileWriteMode, CK_FILE_WRITEMODE::CKFILE_WHOLECOMPRESSED)) {
|
||||
|
||||
CKDWORD comp_buf_size = 0;
|
||||
void* comp_buffer = CKPackData(datparser->GetBase(), datparser->GetSize(), comp_buf_size, m_Ctx->GetCompressionLevel());
|
||||
if (comp_buffer != nullptr) {
|
||||
datparser = std::unique_ptr<CKBufferParser>(new CKBufferParser(comp_buffer, comp_buf_size, true));
|
||||
rawHeader.DataPackSize = comp_buf_size;
|
||||
} else {
|
||||
// fallback
|
||||
rawHeader.DataPackSize = rawHeader.DataUnPackSize;
|
||||
}
|
||||
}
|
||||
|
||||
// ========== Construct File Info ==========
|
||||
// compute crc
|
||||
CKDWORD computedcrc = CKComputeDataCRC(&rawHeader, CKSizeof(CKRawFileInfo), 0u);
|
||||
computedcrc = CKComputeDataCRC(hdrparser->GetBase(), hdrparser->GetSize(), computedcrc);
|
||||
computedcrc = CKComputeDataCRC(datparser->GetBase(), datparser->GetSize(), computedcrc);
|
||||
|
||||
// copy to file info
|
||||
this->m_FileInfo.ProductVersion = rawHeader.ProductVersion;
|
||||
this->m_FileInfo.ProductBuild = rawHeader.ProductBuild;
|
||||
this->m_FileInfo.FileWriteMode = static_cast<CK_FILE_WRITEMODE>(rawHeader.FileWriteMode);
|
||||
this->m_FileInfo.CKVersion = rawHeader.CKVersion;
|
||||
this->m_FileInfo.FileVersion = rawHeader.FileVersion;
|
||||
this->m_FileInfo.Hdr1PackSize = rawHeader.Hdr1PackSize;
|
||||
this->m_FileInfo.Hdr1UnPackSize = rawHeader.Hdr1UnPackSize;
|
||||
this->m_FileInfo.DataPackSize = rawHeader.DataPackSize;
|
||||
this->m_FileInfo.DataUnPackSize = rawHeader.DataUnPackSize;
|
||||
this->m_FileInfo.ManagerCount = rawHeader.ManagerCount;
|
||||
this->m_FileInfo.ObjectCount = rawHeader.ObjectCount;
|
||||
this->m_FileInfo.MaxIDSaved = static_cast<CK_ID>(rawHeader.MaxIDSaved);
|
||||
// fill file size and crc
|
||||
this->m_FileInfo.FileSize = CKSizeof(CKRawFileInfo) + rawHeader.DataPackSize + rawHeader.Hdr1PackSize;
|
||||
this->m_FileInfo.Crc = computedcrc;
|
||||
rawHeader.Crc = computedcrc;
|
||||
|
||||
// ========== Open File & Write Essential Data ==========
|
||||
// open file and test
|
||||
FILE* fs = YYCC::IOHelper::UTF8FOpen(u8_filename, u8"wb");
|
||||
if (fs == nullptr) return CKERROR::CKERR_CANTWRITETOFILE;
|
||||
// write small header + header + data
|
||||
std::fwrite(&rawHeader, sizeof(CKRawFileInfo), 1, fs);
|
||||
std::fwrite(hdrparser->GetBase(), sizeof(CKBYTE), hdrparser->GetSize(), fs);
|
||||
std::fwrite(datparser->GetBase(), sizeof(CKBYTE), datparser->GetSize(), fs);
|
||||
// free buffer
|
||||
hdrparser.reset();
|
||||
datparser.reset();
|
||||
|
||||
// ========== Included Files ==========
|
||||
for (const auto& fentry : m_IncludedFiles) {
|
||||
// get file name from full path
|
||||
XContainer::XString filename(fentry);
|
||||
m_Ctx->GetPathManager()->GetFileName(filename);
|
||||
|
||||
// write filename
|
||||
if (!m_Ctx->GetOrdinaryString(filename, name_conv))
|
||||
m_Ctx->OutputToConsole(u8"Fail to get ordinary string for included file when saving file. Some included files may not be saved correctly.");
|
||||
CKDWORD filenamelen = static_cast<CKDWORD>(name_conv.size());
|
||||
std::fwrite(&filenamelen, sizeof(CKDWORD), 1, fs);
|
||||
std::fwrite(name_conv.data(), sizeof(CKBYTE), filenamelen, fs);
|
||||
|
||||
// try mapping file.
|
||||
std::unique_ptr<VxMath::VxMemoryMappedFile> mappedFile(new VxMath::VxMemoryMappedFile(fentry.c_str()));
|
||||
if (mappedFile->IsValid()) {
|
||||
// write file length
|
||||
CKDWORD filebodylen = mappedFile->GetFileSize();
|
||||
std::fwrite(&filebodylen, sizeof(CKDWORD), 1, fs);
|
||||
|
||||
// write file body
|
||||
std::fwrite(mappedFile->GetBase(), sizeof(CKBYTE), filebodylen, fs);
|
||||
} else {
|
||||
// write zero file length
|
||||
CKDWORD filebodylen = 0;
|
||||
std::fwrite(&filebodylen, sizeof(CKDWORD), 1, fs);
|
||||
|
||||
// report error
|
||||
m_Ctx->OutputToConsoleEx(u8"Fail to open temp file: %" PRI_CKSTRING, fentry.c_str());
|
||||
}
|
||||
|
||||
// release mapped file
|
||||
mappedFile.reset();
|
||||
|
||||
}
|
||||
|
||||
// close file
|
||||
std::fclose(fs);
|
||||
|
||||
// set done flag and return
|
||||
this->m_Done = true;
|
||||
return CKERROR::CKERR_OK;
|
||||
}
|
||||
|
||||
CKERROR CKFileWriter::PrepareFile(CKSTRING filename) {
|
||||
// check nullptr
|
||||
if (filename == nullptr) return CKERROR::CKERR_INVALIDFILE;
|
||||
|
||||
// try open file to check whether we can write it.
|
||||
CKERROR err;
|
||||
FILE* tryfile = YYCC::IOHelper::UTF8FOpen(filename, u8"ab");
|
||||
if (tryfile == nullptr) {
|
||||
err = CKERROR::CKERR_CANTWRITETOFILE;
|
||||
} else {
|
||||
err = CKERROR::CKERR_OK;
|
||||
std::fclose(tryfile);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,469 +0,0 @@
|
||||
#include "../VTUtils.hpp"
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
#define ZLIB_WINAPI
|
||||
#endif
|
||||
#include <zconf.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "CKGlobals.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
|
||||
#include "ObjImpls/CKObject.hpp"
|
||||
#include "ObjImpls/CKSceneObject.hpp"
|
||||
#include "ObjImpls/CKBeObject.hpp"
|
||||
#include "ObjImpls/CKGroup.hpp"
|
||||
#include "ObjImpls/CKRenderObject.hpp"
|
||||
#include "ObjImpls/CK3dEntity.hpp"
|
||||
#include "ObjImpls/CK3dObject.hpp"
|
||||
#include "ObjImpls/CKTexture.hpp"
|
||||
#include "ObjImpls/CKMaterial.hpp"
|
||||
#include "ObjImpls/CKMesh.hpp"
|
||||
#include "ObjImpls/CKLight.hpp"
|
||||
#include "ObjImpls/CKTargetLight.hpp"
|
||||
#include "ObjImpls/CKCamera.hpp"
|
||||
#include "ObjImpls/CKTargetCamera.hpp"
|
||||
|
||||
namespace LibCmo::CK2 {
|
||||
|
||||
#pragma region Compression Utilities
|
||||
|
||||
void* CKPackData(const void* Data, CKDWORD size, CKDWORD& NewSize, CKINT compressionlevel) {
|
||||
// check argument
|
||||
if (Data == nullptr && size != 0u)
|
||||
throw LogicException("Data passed in CKPackData should not be nullptr.");
|
||||
|
||||
// get boundary and allocate buffer.
|
||||
uLong boundary = compressBound(static_cast<uLong>(size));
|
||||
std::unique_ptr<CKBYTE[]> DestBuffer(new CKBYTE[boundary]);
|
||||
|
||||
// do compress
|
||||
uLongf _destLen = static_cast<uLongf>(boundary);
|
||||
if (compress2(
|
||||
reinterpret_cast<Bytef*>(DestBuffer.get()), &_destLen,
|
||||
static_cast<const Bytef*>(Data), static_cast<uLong>(size),
|
||||
static_cast<int>(compressionlevel)) != Z_OK) {
|
||||
// failed
|
||||
NewSize = 0u;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NewSize = static_cast<CKDWORD>(_destLen);
|
||||
return DestBuffer.release();
|
||||
}
|
||||
|
||||
void* CKUnPackData(CKDWORD DestSize, const void* SrcBuffer, CKDWORD SrcSize) {
|
||||
// check argument
|
||||
if (SrcBuffer == nullptr && SrcSize != 0u)
|
||||
throw LogicException("Data passed in CKUnPackData should not be nullptr.");
|
||||
|
||||
// allocate buffer
|
||||
std::unique_ptr<CKBYTE[]> DestBuffer(new CKBYTE[DestSize]);
|
||||
|
||||
uLongf cache = DestSize;
|
||||
if (uncompress(
|
||||
reinterpret_cast<Bytef*>(DestBuffer.get()), &cache,
|
||||
static_cast<const Bytef*>(SrcBuffer), static_cast<uLong>(SrcSize)) != Z_OK) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return DestBuffer.release();
|
||||
}
|
||||
|
||||
CKDWORD CKComputeDataCRC(const void* data, CKDWORD size, CKDWORD PreviousCRC) {
|
||||
// check argument
|
||||
if (data == nullptr && size != 0u)
|
||||
throw LogicException("Data passed in CKComputeDataCRC should not be nullptr.");
|
||||
|
||||
// compute
|
||||
return static_cast<CKDWORD>(adler32(
|
||||
static_cast<uLong>(PreviousCRC),
|
||||
static_cast<const Bytef*>(data),
|
||||
static_cast<uInt>(size)
|
||||
));
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region String Utilities
|
||||
|
||||
template<bool bCaseSenstive>
|
||||
static bool InternalStrEqual(CKSTRING str1, CKSTRING str2) {
|
||||
if (str1 == nullptr) {
|
||||
if (str2 == nullptr) return true;
|
||||
else return false;
|
||||
} else {
|
||||
if (str2 == nullptr) return false;
|
||||
else {
|
||||
// do real compare
|
||||
while (*str1 != u8'\0' && *str2 != u8'\0') {
|
||||
// compare char
|
||||
if constexpr (bCaseSenstive) {
|
||||
if (*str1 != *str2) return false;
|
||||
} else {
|
||||
if (std::tolower(*str1) != std::tolower(*str2)) return false;
|
||||
}
|
||||
// inc step
|
||||
++str1;
|
||||
++str2;
|
||||
}
|
||||
|
||||
// if both of them is zero, return true, otherwise false.
|
||||
return *str1 == u8'\0' && *str2 == u8'\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
bool CKStrEqual(CKSTRING str1, CKSTRING str2) {
|
||||
return InternalStrEqual<true>(str1, str2);
|
||||
}
|
||||
bool CKStrEqualI(CKSTRING str1, CKSTRING str2) {
|
||||
return InternalStrEqual<false>(str1, str2);
|
||||
}
|
||||
|
||||
bool CKStrEmpty(CKSTRING strl) {
|
||||
if (strl == nullptr) return true;
|
||||
return strl[0] == u8'\0';
|
||||
}
|
||||
|
||||
CKDWORD CKStrLen(CKSTRING strl) {
|
||||
if (strl == nullptr) return 0u;
|
||||
size_t len = std::strlen(YYCC::EncodingHelper::ToOrdinary(strl));
|
||||
if (len > static_cast<size_t>(std::numeric_limits<CKDWORD>::max()))
|
||||
throw RuntimeException("Exceed maximum value when cast size_t to CKDWORD.");
|
||||
return static_cast<CKDWORD>(len);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region CKClass Registration
|
||||
|
||||
static XContainer::XArray<CKClassDesc> g_CKClassInfo;
|
||||
|
||||
static bool GetClassIdIndex(CK_CLASSID cid, size_t& intcid) {
|
||||
intcid = static_cast<size_t>(cid);
|
||||
if (intcid >= g_CKClassInfo.size()) return false;
|
||||
if (!g_CKClassInfo[intcid].IsValid) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CKClassNeedNotificationFrom(CK_CLASSID listener, CK_CLASSID listenTo) {
|
||||
size_t idxListener, idxListenTo;
|
||||
if (!GetClassIdIndex(listener, idxListener) || !GetClassIdIndex(listenTo, idxListenTo)) return;
|
||||
|
||||
XContainer::NSXBitArray::Set(g_CKClassInfo[idxListener].ToBeNotify, static_cast<CKDWORD>(idxListenTo));
|
||||
}
|
||||
|
||||
CK_CLASSID CKClassGetNewIdentifier() {
|
||||
size_t classsize = g_CKClassInfo.size();
|
||||
if (classsize < static_cast<size_t>(CK_CLASSID::CKCID_MAXCLASSID)) {
|
||||
return CK_CLASSID::CKCID_MAXCLASSID;
|
||||
} else {
|
||||
return static_cast<CK_CLASSID>(classsize);
|
||||
}
|
||||
}
|
||||
|
||||
void CKClassRegister(CK_CLASSID cid, CK_CLASSID parentCid,
|
||||
CKClassRegisterFct regFct, CKClassCreationFct createFct, CKClassReleaseFct relFct, CKClassNameFct nameFct) {
|
||||
|
||||
// resize class desc array
|
||||
size_t intcid = static_cast<size_t>(cid);
|
||||
if (intcid >= g_CKClassInfo.size()) {
|
||||
g_CKClassInfo.resize(intcid + 1);
|
||||
}
|
||||
|
||||
// emplace desc
|
||||
CKClassDesc desc;
|
||||
desc.IsValid = true;
|
||||
desc.Self = cid;
|
||||
desc.Parent = parentCid;
|
||||
desc.RegisterFct = regFct;
|
||||
desc.CreationFct = createFct;
|
||||
desc.ReleaseFct = relFct;
|
||||
desc.NameFct = nameFct;
|
||||
g_CKClassInfo[intcid] = std::move(desc);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Class Hierarchy Management
|
||||
|
||||
CKDWORD CKGetClassCount() {
|
||||
return static_cast<CKDWORD>(g_CKClassInfo.size());
|
||||
}
|
||||
|
||||
const CKClassDesc* CKGetClassDesc(CK_CLASSID cid) {
|
||||
size_t intcid;
|
||||
if (!GetClassIdIndex(cid, intcid)) return nullptr;
|
||||
return &g_CKClassInfo[intcid];
|
||||
}
|
||||
|
||||
CKSTRING CKClassIDToString(CK_CLASSID cid) {
|
||||
const CKClassDesc* desc = CKGetClassDesc(cid);
|
||||
if (desc == nullptr) return u8"Undefined Type";
|
||||
return desc->NameFct();
|
||||
}
|
||||
|
||||
bool CKIsChildClassOf(CK_CLASSID child, CK_CLASSID parent) {
|
||||
size_t intchild, intparent;
|
||||
if (!GetClassIdIndex(child, intchild) || !GetClassIdIndex(parent, intparent)) return false;
|
||||
return g_CKClassInfo[intchild].Parents[intparent];
|
||||
}
|
||||
|
||||
CK_CLASSID CKGetParentClassID(CK_CLASSID child) {
|
||||
const CKClassDesc* desc = CKGetClassDesc(child);
|
||||
if (desc == nullptr) return CK_CLASSID::CKCID_OBJECT;
|
||||
return desc->Parent;
|
||||
}
|
||||
|
||||
CK_CLASSID CKGetCommonParent(CK_CLASSID cid1, CK_CLASSID cid2) {
|
||||
// I don't know algorithm, I just copy the decompiled code.
|
||||
while (true) {
|
||||
if (CKIsChildClassOf(cid1, cid2)) return cid2;
|
||||
if (CKIsChildClassOf(cid2, cid1)) break;
|
||||
|
||||
cid2 = CKGetParentClassID(cid1);
|
||||
cid1 = cid2;
|
||||
}
|
||||
|
||||
return cid1;
|
||||
}
|
||||
|
||||
bool CKIsNeedNotify(CK_CLASSID listener, CK_CLASSID deletedObjCid) {
|
||||
const CKClassDesc* desc = CKGetClassDesc(listener);
|
||||
if (desc == nullptr) return false;
|
||||
return XContainer::NSXBitArray::IsSet(desc->CommonToBeNotify, static_cast<CKDWORD>(deletedObjCid));
|
||||
}
|
||||
|
||||
XContainer::XBitArray CKGetAllNotifyClassID(const XContainer::XBitArray& delObjCids) {
|
||||
XContainer::XBitArray result;
|
||||
|
||||
for (size_t i = 0; i < delObjCids.size(); ++i) {
|
||||
if (!XContainer::NSXBitArray::IsSet(delObjCids, static_cast<CKDWORD>(i))) continue;
|
||||
|
||||
const CKClassDesc* desc = CKGetClassDesc(static_cast<CK_CLASSID>(i));
|
||||
if (desc == nullptr) continue;
|
||||
|
||||
XContainer::NSXBitArray::Or(result, desc->ToNotify);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Initializations functions
|
||||
|
||||
/*
|
||||
A small hint for ToBeNotify, CommonToBeNotify and ToNotify
|
||||
Assume that A need notification from B, we can see it as that A want buy something from B.
|
||||
This relation is represented in ToBeNotify, a pure relation without any inhertance hierarchy.
|
||||
|
||||
Ok, now we assume A have children AA, B also have children BB.
|
||||
Because B is a businessman, so his children BB also is a businessman.
|
||||
B and BB have the same goods so A can buy his stuff from both of B and BB.
|
||||
This is the first step executed by ComputeParentsNotifyTable().
|
||||
In this step, the function expand existing business relations to all possible business relations (expand to businessman's children)
|
||||
|
||||
Image there is a candy store, C. Because AA still is a kids.
|
||||
So AA want to buy something from C. Now C is in his ToBeNotify.
|
||||
Additionally, A, the parent of AA, force AA to buy something from B, just for A himself.
|
||||
For AA, he does not need want to buy something from B, but his parent A order he to do.
|
||||
This is the second step executed by ComputeParentsNotifyTable().
|
||||
For AA, his parent's relations also need to be merged after he processed his relations with C like I introduced previously.
|
||||
|
||||
Now, AA have a full business list writing all trades he can do.
|
||||
This is represented as CommonToBeNotify.
|
||||
In this time, AA's ToBeNotify only have C, but his CommonToBeNotify have B, BB and C.
|
||||
|
||||
So now, let we change the view from A to B.
|
||||
B want to know whom I can sell something to.
|
||||
Because a full trades list are created from A side.
|
||||
The better solution is just ask the guest: do you want to buy something from me?
|
||||
This operation will fill ToNotify and is implemented at ComputeHierarchyTable().
|
||||
|
||||
At the end of this story,
|
||||
All bussiness man can use ToNofity to see whom they want to sell something to.
|
||||
And all buyer can use CommonToBeNofity to check who they can buy something from.
|
||||
ToBeNotify is just a list to indicate the initial bussiness relation and will never used anymore after real relation established.
|
||||
*/
|
||||
|
||||
static void ComputeParentsTable(CKClassDesc& desc) {
|
||||
// if it has done, do not process it again.
|
||||
if (desc.Done) return;
|
||||
|
||||
// find direct parent
|
||||
CKClassDesc& parent = g_CKClassInfo[static_cast<size_t>(desc.Parent)];
|
||||
if (!parent.IsValid) throw LogicException("No such CK_CLASSID.");
|
||||
|
||||
// if it is not self inheritance, call recursively
|
||||
if (desc.Self != desc.Parent) {
|
||||
ComputeParentsTable(parent);
|
||||
}
|
||||
|
||||
// copy parent's parents
|
||||
desc.Parents = parent.Parents;
|
||||
// and set self as its parent
|
||||
XContainer::NSXBitArray::Set(desc.Parents, static_cast<CKDWORD>(desc.Self));
|
||||
|
||||
// set derivation level
|
||||
desc.DerivationLevel = parent.DerivationLevel + 1;
|
||||
|
||||
// set done
|
||||
desc.Done = true;
|
||||
}
|
||||
static void ComputeParentsNotifyTable(CKClassDesc& desc) {
|
||||
// if it has done, do not process it again.
|
||||
if (desc.Done) return;
|
||||
|
||||
// find direct parent
|
||||
CKClassDesc& parent = g_CKClassInfo[static_cast<size_t>(desc.Parent)];
|
||||
if (!parent.IsValid) throw LogicException("No such CK_CLASSID.");
|
||||
|
||||
// if it is not self inheritance, call recursively
|
||||
if (desc.Self != desc.Parent) {
|
||||
ComputeParentsNotifyTable(parent);
|
||||
}
|
||||
|
||||
// add all children of ToBeNofity list
|
||||
for (CKDWORD idx = 0; idx < desc.ToBeNotify.size(); ++idx) {
|
||||
if (!XContainer::NSXBitArray::IsSet(desc.ToBeNotify, idx))
|
||||
continue;
|
||||
|
||||
CKClassDesc& target = g_CKClassInfo[idx];
|
||||
if (!target.IsValid) continue;
|
||||
XContainer::NSXBitArray::Or(desc.CommonToBeNotify, target.Children);
|
||||
}
|
||||
// and merge parent notify list
|
||||
XContainer::NSXBitArray::Or(desc.CommonToBeNotify, parent.CommonToBeNotify);
|
||||
|
||||
// set done
|
||||
desc.Done = true;
|
||||
}
|
||||
static void CKBuildClassHierarchyTable() {
|
||||
size_t classCount = g_CKClassInfo.size();
|
||||
|
||||
// ===== Build Inhertance Hierarchy =====
|
||||
// set Done to false and resize inhertance XBitArray
|
||||
for (auto& item : g_CKClassInfo) {
|
||||
if (!item.IsValid) continue;
|
||||
item.Done = false;
|
||||
XContainer::NSXBitArray::Resize(item.Parents, static_cast<CKDWORD>(classCount));
|
||||
XContainer::NSXBitArray::Resize(item.Children, static_cast<CKDWORD>(classCount));
|
||||
}
|
||||
// compute parents
|
||||
for (auto& item : g_CKClassInfo) {
|
||||
if (!item.IsValid) continue;
|
||||
ComputeParentsTable(item);
|
||||
}
|
||||
// compute children by parents table
|
||||
// iterate CKClassDesc::Parents and register it self to gotten parents
|
||||
for (auto& item : g_CKClassInfo) {
|
||||
if (!item.IsValid) continue;
|
||||
|
||||
for (size_t idx = 0; idx < classCount; ++idx) {
|
||||
CKClassDesc& checking = g_CKClassInfo[idx];
|
||||
if (!checking.IsValid) continue;
|
||||
|
||||
// if this idx is its parent,
|
||||
// add self to parent.
|
||||
if (XContainer::NSXBitArray::IsSet(item.Parents, static_cast<CKDWORD>(idx))) {
|
||||
XContainer::NSXBitArray::Set(checking.Children, static_cast<CKDWORD>(item.Self));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Register =====
|
||||
// run register
|
||||
for (auto& item : g_CKClassInfo) {
|
||||
if (!item.IsValid || item.RegisterFct == nullptr) continue;
|
||||
item.RegisterFct();
|
||||
}
|
||||
|
||||
// ===== Build Notify Hierarchy =====
|
||||
// set array first
|
||||
for (auto& item : g_CKClassInfo) {
|
||||
if (!item.IsValid) continue;
|
||||
item.Done = false;
|
||||
XContainer::NSXBitArray::Resize(item.ToBeNotify, static_cast<CKDWORD>(classCount));
|
||||
XContainer::NSXBitArray::Resize(item.CommonToBeNotify, static_cast<CKDWORD>(classCount));
|
||||
XContainer::NSXBitArray::Resize(item.ToNotify, static_cast<CKDWORD>(classCount));
|
||||
}
|
||||
// compute CommonToBeNotify
|
||||
for (auto& item : g_CKClassInfo) {
|
||||
if (!item.IsValid) continue;
|
||||
ComputeParentsNotifyTable(item);
|
||||
}
|
||||
// compute ToNotify
|
||||
for (CKDWORD idx = 0; idx < classCount; ++idx) {
|
||||
CKClassDesc& thisDesc = g_CKClassInfo[idx];
|
||||
if (!thisDesc.IsValid) continue;
|
||||
|
||||
for (auto& checking : g_CKClassInfo) {
|
||||
if (!checking.IsValid) continue;
|
||||
|
||||
// if checkingDesc order me, add it.
|
||||
if (XContainer::NSXBitArray::IsSet(checking.CommonToBeNotify, idx)) {
|
||||
XContainer::NSXBitArray::Set(thisDesc.ToNotify, static_cast<CKDWORD>(checking.Self));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void NeedNotificationWrapper(CK_CLASSID thiscid, std::initializer_list<CK_CLASSID> il) {
|
||||
for (auto it = il.begin(); it != il.end(); ++it) {
|
||||
CKClassNeedNotificationFrom(thiscid, *it);
|
||||
}
|
||||
}
|
||||
CKERROR CKStartUp() {
|
||||
// reserve class info array.
|
||||
g_CKClassInfo.reserve(static_cast<size_t>(CK_CLASSID::CKCID_MAXCLASSID));
|
||||
|
||||
// MARK: add class type registrations here
|
||||
#define EasyClassReg(clsname, cid, parentCid, strName) \
|
||||
CKClassRegister(cid, parentCid, \
|
||||
nullptr, \
|
||||
[](CKContext* ctx, CK_ID id, CKSTRING name) -> ObjImpls::CKObject* { return new clsname(ctx, id, name); }, \
|
||||
[](CKContext* ctx, ObjImpls::CKObject* obj) -> void { delete obj; }, \
|
||||
[]() -> CKSTRING { return u8 ## strName; });
|
||||
#define EasyClassRegWithNotify(clsname, cid, parentCid, strName, notifyCids) \
|
||||
CKClassRegister(cid, parentCid, \
|
||||
[]() -> void { NeedNotificationWrapper(cid, notifyCids); }, \
|
||||
[](CKContext* ctx, CK_ID id, CKSTRING name) -> ObjImpls::CKObject* { return new clsname(ctx, id, name); }, \
|
||||
[](CKContext* ctx, ObjImpls::CKObject* obj) -> void { delete obj; }, \
|
||||
[]() -> CKSTRING { return u8 ## strName; });
|
||||
|
||||
EasyClassReg(ObjImpls::CKObject, CK_CLASSID::CKCID_OBJECT, CK_CLASSID::CKCID_OBJECT, "Basic Object");
|
||||
EasyClassReg(ObjImpls::CKSceneObject, CK_CLASSID::CKCID_SCENEOBJECT, CK_CLASSID::CKCID_OBJECT, "Scene Object");
|
||||
EasyClassReg(ObjImpls::CKBeObject, CK_CLASSID::CKCID_BEOBJECT, CK_CLASSID::CKCID_SCENEOBJECT, "Behavioral Object");
|
||||
EasyClassRegWithNotify(ObjImpls::CKGroup, CK_CLASSID::CKCID_GROUP, CK_CLASSID::CKCID_BEOBJECT, "Group", { CK_CLASSID::CKCID_BEOBJECT });
|
||||
EasyClassReg(ObjImpls::CKRenderObject, CK_CLASSID::CKCID_RENDEROBJECT, CK_CLASSID::CKCID_BEOBJECT, "Render Object");
|
||||
EasyClassRegWithNotify(ObjImpls::CK3dEntity, CK_CLASSID::CKCID_3DENTITY, CK_CLASSID::CKCID_RENDEROBJECT, "3D Entity", { CK_CLASSID::CKCID_MESH });
|
||||
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");
|
||||
EasyClassRegWithNotify(ObjImpls::CKMaterial, CK_CLASSID::CKCID_MATERIAL, CK_CLASSID::CKCID_BEOBJECT, "Material", { CK_CLASSID::CKCID_TEXTURE });
|
||||
EasyClassRegWithNotify(ObjImpls::CKMesh, CK_CLASSID::CKCID_MESH, CK_CLASSID::CKCID_BEOBJECT, "Mesh", { CK_CLASSID::CKCID_MATERIAL });
|
||||
EasyClassReg(ObjImpls::CKLight, CK_CLASSID::CKCID_LIGHT, CK_CLASSID::CKCID_3DENTITY, "Light");
|
||||
EasyClassReg(ObjImpls::CKCamera, CK_CLASSID::CKCID_CAMERA, CK_CLASSID::CKCID_3DENTITY, "Camera");
|
||||
EasyClassRegWithNotify(ObjImpls::CKTargetLight, CK_CLASSID::CKCID_TARGETLIGHT, CK_CLASSID::CKCID_LIGHT, "Target Light", { CK_CLASSID::CKCID_3DENTITY });
|
||||
EasyClassRegWithNotify(ObjImpls::CKTargetCamera, CK_CLASSID::CKCID_TARGETCAMERA, CK_CLASSID::CKCID_CAMERA, "Target Camera", { CK_CLASSID::CKCID_3DENTITY });
|
||||
|
||||
#undef EasyClassReg
|
||||
#undef EasyClassRegWithNotify
|
||||
|
||||
CKBuildClassHierarchyTable();
|
||||
|
||||
return CKERROR::CKERR_OK;
|
||||
}
|
||||
|
||||
CKERROR CKShutdown() {
|
||||
// free class infos
|
||||
g_CKClassInfo.clear();
|
||||
|
||||
return CKERROR::CKERR_OK;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
|
||||
}
|
||||
@@ -1,259 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "CKTypes.hpp"
|
||||
#include "../XContainer/XTypes.hpp"
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
namespace LibCmo::CK2 {
|
||||
|
||||
// ========== Compression utilities ==========
|
||||
|
||||
/**
|
||||
* @brief Compress a buffer
|
||||
* @param[in] Data A pointer to the buffer to compress. nullptr is not allowed.
|
||||
* @param[in] size Size of the source buffer.
|
||||
* @param[out] NewSize A reference that will be filled with the size of the compressed buffer. 0 if failed.
|
||||
* @param[in] compressionlevel 0-9 Greater level smaller result size.
|
||||
* @return
|
||||
* A pointer to the compressed buffer. nullptr if failed.
|
||||
* The return pointer should be freed by \c delete[] manually.
|
||||
* @remarks
|
||||
* The size of allocated return value may greater than the passed value of NewSize.
|
||||
* NewSize only indicate the size of the part storing useful data in return value.
|
||||
* @exception LogicException Raised if given buffer is nullptr and size is not equal to zero.
|
||||
* @see CKUnPackData(), CKComputeDataCRC()
|
||||
*/
|
||||
void* CKPackData(const void* Data, CKDWORD size, CKDWORD& NewSize, CKINT compressionlevel);
|
||||
/**
|
||||
* @brief Decompress a buffer
|
||||
* @param[in] DestSize Expected size of the decompressed buffer.
|
||||
* @param[in] SrcBuffer Compressed buffer. nullptr is not allowed.
|
||||
* @param[in] SrcSize Size of the compressed buffer.
|
||||
* @return
|
||||
* A pointer to the decompressed buffer or nullptr if there was a error.
|
||||
* The return pointer should be freed by \c delete[] manually.
|
||||
* @exception LogicException Raised if given buffer is nullptr and size is not equal to zero.
|
||||
* @see CKPackData(), CKComputeDataCRC()
|
||||
*/
|
||||
void* CKUnPackData(CKDWORD DestSize, const void* SrcBuffer, CKDWORD SrcSize);
|
||||
/**
|
||||
* @brief Computes a CRC for a buffer.
|
||||
* @param[in] data A pointer to the buffer to create a CRC for. nullptr is not allowed.
|
||||
* @param[in] size Size of the source buffer.
|
||||
* @param[in] PreviousCRC
|
||||
* The first time a CRC is computed this value should be 0,
|
||||
* but it can be use to compute a single CRC for a several buffers
|
||||
* by using the currently computed CRC for previous buffers in this value.
|
||||
* @return CRC of the buffer.
|
||||
* @exception LogicException Raised if given buffer is nullptr and size is not equal to zero.
|
||||
* @see CKPackData(), CKUnPackData()
|
||||
*/
|
||||
CKDWORD CKComputeDataCRC(const void* data, CKDWORD size, CKDWORD PreviousCRC = 0);
|
||||
|
||||
// ========== String Utilities ==========
|
||||
|
||||
/**
|
||||
* @brief Check whether two string is equal. Case senstive.
|
||||
* @param[in] str1 First string. nullptr is allowed but not suggested.
|
||||
* @param[in] str2 Second string. nullptr is allowed but not suggested.
|
||||
* @return True if two string is equal, otherwise false.
|
||||
* @remarks
|
||||
* nullptr string is not equal to any other string.
|
||||
* However two nullptr string is equal.
|
||||
* @see CKStrEqualI()
|
||||
*/
|
||||
bool CKStrEqual(CKSTRING str1, CKSTRING str2);
|
||||
/**
|
||||
* @brief Check whther two string is equal. Case insenstive.
|
||||
* @param[in] str1 First string. nullptr is allowed but not suggested.
|
||||
* @param[in] str2 Second string. nullptr is allowed but not suggested.
|
||||
* @return True if two string is equal, otherwise false.
|
||||
* @remarks
|
||||
* nullptr string is not equal to any other string.
|
||||
* However two nullptr string is equal.
|
||||
* @see CKStrEqual()
|
||||
*/
|
||||
bool CKStrEqualI(CKSTRING str1, CKSTRING str2);
|
||||
/**
|
||||
* @brief Check whether string is empty
|
||||
* @param[in] strl String for checking. nullptr is allowed but not suggested.
|
||||
* @return True if string is empty, otherwise false.
|
||||
* @remarks nullptr string is seen as empty string.
|
||||
*/
|
||||
bool CKStrEmpty(CKSTRING strl);
|
||||
/**
|
||||
* @brief Get the length of given string.
|
||||
* @param[in] strl String for getting length. nullptr is allowed but not suggested.
|
||||
* @return String length in UTF8 code unit.
|
||||
* @remarks nullptr string will return 0 instead.
|
||||
* @exception RuntimeException Raised if the length of string exceed the maximum value of CKDWORD.
|
||||
*/
|
||||
CKDWORD CKStrLen(CKSTRING strl);
|
||||
|
||||
// ========== Class registration utilities ==========
|
||||
|
||||
/// @brief Function pointer which do extra stuff when registry this class.
|
||||
using CKClassRegisterFct = std::function<void()>;
|
||||
/// @brief Function pointer which create ObjImpls::CKObject pointer by given CKContext, CK_ID and name.
|
||||
using CKClassCreationFct = std::function<ObjImpls::CKObject* (CKContext*, CK_ID, CKSTRING)>;
|
||||
/// @brief Function pointer which free given ObjImpls::CKObject pointer.
|
||||
using CKClassReleaseFct = std::function<void(CKContext*, ObjImpls::CKObject*)>;
|
||||
/// @brief Function pointer which return the name of class.
|
||||
using CKClassNameFct = std::function<CKSTRING()>;
|
||||
//using CKClassDependenciesFct = std::function<CKSTRING(CKINT, CKINT)>;
|
||||
//using CKClassDependenciesCountFct = std::function<CKINT(CKINT)>;
|
||||
|
||||
/**
|
||||
* @brief The representation of a registered class.
|
||||
*/
|
||||
struct CKClassDesc {
|
||||
// Variables used when building class hierarchy table
|
||||
bool IsValid; /**< True if this CKClassDesc is a valid one. Because CK_CLASSID may not be consecutive. */
|
||||
bool Done; /**< Temporary variable indicating this item has been processed when creating table. */
|
||||
|
||||
// Variables initialized upon class registration
|
||||
CK_CLASSID Self; /**< Class identifier of self */
|
||||
CK_CLASSID Parent; /**< Class identifier of parent class */
|
||||
CKClassRegisterFct RegisterFct; /**< Function pointer which do extra stuff when registry this class. */
|
||||
CKClassCreationFct CreationFct; /**< Function pointer which create ObjImpls::CKObject pointer by given CKContext, CK_ID and name. */
|
||||
CKClassReleaseFct ReleaseFct; /**< Function pointer which free given ObjImpls::CKObject pointer. */
|
||||
CKClassNameFct NameFct; /**< Function pointer which return the name of class. */
|
||||
//CKClassDependenciesFct DependsFct; // Pointer to Class dependencies function (Copy,delete,replace...)
|
||||
//CKClassDependenciesCountFct DependsCountFct; // Pointer to Class dependencies Count function (Copy,delete,replace...)
|
||||
|
||||
//// Initialized by class specific registration function
|
||||
//CKDWORD DefaultOptions; // Default options for this class
|
||||
//CKDWORD DefaultCopyDependencies;
|
||||
//CKDWORD DefaultDeleteDependencies;
|
||||
//CKDWORD DefaultReplaceDependencies;
|
||||
//CKDWORD DefaultSaveDependencies;
|
||||
//CKGUID Parameter; // Associated parameter GUID
|
||||
|
||||
// Variables initialized after building class hierarchy table
|
||||
CKINT DerivationLevel; /**< How many parent level it has. 0 for CKObject, etc.. */
|
||||
XContainer::XBitArray Parents; /**< Bit Mask of parents classes */
|
||||
XContainer::XBitArray Children; /**< Bit Mask of children classes */
|
||||
XContainer::XBitArray ToBeNotify; /**< User specified notify list, only for current class. If any deleted objects match class id in this XBitArray, notify the host of this XBitArray. */
|
||||
XContainer::XBitArray CommonToBeNotify; /**< Same as ToBeNotify, but merging all parents' notify list. */
|
||||
XContainer::XBitArray ToNotify; /**< The ClassID to notify when an object of this class is deleted (inverse of ToBeNotify) */
|
||||
|
||||
CKClassDesc() :
|
||||
IsValid(false),
|
||||
Done(false),
|
||||
Self(CK_CLASSID::CKCID_OBJECT), Parent(CK_CLASSID::CKCID_OBJECT),
|
||||
RegisterFct(nullptr), CreationFct(nullptr), ReleaseFct(nullptr), NameFct(nullptr),
|
||||
DerivationLevel(0),
|
||||
Parents(), Children(), ToBeNotify(), CommonToBeNotify()
|
||||
{}
|
||||
YYCC_DEF_CLS_COPY_MOVE(CKClassDesc);
|
||||
};
|
||||
|
||||
// ========== CKClass Registration ==========
|
||||
|
||||
/**
|
||||
* @brief Order that first class id will be notified when deleting object whose class id is second argument
|
||||
* @param[in] listener The id of class will be notified.
|
||||
* @param[in] listenTo The id of class which first argument interested in.
|
||||
* @remarks If one of given class ids is invalid, this function simply return and do nothing.
|
||||
*/
|
||||
void CKClassNeedNotificationFrom(CK_CLASSID listener, CK_CLASSID listenTo);
|
||||
/**
|
||||
* @brief Get an usable class id for registration.
|
||||
* @details This function is usually used by plugin to registering classes.
|
||||
* Because all embedded Virtools classes has constant class id.
|
||||
* @return An usable class id for registration.
|
||||
*/
|
||||
CK_CLASSID CKClassGetNewIdentifier();
|
||||
/**
|
||||
* @brief Register a new class.
|
||||
* @param[in] cid The id of this class.
|
||||
* @param[in] parentCid The id of parent class.
|
||||
* @param[in] regFct Pointer to class specific registration function which do extra stuff when registry this class.
|
||||
* @param[in] createFct Pointer to class instance creation function
|
||||
* @param[in] relFct Pointer to class instance release function
|
||||
* @param[in] nameFct Pointer to class name function
|
||||
*/
|
||||
void CKClassRegister(CK_CLASSID cid, CK_CLASSID parentCid,
|
||||
CKClassRegisterFct regFct, CKClassCreationFct createFct, CKClassReleaseFct relFct, CKClassNameFct nameFct);
|
||||
|
||||
// ========== Class Hierarchy Management ==========
|
||||
|
||||
/**
|
||||
* @brief Get total count of registered classes.
|
||||
* @return The total count of registered classes.
|
||||
*/
|
||||
CKDWORD CKGetClassCount();
|
||||
/**
|
||||
* @brief Get the class description struct by given class id.
|
||||
* @param[in] cid Class id for fetching.
|
||||
* @return The pointer to corresponding class description.
|
||||
* If given class id is invalid, this function will return nullptr.
|
||||
* According to this, caller can utilize this function to validate class id.
|
||||
*/
|
||||
const CKClassDesc* CKGetClassDesc(CK_CLASSID cid);
|
||||
/**
|
||||
* @brief Get the name representation of given class id.
|
||||
* @param[in] cid Class id for fetching.
|
||||
* @return The name of given class id.
|
||||
* If given class id is invalid, it return a predefined name.
|
||||
*/
|
||||
CKSTRING CKClassIDToString(CK_CLASSID cid);
|
||||
|
||||
/**
|
||||
* @brief Check whether first class is second class' child class.
|
||||
* @param[in] child The id of first class assumed as child class.
|
||||
* @param[in] parent The id of second class assumed as parent class.
|
||||
* @return True if relation is satisfied, otherwise false.
|
||||
* If one of given class ids is invalid, this function always return false.
|
||||
*/
|
||||
bool CKIsChildClassOf(CK_CLASSID child, CK_CLASSID parent);
|
||||
/**
|
||||
* @brief Get the parent class id of given class id.
|
||||
* @param[in] child The id to class which need to find parent class.
|
||||
* @return The parent class id.
|
||||
* If given class id is invalid, this function always return the class id of CKObject.
|
||||
*/
|
||||
CK_CLASSID CKGetParentClassID(CK_CLASSID child);
|
||||
/**
|
||||
* @brief Get the cloest common parent of given two classes.
|
||||
* @param[in] cid1 The id of first class finding common parent.
|
||||
* @param[in] cid2 The id of second class finding common parent.
|
||||
* @return The cloest common parent class id.
|
||||
*/
|
||||
CK_CLASSID CKGetCommonParent(CK_CLASSID cid1, CK_CLASSID cid2);
|
||||
|
||||
/**
|
||||
* @brief Check whether object whose class id is first argument, need to be notified when the objects whose class id is second argument, is deleting.
|
||||
* @param[in] listener The class id of checking whether need to be notified.
|
||||
* @param[in] deletedObjCid The class id of deleting object.
|
||||
* @return True if it need to be notified, otherwise false.
|
||||
* If the class id of checking is invalid, this function always return false.
|
||||
*/
|
||||
bool CKIsNeedNotify(CK_CLASSID listener, CK_CLASSID deletedObjCid);
|
||||
/**
|
||||
* @brief Get all class ids need to be notified when objects whose class id included in \c delObjCids are deleting.
|
||||
* @param[in] delObjCids The bit array representing class ids which need to be queried.
|
||||
* @return The queried bit array representing class ids need to be notified.
|
||||
* @see CKIsNeedNotify()
|
||||
*/
|
||||
XContainer::XBitArray CKGetAllNotifyClassID(const XContainer::XBitArray& delObjCids);
|
||||
|
||||
// ========== Initializations Functions ==========
|
||||
|
||||
/**
|
||||
* @brief Initialize CK engine.
|
||||
* @details You must call this function before anything.
|
||||
* @return Error code about initializing.
|
||||
* @see CKShutdown()
|
||||
*/
|
||||
CKERROR CKStartUp();
|
||||
/**
|
||||
* @brief Shutdown CK engine.
|
||||
* @details You must call this function after you have done all things.
|
||||
* @return Error code about shutdown.
|
||||
* @see CKStartUp()
|
||||
*/
|
||||
CKERROR CKShutdown();
|
||||
|
||||
}
|
||||
@@ -1,658 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// This file original name is Ckdefines2.hpp
|
||||
// Change its name accoding to it use to
|
||||
|
||||
#include "../VTUtils.hpp"
|
||||
#include "CKTypes.hpp"
|
||||
#include <cinttypes>
|
||||
#include <cstdint>
|
||||
|
||||
namespace LibCmo::CK2 {
|
||||
|
||||
constexpr const CKDWORD CK_STATESAVE_ALL = 0xFFFFFFFF;
|
||||
|
||||
/**
|
||||
Object
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_OBJECT : CKDWORD {
|
||||
CK_STATESAVE_NAME = 0x00000001, /**< Obsolete */
|
||||
CK_STATESAVE_ID = 0x00000002, /**< Obsolete */
|
||||
CK_STATESAVE_OBJECTHIDDEN = 0x00000004, /**< The object is hidden */
|
||||
CK_STATESAVE_OBJECTHIERAHIDDEN = 0x00000018, /**< The object is hidden hierarchically */
|
||||
CK_STATESAVE_OBJECTALL = 0x0000000F,
|
||||
};
|
||||
/**
|
||||
Be Object
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_BEOBJECT : CKDWORD {
|
||||
CK_STATESAVE_ATTRIBUTES = 0x00000010, /**< Obsolete */
|
||||
CK_STATESAVE_NEWATTRIBUTES = 0x00000011, /**< Save Attributes */
|
||||
CK_STATESAVE_GROUPS = 0x00000020, /**< Obsolete */
|
||||
CK_STATESAVE_DATAS = 0x00000040, /**< Save Flags and (Waiting for message) status */
|
||||
CK_STATESAVE_SOUNDS = 0x00000080, /**< Obsolete */
|
||||
CK_STATESAVE_BEHAVIORS = 0x00000100, /**< Obsolete */
|
||||
CK_STATESAVE_PARAMETERS = 0x00000200, /**< Obsolete */
|
||||
CK_STATESAVE_SINGLEACTIVITY = 0x00000400, /**< SINGLE ACTIVITY */
|
||||
CK_STATESAVE_SCRIPTS = 0x00000800, /**< Obsolete */
|
||||
CK_STATESAVE_BEOBJECTONLY = 0x00000FF0, /**< Save only BeObject specific datas */
|
||||
CK_STATESAVE_BEOBJECTALL = 0x00000FFF, /**< Save All datas */
|
||||
};
|
||||
/**
|
||||
3dEntity
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_3DENTITY : CKDWORD {
|
||||
CK_STATESAVE_3DENTITYSKINDATANORMALS = 0x00001000, /**< Save Skin normals */
|
||||
CK_STATESAVE_ANIMATION = 0x00002000, /**< Obsolete */
|
||||
CK_STATESAVE_MESHS = 0x00004000, /**< Save List of mesh */
|
||||
CK_STATESAVE_PARENT = 0x00008000, /**< Save Parent */
|
||||
CK_STATESAVE_3DENTITYFLAGS = 0x00010000, /**< Save Flags */
|
||||
CK_STATESAVE_3DENTITYMATRIX = 0x00020000, /**< Save Position/orientation/Scale */
|
||||
CK_STATESAVE_3DENTITYHIERARCHY = 0x00040000, /**< obsolete */
|
||||
CK_STATESAVE_3DENTITYPLACE = 0x00080000, /**< Save Place in which the Entity is referenced */
|
||||
CK_STATESAVE_3DENTITYNDATA = 0x00100000, /**< Reserved for future use */
|
||||
CK_STATESAVE_3DENTITYSKINDATA = 0x00200000, /**< Save Skin data */
|
||||
CK_STATESAVE_3DENTITYONLY = 0x003FF000, /**< Save only 3dEntity specific datas */
|
||||
CK_STATESAVE_3DENTITYALL = 0x003FFFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
Light
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_LIGHT : CKDWORD {
|
||||
CK_STATESAVE_LIGHTDATA = 0x00400000, /**< Save Color,Type,Attenuation,Range and cone */
|
||||
CK_STATESAVE_LIGHTDATA2 = 0x00800000, /**< Reserved for future use */
|
||||
CK_STATESAVE_LIGHTRESERVED1 = 0x01000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_LIGHTRESERVED2 = 0x02000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_LIGHTRESERVED3 = 0x04000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_LIGHTRESERVED4 = 0x08000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_LIGHTONLY = 0x0FC00000, /**< Save only Light specific datas */
|
||||
CK_STATESAVE_LIGHTALL = 0x0FFFFFFF, /**< Save All datas for sub-classes Target Light */
|
||||
CK_STATESAVE_TLIGHTTARGET = 0x80000000, /**< Save Light Target */
|
||||
CK_STATESAVE_TLIGHTRESERVED0 = 0x10000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_TLIGHTRESERVED1 = 0x20000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_TLIGHTRESERVED2 = 0x40000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_TLIGHTONLY = 0xF0000000, /**< Save only Target Light specific datas */
|
||||
CK_STATESAVE_TLIGHTALL = 0xFFFFFFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
Camera
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_CAMERA : CKDWORD {
|
||||
CK_STATESAVE_CAMERAFOV = 0x00400000, /**< Save Camera Field of View */
|
||||
CK_STATESAVE_CAMERAPROJTYPE = 0x00800000, /**< Save Camera projection type */
|
||||
CK_STATESAVE_CAMERAOTHOZOOM = 0x01000000, /**< Save Camera orhographic zoom */
|
||||
CK_STATESAVE_CAMERAASPECT = 0x02000000, /**< Save Camera aspect ration */
|
||||
CK_STATESAVE_CAMERAPLANES = 0x04000000, /**< Save Camera near and far clip planes */
|
||||
CK_STATESAVE_CAMERARESERVED2 = 0x08000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_CAMERAONLY = 0x0FC00000, /**< Save only camera specific datas */
|
||||
CK_STATESAVE_CAMERAALL = 0x0FFFFFFF, /**< Save All datas for sub-classes Target Camera */
|
||||
CK_STATESAVE_TCAMERATARGET = 0x10000000, /**< Save camera Target */
|
||||
CK_STATESAVE_TCAMERARESERVED1 = 0x20000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_TCAMERARESERVED2 = 0x40000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_TCAMERAONLY = 0x70000000, /**< Save only Target camera specific datas */
|
||||
CK_STATESAVE_TCAMERAALL = 0x7FFFFFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
Sprite3D
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_SPRITE3D : CKDWORD {
|
||||
CK_STATESAVE_SPRITE3DDATA = 0x00400000, /**< Save offset,mapping,size and material */
|
||||
CK_STATESAVE_SPRITE3DRESERVED0 = 0x00800000, /**< Reserved for future use */
|
||||
CK_STATESAVE_SPRITE3DRESERVED1 = 0x01000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_SPRITE3DRESERVED2 = 0x02000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_SPRITE3DRESERVED3 = 0x04000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_SPRITE3DRESERVED4 = 0x08000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_SPRITE3DONLY = 0x0FC00000, /**< Save only Sprite3D specific datas */
|
||||
CK_STATESAVE_SPRITE3DALL = 0x0FFFFFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
Object 3D
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_3DOBJECT : CKDWORD {
|
||||
CK_STATESAVE_3DOBJECTATTRIBUTES = 0x00400000, /**< Obsolete */
|
||||
CK_STATESAVE_3DOBJECTRESERVED = 0x00800000, /**< Reserved for future use */
|
||||
CK_STATESAVE_3DOBJECTRONLY = 0x00C00000, /**< Save only 3dObject specific datas */
|
||||
CK_STATESAVE_3DOBJECTALL = 0x03FFFFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
BodyPart
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_BODYPART : CKDWORD {
|
||||
CK_STATESAVE_BODYPARTROTJOINT = 0x01000000, /**< Save rotation joint data */
|
||||
CK_STATESAVE_BODYPARTPOSJOINT = 0x02000000, /**< Save position joint data */
|
||||
CK_STATESAVE_BODYPARTCHARACTER = 0x04000000, /**< Save character owning this bodypart */
|
||||
CK_STATESAVE_BODYPARTRESERVED1 = 0x08000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_BODYPARTRESERVED2 = 0x10000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_BODYPARTRESERVED3 = 0x20000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_BODYPARTRESERVED4 = 0x40000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_BODYPARTONLY = 0x7F000000, /**< Save only bodypart specific datas */
|
||||
CK_STATESAVE_BODYPARTALL = 0x7FFFFFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
Character
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_CHARACTER : CKDWORD {
|
||||
CK_STATESAVE_CHARACTERBODYPARTS = 0x00400000, /**< Obsolete */
|
||||
CK_STATESAVE_CHARACTERKINECHAINS = 0x00800000, /**< Obsolete */
|
||||
CK_STATESAVE_CHARACTERANIMATIONS = 0x01000000, /**< Obsolete */
|
||||
CK_STATESAVE_CHARACTERROOT = 0x02000000, /**< Obsolete */
|
||||
CK_STATESAVE_CHARACTERSAVEANIMS = 0x04000000, /**< Save current and next active animations */
|
||||
CK_STATESAVE_CHARACTERSAVECHAINS = 0x08000000, /**< Obsolete */
|
||||
CK_STATESAVE_CHARACTERSAVEPARTS = 0x10000000, /**< Save sub bodyparts and sub-bodyparts data (saved with flag :CK_STATESAVE_BODYPARTALL) */
|
||||
CK_STATESAVE_CHARACTERFLOORREF = 0x20000000, /**< Save Character floor reference object */
|
||||
CK_STATESAVE_CHARACTERRESERVED2 = 0x40000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_CHARACTERRESERVED3 = 0x80000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_CHARACTERONLY = 0xFFC00000, /**< Save only character specific datas */
|
||||
CK_STATESAVE_CHARACTERALL = 0xFFFFFFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
CURVE && Curve Point
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_CURVE : CKDWORD {
|
||||
CK_STATESAVE_CURVEFITCOEFF = 0x00400000, /**< Save fitting coef */
|
||||
CK_STATESAVE_CURVECONTROLPOINT = 0x00800000, /**< Save list of control points */
|
||||
CK_STATESAVE_CURVESTEPS = 0x01000000, /**< Save number of step setting */
|
||||
CK_STATESAVE_CURVEOPEN = 0x02000000, /**< Save Open/Close flag */
|
||||
CK_STATESAVE_CURVERESERVED1 = 0x04000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_CURVERESERVED2 = 0x08000000, /**< Reserved for future use Control points */
|
||||
CK_STATESAVE_CURVEPOINTDEFAULTDATA = 0x10000000, /**< Save Control point setting and position */
|
||||
CK_STATESAVE_CURVEPOINTTCB = 0x20000000, /**< Save Control point tcb settings */
|
||||
CK_STATESAVE_CURVEPOINTTANGENTS = 0x40000000, /**< Save Control point tangents */
|
||||
CK_STATESAVE_CURVEPOINTCURVEPOS = 0x80000000, /**< Save Control point position in curve */
|
||||
CK_STATESAVE_CURVESAVEPOINTS = 0xFF000000, /**< Save control points data */
|
||||
CK_STATESAVE_CURVEONLY = 0xFFC00000, /**< Save only curve specific data */
|
||||
CK_STATESAVE_CURVEALL = 0xFFFFFFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
2dEntity
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_2DENTITY : CKDWORD {
|
||||
CK_STATESAVE_2DENTITYSRCSIZE = 0x00001000, /**< Save source size */
|
||||
CK_STATESAVE_2DENTITYSIZE = 0x00002000, /**< Save size */
|
||||
CK_STATESAVE_2DENTITYFLAGS = 0x00004000, /**< Save Flags */
|
||||
CK_STATESAVE_2DENTITYPOS = 0x00008000, /**< Save position */
|
||||
CK_STATESAVE_2DENTITYZORDER = 0x00100000, /**< Save Z order */
|
||||
CK_STATESAVE_2DENTITYONLY = 0x0010F000, /**< Save only 2dEntity specific data */
|
||||
CK_STATESAVE_2DENTITYMATERIAL = 0x00200000, /**< Save Material */
|
||||
CK_STATESAVE_2DENTITYHIERARCHY = 0x00400000, /**< Save Material */
|
||||
CK_STATESAVE_2DENTITYALL = 0x0070FFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
Sprite
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_SPRITE : CKDWORD {
|
||||
CK_STATESAVE_SPRITECURRENTIMAGE = 0x00010000, /**< Save current image */
|
||||
CK_STATESAVE_SPRITETRANSPARENT = 0x00020000, /**< Save transparency settings */
|
||||
CK_STATESAVE_SPRITEBITMAPS = 0x00040000, /**< Obsolete */
|
||||
CK_STATESAVE_SPRITESHARED = 0x00080000, /**< Save shared sprite */
|
||||
CK_STATESAVE_SPRITEDONOTUSE = 0x00100000, /**< Reseved by CK_STATESAVEFLAGS_2DENTITY */
|
||||
CK_STATESAVE_SPRITEAVIFILENAME = 0x00200000, /**< Obsolete */
|
||||
CK_STATESAVE_SPRITEFILENAMES = 0x00400000, /**< Obsolete */
|
||||
CK_STATESAVE_SPRITECOMPRESSED = 0x00800000, /**< Obsolete */
|
||||
CK_STATESAVE_SPRITEREADER = 0x10000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_SPRITEFORMAT = 0x20000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_SPRITEVIDEOFORMAT = 0x40000000, /**< Video Format */
|
||||
CK_STATESAVE_SPRITESYSTEMCACHING = 0x80000000, /**< System Memory Caching */
|
||||
CK_STATESAVE_SPRITERENDEROPTIONS = 0x80800000, /**< Render options if any... */
|
||||
CK_STATESAVE_SPRITEONLY = 0xF0EF0000, /**< Save only sprite specific data */
|
||||
CK_STATESAVE_SPRITEALL = 0x70FFFFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
Sprite Text
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_SPRITETEXT : CKDWORD {
|
||||
CK_STATESAVE_SPRITETEXT = 0x01000000, /**< Save text */
|
||||
CK_STATESAVE_SPRITEFONT = 0x02000000, /**< Save font settings */
|
||||
CK_STATESAVE_SPRITETEXTCOLOR = 0x04000000, /**< Save text color */
|
||||
CK_STATESAVE_SPRITETEXTRESERVED = 0x08000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_SPRITETEXTDOTNOTUSE = 0x10000000, /**< Reserved by CK_STATESAVE_SPRITEREADER */
|
||||
CK_STATESAVE_SPRITETEXTDONOTUSED2 = 0x20000000, /**< Reserved by CK_STATESAVE_SPRITEFORMAT */
|
||||
CK_STATESAVE_SPRITETEXTONLY = 0x0F000000, /**< Save only SpriteText specific data */
|
||||
CK_STATESAVE_SPRITETEXTALL = 0x3FFFFFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
Sound
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_SOUND : CKDWORD {
|
||||
CK_STATESAVE_SOUNDFILENAME = 0x00001000, /**< Reserved for future use */
|
||||
CK_STATESAVE_SOUNDRESERVED1 = 0x00002000, /**< Reserved for future use */
|
||||
CK_STATESAVE_SOUNDRESERVED2 = 0x00004000, /**< Reserved for future use */
|
||||
CK_STATESAVE_SOUNDRESERVED3 = 0x00008000, /**< Reserved for future use */
|
||||
CK_STATESAVE_SOUNDRESERVED4 = 0x00010000, /**< Reserved for future use */
|
||||
CK_STATESAVE_SOUNDRESERVED5 = 0x00020000, /**< Reserved for future use */
|
||||
CK_STATESAVE_SOUNDRESERVED6 = 0x00040000, /**< Reserved for future use */
|
||||
CK_STATESAVE_SOUNDRESERVED7 = 0x00080000, /**< Reserved for future use */
|
||||
CK_STATESAVE_SOUNDONLY = 0x000FF000, /**< Save only Sound specific data */
|
||||
CK_STATESAVE_SOUNDALL = 0x000FFFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
Wave Sound
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_WAVSOUND : CKDWORD {
|
||||
CK_STATESAVE_WAVSOUNDFILE = 0x00100000, /**< Save sound filename */
|
||||
CK_STATESAVE_WAVSOUNDDATA = 0x00200000, /**< Obsolete */
|
||||
CK_STATESAVE_WAVSOUNDDATA2 = 0x00400000, /**< Save sound properties (3D/2D,pitch,gain,streaming,loop,etc..) */
|
||||
CK_STATESAVE_WAVSOUNDDURATION = 0x00800000, /**< Sound Length (in case it cannot be calculated latter) */
|
||||
CK_STATESAVE_WAVSOUNDRESERVED4 = 0x01000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_WAVSOUNDRESERVED5 = 0x02000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_WAVSOUNDRESERVED6 = 0x04000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_WAVSOUNDRESERVED7 = 0x08000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_WAVSOUNDONLY = 0x0FF00000, /**< Save All datas for sub-classes */
|
||||
CK_STATESAVE_WAVSOUNDALL = 0x0FFFFFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
Wave Sound
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_MIDISOUND : CKDWORD {
|
||||
CK_STATESAVE_MIDISOUNDFILE = 0x00100000, /**< Save sound filename */
|
||||
CK_STATESAVE_MIDISOUNDDATA = 0x00200000, /**< Save midi data */
|
||||
CK_STATESAVE_MIDISOUNDRESERVED2 = 0x00400000, /**< Reserved for future use */
|
||||
CK_STATESAVE_MIDISOUNDRESERVED3 = 0x00800000, /**< Reserved for future use */
|
||||
CK_STATESAVE_MIDISOUNDRESERVED4 = 0x01000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_MIDISOUNDRESERVED5 = 0x02000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_MIDISOUNDRESERVED6 = 0x04000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_MIDISOUNDRESERVED7 = 0x08000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_MIDISOUNDONLY = 0x0FF00000,
|
||||
CK_STATESAVE_MIDISOUNDALL = 0x0FFFFFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
Place
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_PLACE : CKDWORD {
|
||||
CK_STATESAVE_PLACEPORTALS = 0x00001000, /**< Save level using the place */
|
||||
CK_STATESAVE_PLACECAMERA = 0x00002000, /**< Save attached camera */
|
||||
CK_STATESAVE_PLACEREFERENCES = 0x00004000, /**< Save list of objects in the place */
|
||||
CK_STATESAVE_PLACELEVEL = 0x00008000, /**< Save level using the place */
|
||||
CK_STATESAVE_PLACEALL = 0x0000FFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
Level CKSaveObjectState will not save any data
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_LEVEL : CKDWORD {
|
||||
CK_STATESAVE_LEVELRESERVED0 = 0x00001000, /**< Reserved for future use */
|
||||
CK_STATESAVE_LEVELINACTIVEMAN = 0x00002000, /**< Reserved for future use */
|
||||
CK_STATESAVE_LEVELDUPLICATEMAN = 0x00004000, /**< Reserved for future use */
|
||||
CK_STATESAVE_LEVELDEFAULTDATA = 0x20000000, /**< Save Places,Scenes and Objects */
|
||||
CK_STATESAVE_LEVELSCENE = 0x80000000, /**< Default and active scene */
|
||||
CK_STATESAVE_LEVELALL = 0xFFFFFFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
GROUP
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_GROUP : CKDWORD {
|
||||
CK_STATESAVE_GROUPDATA = 0x00001000, /**< Save list of objects in the group */
|
||||
CK_STATESAVE_GROUPRESERVED1 = 0x00002000, /**< Reserved for future use */
|
||||
CK_STATESAVE_GROUPRESERVED2 = 0x00004000, /**< Reserved for future use */
|
||||
CK_STATESAVE_GROUPRESERVED3 = 0x00008000, /**< Reserved for future use */
|
||||
CK_STATESAVE_GROUPRESERVED4 = 0x00010000, /**< Reserved for future use */
|
||||
CK_STATESAVE_GROUPRESERVED5 = 0x00020000, /**< Reserved for future use */
|
||||
CK_STATESAVE_GROUPRESERVED6 = 0x00040000, /**< Reserved for future use */
|
||||
CK_STATESAVE_GROUPRESERVED7 = 0x00080000, /**< Reserved for future use */
|
||||
CK_STATESAVE_GROUPALL = 0x000FFFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
MESH CKSaveOjectSave will save all data and does not take flags into account
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_MESH : CKDWORD {
|
||||
CK_STATESAVE_MESHRESERVED0 = 0x00001000, /**< Reserved for future use */
|
||||
CK_STATESAVE_MESHFLAGS = 0x00002000, /**< Save flags */
|
||||
CK_STATESAVE_MESHCHANNELS = 0x00004000, /**< Save Channels */
|
||||
CK_STATESAVE_MESHFACECHANMASK = 0x00008000, /**< Save face channel Mask */
|
||||
CK_STATESAVE_MESHFACES = 0x00010000, /**< Save face data */
|
||||
CK_STATESAVE_MESHVERTICES = 0x00020000, /**< Save geometry */
|
||||
CK_STATESAVE_MESHLINES = 0x00040000, /**< Save line data */
|
||||
CK_STATESAVE_MESHWEIGHTS = 0x00080000, /**< Save Vertex Weight info */
|
||||
CK_STATESAVE_MESHMATERIALS = 0x00100000, /**< Reserved for future use */
|
||||
CK_STATESAVE_MESHRESERVED1 = 0x00200000, /**< Reserved for future use */
|
||||
CK_STATESAVE_MESHRESERVED2 = 0x00400000, /**< Reserved for future use */
|
||||
CK_STATESAVE_PROGRESSIVEMESH = 0x00800000, /**< Save All datas for sub-classes */
|
||||
CK_STATESAVE_MESHONLY = 0x00FFF000, /**< Save All datas for sub-classes */
|
||||
CK_STATESAVE_MESHALL = 0x00FFFFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
PATCH MESH CKSaveOjectSave will save all data and does not take flags into account
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_PATCHMESH : CKDWORD {
|
||||
CK_STATESAVE_PATCHMESHDATA = 0x00800000, /**< Obsolete */
|
||||
CK_STATESAVE_PATCHMESHDATA2 = 0x01000000, /**< Obsolete */
|
||||
CK_STATESAVE_PATCHMESHSMOOTH = 0x02000000, /**< Obsolete */
|
||||
CK_STATESAVE_PATCHMESHMATERIALS = 0x04000000, /**< Obsolete */
|
||||
CK_STATESAVE_PATCHMESHDATA3 = 0x08000000, /**< Save Patch Data */
|
||||
CK_STATESAVE_PATCHMESHONLY = 0x0FF00000, /**< Save All datas for sub-classes */
|
||||
CK_STATESAVE_PATCHMESHALL = 0x0FFFFFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
Material
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_MATERIAL : CKDWORD {
|
||||
CK_STATESAVE_MATDATA = 0x00001000, /**< Save colors,blending modes,shade modes,fill modes etc... */
|
||||
CK_STATESAVE_MATDATA2 = 0x00002000, /**< Additional texture objects... */
|
||||
CK_STATESAVE_MATDATA3 = 0x00004000, /**< Effect Alone */
|
||||
CK_STATESAVE_MATDATA4 = 0x00008000, /**< none */
|
||||
CK_STATESAVE_MATDATA5 = 0x00010000, /**< Effect + parameter */
|
||||
CK_STATESAVE_MATRESERVED5 = 0x00020000, /**< Reserved for future use */
|
||||
CK_STATESAVE_MATRESERVED6 = 0x00040000, /**< Reserved for future use */
|
||||
CK_STATESAVE_MATRESERVED7 = 0x00080000, /**< Reserved for future use */
|
||||
CK_STATESAVE_MATERIALONLY = 0x000FF000, /**< Save All datas for sub-classes */
|
||||
CK_STATESAVE_MATERIALALL = 0x0FFFFFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
Texture CKSaveOjectSave will save all relevant data and does not take flags into account
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_TEXTURE : CKDWORD {
|
||||
CK_STATESAVE_TEXAVIFILENAME = 0x00001000, /**< Save movie file name */
|
||||
CK_STATESAVE_TEXCURRENTIMAGE = 0x00002000, /**< Save current slot */
|
||||
CK_STATESAVE_TEXBITMAPS = 0x00004000, /**< Obsolete */
|
||||
CK_STATESAVE_TEXTRANSPARENT = 0x00008000, /**< Save transparency data */
|
||||
CK_STATESAVE_TEXFILENAMES = 0x00010000, /**< Save texture slot filenames */
|
||||
CK_STATESAVE_TEXCOMPRESSED = 0x00020000, /**< Save raw texture data */
|
||||
CK_STATESAVE_TEXVIDEOFORMAT = 0x00040000, /**< Save chosen video format */
|
||||
CK_STATESAVE_TEXSAVEFORMAT = 0x00080000, /**< Save chosen save format */
|
||||
CK_STATESAVE_TEXREADER = 0x00100000, /**< Save texture data using a specific BitmapReader */
|
||||
CK_STATESAVE_PICKTHRESHOLD = 0x00200000, /**< Save pick threshold */
|
||||
CK_STATESAVE_USERMIPMAP = 0x00400000, /**< User mipmap levels */
|
||||
CK_STATESAVE_TEXSYSTEMCACHING = 0x00800000, /**< System Memory Caching */
|
||||
CK_STATESAVE_OLDTEXONLY = 0x002FF000, /**< Kept for compatibility */
|
||||
CK_STATESAVE_TEXONLY = 0x00FFF000, /**< Save Only Texture Data (Dot NOT MODIFY ! Texture loading/saving relies on this value) */
|
||||
CK_STATESAVE_TEXALL = 0x002FFFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
2d CURVE && 2d Curve Point
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_2DCURVE : CKDWORD {
|
||||
CK_STATESAVE_2DCURVERESERVED0 = 0x00000010, /**< Reserved for future use */
|
||||
CK_STATESAVE_2DCURVERESERVED4 = 0x00000020, /**< Reserved for future use */
|
||||
CK_STATESAVE_2DCURVEFITCOEFF = 0x00000040, /**< Obsolete */
|
||||
CK_STATESAVE_2DCURVECONTROLPOINT = 0x00000080, /**< Obsolete */
|
||||
CK_STATESAVE_2DCURVENEWDATA = 0x00000100, /**< Save All relevant data */
|
||||
CK_STATESAVE_2DCURVERESERVED2 = 0x00000200, /**< Obsolete */
|
||||
CK_STATESAVE_2DCURVERESERVED3 = 0x00000400, /**< Obsolete */
|
||||
CK_STATESAVE_2DCURVEPOINTTCB = 0x00000800, /**< Obsolete */
|
||||
CK_STATESAVE_2DCURVEPOINTTANGENTS = 0x00001000, /**< Obsolete */
|
||||
CK_STATESAVE_2DCURVEPOINT2DCURVEPOS = 0x00002000, /**< Obsolete */
|
||||
CK_STATESAVE_2DCURVEPOINTDEFAULTDATA = 0x00004000, /**< Obsolete */
|
||||
CK_STATESAVE_2DCURVEPOINTNEWDATA = 0x00008000, /**< Save All relevant data */
|
||||
CK_STATESAVE_2DCURVEPOINTRESERVED1 = 0x00010000, /**< Reserved for future use */
|
||||
CK_STATESAVE_2DCURVEPOINTRESERVED2 = 0x00020000, /**< Reserved for future use */
|
||||
CK_STATESAVE_2DCURVESAVEPOINTS = 0x0003F800, /**< Obsolete */
|
||||
CK_STATESAVE_2DCURVEALL = 0x0007FFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
Kinematic Chain
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_KINEMATICCHAIN : CKDWORD {
|
||||
CK_STATESAVE_KINEMATICCHAINDATA = 0x00000010, /**< Save chain data */
|
||||
CK_STATESAVE_KINEMATICCHAINRESERVED1 = 0x00000020, /**< Reserved for future use */
|
||||
CK_STATESAVE_KINEMATICCHAINRESERVED2 = 0x00000040, /**< Reserved for future use */
|
||||
CK_STATESAVE_KINEMATICCHAINRESERVED3 = 0x00000080, /**< Reserved for future use */
|
||||
CK_STATESAVE_KINEMATICCHAINALL = 0x000000FF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
Animation
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_ANIMATION : CKDWORD {
|
||||
CK_STATESAVE_ANIMATIONDATA = 0x00000010, /**< Save Flags & Framerate data */
|
||||
CK_STATESAVE_ANIMATIONRESERVED1 = 0x00000020, /**< Reserved for future use */
|
||||
CK_STATESAVE_ANIMATIONLENGTH = 0x00000040, /**< Save animation Length */
|
||||
CK_STATESAVE_ANIMATIONBODYPARTS = 0x00000080, /**< Save root & list of bodypart */
|
||||
CK_STATESAVE_ANIMATIONCHARACTER = 0x00000100, /**< Save character */
|
||||
CK_STATESAVE_ANIMATIONCURRENTSTEP = 0x00000200, /**< Save current step */
|
||||
CK_STATESAVE_ANIMATIONRESERVED5 = 0x00000400, /**< Reserved for future use */
|
||||
CK_STATESAVE_ANIMATIONRESERVED6 = 0x00000800, /**< Reserved for future use */
|
||||
CK_STATESAVE_ANIMATIONALL = 0x0FFFFFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
Keyed Anim
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_KEYEDANIMATION : CKDWORD {
|
||||
CK_STATESAVE_KEYEDANIMANIMLIST = 0x00001000, /**< Save list of object animations */
|
||||
CK_STATESAVE_KEYEDANIMLENGTH = 0x00002000, /**< Obsolete */
|
||||
CK_STATESAVE_KEYEDANIMPOSKEYS = 0x00004000, /**< Obsolete */
|
||||
CK_STATESAVE_KEYEDANIMROTKEYS = 0x00008000, /**< Obsolete */
|
||||
CK_STATESAVE_KEYEDANIMMORPHKEYS = 0x00010000, /**< Obsolete */
|
||||
CK_STATESAVE_KEYEDANIMSCLKEYS = 0x00020000, /**< Obsolete */
|
||||
CK_STATESAVE_KEYEDANIMFLAGS = 0x00040000, /**< Obsolete */
|
||||
CK_STATESAVE_KEYEDANIMENTITY = 0x00080000, /**< Obsolete */
|
||||
CK_STATESAVE_KEYEDANIMMERGE = 0x00100000, /**< Save merged animations */
|
||||
CK_STATESAVE_KEYEDANIMSUBANIMS = 0x00200000, /**< Save object animations data (using same flags than CKSaveObjectState) */
|
||||
CK_STATESAVE_KEYEDANIMRESERVED0 = 0x00400000, /**< Reserved for future use */
|
||||
CK_STATESAVE_KEYEDANIMRESERVED1 = 0x00800000, /**< Reserved for future use */
|
||||
CK_STATESAVE_KEYEDANIMRESERVED2 = 0x01000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_KEYEDANIMRESERVED3 = 0x02000000, /**< Reserved for future use */
|
||||
};
|
||||
/**
|
||||
Object Animation CKSaveOjectSave will save all relevant data and does not take flags into account
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_OBJECTANIMATION : CKDWORD {
|
||||
CK_STATESAVE_OBJANIMNEWDATA = 0x00001000, /**< Save all relevant data */
|
||||
CK_STATESAVE_OBJANIMLENGTH = 0x00002000, /**< Not used */
|
||||
CK_STATESAVE_OBJANIMPOSKEYS = 0x00004000, /**< Not used */
|
||||
CK_STATESAVE_OBJANIMROTKEYS = 0x00008000, /**< Not used */
|
||||
CK_STATESAVE_OBJANIMMORPHKEYS = 0x00010000, /**< Not used */
|
||||
CK_STATESAVE_OBJANIMSCLKEYS = 0x00020000, /**< Not used */
|
||||
CK_STATESAVE_OBJANIMFLAGS = 0x00040000, /**< Not used */
|
||||
CK_STATESAVE_OBJANIMENTITY = 0x00080000, /**< Not used */
|
||||
CK_STATESAVE_OBJANIMMERGE = 0x00100000, /**< Not used */
|
||||
CK_STATESAVE_OBJANIMMORPHKEYS2 = 0x00200000, /**< Not used */
|
||||
CK_STATESAVE_OBJANIMNEWSAVE1 = 0x00400000, /**< Not used */
|
||||
CK_STATESAVE_OBJANIMMORPHNORMALS = 0x00800000, /**< Not used (Virtools 1.1) */
|
||||
CK_STATESAVE_OBJANIMMORPHCOMP = 0x01000000, /**< Not used (Virtools 1.1) */
|
||||
CK_STATESAVE_OBJANIMSHARED = 0x02000000, /**< Save Data for a shared animation */
|
||||
CK_STATESAVE_OBJANIMCONTROLLERS = 0x04000000, /**< (Virtools 1.5) Save All Controller information */
|
||||
CK_STATESAVE_OBJANIMONLY = 0x07FFF000,
|
||||
CK_STATESAVE_OBJANIMALL = 0x07FFFFFF,
|
||||
CK_STATESAVE_KEYEDANIMONLY = 0x03FFF000, /**< Save All datas for sub-classes */
|
||||
CK_STATESAVE_KEYEDANIMALL = 0x03FFFFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
IK Animation
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_IKANIMATION : CKDWORD {
|
||||
CK_STATESAVE_IKANIMATIONDATA = 0x00001000, /**< Save IK data */
|
||||
CK_STATESAVE_IKANIMATIONRESERVED2 = 0x00002000, /**< Reserved for future use */
|
||||
CK_STATESAVE_IKANIMATIONRESERVED3 = 0x00004000, /**< Reserved for future use */
|
||||
CK_STATESAVE_IKANIMATIONRESERVED4 = 0x00008000, /**< Reserved for future use */
|
||||
CK_STATESAVE_IKANIMATIONRESERVED5 = 0x00010000, /**< Reserved for future use */
|
||||
CK_STATESAVE_IKANIMATIONRESERVED6 = 0x00020000, /**< Reserved for future use */
|
||||
CK_STATESAVE_IKANIMATIONRESERVED7 = 0x00040000, /**< Reserved for future use */
|
||||
CK_STATESAVE_IKANIMATIONRESERVED8 = 0x00100000, /**< Reserved for future use */
|
||||
CK_STATESAVE_IKANIMATIONRESERVED9 = 0x00200000, /**< Reserved for future use */
|
||||
CK_STATESAVE_IKANIMATIONALL = 0x003FFFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
BehaviorLink
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_BEHAV_LINK : CKDWORD {
|
||||
CK_STATESAVE_BEHAV_LINK_CURDELAY = 0x00000004, /**< Obsolete */
|
||||
CK_STATESAVE_BEHAV_LINK_IOS = 0x00000008, /**< Obsolete */
|
||||
CK_STATESAVE_BEHAV_LINK_DELAY = 0x00000010, /**< Obsolete */
|
||||
CK_STATESAVE_BEHAV_LINK_NEWDATA = 0x00000020, /**< Save all relevant data (In,Out,Activation delay) */
|
||||
CK_STATESAVE_BEHAV_LINKRESERVED5 = 0x00000040, /**< Reserved for future use */
|
||||
CK_STATESAVE_BEHAV_LINKRESERVED6 = 0x00000080, /**< Reserved for future use */
|
||||
CK_STATESAVE_BEHAV_LINKONLY = 0x000000F0, /**< */
|
||||
CK_STATESAVE_BEHAV_LINKALL = 0x000000FF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
BehaviorIO
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_BEHAV_IO : CKDWORD {
|
||||
CK_STATESAVE_BEHAV_IOFLAGS = 0x00000008, /**< Save IO flags */
|
||||
CK_STATESAVE_BEHAV_IORESERVED3 = 0x00000010, /**< Reserved for future use */
|
||||
CK_STATESAVE_BEHAV_IORESERVED4 = 0x00000020, /**< Reserved for future use */
|
||||
CK_STATESAVE_BEHAV_IORESERVED5 = 0x00000040, /**< Reserved for future use */
|
||||
CK_STATESAVE_BEHAV_IORESERVED6 = 0x00000080, /**< Reserved for future use */
|
||||
CK_STATESAVE_BEHAVIOONLY = 0x000000F0, /**< */
|
||||
CK_STATESAVE_BEHAVIOALL = 0x000000FF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
BehaviorPrototype
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_PROTOTYPE : CKDWORD {
|
||||
CK_STATESAVE_PROTORESERVED0 = 0x00000010, /**< Reserved for future use */
|
||||
CK_STATESAVE_PROTORESERVED1 = 0x00000020, /**< Reserved for future use */
|
||||
CK_STATESAVE_PROTOFLAGS = 0x00000040, /**< Save Flags */
|
||||
CK_STATESAVE_PROTOSUBPROTOS = 0x00000080, /**< Save sub prototypes */
|
||||
CK_STATESAVE_PROTOLINKS = 0x00000100, /**< Save links */
|
||||
CK_STATESAVE_PROTOBEHAVFLAG = 0x00000200, /**< Save behavior flags */
|
||||
CK_STATESAVE_PROTOGUID = 0x00000400, /**< Save GUID */
|
||||
CK_STATESAVE_PROTOINPUTS = 0x00000800, /**< Save inputs */
|
||||
CK_STATESAVE_PROTOOUTPUTS = 0x00001000, /**< Save outputs */
|
||||
CK_STATESAVE_PROTOINPARAMS = 0x00002000, /**< Save input parameters */
|
||||
CK_STATESAVE_PROTOOUTPARAMS = 0x00004000, /**< Save output parameters */
|
||||
CK_STATESAVE_PROTOLOCALPARAMS = 0x00008000, /**< Save local parameters */
|
||||
CK_STATESAVE_PROTOOPERATIONS = 0x00010000, /**< Save parameter operations */
|
||||
CK_STATESAVE_PROTOPARAMETERLINKS = 0x00020000, /**< Save parameter links */
|
||||
CK_STATESAVE_PROTOAPPLYTO = 0x00040000, /**< Save ClassID of object to which it applies */
|
||||
CK_STATESAVE_PROTORESERVED14 = 0x00080000, /**< Reserved for future use */
|
||||
CK_STATESAVE_PROTOALL = 0x000FFFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
Behavior
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_BEHAVIOR : CKDWORD {
|
||||
CK_STATESAVE_BEHAVIORRESERVED0 = 0x00000010, /**< Reserved for internal use */
|
||||
CK_STATESAVE_BEHAVIORNEWDATA = 0x00000020, /**< not used */
|
||||
CK_STATESAVE_BEHAVIORFLAGS = 0x00000040, /**< not used */
|
||||
CK_STATESAVE_BEHAVIORCOMPATIBLECID = 0x00000080, /**< not used */
|
||||
CK_STATESAVE_BEHAVIORSUBBEHAV = 0x00000100, /**< Save Sub-Behaviors */
|
||||
CK_STATESAVE_BEHAVIORINPARAMS = 0x00000200, /**< not used */
|
||||
CK_STATESAVE_BEHAVIOROUTPARAMS = 0x00000400, /**< not used */
|
||||
CK_STATESAVE_BEHAVIORINPUTS = 0x00000800, /**< not used */
|
||||
CK_STATESAVE_BEHAVIOROUTPUTS = 0x00001000, /**< not used */
|
||||
CK_STATESAVE_BEHAVIORINFO = 0x00002000, /**< not used */
|
||||
CK_STATESAVE_BEHAVIOROPERATIONS = 0x00004000, /**< not used */
|
||||
CK_STATESAVE_BEHAVIORTYPE = 0x00008000, /**< not used */
|
||||
CK_STATESAVE_BEHAVIOROWNER = 0x00010000, /**< not used */
|
||||
CK_STATESAVE_BEHAVIORLOCALPARAMS = 0x00020000, /**< Save local parameters */
|
||||
CK_STATESAVE_BEHAVIORPROTOGUID = 0x00040000, /**< not used */
|
||||
CK_STATESAVE_BEHAVIORSUBLINKS = 0x00080000, /**< not used */
|
||||
CK_STATESAVE_BEHAVIORACTIVESUBLINKS = 0x00100000, /**< not used */
|
||||
CK_STATESAVE_BEHAVIORSINGLEACTIVITY = 0x00200000, /**< SINGLE ACTIVITY */
|
||||
CK_STATESAVE_BEHAVIORSCRIPTDATA = 0x00400000, /**< not used */
|
||||
CK_STATESAVE_BEHAVIORPRIORITY = 0x00800000, /**< not used */
|
||||
CK_STATESAVE_BEHAVIORTARGET = 0x01000000, /**< not used */
|
||||
CK_STATESAVE_BEHAVIORONLY = 0x01FFFFF0,
|
||||
CK_STATESAVE_BEHAVIORALL = 0x01FFFFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
SCENE CKSaveOjectSave will save all relevant data and does not take flags into account
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_SCENE : CKDWORD {
|
||||
CK_STATESAVE_SCENERESERVED0 = 0x00001000, /**< Reserved for future use */
|
||||
CK_STATESAVE_SCENERESERVED8 = 0x00002000, /**< Reserved for future use */
|
||||
CK_STATESAVE_SCENEFLAGS = 0x00004000,
|
||||
CK_STATESAVE_SCENELEVEL = 0x00008000,
|
||||
CK_STATESAVE_SCENEOBJECTS = 0x00010000,
|
||||
CK_STATESAVE_SCENENEWDATA = 0x00020000, /**< every object description and initial conditions */
|
||||
CK_STATESAVE_SCENELAUNCHED = 0x00040000, /**< Scene was already launched once */
|
||||
CK_STATESAVE_SCENERENDERSETTINGS = 0x00080000, /**< Background Color, Fog Color etc.. */
|
||||
CK_STATESAVE_SCENERESERVED1 = 0x00100000, /**< Reserved for future use */
|
||||
CK_STATESAVE_SCENERESERVED2 = 0x00200000, /**< Reserved for future use */
|
||||
CK_STATESAVE_SCENERESERVED3 = 0x00400000, /**< Reserved for future use */
|
||||
CK_STATESAVE_SCENERESERVED4 = 0x00800000, /**< Reserved for future use */
|
||||
CK_STATESAVE_SCENERESERVED5 = 0x01000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_SCENERESERVED12 = 0x02000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_SCENERESERVED13 = 0x04000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_SCENERESERVED14 = 0x08000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_SCENEALL = 0x0FFFFFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
ParameterIn
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_PARAMETERIN : CKDWORD {
|
||||
CK_STATESAVE_PARAMETERIN_RESERVED4 = 0x00000010, /**< Reserved for future use */
|
||||
CK_STATESAVE_PARAMETERIN_RESERVED0 = 0x00000020, /**< Reserved for future use */
|
||||
CK_STATESAVE_PARAMETERIN_RESERVED1 = 0x00000040, /**< Reserved for future use */
|
||||
CK_STATESAVE_PARAMETERIN_OWNER = 0x00000080, /**< Obsolete */
|
||||
CK_STATESAVE_PARAMETERIN_INSHARED = 0x00000100, /**< Obsolete */
|
||||
CK_STATESAVE_PARAMETERIN_OUTSOURCE = 0x00000200, /**< Obsolete */
|
||||
CK_STATESAVE_PARAMETERIN_DEFAULTDATA = 0x00000400, /**< Obsolete */
|
||||
CK_STATESAVE_PARAMETERIN_DATASHARED = 0x00000800, /**< Save reference to shared inparameter */
|
||||
CK_STATESAVE_PARAMETERIN_DATASOURCE = 0x00001000, /**< Save reference to source outparameter */
|
||||
CK_STATESAVE_PARAMETERIN_DISABLED = 0x00002000, /**< The parameter was disabled */
|
||||
CK_STATESAVE_PARAMETERIN_ALL = 0x0000FFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
ParameterLocal et ParameterOut
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_PARAMETEROUT : CKDWORD {
|
||||
CK_STATESAVE_PARAMETEROUT_RESERVED0 = 0x00000010, /**< Reserved for future use */
|
||||
CK_STATESAVE_PARAMETEROUT_DESTINATIONS = 0x00000020, /**< Save destinations */
|
||||
CK_STATESAVE_PARAMETEROUT_VAL = 0x00000040, /**< Save value */
|
||||
CK_STATESAVE_PARAMETEROUT_OWNER = 0x00000080, /**< Save Owner */
|
||||
CK_STATESAVE_PARAMETEROUT_MYSELF = 0x00000200, /**< */
|
||||
CK_STATESAVE_PARAMETEROUT_ISSETTING = 0x00000400, /**< Reserved for future use */
|
||||
CK_STATESAVE_PARAMETEROUT_ALL = 0x0000FFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
Parameter Operation
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_OPERATION : CKDWORD {
|
||||
CK_STATESAVE_OPERATIONRESERVED0 = 0x00000010, /**< Reserved for future use */
|
||||
CK_STATESAVE_OPERATIONRESERVED1 = 0x00000020, /**< Reserved for future use */
|
||||
CK_STATESAVE_OPERATIONINPUTS = 0x00000040,
|
||||
CK_STATESAVE_OPERATIONOUTPUT = 0x00000080,
|
||||
CK_STATESAVE_OPERATIONOP = 0x00000100,
|
||||
CK_STATESAVE_OPERATIONDEFAULTDATA = 0x00000200,
|
||||
CK_STATESAVE_OPERATIONNEWDATA = 0x00000400,
|
||||
CK_STATESAVE_OPERATIONALL = 0x000007FF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
Synchro Object CKSaveOjectSave will save all relevant data and does not take flags into account
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_SYNCHRO : CKDWORD {
|
||||
CK_STATESAVE_SYNCHRODATA = 0x00000010, /**< Save data */
|
||||
CK_STATESAVE_SYNCHRORESERVED0 = 0x00000040, /**< Reserved for future use */
|
||||
CK_STATESAVE_SYNCHRORESERVED1 = 0x00000080, /**< Reserved for future use */
|
||||
CK_STATESAVE_SYNCHRORESERVED2 = 0x00000100, /**< Reserved for future use */
|
||||
CK_STATESAVE_SYNCHRORESERVED3 = 0x00000200, /**< Reserved for future use */
|
||||
CK_STATESAVE_SYNCHRONALL = 0x000003FF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
Grid
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_GRID : CKDWORD {
|
||||
CK_STATESAVE_GRIDDATA = 0x00400000, /**< Save Grid Data */
|
||||
CK_STATESAVE_GRIDRESERVED0 = 0x00800000, /**< Reserved for future use */
|
||||
CK_STATESAVE_GRIDRESERVED1 = 0x01000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_GRIDRESERVED2 = 0x02000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_GRIDRESERVED3 = 0x04000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_GRIDRESERVED4 = 0x08000000, /**< Reserved for future use */
|
||||
CK_STATESAVE_GRIDONLY = 0x0FC00000, /**< */
|
||||
CK_STATESAVE_GRIDALL = 0x0FFFFFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
Layer (For Grids)
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_LAYER : CKDWORD {
|
||||
CK_STATESAVE_LAYERDATA = 0x00000010, /**< Save Layer Data */
|
||||
CK_STATESAVE_LAYERRESERVED0 = 0x00800020, /**< Reserved for future use */
|
||||
CK_STATESAVE_LAYERRESERVED1 = 0x00000040, /**< Reserved for future use */
|
||||
CK_STATESAVE_LAYERRESERVED2 = 0x00000080, /**< Reserved for future use */
|
||||
CK_STATESAVE_LAYERRESERVED3 = 0x00000100, /**< Reserved for future use */
|
||||
CK_STATESAVE_LAYERRESERVED4 = 0x00000200, /**< Reserved for future use */
|
||||
CK_STATESAVE_LAYERONLY = 0x000003F0, /**< */
|
||||
CK_STATESAVE_LAYERALL = 0x000003FF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
DataArray CKSaveOjectSave will save all relevant data and does not take flags into account
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_DATAARRAY : CKDWORD {
|
||||
CK_STATESAVE_DATAARRAYFORMAT = 0x00001000, /**< Save format */
|
||||
CK_STATESAVE_DATAARRAYDATA = 0x00002000, /**< Save array data */
|
||||
CK_STATESAVE_DATAARRAYMEMBERS = 0x00004000, /**< Save members */
|
||||
CK_STATESAVE_DATAARRAYALL = 0x0000FFFF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
/**
|
||||
SceneObjectDesc
|
||||
*/
|
||||
enum class CK_STATESAVEFLAGS_SCENEOBJECTDESC : CKDWORD {
|
||||
CK_STATESAVE_SCENEOBJECTDESC = 0x00000010,
|
||||
CK_STATESAVE_SCENEOBJECTRES1 = 0x00000020, /**< Reserved for future use */
|
||||
CK_STATESAVE_SCENEOBJECTRES2 = 0x00000040, /**< Reserved for future use */
|
||||
CK_STATESAVE_SCENEOBJECTRES3 = 0x00000080, /**< Reserved for future use */
|
||||
CK_STATESAVE_SCENEOBJECTDESCALL = 0x000000FF, /**< Save All datas for sub-classes */
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@@ -1,670 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../VTInternal.hpp"
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
|
||||
namespace LibCmo::CK2 {
|
||||
|
||||
/**
|
||||
* @remark
|
||||
* + We make sure m_BindContext and m_BindFile always are not nullptr. So some code of BindFile check and write different data struct has been removed.
|
||||
* + Calling StartRead multiple times is illegal.
|
||||
* - The solution is that use StartRead and StopRead to warp the real CKStateChunk consumer. And just calling read functions in real consumer directly.
|
||||
* - See CKFileReader for more infomation.
|
||||
* + Same as StartRead, calling StartWrite multiple times also is illegal. We also remove CKStateChunk merge function, AddChunkAndDelete and AddChunk.
|
||||
* - The solution is same as StartRead solution. Use StartWrite and StopWrite warp the real CKStateChunk writer. Call write function in consumer directly.
|
||||
* - Every inherited CKObject::Save must call SetClassId at the end of function if they have data to write.
|
||||
* - See CKFileWrite for more infomation.
|
||||
*/
|
||||
class CKStateChunk {
|
||||
public:
|
||||
CKStateChunk(CKFileVisitor* visitor, CKContext* ctx);
|
||||
CKStateChunk(const CKStateChunk&);
|
||||
CKStateChunk(CKStateChunk&&);
|
||||
CKStateChunk& operator=(const CKStateChunk&);
|
||||
CKStateChunk& operator=(CKStateChunk&&);
|
||||
~CKStateChunk();
|
||||
|
||||
#pragma region Self Used Data Struct
|
||||
|
||||
public:
|
||||
struct ProfileStateChunk_t {
|
||||
CK_CLASSID m_ClassId;
|
||||
CKDWORD m_DataDwSize;
|
||||
CKDWORD* m_pData;
|
||||
|
||||
CK_STATECHUNK_DATAVERSION m_DataVersion;
|
||||
CK_STATECHUNK_CHUNKVERSION m_ChunkVersion;
|
||||
|
||||
size_t m_ObjectListSize, m_ChunkListSize, m_ManagerListSize;
|
||||
|
||||
CKFileVisitor* m_BindFile;
|
||||
CKContext* m_BindContext;
|
||||
};
|
||||
struct ProfileIdentifier_t {
|
||||
CKDWORD m_Identifier;
|
||||
void* m_DataPtr;
|
||||
CKDWORD m_AreaSize;
|
||||
};
|
||||
|
||||
class LockedReadBufferDeleter {
|
||||
public:
|
||||
LockedReadBufferDeleter() : m_Host(nullptr), m_ConsumedSize(0) {}
|
||||
LockedReadBufferDeleter(CKStateChunk* host, CKDWORD init_size) :
|
||||
m_Host(host), m_ConsumedSize(init_size) {}
|
||||
YYCC_DEF_CLS_COPY_MOVE(LockedReadBufferDeleter);
|
||||
|
||||
void operator()(const void* /*buf*/);
|
||||
void SetConsumedSize(CKDWORD newsize);
|
||||
private:
|
||||
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) {}
|
||||
YYCC_DEF_CLS_COPY_MOVE(LockedWriteBufferDeleter);
|
||||
|
||||
void operator()(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) {}
|
||||
YYCC_DEF_CLS_COPY_MOVE(BufferDeleter);
|
||||
|
||||
void operator()(const void* buf);
|
||||
CKDWORD GetBufferSize() const;
|
||||
private:
|
||||
CKStateChunk* m_Host;
|
||||
CKDWORD m_BufSize;
|
||||
};
|
||||
|
||||
using LockedReadBuffer_t = std::unique_ptr<const void, LockedReadBufferDeleter>;
|
||||
using LockedWriteBuffer_t = std::unique_ptr<void, LockedWriteBufferDeleter>;
|
||||
using Buffer_t = std::unique_ptr<void, BufferDeleter>;
|
||||
|
||||
private:
|
||||
enum class CKStateChunkStatus : CKDWORD {
|
||||
IDLE,
|
||||
READ,
|
||||
WRITE
|
||||
};
|
||||
struct ChunkParser {
|
||||
CKStateChunkStatus m_Status;
|
||||
CKDWORD m_CurrentPos;
|
||||
/**
|
||||
* @brief The runtime size of CKStateChunk internal buf in DWORD unit.
|
||||
* Usually be used and changed when resizing buffer in writing mode.
|
||||
*/
|
||||
CKDWORD m_DataSize;
|
||||
CKDWORD m_PrevIdentifierPos;
|
||||
};
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Member Decl
|
||||
|
||||
CK_CLASSID m_ClassId;
|
||||
CKDWORD m_DataDwSize;
|
||||
CKDWORD* m_pData;
|
||||
|
||||
CK_STATECHUNK_DATAVERSION m_DataVersion;
|
||||
CK_STATECHUNK_CHUNKVERSION m_ChunkVersion;
|
||||
|
||||
ChunkParser m_Parser;
|
||||
|
||||
XContainer::XArray<CKDWORD> m_ObjectList;
|
||||
XContainer::XArray<CKDWORD> m_ChunkList;
|
||||
XContainer::XArray<CKDWORD> m_ManagerList;
|
||||
|
||||
CKFileVisitor* m_BindFile;
|
||||
CKContext* m_BindContext;
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Buffer Related
|
||||
|
||||
public:
|
||||
bool ConvertFromBuffer(const void* buf);
|
||||
CKDWORD ConvertToBuffer(void* buf);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Misc Functions
|
||||
|
||||
public:
|
||||
// ===== 2 Profiles Getter used by Unvirt =====
|
||||
|
||||
/**
|
||||
* @brief Get basic info of this CKStateChunk.
|
||||
* @return The struct describing this CKStateChunk.
|
||||
*/
|
||||
const ProfileStateChunk_t GetStateChunkProfile();
|
||||
/**
|
||||
* @brief Get all indentifier infos of this CKStateChunk,
|
||||
* including identifier self, data area size and address.
|
||||
* @return A arrary, each item describe a single identifier's info.
|
||||
* @remark The detail of implement can be seen in SeekIdentifierAndReturnSize()
|
||||
* @see SeekIdentifierAndReturnSize
|
||||
*/
|
||||
const XContainer::XArray<ProfileIdentifier_t> GetIdentifiersProfile();
|
||||
|
||||
/**
|
||||
* @brief Clear this CKStateChunk, restore it to original status for other using.
|
||||
*/
|
||||
void Clear();
|
||||
/**
|
||||
* @brief Get the size of data buffer in this CKStateChunk.
|
||||
* @return The data buffer's size in CKBYTE.
|
||||
*/
|
||||
CKDWORD GetDataSize() const;
|
||||
/**
|
||||
* @brief Get data version in this CKStateChunk.
|
||||
* @remark Data version is frequently used by calling of CKStateChunk to distinguish different data layout due to compatibility reason.
|
||||
* @return The data version.
|
||||
*/
|
||||
CK_STATECHUNK_DATAVERSION GetDataVersion() const;
|
||||
/**
|
||||
* @brief Set data version of this CKStateChunk.
|
||||
* @param version The set data version
|
||||
* @remark Frequently used when saving someing in CKStateChunk.
|
||||
*/
|
||||
void SetDataVersion(CK_STATECHUNK_DATAVERSION version);
|
||||
/**
|
||||
* @brief Get associated CK_CLASSID of this CKStateChunk
|
||||
* @return Associated CK_CLASSID
|
||||
*/
|
||||
CK_CLASSID GetClassId() const;
|
||||
/**
|
||||
* @brief Set associated CK_CLASSID of this CKStateChunk.
|
||||
* @param cid The set CK_CLASSID
|
||||
* @remark Frequently used at the end of CKObject::Save() override.
|
||||
*/
|
||||
void SetClassId(CK_CLASSID cid);
|
||||
|
||||
|
||||
bool Skip(CKDWORD DwordCount);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Convert byte based size to DWORD based size.
|
||||
*
|
||||
* Becase CKStateChunk use DWORD based buffer, so all data should be aligned to DWORD boundary.
|
||||
* This function can convert byte based size to DWORD based size while keeping its size aligned with DWORD boundary.
|
||||
* For example, caller want to allocate 3 bytes for data storing, this function will first align it to DWORD boundary, 4 bytes.
|
||||
* Then convert it in DWORD size, 1 DWORD.
|
||||
*
|
||||
* @param char_size[in] The size in byte unit.
|
||||
* @return The size in DWORD unit.
|
||||
*/
|
||||
CKDWORD GetCeilDwordSize(size_t char_size);
|
||||
bool ResizeBuffer(CKDWORD new_dwsize);
|
||||
/**
|
||||
* @brief Check whether there are enough buffer to read.
|
||||
*
|
||||
* This function will check whether current CKStateChunk is in read mode and
|
||||
* whether data area is enough to write.
|
||||
* However, it is different with EnsureReadSpace. If no space to write, this function will
|
||||
* try calling ResizeBuffer to get a enough buffer. Only when resize failed,
|
||||
* this function will return false.
|
||||
*
|
||||
* @param dwsize[in] Required buffer size in DWORD unit.
|
||||
* @return True if have enough space to write.
|
||||
* @see EnsureReadSpace
|
||||
*/
|
||||
bool EnsureWriteSpace(CKDWORD dwsize);
|
||||
/**
|
||||
* @brief Check whether there are enough buffer to read.
|
||||
*
|
||||
* This function will check whether current CKStateChunk is in read mode and
|
||||
* whether data area is enough to read.
|
||||
*
|
||||
* @param dword_required[in] Required buffer size in DWORD unit.
|
||||
* @return True if have enough space to read.
|
||||
* @see EnsureWriteSpace
|
||||
*/
|
||||
bool EnsureReadSpace(CKDWORD dword_required);
|
||||
|
||||
/**
|
||||
* @brief The helper writer of m_ObjectList, m_ManagerList and m_ChunkList.
|
||||
*
|
||||
* Add a position indicator into list which represent a single data.
|
||||
*
|
||||
* @param entry_ls[in] The list to be written.
|
||||
* @param pos[int] The written position data.
|
||||
* @remark:
|
||||
* + IntList::AddEntry() redirect to this.
|
||||
*/
|
||||
void AddEntry(XContainer::XArray<CKDWORD>& entry_ls, CKDWORD pos);
|
||||
/**
|
||||
* @brief The helper writer of m_ObjectList, m_ManagerList and m_ChunkList.
|
||||
*
|
||||
* Add a position indicator into list which represent a series of data.
|
||||
*
|
||||
* @param entry_ls[in] The list to be written.
|
||||
* @param pos[int] The written position data.
|
||||
* @remark:
|
||||
* + IntList::AddEntries() redirect to this.
|
||||
*/
|
||||
void AddEntries(XContainer::XArray<CKDWORD>& entry_ls, CKDWORD pos);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Read Function
|
||||
|
||||
public:
|
||||
void StartRead();
|
||||
void StopRead();
|
||||
|
||||
/* ========== Identifier Functions ==========*/
|
||||
|
||||
public:
|
||||
bool SeekIdentifierDword(CKDWORD identifier);
|
||||
bool SeekIdentifierDwordAndReturnSize(CKDWORD identifier, CKDWORD* out_size);
|
||||
template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
inline bool SeekIdentifier(TEnum enum_v) {
|
||||
return SeekIdentifierDword(static_cast<CKDWORD>(enum_v));
|
||||
}
|
||||
template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
inline bool SeekIdentifierAndReturnSize(TEnum enum_v, CKDWORD* out_size) {
|
||||
return SeekIdentifierDwordAndReturnSize(static_cast<CKDWORD>(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 LockedReadBuffer_t (actually is a std::unique_ptr with custom deleter)
|
||||
* @remark
|
||||
* + The return value is just a std::unique_ptr but its deleter have more features.
|
||||
* + If return value contained data is nullptr, it mean that this function is failed.
|
||||
* + If you only use the pointer provided by the return value, the final moving forward byte count is the value passed in this function.
|
||||
* + You also can use LockedReadBuffer_t.get_deleter().SetConsumedSize() to set the final moving forward byte count before the return value free itself.
|
||||
* + The value passed to LockedReadBuffer_t..get_deleter().SetConsumedSize() will finally be passed to UnLockReadBuffer.
|
||||
* + You can use LockedReadBuffer_t.reset() to force free the return value.
|
||||
* @remark Here is a example.
|
||||
* ```
|
||||
* auto buf = chunk->LockReadBufferWrapper(1919810);
|
||||
* if (buf) {
|
||||
* stuff(buf.get()); // do some operation...
|
||||
* buf.get_deleter().SetConsumedSize(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:
|
||||
/**
|
||||
* @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:
|
||||
/**
|
||||
* @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<typename T>
|
||||
bool ReadStruct(T* data) {
|
||||
return ReadByteData(data, CKSizeof(T));
|
||||
}
|
||||
/**
|
||||
* @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<typename T>
|
||||
inline bool ReadStruct(T& data) {
|
||||
return ReadByteData(&data, CKSizeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read string. The content of string will automatically converted into UTF8 format.
|
||||
/// </summary>
|
||||
/// <param name="strl"></param>
|
||||
/// <returns></returns>
|
||||
bool ReadString(XContainer::XString* strl);
|
||||
inline bool ReadString(XContainer::XString& strl) {
|
||||
return ReadString(&strl);
|
||||
}
|
||||
|
||||
/* ========== Complex Data Read Functions ==========*/
|
||||
|
||||
bool ReadObjectID(CK_ID* id);
|
||||
bool ReadObjectPointer(ObjImpls::CKObject** obj);
|
||||
inline bool ReadObjectID(CK_ID& id) {
|
||||
return ReadObjectID(&id);
|
||||
}
|
||||
inline bool ReadObjectPointer(ObjImpls::CKObject*& id) {
|
||||
return ReadObjectPointer(&id);
|
||||
}
|
||||
|
||||
bool ReadManagerInt(CKGUID* guid, CKINT* intval);
|
||||
inline bool ReadManagerInt(CKGUID& guid, CKINT& intval) {
|
||||
return ReadManagerInt(&guid, &intval);
|
||||
}
|
||||
|
||||
///**
|
||||
// * @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 ==========*/
|
||||
|
||||
/*
|
||||
Buffer related function implements:
|
||||
|
||||
ReadBuffer(void**) Read Byte based size. -> ReadBuffer(void**, CKDWORD*)
|
||||
ReadAndFillBuffer(int, void*) User give Byte based size. -> ReadAndFillBuffer(const void*, CKDWORD)
|
||||
ReadAndFillBuffer(void*) Read Byte based size. -> ReadAndFillBuffer(const void*)
|
||||
ReadAndFillBuffer_LEndian(int, void*) User give Byte based size. -> ReadAndFillBuffer(const void*, CKDWORD)
|
||||
ReadAndFillBuffer_LEndian(void*) Read Byte based size. -> ReadAndFillBuffer(const void*)
|
||||
ReadAndFillBuffer_LEndian16(int, void*) User give Byte based size. -> ReadAndFillBuffer(const void*, CKDWORD)
|
||||
ReadAndFillBuffer_LEndian16(void*) Read Byte based size. -> ReadAndFillBuffer(const void*)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Read buffer and copy it.
|
||||
*
|
||||
* The size of buffer will be read from CKStateChunk internally.
|
||||
* It mean the read buffer must be written by WriteBuffer().
|
||||
*
|
||||
* 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**)
|
||||
*/
|
||||
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 ReadAndCopyBuffer(void**, CKDWORD*)
|
||||
* @return Buffer_t (actually is a std::unique_ptr with custom deleter)
|
||||
* @remark
|
||||
* + The return value is std::unique_ptr but its deleter have more features.
|
||||
* + If return value containing value is nullptr, it mean this function is failed.
|
||||
* + Use Buffer_t.get_deleter().GetBufferSize() to get the size of buffer.
|
||||
* + You can use Buffer_t.reset() to force free the return value.
|
||||
* @remark Here is a exmaple about how to use this function
|
||||
* ```
|
||||
* Buffer_t buf = chunk->ReadBufferWrapper(114);
|
||||
* if (buf) {
|
||||
* stuff(buf.get(), buf.get_deleter().GetBufferSize()); // do some operation...
|
||||
* buf.reset(); // immediately free it.
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
Buffer_t ReadBufferWrapper();
|
||||
|
||||
/**
|
||||
* @brief Read buffer and fill user struct.
|
||||
*
|
||||
* The size of buffer will be read from CKStateChunk internally and return to caller.
|
||||
* It mean the read buffer must be written by WriteBuffer().
|
||||
*
|
||||
* @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.
|
||||
* It mean the read buffer must be written by WriteBufferNoSize().
|
||||
*
|
||||
* @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);
|
||||
|
||||
/* ========== Sequence Functions ==========*/
|
||||
|
||||
/// <summary>
|
||||
/// Read Object ID Sequence
|
||||
/// <para>The combination using of StartReadSequence(), ReadObjectID(), and ReadObject() redirect to this.</para>
|
||||
/// </summary>
|
||||
/// <param name="ls"></param>
|
||||
/// <returns></returns>
|
||||
bool ReadObjectIDSequence(XContainer::XObjectArray* ls);
|
||||
inline bool ReadObjectIDSequence(XContainer::XObjectArray& ls) {
|
||||
return ReadObjectIDSequence(&ls);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read Manager Sequence
|
||||
/// <para>The combination using of StartManagerReadSequence() and ReadManagerIntSequence() redirect to this.</para>
|
||||
/// </summary>
|
||||
/// <param name="guid"></param>
|
||||
/// <param name="ls"></param>
|
||||
/// <returns></returns>
|
||||
bool ReadManagerIntSequence(CKGUID* guid, XContainer::XArray<CKINT>* ls);
|
||||
inline bool ReadManagerIntSequence(CKGUID& guid, XContainer::XArray<CKINT>& ls) {
|
||||
return ReadManagerIntSequence(&guid, &ls);
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// Read Sub Chunk Sequence
|
||||
///// <para>The combination using of StartReadSequence() and ReadSubChunk() redirect to this.</para>
|
||||
///// <para>The item of returned CKStateChunk* list should be manually released!</para>
|
||||
///// </summary>
|
||||
///// <param name="ls"></param>
|
||||
///// <returns></returns>
|
||||
//bool ReadSubChunkSequence(XContainer::XArray<CKStateChunk*>* ls);
|
||||
//inline bool ReadSubChunkSequence(XContainer::XArray<CKStateChunk*>& ls) {
|
||||
// return ReadSubChunkSequence(&ls);
|
||||
//}
|
||||
|
||||
/**
|
||||
* @brief Read Object Array (actually still is CK_ID)
|
||||
* @remark ReadObjectArray() and XObjectArray::Load redirect to this.
|
||||
* @param ls The list
|
||||
* @return True if success.
|
||||
*/
|
||||
bool ReadXObjectArray(XContainer::XObjectArray* ls);
|
||||
inline bool ReadXObjectArray(XContainer::XObjectArray& ls) {
|
||||
return ReadXObjectArray(&ls);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read Object Array (actually is CKObject*)
|
||||
* @remark ReadXObjectArray() and XObjectPointerArray::Load redirect to this.
|
||||
* @param ls The list
|
||||
* @return True if success
|
||||
*/
|
||||
bool ReadXObjectPointerArray(XContainer::XObjectPointerArray* ls);
|
||||
inline bool ReadXObjectPointerArray(XContainer::XObjectPointerArray& ls) {
|
||||
return ReadXObjectPointerArray(&ls);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Write Function
|
||||
|
||||
public:
|
||||
void StartWrite();
|
||||
/*
|
||||
* Actually this function mix various functions, including CloseChunk(), UpdateSize() and etc.
|
||||
*/
|
||||
void StopWrite();
|
||||
|
||||
|
||||
/* ========== Identifier Functions ==========*/
|
||||
|
||||
public:
|
||||
bool WriteIdentifierDword(CKDWORD identifier);
|
||||
template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
inline bool WriteIdentifier(TEnum enum_v) {
|
||||
return WriteIdentifierDword(static_cast<CKDWORD>(enum_v));
|
||||
}
|
||||
|
||||
/* ========== Write Buffer Controller ==========*/
|
||||
|
||||
public:
|
||||
bool LockWriteBuffer(void** ppData, CKDWORD size_in_byte);
|
||||
bool UnLockWriteBuffer(CKDWORD size_in_byte);
|
||||
LockedWriteBuffer_t LockWriteBufferWrapper(CKDWORD size_in_byte);
|
||||
|
||||
/* ========== Basic Data Write Functions ==========*/
|
||||
|
||||
private:
|
||||
bool WriteByteData(const void* data_ptr, CKDWORD size_in_byte);
|
||||
|
||||
public:
|
||||
template<typename T>
|
||||
bool WriteStruct(const T* data) {
|
||||
return WriteByteData(data, CKSizeof(T));
|
||||
}
|
||||
template<typename T>
|
||||
inline bool WriteStruct(const T& data) {
|
||||
return WriteByteData(&data, CKSizeof(T));
|
||||
}
|
||||
|
||||
bool WriteString(const XContainer::XString* strl);
|
||||
inline bool WriteString(const XContainer::XString& strl) {
|
||||
return WriteString(&strl);
|
||||
}
|
||||
|
||||
|
||||
/* ========== Complex Data Read Functions ==========*/
|
||||
|
||||
public:
|
||||
bool WriteObjectID(const CK_ID* id);
|
||||
bool WriteObjectPointer(ObjImpls::CKObject* obj);
|
||||
inline bool WriteObjectID(const CK_ID& id) {
|
||||
return WriteObjectID(&id);
|
||||
}
|
||||
|
||||
bool WriteManagerInt(const CKGUID* guid, CKINT intval);
|
||||
inline bool WriteManagerInt(const CKGUID& guid, CKINT intval) {
|
||||
return WriteManagerInt(&guid, intval);
|
||||
}
|
||||
|
||||
// Sub Chunk not support now.
|
||||
// Too complex and I even don't use it in my code.
|
||||
//CKStateChunk* ReadSubChunk();
|
||||
|
||||
/* ========== Buffer Functions ==========*/
|
||||
|
||||
/*
|
||||
Buffer related function implements:
|
||||
|
||||
WriteBuffer(int, void*) Write buffer with size. -> WriteBuffer(const void*, CKDWORD)
|
||||
WriteBufferNoSize(int, void*) Write buffer without size. -> WriteBufferNoSize(const void*, CKDWORD)
|
||||
WriteBuffer_LEndian(int, void*) Write buffer with size. -> WriteBuffer(const void*, CKDWORD)
|
||||
WriteBuffer_LEndian16(int, void*) Write buffer with size. -> WriteBuffer(const void*, CKDWORD)
|
||||
WriteBufferNoSize_LEndian(int, void*) Write buffer without size. -> WriteBufferNoSize(const void*, CKDWORD)
|
||||
WriteBufferNoSize_LEndian16(int, void*) Write buffer without size. -> WriteBufferNoSize(const void*, CKDWORD)
|
||||
*/
|
||||
|
||||
// MARK: buf can be nullptr, but buf size must is 0. and WriteBuffer will only write a zero to indicate there is a zero buffer.
|
||||
|
||||
bool WriteBuffer(const void* buf, CKDWORD size_in_byte);
|
||||
bool WriteBufferNoSize(const void* buf, CKDWORD size_in_byte);
|
||||
|
||||
/* ========== Sequence Functions ==========*/
|
||||
|
||||
public:
|
||||
bool WriteObjectIDSequence(const XContainer::XObjectArray* ls);
|
||||
inline bool WriteObjectIDSequence(const XContainer::XObjectArray& ls) {
|
||||
return WriteObjectIDSequence(&ls);
|
||||
}
|
||||
|
||||
bool WriteManagerIntSequence(const CKGUID* guid, const XContainer::XArray<CKINT>* ls);
|
||||
inline bool WriteManagerIntSequence(const CKGUID& guid, const XContainer::XArray<CKINT>& ls) {
|
||||
return WriteManagerIntSequence(&guid, &ls);
|
||||
}
|
||||
|
||||
// Sub chunk is not available now
|
||||
// Because my code never use it and it is too complex.
|
||||
//bool ReadSubChunkSequence(XContainer::XArray<CKStateChunk*>* ls);
|
||||
//inline bool ReadSubChunkSequence(XContainer::XArray<CKStateChunk*>& ls) {
|
||||
// return ReadSubChunkSequence(&ls);
|
||||
//}
|
||||
|
||||
bool WriteXObjectArray(const XContainer::XObjectArray* ls);
|
||||
inline bool WriteXObjectArray(const XContainer::XObjectArray& ls) {
|
||||
return WriteXObjectArray(&ls);
|
||||
}
|
||||
|
||||
bool WriteXObjectPointerArray(const XContainer::XObjectPointerArray* ls);
|
||||
inline bool WriteXObjectPointerArray(const XContainer::XObjectPointerArray& ls) {
|
||||
return WriteXObjectPointerArray(&ls);
|
||||
}
|
||||
|
||||
|
||||
#pragma endregion
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,518 +0,0 @@
|
||||
#include "CKStateChunk.hpp"
|
||||
#include "CKFile.hpp"
|
||||
#include "CKContext.hpp"
|
||||
|
||||
namespace LibCmo::CK2 {
|
||||
|
||||
#pragma region Ctor Dtor
|
||||
|
||||
CKStateChunk::CKStateChunk(CKFileVisitor* visitor, CKContext* ctx) :
|
||||
m_ClassId(CK_CLASSID::CKCID_OBJECT), m_DataDwSize(0u), m_pData(nullptr),
|
||||
m_DataVersion(CK_STATECHUNK_DATAVERSION::CHUNKDATA_CURRENTVERSION), m_ChunkVersion(CK_STATECHUNK_CHUNKVERSION::CHUNK_VERSION4),
|
||||
m_Parser { CKStateChunkStatus::IDLE, 0u, 0u, 0u },
|
||||
m_ObjectList(), m_ChunkList(), m_ManagerList(),
|
||||
m_BindFile(visitor), m_BindContext(ctx)
|
||||
{}
|
||||
|
||||
CKStateChunk::CKStateChunk(const CKStateChunk& rhs) :
|
||||
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_BindFile(rhs.m_BindFile), m_BindContext(rhs.m_BindContext) {
|
||||
// copy buffer
|
||||
if (rhs.m_pData != nullptr) {
|
||||
this->m_pData = new CKDWORD[rhs.m_DataDwSize];
|
||||
std::memcpy(this->m_pData, rhs.m_pData, sizeof(CKDWORD) * rhs.m_DataDwSize);
|
||||
}
|
||||
}
|
||||
|
||||
CKStateChunk::CKStateChunk(CKStateChunk&& rhs) :
|
||||
m_ClassId(rhs.m_ClassId), m_DataVersion(rhs.m_DataVersion), m_ChunkVersion(rhs.m_ChunkVersion),
|
||||
m_Parser(rhs.m_Parser),
|
||||
m_ObjectList(std::move(rhs.m_ObjectList)), m_ManagerList(std::move(rhs.m_ManagerList)), m_ChunkList(std::move(rhs.m_ChunkList)),
|
||||
m_pData(rhs.m_pData), m_DataDwSize(rhs.m_DataDwSize),
|
||||
m_BindFile(rhs.m_BindFile), m_BindContext(rhs.m_BindContext) {
|
||||
// set to null after steal data
|
||||
rhs.m_pData = nullptr;
|
||||
// and clear it
|
||||
rhs.Clear();
|
||||
}
|
||||
|
||||
CKStateChunk& CKStateChunk::operator=(const CKStateChunk& rhs) {
|
||||
this->Clear();
|
||||
|
||||
this->m_DataVersion = rhs.m_DataVersion;
|
||||
this->m_ChunkVersion = rhs.m_ChunkVersion;
|
||||
this->m_ClassId = rhs.m_ClassId;
|
||||
|
||||
this->m_Parser = rhs.m_Parser;
|
||||
|
||||
this->m_ObjectList = rhs.m_ObjectList;
|
||||
this->m_ManagerList = rhs.m_ManagerList;
|
||||
this->m_ChunkList = rhs.m_ChunkList;
|
||||
|
||||
this->m_BindFile = rhs.m_BindFile;
|
||||
this->m_BindContext = rhs.m_BindContext;
|
||||
|
||||
// copy buffer
|
||||
if (rhs.m_pData != nullptr) {
|
||||
this->m_pData = new CKDWORD[rhs.m_DataDwSize];
|
||||
std::memcpy(this->m_pData, rhs.m_pData, sizeof(CKDWORD) * rhs.m_DataDwSize);
|
||||
}
|
||||
this->m_DataDwSize = rhs.m_DataDwSize;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CKStateChunk& CKStateChunk::operator=(CKStateChunk&& rhs) {
|
||||
this->Clear();
|
||||
|
||||
this->m_DataVersion = rhs.m_DataVersion;
|
||||
this->m_ChunkVersion = rhs.m_ChunkVersion;
|
||||
this->m_ClassId = rhs.m_ClassId;
|
||||
|
||||
this->m_Parser = rhs.m_Parser;
|
||||
|
||||
this->m_ObjectList = rhs.m_ObjectList;
|
||||
this->m_ManagerList = rhs.m_ManagerList;
|
||||
this->m_ChunkList = rhs.m_ChunkList;
|
||||
|
||||
this->m_BindFile = rhs.m_BindFile;
|
||||
this->m_BindContext = rhs.m_BindContext;
|
||||
|
||||
// steal buffer
|
||||
this->m_pData = rhs.m_pData;
|
||||
rhs.m_pData = nullptr;
|
||||
this->m_DataDwSize = rhs.m_DataDwSize;
|
||||
|
||||
// clear steal chunk
|
||||
rhs.Clear();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CKStateChunk::~CKStateChunk() {
|
||||
if (this->m_pData != nullptr)
|
||||
delete[] this->m_pData;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Self Used Data Struct
|
||||
|
||||
void CKStateChunk::LockedReadBufferDeleter::operator()(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()(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 ==========
|
||||
|
||||
const CKStateChunk::ProfileStateChunk_t CKStateChunk::GetStateChunkProfile() {
|
||||
return CKStateChunk::ProfileStateChunk_t {
|
||||
.m_ClassId = this->m_ClassId,
|
||||
.m_DataDwSize = this->m_DataDwSize,
|
||||
.m_pData = this->m_pData,
|
||||
|
||||
.m_DataVersion = this->m_DataVersion,
|
||||
.m_ChunkVersion = this->m_ChunkVersion,
|
||||
|
||||
.m_ObjectListSize = this->m_ObjectList.size(),
|
||||
.m_ChunkListSize = this->m_ChunkList.size(),
|
||||
.m_ManagerListSize = this->m_ManagerList.size(),
|
||||
|
||||
.m_BindFile = this->m_BindFile,
|
||||
.m_BindContext = this->m_BindContext,
|
||||
};
|
||||
}
|
||||
|
||||
const XContainer::XArray<CKStateChunk::ProfileIdentifier_t> CKStateChunk::GetIdentifiersProfile() {
|
||||
XContainer::XArray<CKStateChunk::ProfileIdentifier_t> collection;
|
||||
if (this->m_Parser.m_Status != CKStateChunkStatus::READ) return collection;
|
||||
|
||||
CKDWORD pos = 0u;
|
||||
if (this->m_DataDwSize < 2u) return collection; // impossible to have a identifier
|
||||
|
||||
// iterate identifier
|
||||
while (true) {
|
||||
// add current identifier
|
||||
CKDWORD nextptr = this->m_pData[pos + 1];
|
||||
if (nextptr == 0u) {
|
||||
nextptr = this->m_DataDwSize; // got tail. no more identifier
|
||||
}
|
||||
collection.emplace_back(CKStateChunk::ProfileIdentifier_t{
|
||||
this->m_pData[pos],
|
||||
this->m_pData + pos + 2,
|
||||
CKSizeof(CKDWORD) * (nextptr - pos - 2u)
|
||||
});
|
||||
|
||||
// move to next identifier or exit
|
||||
// got tail. no more identifier
|
||||
if (this->m_pData[pos + 1] == 0u) break;
|
||||
|
||||
pos = this->m_pData[pos + 1];
|
||||
|
||||
// out of buffer
|
||||
if (pos + 1 >= this->m_DataDwSize) break;
|
||||
};
|
||||
return collection;
|
||||
|
||||
}
|
||||
|
||||
void CKStateChunk::Clear() {
|
||||
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_Parser.m_Status = CKStateChunkStatus::IDLE;
|
||||
this->m_Parser.m_CurrentPos = 0;
|
||||
this->m_Parser.m_DataSize = 0;
|
||||
this->m_Parser.m_PrevIdentifierPos = 0;
|
||||
|
||||
this->m_DataDwSize = 0;
|
||||
if (this->m_pData != nullptr) {
|
||||
delete[] this->m_pData;
|
||||
this->m_pData = nullptr;
|
||||
}
|
||||
|
||||
this->m_ObjectList.clear();
|
||||
this->m_ManagerList.clear();
|
||||
this->m_ChunkList.clear();
|
||||
}
|
||||
|
||||
CKDWORD CKStateChunk::GetDataSize() const {
|
||||
return CKSizeof(CKDWORD) * this->m_DataDwSize;
|
||||
}
|
||||
|
||||
CK_STATECHUNK_DATAVERSION CKStateChunk::GetDataVersion() const {
|
||||
return this->m_DataVersion;
|
||||
}
|
||||
|
||||
void CKStateChunk::SetDataVersion(CK_STATECHUNK_DATAVERSION version) {
|
||||
this->m_DataVersion = version;
|
||||
}
|
||||
|
||||
CK_CLASSID CKStateChunk::GetClassId() const {
|
||||
return this->m_ClassId;
|
||||
}
|
||||
|
||||
void CKStateChunk::SetClassId(CK_CLASSID cid) {
|
||||
this->m_ClassId = cid;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// ========== Private Funcs ==========
|
||||
|
||||
CKDWORD CKStateChunk::GetCeilDwordSize(size_t char_size) {
|
||||
return static_cast<CKDWORD>((char_size + 3) >> 2);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// set buf size
|
||||
this->m_Parser.m_DataSize = 0u;
|
||||
} else {
|
||||
// otherwise, we create a new buffer instead it
|
||||
CKDWORD* newbuf = new CKDWORD[new_dwsize];
|
||||
|
||||
// we copy original data only when it has.
|
||||
if (this->m_pData != nullptr) {
|
||||
// MARK: use std::min to copy for the minilist one
|
||||
// otherwise, EnsureWriteSpace or StopWrite will crash.
|
||||
std::memcpy(newbuf, this->m_pData, sizeof(CKDWORD) * std::min(this->m_Parser.m_DataSize, new_dwsize));
|
||||
delete[] this->m_pData;
|
||||
}
|
||||
|
||||
// assign new buffer
|
||||
this->m_pData = newbuf;
|
||||
|
||||
// set buf size
|
||||
this->m_Parser.m_DataSize = new_dwsize;
|
||||
}
|
||||
|
||||
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
|
||||
if (!this->ResizeBuffer(needed)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void CKStateChunk::AddEntry(XContainer::XArray<CKDWORD>& entry_ls, CKDWORD pos) {
|
||||
entry_ls.emplace_back(pos);
|
||||
}
|
||||
|
||||
void CKStateChunk::AddEntries(XContainer::XArray<CKDWORD>& entry_ls, CKDWORD pos) {
|
||||
entry_ls.emplace_back(static_cast<CKDWORD>(-1));
|
||||
entry_ls.emplace_back(pos);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Buffer Related
|
||||
|
||||
bool CKStateChunk::ConvertFromBuffer(const void* buf) {
|
||||
if (buf == nullptr) return false;
|
||||
this->Clear();
|
||||
|
||||
// read chunk ver and data ver first
|
||||
// chunk ver always set in the 3rd BYTE in every format
|
||||
this->m_ChunkVersion = static_cast<CK_STATECHUNK_CHUNKVERSION>(
|
||||
static_cast<const CKBYTE*>(buf)[2]
|
||||
);
|
||||
// data ver always set in the 1st BYTE in every format
|
||||
this->m_DataVersion = static_cast<CK_STATECHUNK_DATAVERSION>(
|
||||
static_cast<const CKBYTE*>(buf)[0]
|
||||
);
|
||||
|
||||
// switch according to chunk ver
|
||||
const CKDWORD* dwbuf = static_cast<const CKDWORD*>(buf);
|
||||
size_t bufpos = 0u;
|
||||
if (this->m_ChunkVersion < CK_STATECHUNK_CHUNKVERSION::CHUNK_VERSION2) {
|
||||
// very old file
|
||||
|
||||
this->m_ClassId = static_cast<CK_CLASSID>(dwbuf[1]);
|
||||
this->m_DataDwSize = dwbuf[2];
|
||||
|
||||
this->m_ObjectList.resize(dwbuf[4]);
|
||||
this->m_ChunkList.resize(dwbuf[5]);
|
||||
bufpos = 6u;
|
||||
|
||||
if (this->m_DataDwSize != 0) {
|
||||
this->m_pData = new CKDWORD[this->m_DataDwSize];
|
||||
std::memcpy(this->m_pData, dwbuf + bufpos, sizeof(CKDWORD) * this->m_DataDwSize);
|
||||
bufpos += this->m_DataDwSize;
|
||||
}
|
||||
if (!this->m_ObjectList.empty()) {
|
||||
std::memcpy(this->m_ObjectList.data(), dwbuf + bufpos, sizeof(CKDWORD) * this->m_ObjectList.size());
|
||||
bufpos += this->m_ObjectList.size();
|
||||
}
|
||||
if (!this->m_ChunkList.empty()) {
|
||||
std::memcpy(this->m_ChunkList.data(), dwbuf + bufpos, sizeof(CKDWORD) * this->m_ChunkList.size());
|
||||
bufpos += this->m_ChunkList.size();
|
||||
}
|
||||
|
||||
// no bind file
|
||||
this->m_BindFile = nullptr;
|
||||
|
||||
} else if (this->m_ChunkVersion == CK_STATECHUNK_CHUNKVERSION::CHUNK_VERSION2) {
|
||||
// medium ver file
|
||||
|
||||
this->m_ClassId = static_cast<CK_CLASSID>(dwbuf[1]);
|
||||
this->m_DataDwSize = dwbuf[2];
|
||||
|
||||
this->m_ObjectList.resize(dwbuf[4]);
|
||||
this->m_ChunkList.resize(dwbuf[5]);
|
||||
this->m_ManagerList.resize(dwbuf[6]);
|
||||
bufpos = 7u;
|
||||
|
||||
if (this->m_DataDwSize != 0) {
|
||||
this->m_pData = new CKDWORD[this->m_DataDwSize];
|
||||
std::memcpy(this->m_pData, dwbuf + bufpos, sizeof(CKDWORD) * this->m_DataDwSize);
|
||||
bufpos += this->m_DataDwSize;
|
||||
}
|
||||
if (!this->m_ObjectList.empty()) {
|
||||
std::memcpy(this->m_ObjectList.data(), dwbuf + bufpos, sizeof(CKDWORD) * this->m_ObjectList.size());
|
||||
bufpos += this->m_ObjectList.size();
|
||||
}
|
||||
if (!this->m_ChunkList.empty()) {
|
||||
std::memcpy(this->m_ChunkList.data(), dwbuf + bufpos, sizeof(CKDWORD) * this->m_ChunkList.size());
|
||||
bufpos += this->m_ChunkList.size();
|
||||
}
|
||||
if (!this->m_ManagerList.empty()) {
|
||||
std::memcpy(this->m_ManagerList.data(), dwbuf + bufpos, sizeof(CKDWORD) * this->m_ManagerList.size());
|
||||
bufpos += this->m_ManagerList.size();
|
||||
}
|
||||
|
||||
// no bind file
|
||||
this->m_BindFile = nullptr;
|
||||
|
||||
} else if (this->m_ChunkVersion <= CK_STATECHUNK_CHUNKVERSION::CHUNK_VERSION4) {
|
||||
// the latest file
|
||||
|
||||
// re-read some extra data
|
||||
// class id located the 2nd BYTE
|
||||
this->m_ClassId = static_cast<CK_CLASSID>(
|
||||
static_cast<const CKBYTE*>(buf)[1]
|
||||
);
|
||||
// options located the 4th BYTE
|
||||
CK_STATECHUNK_CHUNKOPTIONS options = static_cast<CK_STATECHUNK_CHUNKOPTIONS>(
|
||||
static_cast<const CKBYTE*>(buf)[3]
|
||||
);
|
||||
|
||||
// read normal data
|
||||
this->m_DataDwSize = dwbuf[1];
|
||||
bufpos = 2u;
|
||||
|
||||
if (this->m_DataDwSize != 0) {
|
||||
this->m_pData = new CKDWORD[this->m_DataDwSize];
|
||||
std::memcpy(this->m_pData, dwbuf + bufpos, sizeof(CKDWORD) * this->m_DataDwSize);
|
||||
bufpos += this->m_DataDwSize;
|
||||
}
|
||||
if (!YYCC::EnumHelper::Has(options, CK_STATECHUNK_CHUNKOPTIONS::CHNK_OPTION_FILE)) {
|
||||
// forced no bind file
|
||||
this->m_BindFile = nullptr;
|
||||
}
|
||||
if (YYCC::EnumHelper::Has(options, CK_STATECHUNK_CHUNKOPTIONS::CHNK_OPTION_IDS)) {
|
||||
this->m_ObjectList.resize(dwbuf[bufpos]);
|
||||
bufpos += 1u;
|
||||
std::memcpy(this->m_ObjectList.data(), dwbuf + bufpos, sizeof(CKDWORD) * this->m_ObjectList.size());
|
||||
bufpos += this->m_ObjectList.size();
|
||||
}
|
||||
if (YYCC::EnumHelper::Has(options, CK_STATECHUNK_CHUNKOPTIONS::CHNK_OPTION_CHN)) {
|
||||
this->m_ChunkList.resize(dwbuf[bufpos]);
|
||||
bufpos += 1u;
|
||||
std::memcpy(this->m_ChunkList.data(), dwbuf + bufpos, sizeof(CKDWORD) * this->m_ChunkList.size());
|
||||
bufpos += this->m_ChunkList.size();
|
||||
}
|
||||
if (YYCC::EnumHelper::Has(options, CK_STATECHUNK_CHUNKOPTIONS::CHNK_OPTION_MAN)) {
|
||||
this->m_ManagerList.resize(dwbuf[bufpos]);
|
||||
bufpos += 1u;
|
||||
std::memcpy(this->m_ManagerList.data(), dwbuf + bufpos, sizeof(CKDWORD) * this->m_ManagerList.size());
|
||||
bufpos += this->m_ManagerList.size();
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
// too new. can not read, skip
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CKDWORD CKStateChunk::ConvertToBuffer(void* buf) {
|
||||
// calc size and setup options first
|
||||
// size = buffer + buffer_size + header
|
||||
CKDWORD size = (m_DataDwSize * CKSizeof(CKDWORD)) + CKSizeof(CKDWORD) + CKSizeof(CKDWORD);
|
||||
CK_STATECHUNK_CHUNKOPTIONS options = static_cast<CK_STATECHUNK_CHUNKOPTIONS>(0);
|
||||
|
||||
if (!m_ObjectList.empty()) {
|
||||
size += CKSizeof(CKDWORD) * static_cast<CKDWORD>(m_ObjectList.size()) + sizeof(CKDWORD);
|
||||
YYCC::EnumHelper::Add(options, CK_STATECHUNK_CHUNKOPTIONS::CHNK_OPTION_IDS);
|
||||
}
|
||||
if (!m_ChunkList.empty()) {
|
||||
size += CKSizeof(CKDWORD) * static_cast<CKDWORD>(m_ChunkList.size()) + sizeof(CKDWORD);
|
||||
YYCC::EnumHelper::Add(options, CK_STATECHUNK_CHUNKOPTIONS::CHNK_OPTION_CHN);
|
||||
}
|
||||
if (!m_ManagerList.empty()) {
|
||||
size += CKSizeof(CKDWORD) * static_cast<CKDWORD>(m_ManagerList.size()) + sizeof(CKDWORD);
|
||||
YYCC::EnumHelper::Add(options, CK_STATECHUNK_CHUNKOPTIONS::CHNK_OPTION_MAN);
|
||||
}
|
||||
|
||||
if (this->m_BindFile != nullptr) {
|
||||
YYCC::EnumHelper::Add(options, CK_STATECHUNK_CHUNKOPTIONS::CHNK_OPTION_FILE);
|
||||
}
|
||||
|
||||
// if buffer provided, write it
|
||||
if (buf != nullptr) {
|
||||
// write header
|
||||
static_cast<CKBYTE*>(buf)[0] = static_cast<CKBYTE>(this->m_DataVersion);
|
||||
static_cast<CKBYTE*>(buf)[1] = static_cast<CKBYTE>(this->m_ClassId);
|
||||
static_cast<CKBYTE*>(buf)[2] = static_cast<CKBYTE>(this->m_ChunkVersion);
|
||||
static_cast<CKBYTE*>(buf)[3] = static_cast<CKBYTE>(options);
|
||||
|
||||
CKDWORD* dwbuf = static_cast<CKDWORD*>(buf);
|
||||
// write buffer length
|
||||
dwbuf[1] = this->m_DataDwSize;
|
||||
size_t bufpos = 2u;
|
||||
// write buffer
|
||||
std::memcpy(dwbuf + bufpos, this->m_pData, this->m_DataDwSize * sizeof(CKDWORD));
|
||||
bufpos += this->m_DataDwSize;
|
||||
|
||||
// write list
|
||||
if (!m_ObjectList.empty()) {
|
||||
dwbuf[bufpos] = static_cast<CKDWORD>(m_ObjectList.size());
|
||||
std::memcpy(dwbuf + bufpos + 1, m_ObjectList.data(), m_ObjectList.size() * sizeof(CKDWORD));
|
||||
bufpos += m_ObjectList.size() + 1;
|
||||
}
|
||||
if (!m_ChunkList.empty()) {
|
||||
dwbuf[bufpos] = static_cast<CKDWORD>(m_ChunkList.size());
|
||||
std::memcpy(dwbuf + bufpos + 1, m_ChunkList.data(), m_ChunkList.size() * sizeof(CKDWORD));
|
||||
bufpos += m_ChunkList.size() + 1;
|
||||
}
|
||||
if (!m_ManagerList.empty()) {
|
||||
dwbuf[bufpos] = static_cast<CKDWORD>(m_ManagerList.size());
|
||||
std::memcpy(dwbuf + bufpos + 1, m_ManagerList.data(), m_ManagerList.size() * sizeof(CKDWORD));
|
||||
bufpos += m_ManagerList.size() + 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// return expected size.
|
||||
return size;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
}
|
||||
@@ -1,535 +0,0 @@
|
||||
#include "CKStateChunk.hpp"
|
||||
#include "CKFile.hpp"
|
||||
#include "CKContext.hpp"
|
||||
|
||||
namespace LibCmo::CK2 {
|
||||
|
||||
void CKStateChunk::StartRead() {
|
||||
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() {
|
||||
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 self status
|
||||
if (this->m_Parser.m_Status != CKStateChunkStatus::READ) return false;
|
||||
// check arguments
|
||||
if (ppData == nullptr) return false;
|
||||
*ppData = nullptr;
|
||||
|
||||
// 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(u8"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(u8"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 = nullptr;
|
||||
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 = nullptr;
|
||||
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;
|
||||
}
|
||||
|
||||
// check blank string
|
||||
if (strByteSize == 0) {
|
||||
strl->clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
// read data
|
||||
// MARK: the written string has NULL terminal.
|
||||
// strByteSize also include NULL terminal,
|
||||
// so we need minus 1 when resizing (not ReadByteData, because we still need read NULL terminal to skip it.)
|
||||
std::string cache;
|
||||
cache.resize(strByteSize - 1);
|
||||
if (!this->ReadByteData(cache.data(), strByteSize)) {
|
||||
strl->clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
// convert encoding
|
||||
if (!m_BindContext->GetUTF8String(cache, *strl)) {
|
||||
m_BindContext->OutputToConsole(u8"Fail to get UTF8 string when reading CKStateChunk. Some objects may be loaded incorrectly.");
|
||||
strl->clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
// okey
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ========== Complex Data Read Functions ==========*/
|
||||
|
||||
bool CKStateChunk::ReadObjectID(CK_ID* id) {
|
||||
if (id == nullptr) return false;
|
||||
|
||||
// get basic value
|
||||
CKDWORD 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<CK_ID>(gotten_id);
|
||||
return true;
|
||||
}
|
||||
// if it is positive, return corresponding value
|
||||
if ((gotten_id & 0x80000000) == 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::ReadObjectPointer(ObjImpls::CKObject** obj) {
|
||||
CK_ID cache;
|
||||
bool ret = ReadObjectID(&cache);
|
||||
if (ret) {
|
||||
*obj = m_BindContext->GetObject(cache);
|
||||
} else {
|
||||
*obj = nullptr;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
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() {
|
||||
// CKStateChunk* subchunk = nullptr;
|
||||
|
||||
// // get size and do a enough space check
|
||||
// CKDWORD subDwordChunkSize;
|
||||
// if (!this->ReadStruct(subDwordChunkSize)) goto subchunk_defer;
|
||||
// if (!this->EnsureReadSpace(subDwordChunkSize)) 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<CK_STATECHUNK_DATAVERSION>(versionInfo & 0xffff);
|
||||
// subchunk->m_ChunkVersion = static_cast<CK_STATECHUNK_CHUNKVERSION>((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<CKDWORD>(subchunk->m_ObjectList.size()) * CKSizeof(CKDWORD)
|
||||
// )) goto subchunk_defer;
|
||||
// }
|
||||
// if (!subchunk->m_ChunkList.empty()) {
|
||||
// if (!this->ReadByteData(
|
||||
// subchunk->m_ChunkList.data(),
|
||||
// static_cast<CKDWORD>(subchunk->m_ChunkList.size()) * CKSizeof(CKDWORD)
|
||||
// )) goto subchunk_defer;
|
||||
// }
|
||||
// if (!subchunk->m_ManagerList.empty()) {
|
||||
// if (!this->ReadByteData(
|
||||
// subchunk->m_ManagerList.data(),
|
||||
// static_cast<CKDWORD>(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::ReadBuffer(void** ppData, CKDWORD* size_in_byte) {
|
||||
if (ppData == nullptr || size_in_byte == nullptr) return false;
|
||||
*ppData = nullptr;
|
||||
*size_in_byte = 0;
|
||||
|
||||
// read size first
|
||||
if (!this->ReadStruct(size_in_byte)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if it is empty buffer,
|
||||
// simply return it but need return true to notify read okey.
|
||||
if (*size_in_byte == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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[] static_cast<const CKBYTE*>(buf);
|
||||
}
|
||||
|
||||
CKStateChunk::Buffer_t CKStateChunk::ReadBufferWrapper() {
|
||||
void* cache = nullptr;
|
||||
CKDWORD size = 0;
|
||||
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 ==========*/
|
||||
|
||||
bool CKStateChunk::ReadObjectIDSequence(XContainer::XObjectArray* 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<CKINT>* 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<CKStateChunk*>* 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<CK_ID>(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;
|
||||
ls->clear();
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,299 +0,0 @@
|
||||
#include "CKStateChunk.hpp"
|
||||
#include "CKFile.hpp"
|
||||
#include "CKContext.hpp"
|
||||
#include "ObjImpls/CKObject.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;
|
||||
// set data version
|
||||
// MARK: in virtools impl, this statement is written in CKObject::Save
|
||||
// and data version is delivered by merging CKStateChunk.
|
||||
// but we do not use that saving strategy, so we init data version in here.
|
||||
this->m_DataVersion = CK_STATECHUNK_DATAVERSION::CHUNK_DEV_2_1;
|
||||
|
||||
// switch status
|
||||
this->m_Parser.m_Status = CKStateChunkStatus::WRITE;
|
||||
}
|
||||
|
||||
void CKStateChunk::StopWrite() {
|
||||
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;
|
||||
}
|
||||
|
||||
/* ========== Identifier Functions ==========*/
|
||||
|
||||
bool CKStateChunk::WriteIdentifierDword(CKDWORD identifier) {
|
||||
// check self status
|
||||
if (this->m_Parser.m_Status != CKStateChunkStatus::WRITE) return false;
|
||||
// make sure there are 2 DWORD space for writing identifier header
|
||||
if (!EnsureWriteSpace(2)) return false;
|
||||
|
||||
// update the last identifier header to fill its length indicator
|
||||
if (m_Parser.m_PrevIdentifierPos < m_Parser.m_CurrentPos) {
|
||||
m_pData[m_Parser.m_PrevIdentifierPos + 1] = m_Parser.m_CurrentPos;
|
||||
}
|
||||
|
||||
// set prev ident to this new created ident
|
||||
m_Parser.m_PrevIdentifierPos = m_Parser.m_CurrentPos;
|
||||
// write identifier and set default next ident data
|
||||
m_pData[m_Parser.m_CurrentPos++] = identifier;
|
||||
m_pData[m_Parser.m_CurrentPos++] = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ========== Write Buffer Controller ==========*/
|
||||
|
||||
bool CKStateChunk::LockWriteBuffer(void** ppData, CKDWORD size_in_byte) {
|
||||
// same as LockReadBuffer with slight difference.
|
||||
if (this->m_Parser.m_Status != CKStateChunkStatus::WRITE) return false;
|
||||
if (ppData == nullptr) return false;
|
||||
*ppData = nullptr;
|
||||
|
||||
CKDWORD size_in_dword = this->GetCeilDwordSize(size_in_byte);
|
||||
if (this->EnsureWriteSpace(size_in_dword)) {
|
||||
*ppData = this->m_pData + this->m_Parser.m_CurrentPos;
|
||||
return true;
|
||||
} else {
|
||||
m_BindContext->OutputToConsoleEx(u8"CKStateChunk::LockWriteBuffer at buffer pos %" PRIuCKDWORD ".", this->m_Parser.m_CurrentPos);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CKStateChunk::UnLockWriteBuffer(CKDWORD size_in_byte) {
|
||||
// same as UnLockReadBuffer with slight difference.
|
||||
if (this->m_Parser.m_Status != CKStateChunkStatus::WRITE) return false;
|
||||
|
||||
CKDWORD size_in_dword = this->GetCeilDwordSize(size_in_byte);
|
||||
if (this->EnsureWriteSpace(size_in_dword)) {
|
||||
this->m_Parser.m_CurrentPos += size_in_dword;
|
||||
return true;
|
||||
} else {
|
||||
m_BindContext->OutputToConsoleEx(u8"CKStateChunk::UnLockWriteBuffer at buffer pos %" PRIuCKDWORD ".", this->m_Parser.m_CurrentPos);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CKStateChunk::LockedWriteBuffer_t CKStateChunk::LockWriteBufferWrapper(CKDWORD size_in_byte) {
|
||||
// same as LockReadBufferWrapper with slight difference.
|
||||
void* pData = nullptr;
|
||||
bool ret = LockWriteBuffer(&pData, size_in_byte);
|
||||
if (ret) {
|
||||
return LockedWriteBuffer_t(pData, LockedWriteBufferDeleter(this, size_in_byte));
|
||||
} else {
|
||||
return LockedWriteBuffer_t();
|
||||
}
|
||||
}
|
||||
|
||||
/* ========== Basic Data Write Functions ==========*/
|
||||
|
||||
bool CKStateChunk::WriteByteData(const void* data_ptr, CKDWORD size_in_byte) {
|
||||
// same as ReadByteData with slight difference.
|
||||
if (data_ptr == nullptr) return false;
|
||||
|
||||
void* pData = nullptr;
|
||||
bool ret = LockWriteBuffer(&pData, size_in_byte);
|
||||
if (ret) {
|
||||
std::memcpy(pData, data_ptr, size_in_byte);
|
||||
UnLockWriteBuffer(size_in_byte);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CKStateChunk::WriteString(const XContainer::XString* strl) {
|
||||
if (strl == nullptr) return false;
|
||||
|
||||
// convert encoding
|
||||
std::string cache;
|
||||
if (!m_BindContext->GetOrdinaryString(*strl, cache))
|
||||
m_BindContext->OutputToConsole(u8"Fail to get ordinary string when saving CKStateChunk. Some objects may be saved incorrectly.");
|
||||
|
||||
if (cache.empty()) {
|
||||
// write zero string
|
||||
return this->WriteStruct(0);
|
||||
} else {
|
||||
// write string with NULL terminal
|
||||
|
||||
// write size
|
||||
CKDWORD strByteSize = static_cast<CKDWORD>(cache.size()) + 1;
|
||||
if (!this->WriteStruct(strByteSize)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// write data with NULL terminal
|
||||
if (!this->WriteByteData(cache.c_str(), strByteSize)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool CKStateChunk::WriteObjectID(const CK_ID* id) {
|
||||
// if BindFile is not nullptr,
|
||||
// no need to push this obj into obj list according to IDA code.
|
||||
|
||||
if (m_BindFile != nullptr) {
|
||||
this->WriteStruct(m_BindFile->GetIndexByObjectID(id != nullptr ? (*id) : 0));
|
||||
} else {
|
||||
if (id != nullptr) {
|
||||
AddEntry(m_ObjectList, m_Parser.m_CurrentPos);
|
||||
this->WriteStruct(id);
|
||||
} else {
|
||||
this->WriteStruct(static_cast<CKDWORD>(0));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CKStateChunk::WriteObjectPointer(ObjImpls::CKObject* obj) {
|
||||
CK_ID objid = 0;
|
||||
if (obj != nullptr) objid = obj->GetID();
|
||||
|
||||
return WriteObjectID(objid);
|
||||
}
|
||||
|
||||
bool CKStateChunk::WriteManagerInt(const CKGUID* guid, CKINT intval) {
|
||||
// push into manager list
|
||||
AddEntry(m_ManagerList, m_Parser.m_CurrentPos);
|
||||
// write data
|
||||
if (!this->WriteStruct(guid)) return false;
|
||||
if (!this->WriteStruct(intval)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ========== Buffer Functions ==========*/
|
||||
|
||||
bool CKStateChunk::WriteBuffer(const void* buf, CKDWORD size_in_byte) {
|
||||
if (buf != nullptr) {
|
||||
// write size
|
||||
if (!this->WriteStruct(size_in_byte)) return false;
|
||||
// write data
|
||||
auto locker = LockWriteBufferWrapper(size_in_byte);
|
||||
if (locker == nullptr) return false;
|
||||
std::memcpy(locker.get(), buf, size_in_byte);
|
||||
locker.reset();
|
||||
} else {
|
||||
// write blank data
|
||||
if (!this->WriteStruct(0)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CKStateChunk::WriteBufferNoSize(const void* buf, CKDWORD size_in_byte) {
|
||||
if (buf != nullptr) {
|
||||
// write data
|
||||
auto locker = LockWriteBufferWrapper(size_in_byte);
|
||||
if (locker == nullptr) return false;
|
||||
std::memcpy(locker.get(), buf, size_in_byte);
|
||||
locker.reset();
|
||||
}
|
||||
// if nosize buffer is nullptr, nothing need to write.
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ========== Sequence Functions ==========*/
|
||||
|
||||
bool CKStateChunk::WriteObjectIDSequence(const XContainer::XObjectArray* ls) {
|
||||
if (ls == nullptr) return false;
|
||||
|
||||
// add current pos into manager list if no bind file
|
||||
if (ls->size() != 0 && m_BindFile == nullptr) {
|
||||
AddEntries(m_ObjectList, m_Parser.m_CurrentPos);
|
||||
}
|
||||
|
||||
// write size first
|
||||
CKDWORD objidsize = static_cast<CKDWORD>(ls->size());
|
||||
if (!this->WriteStruct(objidsize)) return false;
|
||||
|
||||
// write each item
|
||||
for (auto objid : *ls) {
|
||||
// because we do not want write position data in obj list for each item.
|
||||
// so we do not call WriteObjectID here.
|
||||
if (m_BindFile != nullptr) {
|
||||
if (!this->WriteStruct(m_BindFile->GetIndexByObjectID(objid))) return false;
|
||||
} else {
|
||||
if (!this->WriteStruct(static_cast<CKDWORD>(objid))) return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CKStateChunk::WriteManagerIntSequence(const CKGUID* guid, const XContainer::XArray<CKINT>* ls) {
|
||||
if (guid == nullptr || ls == nullptr) return false;
|
||||
|
||||
// add current pos into manager list
|
||||
AddEntries(m_ManagerList, m_Parser.m_CurrentPos);
|
||||
|
||||
// write data length
|
||||
CKDWORD lssize = static_cast<CKDWORD>(ls->size());
|
||||
if (!this->WriteStruct(lssize)) return false;
|
||||
// write guid
|
||||
if (!this->WriteStruct(guid)) return false;
|
||||
|
||||
// then write each items
|
||||
for (auto iv : *ls) {
|
||||
// MARK: we should not call WriteManagerInt like ReadManagerIntSequence.
|
||||
// because we do not want to write postion info into manager list for each item.
|
||||
if (!this->WriteStruct(iv)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CKStateChunk::WriteXObjectArray(const XContainer::XObjectArray* ls) {
|
||||
// same as WriteObjectIDSequence.
|
||||
return WriteObjectIDSequence(ls);
|
||||
}
|
||||
|
||||
bool CKStateChunk::WriteXObjectPointerArray(const XContainer::XObjectPointerArray* ls) {
|
||||
if (ls == nullptr) return false;
|
||||
|
||||
// convert to id list and pass to id writer.
|
||||
XContainer::XObjectArray conv;
|
||||
for (auto obj : *ls) {
|
||||
if (obj == nullptr) conv.emplace_back(0);
|
||||
else conv.emplace_back(obj->GetID());
|
||||
}
|
||||
|
||||
return WriteObjectIDSequence(conv);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,461 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../VTUtils.hpp"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <cinttypes>
|
||||
#include <compare>
|
||||
|
||||
// ========== Basic Types Section ==========
|
||||
|
||||
namespace LibCmo {
|
||||
|
||||
// Types.
|
||||
// These types are general types used in every module.
|
||||
// So we declare them in LibCmo, not LibCmo::CK2 to make sure every module can use it.
|
||||
|
||||
/**
|
||||
* @brief The representation of constant UTF8 string pointer.
|
||||
*/
|
||||
using CKSTRING = const char8_t*;
|
||||
/**
|
||||
* @brief The representation of mutable CKSTRING (UTF8 string pointer).
|
||||
* @see CKSTRING
|
||||
*/
|
||||
using CKMUTSTRING = char8_t*;
|
||||
/**
|
||||
* @brief The representation of single UTF8 code unit (1 byte).
|
||||
* @remarks
|
||||
* \li Only used with string process.
|
||||
* \li For memory representation and operating, use CKBYTE instead.
|
||||
* @see CKSTRING, CKBYTE
|
||||
*/
|
||||
using CKCHAR = char8_t;
|
||||
|
||||
/**
|
||||
* @brief Platform independent representation of a byte (1 byte, unsigned).
|
||||
* @remarks
|
||||
* \li This type should only be used when representing memory data or position.
|
||||
* \li If you want to represent a character code unit, or a sequence of chars, use CKCHAR instead.
|
||||
* @see CKCHAR
|
||||
*/
|
||||
using CKBYTE = uint8_t;
|
||||
/**
|
||||
* @brief Platform independent representation of a WORD (2 byte, unsigned).
|
||||
*/
|
||||
using CKWORD = uint16_t;
|
||||
/**
|
||||
* @brief Platform independent representation of a DWORD (4 byte, unsigned).
|
||||
* @see CKINT
|
||||
*/
|
||||
using CKDWORD = uint32_t;
|
||||
/**
|
||||
* @brief Platform independent representation of a QWORD (8 byte, unsigned).
|
||||
*/
|
||||
using CKQWORD = uint64_t;
|
||||
|
||||
/**
|
||||
* @brief Platform independent representation of \c int.
|
||||
* @remarks
|
||||
* \li All \c int type presented in original Virtools SDK should be replaced by this type, CKINT, in this project if needed.
|
||||
* \li This type also can be seen as the equvalent of signed CKDWORD.
|
||||
* @see CKDWORD
|
||||
*/
|
||||
using CKINT = int32_t;
|
||||
|
||||
/**
|
||||
* @brief Platform independent representation of a float (32 bit floating point type).
|
||||
*/
|
||||
using CKFLOAT = float;
|
||||
/**
|
||||
* @brief Platform independent representation of a double (64 bit floating point type).
|
||||
*/
|
||||
using CKDOUBLE = double;
|
||||
|
||||
/**
|
||||
* @brief Platform independent representation of a x86 pointer.
|
||||
* @remark
|
||||
* \li This type only should be used when replacing pointer in old Virtools struct / class.
|
||||
* \li Due to Virtools shitty design, in some cases we need read data with x86 memory layout from file.
|
||||
* So we use this type to replace native pointer in struct existed in Virtools SDK
|
||||
* to make sure this program can run perfectly on x64 and more architectures.
|
||||
* \li A example usage can be found in CK2::ObjImpls::CKTexture::Load().
|
||||
*/
|
||||
using CKPTR = uint32_t;
|
||||
|
||||
// Format macro for \c std::printf family of functions
|
||||
|
||||
#define PRI_CKSTRING "s"
|
||||
#define PRI_CKCHAR "c"
|
||||
|
||||
#define CKBYTE_C(v) UINT8_C(v)
|
||||
#define CKWORD_C(v) UINT16_C(v)
|
||||
#define CKDWORD_C(v) UINT32_C(v)
|
||||
#define CKQWORD_C(v) UINT64_C(v)
|
||||
|
||||
#define PRIuCKBYTE PRIu8
|
||||
#define PRIuCKWORD PRIu16
|
||||
#define PRIuCKDWORD PRIu32
|
||||
#define PRIuCKQWORD PRIu64
|
||||
|
||||
#define PRIxCKBYTE PRIx8
|
||||
#define PRIxCKWORD PRIx16
|
||||
#define PRIxCKDWORD PRIx32
|
||||
#define PRIxCKQWORD PRIx64
|
||||
|
||||
#define PRIXCKBYTE PRIX8
|
||||
#define PRIXCKWORD PRIX16
|
||||
#define PRIXCKDWORD PRIX32
|
||||
#define PRIXCKQWORD PRIX64
|
||||
|
||||
#define CKINT_C(v) INT32_C(v)
|
||||
|
||||
#define PRIiCKINT PRIi32
|
||||
|
||||
#define PRIfCKFLOAT "f"
|
||||
#define PRIfCKDOUBLE "lf"
|
||||
#define PRIeCKFLOAT "e"
|
||||
#define PRIeCKDOUBLE "le"
|
||||
|
||||
#define PRIxCKPTR PRIx32
|
||||
#define PRIXCKPTR PRIX32
|
||||
|
||||
/**
|
||||
* @brief The convenient sizeof macro which return \c CKDWORD instead of \c size_t.
|
||||
* @details This macro is frequently used in LibCmo.
|
||||
* Because LibCmo use \c CKDWORD, not \c size_t everywhere.
|
||||
*/
|
||||
#define CKSizeof(_Ty) (static_cast<::LibCmo::CKDWORD>(sizeof(_Ty)))
|
||||
|
||||
}
|
||||
|
||||
// ========== CK2 Section ==========
|
||||
|
||||
/**
|
||||
* @brief The CK2 part of LibCmo. The main part of LibCmo.
|
||||
* @details
|
||||
* This namespace include most implementations of LibCmo,
|
||||
* including important CKContext, CKStateChunk and etc.
|
||||
*/
|
||||
namespace LibCmo::CK2 {
|
||||
|
||||
/**
|
||||
* @brief Unique identifier for all CK2 objects instantiated in CKContext
|
||||
* @remarks
|
||||
* \li Each instance of ObjImpls::CKObject and derived classes are automatically given a global unique ID at creation time.
|
||||
* This ID can be accessed through the ObjImpls::CKObject::GetID() method.
|
||||
* It is safer, though a bit slower, to reference object through their global ID than through a direct pointer.
|
||||
* In some cases the referenced object may be deleted even though the client has a object ID for it.
|
||||
* The client should verify that the referenced object still exists when used via CKContext::GetObject().
|
||||
* \li The global ID for an instance remains unique and unchanged through a application session,
|
||||
* but there is no guarateen that this ID will be the same when a level is saved and loaded back again.
|
||||
* @see ObjImpls::CKObject::GetID(), CKContext::GetObject()
|
||||
*/
|
||||
using CK_ID = CKDWORD;
|
||||
|
||||
/**
|
||||
* @brief The enumeration of all CK2 errors
|
||||
* @details
|
||||
* Some CK2, Vx, XContainer functions will try to return a error code to indicate
|
||||
* whether given operation has been done successfully.
|
||||
*/
|
||||
enum class CKERROR : CKINT {
|
||||
CKERR_OK = 0, /**< Operation successful */
|
||||
CKERR_INVALIDPARAMETER = -1, /**< One of the parameter passed to the function was invalid */
|
||||
CKERR_INVALIDPARAMETERTYPE = -2, /**< One of the parameter passed to the function was invalid */
|
||||
CKERR_INVALIDSIZE = -3, /**< The parameter size was invalid */
|
||||
CKERR_INVALIDOPERATION = -4, /**< The operation type didn't exist */
|
||||
CKERR_OPERATIONNOTIMPLEMENTED = -5, /**< The function used to execute the operation is not yet implemented */
|
||||
CKERR_OUTOFMEMORY = -6, /**< There was not enough memory to perform the action */
|
||||
CKERR_NOTIMPLEMENTED = -7, /**< The function is not yet implemented */
|
||||
CKERR_NOTFOUND = -11, /**< There was an attempt to remove something not present */
|
||||
CKERR_NOLEVEL = -13, /**< There is no level currently created */
|
||||
CKERR_CANCREATERENDERCONTEXT = -14, /**< */
|
||||
CKERR_NOTIFICATIONNOTHANDLED = -16, /**< The notification message was not used */
|
||||
CKERR_ALREADYPRESENT = -17, /**< Attempt to add an item that was already present */
|
||||
CKERR_INVALIDRENDERCONTEXT = -18, /**< the render context is not valid */
|
||||
CKERR_RENDERCONTEXTINACTIVE = -19, /**< the render context is not activated for rendering */
|
||||
CKERR_NOLOADPLUGINS = -20, /**< there was no plugins to load this kind of file */
|
||||
CKERR_NOSAVEPLUGINS = -21, /**< there was no plugins to save this kind of file */
|
||||
CKERR_INVALIDFILE = -22, /**< attempt to load an invalid file */
|
||||
CKERR_INVALIDPLUGIN = -23, /**< attempt to load with an invalid plugin */
|
||||
CKERR_NOTINITIALIZED = -24, /**< attempt use an object that wasnt initialized */
|
||||
CKERR_INVALIDMESSAGE = -25, /**< attempt use a message type that wasn't registred */
|
||||
CKERR_INVALIDPROTOTYPE = -28, /**< attempt use an invalid prototype */
|
||||
CKERR_NODLLFOUND = -29, /**< No dll file found in the parse directory */
|
||||
CKERR_ALREADYREGISTREDDLL = -30, /**< this dll has already been registred */
|
||||
CKERR_INVALIDDLL = -31, /**< this dll does not contain information to create the prototype */
|
||||
CKERR_INVALIDOBJECT = -34, /**< Invalid Object (attempt to Get an object from an invalid ID) */
|
||||
CKERR_INVALIDCONDSOLEWINDOW = -35, /**< Invalid window was provided as console window */
|
||||
CKERR_INVALIDKINEMATICCHAIN = -36, /**< Invalid kinematic chain ( end and start effector may not be part of the same hierarchy ) */
|
||||
CKERR_NOKEYBOARD = -37, /**< Keyboard not attached or not working properly */
|
||||
CKERR_NOMOUSE = -38, /**< Mouse not attached or not working properly */
|
||||
CKERR_NOJOYSTICK = -39, /**< Joystick not attached or not working properly */
|
||||
CKERR_INCOMPATIBLEPARAMETERS = -40, /**< Try to link imcompatible Parameters */
|
||||
CKERR_NORENDERENGINE = -44, /**< There is no render engine dll */
|
||||
CKERR_NOCURRENTLEVEL = -45, /**< There is no current level (use CKSetCurrentLevel ) */
|
||||
CKERR_SOUNDDISABLED = -46, /**< Sound Management has been disabled */
|
||||
CKERR_DINPUTDISABLED = -47, /**< DirectInput Management has been disabled */
|
||||
CKERR_INVALIDGUID = -48, /**< Guid is already in use or invalid */
|
||||
CKERR_NOTENOUGHDISKPLACE = -49, /**< There was no more free space on disk when trying to save a file */
|
||||
CKERR_CANTWRITETOFILE = -50, /**< Impossible to write to file (write-protection ?) */
|
||||
CKERR_BEHAVIORADDDENIEDBYCB = -51, /**< The behavior cannnot be added to this entity */
|
||||
CKERR_INCOMPATIBLECLASSID = -52, /**< The behavior cannnot be added to this entity */
|
||||
CKERR_MANAGERALREADYEXISTS = -53, /**< A manager was registered more than once */
|
||||
CKERR_PAUSED = -54, /**< CKprocess or TimeManager process while CK is paused will fail */
|
||||
CKERR_PLUGINSMISSING = -55, /**< Some plugins were missing whileloading a file */
|
||||
CKERR_OBSOLETEVIRTOOLS = -56, /**< Virtools version too old to load this file */
|
||||
CKERR_FILECRCERROR = -57, /**< CRC Error while loading file */
|
||||
CKERR_ALREADYFULLSCREEN = -58, /**< A Render context is already in Fullscreen Mode */
|
||||
CKERR_CANCELLED = -59, /**< Operation was cancelled by user */
|
||||
CKERR_NOANIMATIONKEY = -121, /**< there were no animation key at the given index */
|
||||
CKERR_INVALIDINDEX = -122, /**< attemp to acces an animation key with an invalid index */
|
||||
CKERR_INVALIDANIMATION = -123, /**< the animation is invalid (no entity associated or zero length) */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Unique class identifier.
|
||||
* @remarks
|
||||
* \li Each class derived from the ObjImpls::CKObject class has a unique class ID.
|
||||
* \li This ID can be accessed through each instance of these classes, with the ObjImpls::CKObject::GetClassID() method.
|
||||
* \li This class ID is used internally for various matching operations, like matching behaviors on objects, etc..
|
||||
* \li Identifiers listed in there is CK2 builtin class identifier list.
|
||||
* In original Virtools SDK, user can use plugin mechanism to register more class identifier in runtime.
|
||||
* Virtools only guarateen that identifiers listed in there must correspond with its real meaning.
|
||||
* However, there is no guarateen that IDs not listed in there will be the same when Virtools engine quit and initialized back again.
|
||||
* @see ObjImpls::CKObject::GetClassID(), CKIsChildClassOf()
|
||||
*/
|
||||
enum class CK_CLASSID : CKINT {
|
||||
CKCID_OBJECT = 1,
|
||||
CKCID_PARAMETERIN = 2,
|
||||
CKCID_PARAMETEROPERATION = 4,
|
||||
CKCID_STATE = 5,
|
||||
CKCID_BEHAVIORLINK = 6,
|
||||
CKCID_BEHAVIOR = 8,
|
||||
CKCID_BEHAVIORIO = 9,
|
||||
CKCID_RENDERCONTEXT = 12,
|
||||
CKCID_KINEMATICCHAIN = 13,
|
||||
CKCID_SCENEOBJECT = 11,
|
||||
CKCID_OBJECTANIMATION = 15,
|
||||
CKCID_ANIMATION = 16,
|
||||
CKCID_KEYEDANIMATION = 18,
|
||||
CKCID_BEOBJECT = 19,
|
||||
CKCID_DATAARRAY = 52,
|
||||
CKCID_SCENE = 10,
|
||||
CKCID_LEVEL = 21,
|
||||
CKCID_PLACE = 22,
|
||||
CKCID_GROUP = 23,
|
||||
CKCID_SOUND = 24,
|
||||
CKCID_WAVESOUND = 25,
|
||||
CKCID_MIDISOUND = 26,
|
||||
CKCID_MATERIAL = 30,
|
||||
CKCID_TEXTURE = 31,
|
||||
CKCID_MESH = 32,
|
||||
CKCID_PATCHMESH = 53,
|
||||
CKCID_RENDEROBJECT = 47,
|
||||
CKCID_2DENTITY = 27,
|
||||
CKCID_SPRITE = 28,
|
||||
CKCID_SPRITETEXT = 29,
|
||||
CKCID_3DENTITY = 33,
|
||||
CKCID_GRID = 50,
|
||||
CKCID_CURVEPOINT = 36,
|
||||
CKCID_SPRITE3D = 37,
|
||||
CKCID_CURVE = 43,
|
||||
CKCID_CAMERA = 34,
|
||||
CKCID_TARGETCAMERA = 35,
|
||||
CKCID_LIGHT = 38,
|
||||
CKCID_TARGETLIGHT = 39,
|
||||
CKCID_CHARACTER = 40,
|
||||
CKCID_3DOBJECT = 41,
|
||||
CKCID_BODYPART = 42,
|
||||
CKCID_PARAMETER = 46,
|
||||
CKCID_PARAMETERLOCAL = 45,
|
||||
CKCID_PARAMETEROUT = 3,
|
||||
CKCID_INTERFACEOBJECTMANAGER = 48,
|
||||
CKCID_CRITICALSECTION = 49,
|
||||
CKCID_LAYER = 51,
|
||||
CKCID_PROGRESSIVEMESH = 54,
|
||||
CKCID_SYNCHRO = 20,
|
||||
CKCID_OBJECTARRAY = 80,
|
||||
CKCID_SCENEOBJECTDESC = 81,
|
||||
CKCID_ATTRIBUTEMANAGER = 82,
|
||||
CKCID_MESSAGEMANAGER = 83,
|
||||
CKCID_COLLISIONMANAGER = 84,
|
||||
CKCID_OBJECTMANAGER = 85,
|
||||
CKCID_FLOORMANAGER = 86,
|
||||
CKCID_RENDERMANAGER = 87,
|
||||
CKCID_BEHAVIORMANAGER = 88,
|
||||
CKCID_INPUTMANAGER = 89,
|
||||
CKCID_PARAMETERMANAGER = 90,
|
||||
CKCID_GRIDMANAGER = 91,
|
||||
CKCID_SOUNDMANAGER = 92,
|
||||
CKCID_TIMEMANAGER = 93,
|
||||
CKCID_CUIKBEHDATA = -1,
|
||||
CKCID_MAXCLASSID = 55,
|
||||
CKCID_MAXMAXCLASSID = 128,
|
||||
};
|
||||
|
||||
// ========== Type Definition ==========
|
||||
// type define
|
||||
|
||||
using CKParameterType = CKINT;
|
||||
using CKOperationType = CKINT;
|
||||
using CKMessageType = CKINT;
|
||||
using CKAttributeType = CKINT;
|
||||
using CKAttributeCategory = CKINT;
|
||||
|
||||
// format constant
|
||||
#define PRIuCKID PRIuCKDWORD
|
||||
#define PRIiCKERROR PRIiCKINT
|
||||
#define PRIiCLASSID PRIiCKINT
|
||||
|
||||
// ========== Class List ==========
|
||||
// We declare these classes in there to make sure that
|
||||
// following code can refer their pointer type safely.
|
||||
|
||||
// Objects and derivated classes
|
||||
namespace ObjImpls {
|
||||
class CKObject;
|
||||
class CKInterfaceObjectManager;
|
||||
class CKRenderContext;
|
||||
class CKParameterIn;
|
||||
class CKParameter;
|
||||
class CKParameterOut;
|
||||
class CKParameterLocal;
|
||||
class CKParameterOperation;
|
||||
class CKBehaviorLink;
|
||||
class CKBehaviorIO;
|
||||
class CKRenderContext;
|
||||
class CKSynchroObject;
|
||||
class CKStateObject;
|
||||
class CKCriticalSectionObject;
|
||||
class CKKinematicChain;
|
||||
class CKObjectAnimation;
|
||||
class CKLayer;
|
||||
class CKSceneObject;
|
||||
class CKBehavior;
|
||||
class CKAnimation;
|
||||
class CKKeyedAnimation;
|
||||
class CKBeObject;
|
||||
class CKScene;
|
||||
class CKLevel;
|
||||
class CKPlace;
|
||||
class CKGroup;
|
||||
class CKMaterial;
|
||||
class CKTexture;
|
||||
class CKMesh;
|
||||
class CKPatchMesh;
|
||||
class CKProgressiveMesh;
|
||||
class CKDataArray;
|
||||
class CKSound;
|
||||
class CKMidiSound;
|
||||
class CKWaveSound;
|
||||
class CKRenderObject;
|
||||
class CK2dEntity;
|
||||
class CKSprite;
|
||||
class CKSpriteText;
|
||||
class CK3dEntity;
|
||||
class CKCamera;
|
||||
class CKTargetCamera;
|
||||
class CKCurvePoint;
|
||||
class CKSprite3D;
|
||||
class CKLight;
|
||||
class CKTargetLight;
|
||||
class CKCharacter;
|
||||
class CK3dObject;
|
||||
class CKBodyPart;
|
||||
class CKCurve;
|
||||
class CKGrid;
|
||||
}
|
||||
|
||||
// Misc
|
||||
class CKBehaviorPrototype;
|
||||
class CKMessage;
|
||||
class CK2dCurvePoint;
|
||||
class CK2dCurve;
|
||||
//class CKStateChunk;
|
||||
//class CKFile;
|
||||
class CKDependencies;
|
||||
class CKDependenciesContext;
|
||||
class CKDebugContext;
|
||||
class CKObjectArray;
|
||||
class CKObjectDeclaration;
|
||||
//class CKContext;
|
||||
class CKBitmapProperties;
|
||||
class CKFileExtension;
|
||||
class CKVertexBuffer;
|
||||
|
||||
// Managers
|
||||
namespace MgrImpls {
|
||||
class CKBaseManager;
|
||||
class CKObjectManager;
|
||||
class CKSoundManager;
|
||||
class CKTimeManager;
|
||||
class CKRenderManager;
|
||||
class CKBehaviorManager;
|
||||
class CKMessageManager;
|
||||
class CKParameterManager;
|
||||
class CKAttributeManager;
|
||||
class CKPathManager;
|
||||
class CKVariableManager;
|
||||
class CKSceneObjectDesc;
|
||||
class CKPluginManager;
|
||||
}
|
||||
|
||||
// Data Handlers
|
||||
namespace DataHandlers {
|
||||
class CKBitmapHandler;
|
||||
class CKMovieHandler;
|
||||
class CKSoundHandler;
|
||||
}
|
||||
|
||||
// Important classes (rewritten hugely)
|
||||
class CKContext;
|
||||
class CKStateChunk;
|
||||
class CKFileReader;
|
||||
class CKFileWriter;
|
||||
class CKFileVisitor;
|
||||
|
||||
/**
|
||||
* @brief Global unique identifier struture.
|
||||
* @remarks
|
||||
* \li Guids are used to uniquely identify plugins, operation types, parameter types and behavior prototypes.
|
||||
* \li Comparison operators are defined so CKGUID can be compared with ==, != , <, > operators.
|
||||
* \li It's defined as following code
|
||||
* \code
|
||||
* typedef struct CKGUID {
|
||||
* union {
|
||||
* struct { CKDWORD d1,d2; };
|
||||
* CKDWORD d[2];
|
||||
* };
|
||||
* };
|
||||
* \endcode
|
||||
*/
|
||||
struct CKGUID {
|
||||
CKDWORD d1, d2;
|
||||
|
||||
constexpr CKGUID(CKDWORD gd1 = 0, CKDWORD gd2 = 0) : d1(gd1), d2(gd2) {}
|
||||
CKGUID(const CKGUID& rhs) : d1(rhs.d1), d2(rhs.d2) {}
|
||||
CKGUID(CKGUID&& rhs) : d1(rhs.d1), d2(rhs.d2) {}
|
||||
CKGUID& operator=(const CKGUID& rhs) {
|
||||
this->d1 = rhs.d1;
|
||||
this->d2 = rhs.d2;
|
||||
return *this;
|
||||
}
|
||||
CKGUID& operator=(CKGUID&& rhs) {
|
||||
this->d1 = rhs.d1;
|
||||
this->d2 = rhs.d2;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto operator<=>(const CKGUID& rhs) const {
|
||||
if (auto cmp = this->d1 <=> rhs.d1; cmp != 0) return cmp;
|
||||
return this->d2 <=> rhs.d2;
|
||||
}
|
||||
bool operator==(const CKGUID& rhs) const {
|
||||
return ((this->d1 == rhs.d1) && (this->d2 == rhs.d2));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,520 +0,0 @@
|
||||
#include "CKBitmapHandler.hpp"
|
||||
#include <stb_image.h>
|
||||
#include <stb_image_write.h>
|
||||
|
||||
namespace LibCmo::CK2::DataHandlers {
|
||||
|
||||
#pragma region Help Functions
|
||||
|
||||
/*
|
||||
ABGR8888 is format used by std image.
|
||||
The data is placed in buffer with RGBA order, so the format is ABGR.
|
||||
BGR888 also is non-alpha format used by std image.
|
||||
The data is placed in buffer with RGB order, so the format is BGR.
|
||||
ARGB8888 is Virtools standard.
|
||||
The data is placed in buffer with BGRA order.
|
||||
*/
|
||||
|
||||
// MARK: for std-image size, we use `n * CKSizeof(CKBYTE)` to calc offset.
|
||||
// for virtools size, we use `VxMath::VxImageDescEx::ColorFactorSize` and `VxMath::VxImageDescEx::PixelSize` to calc offset.
|
||||
|
||||
static void ABGRToARGB(CKDWORD count, const void* _abgr, void* _argb) {
|
||||
const CKBYTE* abgr = static_cast<const CKBYTE*>(_abgr);
|
||||
CKBYTE* argb = static_cast<CKBYTE*>(_argb);
|
||||
// copy R
|
||||
VxMath::VxCopyStructure(
|
||||
count,
|
||||
argb + (2u * VxMath::VxImageDescEx::ColorFactorSize),
|
||||
VxMath::VxImageDescEx::PixelSize,
|
||||
VxMath::VxImageDescEx::ColorFactorSize,
|
||||
abgr + (0u * CKSizeof(CKBYTE)),
|
||||
4u * CKSizeof(CKBYTE)
|
||||
);
|
||||
// copy G
|
||||
VxMath::VxCopyStructure(
|
||||
count,
|
||||
argb + (1u * VxMath::VxImageDescEx::ColorFactorSize),
|
||||
VxMath::VxImageDescEx::PixelSize,
|
||||
VxMath::VxImageDescEx::ColorFactorSize,
|
||||
abgr + (1u * CKSizeof(CKBYTE)),
|
||||
4u * CKSizeof(CKBYTE)
|
||||
);
|
||||
// copy B
|
||||
VxMath::VxCopyStructure(
|
||||
count,
|
||||
argb + (0u * VxMath::VxImageDescEx::ColorFactorSize),
|
||||
VxMath::VxImageDescEx::PixelSize,
|
||||
VxMath::VxImageDescEx::ColorFactorSize,
|
||||
abgr + (2u * CKSizeof(CKBYTE)),
|
||||
4u * CKSizeof(CKBYTE)
|
||||
);
|
||||
// copy A
|
||||
VxMath::VxCopyStructure(
|
||||
count,
|
||||
argb + (3u * VxMath::VxImageDescEx::ColorFactorSize),
|
||||
VxMath::VxImageDescEx::PixelSize,
|
||||
VxMath::VxImageDescEx::ColorFactorSize,
|
||||
abgr + (3u * CKSizeof(CKBYTE)),
|
||||
4u * CKSizeof(CKBYTE)
|
||||
);
|
||||
}
|
||||
|
||||
static void ARGBToABGR(CKDWORD count, const void* _argb, void* _abgr) {
|
||||
const CKBYTE* argb = static_cast<const CKBYTE*>(_argb);
|
||||
CKBYTE* abgr = static_cast<CKBYTE*>(_abgr);
|
||||
// copy R
|
||||
VxMath::VxCopyStructure(
|
||||
count,
|
||||
abgr + (0u * CKSizeof(CKBYTE)),
|
||||
4u * CKSizeof(CKBYTE),
|
||||
VxMath::VxImageDescEx::ColorFactorSize,
|
||||
argb + (2u * VxMath::VxImageDescEx::ColorFactorSize),
|
||||
VxMath::VxImageDescEx::PixelSize
|
||||
);
|
||||
// copy G
|
||||
VxMath::VxCopyStructure(
|
||||
count,
|
||||
abgr + (1u * CKSizeof(CKBYTE)),
|
||||
4u * CKSizeof(CKBYTE),
|
||||
VxMath::VxImageDescEx::ColorFactorSize,
|
||||
argb + (1u * VxMath::VxImageDescEx::ColorFactorSize),
|
||||
VxMath::VxImageDescEx::PixelSize
|
||||
);
|
||||
// copy B
|
||||
VxMath::VxCopyStructure(
|
||||
count,
|
||||
abgr + (2u * CKSizeof(CKBYTE)),
|
||||
4u * CKSizeof(CKBYTE),
|
||||
VxMath::VxImageDescEx::ColorFactorSize,
|
||||
argb + (0u * VxMath::VxImageDescEx::ColorFactorSize),
|
||||
VxMath::VxImageDescEx::PixelSize
|
||||
);
|
||||
// copy A
|
||||
VxMath::VxCopyStructure(
|
||||
count,
|
||||
abgr + (3u * CKSizeof(CKBYTE)),
|
||||
4u * CKSizeof(CKBYTE),
|
||||
VxMath::VxImageDescEx::ColorFactorSize,
|
||||
argb + (3u * VxMath::VxImageDescEx::ColorFactorSize),
|
||||
VxMath::VxImageDescEx::PixelSize
|
||||
);
|
||||
}
|
||||
|
||||
static void ARGBToBGR(CKDWORD count, const void* _argb, void* _bgr) {
|
||||
const CKBYTE* argb = static_cast<const CKBYTE*>(_argb);
|
||||
CKBYTE* bgr = static_cast<CKBYTE*>(_bgr);
|
||||
// copy R
|
||||
VxMath::VxCopyStructure(
|
||||
count,
|
||||
bgr + (0u * CKSizeof(CKBYTE)),
|
||||
3u * CKSizeof(CKBYTE),
|
||||
VxMath::VxImageDescEx::ColorFactorSize,
|
||||
argb + (2u * VxMath::VxImageDescEx::ColorFactorSize),
|
||||
VxMath::VxImageDescEx::PixelSize
|
||||
);
|
||||
// copy G
|
||||
VxMath::VxCopyStructure(
|
||||
count,
|
||||
bgr + (1u * CKSizeof(CKBYTE)),
|
||||
3u * CKSizeof(CKBYTE),
|
||||
VxMath::VxImageDescEx::ColorFactorSize,
|
||||
argb + (1u * VxMath::VxImageDescEx::ColorFactorSize),
|
||||
VxMath::VxImageDescEx::PixelSize
|
||||
);
|
||||
// copy B
|
||||
VxMath::VxCopyStructure(
|
||||
count,
|
||||
bgr + (2u * CKSizeof(CKBYTE)),
|
||||
3u * CKSizeof(CKBYTE),
|
||||
VxMath::VxImageDescEx::ColorFactorSize,
|
||||
argb + (0u * VxMath::VxImageDescEx::ColorFactorSize),
|
||||
VxMath::VxImageDescEx::PixelSize
|
||||
);
|
||||
// skip A factor
|
||||
}
|
||||
|
||||
static bool StbReadFile(CKSTRING u8filename, VxMath::VxImageDescEx* read_image) {
|
||||
if (u8filename == nullptr || read_image == nullptr) return false;
|
||||
FILE* fs = YYCC::IOHelper::UTF8FOpen(u8filename, u8"rb");
|
||||
if (fs == nullptr) return false;
|
||||
|
||||
// read data
|
||||
int x, y, channels_in_file;
|
||||
stbi_uc* data = stbi_load_from_file(fs, &x, &y, &channels_in_file, 4); // 4 == RGBA8888
|
||||
std::fclose(fs);
|
||||
if (data == nullptr) return false;
|
||||
|
||||
// create read image
|
||||
read_image->CreateImage(static_cast<CKDWORD>(x), static_cast<CKDWORD>(y));
|
||||
|
||||
// copy data
|
||||
ABGRToARGB(read_image->GetPixelCount(), data, read_image->GetMutableImage());
|
||||
|
||||
// clear data
|
||||
stbi_image_free(data);
|
||||
return true;
|
||||
}
|
||||
static bool StbReadMemory(const void* memory, CKDWORD size, VxMath::VxImageDescEx* read_image) {
|
||||
// same like ReadFile
|
||||
if (memory == nullptr || read_image == nullptr) return false;
|
||||
|
||||
// read data
|
||||
int x, y, channels_in_file;
|
||||
stbi_uc* data = stbi_load_from_memory(
|
||||
static_cast<const stbi_uc*>(memory),
|
||||
static_cast<int>(size),
|
||||
&x, &y, &channels_in_file, 4 // 4 == RGBA8888
|
||||
);
|
||||
if (data == nullptr) return false;
|
||||
|
||||
// create read image
|
||||
read_image->CreateImage(static_cast<CKDWORD>(x), static_cast<CKDWORD>(y));
|
||||
|
||||
// copy data
|
||||
ABGRToARGB(read_image->GetPixelCount(), data, read_image->GetMutableImage());
|
||||
|
||||
// clear data
|
||||
stbi_image_free(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct FileSaveContext {
|
||||
FileSaveContext(FILE* fs) :
|
||||
m_Fs(fs), m_Counter(0) {}
|
||||
FILE* m_Fs;
|
||||
size_t m_Counter;
|
||||
};
|
||||
static void FileWriteFunction(void* context, void* data, int size) {
|
||||
FileSaveContext* ctx = static_cast<FileSaveContext*>(context);
|
||||
if (ctx->m_Fs != nullptr) {
|
||||
std::fwrite(data, sizeof(CKBYTE), size, ctx->m_Fs);
|
||||
}
|
||||
ctx->m_Counter += size;
|
||||
}
|
||||
struct MemorySaveContext {
|
||||
MemorySaveContext(void* mem) :
|
||||
m_Mem(mem), m_Counter(0) {}
|
||||
void* m_Mem;
|
||||
size_t m_Counter;
|
||||
};
|
||||
static void MemoryWriteFunction(void* context, void* data, int size) {
|
||||
MemorySaveContext* ctx = static_cast<MemorySaveContext*>(context);
|
||||
if (ctx->m_Mem != nullptr) {
|
||||
std::memcpy(ctx->m_Mem, data, size);
|
||||
ctx->m_Mem = static_cast<CKBYTE*>(ctx->m_Mem) + size;
|
||||
}
|
||||
ctx->m_Counter += size;
|
||||
}
|
||||
|
||||
using SaveOperation = std::function<int(stbi_write_func*, void*, int, int, int, const void*)>;
|
||||
static bool StbSaveFile(CKSTRING u8filename, const VxMath::VxImageDescEx* write_image, bool save_alpha, SaveOperation oper) {
|
||||
if (u8filename == nullptr || write_image == nullptr) return false;
|
||||
if (!write_image->IsValid()) return false;
|
||||
FILE* fs = YYCC::IOHelper::UTF8FOpen(u8filename, u8"wb");
|
||||
if (fs == nullptr) return false;
|
||||
|
||||
// allocate buffer and convert data from ARGB to RGBA or RGB
|
||||
CKBYTE* data = nullptr;
|
||||
int channel_count = 0;
|
||||
if (save_alpha) {
|
||||
// save with alpha
|
||||
data = new CKBYTE[CKSizeof(CKBYTE) * 4u * write_image->GetWidth() * write_image->GetHeight()];
|
||||
ARGBToABGR(write_image->GetPixelCount(), write_image->GetImage(), data);
|
||||
channel_count = 4;
|
||||
} else {
|
||||
// save without alpha
|
||||
data = new CKBYTE[CKSizeof(CKBYTE) * 3u * write_image->GetWidth() * write_image->GetHeight()];
|
||||
ARGBToBGR(write_image->GetPixelCount(), write_image->GetImage(), data);
|
||||
channel_count = 3;
|
||||
}
|
||||
|
||||
// write data
|
||||
FileSaveContext* ctx = new FileSaveContext(fs);
|
||||
int ret = oper(
|
||||
&FileWriteFunction, ctx,
|
||||
static_cast<int>(write_image->GetWidth()), static_cast<int>(write_image->GetHeight()),
|
||||
channel_count, data // 4 == RGBA8888
|
||||
);
|
||||
|
||||
// free data
|
||||
delete ctx;
|
||||
delete[] data;
|
||||
std::fclose(fs);
|
||||
|
||||
// ret is 0 mean failed.
|
||||
return ret != 0;
|
||||
}
|
||||
static CKDWORD StbSaveMemory(void* memory, const VxMath::VxImageDescEx* write_image, bool save_alpha, SaveOperation oper) {
|
||||
if (write_image == nullptr) return 0;
|
||||
if (!write_image->IsValid()) return 0;
|
||||
|
||||
// allocate buffer and convert data from ARGB to RGBA or RGB
|
||||
CKBYTE* data = nullptr;
|
||||
int channel_count = 0;
|
||||
if (save_alpha) {
|
||||
// save with alpha
|
||||
data = new CKBYTE[CKSizeof(CKBYTE) * 4u * write_image->GetWidth() * write_image->GetHeight()];
|
||||
ARGBToABGR(write_image->GetPixelCount(), write_image->GetImage(), data);
|
||||
channel_count = 4;
|
||||
} else {
|
||||
// save without alpha
|
||||
data = new CKBYTE[CKSizeof(CKBYTE) * 3u * write_image->GetWidth() * write_image->GetHeight()];
|
||||
ARGBToBGR(write_image->GetPixelCount(), write_image->GetImage(), data);
|
||||
channel_count = 3;
|
||||
}
|
||||
|
||||
// write data
|
||||
MemorySaveContext* ctx = new MemorySaveContext(memory);
|
||||
int ret = oper(
|
||||
&MemoryWriteFunction, ctx,
|
||||
static_cast<int>(write_image->GetWidth()), static_cast<int>(write_image->GetHeight()),
|
||||
channel_count, data // 4 == RGBA8888
|
||||
);
|
||||
|
||||
// free data
|
||||
CKDWORD expected = static_cast<CKDWORD>(ctx->m_Counter);
|
||||
delete ctx;
|
||||
delete[] data;
|
||||
|
||||
// ret is 0 mean failed. return zero size.
|
||||
if (ret == 0) return 0;
|
||||
else return expected;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region CKBitmapBMPHandler
|
||||
|
||||
static const CKBitmapProperties g_BMPProperties(CKGUID(0xBCA97223u, 0x48578BCAu), u8"Bmp");
|
||||
|
||||
CKBitmapBMPHandler::CKBitmapBMPHandler() :
|
||||
CKBitmapHandler() {}
|
||||
|
||||
CKBitmapBMPHandler::~CKBitmapBMPHandler() {}
|
||||
|
||||
const CKBitmapProperties& CKBitmapBMPHandler::GetBitmapDefaultProperties() {
|
||||
return g_BMPProperties;
|
||||
}
|
||||
|
||||
bool CKBitmapBMPHandler::ReadFile(CKSTRING u8filename, VxMath::VxImageDescEx* read_image) {
|
||||
return StbReadFile(u8filename, read_image);
|
||||
}
|
||||
|
||||
bool CKBitmapBMPHandler::ReadMemory(const void* memory, CKDWORD size, VxMath::VxImageDescEx* read_image) {
|
||||
return StbReadMemory(memory, size, read_image);
|
||||
}
|
||||
|
||||
// MARK: when stb-image writing bmp file with alpha channel, it will create a very rare bmp file supporting alpha channel.
|
||||
// this format is not supported by virtools and will result blank image.
|
||||
// so we create an alpha option to forcely change channel count to 3 (RGB) then the bmp writer can work normally.
|
||||
|
||||
bool CKBitmapBMPHandler::SaveFile(CKSTRING u8filename, const VxMath::VxImageDescEx* write_image, const CKBitmapProperties& codec_param) {
|
||||
return StbSaveFile(u8filename, write_image, false, // bmp do not support alpha
|
||||
[&codec_param](stbi_write_func* func, void* context, int w, int h, int comp, const void* data) -> int {
|
||||
return stbi_write_bmp_to_func(func, context, w, h, comp, data);
|
||||
});
|
||||
}
|
||||
|
||||
CKDWORD CKBitmapBMPHandler::SaveMemory(void* memory, const VxMath::VxImageDescEx* write_image, const CKBitmapProperties& codec_param) {
|
||||
return StbSaveMemory(memory, write_image, false, // bmp do not support alpha
|
||||
[&codec_param](stbi_write_func* func, void* context, int w, int h, int comp, const void* data) -> int {
|
||||
return stbi_write_bmp_to_func(func, context, w, h, comp, data);
|
||||
});
|
||||
}
|
||||
|
||||
bool CKBitmapBMPHandler::CanSaveAlpha() {
|
||||
return false;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region CKBitmapTGAHandler
|
||||
|
||||
static const CKBitmapProperties g_TGAProperties(CKGUID(0x585C7216u, 0x33302657u), u8"Tga");
|
||||
|
||||
CKBitmapTGAHandler::CKBitmapTGAHandler() :
|
||||
CKBitmapHandler() {}
|
||||
|
||||
CKBitmapTGAHandler::~CKBitmapTGAHandler() {}
|
||||
|
||||
const CKBitmapProperties& CKBitmapTGAHandler::GetBitmapDefaultProperties() {
|
||||
return g_TGAProperties;
|
||||
}
|
||||
|
||||
bool CKBitmapTGAHandler::ReadFile(CKSTRING u8filename, VxMath::VxImageDescEx* read_image) {
|
||||
return StbReadFile(u8filename, read_image);
|
||||
}
|
||||
|
||||
bool CKBitmapTGAHandler::ReadMemory(const void* memory, CKDWORD size, VxMath::VxImageDescEx* read_image) {
|
||||
return StbReadMemory(memory, size, read_image);
|
||||
}
|
||||
|
||||
bool CKBitmapTGAHandler::SaveFile(CKSTRING u8filename, const VxMath::VxImageDescEx* write_image, const CKBitmapProperties& codec_param) {
|
||||
return StbSaveFile(u8filename, write_image, false, // tga support alpha
|
||||
[&codec_param](stbi_write_func* func, void* context, int w, int h, int comp, const void* data) -> int {
|
||||
return stbi_write_tga_to_func(func, context, w, h, comp, data);
|
||||
});
|
||||
}
|
||||
|
||||
CKDWORD CKBitmapTGAHandler::SaveMemory(void* memory, const VxMath::VxImageDescEx* write_image, const CKBitmapProperties& codec_param) {
|
||||
return StbSaveMemory(memory, write_image, false, // tga support alpha
|
||||
[&codec_param](stbi_write_func* func, void* context, int w, int h, int comp, const void* data) -> int {
|
||||
return stbi_write_tga_to_func(func, context, w, h, comp, data);
|
||||
});
|
||||
}
|
||||
|
||||
bool CKBitmapTGAHandler::CanSaveAlpha() {
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region CKBitmapJPGHandler
|
||||
|
||||
// MARK: this GUID is gotten from Virtools 3.5 Plugins.
|
||||
static const CKBitmapProperties g_JPGProperties(CKGUID(0x4AE51AC4u, 0x04587D76u), u8"jpg");
|
||||
// MARK: this quality is gotten from default value of virtools.
|
||||
constexpr int g_JPGDefaultQuality = 75;
|
||||
|
||||
CKBitmapJPGHandler::CKBitmapJPGHandler() :
|
||||
CKBitmapHandler() {}
|
||||
|
||||
CKBitmapJPGHandler::~CKBitmapJPGHandler() {}
|
||||
|
||||
const CKBitmapProperties& CKBitmapJPGHandler::GetBitmapDefaultProperties() {
|
||||
return g_JPGProperties;
|
||||
}
|
||||
|
||||
bool CKBitmapJPGHandler::ReadFile(CKSTRING u8filename, VxMath::VxImageDescEx* read_image) {
|
||||
return StbReadFile(u8filename, read_image);
|
||||
}
|
||||
|
||||
bool CKBitmapJPGHandler::ReadMemory(const void* memory, CKDWORD size, VxMath::VxImageDescEx* read_image) {
|
||||
return StbReadMemory(memory, size, read_image);
|
||||
}
|
||||
|
||||
bool CKBitmapJPGHandler::SaveFile(CKSTRING u8filename, const VxMath::VxImageDescEx* write_image, const CKBitmapProperties& codec_param) {
|
||||
return StbSaveFile(u8filename, write_image, false, // jpg do not support alpha
|
||||
[&codec_param](stbi_write_func* func, void* context, int w, int h, int comp, const void* data) -> int {
|
||||
return stbi_write_jpg_to_func(func, context, w, h, comp, data, g_JPGDefaultQuality);
|
||||
});
|
||||
}
|
||||
|
||||
CKDWORD CKBitmapJPGHandler::SaveMemory(void* memory, const VxMath::VxImageDescEx* write_image, const CKBitmapProperties& codec_param) {
|
||||
return StbSaveMemory(memory, write_image, false, // jpg do not support alpha
|
||||
[&codec_param](stbi_write_func* func, void* context, int w, int h, int comp, const void* data) -> int {
|
||||
return stbi_write_jpg_to_func(func, context, w, h, comp, data, g_JPGDefaultQuality);
|
||||
});
|
||||
}
|
||||
|
||||
bool CKBitmapJPGHandler::CanSaveAlpha() {
|
||||
return false;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region CKBitmapPNGHandler
|
||||
|
||||
// MARK: this GUID is gotten from Virtools 3.5 Plugins.
|
||||
static const CKBitmapProperties g_PNGProperties(CKGUID(0x02D45C7Bu, 0x4AAC16ECu), u8"png");
|
||||
// MARK: this is compress level gotten from default value of virtools.
|
||||
constexpr int g_PNGDefaultCompressLevel = 3;
|
||||
|
||||
/**
|
||||
* @brief A helper function to get stride parameter passed to png writer.
|
||||
* @param width[in] The width given by general stb writer wrapper.
|
||||
* @param comp[in] The comp given by general stb writer wrapper.
|
||||
* @return The stride data passed to real stb writer.
|
||||
*/
|
||||
static int StbPngStrideGetter(int width, int comp) {
|
||||
return width * comp;
|
||||
}
|
||||
|
||||
CKBitmapPNGHandler::CKBitmapPNGHandler() :
|
||||
CKBitmapHandler() {}
|
||||
|
||||
CKBitmapPNGHandler::~CKBitmapPNGHandler() {}
|
||||
|
||||
const CKBitmapProperties& CKBitmapPNGHandler::GetBitmapDefaultProperties() {
|
||||
return g_PNGProperties;
|
||||
}
|
||||
|
||||
bool CKBitmapPNGHandler::ReadFile(CKSTRING u8filename, VxMath::VxImageDescEx* read_image) {
|
||||
return StbReadFile(u8filename, read_image);
|
||||
}
|
||||
|
||||
bool CKBitmapPNGHandler::ReadMemory(const void* memory, CKDWORD size, VxMath::VxImageDescEx* read_image) {
|
||||
return StbReadMemory(memory, size, read_image);
|
||||
}
|
||||
|
||||
bool CKBitmapPNGHandler::SaveFile(CKSTRING u8filename, const VxMath::VxImageDescEx* write_image, const CKBitmapProperties& codec_param) {
|
||||
return StbSaveFile(u8filename, write_image, false, // png support alpha
|
||||
[&codec_param](stbi_write_func* func, void* context, int w, int h, int comp, const void* data) -> int {
|
||||
// set default compress level
|
||||
stbi_write_png_compression_level = g_PNGDefaultCompressLevel;
|
||||
// write data
|
||||
return stbi_write_png_to_func(func, context, w, h, comp, data, StbPngStrideGetter(w, comp));
|
||||
});
|
||||
}
|
||||
|
||||
CKDWORD CKBitmapPNGHandler::SaveMemory(void* memory, const VxMath::VxImageDescEx* write_image, const CKBitmapProperties& codec_param) {
|
||||
return StbSaveMemory(memory, write_image, false, // png support alpha
|
||||
[&codec_param](stbi_write_func* func, void* context, int w, int h, int comp, const void* data) -> int {
|
||||
stbi_write_png_compression_level = g_PNGDefaultCompressLevel;
|
||||
return stbi_write_png_to_func(func, context, w, h, comp, data, StbPngStrideGetter(w, comp));
|
||||
});
|
||||
}
|
||||
|
||||
bool CKBitmapPNGHandler::CanSaveAlpha() {
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region General Getter Freer
|
||||
|
||||
static CKBitmapHandler* FindHandlerByExt(const CKFileExtension& ext) {
|
||||
if (ext == g_BMPProperties.m_Ext) return new CKBitmapBMPHandler();
|
||||
if (ext == g_TGAProperties.m_Ext) return new CKBitmapTGAHandler();
|
||||
if (ext == g_JPGProperties.m_Ext) return new CKBitmapJPGHandler();
|
||||
if (ext == g_PNGProperties.m_Ext) return new CKBitmapPNGHandler();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static CKBitmapHandler* FindHandlerByGuid(const CKGUID& guid) {
|
||||
if (guid == g_BMPProperties.m_ReaderGuid) return new CKBitmapBMPHandler();
|
||||
if (guid == g_TGAProperties.m_ReaderGuid) return new CKBitmapTGAHandler();
|
||||
if (guid == g_JPGProperties.m_ReaderGuid) return new CKBitmapJPGHandler();
|
||||
if (guid == g_PNGProperties.m_ReaderGuid) return new CKBitmapPNGHandler();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CKBitmapHandler* CKBitmapHandler::GetBitmapHandler(const CKFileExtension& ext, const CKGUID& guid) {
|
||||
CKBitmapHandler* handler = nullptr;
|
||||
|
||||
// check ext first
|
||||
handler = FindHandlerByExt(ext);
|
||||
if (handler != nullptr) return handler;
|
||||
|
||||
// check guid
|
||||
handler = FindHandlerByGuid(guid);
|
||||
if (handler != nullptr) return handler;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CKBitmapHandlerWrapper_t CKBitmapHandler::GetBitmapHandlerWrapper(const CKFileExtension& ext, const CKGUID& guid) {
|
||||
return CKBitmapHandlerWrapper_t(GetBitmapHandler(ext, guid));
|
||||
}
|
||||
|
||||
void CKBitmapHandlerDeleter::operator()(CKBitmapHandler* handler) {
|
||||
CKBitmapHandler::ReleaseBitmapHandler(handler);
|
||||
}
|
||||
|
||||
void CKBitmapHandler::ReleaseBitmapHandler(CKBitmapHandler* handler) {
|
||||
if (handler != nullptr) delete handler;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../VTInternal.hpp"
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
namespace LibCmo::CK2::DataHandlers {
|
||||
|
||||
class CKBitmapHandler;
|
||||
/**
|
||||
* @brief An assist class which can applied to std::unique_ptr as a custom deleter
|
||||
* to make sure the CKBitmapHandler* can be free correctly.
|
||||
*/
|
||||
struct CKBitmapHandlerDeleter {
|
||||
CKBitmapHandlerDeleter() = default;
|
||||
CKBitmapHandlerDeleter(const CKBitmapHandlerDeleter&) noexcept {}
|
||||
void operator()(CKBitmapHandler* handler);
|
||||
};
|
||||
/**
|
||||
* @brief The type of Auto-free wrapper of CKBitmapHandler.
|
||||
*/
|
||||
using CKBitmapHandlerWrapper_t = std::unique_ptr<CKBitmapHandler, CKBitmapHandlerDeleter>;
|
||||
|
||||
/**
|
||||
* The interface about processing bitmap data between raw data and specific data.
|
||||
* This interface will be capable to converting specific bitmap data into raw ARGB8888 raw data,
|
||||
* or convert them back.
|
||||
*/
|
||||
class CKBitmapHandler {
|
||||
public:
|
||||
CKBitmapHandler() {}
|
||||
virtual ~CKBitmapHandler() {}
|
||||
YYCC_DEL_CLS_COPY_MOVE(CKBitmapHandler);
|
||||
|
||||
/**
|
||||
* @brief General CKBitmapHandler getter.
|
||||
* @param ext[in] The file extention help finding corresponding bitmap handler.
|
||||
* @param guid[in] The GUID of bitmap handler used in fall back finding.
|
||||
* @remark
|
||||
* + Returns a reader capable of reading file with the given extension ext
|
||||
* + It may be several plugins that support the same extension, in which case a preferedGUID identifying the reader that should be returned can be given (optionnal).
|
||||
* @return The pointer to CKBitmapHandler. nullptr if fail to find.
|
||||
*/
|
||||
static CKBitmapHandler* GetBitmapHandler(const CKFileExtension& ext, const CKGUID& guid);
|
||||
/**
|
||||
* @brief A auto free wrapper for GetBitmapHandler
|
||||
*/
|
||||
static CKBitmapHandlerWrapper_t GetBitmapHandlerWrapper(const CKFileExtension& ext, const CKGUID& guid);
|
||||
/**
|
||||
* @brief General CKBitmapHandler disposer
|
||||
* @param handler[in] The handler need to be free.
|
||||
*/
|
||||
static void ReleaseBitmapHandler(CKBitmapHandler* handler);
|
||||
|
||||
/**
|
||||
@brief Loads a bitmap file.
|
||||
@return Returns true if successful.
|
||||
@param u8filename[in] The file ready to read.
|
||||
@param read_image[out] The pointer point to a blank image desc to receive read image.
|
||||
@see ReadMemory
|
||||
*/
|
||||
virtual bool ReadFile(CKSTRING u8filename, VxMath::VxImageDescEx* read_image) = 0;
|
||||
/**
|
||||
@brief Loads a bitmap file stored in memory.
|
||||
@return Returns true if successful.
|
||||
@param memory[in] The pointer to memory.
|
||||
@param size[in] The size of memory.
|
||||
@param read_image[out] The pointer point to a blank image desc to receive read image.
|
||||
@see ReadFile
|
||||
*/
|
||||
virtual bool ReadMemory(const void* memory, CKDWORD size, VxMath::VxImageDescEx* read_image) = 0;
|
||||
/**
|
||||
@brief Saves a image to a file
|
||||
@return Returns true if successful.
|
||||
@param u8filename[in] The file ready to write.
|
||||
@param write_image[in] The image will be written in file.
|
||||
@param codec_param[in] The written image format parameter.
|
||||
@see SaveMemory
|
||||
*/
|
||||
virtual bool SaveFile(CKSTRING u8filename, const VxMath::VxImageDescEx* write_image, const CKBitmapProperties& codec_param) = 0;
|
||||
/**
|
||||
@brief Saves an image into a memory block.
|
||||
@return Returns the number of written bytes if successful, 0 otherwise.
|
||||
@param memory[in] The memory where the image will be written. nullptr if you need a dry run to get how many bytes you need to allocate.
|
||||
@param write_image[in] The image will be written in file.
|
||||
@param codec_param[in] The written image format parameter.
|
||||
@remark
|
||||
+ You should call this method first to get how much bytes will be written.
|
||||
+ Then you allocate a proper buffer.
|
||||
+ Finally, call this method with your allocated buffer to get the result.
|
||||
@see SaveFile
|
||||
*/
|
||||
virtual CKDWORD SaveMemory(void* memory, const VxMath::VxImageDescEx* write_image, const CKBitmapProperties& codec_param) = 0;
|
||||
/**
|
||||
* @brief Check whether this bitmap handler can save alpha data.
|
||||
* @return True if this bitmap handler can save alpha data.
|
||||
*/
|
||||
virtual bool CanSaveAlpha() = 0;
|
||||
};
|
||||
|
||||
class CKBitmapBMPHandler : public CKBitmapHandler {
|
||||
public:
|
||||
CKBitmapBMPHandler();
|
||||
virtual ~CKBitmapBMPHandler();
|
||||
YYCC_DEL_CLS_COPY_MOVE(CKBitmapBMPHandler);
|
||||
|
||||
static const CKBitmapProperties& GetBitmapDefaultProperties();
|
||||
|
||||
virtual bool ReadFile(CKSTRING u8filename, VxMath::VxImageDescEx* read_image) override;
|
||||
virtual bool ReadMemory(const void* memory, CKDWORD size, VxMath::VxImageDescEx* read_image) override;
|
||||
virtual bool SaveFile(CKSTRING u8filename, const VxMath::VxImageDescEx* write_image, const CKBitmapProperties& codec_param) override;
|
||||
virtual CKDWORD SaveMemory(void* memory, const VxMath::VxImageDescEx* write_image, const CKBitmapProperties& codec_param) override;
|
||||
virtual bool CanSaveAlpha() override;
|
||||
|
||||
};
|
||||
|
||||
class CKBitmapTGAHandler : public CKBitmapHandler {
|
||||
public:
|
||||
CKBitmapTGAHandler();
|
||||
virtual ~CKBitmapTGAHandler();
|
||||
YYCC_DEL_CLS_COPY_MOVE(CKBitmapTGAHandler);
|
||||
|
||||
static const CKBitmapProperties& GetBitmapDefaultProperties();
|
||||
|
||||
virtual bool ReadFile(CKSTRING u8filename, VxMath::VxImageDescEx* read_image) override;
|
||||
virtual bool ReadMemory(const void* memory, CKDWORD size, VxMath::VxImageDescEx* read_image) override;
|
||||
virtual bool SaveFile(CKSTRING u8filename, const VxMath::VxImageDescEx* write_image, const CKBitmapProperties& codec_param) override;
|
||||
virtual CKDWORD SaveMemory(void* memory, const VxMath::VxImageDescEx* write_image, const CKBitmapProperties& codec_param) override;
|
||||
virtual bool CanSaveAlpha() override;
|
||||
|
||||
};
|
||||
|
||||
class CKBitmapJPGHandler : public CKBitmapHandler {
|
||||
public:
|
||||
CKBitmapJPGHandler();
|
||||
virtual ~CKBitmapJPGHandler();
|
||||
YYCC_DEL_CLS_COPY_MOVE(CKBitmapJPGHandler);
|
||||
|
||||
static const CKBitmapProperties& GetBitmapDefaultProperties();
|
||||
|
||||
virtual bool ReadFile(CKSTRING u8filename, VxMath::VxImageDescEx* read_image) override;
|
||||
virtual bool ReadMemory(const void* memory, CKDWORD size, VxMath::VxImageDescEx* read_image) override;
|
||||
virtual bool SaveFile(CKSTRING u8filename, const VxMath::VxImageDescEx* write_image, const CKBitmapProperties& codec_param) override;
|
||||
virtual CKDWORD SaveMemory(void* memory, const VxMath::VxImageDescEx* write_image, const CKBitmapProperties& codec_param) override;
|
||||
virtual bool CanSaveAlpha() override;
|
||||
|
||||
};
|
||||
|
||||
class CKBitmapPNGHandler : public CKBitmapHandler {
|
||||
public:
|
||||
CKBitmapPNGHandler();
|
||||
virtual ~CKBitmapPNGHandler();
|
||||
YYCC_DEL_CLS_COPY_MOVE(CKBitmapPNGHandler);
|
||||
|
||||
static const CKBitmapProperties& GetBitmapDefaultProperties();
|
||||
|
||||
virtual bool ReadFile(CKSTRING u8filename, VxMath::VxImageDescEx* read_image) override;
|
||||
virtual bool ReadMemory(const void* memory, CKDWORD size, VxMath::VxImageDescEx* read_image) override;
|
||||
virtual bool SaveFile(CKSTRING u8filename, const VxMath::VxImageDescEx* write_image, const CKBitmapProperties& codec_param) override;
|
||||
virtual CKDWORD SaveMemory(void* memory, const VxMath::VxImageDescEx* write_image, const CKBitmapProperties& codec_param) override;
|
||||
virtual bool CanSaveAlpha() override;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
#include "CKBaseManager.hpp"
|
||||
|
||||
namespace LibCmo::CK2::MgrImpls {
|
||||
// todo: this file maybe not used anymore. if more manager added. i think this file can be removed.
|
||||
|
||||
//CKERROR CKBaseManager::LoadData(CKStateChunk* statechunk, CKFileDocument* doc) {
|
||||
// return CKERROR::CKERR_OK;
|
||||
//}
|
||||
//CKStateChunk* CKBaseManager::SaveData(CKFileDocument* doc) {
|
||||
// return nullptr;
|
||||
//}
|
||||
|
||||
|
||||
//CKAttributeManager::CKAttributeManager(CKMinContext* ctx, CK_ID ckid) : CKBaseManager(ctx, ckid) {
|
||||
|
||||
//}
|
||||
//CKAttributeManager::~CKAttributeManager() {
|
||||
|
||||
//}
|
||||
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../VTInternal.hpp"
|
||||
|
||||
/**
|
||||
CKBaseManager virtual functions implementations help
|
||||
All virtual functions is not supported except we implemented.
|
||||
|
||||
Read/Write functions:
|
||||
- SaveData()
|
||||
- LoadData()
|
||||
|
||||
Clear functions:
|
||||
- PreClearAll()
|
||||
- PostClearAll()
|
||||
|
||||
Delete functions:
|
||||
- SequenceToBeDeleted()
|
||||
- SequenceDeleted()
|
||||
|
||||
Moved functions
|
||||
- OnCKInit(): Implement in ctor
|
||||
- OnCKEnd(): Implement in dtor
|
||||
|
||||
*/
|
||||
|
||||
namespace LibCmo::CK2::MgrImpls {
|
||||
|
||||
/**
|
||||
@brief Base Class for managers.
|
||||
@remark
|
||||
+ This class provides virtual methods that can be override by any managers. Any manager that inherits from CKBaseManager can override function to do some processing.
|
||||
+ The instances of managers may be retrieved through the global function CKContext::GetManagerByGuid()
|
||||
+ Some default managers implemented in Virtools can be accessed directly: See Managers Access
|
||||
*/
|
||||
class CKBaseManager {
|
||||
public:
|
||||
CKBaseManager(CKContext* ctx, CKGUID guid, CKSTRING name) :
|
||||
m_ManagerGuid(guid), m_ManagerName(), m_Context(ctx) {
|
||||
XContainer::NSXString::FromCKSTRING(m_ManagerName, name);
|
||||
}
|
||||
virtual ~CKBaseManager() {}
|
||||
YYCC_DEL_CLS_COPY_MOVE(CKBaseManager);
|
||||
|
||||
/**
|
||||
@brief Acces to Manager GUID
|
||||
@return CKGUID of this manager.
|
||||
@remark
|
||||
+ Each Manager is given an unique GUID. When creating a new manager it should
|
||||
assign itself a GUID and name before registering itsef.
|
||||
```
|
||||
CKAttributeManager::CKAttributeManager(CKContext *Context) : CKBaseManager(Context, ATTRIBUTE_MANAGER_GUID, "Attribute Manager")
|
||||
{
|
||||
// ....
|
||||
// ....
|
||||
Context->RegisterNewManager(this);
|
||||
}
|
||||
```
|
||||
@see CKContext::RegisterNewManager, GetName
|
||||
*/
|
||||
CKGUID GetGuid() {
|
||||
return m_ManagerGuid;
|
||||
}
|
||||
/**
|
||||
@brief Acces to Manager name
|
||||
@return Name of this manager.
|
||||
@remark
|
||||
+ Each Manager is given an unique GUID. When creating a new manager it should
|
||||
assign itself a GUID and name before registering itsef.
|
||||
```
|
||||
CKAttributeManager::CKAttributeManager(CKContext *Context) : CKBaseManager(Context, ATTRIBUTE_MANAGER_GUID, "Attribute Manager")
|
||||
{
|
||||
// ....
|
||||
// ....
|
||||
Context->RegisterNewManager(this);
|
||||
}
|
||||
```
|
||||
*/
|
||||
CKSTRING GetName() {
|
||||
return XContainer::NSXString::ToCKSTRING(m_ManagerName);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Called to save manager data.
|
||||
@param SavedFile A pointer to the CKFile being saved.
|
||||
@return This function should return true or false if there is nothing to save.
|
||||
@remark
|
||||
+ During a save operation, each manager is given the opportunity to save its data in the file.
|
||||
+ The file being saved is given for information only and must not be modified. It can be used to decide whether it is worth saving
|
||||
data for your manager.
|
||||
@see CKStateChunk, LoadData
|
||||
*/
|
||||
virtual bool SaveData([[maybe_unused]] CKStateChunk* chunk, [[maybe_unused]] CKFileVisitor* SavedFile) {
|
||||
return false; // default value is false to indicate this manager do not need save any data.
|
||||
}
|
||||
/**
|
||||
@brief Called to load manager data.
|
||||
@param chunk A pointer to a CKStateChunk that was saved in the file.
|
||||
@param LoadedFile A pointer to the CKFile being loaded.
|
||||
@return CKERR_OK if successful or an error code otherwise.
|
||||
@remark
|
||||
+ During a load operation, each manager is automatically called if there was a chunk saved in the file with SaveData.
|
||||
@see CKStateChunk, SaveData
|
||||
*/
|
||||
virtual CKERROR LoadData([[maybe_unused]] CKStateChunk* chunk, [[maybe_unused]] CKFileVisitor* LoadedFile) {
|
||||
return CKERROR::CKERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Called at the beginning of a CKContext::ClearAll operation.
|
||||
@return CK_OK if successful or an error code otherwise.
|
||||
@remark
|
||||
+ You can override this function to add specific processing at the beginning of a CKContext::ClearAll operation.
|
||||
+ You must override GetValidFunctionsMask and return a value including
|
||||
CKMANAGER_FUNC_PreClearAll for this function to get called.
|
||||
@see Main Virtools Events, PostClearAll, CKContext::ClearAll
|
||||
*/
|
||||
virtual CKERROR PreClearAll() { return CKERROR::CKERR_OK; }
|
||||
/**
|
||||
@brief Called at the end of a CKContext::ClearAll operation.
|
||||
@return CKERR_OK if successful or an error code otherwise.
|
||||
@remark
|
||||
+ You can override this function to add specific processing at the end of a CKContext::ClearAll operation.
|
||||
+ You must override GetValidFunctionsMask and return a value including
|
||||
CKMANAGER_FUNC_PostClearAll for this function to get called.
|
||||
@see Main Virtools Events, PreClearAll, CKContext::ClearAll
|
||||
*/
|
||||
virtual CKERROR PostClearAll() { return CKERROR::CKERR_OK; }
|
||||
|
||||
/**
|
||||
@brief Called just before objects are deleted.
|
||||
@return CK_OK if successful or an error code otherwise.
|
||||
@param objids[in] A pointer to a list of CK_ID of the objects being deleted.
|
||||
@param count[in] number of objects in objids list.
|
||||
@remark
|
||||
+ You can override this function if you need to add specific processing before objects are deleted.
|
||||
+ You must override GetValidFunctionsMask and return a value including
|
||||
CKMANAGER_FUNC_OnSequenceToBeDeleted for this function to get called.
|
||||
@see Main Virtools Events, CKContext::DestroyObjects, SequenceDeleted
|
||||
*/
|
||||
virtual CKERROR SequenceToBeDeleted([[maybe_unused]] const CK_ID* objids, [[maybe_unused]] CKDWORD count) { return CKERROR::CKERR_OK; }
|
||||
/**
|
||||
@brief Called after objects have been deleted.
|
||||
@return CK_OK if successful or an error code otherwise.
|
||||
@param objids[in] A pointer to a list of CK_ID of the objects being deleted.
|
||||
@param count[in] number of objects in objids list.
|
||||
@remark
|
||||
+ You can override this function if you need to add specific processing after objects have been deleted.
|
||||
+ You must override GetValidFunctionsMask and return a value including
|
||||
CKMANAGER_FUNC_OnSequenceDeleted for this function to get called.
|
||||
@see Main Virtools Events, CKContext::DestroyObjects, SequenceToBeDeleted
|
||||
*/
|
||||
virtual CKERROR SequenceDeleted([[maybe_unused]] const CK_ID* objids, [[maybe_unused]] CKDWORD count) { return CKERROR::CKERR_OK; }
|
||||
|
||||
|
||||
protected:
|
||||
CKGUID m_ManagerGuid; ///> Manager GUID
|
||||
XContainer::XString m_ManagerName; ///> Manager Name
|
||||
CKContext* m_Context; ///> A pointer to the CKContext on which this manager is valid.
|
||||
|
||||
};
|
||||
|
||||
//class CKAttributeManager : public CKBaseManager {
|
||||
//public:
|
||||
// CKAttributeManager(CKContext* ctx, CK_ID ckid);
|
||||
// CKAttributeManager(const CKAttributeManager&) = delete;
|
||||
// CKAttributeManager& operator=(const CKAttributeManager&) = delete;
|
||||
// virtual ~CKAttributeManager();
|
||||
|
||||
//private:
|
||||
|
||||
//};
|
||||
|
||||
}
|
||||
@@ -1,268 +0,0 @@
|
||||
#include "CKObjectManager.hpp"
|
||||
#include "../CKContext.hpp"
|
||||
#include "../ObjImpls/CKObject.hpp"
|
||||
|
||||
namespace LibCmo::CK2::MgrImpls {
|
||||
|
||||
CKObjectManager::CKObjectManager(CKContext* ctx) :
|
||||
CKBaseManager(ctx, OBJECT_MANAGER_GUID, u8"Object Manager"),
|
||||
m_ObjectsList(), m_ReturnedObjectOffsets(), m_ObjectCount(0),
|
||||
m_GroupGlobalIndex(), m_SceneGlobalIndex(),
|
||||
m_ObjectsListByClass(CKGetClassCount()) {}
|
||||
|
||||
CKObjectManager::~CKObjectManager() {
|
||||
DestroyAllObjects();
|
||||
}
|
||||
|
||||
ObjImpls::CKObject* CKObjectManager::CreateObject(CK_CLASSID cls, CKSTRING name,
|
||||
CK_OBJECTCREATION_OPTIONS options, CK_CREATIONMODE* res) {
|
||||
// todo: Process paramter options and res
|
||||
|
||||
// get description first
|
||||
const CKClassDesc* desc = CKGetClassDesc(cls);
|
||||
// if no description, return directly to reject creating object.
|
||||
if (desc == nullptr) return nullptr;
|
||||
|
||||
// allocate a CK_ID first
|
||||
CKDWORD decided_off;
|
||||
if (this->m_ReturnedObjectOffsets.empty()) {
|
||||
// create new CK_ID.
|
||||
decided_off = static_cast<CKDWORD>(m_ObjectsList.size());
|
||||
m_ObjectsList.resize(decided_off + 1);
|
||||
} else {
|
||||
// use returned CK_ID.
|
||||
decided_off = m_ReturnedObjectOffsets.back();
|
||||
m_ReturnedObjectOffsets.pop_back();
|
||||
}
|
||||
|
||||
// create one
|
||||
ObjImpls::CKObject* obj = desc->CreationFct(m_Context, Offset2Id(decided_off), name);
|
||||
|
||||
// put into slot and inc count
|
||||
m_ObjectsList[decided_off] = obj;
|
||||
++m_ObjectCount;
|
||||
|
||||
// add into classid indexed object list
|
||||
m_ObjectsListByClass[static_cast<size_t>(cls)].push_back(Offset2Id(decided_off));
|
||||
|
||||
// set out variable
|
||||
return obj;
|
||||
}
|
||||
|
||||
ObjImpls::CKObject* CKObjectManager::GetObject(CK_ID id) {
|
||||
CKDWORD off = Id2Offset(id);
|
||||
if (off >= m_ObjectsList.size()) return nullptr;
|
||||
return m_ObjectsList[off];
|
||||
}
|
||||
|
||||
CKDWORD CKObjectManager::GetObjectCount() {
|
||||
return m_ObjectCount;
|
||||
}
|
||||
|
||||
void CKObjectManager::InternalDestroy(ObjImpls::CKObject* obj) {
|
||||
// find desc by classid
|
||||
const CKClassDesc* desc = CKGetClassDesc(obj->GetClassID());
|
||||
// a create CKObject instance definitely can find corresponding desc.
|
||||
// if not, throw exception.
|
||||
if (desc == nullptr) throw LogicException("Invalid CK_CLASSID");
|
||||
// free it
|
||||
desc->ReleaseFct(m_Context, obj);
|
||||
}
|
||||
|
||||
void CKObjectManager::DestroyObjects(CK_ID* ids, CKDWORD count) {
|
||||
// pre notice manager
|
||||
m_Context->ExecuteManagersOnSequenceToBeDeleted(ids, count);
|
||||
|
||||
// collect object data
|
||||
// remove invalid object first, set them to be deleted
|
||||
// collection deleted object class ids
|
||||
XContainer::XObjectArray validObjIds;
|
||||
XContainer::XBitArray cids;
|
||||
for (CKDWORD i = 0; i < count; ++i) {
|
||||
CKDWORD off = Id2Offset(ids[i]);
|
||||
if (off >= m_ObjectsList.size()) continue;
|
||||
ObjImpls::CKObject* obj = m_ObjectsList[off];
|
||||
if (obj == nullptr) continue;
|
||||
|
||||
// set to be deleted
|
||||
CK_OBJECT_FLAGS objflag = obj->GetObjectFlags();
|
||||
YYCC::EnumHelper::Add(objflag, CK_OBJECT_FLAGS::CK_OBJECT_TOBEDELETED);
|
||||
obj->SetObjectFlags(objflag);
|
||||
|
||||
// collect class id
|
||||
XContainer::NSXBitArray::Set(cids, static_cast<CKDWORD>(obj->GetClassID()));
|
||||
|
||||
// add into list
|
||||
validObjIds.emplace_back(ids[i]);
|
||||
}
|
||||
|
||||
// then remove deleted object from m_ObjectListByClass
|
||||
// because we have set to be deleted flag.
|
||||
for (size_t i = 0; i < m_ObjectsListByClass.size(); ++i) {
|
||||
if (!XContainer::NSXBitArray::IsSet(cids, static_cast<CKDWORD>(i)))
|
||||
continue;
|
||||
XContainer::NSXObjectArray::PreDeletedCheck(m_ObjectsListByClass[i], m_Context);
|
||||
}
|
||||
|
||||
// use collected cid to get all class ids which need receive notify
|
||||
// and use m_ObjectListByClass to notify them
|
||||
XContainer::XBitArray notifyCids = CKGetAllNotifyClassID(cids);
|
||||
for (size_t i = 0; i < m_ObjectsListByClass.size(); ++i) {
|
||||
if (!XContainer::NSXBitArray::IsSet(notifyCids, static_cast<CKDWORD>(i)))
|
||||
continue;
|
||||
for (auto& objid : m_ObjectsListByClass[i]) {
|
||||
m_ObjectsList[Id2Offset(objid)]->CheckPreDeletion();
|
||||
}
|
||||
}
|
||||
|
||||
// calling PreDelete function for deleted objects
|
||||
for (const auto& objid : validObjIds) {
|
||||
m_ObjectsList[Id2Offset(objid)]->PreDelete();
|
||||
}
|
||||
|
||||
// then free all valid object
|
||||
for (const auto& objid : validObjIds) {
|
||||
CKDWORD off = Id2Offset(objid);
|
||||
ObjImpls::CKObject* obj = m_ObjectsList[off];
|
||||
|
||||
// free it
|
||||
InternalDestroy(obj);
|
||||
|
||||
// return its allocated id.
|
||||
// and dec count
|
||||
m_ObjectsList[off] = nullptr;
|
||||
m_ReturnedObjectOffsets.emplace_back(off);
|
||||
--m_ObjectCount;
|
||||
}
|
||||
|
||||
// run post deletion for notify object
|
||||
for (size_t i = 0; i < m_ObjectsListByClass.size(); ++i) {
|
||||
if (!XContainer::NSXBitArray::IsSet(notifyCids, static_cast<CKDWORD>(i)))
|
||||
continue;
|
||||
for (auto& objid : m_ObjectsListByClass[i]) {
|
||||
m_ObjectsList[Id2Offset(objid)]->CheckPostDeletion();
|
||||
}
|
||||
}
|
||||
|
||||
// notice post
|
||||
m_Context->ExecuteManagersOnSequenceDeleted(ids, count);
|
||||
}
|
||||
|
||||
void CKObjectManager::DestroyAllObjects() {
|
||||
// free all created objects
|
||||
for (auto& ptr : m_ObjectsList) {
|
||||
if (ptr != nullptr) {
|
||||
InternalDestroy(ptr);
|
||||
}
|
||||
}
|
||||
// restore returned object list
|
||||
m_ReturnedObjectOffsets.clear();
|
||||
// empty object list
|
||||
m_ObjectsList.clear();
|
||||
// reset count
|
||||
m_ObjectCount = 0;
|
||||
|
||||
// clear obj by class list
|
||||
for (auto& ls : m_ObjectsListByClass) {
|
||||
ls.clear();
|
||||
}
|
||||
|
||||
// clear group and scene global index at the same time
|
||||
m_SceneGlobalIndex.clear();
|
||||
m_GroupGlobalIndex.clear();
|
||||
}
|
||||
|
||||
XContainer::XObjectPointerArray CKObjectManager::GetObjectByNameAndClass(CKSTRING name, CK_CLASSID cid, bool derived) {
|
||||
XContainer::XObjectPointerArray result;
|
||||
|
||||
for (size_t i = 0; i < m_ObjectsListByClass.size(); ++i) {
|
||||
// check class id first
|
||||
if (derived) {
|
||||
if (!CKIsChildClassOf(static_cast<CK_CLASSID>(i), cid)) continue;
|
||||
} else {
|
||||
if (static_cast<CK_CLASSID>(i) != cid) continue;
|
||||
}
|
||||
|
||||
// iterate all sub object and check name
|
||||
for (const auto& objoff : m_ObjectsListByClass[i]) {
|
||||
ObjImpls::CKObject* obj = m_ObjectsList[objoff];
|
||||
|
||||
if (name == nullptr) {
|
||||
// directly add
|
||||
result.emplace_back(obj);
|
||||
} else {
|
||||
// check name
|
||||
if (CKStrEqual(name, obj->GetName())) {
|
||||
result.emplace_back(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#pragma region Object Check
|
||||
|
||||
bool CKObjectManager::IsObjectSafe(CK_ID objid) {
|
||||
CKDWORD off = Id2Offset(objid);
|
||||
if (off >= m_ObjectsList.size()) return false;
|
||||
return m_ObjectsList[off] != nullptr;
|
||||
}
|
||||
|
||||
bool CKObjectManager::IsObjectPointerSafe(const ObjImpls::CKObject* objptr) {
|
||||
if (objptr == nullptr) return false;
|
||||
|
||||
// iterate all object list to check
|
||||
for (const auto& ptr : m_ObjectsList) {
|
||||
if (ptr == objptr) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Special Functions
|
||||
|
||||
CKDWORD CKObjectManager::AllocateGroupGlobalIndex() {
|
||||
// try find first non-true position
|
||||
CKDWORD index;
|
||||
if (!XContainer::NSXBitArray::GetUnsetBitPosition(m_GroupGlobalIndex, 0, index)) {
|
||||
// failed. distribute new one
|
||||
index = static_cast<CKDWORD>(m_GroupGlobalIndex.size());
|
||||
m_GroupGlobalIndex.resize(m_GroupGlobalIndex.size() + 1);
|
||||
}
|
||||
|
||||
// set to occupy
|
||||
XContainer::NSXBitArray::Set(m_GroupGlobalIndex, index);
|
||||
return index;
|
||||
}
|
||||
|
||||
CKDWORD CKObjectManager::AllocateSceneGlobalIndex() {
|
||||
// same as group
|
||||
CKDWORD index;
|
||||
if (!XContainer::NSXBitArray::GetUnsetBitPosition(m_SceneGlobalIndex, 0, index)) {
|
||||
index = static_cast<CKDWORD>(m_SceneGlobalIndex.size());
|
||||
m_SceneGlobalIndex.resize(m_SceneGlobalIndex.size() + 1);
|
||||
}
|
||||
|
||||
XContainer::NSXBitArray::Set(m_SceneGlobalIndex, index);
|
||||
return index;
|
||||
}
|
||||
|
||||
void CKObjectManager::FreeGroupGlobalIndex(CKDWORD id) {
|
||||
// check position
|
||||
if (id >= m_GroupGlobalIndex.size()) return;
|
||||
// set value
|
||||
XContainer::NSXBitArray::Unset(m_GroupGlobalIndex, id);
|
||||
}
|
||||
|
||||
void CKObjectManager::FreeSceneGlobalIndex(CKDWORD id) {
|
||||
// same as group
|
||||
if (id >= m_SceneGlobalIndex.size()) return;
|
||||
XContainer::NSXBitArray::Unset(m_SceneGlobalIndex, id);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../VTInternal.hpp"
|
||||
#include "CKBaseManager.hpp"
|
||||
#include <deque>
|
||||
|
||||
namespace LibCmo::CK2::MgrImpls {
|
||||
|
||||
class CKObjectManager : public CKBaseManager {
|
||||
public:
|
||||
CKObjectManager(CKContext* ctx);
|
||||
virtual ~CKObjectManager();
|
||||
YYCC_DEL_CLS_COPY_MOVE(CKObjectManager);
|
||||
|
||||
// ========== Objects Management ==========
|
||||
|
||||
/**
|
||||
* @brief Creates a CKObject or derived class instance.
|
||||
* @param[in] cls Class Identifier (CK_CLASSID) of the object to create.
|
||||
* @param[in] name The name of this object.
|
||||
* @param[in] options Tell CKContext how to create this object when conflict happended.
|
||||
* @param[out] res The value indicate the real method how this object created.
|
||||
* @return A pointer to the newly created object.
|
||||
* @remark CKObjects must be destroy with the DestroyObject method.
|
||||
* @see CKObject, DestroyObject
|
||||
*/
|
||||
ObjImpls::CKObject* CreateObject(CK_CLASSID cls, CKSTRING name,
|
||||
CK_OBJECTCREATION_OPTIONS options = CK_OBJECTCREATION_OPTIONS::CK_OBJECTCREATION_NONAMECHECK,
|
||||
CK_CREATIONMODE* res = nullptr);
|
||||
// todo: implement CopyObject by CKClassDesc
|
||||
//ObjImpls::CKObject* CopyObject(ObjImpls::CKObject *src,
|
||||
// CKDependencies* Dependencies = nullptr,
|
||||
// CKSTRING AppendName = nullptr,
|
||||
// CK_OBJECTCREATION_OPTIONS Options = CK_OBJECTCREATION_OPTIONS::CK_OBJECTCREATION_NONAMECHECK);
|
||||
|
||||
ObjImpls::CKObject* GetObject(CK_ID id);
|
||||
CKDWORD GetObjectCount();
|
||||
void DestroyObjects(CK_ID* ids, CKDWORD count);
|
||||
void DestroyAllObjects();
|
||||
|
||||
// ========== Objects Access ==========
|
||||
|
||||
/**
|
||||
* @brief General object list query.
|
||||
* @param name nullptr if no requirement.
|
||||
* @param cid the class id
|
||||
* @param derived whether considering derived class
|
||||
* @return the result pointer list.
|
||||
*/
|
||||
XContainer::XObjectPointerArray GetObjectByNameAndClass(
|
||||
CKSTRING name, CK_CLASSID cid, bool derived);
|
||||
|
||||
// ========== Object Check ==========
|
||||
bool IsObjectSafe(CK_ID objid);
|
||||
bool IsObjectPointerSafe(const ObjImpls::CKObject* objptr);
|
||||
|
||||
// ========== Special Functions ==========
|
||||
|
||||
CKDWORD AllocateGroupGlobalIndex();
|
||||
CKDWORD AllocateSceneGlobalIndex();
|
||||
void FreeGroupGlobalIndex(CKDWORD id);
|
||||
void FreeSceneGlobalIndex(CKDWORD id);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* The global offset of created CK_ID.
|
||||
* The value close to zero may cause some issue.
|
||||
* So we add a static offset to every created CK_ID.
|
||||
*/
|
||||
const CK_ID c_ObjectIdOffset = 61u;
|
||||
CKDWORD Id2Offset(CK_ID id) { return static_cast<CKDWORD>(id - c_ObjectIdOffset); }
|
||||
CK_ID Offset2Id(CKDWORD off) { return static_cast<CK_ID>(off + c_ObjectIdOffset); }
|
||||
|
||||
/**
|
||||
* @brief The real CKObject destroy worker shared by CKObjectManager::DestroyObject and CKObjectManager::~CKObjectManager
|
||||
* @param[in] obj The CKObject need to be free.
|
||||
*/
|
||||
void InternalDestroy(ObjImpls::CKObject* obj);
|
||||
|
||||
CKDWORD m_ObjectCount;
|
||||
XContainer::XObjectPointerArray m_ObjectsList;
|
||||
XContainer::XArray<XContainer::XList<CKDWORD>> m_ObjectsListByClass;
|
||||
std::deque<CKDWORD> m_ReturnedObjectOffsets;
|
||||
|
||||
XContainer::XBitArray m_GroupGlobalIndex;
|
||||
XContainer::XBitArray m_SceneGlobalIndex;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
#include "CKPathManager.hpp"
|
||||
|
||||
namespace LibCmo::CK2::MgrImpls {
|
||||
|
||||
static constexpr char8_t g_UniqueFolder[] = u8"LibCmo";
|
||||
|
||||
CKPathManager::CKPathManager(CKContext* ctx) :
|
||||
CKBaseManager(ctx, PATH_MANAGER_GUID, u8"Path Manager"),
|
||||
m_TempFolder(), m_ExtraPathes() {
|
||||
// preset for temp folder
|
||||
// todo: add current CKContext pointer as the part of temp path.
|
||||
// thus multiple CKContext can work.
|
||||
m_TempFolder = std::filesystem::temp_directory_path();
|
||||
m_TempFolder /= std::filesystem::path(g_UniqueFolder);
|
||||
std::filesystem::create_directories(m_TempFolder);
|
||||
|
||||
}
|
||||
CKPathManager::~CKPathManager() {}
|
||||
|
||||
bool CKPathManager::SetTempFolder(CKSTRING u8_temp) {
|
||||
if (u8_temp == nullptr) return false;
|
||||
|
||||
std::filesystem::path cache(u8_temp);
|
||||
if (std::filesystem::is_directory(cache)) {
|
||||
m_TempFolder = cache;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
XContainer::XString CKPathManager::GetTempFolder() {
|
||||
return this->m_TempFolder.u8string();
|
||||
}
|
||||
|
||||
XContainer::XString CKPathManager::GetTempFilePath(CKSTRING u8_filename) {
|
||||
if (u8_filename == nullptr) return XContainer::XString();
|
||||
|
||||
std::filesystem::path stdfilename(u8_filename);
|
||||
auto realfile = this->m_TempFolder / stdfilename;
|
||||
|
||||
return realfile.u8string();
|
||||
}
|
||||
|
||||
bool CKPathManager::AddPath(CKSTRING u8path) {
|
||||
if (u8path == nullptr) return false;
|
||||
|
||||
std::filesystem::path newpath(u8path);
|
||||
if (std::filesystem::is_directory(newpath)) {
|
||||
m_ExtraPathes.emplace_back(std::move(newpath));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void CKPathManager::ClearPath() {
|
||||
m_ExtraPathes.clear();
|
||||
}
|
||||
|
||||
bool CKPathManager::ResolveFileName(XContainer::XString& u8_filename) {
|
||||
std::filesystem::path filepath(u8_filename);
|
||||
|
||||
// if it is absolute path, return it directly
|
||||
if (filepath.is_absolute()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// test in temp folder
|
||||
auto tempfile = m_TempFolder / filepath;
|
||||
if (std::filesystem::is_regular_file(tempfile)) {
|
||||
u8_filename = tempfile.u8string();
|
||||
return true;
|
||||
}
|
||||
|
||||
// otherwise check it in extra path
|
||||
for (const auto& extrapath : m_ExtraPathes) {
|
||||
auto combinedpath = extrapath / filepath;
|
||||
if (std::filesystem::is_regular_file(combinedpath)) {
|
||||
// this is correct
|
||||
u8_filename = combinedpath.u8string();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// failed
|
||||
return false;
|
||||
}
|
||||
|
||||
void CKPathManager::GetFileName(XContainer::XString& u8path) {
|
||||
std::filesystem::path filepath(u8path);
|
||||
|
||||
auto result = filepath.filename();
|
||||
u8path = result.u8string();
|
||||
}
|
||||
|
||||
void CKPathManager::GetExtension(XContainer::XString& u8path) {
|
||||
std::filesystem::path filepath(u8path);
|
||||
|
||||
auto result = filepath.extension();
|
||||
u8path = result.u8string();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../VTInternal.hpp"
|
||||
#include "CKBaseManager.hpp"
|
||||
#include <filesystem>
|
||||
|
||||
namespace LibCmo::CK2::MgrImpls {
|
||||
|
||||
class CKPathManager : public CKBaseManager {
|
||||
public:
|
||||
CKPathManager(CKContext* ctx);
|
||||
virtual ~CKPathManager();
|
||||
YYCC_DEL_CLS_COPY_MOVE(CKPathManager);
|
||||
|
||||
/**
|
||||
* @brief Set the temp folder of current context.
|
||||
* @param u8_temp The temp folder you need to assign
|
||||
* @return true if success.
|
||||
*/
|
||||
bool SetTempFolder(CKSTRING u8_temp);
|
||||
/**
|
||||
* @brief Get current temp folder.
|
||||
* @return
|
||||
*/
|
||||
XContainer::XString GetTempFolder();
|
||||
/**
|
||||
* @brief Get the path of temp file.
|
||||
* @param u8_filename The relative path of file.
|
||||
* @return The path of given path based on temp folder.
|
||||
*/
|
||||
XContainer::XString GetTempFilePath(CKSTRING u8_filename);
|
||||
|
||||
/**
|
||||
* @brief Add extra path for ResolveFileName
|
||||
* @param u8path The added path.
|
||||
* @return true if success.
|
||||
*/
|
||||
bool AddPath(CKSTRING u8path);
|
||||
/**
|
||||
* @brief Clear all extra path.
|
||||
*/
|
||||
void ClearPath();
|
||||
|
||||
/**
|
||||
* @brief Finds a file in the paths
|
||||
* @param u8_filename[inout] The given file path. overwritten by the final path if success.
|
||||
* @remark
|
||||
* We match file in following order.
|
||||
* + Whether given file is absolute path. return if true.
|
||||
* + Virtools temp folder.
|
||||
* + User provided extra path.
|
||||
* @return true if success
|
||||
*/
|
||||
bool ResolveFileName(XContainer::XString& u8_filename);
|
||||
|
||||
/**
|
||||
* @brief Get file name part of given path.
|
||||
* @param u8path[inout] The given path. overwritten by the gotten file name.
|
||||
*/
|
||||
void GetFileName(XContainer::XString& u8path);
|
||||
|
||||
/**
|
||||
* @brief Returns the file extension including period (.)
|
||||
* @param u8path[inout] The given path. overwritten by the gotten extension.
|
||||
*/
|
||||
void GetExtension(XContainer::XString& u8path);
|
||||
|
||||
protected:
|
||||
std::filesystem::path m_TempFolder;
|
||||
XContainer::XArray<std::filesystem::path> m_ExtraPathes;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,321 +0,0 @@
|
||||
#include "CK3dEntity.hpp"
|
||||
#include "../CKStateChunk.hpp"
|
||||
#include "../CKContext.hpp"
|
||||
#include "CKMesh.hpp"
|
||||
|
||||
namespace LibCmo::CK2::ObjImpls {
|
||||
|
||||
CK3dEntity::CK3dEntity(CKContext* ctx, CK_ID ckid, CKSTRING name) :
|
||||
CKRenderObject(ctx, ckid, name),
|
||||
m_PotentialMeshes(), m_CurrentMesh(nullptr),
|
||||
m_WorldMatrix(), m_ZOrder(0),
|
||||
m_MoveableFlags(YYCC::EnumHelper::Merge(
|
||||
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_PICKABLE,
|
||||
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_VISIBLE,
|
||||
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_RENDERCHANNELS,
|
||||
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_INVERSEWORLDMATVALID
|
||||
)),
|
||||
m_3dEntityFlags(static_cast<CK_3DENTITY_FLAGS>(0)) {}
|
||||
|
||||
CK3dEntity::~CK3dEntity() {}
|
||||
|
||||
void CK3dEntity::CheckPreDeletion() {
|
||||
CKRenderObject::CheckPreDeletion();
|
||||
|
||||
// check active mesh
|
||||
if (m_CurrentMesh->IsToBeDeleted()) {
|
||||
m_CurrentMesh = nullptr;
|
||||
}
|
||||
|
||||
// check potential meshes
|
||||
XContainer::NSXObjectPointerArray::PreDeletedCheck(m_PotentialMeshes, m_Context);
|
||||
}
|
||||
|
||||
bool CK3dEntity::Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) {
|
||||
bool suc = CKRenderObject::Save(chunk, file, flags);
|
||||
if (!suc) return false;
|
||||
|
||||
// write associated mesh data
|
||||
if (m_CurrentMesh != nullptr || m_PotentialMeshes.size() != 0) {
|
||||
chunk->WriteIdentifier(CK_STATESAVEFLAGS_3DENTITY::CK_STATESAVE_MESHS);
|
||||
|
||||
// write current mesh
|
||||
chunk->WriteObjectPointer(m_CurrentMesh);
|
||||
|
||||
// write potential meshes
|
||||
chunk->WriteXObjectPointerArray(m_PotentialMeshes);
|
||||
}
|
||||
|
||||
// write core entity data
|
||||
{
|
||||
chunk->WriteIdentifier(CK_STATESAVEFLAGS_3DENTITY::CK_STATESAVE_3DENTITYNDATA);
|
||||
|
||||
// regulate self flag again
|
||||
// MARK: originally we should check parent here.
|
||||
// but we do not support parent and hierarchy feature, so we simply remove flag
|
||||
YYCC::EnumHelper::Remove(m_3dEntityFlags, CK_3DENTITY_FLAGS::CK_3DENTITY_PARENTVALID);
|
||||
// MARK: originally we should check grouped into CKPlace here.
|
||||
// but we do not support CKPlace, so we simply remove this flag
|
||||
YYCC::EnumHelper::Remove(m_3dEntityFlags, CK_3DENTITY_FLAGS::CK_3DENTITY_PLACEVALID);
|
||||
// check z-order, if not zero, save it
|
||||
if (m_ZOrder != 0) {
|
||||
YYCC::EnumHelper::Add(m_3dEntityFlags, CK_3DENTITY_FLAGS::CK_3DENTITY_ZORDERVALID);
|
||||
} else {
|
||||
YYCC::EnumHelper::Remove(m_3dEntityFlags, CK_3DENTITY_FLAGS::CK_3DENTITY_ZORDERVALID);
|
||||
}
|
||||
|
||||
// write 2 flags
|
||||
chunk->WriteStruct(m_3dEntityFlags);
|
||||
chunk->WriteStruct(m_MoveableFlags);
|
||||
|
||||
// write world matrix
|
||||
chunk->WriteStruct(reinterpret_cast<const VxMath::VxVector3*>(&m_WorldMatrix[0]));
|
||||
chunk->WriteStruct(reinterpret_cast<const VxMath::VxVector3*>(&m_WorldMatrix[1]));
|
||||
chunk->WriteStruct(reinterpret_cast<const VxMath::VxVector3*>(&m_WorldMatrix[2]));
|
||||
chunk->WriteStruct(reinterpret_cast<const VxMath::VxVector3*>(&m_WorldMatrix[3]));
|
||||
|
||||
// MARK: because we do not support Parent and CKPlace,
|
||||
// and the IDA code also instruct that no need to write any data if no Parent or CKPlace.
|
||||
// so we skip the Parent and CKPlace writing
|
||||
|
||||
// write z-order
|
||||
if (m_ZOrder != 0) {
|
||||
chunk->WriteStruct(m_ZOrder);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
chunk->SetClassId(CK_CLASSID::CKCID_3DENTITY);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CK3dEntity::Load(CKStateChunk* chunk, CKFileVisitor* file) {
|
||||
bool suc = CKRenderObject::Load(chunk, file);
|
||||
if (!suc) return false;
|
||||
|
||||
// backup moveable flags
|
||||
bool hasWorldAligned = YYCC::EnumHelper::Has(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_WORLDALIGNED);
|
||||
|
||||
// MARK: object animation is skipped
|
||||
|
||||
// read associated meshs data
|
||||
if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_3DENTITY::CK_STATESAVE_MESHS)) {
|
||||
// MARK: I don't know why origianl code do not clear potential mesh list
|
||||
// so I clear it in there.
|
||||
m_PotentialMeshes.clear();
|
||||
|
||||
// read current mesh
|
||||
CKObject* pendingMesh = nullptr;
|
||||
chunk->ReadObjectPointer(pendingMesh);
|
||||
if (pendingMesh != nullptr && pendingMesh->GetClassID() == CK_CLASSID::CKCID_MESH) {
|
||||
m_CurrentMesh = static_cast<CKMesh*>(pendingMesh);
|
||||
}
|
||||
|
||||
// read other meshs
|
||||
XContainer::XObjectPointerArray potentials;
|
||||
chunk->ReadXObjectPointerArray(potentials);
|
||||
for (const auto& ptr : potentials) {
|
||||
if (ptr == nullptr) continue;
|
||||
XContainer::NSXObjectPointerArray::AddIfNotHere(m_PotentialMeshes, ptr);
|
||||
}
|
||||
|
||||
// add current mesh to potential meshes
|
||||
if (m_CurrentMesh != nullptr) {
|
||||
XContainer::NSXObjectPointerArray::AddIfNotHere(m_PotentialMeshes, m_CurrentMesh);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// read core entity data
|
||||
if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_3DENTITY::CK_STATESAVE_3DENTITYNDATA)) {
|
||||
// read 2 flags
|
||||
chunk->ReadStruct(m_3dEntityFlags);
|
||||
chunk->ReadStruct(m_MoveableFlags);
|
||||
// remove some properties
|
||||
YYCC::EnumHelper::Remove(m_3dEntityFlags,
|
||||
CK_3DENTITY_FLAGS::CK_3DENTITY_UPDATELASTFRAME,
|
||||
CK_3DENTITY_FLAGS::CK_3DENTITY_RESERVED0
|
||||
);
|
||||
YYCC::EnumHelper::Remove(m_MoveableFlags,
|
||||
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_RESERVED2,
|
||||
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_STENCILONLY,
|
||||
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_DONTUPDATEFROMPARENT,
|
||||
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_INVERSEWORLDMATVALID,
|
||||
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_HASMOVED,
|
||||
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_BOXVALID,
|
||||
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_USERBOX,
|
||||
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_UPTODATE
|
||||
);
|
||||
if (hasWorldAligned) {
|
||||
YYCC::EnumHelper::Add(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_WORLDALIGNED);
|
||||
}
|
||||
|
||||
// if order render first
|
||||
if (YYCC::EnumHelper::Has(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_RENDERFIRST)) {
|
||||
m_ZOrder = 10000;
|
||||
}
|
||||
|
||||
// read matrix
|
||||
// reset
|
||||
m_WorldMatrix.SetIdentity();
|
||||
// force read as vector3
|
||||
chunk->ReadStruct(reinterpret_cast<VxMath::VxVector3*>(&m_WorldMatrix[0]));
|
||||
chunk->ReadStruct(reinterpret_cast<VxMath::VxVector3*>(&m_WorldMatrix[1]));
|
||||
chunk->ReadStruct(reinterpret_cast<VxMath::VxVector3*>(&m_WorldMatrix[2]));
|
||||
chunk->ReadStruct(reinterpret_cast<VxMath::VxVector3*>(&m_WorldMatrix[3]));
|
||||
// MARK: check right-hand?
|
||||
// I don't know how it checked, just reinterpter IDA code.
|
||||
VxMath::VxVector3 col2(*reinterpret_cast<const VxMath::VxVector3*>(&m_WorldMatrix[2])),
|
||||
col1(*reinterpret_cast<const VxMath::VxVector3*>(&m_WorldMatrix[1])),
|
||||
col0(*reinterpret_cast<const VxMath::VxVector3*>(&m_WorldMatrix[0]));
|
||||
VxMath::VxVector3 crossProduct = VxMath::NSVxVector::CrossProduct(col0, col1);
|
||||
CKFLOAT dotProduct = VxMath::NSVxVector::DotProduct(crossProduct, col2);
|
||||
if (dotProduct >= 0.0f) {
|
||||
YYCC::EnumHelper::Remove(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_INDIRECTMATRIX);
|
||||
} else {
|
||||
YYCC::EnumHelper::Add(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_INDIRECTMATRIX);
|
||||
}
|
||||
|
||||
// copy visible data
|
||||
// process direct visible
|
||||
if (YYCC::EnumHelper::Has(m_ObjectFlags, CK_OBJECT_FLAGS::CK_OBJECT_VISIBLE)) {
|
||||
YYCC::EnumHelper::Add(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_VISIBLE);
|
||||
} else {
|
||||
YYCC::EnumHelper::Remove(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_VISIBLE);
|
||||
}
|
||||
// process indirect visible
|
||||
if (YYCC::EnumHelper::Has(m_ObjectFlags, CK_OBJECT_FLAGS::CK_OBJECT_HIERACHICALHIDE)) {
|
||||
YYCC::EnumHelper::Add(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_HIERARCHICALHIDE);
|
||||
} else {
|
||||
YYCC::EnumHelper::Remove(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_HIERARCHICALHIDE);
|
||||
}
|
||||
|
||||
// read associated CKPlace
|
||||
if (YYCC::EnumHelper::Has(m_3dEntityFlags, CK_3DENTITY_FLAGS::CK_3DENTITY_PLACEVALID)) {
|
||||
// MARK: we drop the support of CKPlace.
|
||||
// so we just read it and skip it.
|
||||
CK_ID placeid;
|
||||
chunk->ReadObjectID(placeid);
|
||||
// and remove this flag
|
||||
YYCC::EnumHelper::Remove(m_3dEntityFlags, CK_3DENTITY_FLAGS::CK_3DENTITY_PLACEVALID);
|
||||
}
|
||||
|
||||
// read parent
|
||||
if (YYCC::EnumHelper::Has(m_3dEntityFlags, CK_3DENTITY_FLAGS::CK_3DENTITY_PARENTVALID)) {
|
||||
// MAKR: we drop the support of parent and the whole 3dentity hierarchy system
|
||||
// we ignore this field.
|
||||
CK_ID parentid;
|
||||
chunk->ReadObjectID(parentid);
|
||||
// and remove this flag
|
||||
YYCC::EnumHelper::Remove(m_3dEntityFlags, CK_3DENTITY_FLAGS::CK_3DENTITY_PARENTVALID);
|
||||
}
|
||||
|
||||
// read priority (non-zero zorder)
|
||||
if (YYCC::EnumHelper::Has(m_3dEntityFlags, CK_3DENTITY_FLAGS::CK_3DENTITY_ZORDERVALID)) {
|
||||
chunk->ReadStruct(m_ZOrder);
|
||||
}
|
||||
|
||||
}
|
||||
// MARK: compatibility alternative core data read code removed because I don't need them
|
||||
|
||||
|
||||
// MARK: skin and bone are skipped.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CK3dEntity::Show(CK_OBJECT_SHOWOPTION show) {
|
||||
CKObject::Show(show);
|
||||
|
||||
YYCC::EnumHelper::Remove(m_MoveableFlags,
|
||||
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_VISIBLE,
|
||||
VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_HIERARCHICALHIDE
|
||||
);
|
||||
switch (show) {
|
||||
case CK_OBJECT_SHOWOPTION::CKSHOW:
|
||||
YYCC::EnumHelper::Add(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_VISIBLE);
|
||||
break;
|
||||
case CK_OBJECT_SHOWOPTION::CKHIERARCHICALHIDE:
|
||||
YYCC::EnumHelper::Add(m_MoveableFlags, VxMath::VX_MOVEABLE_FLAGS::VX_MOVEABLE_HIERARCHICALHIDE);
|
||||
break;
|
||||
case CK_OBJECT_SHOWOPTION::CKHIDE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool CK3dEntity::IsVisible() const {
|
||||
// MARK: originally there is a call to this->IsHiddenByParent.
|
||||
// but we drop the support of parent, so we drop that condition.
|
||||
return CKObject::IsVisible();
|
||||
}
|
||||
|
||||
#pragma region Misc Oper
|
||||
|
||||
const VxMath::VxMatrix& CK3dEntity::GetWorldMatrix() const {
|
||||
return m_WorldMatrix;
|
||||
}
|
||||
|
||||
void CK3dEntity::SetWorldMatrix(const VxMath::VxMatrix& mat) {
|
||||
m_WorldMatrix = mat;
|
||||
}
|
||||
|
||||
CK_3DENTITY_FLAGS CK3dEntity::GetEntityFlags() const {
|
||||
return m_3dEntityFlags;
|
||||
}
|
||||
|
||||
void CK3dEntity::SetEntityFlags(CK_3DENTITY_FLAGS flags) {
|
||||
m_3dEntityFlags = flags;
|
||||
}
|
||||
|
||||
VxMath::VX_MOVEABLE_FLAGS CK3dEntity::GetMoveableFlags() const {
|
||||
return m_MoveableFlags;
|
||||
}
|
||||
|
||||
void CK3dEntity::SetMoveableFlags(VxMath::VX_MOVEABLE_FLAGS flags) {
|
||||
m_MoveableFlags = flags;
|
||||
}
|
||||
|
||||
CKDWORD CK3dEntity::GetZOrder() const {
|
||||
return m_ZOrder;
|
||||
}
|
||||
|
||||
void CK3dEntity::SetZOrder(CKDWORD ord) {
|
||||
m_ZOrder = ord;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Mesh Oper
|
||||
|
||||
void CK3dEntity::AddPotentialMesh(CKMesh* mesh) {
|
||||
XContainer::NSXObjectPointerArray::AddIfNotHere(m_PotentialMeshes, mesh);
|
||||
}
|
||||
|
||||
void CK3dEntity::RemovePotentialMesh(CKMesh* mesh) {
|
||||
std::erase(m_PotentialMeshes, mesh);
|
||||
}
|
||||
|
||||
CKDWORD CK3dEntity::GetPotentialMeshCount() const {
|
||||
return static_cast<CKDWORD>(m_PotentialMeshes.size());
|
||||
}
|
||||
|
||||
CKMesh* CK3dEntity::GetPotentialMesh(CKDWORD idx) const {
|
||||
if (idx >= m_PotentialMeshes.size()) return nullptr;
|
||||
return static_cast<CKMesh*>(m_PotentialMeshes[idx]);
|
||||
}
|
||||
|
||||
CKMesh* CK3dEntity::GetCurrentMesh() const {
|
||||
return m_CurrentMesh;
|
||||
}
|
||||
|
||||
void CK3dEntity::SetCurrentMesh(CKMesh* mesh) {
|
||||
m_CurrentMesh = mesh;
|
||||
if (mesh != nullptr) {
|
||||
AddPotentialMesh(mesh);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../VTInternal.hpp"
|
||||
#include "CKRenderObject.hpp"
|
||||
|
||||
namespace LibCmo::CK2::ObjImpls {
|
||||
|
||||
class CK3dEntity : public CKRenderObject {
|
||||
public:
|
||||
CK3dEntity(CKContext* ctx, CK_ID ckid, CKSTRING name);
|
||||
virtual ~CK3dEntity();
|
||||
YYCC_DEL_CLS_COPY_MOVE(CK3dEntity);
|
||||
|
||||
virtual CK_CLASSID GetClassID() override {
|
||||
return CK_CLASSID::CKCID_3DENTITY;
|
||||
}
|
||||
|
||||
virtual void CheckPreDeletion() override;
|
||||
|
||||
// 2 RW functions
|
||||
virtual bool Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) override;
|
||||
virtual bool Load(CKStateChunk* chunk, CKFileVisitor* file) override;
|
||||
|
||||
// it have special Show and IsVisible method
|
||||
virtual void Show(CK_OBJECT_SHOWOPTION show = CK_OBJECT_SHOWOPTION::CKSHOW) override;
|
||||
virtual bool IsVisible() const override;
|
||||
|
||||
const VxMath::VxMatrix& GetWorldMatrix() const;
|
||||
void SetWorldMatrix(const VxMath::VxMatrix& mat);
|
||||
CK_3DENTITY_FLAGS GetEntityFlags() const;
|
||||
void SetEntityFlags(CK_3DENTITY_FLAGS flags);
|
||||
VxMath::VX_MOVEABLE_FLAGS GetMoveableFlags() const;
|
||||
void SetMoveableFlags(VxMath::VX_MOVEABLE_FLAGS flags);
|
||||
CKDWORD GetZOrder() const;
|
||||
void SetZOrder(CKDWORD ord);
|
||||
|
||||
void AddPotentialMesh(CKMesh* mesh);
|
||||
void RemovePotentialMesh(CKMesh* mesh);
|
||||
CKDWORD GetPotentialMeshCount() const;
|
||||
CKMesh* GetPotentialMesh(CKDWORD idx) const;
|
||||
CKMesh* GetCurrentMesh() const;
|
||||
void SetCurrentMesh(CKMesh* mesh);
|
||||
|
||||
protected:
|
||||
XContainer::XObjectPointerArray m_PotentialMeshes;
|
||||
CKMesh* m_CurrentMesh;
|
||||
VxMath::VxMatrix m_WorldMatrix;
|
||||
CKDWORD m_ZOrder; // replace the whole heavy CKSceneGraphNode
|
||||
|
||||
VxMath::VX_MOVEABLE_FLAGS m_MoveableFlags;
|
||||
// MARK: This field is called m_EntityFlags in reverse project.
|
||||
// I change this because I want to give it a more explicit name to make it is different with other flags.
|
||||
CK_3DENTITY_FLAGS m_3dEntityFlags;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../VTInternal.hpp"
|
||||
#include "CK3dEntity.hpp"
|
||||
|
||||
namespace LibCmo::CK2::ObjImpls {
|
||||
|
||||
class CK3dObject : public CK3dEntity {
|
||||
public:
|
||||
CK3dObject(CKContext* ctx, CK_ID ckid, CKSTRING name) :
|
||||
CK3dEntity(ctx, ckid, name)
|
||||
{}
|
||||
virtual ~CK3dObject() {}
|
||||
YYCC_DEL_CLS_COPY_MOVE(CK3dObject);
|
||||
|
||||
virtual CK_CLASSID GetClassID() override {
|
||||
return CK_CLASSID::CKCID_3DOBJECT;
|
||||
}
|
||||
// CK3dObject do not implement any load/save functions
|
||||
//virtual void PreSave(CKFileVisitor* file, CKDWORD flags) override;
|
||||
//virtual bool Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) override;
|
||||
//virtual bool Load(CKStateChunk* chunk, CKFileVisitor* file) override;
|
||||
//virtual void PostLoad() override;
|
||||
protected:
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
#include "CKSceneObject.hpp"
|
||||
#include "../CKStateChunk.hpp"
|
||||
#include "../CKContext.hpp"
|
||||
#include "../MgrImpls/CKObjectManager.hpp"
|
||||
#include "CKBeObject.hpp"
|
||||
#include "CKGroup.hpp"
|
||||
|
||||
namespace LibCmo::CK2::ObjImpls {
|
||||
|
||||
CKBeObject::CKBeObject(CKContext* ctx, CK_ID ckid, CKSTRING name) :
|
||||
CKSceneObject(ctx, ckid, name), m_Groups() {}
|
||||
|
||||
CKBeObject::~CKBeObject() {}
|
||||
|
||||
bool CKBeObject::Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) {
|
||||
bool suc = CKSceneObject::Save(chunk, file, flags);
|
||||
if (!suc) return false;
|
||||
|
||||
chunk->SetClassId(CK_CLASSID::CKCID_BEOBJECT);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CKBeObject::Load(CKStateChunk* chunk, CKFileVisitor* file) {
|
||||
bool suc = CKSceneObject::Load(chunk, file);
|
||||
if (!suc) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CKBeObject::IsInGroup(CKGroup* group) const {
|
||||
if (group == nullptr) return false;
|
||||
CKDWORD idx = group->GetGroupIndex();
|
||||
return XContainer::NSXBitArray::IsSet(m_Groups, idx);
|
||||
}
|
||||
|
||||
void CKBeObject::ExplicitSetGroup(CKDWORD pos, bool val) {
|
||||
if (val) {
|
||||
XContainer::NSXBitArray::Set(m_Groups, pos);
|
||||
} else {
|
||||
XContainer::NSXBitArray::Unset(m_Groups, pos);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../VTInternal.hpp"
|
||||
#include "CKSceneObject.hpp"
|
||||
|
||||
namespace LibCmo::CK2::ObjImpls {
|
||||
|
||||
class CKBeObject : public CKSceneObject {
|
||||
public:
|
||||
CKBeObject(CKContext* ctx, CK_ID ckid, CKSTRING name);
|
||||
virtual ~CKBeObject();
|
||||
YYCC_DEL_CLS_COPY_MOVE(CKBeObject);
|
||||
|
||||
virtual CK_CLASSID GetClassID() override {
|
||||
return CK_CLASSID::CKCID_BEOBJECT;
|
||||
}
|
||||
|
||||
virtual bool Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) override;
|
||||
virtual bool Load(CKStateChunk* chunk, CKFileVisitor* file) override;
|
||||
|
||||
/**
|
||||
* @brief Check whether this object is in specified group.
|
||||
* @param group[in] The group to be checked.
|
||||
* @return True if in it.
|
||||
*/
|
||||
bool IsInGroup(CKGroup* group) const;
|
||||
/**
|
||||
* @brief Directly set group data.
|
||||
* @param pos
|
||||
* @param val
|
||||
* @warning This function only should be called by CKGroup. Any other classes should not call this.
|
||||
*/
|
||||
void ExplicitSetGroup(CKDWORD pos, bool val);
|
||||
|
||||
protected:
|
||||
XContainer::XBitArray m_Groups;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
#include "CKCamera.hpp"
|
||||
#include "../CKStateChunk.hpp"
|
||||
|
||||
namespace LibCmo::CK2::ObjImpls {
|
||||
|
||||
// Convenient macro to mark this object is not UPTODATE.
|
||||
#define REMOVE_UPTODATE_FLAG { \
|
||||
CK_OBJECT_FLAGS obj_flags = GetObjectFlags(); \
|
||||
YYCC::EnumHelper::Remove(obj_flags, CK_OBJECT_FLAGS::CK_OBJECT_UPTODATE); \
|
||||
SetObjectFlags(obj_flags); \
|
||||
}
|
||||
|
||||
CKCamera::CKCamera(CKContext* ctx, CK_ID ckid, CKSTRING name) :
|
||||
CK3dEntity(ctx, ckid, name),
|
||||
m_ProjectType(CK_CAMERA_PROJECTION::CK_PERSPECTIVEPROJECTION),
|
||||
m_Fov(0.5f), m_OrthographicZoom(1.0f),
|
||||
m_Width(4), m_Height(3),
|
||||
m_FrontPlane(1.0f), m_BackPlane(4000.0f) {
|
||||
REMOVE_UPTODATE_FLAG;
|
||||
}
|
||||
|
||||
CKCamera::~CKCamera() {}
|
||||
|
||||
bool CKCamera::Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) {
|
||||
bool suc = CK3dEntity::Save(chunk, file, flags);
|
||||
if (!suc) return false;
|
||||
|
||||
// Save main data
|
||||
{
|
||||
chunk->WriteIdentifier(CK_STATESAVEFLAGS_CAMERA::CK_STATESAVE_CAMERAONLY);
|
||||
chunk->WriteStruct(m_ProjectType);
|
||||
chunk->WriteStruct(m_Fov);
|
||||
chunk->WriteStruct(m_OrthographicZoom);
|
||||
|
||||
// Build width and height compound.
|
||||
CKDWORD widht_and_height = m_Width & 0x0000FFFFu;
|
||||
widht_and_height |= (m_Height << 16) & 0xFFFF0000u;
|
||||
chunk->WriteStruct(widht_and_height);
|
||||
|
||||
chunk->WriteStruct(m_FrontPlane);
|
||||
chunk->WriteStruct(m_BackPlane);
|
||||
}
|
||||
|
||||
chunk->SetClassId(CK_CLASSID::CKCID_CAMERA);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CKCamera::Load(CKStateChunk* chunk, CKFileVisitor* file) {
|
||||
bool suc = CK3dEntity::Load(chunk, file);
|
||||
if (!suc) return false;
|
||||
|
||||
// MARK: Drop the support for very old file format.
|
||||
if (chunk->GetDataVersion() < CK_STATECHUNK_DATAVERSION::CHUNK_MAJORCHANGE_VERSION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read main data
|
||||
if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_CAMERA::CK_STATESAVE_CAMERAONLY)) {
|
||||
chunk->ReadStruct(m_ProjectType);
|
||||
chunk->ReadStruct(m_Fov);
|
||||
chunk->ReadStruct(m_OrthographicZoom);
|
||||
|
||||
// Width and Height is stored in one DWORD
|
||||
// Higher WORD is height and lower WORD is width.
|
||||
// HIGH >>> height (2 bytes), width (2 bytes) <<< LOW
|
||||
CKDWORD widht_and_height;
|
||||
chunk->ReadStruct(widht_and_height);
|
||||
m_Width = widht_and_height & 0x0000FFFFu;
|
||||
m_Height = (widht_and_height & 0xFFFF0000u) >> 16;
|
||||
|
||||
chunk->ReadStruct(m_FrontPlane);
|
||||
chunk->ReadStruct(m_BackPlane);
|
||||
}
|
||||
|
||||
REMOVE_UPTODATE_FLAG;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma region Class Operations
|
||||
|
||||
CK_CAMERA_PROJECTION CKCamera::GetProjectionType() const {
|
||||
return m_ProjectType;
|
||||
}
|
||||
void CKCamera::SetProjectionType(CK_CAMERA_PROJECTION proj) {
|
||||
m_ProjectType = proj;
|
||||
REMOVE_UPTODATE_FLAG;
|
||||
}
|
||||
|
||||
CKFLOAT CKCamera::GetOrthographicZoom() const {
|
||||
return m_OrthographicZoom;
|
||||
}
|
||||
void CKCamera::SetOrthographicZoom(CKFLOAT zoom) {
|
||||
m_OrthographicZoom = zoom;
|
||||
REMOVE_UPTODATE_FLAG;
|
||||
}
|
||||
|
||||
CKFLOAT CKCamera::GetFrontPlane() const {
|
||||
return m_FrontPlane;
|
||||
}
|
||||
CKFLOAT CKCamera::GetBackPlane() const {
|
||||
return m_BackPlane;
|
||||
}
|
||||
CKFLOAT CKCamera::GetFov() const {
|
||||
return m_Fov;
|
||||
}
|
||||
void CKCamera::SetFrontPlane(CKFLOAT front) {
|
||||
m_FrontPlane = front;
|
||||
REMOVE_UPTODATE_FLAG;
|
||||
}
|
||||
void CKCamera::SetBackPlane(CKFLOAT back) {
|
||||
m_BackPlane = back;
|
||||
REMOVE_UPTODATE_FLAG;
|
||||
}
|
||||
void CKCamera::SetFov(CKFLOAT fov) {
|
||||
m_Fov = fov;
|
||||
REMOVE_UPTODATE_FLAG;
|
||||
}
|
||||
|
||||
void CKCamera::GetAspectRatio(CKDWORD& width, CKDWORD& height) const {
|
||||
width = m_Width;
|
||||
height = m_Height;
|
||||
}
|
||||
void CKCamera::SetAspectRatio(CKDWORD width, CKDWORD height) {
|
||||
m_Width = width;
|
||||
m_Height = height;
|
||||
REMOVE_UPTODATE_FLAG;
|
||||
}
|
||||
|
||||
void CKCamera::ComputeProjectionMatrix(VxMath::VxMatrix& mat) const {
|
||||
CKFLOAT aspect = static_cast<CKFLOAT>(m_Width) / m_Height;
|
||||
if (m_ProjectType == CK_CAMERA_PROJECTION::CK_PERSPECTIVEPROJECTION) {
|
||||
mat.Perspective(m_Fov, aspect, m_FrontPlane, m_BackPlane);
|
||||
} else {
|
||||
mat.Orthographic(m_OrthographicZoom, aspect, m_FrontPlane, m_BackPlane);
|
||||
}
|
||||
}
|
||||
|
||||
//void CKCamera::ResetRoll() {}
|
||||
//void CKCamera::Roll(CKFLOAT angle) {}
|
||||
|
||||
CK3dEntity* CKCamera::GetTarget() const {
|
||||
// Not supported, return nullptr anyway.
|
||||
return nullptr;
|
||||
}
|
||||
void CKCamera::SetTarget(CK3dEntity* target) {
|
||||
// Do nothing because no support.
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
// Undef convenient macro
|
||||
#undef REMOVE_UPTODATE_FLAG
|
||||
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../VTInternal.hpp"
|
||||
#include "CK3dEntity.hpp"
|
||||
|
||||
namespace LibCmo::CK2::ObjImpls {
|
||||
|
||||
class CKCamera : public CK3dEntity {
|
||||
public:
|
||||
CKCamera(CKContext* ctx, CK_ID ckid, CKSTRING name);
|
||||
virtual ~CKCamera();
|
||||
YYCC_DEL_CLS_COPY_MOVE(CKCamera);
|
||||
|
||||
virtual CK_CLASSID GetClassID() override {
|
||||
return CK_CLASSID::CKCID_CAMERA;
|
||||
}
|
||||
|
||||
// 2 RW funcions
|
||||
virtual bool Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) override;
|
||||
virtual bool Load(CKStateChunk* chunk, CKFileVisitor* file) override;
|
||||
|
||||
|
||||
CK_CAMERA_PROJECTION GetProjectionType() const;
|
||||
void SetProjectionType(CK_CAMERA_PROJECTION proj);
|
||||
|
||||
CKFLOAT GetOrthographicZoom() const;
|
||||
void SetOrthographicZoom(CKFLOAT zoom);
|
||||
|
||||
CKFLOAT GetFrontPlane() const;
|
||||
CKFLOAT GetBackPlane() const;
|
||||
CKFLOAT GetFov() const;
|
||||
void SetFrontPlane(CKFLOAT front);
|
||||
void SetBackPlane(CKFLOAT back);
|
||||
void SetFov(CKFLOAT fov);
|
||||
|
||||
void GetAspectRatio(CKDWORD& width, CKDWORD& height) const;
|
||||
void SetAspectRatio(CKDWORD width, CKDWORD height);
|
||||
|
||||
void ComputeProjectionMatrix(VxMath::VxMatrix& mat) const;
|
||||
|
||||
// TODO: Finish CKCamera roll feature because it now involve some functions which is not implemented in CK3dEntity.
|
||||
// Roll Angle
|
||||
//void ResetRoll();
|
||||
//void Roll(CKFLOAT angle);
|
||||
|
||||
// Target access
|
||||
virtual CK3dEntity* GetTarget() const;
|
||||
virtual void SetTarget(CK3dEntity* target);
|
||||
|
||||
protected:
|
||||
CKFLOAT m_Fov;
|
||||
CKFLOAT m_FrontPlane, m_BackPlane;
|
||||
CK_CAMERA_PROJECTION m_ProjectType;
|
||||
CKFLOAT m_OrthographicZoom;
|
||||
CKDWORD m_Width, m_Height;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
#include "CKGroup.hpp"
|
||||
#include "../CKStateChunk.hpp"
|
||||
#include "../CKContext.hpp"
|
||||
#include "../MgrImpls/CKObjectManager.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
namespace LibCmo::CK2::ObjImpls {
|
||||
|
||||
CKGroup::CKGroup(CKContext* ctx, CK_ID ckid, CKSTRING name) :
|
||||
CKBeObject(ctx, ckid, name),
|
||||
m_ObjectArray(),
|
||||
m_GroupIndex(m_Context->GetObjectManager()->AllocateGroupGlobalIndex()) {}
|
||||
|
||||
CKGroup::~CKGroup() {
|
||||
// free self allocated id
|
||||
m_Context->GetObjectManager()->FreeGroupGlobalIndex(m_GroupIndex);
|
||||
}
|
||||
|
||||
void CKGroup::PreDelete() {
|
||||
CKBeObject::PreDelete();
|
||||
|
||||
// unlink all grouped object
|
||||
for (auto& ptr : m_ObjectArray) {
|
||||
static_cast<CKBeObject*>(ptr)->ExplicitSetGroup(m_GroupIndex, false);
|
||||
}
|
||||
m_ObjectArray.clear();
|
||||
}
|
||||
|
||||
void CKGroup::CheckPreDeletion() {
|
||||
CKBeObject::CheckPreDeletion();
|
||||
|
||||
// remove self invalid object ptr
|
||||
XContainer::NSXObjectPointerArray::PreDeletedCheck(m_ObjectArray, m_Context);
|
||||
}
|
||||
|
||||
bool CKGroup::Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) {
|
||||
bool suc = CKBeObject::Save(chunk, file, flags);
|
||||
if (!suc) return false;
|
||||
|
||||
// write grouped object
|
||||
{
|
||||
chunk->WriteIdentifier(CK_STATESAVEFLAGS_GROUP::CK_STATESAVE_GROUPALL);
|
||||
chunk->WriteXObjectPointerArray(m_ObjectArray);
|
||||
}
|
||||
|
||||
chunk->SetClassId(CK_CLASSID::CKCID_GROUP);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CKGroup::Load(CKStateChunk* chunk, CKFileVisitor* file) {
|
||||
bool suc = CKBeObject::Load(chunk, file);
|
||||
if (!suc) return false;
|
||||
|
||||
// cleat self
|
||||
this->Clear();
|
||||
|
||||
// get grouped objects
|
||||
if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_GROUP::CK_STATESAVE_GROUPALL)) {
|
||||
XContainer::XObjectPointerArray ptrs;
|
||||
chunk->ReadXObjectPointerArray(ptrs);
|
||||
|
||||
// filter pointer and check them type
|
||||
for (auto& ptr : ptrs) {
|
||||
// skip bad one
|
||||
if (ptr == nullptr || ptr == this
|
||||
|| !CKIsChildClassOf(ptr->GetClassID(), CK_CLASSID::CKCID_BEOBJECT)) {
|
||||
continue;
|
||||
}
|
||||
CKBeObject* beobj = static_cast<CKBeObject*>(ptr);
|
||||
if (beobj->IsInGroup(this)) continue;
|
||||
|
||||
// add good one
|
||||
beobj->ExplicitSetGroup(m_GroupIndex, true);
|
||||
m_ObjectArray.emplace_back(beobj);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CKGroup::Show(CK_OBJECT_SHOWOPTION show) {
|
||||
CKObject::Show(show);
|
||||
|
||||
// call Show for all sub object
|
||||
for (auto& ptr : m_ObjectArray) {
|
||||
if (ptr == nullptr) continue;
|
||||
ptr->Show(show);
|
||||
}
|
||||
}
|
||||
|
||||
CKDWORD CKGroup::GetGroupIndex() const {
|
||||
return m_GroupIndex;
|
||||
}
|
||||
|
||||
CKERROR CKGroup::AddObject(CKBeObject* o) {
|
||||
if (o == nullptr || o == this || !CKIsChildClassOf(o->GetClassID(), CK_CLASSID::CKCID_BEOBJECT)) {
|
||||
return CKERROR::CKERR_INVALIDPARAMETER;
|
||||
}
|
||||
if (o->IsInGroup(this)) {
|
||||
return CKERROR::CKERR_ALREADYPRESENT;
|
||||
}
|
||||
|
||||
// set object
|
||||
o->ExplicitSetGroup(m_GroupIndex, true);
|
||||
// set self
|
||||
m_ObjectArray.emplace_back(o);
|
||||
return CKERROR::CKERR_OK;
|
||||
}
|
||||
|
||||
CKBeObject* CKGroup::RemoveObject(CKDWORD pos) {
|
||||
// check pos
|
||||
if (pos >= m_ObjectArray.size()) return nullptr;
|
||||
|
||||
auto it = m_ObjectArray.begin() + pos;
|
||||
CKBeObject* obj = static_cast<CKBeObject*>(*it);
|
||||
// set object
|
||||
obj->ExplicitSetGroup(m_GroupIndex, false);
|
||||
// remove self
|
||||
m_ObjectArray.erase(it);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void CKGroup::RemoveObject(CKBeObject* obj) {
|
||||
// find first
|
||||
auto finder = std::find(m_ObjectArray.begin(), m_ObjectArray.end(), static_cast<CKObject*>(obj));
|
||||
if (finder != m_ObjectArray.end()) {
|
||||
// set object
|
||||
static_cast<CKBeObject*>(*finder)->ExplicitSetGroup(m_GroupIndex, false);
|
||||
// remove self
|
||||
m_ObjectArray.erase(finder);
|
||||
}
|
||||
}
|
||||
|
||||
void CKGroup::Clear() {
|
||||
for (auto& beobj : m_ObjectArray) {
|
||||
// set object
|
||||
static_cast<CKBeObject*>(beobj)->ExplicitSetGroup(m_GroupIndex, false);
|
||||
}
|
||||
|
||||
m_ObjectArray.clear();
|
||||
}
|
||||
|
||||
CKBeObject* CKGroup::GetObject(CKDWORD pos) const {
|
||||
if (pos >= m_ObjectArray.size()) return nullptr;
|
||||
else return static_cast<CKBeObject*>(m_ObjectArray[pos]);
|
||||
}
|
||||
|
||||
CKDWORD CKGroup::GetObjectCount() const {
|
||||
return static_cast<CKDWORD>(m_ObjectArray.size());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../VTInternal.hpp"
|
||||
#include "CKBeObject.hpp"
|
||||
|
||||
namespace LibCmo::CK2::ObjImpls {
|
||||
|
||||
class CKGroup : public CKBeObject {
|
||||
public:
|
||||
CKGroup(CKContext* ctx, CK_ID ckid, CKSTRING name);
|
||||
virtual ~CKGroup();
|
||||
YYCC_DEL_CLS_COPY_MOVE(CKGroup);
|
||||
|
||||
virtual CK_CLASSID GetClassID() override {
|
||||
return CK_CLASSID::CKCID_GROUP;
|
||||
}
|
||||
|
||||
virtual void PreDelete() override;
|
||||
virtual void CheckPreDeletion() override;
|
||||
|
||||
//virtual void PreSave(CKFileVisitor* file, CKDWORD flags) override;
|
||||
virtual bool Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) override;
|
||||
virtual bool Load(CKStateChunk* chunk, CKFileVisitor* file) override;
|
||||
//virtual void PostLoad() override;
|
||||
|
||||
// it only have special Show method
|
||||
virtual void Show(CK_OBJECT_SHOWOPTION show = CK_OBJECT_SHOWOPTION::CKSHOW) override;
|
||||
|
||||
CKDWORD GetGroupIndex() const;
|
||||
|
||||
// ===== Insert =====
|
||||
CKERROR AddObject(CKBeObject *o);
|
||||
|
||||
// ===== Remove =====
|
||||
CKBeObject* RemoveObject(CKDWORD pos);
|
||||
void RemoveObject(CKBeObject *obj);
|
||||
void Clear();
|
||||
|
||||
// ===== Access =====
|
||||
CKBeObject* GetObject(CKDWORD pos) const;
|
||||
CKDWORD GetObjectCount() const;
|
||||
|
||||
protected:
|
||||
XContainer::XObjectPointerArray m_ObjectArray;
|
||||
CKDWORD m_GroupIndex;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,232 +0,0 @@
|
||||
#include "CKLight.hpp"
|
||||
#include "../CKStateChunk.hpp"
|
||||
#include <numbers>
|
||||
|
||||
namespace LibCmo::CK2::ObjImpls {
|
||||
|
||||
CKLight::CKLight(CKContext* ctx, CK_ID ckid, CKSTRING name) :
|
||||
CK3dEntity(ctx, ckid, name),
|
||||
m_LightData(), m_LightFlags(LightFlags::Active), m_LightPower(1.0f) {
|
||||
// Setup light data
|
||||
m_LightData.m_Type = VxMath::VXLIGHT_TYPE::VX_LIGHTPOINT;
|
||||
m_LightData.m_Diffuse = VxMath::VxColor(1.0f, 1.0f, 1.0f);
|
||||
m_LightData.m_Specular = VxMath::VxColor(0);
|
||||
m_LightData.m_Ambient = VxMath::VxColor(0.0f, 0.0f, 0.0f);
|
||||
m_LightData.m_Range = 5000.0f;
|
||||
m_LightData.m_Falloff = 1.0f;
|
||||
m_LightData.m_Attenuation0 = 1.0f;
|
||||
m_LightData.m_Attenuation1 = 0.0f;
|
||||
m_LightData.m_Attenuation2 = 0.0f;
|
||||
m_LightData.m_InnerSpotCone = 40.0f / 180.0f * std::numbers::pi_v<float>; // MARK: Original value is 0.69813174f. Perhaps 40 deg in rad.
|
||||
m_LightData.m_OuterSpotCone = 45.0f / 180.0f * std::numbers::pi_v<float>; // MARK: Original value is 0.78539819f. Perhaps 45 deg in rad.
|
||||
}
|
||||
|
||||
CKLight::~CKLight() {}
|
||||
|
||||
bool CKLight::Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) {
|
||||
bool suc = CK3dEntity::Save(chunk, file, flags);
|
||||
if (!suc) return false;
|
||||
|
||||
// Save main data
|
||||
{
|
||||
chunk->WriteIdentifier(CK_STATESAVEFLAGS_LIGHT::CK_STATESAVE_LIGHTDATA);
|
||||
|
||||
// Combine light type and flags data.
|
||||
CKDWORD light_type_and_flags = static_cast<CKDWORD>(m_LightFlags) & 0xFFFFFF00u;
|
||||
light_type_and_flags |= static_cast<CKDWORD>(m_LightData.m_Type) & 0xFFu;
|
||||
chunk->WriteStruct(light_type_and_flags);
|
||||
|
||||
// Save diffuse color with constant 1.0 alpha factor.
|
||||
chunk->WriteStruct(m_LightData.m_Diffuse.ToARGB() | 0xFF000000u);
|
||||
|
||||
chunk->WriteStruct(m_LightData.m_Attenuation0);
|
||||
chunk->WriteStruct(m_LightData.m_Attenuation1);
|
||||
chunk->WriteStruct(m_LightData.m_Attenuation2);
|
||||
|
||||
chunk->WriteStruct(m_LightData.m_Range);
|
||||
|
||||
if (m_LightData.m_Type == VxMath::VXLIGHT_TYPE::VX_LIGHTSPOT) {
|
||||
chunk->WriteStruct(m_LightData.m_OuterSpotCone);
|
||||
chunk->WriteStruct(m_LightData.m_InnerSpotCone);
|
||||
chunk->WriteStruct(m_LightData.m_Falloff);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Save light power
|
||||
if (m_LightPower != 1.0f) {
|
||||
chunk->WriteIdentifier(CK_STATESAVEFLAGS_LIGHT::CK_STATESAVE_LIGHTDATA2);
|
||||
chunk->WriteStruct(m_LightPower);
|
||||
}
|
||||
|
||||
chunk->SetClassId(CK_CLASSID::CKCID_LIGHT);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CKLight::Load(CKStateChunk* chunk, CKFileVisitor* file) {
|
||||
bool suc = CK3dEntity::Load(chunk, file);
|
||||
if (!suc) return false;
|
||||
|
||||
// MARK: I drop the read process for too low version.
|
||||
// return false anyway.
|
||||
if (chunk->GetDataVersion() < CK_STATECHUNK_DATAVERSION::CHUNK_MAJORCHANGE_VERSION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read main data
|
||||
if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_LIGHT::CK_STATESAVE_LIGHTDATA)) {
|
||||
// Read a DWORD storing light type and flags
|
||||
// Because the lowest byte in light flags always is 0x00,
|
||||
// so Virtools use it to store light type.
|
||||
// After removing light type component, the rest of data is light flags.
|
||||
// (do not need any SHIFT! It's okey that just set the lowest byte to zero.)
|
||||
CKDWORD light_type_and_flags;
|
||||
chunk->ReadStruct(light_type_and_flags);
|
||||
m_LightData.m_Type = static_cast<VxMath::VXLIGHT_TYPE>(light_type_and_flags & 0xFFu);
|
||||
m_LightFlags = static_cast<LightFlags>(light_type_and_flags & 0xFFFFFF00u);
|
||||
|
||||
CKDWORD dword_diffuse;
|
||||
chunk->ReadStruct(dword_diffuse);
|
||||
m_LightData.m_Diffuse = VxMath::VxColor(dword_diffuse);
|
||||
|
||||
chunk->ReadStruct(m_LightData.m_Attenuation0);
|
||||
chunk->ReadStruct(m_LightData.m_Attenuation1);
|
||||
chunk->ReadStruct(m_LightData.m_Attenuation2);
|
||||
|
||||
chunk->ReadStruct(m_LightData.m_Range);
|
||||
|
||||
if (m_LightData.m_Type == VxMath::VXLIGHT_TYPE::VX_LIGHTSPOT) {
|
||||
chunk->ReadStruct(m_LightData.m_OuterSpotCone);
|
||||
chunk->ReadStruct(m_LightData.m_InnerSpotCone);
|
||||
chunk->ReadStruct(m_LightData.m_Falloff);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Read light power
|
||||
if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_LIGHT::CK_STATESAVE_LIGHTDATA2)) {
|
||||
chunk->ReadStruct(m_LightPower);
|
||||
} else {
|
||||
m_LightPower = 1.0f;
|
||||
}
|
||||
|
||||
// Correct light type to prevent accident out of range value.
|
||||
switch (m_LightData.m_Type) {
|
||||
case VxMath::VXLIGHT_TYPE::VX_LIGHTPOINT:
|
||||
case VxMath::VXLIGHT_TYPE::VX_LIGHTSPOT:
|
||||
case VxMath::VXLIGHT_TYPE::VX_LIGHTDIREC:
|
||||
case VxMath::VXLIGHT_TYPE::VX_LIGHTPARA:
|
||||
// do nothing
|
||||
break;
|
||||
default:
|
||||
// reset it to point
|
||||
m_LightData.m_Type = VxMath::VXLIGHT_TYPE::VX_LIGHTPOINT;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma region Class Operations
|
||||
|
||||
VxMath::VXLIGHT_TYPE CKLight::GetType() const {
|
||||
return m_LightData.m_Type;
|
||||
}
|
||||
void CKLight::SetType(VxMath::VXLIGHT_TYPE light_type) {
|
||||
m_LightData.m_Type = light_type;
|
||||
}
|
||||
|
||||
const VxMath::VxColor& CKLight::GetColor() const {
|
||||
return m_LightData.m_Diffuse;
|
||||
}
|
||||
void CKLight::SetColor(const VxMath::VxColor& c) {
|
||||
m_LightData.m_Diffuse = c;
|
||||
}
|
||||
|
||||
CKFLOAT CKLight::GetConstantAttenuation() const {
|
||||
return m_LightData.m_Attenuation0;
|
||||
}
|
||||
CKFLOAT CKLight::GetLinearAttenuation() const {
|
||||
return m_LightData.m_Attenuation1;
|
||||
}
|
||||
CKFLOAT CKLight::GetQuadraticAttenuation() const {
|
||||
return m_LightData.m_Attenuation2;
|
||||
}
|
||||
void CKLight::SetConstantAttenuation(CKFLOAT value) {
|
||||
m_LightData.m_Attenuation0 = value;
|
||||
}
|
||||
void CKLight::SetLinearAttenuation(CKFLOAT value) {
|
||||
m_LightData.m_Attenuation1 = value;
|
||||
}
|
||||
void CKLight::SetQuadraticAttenuation(CKFLOAT value) {
|
||||
m_LightData.m_Attenuation2 = value;
|
||||
}
|
||||
|
||||
CKFLOAT CKLight::GetRange() const {
|
||||
return m_LightData.m_Range;
|
||||
}
|
||||
void CKLight::SetRange(CKFLOAT value) {
|
||||
m_LightData.m_Range = value;
|
||||
}
|
||||
|
||||
CKFLOAT CKLight::GetHotSpot() const {
|
||||
return m_LightData.m_InnerSpotCone;
|
||||
}
|
||||
CKFLOAT CKLight::GetFalloff() const {
|
||||
return m_LightData.m_OuterSpotCone;
|
||||
}
|
||||
CKFLOAT CKLight::GetFalloffShape() const {
|
||||
return m_LightData.m_Falloff;
|
||||
}
|
||||
void CKLight::SetHotSpot(CKFLOAT value) {
|
||||
m_LightData.m_InnerSpotCone = value;
|
||||
}
|
||||
void CKLight::SetFalloff(CKFLOAT value) {
|
||||
m_LightData.m_OuterSpotCone = value;
|
||||
}
|
||||
void CKLight::SetFalloffShape(CKFLOAT value) {
|
||||
m_LightData.m_Falloff = value;
|
||||
}
|
||||
|
||||
bool CKLight::GetActivity() const {
|
||||
return YYCC::EnumHelper::Has(m_LightFlags, LightFlags::Active);
|
||||
}
|
||||
void CKLight::Active(bool active) {
|
||||
if (active) {
|
||||
YYCC::EnumHelper::Add(m_LightFlags, LightFlags::Active);
|
||||
} else {
|
||||
YYCC::EnumHelper::Remove(m_LightFlags, LightFlags::Active);
|
||||
}
|
||||
}
|
||||
|
||||
bool CKLight::GetSpecularFlag() const {
|
||||
return YYCC::EnumHelper::Has(m_LightFlags, LightFlags::Specular);
|
||||
}
|
||||
void CKLight::SetSpecularFlag(bool specular) {
|
||||
if (specular) {
|
||||
YYCC::EnumHelper::Add(m_LightFlags, LightFlags::Specular);
|
||||
} else {
|
||||
YYCC::EnumHelper::Remove(m_LightFlags, LightFlags::Specular);
|
||||
}
|
||||
}
|
||||
|
||||
CK3dEntity* CKLight::GetTarget() const {
|
||||
// Normal light do not support target.
|
||||
// So it always return nullptr.
|
||||
return nullptr;
|
||||
}
|
||||
void CKLight::SetTarget(CK3dEntity* target) {
|
||||
// Normal light do not support target.
|
||||
// So, do nothing.
|
||||
}
|
||||
|
||||
CKFLOAT CKLight::GetLightPower() const {
|
||||
return m_LightPower;
|
||||
}
|
||||
void CKLight::SetLightPower(CKFLOAT power) {
|
||||
m_LightPower = power;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../VTInternal.hpp"
|
||||
#include "CK3dEntity.hpp"
|
||||
|
||||
namespace LibCmo::CK2::ObjImpls {
|
||||
|
||||
class CKLight : public CK3dEntity {
|
||||
public:
|
||||
CKLight(CKContext* ctx, CK_ID ckid, CKSTRING name);
|
||||
virtual ~CKLight();
|
||||
YYCC_DEL_CLS_COPY_MOVE(CKLight);
|
||||
|
||||
virtual CK_CLASSID GetClassID() override {
|
||||
return CK_CLASSID::CKCID_LIGHT;
|
||||
}
|
||||
|
||||
// 2 RW funcions
|
||||
virtual bool Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) override;
|
||||
virtual bool Load(CKStateChunk* chunk, CKFileVisitor* file) override;
|
||||
|
||||
// Type
|
||||
VxMath::VXLIGHT_TYPE GetType() const;
|
||||
void SetType(VxMath::VXLIGHT_TYPE light_type);
|
||||
|
||||
const VxMath::VxColor& GetColor() const;
|
||||
void SetColor(const VxMath::VxColor& c);
|
||||
|
||||
CKFLOAT GetConstantAttenuation() const;
|
||||
CKFLOAT GetLinearAttenuation() const;
|
||||
CKFLOAT GetQuadraticAttenuation() const;
|
||||
void SetConstantAttenuation(CKFLOAT value);
|
||||
void SetLinearAttenuation(CKFLOAT value);
|
||||
void SetQuadraticAttenuation(CKFLOAT value);
|
||||
|
||||
// Range
|
||||
CKFLOAT GetRange() const;
|
||||
void SetRange(CKFLOAT value);
|
||||
|
||||
// Spotlight options
|
||||
CKFLOAT GetHotSpot() const;
|
||||
CKFLOAT GetFalloff() const;
|
||||
CKFLOAT GetFalloffShape() const;
|
||||
void SetHotSpot(CKFLOAT value);
|
||||
void SetFalloff(CKFLOAT value);
|
||||
void SetFalloffShape(CKFLOAT value);
|
||||
|
||||
// Activity options
|
||||
bool GetActivity() const;
|
||||
void Active(bool active);
|
||||
|
||||
bool GetSpecularFlag() const;
|
||||
void SetSpecularFlag(bool specular);
|
||||
|
||||
// Target access
|
||||
virtual CK3dEntity* GetTarget() const;
|
||||
virtual void SetTarget(CK3dEntity* target);
|
||||
|
||||
CKFLOAT GetLightPower() const;
|
||||
void SetLightPower(CKFLOAT power = 1.0f);
|
||||
|
||||
protected:
|
||||
enum class LightFlags : CKDWORD {
|
||||
None = 0,
|
||||
Active = 0x100u, /**< if set, this light is active. */
|
||||
Specular = 0x200u, /**< if set, this light has specular flag. */
|
||||
};
|
||||
|
||||
CKLightData m_LightData;
|
||||
// MARK: This variable is called in m_Flags in reverse code.
|
||||
LightFlags m_LightFlags;
|
||||
CKFLOAT m_LightPower;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,415 +0,0 @@
|
||||
#include "CKMaterial.hpp"
|
||||
#include "../CKStateChunk.hpp"
|
||||
#include "../CKContext.hpp"
|
||||
#include "CKTexture.hpp"
|
||||
|
||||
namespace LibCmo::CK2::ObjImpls {
|
||||
|
||||
CKMaterial::CKMaterial(CKContext* ctx, CK_ID ckid, CKSTRING name) :
|
||||
CKBeObject(ctx, ckid, name),
|
||||
// following init are gotten from SDK document.
|
||||
m_Diffuse(0.7f, 0.7f, 0.7f, 1.0f),
|
||||
m_Ambient(0.3f, 0.3f, 0.3f, 1.0f),
|
||||
m_Specular(0.5f, 0.5f, 0.5f, 1.0f), m_SpecularPower(0.0f),
|
||||
m_Emissive(0.0f, 0.0f, 0.0f, 1.0f),
|
||||
m_EnableTwoSided(false),
|
||||
m_Textures { nullptr, nullptr, nullptr, nullptr },
|
||||
m_TextureMinMode(VxMath::VXTEXTURE_FILTERMODE::VXTEXTUREFILTER_LINEAR), m_TextureMagMode(VxMath::VXTEXTURE_FILTERMODE::VXTEXTUREFILTER_LINEAR),
|
||||
m_SourceBlend(VxMath::VXBLEND_MODE::VXBLEND_ONE), m_DestBlend(VxMath::VXBLEND_MODE::VXBLEND_ZERO), m_EnableAlphaBlend(false),
|
||||
m_ShadeMode(VxMath::VXSHADE_MODE::VXSHADE_GOURAUD),
|
||||
m_FillMode(VxMath::VXFILL_MODE::VXFILL_SOLID),
|
||||
m_EnableAlphaTest(false),
|
||||
m_EnableZWrite(true),
|
||||
// following init are gotten from IDA.
|
||||
m_EnablePerspectiveCorrection(true),
|
||||
m_TextureBlendMode(VxMath::VXTEXTURE_BLENDMODE::VXTEXTUREBLEND_MODULATEALPHA),
|
||||
m_TextureAddressMode(VxMath::VXTEXTURE_ADDRESSMODE::VXTEXTURE_ADDRESSWRAP),
|
||||
m_ZFunc(VxMath::VXCMPFUNC::VXCMP_LESSEQUAL),
|
||||
m_AlphaFunc(VxMath::VXCMPFUNC::VXCMP_ALWAYS),
|
||||
m_Effect(VxMath::VX_EFFECT::VXEFFECT_NONE),
|
||||
m_TextureBorderColor(0),
|
||||
m_AlphaRef(0)
|
||||
{}
|
||||
|
||||
CKMaterial::~CKMaterial() {}
|
||||
|
||||
void CKMaterial::CheckPreDeletion() {
|
||||
CKBeObject::CheckPreDeletion();
|
||||
|
||||
// check 4 textures
|
||||
for (auto& tex : m_Textures) {
|
||||
if (tex != nullptr && tex->IsToBeDeleted()) {
|
||||
tex = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CKMaterial::Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) {
|
||||
bool suc = CKBeObject::Save(chunk, file, flags);
|
||||
if (!suc) return false;
|
||||
|
||||
// save main data
|
||||
{
|
||||
chunk->WriteIdentifier(CK_STATESAVEFLAGS_MATERIAL::CK_STATESAVE_MATDATA);
|
||||
|
||||
// 4 basic color and some power
|
||||
CKDWORD col;
|
||||
col = m_Diffuse.ToARGB();
|
||||
chunk->WriteStruct(col);
|
||||
col = m_Ambient.ToARGB();
|
||||
chunk->WriteStruct(col);
|
||||
col = m_Specular.ToARGB();
|
||||
chunk->WriteStruct(col);
|
||||
col = m_Emissive.ToARGB();
|
||||
chunk->WriteStruct(col);
|
||||
|
||||
chunk->WriteStruct(m_SpecularPower);
|
||||
|
||||
// write main texture
|
||||
chunk->WriteObjectPointer(m_Textures[0]);
|
||||
|
||||
// misc data
|
||||
chunk->WriteStruct(m_TextureBorderColor);
|
||||
|
||||
// write mix data 1
|
||||
// construct mix data, see Read for more info about this mix data
|
||||
CKDWORD mixdata = 0;
|
||||
mixdata |= static_cast<CKDWORD>(m_TextureAddressMode) & 0xF;
|
||||
mixdata <<= 4;
|
||||
mixdata |= static_cast<CKDWORD>(m_FillMode) & 0xF;
|
||||
mixdata <<= 4;
|
||||
mixdata |= static_cast<CKDWORD>(m_ShadeMode) & 0xF;
|
||||
mixdata <<= 4;
|
||||
mixdata |= static_cast<CKDWORD>(m_DestBlend) & 0xF;
|
||||
mixdata <<= 4;
|
||||
mixdata |= static_cast<CKDWORD>(m_SourceBlend) & 0xF;
|
||||
mixdata <<= 4;
|
||||
mixdata |= static_cast<CKDWORD>(m_TextureMagMode) & 0xF;
|
||||
mixdata <<= 4;
|
||||
mixdata |= static_cast<CKDWORD>(m_TextureMinMode) & 0xF;
|
||||
mixdata <<= 4;
|
||||
mixdata |= static_cast<CKDWORD>(m_TextureBlendMode) & 0xF;
|
||||
// write it
|
||||
chunk->WriteStruct(mixdata);
|
||||
|
||||
// write mix data 2
|
||||
// construct it first, see Read for more info about this mix data
|
||||
mixdata = 0;
|
||||
mixdata |= static_cast<CKDWORD>(m_AlphaRef) & 0xFF;
|
||||
mixdata <<= 8;
|
||||
mixdata |= static_cast<CKDWORD>(m_AlphaFunc) & 0xFF;
|
||||
mixdata <<= 8;
|
||||
mixdata |= static_cast<CKDWORD>(m_ZFunc) & 0xFF;
|
||||
mixdata <<= 8;
|
||||
// sub mix flags
|
||||
CKDWORD mixflags = 0;
|
||||
if (m_EnableTwoSided) mixflags |= 0b1;
|
||||
if (m_EnableZWrite) mixflags |= 0b10;
|
||||
if (m_EnablePerspectiveCorrection) mixflags |= 0b100;
|
||||
if (m_EnableAlphaBlend) mixflags |= 0b1000;
|
||||
if (m_EnableAlphaTest) mixflags |= 0b10000;
|
||||
// merge into mix data
|
||||
mixdata |= mixflags & 0xFF;
|
||||
// write it
|
||||
chunk->WriteStruct(mixdata);
|
||||
|
||||
}
|
||||
|
||||
// write effect and extra texture.
|
||||
// it seems that extra textures only available when a valid effect existing
|
||||
if (m_Effect != VxMath::VX_EFFECT::VXEFFECT_NONE) {
|
||||
// we only support no-parameter effect, write it
|
||||
chunk->WriteIdentifier(CK_STATESAVEFLAGS_MATERIAL::CK_STATESAVE_MATDATA3);
|
||||
chunk->WriteStruct(m_Effect);
|
||||
|
||||
// if have extra textures, write it
|
||||
if (m_Textures[1] != nullptr || m_Textures[2] != nullptr || m_Textures[3] != nullptr) {
|
||||
chunk->WriteIdentifier(CK_STATESAVEFLAGS_MATERIAL::CK_STATESAVE_MATDATA2);
|
||||
chunk->WriteObjectPointer(m_Textures[1]);
|
||||
chunk->WriteObjectPointer(m_Textures[2]);
|
||||
chunk->WriteObjectPointer(m_Textures[3]);
|
||||
}
|
||||
}
|
||||
|
||||
chunk->SetClassId(CK_CLASSID::CKCID_MATERIAL);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CKMaterial::Load(CKStateChunk* chunk, CKFileVisitor* file) {
|
||||
bool suc = CKBeObject::Load(chunk, file);
|
||||
if (!suc) return false;
|
||||
|
||||
// clear textures
|
||||
for (auto& tex : m_Textures) {
|
||||
tex = nullptr;
|
||||
}
|
||||
|
||||
// read main data
|
||||
if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_MATERIAL::CK_STATESAVE_MATDATA)) {
|
||||
if (chunk->GetDataVersion() < CK_STATECHUNK_DATAVERSION::CHUNK_MAJORCHANGE_VERSION) {
|
||||
// MARK: old data process. i don't want to implement
|
||||
// because it is not related to my work. return false
|
||||
return false;
|
||||
} else {
|
||||
// 4 basic color and some power
|
||||
CKDWORD col;
|
||||
chunk->ReadStruct(col);
|
||||
m_Diffuse.FromARGB(col);
|
||||
chunk->ReadStruct(col);
|
||||
m_Ambient.FromARGB(col);
|
||||
chunk->ReadStruct(col);
|
||||
m_Specular.FromARGB(col);
|
||||
chunk->ReadStruct(col);
|
||||
m_Emissive.FromARGB(col);
|
||||
|
||||
chunk->ReadStruct(m_SpecularPower);
|
||||
|
||||
// main texture
|
||||
CKObject* tex = nullptr;
|
||||
chunk->ReadObjectPointer(tex);
|
||||
if (tex != nullptr && tex->GetClassID() == CK_CLASSID::CKCID_TEXTURE) {
|
||||
m_Textures[0] = static_cast<CKTexture*>(tex);
|
||||
}
|
||||
|
||||
// misc data
|
||||
chunk->ReadStruct(m_TextureBorderColor);
|
||||
|
||||
// mix data 1, including some blend enums
|
||||
// 32bit data. each 4 bit store a value. total 8 data.
|
||||
// HIGH >>> m_TextureAddressMode, m_FillMode, m_ShadeMode, m_DestBlend, m_SourceBlend, m_TextureMagMode, m_TextureMinMode, m_TextureBlendMode <<< LOW
|
||||
CKDWORD mixdata;
|
||||
chunk->ReadStruct(mixdata);
|
||||
m_TextureBlendMode = static_cast<VxMath::VXTEXTURE_BLENDMODE>(mixdata & 0xF);
|
||||
mixdata >>= 4;
|
||||
m_TextureMinMode = static_cast<VxMath::VXTEXTURE_FILTERMODE>(mixdata & 0xF);
|
||||
mixdata >>= 4;
|
||||
m_TextureMagMode = static_cast<VxMath::VXTEXTURE_FILTERMODE>(mixdata & 0xF);
|
||||
mixdata >>= 4;
|
||||
m_SourceBlend = static_cast<VxMath::VXBLEND_MODE>(mixdata & 0xF);
|
||||
mixdata >>= 4;
|
||||
m_DestBlend = static_cast<VxMath::VXBLEND_MODE>(mixdata & 0xF);
|
||||
mixdata >>= 4;
|
||||
m_ShadeMode = static_cast<VxMath::VXSHADE_MODE>(mixdata & 0xF);
|
||||
mixdata >>= 4;
|
||||
m_FillMode = static_cast<VxMath::VXFILL_MODE>(mixdata & 0xF);
|
||||
mixdata >>= 4;
|
||||
m_TextureAddressMode = static_cast<VxMath::VXTEXTURE_ADDRESSMODE>(mixdata & 0xF);
|
||||
|
||||
// mix data 2, including enable flag and transparent data
|
||||
// 32bit data.
|
||||
// HIGH >>> 0xFFFF(m_AlphaRef) 0xFFFF(m_AlphaFunc) 0xFFFF(m_ZFunc) 0xFFFF(enable flags) <<< LOW
|
||||
// for enable flags, total 8 bit. only low 5 bit used.
|
||||
// HIGH >>> 0(not used) 0(not used) 0(not used) 1(m_EnableAlphaTest) 1(m_EnableAlphaBlend) 1(m_EnablePerspectiveCorrection) 1(m_EnableZWrite) 1(m_EnableTwoSided) <<< LOW
|
||||
chunk->ReadStruct(mixdata);
|
||||
m_EnableTwoSided = mixdata & 0b1;
|
||||
m_EnableZWrite = mixdata & 0b10;
|
||||
m_EnablePerspectiveCorrection = mixdata & 0b100;
|
||||
m_EnableAlphaBlend = mixdata & 0b1000;
|
||||
m_EnableAlphaTest = mixdata & 0b10000;
|
||||
mixdata >>= 8;
|
||||
m_ZFunc = static_cast<VxMath::VXCMPFUNC>(mixdata & 0xFF);
|
||||
mixdata >>= 8;
|
||||
m_AlphaFunc = static_cast<VxMath::VXCMPFUNC>(mixdata & 0xFF);
|
||||
mixdata >>= 8;
|
||||
m_AlphaRef = static_cast<CKBYTE>(mixdata & 0xFF);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// extra texture data
|
||||
if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_MATERIAL::CK_STATESAVE_MATDATA2)) {
|
||||
// read 3 extra texture
|
||||
CKObject* tex = nullptr;
|
||||
|
||||
for (size_t i = 1; i < 4; ++i) {
|
||||
chunk->ReadObjectPointer(tex);
|
||||
if (tex != nullptr && tex->GetClassID() == CK_CLASSID::CKCID_TEXTURE) {
|
||||
m_Textures[i] = static_cast<CKTexture*>(tex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// single effect
|
||||
if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_MATERIAL::CK_STATESAVE_MATDATA3)) {
|
||||
chunk->ReadStruct(m_Effect);
|
||||
}
|
||||
|
||||
// effect with parameter
|
||||
if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_MATERIAL::CK_STATESAVE_MATDATA5)) {
|
||||
// MARK: i do not support CKParameter anymore.
|
||||
// so we downgrade it into single effect type.
|
||||
// hope this convertion will not break anything.
|
||||
|
||||
// drop parameter id.
|
||||
CK_ID paramid;
|
||||
chunk->ReadObjectID(paramid);
|
||||
|
||||
// read effect self
|
||||
chunk->ReadStruct(m_Effect);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma region Data Visitor
|
||||
|
||||
const VxMath::VxColor& CKMaterial::GetDiffuse() const {
|
||||
return m_Diffuse;
|
||||
}
|
||||
void CKMaterial::SetDiffuse(const VxMath::VxColor& col) {
|
||||
m_Diffuse = col;
|
||||
}
|
||||
const VxMath::VxColor& CKMaterial::GetAmbient() const {
|
||||
return m_Ambient;
|
||||
}
|
||||
void CKMaterial::SetAmbient(const VxMath::VxColor& col) {
|
||||
m_Ambient = col;
|
||||
}
|
||||
const VxMath::VxColor& CKMaterial::GetSpecular() const {
|
||||
return m_Specular;
|
||||
}
|
||||
void CKMaterial::SetSpecular(const VxMath::VxColor& col) {
|
||||
m_Specular = col;
|
||||
}
|
||||
const VxMath::VxColor& CKMaterial::GetEmissive() const {
|
||||
return m_Emissive;
|
||||
}
|
||||
void CKMaterial::SetEmissive(const VxMath::VxColor& col) {
|
||||
m_Emissive = col;
|
||||
}
|
||||
CKFLOAT CKMaterial::GetSpecularPower() const {
|
||||
return m_SpecularPower;
|
||||
}
|
||||
void CKMaterial::SetSpecularPower(CKFLOAT val) {
|
||||
m_SpecularPower = val;
|
||||
}
|
||||
|
||||
|
||||
CKTexture* CKMaterial::GetTexture(CKDWORD idx) const {
|
||||
if (idx >= m_Textures.size()) return nullptr;
|
||||
return m_Textures[idx];
|
||||
}
|
||||
void CKMaterial::SetTexture(CKTexture* tex, CKDWORD idx) {
|
||||
if (idx >= m_Textures.size()) return;
|
||||
m_Textures[idx] = tex;
|
||||
}
|
||||
CKDWORD CKMaterial::GetTextureBorderColor() const {
|
||||
return m_TextureBorderColor;
|
||||
}
|
||||
void CKMaterial::SetTextureBorderColor(CKDWORD val) {
|
||||
m_TextureBorderColor = val;
|
||||
}
|
||||
|
||||
|
||||
VxMath::VXTEXTURE_BLENDMODE CKMaterial::GetTextureBlendMode() const {
|
||||
return m_TextureBlendMode;
|
||||
}
|
||||
void CKMaterial::SetTextureBlendMode(VxMath::VXTEXTURE_BLENDMODE val) {
|
||||
m_TextureBlendMode = val;
|
||||
}
|
||||
VxMath::VXTEXTURE_FILTERMODE CKMaterial::GetTextureMinMode() const {
|
||||
return m_TextureMinMode;
|
||||
}
|
||||
void CKMaterial::SetTextureMinMode(VxMath::VXTEXTURE_FILTERMODE val) {
|
||||
m_TextureMinMode = val;
|
||||
}
|
||||
VxMath::VXTEXTURE_FILTERMODE CKMaterial::GetTextureMagMode() const {
|
||||
return m_TextureMagMode;
|
||||
}
|
||||
void CKMaterial::SetTextureMagMode(VxMath::VXTEXTURE_FILTERMODE val) {
|
||||
m_TextureMagMode = val;
|
||||
}
|
||||
VxMath::VXTEXTURE_ADDRESSMODE CKMaterial::GetTextureAddressMode() const {
|
||||
return m_TextureAddressMode;
|
||||
}
|
||||
void CKMaterial::SetTextureAddressMode(VxMath::VXTEXTURE_ADDRESSMODE val) {
|
||||
m_TextureAddressMode = val;
|
||||
}
|
||||
|
||||
|
||||
VxMath::VXBLEND_MODE CKMaterial::GetSourceBlend() const {
|
||||
return m_SourceBlend;
|
||||
}
|
||||
void CKMaterial::SetSourceBlend(VxMath::VXBLEND_MODE val) {
|
||||
m_SourceBlend = val;
|
||||
}
|
||||
VxMath::VXBLEND_MODE CKMaterial::GetDestBlend() const {
|
||||
return m_DestBlend;
|
||||
}
|
||||
void CKMaterial::SetDestBlend(VxMath::VXBLEND_MODE val) {
|
||||
m_DestBlend = val;
|
||||
}
|
||||
VxMath::VXFILL_MODE CKMaterial::GetFillMode() const {
|
||||
return m_FillMode;
|
||||
}
|
||||
void CKMaterial::SetFillMode(VxMath::VXFILL_MODE val) {
|
||||
m_FillMode = val;
|
||||
}
|
||||
VxMath::VXSHADE_MODE CKMaterial::GetShadeMode() const {
|
||||
return m_ShadeMode;
|
||||
}
|
||||
void CKMaterial::SetShadeMode(VxMath::VXSHADE_MODE val) {
|
||||
m_ShadeMode = val;
|
||||
}
|
||||
|
||||
|
||||
bool CKMaterial::GetAlphaTestEnabled() const {
|
||||
return m_EnableAlphaTest;
|
||||
}
|
||||
void CKMaterial::SetAlphaTestEnabled(bool enabled) {
|
||||
m_EnableAlphaTest = enabled;
|
||||
}
|
||||
bool CKMaterial::GetAlphaBlendEnabled() const {
|
||||
return m_EnableAlphaBlend;
|
||||
}
|
||||
void CKMaterial::SetAlphaBlendEnabled(bool enabled) {
|
||||
m_EnableAlphaBlend = enabled;
|
||||
}
|
||||
bool CKMaterial::GetPerspectiveCorrectionEnabled() const {
|
||||
return m_EnablePerspectiveCorrection;
|
||||
}
|
||||
void CKMaterial::SetPerspectiveCorrectionEnabled(bool enabled) {
|
||||
m_EnablePerspectiveCorrection = enabled;
|
||||
}
|
||||
bool CKMaterial::GetZWriteEnabled() const {
|
||||
return m_EnableZWrite;
|
||||
}
|
||||
void CKMaterial::SetZWriteEnabled(bool enabled) {
|
||||
m_EnableZWrite = enabled;
|
||||
}
|
||||
bool CKMaterial::GetTwoSidedEnabled() const {
|
||||
return m_EnableTwoSided;
|
||||
}
|
||||
void CKMaterial::SetTwoSidedEnabled(bool enabled) {
|
||||
m_EnableTwoSided = enabled;
|
||||
}
|
||||
|
||||
|
||||
CKBYTE CKMaterial::GetAlphaRef() const {
|
||||
return m_AlphaRef;
|
||||
}
|
||||
void CKMaterial::SetAlphaRef(CKBYTE val) {
|
||||
m_AlphaRef = val;
|
||||
}
|
||||
VxMath::VXCMPFUNC CKMaterial::GetAlphaFunc() const {
|
||||
return m_AlphaFunc;
|
||||
}
|
||||
void CKMaterial::SetAlphaFunc(VxMath::VXCMPFUNC val) {
|
||||
m_AlphaFunc = val;
|
||||
}
|
||||
VxMath::VXCMPFUNC CKMaterial::GetZFunc() const {
|
||||
return m_ZFunc;
|
||||
}
|
||||
void CKMaterial::SetZFunc(VxMath::VXCMPFUNC val) {
|
||||
m_ZFunc = val;
|
||||
}
|
||||
VxMath::VX_EFFECT CKMaterial::GetEffect() const {
|
||||
return m_Effect;
|
||||
}
|
||||
void CKMaterial::SetEffect(VxMath::VX_EFFECT val) {
|
||||
m_Effect = val;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../VTInternal.hpp"
|
||||
#include "CKBeObject.hpp"
|
||||
#include <array>
|
||||
|
||||
namespace LibCmo::CK2::ObjImpls {
|
||||
|
||||
class CKMaterial : public CKBeObject {
|
||||
public:
|
||||
CKMaterial(CKContext* ctx, CK_ID ckid, CKSTRING name);
|
||||
virtual ~CKMaterial();
|
||||
YYCC_DEL_CLS_COPY_MOVE(CKMaterial);
|
||||
|
||||
virtual CK_CLASSID GetClassID() override {
|
||||
return CK_CLASSID::CKCID_MATERIAL;
|
||||
}
|
||||
|
||||
virtual void CheckPreDeletion() override;
|
||||
|
||||
// 2 RW functions
|
||||
virtual bool Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) override;
|
||||
virtual bool Load(CKStateChunk* chunk, CKFileVisitor* file) override;
|
||||
|
||||
const VxMath::VxColor& GetDiffuse() const;
|
||||
void SetDiffuse(const VxMath::VxColor& col);
|
||||
const VxMath::VxColor& GetAmbient() const;
|
||||
void SetAmbient(const VxMath::VxColor& col);
|
||||
const VxMath::VxColor& GetSpecular() const;
|
||||
void SetSpecular(const VxMath::VxColor& col);
|
||||
const VxMath::VxColor& GetEmissive() const;
|
||||
void SetEmissive(const VxMath::VxColor& col);
|
||||
CKFLOAT GetSpecularPower() const;
|
||||
void SetSpecularPower(CKFLOAT val);
|
||||
|
||||
CKTexture* GetTexture(CKDWORD idx = 0) const;
|
||||
void SetTexture(CKTexture* tex, CKDWORD idx = 0);
|
||||
CKDWORD GetTextureBorderColor() const;
|
||||
void SetTextureBorderColor(CKDWORD val);
|
||||
|
||||
VxMath::VXTEXTURE_BLENDMODE GetTextureBlendMode() const;
|
||||
void SetTextureBlendMode(VxMath::VXTEXTURE_BLENDMODE val);
|
||||
VxMath::VXTEXTURE_FILTERMODE GetTextureMinMode() const;
|
||||
void SetTextureMinMode(VxMath::VXTEXTURE_FILTERMODE val);
|
||||
VxMath::VXTEXTURE_FILTERMODE GetTextureMagMode() const;
|
||||
void SetTextureMagMode(VxMath::VXTEXTURE_FILTERMODE val);
|
||||
VxMath::VXTEXTURE_ADDRESSMODE GetTextureAddressMode() const;
|
||||
void SetTextureAddressMode(VxMath::VXTEXTURE_ADDRESSMODE val);
|
||||
|
||||
VxMath::VXBLEND_MODE GetSourceBlend() const;
|
||||
void SetSourceBlend(VxMath::VXBLEND_MODE val);
|
||||
VxMath::VXBLEND_MODE GetDestBlend() const;
|
||||
void SetDestBlend(VxMath::VXBLEND_MODE val);
|
||||
VxMath::VXFILL_MODE GetFillMode() const;
|
||||
void SetFillMode(VxMath::VXFILL_MODE val);
|
||||
VxMath::VXSHADE_MODE GetShadeMode() const;
|
||||
void SetShadeMode(VxMath::VXSHADE_MODE val);
|
||||
|
||||
bool GetAlphaTestEnabled() const;
|
||||
void SetAlphaTestEnabled(bool enabled);
|
||||
bool GetAlphaBlendEnabled() const;
|
||||
void SetAlphaBlendEnabled(bool enabled);
|
||||
bool GetPerspectiveCorrectionEnabled() const;
|
||||
void SetPerspectiveCorrectionEnabled(bool enabled);
|
||||
bool GetZWriteEnabled() const;
|
||||
void SetZWriteEnabled(bool enabled);
|
||||
bool GetTwoSidedEnabled() const;
|
||||
void SetTwoSidedEnabled(bool enabled);
|
||||
|
||||
CKBYTE GetAlphaRef() const;
|
||||
void SetAlphaRef(CKBYTE val);
|
||||
VxMath::VXCMPFUNC GetAlphaFunc() const;
|
||||
void SetAlphaFunc(VxMath::VXCMPFUNC val);
|
||||
VxMath::VXCMPFUNC GetZFunc() const;
|
||||
void SetZFunc(VxMath::VXCMPFUNC val);
|
||||
VxMath::VX_EFFECT GetEffect() const;
|
||||
void SetEffect(VxMath::VX_EFFECT val);
|
||||
|
||||
protected:
|
||||
VxMath::VxColor m_Diffuse;
|
||||
VxMath::VxColor m_Ambient;
|
||||
VxMath::VxColor m_Specular;
|
||||
VxMath::VxColor m_Emissive;
|
||||
CKFLOAT m_SpecularPower;
|
||||
|
||||
std::array<CKTexture*, 4> m_Textures;
|
||||
CKDWORD m_TextureBorderColor;
|
||||
|
||||
VxMath::VXTEXTURE_BLENDMODE m_TextureBlendMode;
|
||||
VxMath::VXTEXTURE_FILTERMODE m_TextureMinMode;
|
||||
VxMath::VXTEXTURE_FILTERMODE m_TextureMagMode;
|
||||
VxMath::VXTEXTURE_ADDRESSMODE m_TextureAddressMode;
|
||||
|
||||
VxMath::VXBLEND_MODE m_SourceBlend;
|
||||
VxMath::VXBLEND_MODE m_DestBlend;
|
||||
VxMath::VXFILL_MODE m_FillMode;
|
||||
VxMath::VXSHADE_MODE m_ShadeMode;
|
||||
|
||||
bool m_EnableAlphaTest;
|
||||
bool m_EnableAlphaBlend;
|
||||
bool m_EnablePerspectiveCorrection;
|
||||
bool m_EnableZWrite;
|
||||
bool m_EnableTwoSided;
|
||||
|
||||
CKBYTE m_AlphaRef;
|
||||
VxMath::VXCMPFUNC m_AlphaFunc;
|
||||
VxMath::VXCMPFUNC m_ZFunc;
|
||||
VxMath::VX_EFFECT m_Effect;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@@ -1,743 +0,0 @@
|
||||
#include "CKMesh.hpp"
|
||||
#include "../CKStateChunk.hpp"
|
||||
#include "../CKContext.hpp"
|
||||
#include "CKMaterial.hpp"
|
||||
|
||||
namespace LibCmo::CK2::ObjImpls {
|
||||
|
||||
CKMesh::CKMesh(CKContext* ctx, CK_ID ckid, CKSTRING name) :
|
||||
CKBeObject(ctx, ckid, name),
|
||||
// init vertex
|
||||
m_VertexCount(0),
|
||||
m_VertexPosition(), m_VertexNormal(), m_VertexUV(),
|
||||
m_VertexColor(), m_VertexSpecularColor(),
|
||||
// init mtl slots
|
||||
m_MaterialSlotCount(0),
|
||||
m_MaterialSlot(),
|
||||
// init face data
|
||||
m_FaceCount(0),
|
||||
m_FaceIndices(), m_FaceMtlIndex(), m_FaceOthers(),
|
||||
// init line
|
||||
m_LineCount(0),
|
||||
m_LineIndices(),
|
||||
// init flags
|
||||
m_Flags(YYCC::EnumHelper::Merge(
|
||||
VxMath::VXMESH_FLAGS::VXMESH_VISIBLE,
|
||||
VxMath::VXMESH_FLAGS::VXMESH_RENDERCHANNELS
|
||||
)) {
|
||||
// set visible in default
|
||||
YYCC::EnumHelper::Add(m_ObjectFlags, CK_OBJECT_FLAGS::CK_OBJECT_VISIBLE);
|
||||
}
|
||||
|
||||
CKMesh::~CKMesh() {}
|
||||
|
||||
void CKMesh::CheckPreDeletion() {
|
||||
CKBeObject::CheckPreDeletion();
|
||||
|
||||
// check material slots
|
||||
for (auto& slot : m_MaterialSlot) {
|
||||
if (slot != nullptr && slot->IsToBeDeleted()) {
|
||||
slot = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CKMesh::Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) {
|
||||
bool suc = CKBeObject::Save(chunk, file, flags);
|
||||
if (!suc) return false;
|
||||
|
||||
// write mesh flags
|
||||
{
|
||||
chunk->WriteIdentifier(CK_STATESAVEFLAGS_MESH::CK_STATESAVE_MESHFLAGS);
|
||||
chunk->WriteStruct(m_Flags);
|
||||
}
|
||||
|
||||
// write material slots
|
||||
// MARK: due to virtools shit implement, we must make sure there is at least one material channel existed.
|
||||
// so if the material slot is empty, we write a mullptr slot for it.
|
||||
{
|
||||
chunk->WriteIdentifier(CK_STATESAVEFLAGS_MESH::CK_STATESAVE_MESHMATERIALS);
|
||||
|
||||
if (GetMaterialSlotCount() != 0) {
|
||||
// write real slots
|
||||
chunk->WriteStruct(GetMaterialSlotCount());
|
||||
for (auto& mtlSlot : m_MaterialSlot) {
|
||||
// write object id
|
||||
chunk->WriteObjectPointer(mtlSlot);
|
||||
// MARK: write a zero? idk what the fuck it is.
|
||||
chunk->WriteStruct(static_cast<CKDWORD>(0));
|
||||
}
|
||||
} else {
|
||||
// write fake one like real one
|
||||
chunk->WriteStruct(static_cast<CKDWORD>(1));
|
||||
// write id and blank
|
||||
chunk->WriteObjectPointer(nullptr);
|
||||
chunk->WriteStruct(static_cast<CKDWORD>(0));
|
||||
}
|
||||
}
|
||||
|
||||
// write face data
|
||||
if (GetFaceCount() != 0) {
|
||||
CKDWORD faceCount = GetFaceCount();
|
||||
chunk->WriteIdentifier(CK_STATESAVEFLAGS_MESH::CK_STATESAVE_MESHFACES);
|
||||
chunk->WriteStruct(faceCount);
|
||||
|
||||
// write compressed data, see Read for more info about this struct
|
||||
// lock buffer first
|
||||
auto buf = chunk->LockWriteBufferWrapper(faceCount * CKSizeof(CKDWORD) * 2);
|
||||
CKWORD* rawbuf = static_cast<CKWORD*>(buf.get());
|
||||
|
||||
// copy indice
|
||||
VxMath::VxCopyStructure(
|
||||
faceCount,
|
||||
rawbuf,
|
||||
2 * CKSizeof(CKDWORD),
|
||||
3 * CKSizeof(CKWORD),
|
||||
m_FaceIndices.data(),
|
||||
3 * CKSizeof(CKWORD)
|
||||
);
|
||||
// copy mtl index
|
||||
VxMath::VxCopyStructure(
|
||||
faceCount,
|
||||
rawbuf + 3,
|
||||
2 * CKSizeof(CKDWORD),
|
||||
CKSizeof(CKWORD),
|
||||
m_FaceMtlIndex.data(),
|
||||
CKSizeof(CKWORD)
|
||||
);
|
||||
|
||||
// free buf
|
||||
buf.reset();
|
||||
}
|
||||
|
||||
// write line data
|
||||
if (GetLineCount() != 0) {
|
||||
CKDWORD lineCount = GetLineCount();
|
||||
chunk->WriteIdentifier(CK_STATESAVEFLAGS_MESH::CK_STATESAVE_MESHLINES);
|
||||
chunk->WriteStruct(lineCount);
|
||||
|
||||
chunk->WriteBuffer(m_LineIndices.data(), CKSizeof(CKWORD) * 2 * lineCount);
|
||||
}
|
||||
|
||||
// write vertex data
|
||||
if (GetVertexCount() != 0) {
|
||||
CKDWORD vtxCount = GetVertexCount();
|
||||
chunk->WriteIdentifier(CK_STATESAVEFLAGS_MESH::CK_STATESAVE_MESHVERTICES);
|
||||
chunk->WriteStruct(vtxCount);
|
||||
|
||||
// construct vertex save flags
|
||||
// and save it
|
||||
VertexSaveFlags saveflags = GenerateSaveFlags();
|
||||
chunk->WriteStruct(saveflags);
|
||||
|
||||
// reserve enough space for full data written, but we can specify the real consumed size later
|
||||
// we also need calc the consumed size when writing file
|
||||
auto buf = chunk->LockWriteBufferWrapper((
|
||||
CKSizeof(VxMath::VxVector3) + // vertex position
|
||||
CKSizeof(CKDWORD) + CKSizeof(CKDWORD) + // color and specular color
|
||||
CKSizeof(VxMath::VxVector3) + // vertex normal
|
||||
CKSizeof(VxMath::VxVector2) // vertex uv
|
||||
) * vtxCount); // mul vertex count
|
||||
CKBYTE* rawbuf = static_cast<CKBYTE*>(buf.get());
|
||||
|
||||
// reserve length data
|
||||
CKDWORD* reservedBufDwordSize = reinterpret_cast<CKDWORD*>(rawbuf);
|
||||
rawbuf += CKSizeof(CKDWORD);
|
||||
|
||||
// write vertex position
|
||||
if (!YYCC::EnumHelper::Has(saveflags, VertexSaveFlags::NoPos)) {
|
||||
CKDWORD consumed = CKSizeof(VxMath::VxVector3) * vtxCount;
|
||||
std::memcpy(rawbuf, m_VertexPosition.data(), consumed);
|
||||
rawbuf += consumed;
|
||||
}
|
||||
|
||||
// write color and specular color
|
||||
{
|
||||
CKDWORD consumed = 0;
|
||||
if (!YYCC::EnumHelper::Has(saveflags, VertexSaveFlags::SingleColor)) {
|
||||
consumed = CKSizeof(CKDWORD) * vtxCount;
|
||||
} else {
|
||||
consumed = CKSizeof(CKDWORD);
|
||||
}
|
||||
|
||||
std::memcpy(rawbuf, m_VertexColor.data(), consumed);
|
||||
rawbuf += consumed;
|
||||
}
|
||||
{
|
||||
CKDWORD consumed = 0;
|
||||
if (!YYCC::EnumHelper::Has(saveflags, VertexSaveFlags::SingleSpecularColor)) {
|
||||
consumed = CKSizeof(CKDWORD) * vtxCount;
|
||||
} else {
|
||||
consumed = CKSizeof(CKDWORD);
|
||||
}
|
||||
|
||||
std::memcpy(rawbuf, m_VertexSpecularColor.data(), consumed);
|
||||
rawbuf += consumed;
|
||||
}
|
||||
|
||||
// write normal
|
||||
if (!YYCC::EnumHelper::Has(saveflags, VertexSaveFlags::NoNormal)) {
|
||||
CKDWORD consumed = CKSizeof(VxMath::VxVector3) * vtxCount;
|
||||
std::memcpy(rawbuf, m_VertexNormal.data(), consumed);
|
||||
rawbuf += consumed;
|
||||
}
|
||||
|
||||
// write uv
|
||||
{
|
||||
CKDWORD consumed = 0;
|
||||
if (!YYCC::EnumHelper::Has(saveflags, VertexSaveFlags::SingleUV)) {
|
||||
consumed = CKSizeof(VxMath::VxVector2) * vtxCount;
|
||||
} else {
|
||||
consumed = CKSizeof(VxMath::VxVector2);
|
||||
}
|
||||
|
||||
std::memcpy(rawbuf, m_VertexUV.data(), consumed);
|
||||
rawbuf += consumed;
|
||||
}
|
||||
|
||||
// calc real consumed size
|
||||
CKDWORD realConsumedSize = static_cast<CKDWORD>(rawbuf - static_cast<CKBYTE*>(buf.get()));
|
||||
// assign to reserved length field
|
||||
// length also include length indicator it self
|
||||
*reservedBufDwordSize = realConsumedSize / CKSizeof(CKDWORD);
|
||||
// notify buffer real consumed size
|
||||
buf.get_deleter().SetConsumedSize(realConsumedSize);
|
||||
|
||||
// free buffer
|
||||
buf.reset();
|
||||
|
||||
}
|
||||
|
||||
chunk->SetClassId(CK_CLASSID::CKCID_MESH);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CKMesh::Load(CKStateChunk* chunk, CKFileVisitor* file) {
|
||||
bool suc = CKBeObject::Load(chunk, file);
|
||||
if (!suc) return false;
|
||||
|
||||
// clear all data
|
||||
CleanMesh();
|
||||
|
||||
// check data version.
|
||||
// MARK: too low data is not supported.
|
||||
// because my work are not related to them
|
||||
if (chunk->GetDataVersion() < CK_STATECHUNK_DATAVERSION::CHUNK_MESHCHANGE_VERSION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// read flag
|
||||
if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_MESH::CK_STATESAVE_MESHFLAGS)) {
|
||||
chunk->ReadStruct(m_Flags);
|
||||
YYCC::EnumHelper::Mask(m_Flags, VxMath::VXMESH_FLAGS::VXMESH_ALLFLAGS);
|
||||
|
||||
// I don't know why, just interpter the IDA code.
|
||||
YYCC::EnumHelper::Remove(m_Flags,
|
||||
VxMath::VXMESH_FLAGS::VXMESH_BOUNDINGUPTODATE,
|
||||
VxMath::VXMESH_FLAGS::VXMESH_OPTIMIZED
|
||||
);
|
||||
}
|
||||
|
||||
// read material slots
|
||||
if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_MESH::CK_STATESAVE_MESHMATERIALS)) {
|
||||
// get and set material count
|
||||
CKDWORD mtlCount;
|
||||
chunk->ReadStruct(mtlCount);
|
||||
SetMaterialSlotCount(mtlCount);
|
||||
|
||||
// read slot
|
||||
CKDWORD ph;
|
||||
CKObject* objptr = nullptr;
|
||||
for (auto& mtlSlot : m_MaterialSlot) {
|
||||
// read id
|
||||
chunk->ReadObjectPointer(objptr);
|
||||
// and read a place holder idk what the fuck it is.
|
||||
chunk->ReadStruct(ph);
|
||||
|
||||
// try to assign
|
||||
if (objptr != nullptr && objptr->GetClassID() == CK_CLASSID::CKCID_MATERIAL) {
|
||||
mtlSlot = static_cast<CKMaterial*>(objptr);
|
||||
} else {
|
||||
mtlSlot = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// read vertex data
|
||||
VertexSaveFlags saveflags = VertexSaveFlags::None;
|
||||
if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_MESH::CK_STATESAVE_MESHVERTICES)) {
|
||||
// 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 (including it self)
|
||||
CKDWORD sizeInDword;
|
||||
chunk->ReadStruct(sizeInDword);
|
||||
--sizeInDword; // remove self.
|
||||
|
||||
// lock read buffer
|
||||
auto buf = chunk->LockReadBufferWrapper(sizeInDword * CKSizeof(CKDWORD));
|
||||
const CKBYTE* rawbuf = static_cast<const CKBYTE*>(buf.get());
|
||||
|
||||
// copy position if it have
|
||||
if (!YYCC::EnumHelper::Has(saveflags, VertexSaveFlags::NoPos)) {
|
||||
CKDWORD consumed = CKSizeof(VxMath::VxVector3) * vertexCount;
|
||||
std::memcpy(m_VertexPosition.data(), rawbuf, consumed);
|
||||
rawbuf += consumed;
|
||||
}
|
||||
|
||||
// copy color or apply single color
|
||||
if (!YYCC::EnumHelper::Has(saveflags, VertexSaveFlags::SingleColor)) {
|
||||
CKDWORD consumed = CKSizeof(CKDWORD) * vertexCount;
|
||||
std::memcpy(m_VertexColor.data(), rawbuf, consumed);
|
||||
rawbuf += consumed;
|
||||
} else {
|
||||
VxMath::VxCopyStructure(
|
||||
vertexCount,
|
||||
m_VertexColor.data(),
|
||||
CKSizeof(CKDWORD),
|
||||
CKSizeof(CKDWORD),
|
||||
rawbuf,
|
||||
0 // InStride = 0 to make sure copy this single value to every elements.
|
||||
);
|
||||
rawbuf += CKSizeof(CKDWORD);
|
||||
}
|
||||
|
||||
// copy specular color or apply a single color
|
||||
if (!YYCC::EnumHelper::Has(saveflags, VertexSaveFlags::SingleSpecularColor)) {
|
||||
CKDWORD consumed = CKSizeof(CKDWORD) * vertexCount;
|
||||
std::memcpy(m_VertexSpecularColor.data(), rawbuf, consumed);
|
||||
rawbuf += consumed;
|
||||
} else {
|
||||
VxMath::VxCopyStructure(
|
||||
vertexCount,
|
||||
m_VertexSpecularColor.data(),
|
||||
CKSizeof(CKDWORD),
|
||||
CKSizeof(CKDWORD),
|
||||
rawbuf,
|
||||
0 // InStride = 0 to make sure copy this single value to every elements.
|
||||
);
|
||||
rawbuf += CKSizeof(CKDWORD);
|
||||
}
|
||||
|
||||
// copy normals if it has
|
||||
if (!YYCC::EnumHelper::Has(saveflags, VertexSaveFlags::NoNormal)) {
|
||||
CKDWORD consumed = CKSizeof(VxMath::VxVector3) * vertexCount;
|
||||
std::memcpy(m_VertexNormal.data(), rawbuf, consumed);
|
||||
rawbuf += consumed;
|
||||
}
|
||||
|
||||
// copy uv or apply single uv
|
||||
if (!YYCC::EnumHelper::Has(saveflags, VertexSaveFlags::SingleUV)) {
|
||||
CKDWORD consumed = CKSizeof(VxMath::VxVector2) * vertexCount;
|
||||
std::memcpy(m_VertexUV.data(), rawbuf, consumed);
|
||||
rawbuf += consumed;
|
||||
} else {
|
||||
VxMath::VxCopyStructure(
|
||||
vertexCount,
|
||||
m_VertexUV.data(),
|
||||
CKSizeof(VxMath::VxVector2),
|
||||
CKSizeof(VxMath::VxVector2),
|
||||
rawbuf,
|
||||
0 // InStride = 0 to make sure copy this single value to every elements.
|
||||
);
|
||||
rawbuf += CKSizeof(VxMath::VxVector2);
|
||||
}
|
||||
|
||||
// free buf
|
||||
buf.reset();
|
||||
}
|
||||
}
|
||||
|
||||
// read face data
|
||||
if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_MESH::CK_STATESAVE_MESHFACES)) {
|
||||
// read face count and set
|
||||
CKDWORD faceCount;
|
||||
chunk->ReadStruct(faceCount);
|
||||
SetFaceCount(faceCount);
|
||||
|
||||
// lock buffer
|
||||
auto buf = chunk->LockReadBufferWrapper(faceCount * CKSizeof(CKDWORD) * 2);
|
||||
const CKWORD* rawbuf = static_cast<const CKWORD*>(buf.get());
|
||||
// each face use 2 CKDWORD to describe
|
||||
// first CKDWORD describe first 2 face vertex indices
|
||||
// HIGH >>> 0xFFFF(indice 1) 0xFFFF(indice 0) <<< LOW
|
||||
// second CKDWORD describe the third indices and used material slot index
|
||||
// HIGH >>> 0xFFFF(mtl slot index) 0xFFFF(indice 2) <<< LOW
|
||||
|
||||
// due to little endian, the data listed before are placed in memory like this:
|
||||
// (indice 0) (indice 1) (indice 2) (mtl idx)
|
||||
|
||||
// copy indice
|
||||
VxMath::VxCopyStructure(
|
||||
faceCount,
|
||||
m_FaceIndices.data(),
|
||||
3 * CKSizeof(CKWORD),
|
||||
3 * CKSizeof(CKWORD),
|
||||
rawbuf,
|
||||
2 * CKSizeof(CKDWORD)
|
||||
);
|
||||
// copy mtl index
|
||||
VxMath::VxCopyStructure(
|
||||
faceCount,
|
||||
m_FaceMtlIndex.data(),
|
||||
CKSizeof(CKWORD),
|
||||
CKSizeof(CKWORD),
|
||||
rawbuf + 3,
|
||||
2 * CKSizeof(CKDWORD)
|
||||
);
|
||||
|
||||
// free buf
|
||||
buf.reset();
|
||||
}
|
||||
|
||||
// read line data
|
||||
if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_MESH::CK_STATESAVE_MESHLINES)) {
|
||||
// read and set line count;
|
||||
CKDWORD lineCount;
|
||||
chunk->ReadStruct(lineCount);
|
||||
SetLineCount(lineCount);
|
||||
|
||||
chunk->ReadAndFillBuffer(m_LineIndices.data());
|
||||
}
|
||||
|
||||
// build normals
|
||||
if (YYCC::EnumHelper::Has(saveflags, VertexSaveFlags::NoNormal)) {
|
||||
BuildNormals();
|
||||
} else {
|
||||
BuildFaceNormals();
|
||||
}
|
||||
|
||||
// MARK: material channels, vertex weight, face mask added originally
|
||||
// but removed at Oct 1st, 2023 because I will not use them and I couldn't test them.
|
||||
|
||||
// MARK: progressive mesh data is dropper.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CKMesh::Show(CK_OBJECT_SHOWOPTION show) {
|
||||
CKObject::Show(show);
|
||||
|
||||
if (show == CK_OBJECT_SHOWOPTION::CKSHOW) {
|
||||
YYCC::EnumHelper::Add(m_Flags, VxMath::VXMESH_FLAGS::VXMESH_VISIBLE);
|
||||
} else {
|
||||
YYCC::EnumHelper::Remove(m_Flags, VxMath::VXMESH_FLAGS::VXMESH_VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma region Misc Section
|
||||
|
||||
void CKMesh::CleanMesh() {
|
||||
SetVertexCount(0);
|
||||
SetMaterialSlotCount(0);
|
||||
SetFaceCount(0);
|
||||
SetLineCount(0);
|
||||
}
|
||||
|
||||
VxMath::VXMESH_FLAGS CKMesh::GetMeshFlags() const {
|
||||
return m_Flags;
|
||||
}
|
||||
|
||||
void CKMesh::SetMeshFlags(VxMath::VXMESH_FLAGS flags) {
|
||||
// set value
|
||||
m_Flags = flags;
|
||||
|
||||
// sync visibility to CKObject layer.
|
||||
if (YYCC::EnumHelper::Has(m_Flags, VxMath::VXMESH_FLAGS::VXMESH_VISIBLE)) {
|
||||
YYCC::EnumHelper::Add(m_ObjectFlags, CK_OBJECT_FLAGS::CK_OBJECT_VISIBLE);
|
||||
} else {
|
||||
YYCC::EnumHelper::Remove(m_ObjectFlags, CK_OBJECT_FLAGS::CK_OBJECT_VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
VxMath::VXMESH_LITMODE CKMesh::GetLitMode() const {
|
||||
if (YYCC::EnumHelper::Has(m_Flags, VxMath::VXMESH_FLAGS::VXMESH_PRELITMODE)) {
|
||||
return VxMath::VXMESH_LITMODE::VX_PRELITMESH;
|
||||
} else {
|
||||
return VxMath::VXMESH_LITMODE::VX_LITMESH;
|
||||
}
|
||||
}
|
||||
|
||||
void CKMesh::SetLitMode(VxMath::VXMESH_LITMODE mode) {
|
||||
switch (mode) {
|
||||
case VxMath::VXMESH_LITMODE::VX_PRELITMESH:
|
||||
YYCC::EnumHelper::Add(m_Flags, VxMath::VXMESH_FLAGS::VXMESH_PRELITMODE);
|
||||
break;
|
||||
case VxMath::VXMESH_LITMODE::VX_LITMESH:
|
||||
YYCC::EnumHelper::Remove(m_Flags, VxMath::VXMESH_FLAGS::VXMESH_PRELITMODE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
VxMath::VXTEXTURE_WRAPMODE CKMesh::GetWrapMode() const {
|
||||
VxMath::VXTEXTURE_WRAPMODE ret = VxMath::VXTEXTURE_WRAPMODE::VXTEXTUREWRAP_NONE;
|
||||
|
||||
if (YYCC::EnumHelper::Has(m_Flags, VxMath::VXMESH_FLAGS::VXMESH_WRAPU)) {
|
||||
YYCC::EnumHelper::Add(ret, VxMath::VXTEXTURE_WRAPMODE::VXTEXTUREWRAP_U);
|
||||
}
|
||||
if (YYCC::EnumHelper::Has(m_Flags, VxMath::VXMESH_FLAGS::VXMESH_WRAPV)) {
|
||||
YYCC::EnumHelper::Add(ret, VxMath::VXTEXTURE_WRAPMODE::VXTEXTUREWRAP_V);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CKMesh::SetWrapMode(VxMath::VXTEXTURE_WRAPMODE mode) {
|
||||
if (YYCC::EnumHelper::Has(mode, VxMath::VXTEXTURE_WRAPMODE::VXTEXTUREWRAP_U)) {
|
||||
YYCC::EnumHelper::Add(m_Flags, VxMath::VXMESH_FLAGS::VXMESH_WRAPU);
|
||||
} else {
|
||||
YYCC::EnumHelper::Remove(m_Flags, VxMath::VXMESH_FLAGS::VXMESH_WRAPU);
|
||||
}
|
||||
|
||||
if (YYCC::EnumHelper::Has(mode, VxMath::VXTEXTURE_WRAPMODE::VXTEXTUREWRAP_V)) {
|
||||
YYCC::EnumHelper::Add(m_Flags, VxMath::VXMESH_FLAGS::VXMESH_WRAPV);
|
||||
} else {
|
||||
YYCC::EnumHelper::Remove(m_Flags, VxMath::VXMESH_FLAGS::VXMESH_WRAPV);
|
||||
}
|
||||
}
|
||||
|
||||
CKMesh::VertexSaveFlags CKMesh::GenerateSaveFlags() {
|
||||
// set to initial status
|
||||
VertexSaveFlags saveflags = YYCC::EnumHelper::Merge(
|
||||
VertexSaveFlags::SingleColor,
|
||||
VertexSaveFlags::SingleSpecularColor,
|
||||
VertexSaveFlags::NoNormal,
|
||||
VertexSaveFlags::SingleUV
|
||||
);
|
||||
|
||||
// check no pos
|
||||
// if position is generated, skip saving position
|
||||
if (YYCC::EnumHelper::Has(m_Flags, VxMath::VXMESH_FLAGS::VXMESH_PROCEDURALPOS)) {
|
||||
YYCC::EnumHelper::Add(saveflags, VertexSaveFlags::NoPos);
|
||||
}
|
||||
|
||||
// check uv
|
||||
// if uv is not generated and all uv are not the same value, remove single uv
|
||||
if (!YYCC::EnumHelper::Has(m_Flags, VxMath::VXMESH_FLAGS::VXMESH_PROCEDURALUV)) {
|
||||
for (const auto& uv : m_VertexUV) {
|
||||
if (uv != m_VertexUV.front()) {
|
||||
YYCC::EnumHelper::Remove(saveflags, VertexSaveFlags::SingleUV);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check color and specular color
|
||||
// if all color are not the same value, remove single color
|
||||
for (const auto& col : m_VertexColor) {
|
||||
if (col != m_VertexColor.front()) {
|
||||
YYCC::EnumHelper::Remove(saveflags, VertexSaveFlags::SingleColor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (const auto& col : m_VertexSpecularColor) {
|
||||
if (col != m_VertexSpecularColor.front()) {
|
||||
YYCC::EnumHelper::Remove(saveflags, VertexSaveFlags::SingleSpecularColor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if normal not changed, and position is not generated, we should consider whether we need save normal (step into if)
|
||||
if (!YYCC::EnumHelper::Has(m_Flags, VxMath::VXMESH_FLAGS::VXMESH_NORMAL_CHANGED, VxMath::VXMESH_FLAGS::VXMESH_PROCEDURALPOS)) {
|
||||
// MARK: we should build face normal first
|
||||
// then we build vertex normal like BuildNormals.
|
||||
// then, we compare the difference between the generated normals and user specified normals, by simply using operator- (userNml - generatedNml) and abs the result.
|
||||
// then we accumulate these difference, by simply adding them together.
|
||||
// then we div the accumulation by the count of vertex, we got a normalized accumulated difference.
|
||||
// we compare its length with 0.001. if is length is lower than 0.001, it prove that the difference is enough small and we can skip normal save.
|
||||
// othersize we should save normal one by one.
|
||||
|
||||
BuildFaceNormals();
|
||||
|
||||
// init generated nml list first
|
||||
XContainer::XArray<VxMath::VxVector3> generated(m_VertexCount, VxMath::VxVector3());
|
||||
// and accumulated for each normal
|
||||
for (CKDWORD fid = 0; fid < m_FaceCount; ++fid) {
|
||||
generated[m_FaceIndices[fid * 3]] += m_FaceOthers[fid].m_Normal;
|
||||
generated[m_FaceIndices[fid * 3 + 1]] += m_FaceOthers[fid].m_Normal;
|
||||
generated[m_FaceIndices[fid * 3 + 2]] += m_FaceOthers[fid].m_Normal;
|
||||
}
|
||||
|
||||
// init accumulated difference vector first
|
||||
VxMath::VxVector3 accnml;
|
||||
// accumulate difference
|
||||
for (CKDWORD vid = 0; vid < m_VertexCount; ++vid) {
|
||||
// normalize generated normal first
|
||||
generated[vid].Normalized();
|
||||
// get diff by distance
|
||||
VxMath::VxVector3 diff = m_VertexNormal[vid] - generated[vid];
|
||||
// abs the diff and add into accumulated diff
|
||||
VxMath::NSVxVector::Absolute(diff);
|
||||
accnml += diff;
|
||||
}
|
||||
|
||||
// div by vertex count and compare its length
|
||||
accnml /= static_cast<CKFLOAT>(m_VertexCount);
|
||||
if (accnml.Length() > 0.001f) {
|
||||
// too large difference, we need save normal
|
||||
YYCC::EnumHelper::Remove(saveflags, VertexSaveFlags::NoNormal);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return saveflags;
|
||||
}
|
||||
|
||||
void CKMesh::BuildNormals() {
|
||||
if (m_FaceCount == 0 || m_VertexCount == 0) return;
|
||||
|
||||
// build face normal first
|
||||
BuildFaceNormals();
|
||||
|
||||
// iterate all face and add face normal to each point's normal
|
||||
for (CKDWORD fid = 0; fid < m_FaceCount; ++fid) {
|
||||
m_VertexNormal[m_FaceIndices[fid * 3]] += m_FaceOthers[fid].m_Normal;
|
||||
m_VertexNormal[m_FaceIndices[fid * 3 + 1]] += m_FaceOthers[fid].m_Normal;
|
||||
m_VertexNormal[m_FaceIndices[fid * 3 + 2]] += m_FaceOthers[fid].m_Normal;
|
||||
}
|
||||
|
||||
// then normalize all vertex normal
|
||||
for (auto& nml : m_VertexNormal) {
|
||||
nml.Normalized();
|
||||
}
|
||||
}
|
||||
|
||||
void CKMesh::BuildFaceNormals() {
|
||||
if (m_FaceCount == 0 || m_VertexCount == 0) return;
|
||||
|
||||
// iertate all face to build face normal according to position data
|
||||
for (CKDWORD fid = 0; fid < m_FaceCount; ++fid) {
|
||||
VxMath::VxVector3 *p0 = &m_VertexPosition[m_FaceIndices[fid * 3]];
|
||||
|
||||
VxMath::VxVector3 p0_p1 = m_VertexPosition[m_FaceIndices[fid * 3 + 1]] - *p0,
|
||||
p0_p2 = m_VertexPosition[m_FaceIndices[fid * 3 + 2]] - *p0;
|
||||
|
||||
// cross product to get normal
|
||||
// and normalize it
|
||||
VxMath::VxVector3 nml = VxMath::NSVxVector::CrossProduct(p0_p1, p0_p2);
|
||||
nml.Normalized();
|
||||
|
||||
// assign it
|
||||
m_FaceOthers[fid].m_Normal = nml;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Vertex Section
|
||||
|
||||
CKDWORD CKMesh::GetVertexCount() const {
|
||||
return m_VertexCount;
|
||||
}
|
||||
|
||||
void CKMesh::SetVertexCount(CKDWORD count) {
|
||||
m_VertexCount = count;
|
||||
m_VertexPosition.resize(count);
|
||||
m_VertexNormal.resize(count);
|
||||
m_VertexUV.resize(count);
|
||||
m_VertexColor.resize(count, 0xFFFFFFFF);
|
||||
m_VertexSpecularColor.resize(count, 0x00000000);
|
||||
}
|
||||
|
||||
VxMath::VxVector3* CKMesh::GetVertexPositions() {
|
||||
if (m_VertexCount == 0) return nullptr;
|
||||
return m_VertexPosition.data();
|
||||
}
|
||||
|
||||
VxMath::VxVector3* CKMesh::GetVertexNormals() {
|
||||
if (m_VertexCount == 0) return nullptr;
|
||||
return m_VertexNormal.data();
|
||||
}
|
||||
|
||||
VxMath::VxVector2* CKMesh::GetVertexUVs() {
|
||||
if (m_VertexCount == 0) return nullptr;
|
||||
return m_VertexUV.data();
|
||||
}
|
||||
|
||||
CKDWORD* CKMesh::GetVertexColors() {
|
||||
if (m_VertexCount == 0) return nullptr;
|
||||
return m_VertexColor.data();
|
||||
}
|
||||
|
||||
CKDWORD* CKMesh::GetVertexSpecularColors() {
|
||||
if (m_VertexCount == 0) return nullptr;
|
||||
return m_VertexSpecularColor.data();
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Material Slot Section
|
||||
|
||||
CKDWORD CKMesh::GetMaterialSlotCount() const {
|
||||
return m_MaterialSlotCount;
|
||||
}
|
||||
|
||||
void CKMesh::SetMaterialSlotCount(CKDWORD count) {
|
||||
m_MaterialSlotCount = count;
|
||||
m_MaterialSlot.resize(count, nullptr);
|
||||
}
|
||||
|
||||
CKMaterial** CKMesh::GetMaterialSlots() {
|
||||
if (m_MaterialSlotCount == 0) return nullptr;
|
||||
return m_MaterialSlot.data();
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Face Section
|
||||
|
||||
CKDWORD CKMesh::GetFaceCount() const {
|
||||
return m_FaceCount;
|
||||
}
|
||||
|
||||
void CKMesh::SetFaceCount(CKDWORD count) {
|
||||
m_FaceCount = count;
|
||||
m_FaceIndices.resize(count * 3, 0);
|
||||
m_FaceMtlIndex.resize(count, 0);
|
||||
m_FaceOthers.resize(count);
|
||||
}
|
||||
|
||||
CKWORD* CKMesh::GetFaceIndices() {
|
||||
if (m_FaceCount == 0) return nullptr;
|
||||
return m_FaceIndices.data();
|
||||
}
|
||||
|
||||
CKWORD* CKMesh::GetFaceMaterialSlotIndexs() {
|
||||
if (m_FaceCount == 0) return nullptr;
|
||||
return m_FaceMtlIndex.data();
|
||||
}
|
||||
|
||||
VxMath::VxVector3* CKMesh::GetFaceNormals(CKDWORD& stride) {
|
||||
stride = CKSizeof(FaceData_t);
|
||||
|
||||
if (m_FaceCount == 0) return nullptr;
|
||||
return &m_FaceOthers.data()->m_Normal;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Line Section
|
||||
|
||||
CKDWORD CKMesh::GetLineCount() const {
|
||||
return m_LineCount;
|
||||
}
|
||||
|
||||
void CKMesh::SetLineCount(CKDWORD count) {
|
||||
m_LineCount = count;
|
||||
m_LineIndices.resize(count * 2, 0);
|
||||
}
|
||||
|
||||
CKWORD* CKMesh::GetLineIndices() {
|
||||
if (m_LineCount == 0) return nullptr;
|
||||
return m_LineIndices.data();
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../VTInternal.hpp"
|
||||
#include "CKBeObject.hpp"
|
||||
|
||||
namespace LibCmo::CK2::ObjImpls {
|
||||
|
||||
class CKMesh : public CKBeObject {
|
||||
protected:
|
||||
enum class VertexSaveFlags : CKDWORD {
|
||||
None = 0,
|
||||
SingleColor = 0x1u, /**< if not set, the VertexColor is a list, otherwise a single global CKDWORD.*/
|
||||
SingleSpecularColor = 0x2u, /**< if not set, the VertexSpecularColor is a list, otherwise a single global CKDWORD. */
|
||||
NoNormal = 0x4u, /**< if set, there are no normal data for vertex. */
|
||||
SingleUV = 0x8u, /**< if not set, the VertexUV is a list, otherwise a single global VxVertex2. */
|
||||
NoPos = 0x10u, /**< if set, there are no position data for vertex. */
|
||||
};
|
||||
|
||||
public:
|
||||
CKMesh(CKContext* ctx, CK_ID ckid, CKSTRING name);
|
||||
virtual ~CKMesh();
|
||||
YYCC_DEL_CLS_COPY_MOVE(CKMesh);
|
||||
|
||||
virtual CK_CLASSID GetClassID() override {
|
||||
return CK_CLASSID::CKCID_MESH;
|
||||
}
|
||||
|
||||
virtual void CheckPreDeletion() override;
|
||||
|
||||
// 2 RW functions
|
||||
virtual bool Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) override;
|
||||
virtual bool Load(CKStateChunk* chunk, CKFileVisitor* file) override;
|
||||
|
||||
// it only have special Show method
|
||||
virtual void Show(CK_OBJECT_SHOWOPTION show = CK_OBJECT_SHOWOPTION::CKSHOW) override;
|
||||
|
||||
// ===== Misc Section =====
|
||||
public:
|
||||
void CleanMesh();
|
||||
VxMath::VXMESH_FLAGS GetMeshFlags() const;
|
||||
void SetMeshFlags(VxMath::VXMESH_FLAGS flags);
|
||||
VxMath::VXMESH_LITMODE GetLitMode() const;
|
||||
void SetLitMode(VxMath::VXMESH_LITMODE mode);
|
||||
VxMath::VXTEXTURE_WRAPMODE GetWrapMode() const;
|
||||
void SetWrapMode(VxMath::VXTEXTURE_WRAPMODE mode);
|
||||
protected:
|
||||
VertexSaveFlags GenerateSaveFlags();
|
||||
void BuildNormals();
|
||||
void BuildFaceNormals();
|
||||
|
||||
// ===== Vertex Section =====
|
||||
public:
|
||||
CKDWORD GetVertexCount() const;
|
||||
void SetVertexCount(CKDWORD count);
|
||||
VxMath::VxVector3* GetVertexPositions();
|
||||
VxMath::VxVector3* GetVertexNormals();
|
||||
VxMath::VxVector2* GetVertexUVs();
|
||||
CKDWORD* GetVertexColors();
|
||||
CKDWORD* GetVertexSpecularColors();
|
||||
|
||||
// ===== Material Slot Section =====
|
||||
public:
|
||||
CKDWORD GetMaterialSlotCount() const;
|
||||
void SetMaterialSlotCount(CKDWORD count);
|
||||
CKMaterial** GetMaterialSlots();
|
||||
|
||||
// ===== Face Section =====
|
||||
public:
|
||||
CKDWORD GetFaceCount() const;
|
||||
void SetFaceCount(CKDWORD count);
|
||||
CKWORD* GetFaceIndices();
|
||||
CKWORD* GetFaceMaterialSlotIndexs();
|
||||
VxMath::VxVector3* GetFaceNormals(CKDWORD& stride);
|
||||
|
||||
// ===== Line Section =====
|
||||
public:
|
||||
CKDWORD GetLineCount() const;
|
||||
void SetLineCount(CKDWORD count);
|
||||
CKWORD* GetLineIndices();
|
||||
|
||||
protected:
|
||||
struct FaceData_t {
|
||||
FaceData_t() :
|
||||
m_Normal()
|
||||
{}
|
||||
VxMath::VxVector3 m_Normal;
|
||||
};
|
||||
VxMath::VXMESH_FLAGS m_Flags;
|
||||
CKDWORD m_VertexCount;
|
||||
CKDWORD m_LineCount;
|
||||
CKDWORD m_MaterialSlotCount;
|
||||
CKDWORD m_FaceCount;
|
||||
|
||||
XContainer::XArray<VxMath::VxVector3> m_VertexPosition;
|
||||
XContainer::XArray<VxMath::VxVector3> m_VertexNormal;
|
||||
XContainer::XArray<VxMath::VxVector2> m_VertexUV;
|
||||
XContainer::XArray<CKDWORD> m_VertexColor;
|
||||
XContainer::XArray<CKDWORD> m_VertexSpecularColor;
|
||||
|
||||
XContainer::XArray<CKMaterial*> m_MaterialSlot;
|
||||
|
||||
XContainer::XArray<CKWORD> m_FaceIndices;
|
||||
XContainer::XArray<CKWORD> m_FaceMtlIndex;
|
||||
XContainer::XArray<FaceData_t> m_FaceOthers;
|
||||
|
||||
XContainer::XArray<CKWORD> m_LineIndices;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
#include "CKObject.hpp"
|
||||
#include "../CKStateChunk.hpp"
|
||||
|
||||
namespace LibCmo::CK2::ObjImpls {
|
||||
|
||||
CKObject::CKObject(CKContext* ctx, CK_ID ckid, CKSTRING name) :
|
||||
m_ID(ckid),
|
||||
m_Name(),
|
||||
m_Context(ctx),
|
||||
m_ObjectFlags(CK_OBJECT_FLAGS::CK_OBJECT_VISIBLE) {
|
||||
// set name with possible nullptr.
|
||||
XContainer::NSXString::FromCKSTRING(m_Name, name);
|
||||
}
|
||||
|
||||
CKObject::~CKObject() {}
|
||||
|
||||
#pragma region Non-virtual Functions
|
||||
|
||||
CK_ID CKObject::GetID() const {
|
||||
return m_ID;
|
||||
}
|
||||
CKSTRING CKObject::GetName() const {
|
||||
return XContainer::NSXString::ToCKSTRING(m_Name);
|
||||
}
|
||||
void CKObject::SetName(CKSTRING u8_name) {
|
||||
XContainer::NSXString::FromCKSTRING(m_Name, u8_name);
|
||||
}
|
||||
CK_OBJECT_FLAGS CKObject::GetObjectFlags() const {
|
||||
return m_ObjectFlags;
|
||||
}
|
||||
void CKObject::SetObjectFlags(CK_OBJECT_FLAGS flags) {
|
||||
m_ObjectFlags = flags;
|
||||
}
|
||||
bool CKObject::IsToBeDeleted() const {
|
||||
return YYCC::EnumHelper::Has(m_ObjectFlags, CK_OBJECT_FLAGS::CK_OBJECT_TOBEDELETED);
|
||||
}
|
||||
CKContext* CKObject::GetCKContext() const {
|
||||
return m_Context;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
|
||||
void CKObject::PreDelete() {}
|
||||
|
||||
void CKObject::CheckPreDeletion() {}
|
||||
|
||||
void CKObject::CheckPostDeletion() {}
|
||||
|
||||
|
||||
void CKObject::PreSave(CKFileVisitor* file, CKDWORD flags) {}
|
||||
|
||||
bool CKObject::Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) {
|
||||
if (YYCC::EnumHelper::Has(m_ObjectFlags, CK_OBJECT_FLAGS::CK_OBJECT_HIERACHICALHIDE)) {
|
||||
// if hierarchy hidden
|
||||
chunk->WriteIdentifier(CK_STATESAVEFLAGS_OBJECT::CK_STATESAVE_OBJECTHIERAHIDDEN);
|
||||
} else if (!YYCC::EnumHelper::Has(m_ObjectFlags, CK_OBJECT_FLAGS::CK_OBJECT_VISIBLE)) {
|
||||
// if really hidden
|
||||
chunk->WriteIdentifier(CK_STATESAVEFLAGS_OBJECT::CK_STATESAVE_OBJECTHIDDEN);
|
||||
}
|
||||
|
||||
chunk->SetClassId(CK_CLASSID::CKCID_OBJECT);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CKObject::Load(CKStateChunk* chunk, CKFileVisitor* file) {
|
||||
if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_OBJECT::CK_STATESAVE_OBJECTHIDDEN)) {
|
||||
YYCC::EnumHelper::Remove(this->m_ObjectFlags,
|
||||
CK_OBJECT_FLAGS::CK_OBJECT_VISIBLE,
|
||||
CK_OBJECT_FLAGS::CK_OBJECT_HIERACHICALHIDE
|
||||
);
|
||||
} else {
|
||||
if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_OBJECT::CK_STATESAVE_OBJECTHIERAHIDDEN)) {
|
||||
// != 0
|
||||
YYCC::EnumHelper::Remove(this->m_ObjectFlags, CK_OBJECT_FLAGS::CK_OBJECT_VISIBLE);
|
||||
YYCC::EnumHelper::Add(this->m_ObjectFlags, CK_OBJECT_FLAGS::CK_OBJECT_HIERACHICALHIDE);
|
||||
|
||||
} else {
|
||||
// == 0
|
||||
YYCC::EnumHelper::Add(this->m_ObjectFlags, CK_OBJECT_FLAGS::CK_OBJECT_VISIBLE);
|
||||
YYCC::EnumHelper::Remove(this->m_ObjectFlags, CK_OBJECT_FLAGS::CK_OBJECT_HIERACHICALHIDE);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CKObject::PostLoad() {}
|
||||
|
||||
|
||||
void CKObject::Show(CK_OBJECT_SHOWOPTION show) {
|
||||
// clear all visible data of object flags
|
||||
YYCC::EnumHelper::Remove(m_ObjectFlags,
|
||||
CK_OBJECT_FLAGS::CK_OBJECT_HIERACHICALHIDE,
|
||||
CK_OBJECT_FLAGS::CK_OBJECT_VISIBLE
|
||||
);
|
||||
|
||||
switch (show) {
|
||||
case CK_OBJECT_SHOWOPTION::CKSHOW:
|
||||
YYCC::EnumHelper::Add(m_ObjectFlags, CK_OBJECT_FLAGS::CK_OBJECT_VISIBLE);
|
||||
break;
|
||||
case CK_OBJECT_SHOWOPTION::CKHIERARCHICALHIDE:
|
||||
YYCC::EnumHelper::Add(m_ObjectFlags, CK_OBJECT_FLAGS::CK_OBJECT_HIERACHICALHIDE);
|
||||
break;
|
||||
case CK_OBJECT_SHOWOPTION::CKHIDE:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool CKObject::IsVisible() const {
|
||||
return YYCC::EnumHelper::Has(m_ObjectFlags, CK_OBJECT_FLAGS::CK_OBJECT_VISIBLE);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../VTInternal.hpp"
|
||||
|
||||
/**
|
||||
CKObject virtual functions implementations help
|
||||
|
||||
Implement as original meaning:
|
||||
- PreSave()
|
||||
- Save()
|
||||
- Load()
|
||||
- PostLoad()
|
||||
|
||||
- GetClassID()
|
||||
|
||||
- Show()
|
||||
- IsVisible()
|
||||
|
||||
- PreDelete()
|
||||
- CheckPreDeletion()
|
||||
- CheckPostDeletion()
|
||||
|
||||
No implement because don't care:
|
||||
- GetMemoryOccupation()
|
||||
- IsObjectUsed()
|
||||
- PrepareDependencies()
|
||||
- RemapDependencies()
|
||||
|
||||
- IsHiddenByParent()
|
||||
- CanBeHide()
|
||||
|
||||
Implement moved into other location:
|
||||
- Copy(): Use CKObject::CKObject(CK_ID newid, const CKObject* obj) ctor and CKClassDesc to implement.
|
||||
|
||||
*/
|
||||
|
||||
namespace LibCmo::CK2::ObjImpls {
|
||||
|
||||
class CKObject {
|
||||
public:
|
||||
CKObject(CKContext* ctx, CK_ID ckid, CKSTRING name);
|
||||
virtual ~CKObject();
|
||||
YYCC_DEL_CLS_COPY_MOVE(CKObject);
|
||||
|
||||
CK_ID GetID() const;
|
||||
CKSTRING GetName() const;
|
||||
void SetName(CKSTRING u8_name);
|
||||
CK_OBJECT_FLAGS GetObjectFlags() const;
|
||||
void SetObjectFlags(CK_OBJECT_FLAGS flags);
|
||||
bool IsToBeDeleted() const;
|
||||
CKContext* GetCKContext() const;
|
||||
|
||||
virtual CK_CLASSID GetClassID() {
|
||||
return CK_CLASSID::CKCID_OBJECT;
|
||||
}
|
||||
|
||||
virtual void PreDelete();
|
||||
virtual void CheckPreDeletion();
|
||||
virtual void CheckPostDeletion();
|
||||
|
||||
virtual void PreSave(CKFileVisitor* file, CKDWORD flags);
|
||||
virtual bool Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags);
|
||||
virtual bool Load(CKStateChunk* chunk, CKFileVisitor* file);
|
||||
virtual void PostLoad();
|
||||
|
||||
/**
|
||||
* @brief Shows or hides an object
|
||||
* @remark
|
||||
* + If show is set to CKHIERARCHICALHIDE this object will be hidden along with all its children.
|
||||
* + The render engine renders objets in a hierarchical way and stops iterating a hierarchy if it encounters an object which is hierarchically hidden. The problem is that, for performance reason, children objets visibility flags are left unchanged (ie. if a child object was visible CKObject::IsVisible will still return TRUE). To test is a object is hidden because one of its parent object is hierarchically hidden use CKObject::IsHiddenByParent
|
||||
* + The CKHIERARCHICALHIDE option is only relevant for CK2dEntity and CK3dEntity derived classes.
|
||||
* + If show is set to CKSHOW the object will be shown and it also removes the hierarchically hidden flags.
|
||||
* + If show is set to CKHIDE the object will be hidden and it also removes the hierarchically hidden flags.
|
||||
* + This function is overload by CKGroup,CKMesh and CK3dEntity.
|
||||
*/
|
||||
virtual void Show(CK_OBJECT_SHOWOPTION show = CK_OBJECT_SHOWOPTION::CKSHOW);
|
||||
/**
|
||||
* @brief Returns whether this object is visible
|
||||
* @return TRUE if the object is visible, FALSE otherwise
|
||||
* @remark
|
||||
* + Only CKRenderObject and derived classes(CK2dEntity,CK3dEntity),CKMesh and CKGroup return relevant information about their visibility state. Other classes may return any values.
|
||||
* + An object can return CKSHOW and still be hidden if its parent (see CK3dEntity::GetParent and CK2dEntity::GetParent) is hierarchically hidden (see CKObject::Show)
|
||||
*/
|
||||
virtual bool IsVisible() const;
|
||||
|
||||
protected:
|
||||
CK_ID m_ID;
|
||||
XContainer::XString m_Name;
|
||||
CK_OBJECT_FLAGS m_ObjectFlags;
|
||||
CKContext* m_Context;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../VTInternal.hpp"
|
||||
#include "CKBeObject.hpp"
|
||||
|
||||
namespace LibCmo::CK2::ObjImpls {
|
||||
|
||||
class CKRenderObject : public CKBeObject {
|
||||
public:
|
||||
CKRenderObject(CKContext* ctx, CK_ID ckid, CKSTRING name) :
|
||||
CKBeObject(ctx, ckid, name)
|
||||
{}
|
||||
virtual ~CKRenderObject() {}
|
||||
YYCC_DEL_CLS_COPY_MOVE(CKRenderObject);
|
||||
|
||||
virtual CK_CLASSID GetClassID() override {
|
||||
return CK_CLASSID::CKCID_RENDEROBJECT;
|
||||
}
|
||||
|
||||
// CKRenderObject do not implement any load/save functions
|
||||
//virtual void PreSave(CKFileVisitor* file, CKDWORD flags) override;
|
||||
//virtual bool Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) override;
|
||||
//virtual bool Load(CKStateChunk* chunk, CKFileVisitor* file) override;
|
||||
//virtual void PostLoad() override;
|
||||
|
||||
protected:
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../VTInternal.hpp"
|
||||
#include "CKObject.hpp"
|
||||
|
||||
namespace LibCmo::CK2::ObjImpls {
|
||||
|
||||
class CKSceneObject : public CKObject {
|
||||
public:
|
||||
CKSceneObject(CKContext* ctx, CK_ID ckid, CKSTRING name) :
|
||||
CKObject(ctx, ckid, name),
|
||||
m_Scenes() {}
|
||||
virtual ~CKSceneObject() {}
|
||||
YYCC_DEL_CLS_COPY_MOVE(CKSceneObject);
|
||||
|
||||
virtual CK_CLASSID GetClassID() override {
|
||||
return CK_CLASSID::CKCID_SCENEOBJECT;
|
||||
}
|
||||
// CKSceneObject do not override any RW functions.
|
||||
//virtual void PreSave(CKFileVisitor* file, CKDWORD flags) override;
|
||||
//virtual bool Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) override;
|
||||
//virtual bool Load(CKStateChunk* chunk, CKFileVisitor* file) override;
|
||||
//virtual void PostLoad() override;
|
||||
|
||||
protected:
|
||||
XContainer::XBitArray m_Scenes;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
#include "CKTargetCamera.hpp"
|
||||
#include "../CKStateChunk.hpp"
|
||||
#include "../CKContext.hpp"
|
||||
|
||||
namespace LibCmo::CK2::ObjImpls {
|
||||
|
||||
// MARK: THIS CODE IS BARELY FULL COPY OF CKTargetLight.
|
||||
// Please sync them if you modify one of them!
|
||||
|
||||
CKTargetCamera::CKTargetCamera(CKContext* ctx, CK_ID ckid, CKSTRING name) :
|
||||
CKCamera(ctx, ckid, name), m_Target3dEntity(0) {}
|
||||
|
||||
CKTargetCamera::~CKTargetCamera() {}
|
||||
|
||||
void CKTargetCamera::PreDelete() {
|
||||
// Remove associated target
|
||||
SetTarget(nullptr);
|
||||
}
|
||||
|
||||
void CKTargetCamera::CheckPostDeletion() {
|
||||
CKCamera::CheckPostDeletion();
|
||||
|
||||
// Remove target if is not existing.
|
||||
CKObject* target = m_Context->GetObject(m_Target3dEntity);
|
||||
if (target == nullptr) {
|
||||
m_Target3dEntity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool CKTargetCamera::Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) {
|
||||
bool suc = CKCamera::Save(chunk, file, flags);
|
||||
if (!suc) return false;
|
||||
|
||||
// Save target
|
||||
{
|
||||
chunk->WriteIdentifier(CK_STATESAVEFLAGS_CAMERA::CK_STATESAVE_TCAMERATARGET);
|
||||
CKObject* target = m_Context->GetObject(m_Target3dEntity);
|
||||
chunk->WriteObjectPointer(target);
|
||||
}
|
||||
|
||||
chunk->SetClassId(CK_CLASSID::CKCID_TARGETCAMERA);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CKTargetCamera::Load(CKStateChunk* chunk, CKFileVisitor* file) {
|
||||
bool suc = CKCamera::Load(chunk, file);
|
||||
if (!suc) return false;
|
||||
|
||||
// Read target
|
||||
if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_CAMERA::CK_STATESAVE_TCAMERATARGET)) {
|
||||
chunk->ReadObjectID(m_Target3dEntity);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CK3dEntity* CKTargetCamera::GetTarget() const {
|
||||
return static_cast<CK3dEntity*>(m_Context->GetObject(m_Target3dEntity));
|
||||
}
|
||||
|
||||
void CKTargetCamera::SetTarget(CK3dEntity* target) {
|
||||
// The target can not be self.
|
||||
if (target == this) return;
|
||||
|
||||
// First remove current target
|
||||
CK3dEntity* old_target = static_cast<CK3dEntity*>(m_Context->GetObject(m_Target3dEntity));
|
||||
if (old_target != nullptr) {
|
||||
CK_3DENTITY_FLAGS old_target_flags = old_target->GetEntityFlags();
|
||||
YYCC::EnumHelper::Remove(old_target_flags, CK_3DENTITY_FLAGS::CK_3DENTITY_TARGETCAMERA);
|
||||
YYCC::EnumHelper::Add(old_target_flags, CK_3DENTITY_FLAGS::CK_3DENTITY_FRAME);
|
||||
old_target->SetEntityFlags(old_target_flags);
|
||||
}
|
||||
|
||||
// Then add specified target
|
||||
if (target != nullptr) {
|
||||
CK_3DENTITY_FLAGS new_target_flags = target->GetEntityFlags();
|
||||
YYCC::EnumHelper::Add(new_target_flags, CK_3DENTITY_FLAGS::CK_3DENTITY_TARGETCAMERA);
|
||||
YYCC::EnumHelper::Remove(new_target_flags, CK_3DENTITY_FLAGS::CK_3DENTITY_FRAME);
|
||||
target->SetEntityFlags(new_target_flags);
|
||||
}
|
||||
|
||||
// Get CK_ID of new target
|
||||
CK_ID target_id = 0;
|
||||
if (target != nullptr)
|
||||
target_id = target->GetID();
|
||||
|
||||
// Assign target id.
|
||||
m_Target3dEntity = target_id;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../VTInternal.hpp"
|
||||
#include "CKCamera.hpp"
|
||||
|
||||
namespace LibCmo::CK2::ObjImpls {
|
||||
|
||||
class CKTargetCamera : public CKCamera {
|
||||
public:
|
||||
CKTargetCamera(CKContext* ctx, CK_ID ckid, CKSTRING name);
|
||||
virtual ~CKTargetCamera();
|
||||
YYCC_DEL_CLS_COPY_MOVE(CKTargetCamera);
|
||||
|
||||
virtual CK_CLASSID GetClassID() override {
|
||||
return CK_CLASSID::CKCID_TARGETCAMERA;
|
||||
}
|
||||
|
||||
virtual void PreDelete() override;
|
||||
virtual void CheckPostDeletion() override;
|
||||
|
||||
// 2 RW funcions
|
||||
virtual bool Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) override;
|
||||
virtual bool Load(CKStateChunk* chunk, CKFileVisitor* file) override;
|
||||
|
||||
|
||||
virtual CK3dEntity* GetTarget() const override;
|
||||
virtual void SetTarget(CK3dEntity* target) override;
|
||||
|
||||
protected:
|
||||
CK_ID m_Target3dEntity;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
#include "CKTargetLight.hpp"
|
||||
#include "../CKStateChunk.hpp"
|
||||
#include "../CKContext.hpp"
|
||||
|
||||
namespace LibCmo::CK2::ObjImpls {
|
||||
|
||||
// MARK: THIS CODE IS BARELY FULL COPY OF CKTargetCamera.
|
||||
// Please sync them if you modify one of them!
|
||||
|
||||
CKTargetLight::CKTargetLight(CKContext* ctx, CK_ID ckid, CKSTRING name) :
|
||||
CKLight(ctx, ckid, name), m_Target3dEntity(0) {}
|
||||
|
||||
CKTargetLight::~CKTargetLight() {}
|
||||
|
||||
void CKTargetLight::PreDelete() {
|
||||
// MARK: In original code, there is no such override.
|
||||
// Following statement is written in a function called "vector deleting destructor".
|
||||
// Idk what it is. There is no resetting target code in its dtor and elsewhere.
|
||||
// I think this is crucial, so I add this overload as my understandings.
|
||||
|
||||
// Remove associated target
|
||||
SetTarget(nullptr);
|
||||
}
|
||||
|
||||
void CKTargetLight::CheckPostDeletion() {
|
||||
CKLight::CheckPostDeletion();
|
||||
|
||||
// Remove target if is not existing.
|
||||
CKObject* target = m_Context->GetObject(m_Target3dEntity);
|
||||
if (target == nullptr) {
|
||||
m_Target3dEntity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool CKTargetLight::Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) {
|
||||
bool suc = CKLight::Save(chunk, file, flags);
|
||||
if (!suc) return false;
|
||||
|
||||
// Save target
|
||||
{
|
||||
chunk->WriteIdentifier(CK_STATESAVEFLAGS_LIGHT::CK_STATESAVE_TLIGHTTARGET);
|
||||
CKObject* target = m_Context->GetObject(m_Target3dEntity);
|
||||
chunk->WriteObjectPointer(target);
|
||||
}
|
||||
|
||||
chunk->SetClassId(CK_CLASSID::CKCID_TARGETLIGHT);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CKTargetLight::Load(CKStateChunk* chunk, CKFileVisitor* file) {
|
||||
bool suc = CKLight::Load(chunk, file);
|
||||
if (!suc) return false;
|
||||
|
||||
// Read target
|
||||
if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_LIGHT::CK_STATESAVE_TLIGHTTARGET)) {
|
||||
chunk->ReadObjectID(m_Target3dEntity);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CK3dEntity* CKTargetLight::GetTarget() const {
|
||||
return static_cast<CK3dEntity*>(m_Context->GetObject(m_Target3dEntity));
|
||||
}
|
||||
void CKTargetLight::SetTarget(CK3dEntity* target) {
|
||||
// The target can not be self.
|
||||
if (target == this) return;
|
||||
|
||||
// First remove current target
|
||||
CK3dEntity* old_target = static_cast<CK3dEntity*>(m_Context->GetObject(m_Target3dEntity));
|
||||
if (old_target != nullptr) {
|
||||
CK_3DENTITY_FLAGS old_target_flags = old_target->GetEntityFlags();
|
||||
YYCC::EnumHelper::Remove(old_target_flags, CK_3DENTITY_FLAGS::CK_3DENTITY_TARGETLIGHT);
|
||||
YYCC::EnumHelper::Add(old_target_flags, CK_3DENTITY_FLAGS::CK_3DENTITY_FRAME);
|
||||
old_target->SetEntityFlags(old_target_flags);
|
||||
}
|
||||
|
||||
// Then add specified target
|
||||
if (target != nullptr) {
|
||||
CK_3DENTITY_FLAGS new_target_flags = target->GetEntityFlags();
|
||||
YYCC::EnumHelper::Add(new_target_flags, CK_3DENTITY_FLAGS::CK_3DENTITY_TARGETLIGHT);
|
||||
YYCC::EnumHelper::Remove(new_target_flags, CK_3DENTITY_FLAGS::CK_3DENTITY_FRAME);
|
||||
target->SetEntityFlags(new_target_flags);
|
||||
}
|
||||
|
||||
// Get CK_ID of new target
|
||||
CK_ID target_id = 0;
|
||||
if (target != nullptr)
|
||||
target_id = target->GetID();
|
||||
|
||||
// Assign target id.
|
||||
m_Target3dEntity = target_id;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../VTInternal.hpp"
|
||||
#include "CKLight.hpp"
|
||||
|
||||
namespace LibCmo::CK2::ObjImpls {
|
||||
|
||||
class CKTargetLight : public CKLight {
|
||||
public:
|
||||
CKTargetLight(CKContext* ctx, CK_ID ckid, CKSTRING name);
|
||||
virtual ~CKTargetLight();
|
||||
YYCC_DEL_CLS_COPY_MOVE(CKTargetLight);
|
||||
|
||||
virtual CK_CLASSID GetClassID() override {
|
||||
return CK_CLASSID::CKCID_TARGETLIGHT;
|
||||
}
|
||||
|
||||
virtual void PreDelete() override;
|
||||
virtual void CheckPostDeletion() override;
|
||||
|
||||
// 2 RW funcions
|
||||
virtual bool Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) override;
|
||||
virtual bool Load(CKStateChunk* chunk, CKFileVisitor* file) override;
|
||||
|
||||
|
||||
virtual CK3dEntity* GetTarget() const override;
|
||||
virtual void SetTarget(CK3dEntity* target) override;
|
||||
|
||||
protected:
|
||||
CK_ID m_Target3dEntity;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,400 +0,0 @@
|
||||
#include "CKTexture.hpp"
|
||||
#include "../CKStateChunk.hpp"
|
||||
#include "../CKContext.hpp"
|
||||
#include "../MgrImpls/CKPathManager.hpp"
|
||||
|
||||
namespace LibCmo::CK2::ObjImpls {
|
||||
|
||||
/**
|
||||
* @brief A fake struct define.
|
||||
* This define is served for a buffer read / write in CKTexture.
|
||||
* Because Virtools directly write a raw struct into file,
|
||||
* and our defines are different with Virtools.
|
||||
* So we need create a fake struct.
|
||||
* @remark
|
||||
* All pointers should translate to DWORD(32 bit) for platform independent.
|
||||
* Otherwise this struct may be corrupted in x64 platform because pointer is QWORD in x64.
|
||||
*/
|
||||
#pragma pack(4)
|
||||
struct FakeBitmapProperties {
|
||||
CKINT m_Size;
|
||||
struct {
|
||||
// fake CKGUID
|
||||
CKDWORD d1, d2;
|
||||
}m_ReaderGuid;
|
||||
struct {
|
||||
// fake CKFileExtension
|
||||
char m_Data[4];
|
||||
}m_Ext;
|
||||
struct {
|
||||
// fake VxImageDescEx
|
||||
CKINT Size; ///< Size of the structure
|
||||
CKDWORD Flags; ///< Reserved for special formats (such as compressed ) 0 otherwise
|
||||
|
||||
CKINT Width; ///< Width in pixel of the image
|
||||
CKINT Height; ///< Height in pixel of the image
|
||||
union {
|
||||
CKINT BytesPerLine; ///< Pitch (width in bytes) of the image
|
||||
CKINT TotalImageSize; ///< For compressed image (DXT1...) the total size of the image
|
||||
};
|
||||
CKINT BitsPerPixel; ///< Number of bits per pixel
|
||||
union {
|
||||
CKDWORD RedMask; ///< Mask for Red component
|
||||
CKDWORD BumpDuMask; ///< Mask for Bump Du component
|
||||
};
|
||||
union {
|
||||
CKDWORD GreenMask; ///< Mask for Green component
|
||||
CKDWORD BumpDvMask; ///< Mask for Bump Dv component
|
||||
};
|
||||
union {
|
||||
CKDWORD BlueMask; ///< Mask for Blue component
|
||||
CKDWORD BumpLumMask; ///< Mask for Luminance component
|
||||
|
||||
};
|
||||
CKDWORD AlphaMask; ///< Mask for Alpha component
|
||||
|
||||
CKWORD BytesPerColorEntry; ///< ColorMap Stride
|
||||
CKWORD ColorMapEntries; ///< If other than 0 image is palletized
|
||||
|
||||
/*CKBYTE**/CKPTR ColorMap; ///< Palette colors
|
||||
/*CKBYTE**/CKPTR Image; ///< Image
|
||||
}m_Format;
|
||||
/*void**/CKPTR m_Data;
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
CKTexture::CKTexture(CKContext* ctx, CK_ID ckid, CKSTRING name) :
|
||||
CKBeObject(ctx, ckid, name),
|
||||
m_ImageHost(ctx),
|
||||
m_VideoFormat(VxMath::VX_PIXELFORMAT::_16_ARGB1555), m_UseMipMap(false), m_MipmapImages() {}
|
||||
|
||||
CKTexture::~CKTexture() {}
|
||||
|
||||
bool CKTexture::Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) {
|
||||
bool suc = CKBeObject::Save(chunk, file, flags);
|
||||
if (!suc) return false;
|
||||
|
||||
// save base image
|
||||
suc = m_ImageHost.DumpToChunk(chunk, file, CKBitmapDataWriteIdentifiers {
|
||||
.m_SpecificFormat = static_cast<CKDWORD>(CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_TEXREADER),
|
||||
.m_RawData = static_cast<CKDWORD>(CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_TEXCOMPRESSED),
|
||||
.m_FileNames = static_cast<CKDWORD>(CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_TEXFILENAMES),
|
||||
.m_MovieFileName = static_cast<CKDWORD>(CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_TEXAVIFILENAME)
|
||||
});
|
||||
if (!suc) return false;
|
||||
|
||||
// write main properties
|
||||
{
|
||||
// write ident
|
||||
chunk->WriteIdentifier(CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_OLDTEXONLY);
|
||||
|
||||
// prepare mix data. see Read for more info about the struct of this mix data
|
||||
CKDWORD mixdata = 0;
|
||||
// save options
|
||||
mixdata |= static_cast<CKDWORD>(m_ImageHost.GetSaveOptions()) & 0xFF;
|
||||
mixdata <<= 8;
|
||||
// mix flags
|
||||
CKDWORD mixflags = 0;
|
||||
if (m_ImageHost.IsTransparent()) {
|
||||
mixflags |= 0x1;
|
||||
}
|
||||
if (m_VideoFormat != VxMath::VX_PIXELFORMAT::UNKNOWN_PF) {
|
||||
mixflags |= 0x2;
|
||||
}
|
||||
if (m_ImageHost.IsCubeMap()) {
|
||||
mixflags |= 0x4;
|
||||
}
|
||||
mixdata |= mixflags & 0xFF;
|
||||
mixdata <<= 8;
|
||||
// mipmap
|
||||
mixdata |= (IsUseMipmap() ? 0xFF : 0);
|
||||
|
||||
// write mix data
|
||||
chunk->WriteStruct(mixdata);
|
||||
|
||||
// transparent color
|
||||
chunk->WriteStruct(m_ImageHost.GetTransparentColor());
|
||||
// current slot
|
||||
if (m_ImageHost.GetSlotCount() > 1) {
|
||||
chunk->WriteStruct(m_ImageHost.GetCurrentSlot());
|
||||
}
|
||||
// video fmt
|
||||
if (m_VideoFormat != VxMath::VX_PIXELFORMAT::UNKNOWN_PF) {
|
||||
chunk->WriteStruct(m_VideoFormat);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// mipmap
|
||||
if (GetMipmapLevel() != 0) {
|
||||
chunk->WriteIdentifier(CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_USERMIPMAP);
|
||||
|
||||
// write mipmap level
|
||||
chunk->WriteStruct(GetMipmapLevel());
|
||||
|
||||
// write data
|
||||
for (auto& level : m_MipmapImages) {
|
||||
if (level.IsValid()) {
|
||||
// do upside down and write
|
||||
VxMath::VxImageDescEx upsidedown(level.GetWidth(), level.GetHeight());
|
||||
VxMath::VxDoBlitUpsideDown(&level, &upsidedown);
|
||||
CKBitmapData::WriteRawBitmap(chunk, &upsidedown);
|
||||
} else {
|
||||
// write it directly
|
||||
CKBitmapData::WriteRawBitmap(chunk, &level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pick threshold
|
||||
if (m_ImageHost.GetPickThreshold() != 0) {
|
||||
chunk->WriteIdentifier(CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_PICKTHRESHOLD);
|
||||
chunk->WriteStruct(m_ImageHost.GetPickThreshold());
|
||||
}
|
||||
|
||||
// bitmap properties
|
||||
{
|
||||
chunk->WriteIdentifier(CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_TEXSAVEFORMAT);
|
||||
|
||||
// prepare a fake one
|
||||
FakeBitmapProperties props;
|
||||
const CKBitmapProperties& realprops = m_ImageHost.GetSaveFormat();
|
||||
|
||||
// setup fake self
|
||||
props.m_Size = CKSizeof(props);
|
||||
props.m_Data = 0;
|
||||
|
||||
// setup fake VxImageDescEx
|
||||
props.m_Format.Size = CKSizeof(props.m_Format);
|
||||
props.m_Format.Flags = static_cast<CKDWORD>(VxMath::VX_PIXELFORMAT::_32_ARGB8888);
|
||||
props.m_Format.Width = m_ImageHost.GetWidth();
|
||||
props.m_Format.Height = m_ImageHost.GetHeight();
|
||||
props.m_Format.BytesPerLine = VxMath::VxImageDescEx::PixelSize * props.m_Format.Width;
|
||||
props.m_Format.BitsPerPixel = 32;
|
||||
|
||||
props.m_Format.RedMask = 0x00FF0000;
|
||||
props.m_Format.GreenMask = 0x0000FF00;
|
||||
props.m_Format.BlueMask = 0x000000FF;
|
||||
props.m_Format.AlphaMask = 0xFF000000;
|
||||
|
||||
props.m_Format.BytesPerColorEntry = 0;
|
||||
props.m_Format.ColorMapEntries = 0;
|
||||
|
||||
props.m_Format.ColorMap = 0;
|
||||
props.m_Format.Image = 0;
|
||||
|
||||
// setup ext and guid
|
||||
props.m_ReaderGuid.d1 = realprops.m_ReaderGuid.d1;
|
||||
props.m_ReaderGuid.d2 = realprops.m_ReaderGuid.d2;
|
||||
std::string ext;
|
||||
if (!m_Context->GetOrdinaryString(realprops.m_Ext.GetExt(), ext))
|
||||
m_Context->OutputToConsole(u8"Fail to get ordinary string for the extension of bitmap properties when saving CKTexture. Some textures may be saved with blank extension.");
|
||||
std::memcpy(
|
||||
props.m_Ext.m_Data,
|
||||
ext.c_str(),
|
||||
std::min(CKSizeof(props.m_Ext.m_Data) - CKDWORD_C(1), static_cast<CKDWORD>(ext.size()))
|
||||
);
|
||||
|
||||
// write fake one
|
||||
chunk->WriteBuffer(&props, CKSizeof(props));
|
||||
}
|
||||
|
||||
chunk->SetClassId(CK_CLASSID::CKCID_TEXTURE);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CKTexture::Load(CKStateChunk* chunk, CKFileVisitor* file) {
|
||||
bool suc = CKBeObject::Load(chunk, file);
|
||||
if (!suc) return false;
|
||||
|
||||
// read base image
|
||||
suc = m_ImageHost.ReadFromChunk(chunk, file, CKBitmapDataReadIdentifiers {
|
||||
.m_SpecificFormat = static_cast<CKDWORD>(CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_TEXREADER),
|
||||
.m_RawData = static_cast<CKDWORD>(CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_TEXCOMPRESSED),
|
||||
.m_OldRawData = static_cast<CKDWORD>(CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_TEXBITMAPS),
|
||||
.m_FileNames = static_cast<CKDWORD>(CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_TEXFILENAMES),
|
||||
.m_MovieFileName = static_cast<CKDWORD>(CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_TEXAVIFILENAME)
|
||||
});
|
||||
if (!suc) return false;
|
||||
|
||||
if (chunk->GetDataVersion() < CK_STATECHUNK_DATAVERSION::CHUNK_MAJORCHANGE_VERSION) {
|
||||
// MARK: old data process. i don't want to process it anymore.
|
||||
// thus return false directly.
|
||||
return false;
|
||||
} else {
|
||||
CKDWORD fmtbytesize;
|
||||
// MARK: there is a patch for virtools 2.1 implement.
|
||||
// CK_STATESAVE_TEXONLY is noy valid in 2.1 but we add it for cpmpatibility reason.
|
||||
if (chunk->SeekIdentifierAndReturnSize(CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_OLDTEXONLY, &fmtbytesize) ||
|
||||
chunk->SeekIdentifierAndReturnSize(CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_TEXONLY, &fmtbytesize)) {
|
||||
// for mid data:
|
||||
// HIGH >>> 0xFF (blank) 0xFF (save options) 0xFF (transparent + movie info + video fmt) 0xFF (mip map) <<< LOW
|
||||
// for mixed flags:
|
||||
// HIGH >>> 1(blank) 1(cubemap) 1(has video fmt) 1(is transparent)
|
||||
CKDWORD mixdata;
|
||||
chunk->ReadStruct(mixdata);
|
||||
// set mipmap
|
||||
UseMipmap(mixdata & 0xFF);
|
||||
mixdata >>= 8;
|
||||
// mix flags
|
||||
CKDWORD mixflags = mixdata & 0xFF;
|
||||
mixdata >>= 8;
|
||||
m_ImageHost.SetTransparent(mixflags & 0x1);
|
||||
bool hasVideoFmt = mixflags & 0x2;
|
||||
m_ImageHost.SetCubeMap(mixflags & 0x4);
|
||||
// save options
|
||||
m_ImageHost.SetSaveOptions(static_cast<CK_TEXTURE_SAVEOPTIONS>(mixdata & 0xFF));
|
||||
mixdata >>= 8;
|
||||
|
||||
// set current slot, transparent color, and video format.
|
||||
CKDWORD currentSlot, transColor;
|
||||
fmtbytesize -= CKSizeof(CKDWORD);
|
||||
switch (fmtbytesize) {
|
||||
case (3 * CKSizeof(CKDWORD)):
|
||||
chunk->ReadStruct(transColor);
|
||||
m_ImageHost.SetTransparentColor(transColor);
|
||||
chunk->ReadStruct(currentSlot);
|
||||
m_ImageHost.SetCurrentSlot(currentSlot);
|
||||
chunk->ReadStruct(m_VideoFormat);
|
||||
break;
|
||||
case (2 * CKSizeof(CKDWORD)):
|
||||
if (m_ImageHost.GetSlotCount() <= 1 || !hasVideoFmt) {
|
||||
chunk->ReadStruct(transColor);
|
||||
m_ImageHost.SetTransparentColor(transColor);
|
||||
}
|
||||
if (m_ImageHost.GetSlotCount() > 1) {
|
||||
chunk->ReadStruct(currentSlot);
|
||||
m_ImageHost.SetCurrentSlot(currentSlot);
|
||||
}
|
||||
if (hasVideoFmt) {
|
||||
chunk->ReadStruct(m_VideoFormat);
|
||||
}
|
||||
break;
|
||||
case (CKSizeof(CKDWORD)):
|
||||
if (hasVideoFmt) {
|
||||
chunk->ReadStruct(m_VideoFormat);
|
||||
} else if (m_ImageHost.GetSlotCount() <= 1) {
|
||||
chunk->ReadStruct(transColor);
|
||||
m_ImageHost.SetTransparentColor(transColor);
|
||||
} else {
|
||||
chunk->ReadStruct(currentSlot);
|
||||
m_ImageHost.SetCurrentSlot(currentSlot);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// read mipmap
|
||||
if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_USERMIPMAP)) {
|
||||
CKDWORD mipmapCount;
|
||||
chunk->ReadStruct(mipmapCount);
|
||||
SetMipmapLevel(mipmapCount);
|
||||
|
||||
for (CKDWORD i = 0; i < mipmapCount; ++i) {
|
||||
VxMath::VxImageDescEx cache;
|
||||
if (CKBitmapData::ReadRawBitmap(chunk, &cache)) {
|
||||
VxMath::VxDoBlitUpsideDown(&cache, &m_MipmapImages[i]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// pick threshold
|
||||
if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_PICKTHRESHOLD)) {
|
||||
CKDWORD threshold;
|
||||
chunk->ReadStruct(threshold);
|
||||
m_ImageHost.SetPickThreshold(threshold);
|
||||
}
|
||||
|
||||
// save properties
|
||||
if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_TEXSAVEFORMAT)) {
|
||||
auto buf = chunk->ReadBufferWrapper();
|
||||
if (buf != nullptr) {
|
||||
FakeBitmapProperties* props = static_cast<FakeBitmapProperties*>(buf.get());
|
||||
|
||||
// get utf8 extension
|
||||
XContainer::XString ext;
|
||||
if (!m_Context->GetUTF8String(props->m_Ext.m_Data, ext))
|
||||
m_Context->OutputToConsole(u8"Fail to get UTF8 extension when loading CKTexture. Some textures may have blank extension in bitmap properties.");
|
||||
|
||||
// get my bitmap prop
|
||||
CKBitmapProperties myprops(
|
||||
CKGUID(props->m_ReaderGuid.d1, props->m_ReaderGuid.d2),
|
||||
ext.c_str()
|
||||
);
|
||||
m_ImageHost.SetSaveFormat(myprops);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// correct video format
|
||||
if (m_VideoFormat > VxMath::VX_PIXELFORMAT::_32_X8L8V8U8) {
|
||||
m_VideoFormat = VxMath::VX_PIXELFORMAT::_16_ARGB1555;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma region Visitor
|
||||
|
||||
CKBitmapData& CKTexture::GetUnderlyingData() {
|
||||
return m_ImageHost;
|
||||
}
|
||||
|
||||
bool CKTexture::LoadImage(CKSTRING filename, CKDWORD slot) {
|
||||
// check file name
|
||||
if (filename == nullptr) return false;
|
||||
// check slot
|
||||
if (slot >= m_ImageHost.GetSlotCount()) return false;
|
||||
|
||||
// resolve file name first
|
||||
XContainer::XString filepath;
|
||||
XContainer::NSXString::FromCKSTRING(filepath, filename);
|
||||
if (!m_Context->GetPathManager()->ResolveFileName(filepath)) return false;
|
||||
|
||||
// try loading image
|
||||
if (!m_ImageHost.LoadImage(XContainer::NSXString::ToCKSTRING(filepath), slot)) return false;
|
||||
|
||||
// sync file name
|
||||
return m_ImageHost.SetSlotFileName(slot, XContainer::NSXString::ToCKSTRING(filepath));
|
||||
}
|
||||
|
||||
bool CKTexture::IsUseMipmap() const {
|
||||
return m_UseMipMap;
|
||||
}
|
||||
|
||||
void CKTexture::UseMipmap(bool isUse) {
|
||||
m_UseMipMap = isUse;
|
||||
|
||||
if (!m_UseMipMap) {
|
||||
m_MipmapImages.clear();
|
||||
}
|
||||
}
|
||||
|
||||
CKDWORD CKTexture::GetMipmapLevel() const {
|
||||
return static_cast<CKDWORD>(m_MipmapImages.size());
|
||||
}
|
||||
|
||||
void CKTexture::SetMipmapLevel(CKDWORD level) {
|
||||
m_MipmapImages.resize(level);
|
||||
}
|
||||
|
||||
VxMath::VxImageDescEx* CKTexture::GetMipmapLevelData(CKDWORD level) {
|
||||
if (!m_UseMipMap || level >= m_MipmapImages.size()) return nullptr;
|
||||
return &m_MipmapImages[level];
|
||||
}
|
||||
|
||||
VxMath::VX_PIXELFORMAT CKTexture::GetVideoFormat() const {
|
||||
return m_VideoFormat;
|
||||
}
|
||||
|
||||
void CKTexture::SetVideoFormat(VxMath::VX_PIXELFORMAT fmt) {
|
||||
m_VideoFormat = fmt;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../VTInternal.hpp"
|
||||
#include "../CKBitmapData.hpp"
|
||||
#include "CKBeObject.hpp"
|
||||
|
||||
namespace LibCmo::CK2::ObjImpls {
|
||||
|
||||
class CKTexture : public CKBeObject {
|
||||
public:
|
||||
CKTexture(CKContext* ctx, CK_ID ckid, CKSTRING name);
|
||||
virtual ~CKTexture();
|
||||
YYCC_DEL_CLS_COPY_MOVE(CKTexture);
|
||||
|
||||
virtual CK_CLASSID GetClassID() override {
|
||||
return CK_CLASSID::CKCID_TEXTURE;
|
||||
}
|
||||
|
||||
virtual bool Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) override;
|
||||
virtual bool Load(CKStateChunk* chunk, CKFileVisitor* file) override;
|
||||
|
||||
CKBitmapData& GetUnderlyingData();
|
||||
|
||||
/**
|
||||
* @brief A wrapper of underlying CKBitmapData::LoadImage. Not only load image, but also set file name.
|
||||
* @param filename[in] File name of loading image.
|
||||
* @param slot[in] The slot that image will be loaded into.
|
||||
* @return True if success.
|
||||
*/
|
||||
bool LoadImage(CKSTRING filename, CKDWORD slot);
|
||||
|
||||
bool IsUseMipmap() const;
|
||||
void UseMipmap(bool isUse);
|
||||
CKDWORD GetMipmapLevel() const;
|
||||
void SetMipmapLevel(CKDWORD level);
|
||||
VxMath::VxImageDescEx* GetMipmapLevelData(CKDWORD level);
|
||||
|
||||
VxMath::VX_PIXELFORMAT GetVideoFormat() const;
|
||||
void SetVideoFormat(VxMath::VX_PIXELFORMAT fmt);
|
||||
|
||||
protected:
|
||||
CKBitmapData m_ImageHost;
|
||||
VxMath::VX_PIXELFORMAT m_VideoFormat;
|
||||
bool m_UseMipMap;
|
||||
XContainer::XArray<VxMath::VxImageDescEx> m_MipmapImages;
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user