2023-09-10 21:33:43 +08:00
# include "CKTexture.hpp"
# include "../CKStateChunk.hpp"
2023-10-08 10:42:07 +08:00
# include "../CKContext.hpp"
# include "../MgrImpls/CKPathManager.hpp"
2023-09-10 21:33:43 +08:00
namespace LibCmo : : CK2 : : ObjImpls {
2023-09-12 17:03:06 +08:00
/**
* @ 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 .
2023-09-13 22:33:41 +08:00
* @ 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 .
2023-09-12 17:03:06 +08:00
*/
2024-08-23 17:38:45 +08:00
# pragma pack(4)
2023-09-12 17:03:06 +08:00
struct FakeBitmapProperties {
CKINT m_Size ;
struct {
// fake CKGUID
CKDWORD d1 , d2 ;
} m_ReaderGuid ;
struct {
// fake CKFileExtension
2024-08-23 17:38:45 +08:00
char m_Data [ 4 ] ;
2023-09-12 17:03:06 +08:00
} m_Ext ;
struct {
// fake VxImageDescEx
2023-09-16 18:31:25 +08:00
CKINT Size ; ///< Size of the structure
CKDWORD Flags ; ///< Reserved for special formats (such as compressed ) 0 otherwise
2023-09-12 17:03:06 +08:00
2023-09-16 18:31:25 +08:00
CKINT Width ; ///< Width in pixel of the image
CKINT Height ; ///< Height in pixel of the image
2023-09-12 17:03:06 +08:00
union {
2023-09-16 18:31:25 +08:00
CKINT BytesPerLine ; ///< Pitch (width in bytes) of the image
CKINT TotalImageSize ; ///< For compressed image (DXT1...) the total size of the image
2023-09-12 17:03:06 +08:00
} ;
2023-09-16 18:31:25 +08:00
CKINT BitsPerPixel ; ///< Number of bits per pixel
2023-09-12 17:03:06 +08:00
union {
2023-09-16 18:31:25 +08:00
CKDWORD RedMask ; ///< Mask for Red component
CKDWORD BumpDuMask ; ///< Mask for Bump Du component
2023-09-12 17:03:06 +08:00
} ;
union {
2023-09-16 18:31:25 +08:00
CKDWORD GreenMask ; ///< Mask for Green component
CKDWORD BumpDvMask ; ///< Mask for Bump Dv component
2023-09-12 17:03:06 +08:00
} ;
union {
2023-09-16 18:31:25 +08:00
CKDWORD BlueMask ; ///< Mask for Blue component
CKDWORD BumpLumMask ; ///< Mask for Luminance component
2023-09-12 17:03:06 +08:00
} ;
2023-09-16 18:31:25 +08:00
CKDWORD AlphaMask ; ///< Mask for Alpha component
2023-09-12 17:03:06 +08:00
2023-09-16 18:31:25 +08:00
CKWORD BytesPerColorEntry ; ///< ColorMap Stride
CKWORD ColorMapEntries ; ///< If other than 0 image is palletized
2023-09-12 17:03:06 +08:00
2023-09-16 18:31:25 +08:00
/*CKBYTE**/ CKPTR ColorMap ; ///< Palette colors
/*CKBYTE**/ CKPTR Image ; ///< Image
2023-09-12 17:03:06 +08:00
} m_Format ;
2023-09-16 18:31:25 +08:00
/*void**/ CKPTR m_Data ;
2023-09-12 17:03:06 +08:00
} ;
2024-08-23 17:38:45 +08:00
# pragma pack()
2023-09-12 17:03:06 +08:00
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 ( ) { }
2023-09-10 21:33:43 +08:00
CKTexture : : ~ CKTexture ( ) { }
bool CKTexture : : Save ( CKStateChunk * chunk , CKFileVisitor * file , CKDWORD flags ) {
bool suc = CKBeObject : : Save ( chunk , file , flags ) ;
if ( ! suc ) return false ;
2023-09-30 11:51:04 +08:00
// 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
2023-09-30 16:01:39 +08:00
mixdata | = static_cast < CKDWORD > ( m_ImageHost . GetSaveOptions ( ) ) & 0xFF ;
2023-09-30 11:51:04 +08:00
mixdata < < = 8 ;
// mix flags
CKDWORD mixflags = 0 ;
if ( m_ImageHost . IsTransparent ( ) ) {
2023-09-30 16:01:39 +08:00
mixflags | = 0x1 ;
2023-09-30 11:51:04 +08:00
}
if ( m_VideoFormat ! = VxMath : : VX_PIXELFORMAT : : UNKNOWN_PF ) {
2023-09-30 16:01:39 +08:00
mixflags | = 0x2 ;
2023-09-30 11:51:04 +08:00
}
if ( m_ImageHost . IsCubeMap ( ) ) {
2023-09-30 16:01:39 +08:00
mixflags | = 0x4 ;
2023-09-30 11:51:04 +08:00
}
2023-09-30 16:01:39 +08:00
mixdata | = mixflags & 0xFF ;
2023-09-30 11:51:04 +08:00
mixdata < < = 8 ;
// mipmap
2023-09-30 16:01:39 +08:00
mixdata | = ( IsUseMipmap ( ) ? 0xFF : 0 ) ;
2023-09-30 11:51:04 +08:00
// 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 ) ;
2023-09-30 14:24:37 +08:00
props . m_Data = 0 ;
2023-09-30 11:51:04 +08:00
// 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 ( ) ;
2023-09-30 14:24:37 +08:00
props . m_Format . BytesPerLine = VxMath : : VxImageDescEx : : PixelSize * props . m_Format . Width ;
2023-09-30 11:51:04 +08:00
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 ;
2024-08-23 17:38:45 +08:00
std : : string ext ;
2024-08-27 11:25:53 +08:00
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. " ) ;
2023-09-30 11:51:04 +08:00
std : : memcpy (
2024-08-23 17:38:45 +08:00
props . m_Ext . m_Data ,
ext . c_str ( ) ,
std : : min ( CKSizeof ( props . m_Ext . m_Data ) - CKDWORD_C ( 1 ) , static_cast < CKDWORD > ( ext . size ( ) ) )
2023-09-30 11:51:04 +08:00
) ;
// write fake one
chunk - > WriteBuffer ( & props , CKSizeof ( props ) ) ;
}
chunk - > SetClassId ( CK_CLASSID : : CKCID_TEXTURE ) ;
2023-09-10 21:33:43 +08:00
return true ;
}
bool CKTexture : : Load ( CKStateChunk * chunk , CKFileVisitor * file ) {
bool suc = CKBeObject : : Load ( chunk , file ) ;
if ( ! suc ) return false ;
2023-09-12 17:03:06 +08:00
// 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 ;
2023-09-22 22:31:51 +08:00
// 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 ) ) {
2023-09-22 14:48:45 +08:00
// 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)
2023-09-12 17:03:06 +08:00
CKDWORD mixdata ;
chunk - > ReadStruct ( mixdata ) ;
2023-09-22 22:31:51 +08:00
// set mipmap
2023-09-30 11:51:04 +08:00
UseMipmap ( mixdata & 0xFF ) ;
2023-09-22 22:31:51 +08:00
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 ;
2023-09-12 17:03:06 +08:00
// set current slot, transparent color, and video format.
2023-09-17 10:38:46 +08:00
CKDWORD currentSlot , transColor ;
2023-09-12 17:03:06 +08:00
fmtbytesize - = CKSizeof ( CKDWORD ) ;
switch ( fmtbytesize ) {
2023-09-22 22:31:51 +08:00
case ( 3 * CKSizeof ( CKDWORD ) ) :
2023-09-12 17:03:06 +08:00
chunk - > ReadStruct ( transColor ) ;
m_ImageHost . SetTransparentColor ( transColor ) ;
chunk - > ReadStruct ( currentSlot ) ;
m_ImageHost . SetCurrentSlot ( currentSlot ) ;
chunk - > ReadStruct ( m_VideoFormat ) ;
break ;
2023-09-22 22:31:51 +08:00
case ( 2 * CKSizeof ( CKDWORD ) ) :
2023-09-12 17:03:06 +08:00
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 ;
2023-09-22 22:31:51 +08:00
case ( CKSizeof ( CKDWORD ) ) :
2023-09-12 17:03:06 +08:00
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 ) ;
2023-09-30 11:51:04 +08:00
SetMipmapLevel ( mipmapCount ) ;
2023-09-12 17:03:06 +08:00
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 ) ) {
2023-09-18 16:37:05 +08:00
auto buf = chunk - > ReadBufferWrapper ( ) ;
2023-09-12 17:03:06 +08:00
if ( buf ! = nullptr ) {
2023-09-20 10:49:32 +08:00
FakeBitmapProperties * props = static_cast < FakeBitmapProperties * > ( buf . get ( ) ) ;
2023-09-12 17:03:06 +08:00
2024-08-23 17:38:45 +08:00
// get utf8 extension
XContainer : : XString ext ;
2024-08-27 11:25:53 +08:00
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. " ) ;
2024-08-23 17:38:45 +08:00
// get my bitmap prop
2023-09-12 17:03:06 +08:00
CKBitmapProperties myprops (
CKGUID ( props - > m_ReaderGuid . d1 , props - > m_ReaderGuid . d2 ) ,
2024-08-23 17:38:45 +08:00
ext . c_str ( )
2023-09-12 17:03:06 +08:00
) ;
m_ImageHost . SetSaveFormat ( myprops ) ;
}
}
}
// correct video format
if ( m_VideoFormat > VxMath : : VX_PIXELFORMAT : : _32_X8L8V8U8 ) {
m_VideoFormat = VxMath : : VX_PIXELFORMAT : : _16_ARGB1555 ;
}
2023-09-10 21:33:43 +08:00
return true ;
}
2023-09-22 14:48:45 +08:00
# pragma region Visitor
CKBitmapData & CKTexture : : GetUnderlyingData ( ) {
return m_ImageHost ;
}
2023-10-08 10:42:07 +08:00
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 ) ) ;
}
2023-09-22 14:48:45 +08:00
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
2023-09-10 21:33:43 +08:00
}