diff --git a/src/ImageConverter/ImageConverter.cpp b/src/ImageConverter/ImageConverter.cpp index 1c0a8623..7f180876 100644 --- a/src/ImageConverter/ImageConverter.cpp +++ b/src/ImageConverter/ImageConverter.cpp @@ -1,11 +1,17 @@ #include "ImageConverter.h" +#include "Image/DdsLoader.h" #include "Image/DdsWriter.h" #include "Image/IwiLoader.h" +#include "Image/IwiWriter13.h" +#include "Image/IwiWriter27.h" +#include "Image/IwiWriter6.h" +#include "Image/IwiWriter8.h" #include "Image/Texture.h" #include "ImageConverterArgs.h" #include "Utils/StringUtils.h" +#include #include #include #include @@ -16,19 +22,7 @@ namespace fs = std::filesystem; namespace image_converter { constexpr auto EXTENSION_IWI = ".iwi"; - - class ImageLoader - { - public: - ImageLoader() = default; - virtual ~ImageLoader() = default; - ImageLoader(const ImageLoader& other) = default; - ImageLoader(ImageLoader&& other) noexcept = default; - ImageLoader& operator=(const ImageLoader& other) = default; - ImageLoader& operator=(ImageLoader&& other) noexcept = default; - - // virtual Texture* - }; + constexpr auto EXTENSION_DDS = ".dds"; class ImageConverterImpl final : public ImageConverter { @@ -63,13 +57,11 @@ namespace image_converter utils::MakeStringLowerCase(extension); if (extension == EXTENSION_IWI) - { ConvertIwi(filePath); - } + else if (extension == EXTENSION_DDS) + ConvertDds(filePath); else - { std::cerr << std::format("Unsupported extension {}\n", extension); - } } bool ConvertIwi(const fs::path& iwiPath) @@ -99,9 +91,108 @@ namespace image_converter return true; } + bool ConvertDds(const fs::path& ddsPath) + { + std::ifstream file(ddsPath, std::ios::in | std::ios::binary); + if (!file.is_open()) + { + std::cerr << std::format("Failed to open input file {}\n", ddsPath.string()); + return false; + } + + const auto texture = dds::LoadDds(file); + if (!texture) + return false; + + if (!EnsureIwiWriterIsPresent()) + return false; + + auto outPath = ddsPath; + outPath.replace_extension(".iwi"); + + std::ofstream outFile(outPath, std::ios::out | std::ios::binary); + if (!outFile.is_open()) + { + std::cerr << std::format("Failed to open output file {}\n", outPath.string()); + return false; + } + + m_iwi_writer->DumpImage(outFile, texture.get()); + return true; + } + + bool EnsureIwiWriterIsPresent() + { + if (m_iwi_writer) + return true; + + if (m_game_to_convert_to == Game::UNKNOWN && !ShowGameTui()) + return false; + + switch (m_game_to_convert_to) + { + case Game::IW3: + m_iwi_writer = std::make_unique(); + break; + case Game::IW4: + case Game::IW5: + m_iwi_writer = std::make_unique(); + break; + case Game::T5: + m_iwi_writer = std::make_unique(); + break; + case Game::T6: + m_iwi_writer = std::make_unique(); + break; + default: + assert(false); + return false; + } + + return true; + } + + bool ShowGameTui() + { + std::cout << "Select the game to convert to:\n"; + std::cout << " 1 - Call Of Duty 4: Modern Warfare (IW3)\n"; + std::cout << " 2 - Call Of Duty: Modern Warfare 2 (IW4)\n"; + std::cout << " 3 - Call Of Duty: Modern Warfare 3 (IW5)\n"; + std::cout << " 4 - Call Of Duty: Black Ops (T5)\n"; + std::cout << " 5 - Call Of Duty: Black Ops 2 (T6)\n"; + + unsigned num; + std::cin >> num; + + switch (num) + { + case 1: + m_game_to_convert_to = Game::IW3; + break; + case 2: + m_game_to_convert_to = Game::IW4; + break; + case 3: + m_game_to_convert_to = Game::IW5; + break; + case 4: + m_game_to_convert_to = Game::T5; + break; + case 5: + m_game_to_convert_to = Game::T6; + break; + default: + std::cerr << "Invalid input\n"; + return false; + } + + return true; + } + ImageConverterArgs m_args; image_converter::Game m_game_to_convert_to; DdsWriter m_dds_writer; + std::unique_ptr m_iwi_writer; }; } // namespace image_converter diff --git a/src/ObjLoading/Game/IW3/AssetLoaders/AssetLoaderGfxImage.cpp b/src/ObjLoading/Game/IW3/AssetLoaders/AssetLoaderGfxImage.cpp index c65d75d3..7daaf720 100644 --- a/src/ObjLoading/Game/IW3/AssetLoaders/AssetLoaderGfxImage.cpp +++ b/src/ObjLoading/Game/IW3/AssetLoaders/AssetLoaderGfxImage.cpp @@ -40,12 +40,10 @@ bool AssetLoaderGfxImage::LoadFromRaw( if (!file.IsOpen()) return false; - const DdsLoader ddsLoader(zone->GetMemory()); - auto* texture = ddsLoader.LoadDds(*file.m_stream); - - if (texture == nullptr) + const auto texture = dds::LoadDds(*file.m_stream); + if (!texture) { - std::cout << std::format("Failed to load dds file for image asset \"{}\"\n", assetName); + std::cerr << std::format("Failed to load dds file for image asset \"{}\"\n", assetName); return false; } diff --git a/src/ObjLoading/Image/DdsLoader.cpp b/src/ObjLoading/Image/DdsLoader.cpp index d8dc118c..bc9e0c99 100644 --- a/src/ObjLoading/Image/DdsLoader.cpp +++ b/src/ObjLoading/Image/DdsLoader.cpp @@ -4,273 +4,268 @@ #include "Utils/ClassUtils.h" #include "Utils/FileUtils.h" +#include #include +#include -class DdsLoaderInternal +namespace dds { - static constexpr auto DDS_MAGIC = FileUtils::MakeMagic32('D', 'D', 'S', ' '); + class DdsLoaderInternal + { + static constexpr auto DDS_MAGIC = FileUtils::MakeMagic32('D', 'D', 'S', ' '); - MemoryManager* m_memory_manager; - std::istream& m_stream; + std::istream& m_stream; - TextureType m_texture_type; - bool m_has_mip_maps; - size_t m_width; - size_t m_height; - size_t m_depth; - const ImageFormat* m_format; + TextureType m_texture_type; + bool m_has_mip_maps; + size_t m_width; + size_t m_height; + size_t m_depth; + const ImageFormat* m_format; - _NODISCARD bool ReadMagic() const - { - uint32_t magic; - m_stream.read(reinterpret_cast(&magic), sizeof(magic)); - if (m_stream.gcount() != sizeof(magic)) + _NODISCARD bool ReadMagic() const { - std::cout << "Failed to read dds data\n"; - return false; - } - - if (magic != DDS_MAGIC) - { - std::cout << "Invalid magic for dds\n"; - return false; - } + uint32_t magic; + m_stream.read(reinterpret_cast(&magic), sizeof(magic)); + if (m_stream.gcount() != sizeof(magic)) + { + std::cerr << "Failed to read dds data\n"; + return false; + } - return true; - } + if (magic != DDS_MAGIC) + { + std::cerr << "Invalid magic for dds\n"; + return false; + } - _NODISCARD bool ReadDxt10Header() - { - DDS_HEADER_DXT10 headerDx10{}; - m_stream.read(reinterpret_cast(&headerDx10), sizeof(headerDx10)); - if (m_stream.gcount() != sizeof(headerDx10)) - { - std::cout << "Failed to read dds data\n"; - return false; + return true; } - if (headerDx10.resourceDimension == D3D10_RESOURCE_DIMENSION_TEXTURE3D) + _NODISCARD bool ReadDxt10Header() { - m_texture_type = TextureType::T_3D; - } - else if (headerDx10.resourceDimension == D3D10_RESOURCE_DIMENSION_TEXTURE2D) - { - if (headerDx10.miscFlag & DDS_RESOURCE_MISC_TEXTURECUBE || headerDx10.arraySize == 6) + DDS_HEADER_DXT10 headerDx10{}; + m_stream.read(reinterpret_cast(&headerDx10), sizeof(headerDx10)); + if (m_stream.gcount() != sizeof(headerDx10)) { - m_texture_type = TextureType::T_CUBE; + std::cerr << "Failed to read dds data\n"; + return false; + } + + if (headerDx10.resourceDimension == D3D10_RESOURCE_DIMENSION_TEXTURE3D) + { + m_texture_type = TextureType::T_3D; + } + else if (headerDx10.resourceDimension == D3D10_RESOURCE_DIMENSION_TEXTURE2D) + { + if (headerDx10.miscFlag & DDS_RESOURCE_MISC_TEXTURECUBE || headerDx10.arraySize == 6) + { + m_texture_type = TextureType::T_CUBE; + } + else + { + m_texture_type = TextureType::T_2D; + } } else { - m_texture_type = TextureType::T_2D; + std::cerr << std::format("Unsupported dds resourceDimension {}\n", static_cast(headerDx10.resourceDimension)); + return false; } - } - else - { - std::cout << "Unsupported dds resourceDimension " << headerDx10.resourceDimension << "\n"; - return false; - } - for (const auto* imageFormat : ImageFormat::ALL_FORMATS) - { - if (imageFormat->GetDxgiFormat() == headerDx10.dxgiFormat) + for (const auto* imageFormat : ImageFormat::ALL_FORMATS) { - m_format = imageFormat; - return true; + if (imageFormat->GetDxgiFormat() == headerDx10.dxgiFormat) + { + m_format = imageFormat; + return true; + } } - } - std::cout << "Unsupported dds dxgi format " << headerDx10.dxgiFormat << "\n"; - return false; - } + std::cerr << std::format("Unsupported dds dxgi format {}\n", static_cast(headerDx10.dxgiFormat)); + return false; + } - _NODISCARD bool ReadPixelFormatFourCc(DDS_PIXELFORMAT& pf) - { - switch (pf.dwFourCC) + _NODISCARD bool ReadPixelFormatFourCc(DDS_PIXELFORMAT& pf) { - case FileUtils::MakeMagic32('D', 'X', 'T', '1'): - m_format = &ImageFormat::FORMAT_BC1; - return true; + switch (pf.dwFourCC) + { + case FileUtils::MakeMagic32('D', 'X', 'T', '1'): + m_format = &ImageFormat::FORMAT_BC1; + return true; - case FileUtils::MakeMagic32('D', 'X', 'T', '3'): - m_format = &ImageFormat::FORMAT_BC2; - return true; + case FileUtils::MakeMagic32('D', 'X', 'T', '3'): + m_format = &ImageFormat::FORMAT_BC2; + return true; - case FileUtils::MakeMagic32('D', 'X', 'T', '5'): - m_format = &ImageFormat::FORMAT_BC3; - return true; + case FileUtils::MakeMagic32('D', 'X', 'T', '5'): + m_format = &ImageFormat::FORMAT_BC3; + return true; - case FileUtils::MakeMagic32('D', 'X', '1', '0'): - return ReadDxt10Header(); + case FileUtils::MakeMagic32('D', 'X', '1', '0'): + return ReadDxt10Header(); - default: - std::cout << "Unknown dds FourCC " << pf.dwFourCC << "\n"; - return false; + default: + std::cerr << std::format("Unknown dds FourCC {}\n", pf.dwFourCC); + return false; + } } - } - - static void ExtractSizeAndOffsetFromMask(uint32_t mask, unsigned& offset, unsigned& size) - { - offset = 0; - size = 0; - - if (mask == 0) - return; - while ((mask & 1) == 0) + static void ExtractSizeAndOffsetFromMask(uint32_t mask, unsigned& offset, unsigned& size) { - offset++; - mask >>= 1; - } + offset = 0; + size = 0; - while ((mask & 1) == 1) - { - size++; - mask >>= 1; - } - } + if (mask == 0) + return; - _NODISCARD bool ReadPixelFormatUnsigned(DDS_PIXELFORMAT& pf) - { - unsigned rOffset, rSize, gOffset, gSize, bOffset, bSize, aOffset, aSize; + while ((mask & 1) == 0) + { + offset++; + mask >>= 1; + } - ExtractSizeAndOffsetFromMask(pf.dwRBitMask, rOffset, rSize); - ExtractSizeAndOffsetFromMask(pf.dwGBitMask, gOffset, gSize); - ExtractSizeAndOffsetFromMask(pf.dwBBitMask, bOffset, bSize); - ExtractSizeAndOffsetFromMask(pf.dwABitMask, aOffset, aSize); + while ((mask & 1) == 1) + { + size++; + mask >>= 1; + } + } - for (const auto* imageFormat : ImageFormat::ALL_FORMATS) + _NODISCARD bool ReadPixelFormatUnsigned(DDS_PIXELFORMAT& pf) { - if (imageFormat->GetType() != ImageFormatType::UNSIGNED) - continue; + unsigned rOffset, rSize, gOffset, gSize, bOffset, bSize, aOffset, aSize; - const auto* unsignedImageFormat = dynamic_cast(imageFormat); + ExtractSizeAndOffsetFromMask(pf.dwRBitMask, rOffset, rSize); + ExtractSizeAndOffsetFromMask(pf.dwGBitMask, gOffset, gSize); + ExtractSizeAndOffsetFromMask(pf.dwBBitMask, bOffset, bSize); + ExtractSizeAndOffsetFromMask(pf.dwABitMask, aOffset, aSize); - if (unsignedImageFormat->m_r_offset == rOffset && unsignedImageFormat->m_r_size == rSize && unsignedImageFormat->m_g_offset == gOffset - && unsignedImageFormat->m_g_size == gSize && unsignedImageFormat->m_b_offset == bOffset && unsignedImageFormat->m_b_size == bSize - && unsignedImageFormat->m_a_offset == aOffset && unsignedImageFormat->m_a_size == aSize) + for (const auto* imageFormat : ImageFormat::ALL_FORMATS) { - m_format = imageFormat; - return true; - } - } + if (imageFormat->GetType() != ImageFormatType::UNSIGNED) + continue; - std::cout << "Failed to find dds pixel format: R=" << std::hex << pf.dwRBitMask << " G=" << std::hex << pf.dwGBitMask << " B=" << std::hex - << pf.dwBBitMask << " A=" << std::hex << pf.dwABitMask << "\n"; + const auto* unsignedImageFormat = dynamic_cast(imageFormat); - return false; - } + if (unsignedImageFormat->m_r_offset == rOffset && unsignedImageFormat->m_r_size == rSize && unsignedImageFormat->m_g_offset == gOffset + && unsignedImageFormat->m_g_size == gSize && unsignedImageFormat->m_b_offset == bOffset && unsignedImageFormat->m_b_size == bSize + && unsignedImageFormat->m_a_offset == aOffset && unsignedImageFormat->m_a_size == aSize) + { + m_format = imageFormat; + return true; + } + } - _NODISCARD bool ReadPixelFormat(DDS_PIXELFORMAT& pf) - { - if (pf.dwFlags & DDPF_FOURCC) - return ReadPixelFormatFourCc(pf); + std::cerr << std::format( + "Failed to find dds pixel format: R={:#x} G={:#x} B={:#x} A={:#x}\n", pf.dwRBitMask, pf.dwGBitMask, pf.dwBBitMask, pf.dwABitMask); - return ReadPixelFormatUnsigned(pf); - } + return false; + } - _NODISCARD bool ReadHeader() - { - DDS_HEADER header{}; - m_stream.read(reinterpret_cast(&header), sizeof(header)); - if (m_stream.gcount() != sizeof(header)) + _NODISCARD bool ReadPixelFormat(DDS_PIXELFORMAT& pf) { - std::cout << "Failed to read dds data\n"; - return false; + if (pf.dwFlags & DDPF_FOURCC) + return ReadPixelFormatFourCc(pf); + + return ReadPixelFormatUnsigned(pf); } - m_width = header.dwWidth; - m_height = header.dwHeight; - m_depth = header.dwDepth; - m_has_mip_maps = (header.dwCaps & DDSCAPS_MIPMAP) != 0 || header.dwMipMapCount > 1; + _NODISCARD bool ReadHeader() + { + DDS_HEADER header{}; + m_stream.read(reinterpret_cast(&header), sizeof(header)); + if (m_stream.gcount() != sizeof(header)) + { + std::cerr << "Failed to read dds data\n"; + return false; + } - if (header.dwCaps2 & DDSCAPS2_CUBEMAP) - m_texture_type = TextureType::T_CUBE; - else if (header.dwDepth > 1) - m_texture_type = TextureType::T_3D; - else - m_texture_type = TextureType::T_2D; + m_width = header.dwWidth; + m_height = header.dwHeight; + m_depth = header.dwDepth; + m_has_mip_maps = (header.dwCaps & DDSCAPS_MIPMAP) != 0 || header.dwMipMapCount > 1; - return ReadPixelFormat(header.ddspf); - } + if (header.dwCaps2 & DDSCAPS2_CUBEMAP) + m_texture_type = TextureType::T_CUBE; + else if (header.dwDepth > 1) + m_texture_type = TextureType::T_3D; + else + m_texture_type = TextureType::T_2D; - _NODISCARD Texture* ReadTextureData() const - { - Texture* result; + return ReadPixelFormat(header.ddspf); + } - switch (m_texture_type) + _NODISCARD std::unique_ptr ReadTextureData() const { - case TextureType::T_2D: - result = new Texture2D(m_format, m_width, m_height, m_has_mip_maps); - break; + std::unique_ptr result; - case TextureType::T_3D: - result = new Texture3D(m_format, m_width, m_height, m_depth, m_has_mip_maps); - break; + switch (m_texture_type) + { + case TextureType::T_2D: + result = std::make_unique(m_format, m_width, m_height, m_has_mip_maps); + break; - case TextureType::T_CUBE: - result = new TextureCube(m_format, m_width, m_height, m_has_mip_maps); - break; + case TextureType::T_3D: + result = std::make_unique(m_format, m_width, m_height, m_depth, m_has_mip_maps); + break; - default: - return nullptr; - } + case TextureType::T_CUBE: + result = std::make_unique(m_format, m_width, m_height, m_has_mip_maps); + break; - const auto mipMapCount = m_has_mip_maps ? result->GetMipMapCount() : 1; - const auto faceCount = m_texture_type == TextureType::T_CUBE ? 6 : 1; + default: + return nullptr; + } - result->Allocate(); + const auto mipMapCount = m_has_mip_maps ? result->GetMipMapCount() : 1; + const auto faceCount = m_texture_type == TextureType::T_CUBE ? 6 : 1; - for (auto mipLevel = 0; mipLevel < mipMapCount; mipLevel++) - { - const auto mipSize = result->GetSizeOfMipLevel(mipLevel); + result->Allocate(); - for (auto face = 0; face < faceCount; face++) + for (auto mipLevel = 0; mipLevel < mipMapCount; mipLevel++) { - m_stream.read(reinterpret_cast(result->GetBufferForMipLevel(mipLevel, face)), mipSize); + const auto mipSize = result->GetSizeOfMipLevel(mipLevel); - if (m_stream.gcount() != mipSize) + for (auto face = 0; face < faceCount; face++) { - std::cout << "Failed to read texture data from dds\n"; - delete result; - return nullptr; + m_stream.read(reinterpret_cast(result->GetBufferForMipLevel(mipLevel, face)), mipSize); + + if (m_stream.gcount() != mipSize) + { + std::cerr << "Failed to read texture data from dds\n"; + return nullptr; + } } } - } - return result; - } - -public: - DdsLoaderInternal(MemoryManager* memoryManager, std::istream& stream) - : m_memory_manager(memoryManager), - m_stream(stream), - m_texture_type(TextureType::T_2D), - m_has_mip_maps(false), - m_width(0u), - m_height(0u), - m_depth(0u), - m_format(nullptr) - { - } + return result; + } - Texture* LoadDds() - { - if (!ReadMagic() || !ReadHeader()) + public: + explicit DdsLoaderInternal(std::istream& stream) + : m_stream(stream), + m_texture_type(TextureType::T_2D), + m_has_mip_maps(false), + m_width(0u), + m_height(0u), + m_depth(0u), + m_format(nullptr) { - return nullptr; } - return ReadTextureData(); - } -}; + std::unique_ptr LoadDds() + { + if (!ReadMagic() || !ReadHeader()) + return nullptr; -DdsLoader::DdsLoader(MemoryManager* memoryManager) - : m_memory_manager(memoryManager) -{ -} + return ReadTextureData(); + } + }; -Texture* DdsLoader::LoadDds(std::istream& stream) const -{ - DdsLoaderInternal internal(m_memory_manager, stream); - return internal.LoadDds(); -} + std::unique_ptr LoadDds(std::istream& stream) + { + DdsLoaderInternal internal(stream); + return internal.LoadDds(); + } +} // namespace dds diff --git a/src/ObjLoading/Image/DdsLoader.h b/src/ObjLoading/Image/DdsLoader.h index 28aa59ac..189d0400 100644 --- a/src/ObjLoading/Image/DdsLoader.h +++ b/src/ObjLoading/Image/DdsLoader.h @@ -1,16 +1,10 @@ #pragma once #include "Image/Texture.h" -#include "Utils/MemoryManager.h" #include -class DdsLoader +namespace dds { - MemoryManager* m_memory_manager; - -public: - explicit DdsLoader(MemoryManager* memoryManager); - - Texture* LoadDds(std::istream& stream) const; -}; + std::unique_ptr LoadDds(std::istream& stream); +}