From eedd9bdb55d82effad566924dd95a4e4843886a3 Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Sat, 30 Sep 2023 11:51:04 +0800 Subject: [PATCH] finish CKtexture writer - finish CKTexture writer - add std::enable_if for CKStateChunk::ReadIdent&WriteIdent to limit it in enum type. - fix error function calling in CKBitmapData writer. - let CKTexture reader mor formal for mipmap. - fix CKStateChunk ReadBuffer redirect guideline. - add a stupid height and width getter for CKBitmapData to serve CKTexture using. --- LibCmo/CK2/CKBitmapData.cpp | 51 +++++++----- LibCmo/CK2/CKBitmapData.hpp | 11 +++ LibCmo/CK2/CKStateChunk.hpp | 26 +++--- LibCmo/CK2/CKStateChunkWriter.cpp | 5 ++ LibCmo/CK2/ObjImpls/CKTexture.cpp | 127 +++++++++++++++++++++++++++++- Unvirt/.editorconfig | 69 ---------------- 6 files changed, 189 insertions(+), 100 deletions(-) delete mode 100644 Unvirt/.editorconfig diff --git a/LibCmo/CK2/CKBitmapData.cpp b/LibCmo/CK2/CKBitmapData.cpp index 06b91af..4d9eef0 100644 --- a/LibCmo/CK2/CKBitmapData.cpp +++ b/LibCmo/CK2/CKBitmapData.cpp @@ -458,40 +458,35 @@ namespace LibCmo::CK2 { // save data if (saveopt == CK_TEXTURE_SAVEOPTIONS::CKTEXTURE_RAWDATA) { // save as raw data - chunk->WriteIdentifier(identifiers.m_RawData); + chunk->WriteIdentifierDword(identifiers.m_RawData); chunk->WriteStruct(slotcount); VxMath::VxImageDescEx invalidDesc; for (CKDWORD i = 0; i < slotcount; ++i) { - if (XContainer::NSXBitArray::IsSet(validExternalSavingSlot, 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 - WriteRawBitmap(chunk, GetImageDesc(i)); + // 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->WriteIdentifier(identifiers.m_SpecificFormat); + chunk->WriteIdentifierDword(identifiers.m_SpecificFormat); chunk->WriteStruct(slotcount); - // prepare height, width, bpp data - CKDWORD height = 0, width = 0, bpp = 32; - for (CKDWORD i = 0; i < slotcount; ++i) { - VxMath::VxImageDescEx* desc = GetImageDesc(i); - if (desc->IsValid()) { - height = desc->GetHeight(); - width = desc->GetWidth(); - break; - } - } - // write it - chunk->WriteStruct(width); - chunk->WriteStruct(height); - chunk->WriteStruct(bpp); + // 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) { @@ -502,7 +497,7 @@ namespace LibCmo::CK2 { // write filename { - chunk->WriteIdentifier(identifiers.m_FileNames); + chunk->WriteIdentifierDword(identifiers.m_FileNames); chunk->WriteStruct(slotcount); XContainer::XString filename; @@ -629,6 +624,24 @@ namespace LibCmo::CK2 { else return m_Slots[slot].m_FileName.c_str(); } + 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 diff --git a/LibCmo/CK2/CKBitmapData.hpp b/LibCmo/CK2/CKBitmapData.hpp index 830f5e6..9354b21 100644 --- a/LibCmo/CK2/CKBitmapData.hpp +++ b/LibCmo/CK2/CKBitmapData.hpp @@ -106,6 +106,17 @@ namespace LibCmo::CK2 { */ 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 diff --git a/LibCmo/CK2/CKStateChunk.hpp b/LibCmo/CK2/CKStateChunk.hpp index c35c9ea..8079a73 100644 --- a/LibCmo/CK2/CKStateChunk.hpp +++ b/LibCmo/CK2/CKStateChunk.hpp @@ -3,6 +3,7 @@ #include "../VTAll.hpp" #include #include +#include namespace LibCmo::CK2 { @@ -268,11 +269,11 @@ namespace LibCmo::CK2 { public: bool SeekIdentifierDword(CKDWORD identifier); bool SeekIdentifierDwordAndReturnSize(CKDWORD identifier, CKDWORD* out_size); - template + template, int> = 0> inline bool SeekIdentifier(TEnum enum_v) { return SeekIdentifierDword(static_cast(enum_v)); } - template + template, int> = 0> inline bool SeekIdentifierAndReturnSize(TEnum enum_v, CKDWORD* out_size) { return SeekIdentifierDwordAndReturnSize(static_cast(enum_v), out_size); } @@ -403,18 +404,21 @@ namespace LibCmo::CK2 { /* Buffer related function implements: - ReadBuffer(void**) Read Byte based size. -> ReadAndCopyBuffer(void**, CKDWORD*) - ReadAndFillBuffer(int, void*) User give Byte based size. -> ReadBuffer(const void**, CKDWORD) - ReadAndFillBuffer(void*) Read Byte based size. -> ReadBuffer(const void**, CKDWORD*) - ReadAndFillBuffer_LEndian(int, void*) User give Byte based size. -> ReadBuffer(const void**, CKDWORD) - ReadAndFillBuffer_LEndian(void*) Read Byte based size. -> ReadBuffer(const void**, CKDWORD*) - ReadAndFillBuffer_LEndian16(int, void*) User give Byte based size. -> ReadBuffer(const void**, CKDWORD) - ReadAndFillBuffer_LEndian16(void*) Read Byte based size. -> ReadBuffer(const void**, CKDWORD*) + 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*). * @@ -454,6 +458,7 @@ namespace LibCmo::CK2 { * @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. @@ -467,6 +472,7 @@ namespace LibCmo::CK2 { * @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 @@ -553,7 +559,7 @@ namespace LibCmo::CK2 { public: bool WriteIdentifierDword(CKDWORD identifier); - template + template, int> = 0> inline bool WriteIdentifier(TEnum enum_v) { return WriteIdentifierDword(static_cast(enum_v)); } diff --git a/LibCmo/CK2/CKStateChunkWriter.cpp b/LibCmo/CK2/CKStateChunkWriter.cpp index 5b6714f..ee4aafc 100644 --- a/LibCmo/CK2/CKStateChunkWriter.cpp +++ b/LibCmo/CK2/CKStateChunkWriter.cpp @@ -22,6 +22,11 @@ namespace LibCmo::CK2 { // 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; diff --git a/LibCmo/CK2/ObjImpls/CKTexture.cpp b/LibCmo/CK2/ObjImpls/CKTexture.cpp index 4ccc616..21062f8 100644 --- a/LibCmo/CK2/ObjImpls/CKTexture.cpp +++ b/LibCmo/CK2/ObjImpls/CKTexture.cpp @@ -70,6 +70,129 @@ namespace LibCmo::CK2::ObjImpls { 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(CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_TEXREADER), + .m_RawData = static_cast(CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_TEXCOMPRESSED), + .m_FileNames = static_cast(CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_TEXFILENAMES), + .m_MovieFileName = static_cast(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(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 = 6172; + + // setup fake VxImageDescEx + props.m_Format.Size = CKSizeof(props.m_Format); + props.m_Format.Flags = static_cast(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.Height * 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::memcpy( + props.m_Ext.m_Data, + realprops.m_Ext.GetExt(), + std::min(CKSizeof(props.m_Ext.m_Data), realprops.m_Ext.GetSize()) + ); + + // write fake one + chunk->WriteBuffer(&props, CKSizeof(props)); + } + + chunk->SetClassId(CK_CLASSID::CKCID_TEXTURE); return true; } @@ -104,7 +227,7 @@ namespace LibCmo::CK2::ObjImpls { CKDWORD mixdata; chunk->ReadStruct(mixdata); // set mipmap - m_UseMipMap = (mixdata & 0xFF); + UseMipmap(mixdata & 0xFF); mixdata >>= 8; // mix flags CKDWORD mixflags = mixdata & 0xFF; @@ -159,7 +282,7 @@ namespace LibCmo::CK2::ObjImpls { if (chunk->SeekIdentifier(CK_STATESAVEFLAGS_TEXTURE::CK_STATESAVE_USERMIPMAP)) { CKDWORD mipmapCount; chunk->ReadStruct(mipmapCount); - m_MipmapImages.resize(mipmapCount); + SetMipmapLevel(mipmapCount); for (CKDWORD i = 0; i < mipmapCount; ++i) { VxMath::VxImageDescEx cache; diff --git a/Unvirt/.editorconfig b/Unvirt/.editorconfig deleted file mode 100644 index 2d0456f..0000000 --- a/Unvirt/.editorconfig +++ /dev/null @@ -1,69 +0,0 @@ -# Visual Studio 生成了具有 C++ 设置的 .editorconfig 文件。 -root = true - -[*.{c++,cc,cpp,cppm,cxx,h,h++,hh,hpp,hxx,inl,ipp,ixx,tlh,tli}] - -# Visual C++ 代码样式设置 - -cpp_generate_documentation_comments = doxygen_slash_star - -# Visual C++ 格式设置 - -cpp_indent_braces = false -cpp_indent_multi_line_relative_to = statement_begin -cpp_indent_within_parentheses = indent -cpp_indent_preserve_within_parentheses = true -cpp_indent_case_contents = true -cpp_indent_case_labels = true -cpp_indent_case_contents_when_block = false -cpp_indent_lambda_braces_when_parameter = true -cpp_indent_goto_labels = one_left -cpp_indent_preprocessor = leftmost_column -cpp_indent_access_specifiers = false -cpp_indent_namespace_contents = true -cpp_indent_preserve_comments = false -cpp_new_line_before_open_brace_namespace = same_line -cpp_new_line_before_open_brace_type = same_line -cpp_new_line_before_open_brace_function = same_line -cpp_new_line_before_open_brace_block = same_line -cpp_new_line_before_open_brace_lambda = same_line -cpp_new_line_scope_braces_on_separate_lines = true -cpp_new_line_close_brace_same_line_empty_type = true -cpp_new_line_close_brace_same_line_empty_function = true -cpp_new_line_before_catch = false -cpp_new_line_before_else = false -cpp_new_line_before_while_in_do_while = false -cpp_space_before_function_open_parenthesis = remove -cpp_space_within_parameter_list_parentheses = false -cpp_space_between_empty_parameter_list_parentheses = false -cpp_space_after_keywords_in_control_flow_statements = true -cpp_space_within_control_flow_statement_parentheses = false -cpp_space_before_lambda_open_parenthesis = false -cpp_space_within_cast_parentheses = false -cpp_space_after_cast_close_parenthesis = false -cpp_space_within_expression_parentheses = false -cpp_space_before_block_open_brace = true -cpp_space_between_empty_braces = false -cpp_space_before_initializer_list_open_brace = true -cpp_space_within_initializer_list_braces = true -cpp_space_preserve_in_initializer_list = true -cpp_space_before_open_square_bracket = false -cpp_space_within_square_brackets = false -cpp_space_before_empty_square_brackets = false -cpp_space_between_empty_square_brackets = false -cpp_space_group_square_brackets = true -cpp_space_within_lambda_brackets = false -cpp_space_between_empty_lambda_brackets = false -cpp_space_before_comma = false -cpp_space_after_comma = true -cpp_space_remove_around_member_operators = true -cpp_space_before_inheritance_colon = true -cpp_space_before_constructor_colon = true -cpp_space_remove_before_semicolon = true -cpp_space_after_semicolon = true -cpp_space_remove_around_unary_operator = true -cpp_space_around_binary_operator = insert -cpp_space_around_assignment_operator = insert -cpp_space_pointer_reference_alignment = left -cpp_space_around_ternary_operator = insert -cpp_wrap_preserve_blocks = one_liners