libcmo21/LibCmo/CK2/CKBitmapData.cpp

422 lines
12 KiB
C++
Raw Normal View History

#include "CKBitmapData.hpp"
2023-09-10 21:33:43 +08:00
#include "CKContext.hpp"
#include "CKStateChunk.hpp"
#include "DataHandlers/CKBitmapHandler.hpp"
#include "MgrImpls/CKPathManager.hpp"
#include <memory>
namespace LibCmo::CK2 {
2023-09-10 13:23:04 +08:00
#pragma region Assist RW Functions
2023-09-11 14:39:07 +08:00
bool CKBitmapData::ReadSpecificFormatBitmap(CKStateChunk* chk, VxMath::VxImageDescEx* slot) {
2023-09-11 22:20:45 +08:00
// read transparent prop
CKDWORD transprop;
chk->ReadStruct(transprop);
// get ext and guid to find correct guid
CKCHAR filerawext[4];
CKGUID fileguid;
chk->ReadNoSizeBuffer(CKSizeof(filerawext), 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
2023-09-18 16:37:05 +08:00
auto imgdata = chk->LockReadBufferWrapper(imgbytesize);
if (imgdata == nullptr) {
2023-09-11 22:20:45 +08:00
return false;
}
// parse image
VxMath::VxImageDescEx cache;
2023-09-18 16:37:05 +08:00
if (!reader->ReadMemory(imgdata.get(), imgbytesize, &cache)) {
2023-09-11 22:20:45 +08:00
return false;
}
2023-09-18 16:37:05 +08:00
// unlock buffer
imgdata.reset();
2023-09-11 22:20:45 +08:00
// post proc image (copy to slot)
VxMath::VxDoBlit(&cache, slot);
// proc image alpha
if (transprop == 2) {
CKDWORD alphacount;
chk->ReadStruct(alphacount);
if (alphacount == 1) {
CKDWORD globalalpha;
chk->ReadStruct(globalalpha);
VxMath::VxDoAlphaBlit(slot, static_cast<CKBYTE>(globalalpha));
} else {
2023-09-18 16:37:05 +08:00
auto alphabuf = chk->ReadBufferWrapper();
2023-09-11 22:20:45 +08:00
VxMath::VxDoAlphaBlit(slot, reinterpret_cast<CKBYTE*>(alphabuf.get()));
}
}
}
return true;
2023-09-10 13:23:04 +08:00
}
2023-09-11 14:39:07 +08:00
bool CKBitmapData::ReadRawBitmap(CKStateChunk* chk, VxMath::VxImageDescEx* slot) {
2023-09-11 22:20:45 +08:00
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
2023-09-18 16:37:05 +08:00
CKStateChunk::Buffer_t redBuffer, greenBuffer, blueBuffer, alphaBuffer;
2023-09-11 22:20:45 +08:00
CKDWORD bufsize;
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 {
2023-09-18 16:37:05 +08:00
blueBuffer = chk->ReadBufferWrapper();
greenBuffer = chk->ReadBufferWrapper();
redBuffer = chk->ReadBufferWrapper();
2023-09-11 22:20:45 +08:00
}
2023-09-18 16:37:05 +08:00
alphaBuffer = chk->ReadBufferWrapper();
2023-09-11 22:20:45 +08:00
// 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 = reinterpret_cast<CKBYTE*>(redBuffer.get()),
* greenSrc = reinterpret_cast<CKBYTE*>(greenBuffer.get()),
* blueSrc = reinterpret_cast<CKBYTE*>(blueBuffer.get()),
* alphaSrc = reinterpret_cast<CKBYTE*>(alphaBuffer.get());
for (CKDWORD p = 0; p < pixelcount; ++p) {
2023-09-12 20:49:19 +08:00
*(dst++) = *(blueSrc++);
*(dst++) = *(greenSrc++);
*(dst++) = *(redSrc++);
2023-09-11 22:20:45 +08:00
// if no alpha data, set to 0xFF
*(dst++) = (alphaBuffer != nullptr ? (*(alphaSrc++)) : 0xFFu);
}
}
return true;
2023-09-10 13:23:04 +08:00
}
2023-09-11 14:39:07 +08:00
bool CKBitmapData::ReadOldRawBitmap(CKStateChunk* chk, VxMath::VxImageDescEx* slot) {
2023-09-11 22:20:45 +08:00
// MARK: not supported because all of my work do not involve this function.
2023-09-10 13:23:04 +08:00
return false;
}
2023-09-11 14:39:07 +08:00
void CKBitmapData::WriteSpecificFormatBitmap(CKStateChunk* chk, const VxMath::VxImageDescEx* slot) {
2023-09-10 13:23:04 +08:00
}
2023-09-11 14:39:07 +08:00
void CKBitmapData::WriteRawBitmap(CKStateChunk* chk, const VxMath::VxImageDescEx* slot) {
2023-09-10 13:23:04 +08:00
}
#pragma endregion
2023-09-10 21:33:43 +08:00
#pragma region Core Read / Write
2023-09-10 13:23:04 +08:00
2023-09-10 21:33:43 +08:00
bool CKBitmapData::ReadFromChunk(CKStateChunk* chunk, CKFileVisitor* file, const CKBitmapDataReadIdentifiers& identifiers) {
2023-09-12 17:03:06 +08:00
XContainer::XBitArray hasReadSlot;
2023-09-10 21:33:43 +08:00
// check 3 types enbedded image
2023-09-11 22:20:45 +08:00
// 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.
2023-09-10 21:33:43 +08:00
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);
2023-09-12 17:03:06 +08:00
hasReadSlot.resize(slotcount, false);
2023-09-10 21:33:43 +08:00
2023-09-11 22:20:45 +08:00
// 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.
2023-09-10 21:33:43 +08:00
if (width > 0 && height > 0) {
for (CKDWORD i = 0; i < slotcount; ++i) {
CreateImage(width, height, i);
2023-09-11 14:39:07 +08:00
if (ReadSpecificFormatBitmap(chunk, GetImageDesc(i))) {
XContainer::NSXBitArray::Set(hasReadSlot, i);
2023-09-10 21:33:43 +08:00
} else {
ReleaseImage(i);
}
}
}
} else if (chunk->SeekIdentifierDword(identifiers.m_RawData)) {
// raw data
CKDWORD slotcount;
chunk->ReadStruct(slotcount);
SetSlotCount(slotcount);
2023-09-12 17:03:06 +08:00
hasReadSlot.resize(slotcount, false);
2023-09-10 21:33:43 +08:00
2023-09-11 22:20:45 +08:00
// 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.
2023-09-10 21:33:43 +08:00
for (CKDWORD i = 0; i < slotcount; ++i) {
2023-09-11 22:20:45 +08:00
VxMath::VxImageDescEx rawcache;
if (ReadRawBitmap(chunk, &rawcache)) {
XContainer::NSXBitArray::Set(hasReadSlot, i);
2023-09-11 22:20:45 +08:00
// do upside down blit
CreateImage(rawcache.GetWidth(), rawcache.GetHeight(), i);
VxMath::VxDoBlitUpsideDown(&rawcache, GetImageDesc(i));
2023-09-10 21:33:43 +08:00
}
}
} else if (chunk->SeekIdentifierDword(identifiers.m_OldRawData)) {
// raw data (old format)
CKDWORD slotcount;
chunk->ReadStruct(slotcount);
SetSlotCount(slotcount);
2023-09-12 17:03:06 +08:00
hasReadSlot.resize(slotcount, false);
2023-09-10 21:33:43 +08:00
2023-09-11 22:20:45 +08:00
// MARK: a rough implement because we do not support this identifier
2023-09-10 21:33:43 +08:00
for (CKDWORD i = 0; i < slotcount; ++i) {
2023-09-11 14:39:07 +08:00
if (ReadOldRawBitmap(chunk, GetImageDesc(i))) {
XContainer::NSXBitArray::Set(hasReadSlot, i);
2023-09-10 21:33:43 +08:00
} 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) {
2023-09-16 18:31:25 +08:00
XContainer::XString filename;
2023-09-10 21:33:43 +08:00
chunk->ReadString(filename);
2023-09-11 22:20:45 +08:00
if (filename.empty()) continue;
bool isNotLoaded = (i >= hasReadSlot.size()) || (!XContainer::NSXBitArray::IsSet(hasReadSlot, i));
2023-09-11 22:20:45 +08:00
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());
2023-09-10 21:33:43 +08:00
}
2023-09-11 22:20:45 +08:00
2023-09-10 21:33:43 +08:00
}
}
// movie info
if (chunk->SeekIdentifierDword(identifiers.m_MovieFileName)) {
// MARK: movie is not implemented here.
}
2023-09-11 22:20:45 +08:00
return true;
2023-09-10 21:33:43 +08:00
}
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<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 (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];
2023-09-11 14:39:07 +08:00
slotdata.m_ImageData.CreateImage(Width, Height);
VxMath::VxDoAlphaBlit(&slotdata.m_ImageData, 0xFFu);
2023-09-10 21:33:43 +08:00
}
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
2023-09-16 18:31:25 +08:00
XContainer::XString ext(filename);
2023-09-10 21:33:43 +08:00
m_Context->GetPathManager()->GetExtension(ext);
2023-09-11 22:20:45 +08:00
auto reader = DataHandlers::CKBitmapHandler::GetBitmapHandlerWrapper(CKFileExtension(ext.c_str()), CKGUID());
2023-09-10 21:33:43 +08:00
if (reader == nullptr) return false;
2023-09-11 22:20:45 +08:00
// get desc and read data
if (!reader->ReadFile(filename, GetImageDesc(slot))) {
2023-09-10 21:33:43 +08:00
return false;
}
return true;
}
2023-09-11 22:20:45 +08:00
bool CKBitmapData::SaveImage(CKSTRING filename, CKDWORD slot, bool isForceThisFmt) {
2023-09-10 21:33:43 +08:00
if (filename == nullptr) return false;
if (slot >= m_Slots.size()) return false;
2023-09-11 22:20:45 +08:00
// prepare save format
CKBitmapProperties savefmt;
if (isForceThisFmt) {
savefmt = this->m_SaveProperties;
} else {
2023-09-16 18:31:25 +08:00
XContainer::XString ext(filename);
2023-09-11 22:20:45 +08:00
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;
}
2023-09-10 21:33:43 +08:00
return true;
}
VxMath::VxImageDescEx* CKBitmapData::GetImageDesc(CKDWORD slot) {
if (slot >= m_Slots.size()) return nullptr;
2023-09-11 14:39:07 +08:00
return &m_Slots[slot].m_ImageData;
2023-09-10 21:33:43 +08:00
}
void CKBitmapData::ReleaseImage(CKDWORD slot) {
if (slot >= m_Slots.size()) return;
2023-09-11 14:39:07 +08:00
m_Slots[slot].m_ImageData.FreeImage();
2023-09-10 21:33:43 +08:00
}
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) {
2023-09-11 22:20:45 +08:00
if (Transparency) {
EnumsHelper::Add(m_BitmapFlags, CK_BITMAPDATA_FLAGS::CKBITMAPDATA_TRANSPARENT);
} else {
EnumsHelper::Rm(m_BitmapFlags, CK_BITMAPDATA_FLAGS::CKBITMAPDATA_TRANSPARENT);
}
2023-09-10 21:33:43 +08:00
}
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;
}
2023-09-12 17:03:06 +08:00
void CKBitmapData::SetPickThreshold(CKDWORD threshold) {
m_PickThreshold = threshold;
}
CKDWORD CKBitmapData::GetPickThreshold() {
return m_PickThreshold;
}
2023-09-10 21:33:43 +08:00
#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() {}
}