1
0

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:
2026-01-24 17:13:54 +08:00
parent af6a50c2f9
commit f9ab66dfc2
78 changed files with 290 additions and 283 deletions

View File

@@ -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() {}
}

View File

@@ -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;
};
}

View File

@@ -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
}

View File

@@ -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;
};
}

View File

@@ -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
}

View File

@@ -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,
};
}

View File

@@ -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;
};
}

View File

@@ -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
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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
}

View File

@@ -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();
}

View File

@@ -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 */
};
}

View File

@@ -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
};
}

View File

@@ -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
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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));
}
};
}

View File

@@ -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
}

View File

@@ -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;
};
}

View File

@@ -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() {
//}
}

View File

@@ -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:
//};
}

View File

@@ -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
}

View File

@@ -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;
};
}

View File

@@ -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();
}
}

View File

@@ -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;
};
}

View File

@@ -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
}

View File

@@ -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;
};
}

View File

@@ -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:
};
}

View File

@@ -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);
}
}
}

View File

@@ -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;
};
}

View File

@@ -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
}

View File

@@ -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;
};
}

View File

@@ -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());
}
}

View File

@@ -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;
};
}

View File

@@ -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
}

View File

@@ -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;
};
}

View File

@@ -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
}

View File

@@ -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;
};
}

View File

@@ -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
}

View File

@@ -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;
};
}

View File

@@ -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);
}
}

View File

@@ -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;
};
}

View File

@@ -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:
};
}

View File

@@ -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;
};
}

View File

@@ -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;
}
}

View File

@@ -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;
};
}

View File

@@ -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;
}
}

View File

@@ -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;
};
}

View File

@@ -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
}

View File

@@ -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;
};
}