diff --git a/LibCmo/CK2/CKBitmapData.cpp b/LibCmo/CK2/CKBitmapData.cpp index 3e4da01..8577753 100644 --- a/LibCmo/CK2/CKBitmapData.cpp +++ b/LibCmo/CK2/CKBitmapData.cpp @@ -1,33 +1,272 @@ #include "CKBitmapData.hpp" +#include "CKContext.hpp" +#include "CKStateChunk.hpp" +#include "DataHandlers/CKBitmapHandler.hpp" +#include "MgrImpls/CKPathManager.hpp" +#include namespace LibCmo::CK2 { #pragma region Assist RW Functions - bool CKBitmapData::ReadSpecificFormatBitmap(CKStateChunk* chk, VxMath::VxImageDescEx* desc) { + bool CKBitmapData::ReadSpecificFormatBitmap(CKStateChunk* chk, CKBitmapSlot* slot) { return false; } - bool CKBitmapData::ReadRawBitmap(CKStateChunk* chk, VxMath::VxImageDescEx* desc) { + bool CKBitmapData::ReadRawBitmap(CKStateChunk* chk, CKBitmapSlot* slot) { return false; } - bool CKBitmapData::ReadOldRawBitmap(CKStateChunk* chk, VxMath::VxImageDescEx* desc) { + bool CKBitmapData::ReadOldRawBitmap(CKStateChunk* chk, CKBitmapSlot* slot) { return false; } - void CKBitmapData::WriteSpecificFormatBitmap(CKStateChunk* chk, const VxMath::VxImageDescEx* desc) { + void CKBitmapData::WriteSpecificFormatBitmap(CKStateChunk* chk, CKBitmapSlot* slot) { } - void CKBitmapData::WriteRawBitmap(CKStateChunk* chk, const VxMath::VxImageDescEx* desc) { + void CKBitmapData::WriteRawBitmap(CKStateChunk* chk, CKBitmapSlot* slot) { } #pragma endregion +#pragma region Core Read / Write - CKBitmapData::CKBitmapData(CKContext* ctx) {} + bool CKBitmapData::ReadFromChunk(CKStateChunk* chunk, CKFileVisitor* file, const CKBitmapDataReadIdentifiers& identifiers) { + XContainer::XBitArray notReadSlot; + + // check 3 types enbedded image + 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); + notReadSlot.resize(slotcount, false); + + if (width > 0 && height > 0) { + for (CKDWORD i = 0; i < slotcount; ++i) { + CreateImage(width, height, i); + if (ReadSpecificFormatBitmap(chunk, GetImageSlot(i))) { + notReadSlot[i] = true; + } else { + ReleaseImage(i); + } + } + } + + } else if (chunk->SeekIdentifierDword(identifiers.m_RawData)) { + // raw data + CKDWORD slotcount; + chunk->ReadStruct(slotcount); + + SetSlotCount(slotcount); + notReadSlot.resize(slotcount, false); + + for (CKDWORD i = 0; i < slotcount; ++i) { + if (ReadRawBitmap(chunk, GetImageSlot(i))) { + notReadSlot[i] = true; + } else { + ReleaseImage(i); + } + } + + } else if (chunk->SeekIdentifierDword(identifiers.m_OldRawData)) { + // raw data (old format) + CKDWORD slotcount; + chunk->ReadStruct(slotcount); + + SetSlotCount(slotcount); + notReadSlot.resize(slotcount, false); + + for (CKDWORD i = 0; i < slotcount; ++i) { + if (ReadOldRawBitmap(chunk, GetImageSlot(i))) { + notReadSlot[i] = true; + } 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) { + std::string filename; + chunk->ReadString(filename); + if (filename.empty()) return; + + // set + SetSlotFileName(i, filename.c_str()); + + // if not loaded, and suc in resolve file path. + // load it + bool isNotLoaded = i >= notReadSlot.size() || notReadSlot[i]; + if (isNotLoaded && m_Context->GetPathManager()->ResolveFileName(filename)) { + LoadImage(filename.c_str(), i); + } + } + } + + // movie info + if (chunk->SeekIdentifierDword(identifiers.m_MovieFileName)) { + // MARK: movie is not implemented here. + } + + } + + bool CKBitmapData::DumpToChunk(CKStateChunk* chunk, CKFileVisitor* file, const CKBitmapDataWriteIdentifiers& identifiers) { + return false; + } + +#pragma endregion + +#pragma region Misc Functions + + void CKBitmapData::SetSlotCount(CKDWORD count) { + m_Slots.resize(count); + + if (count == 0) { + EnumsHelper::Add(m_BitmapFlags, CK_BITMAPDATA_FLAGS::CKBITMAPDATA_INVALID); + } + } + + CKDWORD CKBitmapData::GetSlotCount() { + return static_cast(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 (EnumsHelper::Has(m_BitmapFlags, CK_BITMAPDATA_FLAGS::CKBITMAPDATA_CUBEMAP)) { + EnumsHelper::Add(m_BitmapFlags, CK_BITMAPDATA_FLAGS::CKBITMAPDATA_FORCERESTORE); + } + } + + CKDWORD CKBitmapData::GetCurrentSlot() { + return m_CurrentSlot; + } + + void CKBitmapData::CreateImage(CKDWORD Width, CKDWORD Height, CKDWORD Slot) { + if (Slot >= m_Slots.size()) return; + + CKBitmapSlot& slotdata = m_Slots[Slot]; + slotdata.CreateImage(Width, Height); + } + + 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 + std::string ext(filename); + m_Context->GetPathManager()->GetExtension(ext); + std::unique_ptr reader( + DataHandlers::CKBitmapHandler::GetBitmapHandler(CKFileExtension(ext.c_str()), CKGUID()) + ); + if (reader == nullptr) return false; + + // get desc + VxMath::VxImageDescEx* desc = GetImageDesc(slot); + if (desc == nullptr) return false; + + // read data + if (!reader->ReadFile(filename, desc)) { + return false; + } + + // free reader + reader.reset(); + return true; + } + + bool CKBitmapData::SaveImage(CKSTRING filename, CKDWORD slot) { + if (filename == nullptr) return false; + if (slot >= m_Slots.size()) return false; + + return true; + } + + VxMath::VxImageDescEx* CKBitmapData::GetImageDesc(CKDWORD slot) { + if (slot >= m_Slots.size()) return nullptr; + return m_Slots[slot].m_ImageData; + } + + CKBitmapSlot* CKBitmapData::GetImageSlot(CKDWORD slot) { + if (slot >= m_Slots.size()) return nullptr; + return &m_Slots[slot]; + } + + void CKBitmapData::ReleaseImage(CKDWORD slot) { + if (slot >= m_Slots.size()) return; + m_Slots[slot].FreeImage(); + } + + void CKBitmapData::SetSlotFileName(CKDWORD slot, CKSTRING filename) { + if (slot >= m_Slots.size()) return; + if (filename == nullptr) return; + m_Slots[slot].m_FileName = filename; + } + + CKSTRING CKBitmapData::GetSlotFileName(CKDWORD slot) { + if (slot >= m_Slots.size()) return nullptr; + return m_Slots[slot].m_FileName.c_str(); + } + + const CKBitmapProperties& CKBitmapData::GetSaveFormat() { + return m_SaveProperties; + } + + void CKBitmapData::SetSaveFormat(const CKBitmapProperties& props) { + m_SaveProperties = props; + } + + CK_TEXTURE_SAVEOPTIONS CKBitmapData::GetSaveOptions() { + return m_SaveOptions; + } + + void CKBitmapData::SetSaveOptions(CK_TEXTURE_SAVEOPTIONS opts) { + m_SaveOptions = opts; + } + + void CKBitmapData::SetTransparent(bool Transparency) { + EnumsHelper::Add(m_BitmapFlags, CK_BITMAPDATA_FLAGS::CKBITMAPDATA_TRANSPARENT); + } + + bool CKBitmapData::IsTransparent() { + return EnumsHelper::Has(m_BitmapFlags, CK_BITMAPDATA_FLAGS::CKBITMAPDATA_TRANSPARENT); + } + + void CKBitmapData::SetTransparentColor(CKDWORD col) { + SetTransparent(true); + m_TransColor = col; + } + + CKDWORD CKBitmapData::GetTransparentColor() { + return m_TransColor; + } + +#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() {} diff --git a/LibCmo/CK2/CKBitmapData.hpp b/LibCmo/CK2/CKBitmapData.hpp index 4869be3..3fa6095 100644 --- a/LibCmo/CK2/CKBitmapData.hpp +++ b/LibCmo/CK2/CKBitmapData.hpp @@ -28,6 +28,33 @@ namespace LibCmo::CK2 { ~CKBitmapSlot() { FreeImage(); } + CKBitmapSlot(const CKBitmapSlot& rhs) : + m_ImageData(nullptr), m_FileName(rhs.m_FileName) { + if (rhs.m_ImageData != nullptr) { + m_ImageData = new VxMath::VxImageDescEx(*rhs.m_ImageData); + } + } + CKBitmapSlot(CKBitmapSlot&& rhs) : + m_ImageData(rhs.m_ImageData), m_FileName(std::move(rhs.m_FileName)) { + rhs.m_ImageData = nullptr; + } + CKBitmapSlot& operator=(const CKBitmapSlot& rhs) { + m_FileName = rhs.m_FileName; + + FreeImage(); + if (rhs.m_ImageData != nullptr) { + m_ImageData = new VxMath::VxImageDescEx(*rhs.m_ImageData); + } + + return *this; + } + CKBitmapSlot& operator=(CKBitmapSlot&& rhs) { + m_FileName = std::move(rhs.m_FileName); + + FreeImage(); + m_ImageData = rhs.m_ImageData; + rhs.m_ImageData = nullptr; + } void CreateImage(CKDWORD Width, CKDWORD Height) { FreeImage(); @@ -50,11 +77,11 @@ namespace LibCmo::CK2 { ~CKBitmapData(); LIBCMO_DISABLE_COPY_MOVE(CKBitmapData); - static bool ReadSpecificFormatBitmap(CKStateChunk* chk, VxMath::VxImageDescEx* desc); - static bool ReadRawBitmap(CKStateChunk* chk, VxMath::VxImageDescEx* desc); - static bool ReadOldRawBitmap(CKStateChunk* chk, VxMath::VxImageDescEx* desc); - static void WriteSpecificFormatBitmap(CKStateChunk* chk, const VxMath::VxImageDescEx* desc); - static void WriteRawBitmap(CKStateChunk* chk, const VxMath::VxImageDescEx* desc); + static bool ReadSpecificFormatBitmap(CKStateChunk* chk, CKBitmapSlot* slot); + static bool ReadRawBitmap(CKStateChunk* chk, CKBitmapSlot* slot); + static bool ReadOldRawBitmap(CKStateChunk* chk, CKBitmapSlot* slot); + static void WriteSpecificFormatBitmap(CKStateChunk* chk, CKBitmapSlot* slot); + static void WriteRawBitmap(CKStateChunk* chk, CKBitmapSlot* slot); bool ReadFromChunk(CKStateChunk* chunk, CKFileVisitor* file, const CKBitmapDataReadIdentifiers& identifiers); bool DumpToChunk(CKStateChunk* chunk, CKFileVisitor* file, const CKBitmapDataWriteIdentifiers& identifiers); @@ -68,17 +95,16 @@ namespace LibCmo::CK2 { bool LoadImage(CKSTRING filename, CKDWORD slot); bool SaveImage(CKSTRING filename, CKDWORD slot); VxMath::VxImageDescEx* GetImageDesc(CKDWORD slot); + CKBitmapSlot* GetImageSlot(CKDWORD slot); void ReleaseImage(CKDWORD slot); void SetSlotFileName(CKDWORD slot, CKSTRING filename); CKSTRING GetSlotFileName(CKDWORD slot); - const CKBitmapProperties& GetSaveProperties(); - void SetSaveProperties(const CKBitmapProperties& props); + const CKBitmapProperties& GetSaveFormat(); + void SetSaveFormat(const CKBitmapProperties& props); CK_TEXTURE_SAVEOPTIONS GetSaveOptions(); void SetSaveOptions(CK_TEXTURE_SAVEOPTIONS opts); - VxMath::VX_PIXELFORMAT GetDesiredVideoFormat(); - void SetDesiredVideoFormat(VxMath::VX_PIXELFORMAT fmt); /** Summary: Enables or disables the color key transparency. @@ -106,16 +132,6 @@ namespace LibCmo::CK2 { */ bool IsTransparent(); /** - Summary: Returns the transparent color. - Return Value: - Color: A 32 bit ARGB transparent color. - Remarks: - + 0x00000000 (black) is the default transparent color. - - See also: SetTranparentColor,SetTransparent - */ - CKDWORD GetTransparentColor(); - /** Summary: Sets the transparent color. Arguments: Color: A 32 bit ARGB transparent color. @@ -128,18 +144,27 @@ namespace LibCmo::CK2 { See also: GetTranparentColor,SetTransparent */ void SetTransparentColor(CKDWORD col); + /** + Summary: Returns the transparent color. + Return Value: + Color: A 32 bit ARGB transparent color. + Remarks: + + 0x00000000 (black) is the default transparent color. + + See also: SetTranparentColor,SetTransparent + */ + CKDWORD GetTransparentColor(); protected: CKContext* m_Context; XContainer::XArray m_Slots; CKDWORD m_CurrentSlot; CKINT m_PickThreshold; - CKDWORD m_BitmapFlags; + CK_BITMAPDATA_FLAGS m_BitmapFlags; CKDWORD m_TransColor; CKBitmapProperties m_SaveProperties; CK_TEXTURE_SAVEOPTIONS m_SaveOptions; - VxMath::VX_PIXELFORMAT m_DesiredVideoFormat; }; } diff --git a/LibCmo/CK2/CKStateChunk.cpp b/LibCmo/CK2/CKStateChunk.cpp index a645a61..5b4a65a 100644 --- a/LibCmo/CK2/CKStateChunk.cpp +++ b/LibCmo/CK2/CKStateChunk.cpp @@ -585,11 +585,15 @@ namespace LibCmo::CK2 { } // read data - strl->resize(strByteSize); - if (!this->ReadByteData(strl->data(), strByteSize)) { + std::string cache; + cache.resize(strByteSize); + if (!this->ReadByteData(cache.data(), strByteSize)) { strl->clear(); return false; } + + // convert encoding + m_BindContext->GetUtf8String(cache, *strl); return true; } diff --git a/LibCmo/CK2/CKStateChunk.hpp b/LibCmo/CK2/CKStateChunk.hpp index 08eae81..b258b45 100644 --- a/LibCmo/CK2/CKStateChunk.hpp +++ b/LibCmo/CK2/CKStateChunk.hpp @@ -156,7 +156,7 @@ namespace LibCmo::CK2 { } /// - /// Read string + /// Read string. The content of string will automatically converted into UTF8 format. /// /// /// diff --git a/LibCmo/CK2/DataHandlers/CKBitmapHandler.hpp b/LibCmo/CK2/DataHandlers/CKBitmapHandler.hpp index 062669b..bd8abb9 100644 --- a/LibCmo/CK2/DataHandlers/CKBitmapHandler.hpp +++ b/LibCmo/CK2/DataHandlers/CKBitmapHandler.hpp @@ -79,6 +79,18 @@ namespace LibCmo::CK2::DataHandlers { }; + /** + * @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) { + CKBitmapHandler::ReleaseBitmapHandler(handler); + } + }; + class CKBitmapBMPHandler : public CKBitmapHandler { public: CKBitmapBMPHandler(); diff --git a/LibCmo/CK2/MgrImpls/CKPathManager.cpp b/LibCmo/CK2/MgrImpls/CKPathManager.cpp index 28b187e..949d753 100644 --- a/LibCmo/CK2/MgrImpls/CKPathManager.cpp +++ b/LibCmo/CK2/MgrImpls/CKPathManager.cpp @@ -94,5 +94,13 @@ namespace LibCmo::CK2::MgrImpls { return false; } + void CKPathManager::GetExtension(std::string& u8path) { + std::filesystem::path filepath; + EncodingHelper::U8PathToStdPath(filepath, u8path.c_str()); + + auto result = filepath.extension(); + EncodingHelper::StdPathToU8Path(u8path, result); + } + } diff --git a/LibCmo/CK2/MgrImpls/CKPathManager.hpp b/LibCmo/CK2/MgrImpls/CKPathManager.hpp index e34614e..309df0c 100644 --- a/LibCmo/CK2/MgrImpls/CKPathManager.hpp +++ b/LibCmo/CK2/MgrImpls/CKPathManager.hpp @@ -53,6 +53,12 @@ namespace LibCmo::CK2::MgrImpls { */ bool ResolveFileName(std::string& u8_filename); + /** + * @brief Returns the file extension including period (.) + * @param u8path[inout] The given path. overwritten by the gotten extension. set to blank when failed. + */ + void GetExtension(std::string& u8path); + protected: std::filesystem::path m_TempFolder; XContainer::XArray m_ExtraPathes; diff --git a/LibCmo/CK2/ObjImpls/CKTexture.cpp b/LibCmo/CK2/ObjImpls/CKTexture.cpp new file mode 100644 index 0000000..6f7aecd --- /dev/null +++ b/LibCmo/CK2/ObjImpls/CKTexture.cpp @@ -0,0 +1,24 @@ +#include "CKTexture.hpp" +#include "../CKStateChunk.hpp" + +namespace LibCmo::CK2::ObjImpls { + + CKTexture::CKTexture(CKContext* ctx, CK_ID ckid, CKSTRING name) {} + + CKTexture::~CKTexture() {} + + bool CKTexture::Save(CKStateChunk* chunk, CKFileVisitor* file, CKDWORD flags) { + bool suc = CKBeObject::Save(chunk, file, flags); + if (!suc) return false; + + return true; + } + + bool CKTexture::Load(CKStateChunk* chunk, CKFileVisitor* file) { + bool suc = CKBeObject::Load(chunk, file); + if (!suc) return false; + + return true; + } + +} diff --git a/LibCmo/CK2/ObjImpls/CKTexture.hpp b/LibCmo/CK2/ObjImpls/CKTexture.hpp new file mode 100644 index 0000000..e793762 --- /dev/null +++ b/LibCmo/CK2/ObjImpls/CKTexture.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include "../../VTAll.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(); + LIBCMO_DISABLE_COPY_MOVE(CKTexture); + + virtual CK_CLASSID GetClassID(void) override { + return CK_CLASSID::CKCID_TEXTURE; + } + // 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: + CKBitmapData m_ImageHost; + VxMath::VX_PIXELFORMAT m_VideoFormat; + bool m_UseMipMap; + }; + + //class CKRenderObject : public CKBeObject { + //public: + // CKRenderObject(CKContext* ctx, CK_ID ckid, CKSTRING name) : + // CKBeObject(ctx, ckid, name) + // {} + // virtual ~CKRenderObject() {} + // LIBCMO_DISABLE_COPY_MOVE(CKRenderObject); + + // virtual CK_CLASSID GetClassID(void) 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: + + //}; + +} diff --git a/LibCmo/LibCmo.vcxproj b/LibCmo/LibCmo.vcxproj index fbc7aac..4633b9d 100644 --- a/LibCmo/LibCmo.vcxproj +++ b/LibCmo/LibCmo.vcxproj @@ -190,6 +190,7 @@ + @@ -220,6 +221,7 @@ + diff --git a/LibCmo/LibCmo.vcxproj.filters b/LibCmo/LibCmo.vcxproj.filters index 7b4d72c..7fd3a1c 100644 --- a/LibCmo/LibCmo.vcxproj.filters +++ b/LibCmo/LibCmo.vcxproj.filters @@ -120,6 +120,9 @@ Sources\CK2 + + Sources\CK2\ObjImpls + @@ -212,5 +215,8 @@ Headers\CK2 + + Headers\CK2\ObjImpls + \ No newline at end of file diff --git a/LibCmo/VxMath/VxTypes.hpp b/LibCmo/VxMath/VxTypes.hpp index 8df55fc..8730225 100644 --- a/LibCmo/VxMath/VxTypes.hpp +++ b/LibCmo/VxMath/VxTypes.hpp @@ -79,11 +79,18 @@ namespace LibCmo::VxMath { class VxImageDescEx { public: VxImageDescEx(CK2::CKDWORD width, CK2::CKDWORD height) : - m_Width(width), m_Height(height), + m_Width(width), m_Height(height), //m_RedMask(0), m_GreenMask(0), m_BlueMask(0), m_AlphaMask(0), - m_Image(nullptr){ + m_Image(nullptr) { m_Image = new CK2::CKBYTE[GetImageSize()]; } + VxImageDescEx(const VxImageDescEx& rhs) : + m_Width(rhs.m_Width), m_Height(rhs.m_Height), + m_Image(nullptr) { + // copy image + m_Image = new CK2::CKBYTE[GetImageSize()]; + std::memcpy(m_Image, rhs.m_Image, GetImageSize()); + } ~VxImageDescEx() { delete[] m_Image; }