diff --git a/src/ObjCommon/Image/IwiTypes.h b/src/ObjCommon/Image/IwiTypes.h index 133d774e..6e854b13 100644 --- a/src/ObjCommon/Image/IwiTypes.h +++ b/src/ObjCommon/Image/IwiTypes.h @@ -19,6 +19,61 @@ namespace iwi8 uint16_t dimensions[3]; uint32_t fileSizeForPicmip[4]; }; + + enum class IwiFormat + { + IMG_FORMAT_INVALID = 0x0, + IMG_FORMAT_BITMAP_RGBA = 0x1, + IMG_FORMAT_BITMAP_RGB = 0x2, + IMG_FORMAT_BITMAP_LUMINANCE_ALPHA = 0x3, + IMG_FORMAT_BITMAP_LUMINANCE = 0x4, + IMG_FORMAT_BITMAP_ALPHA = 0x5, + IMG_FORMAT_WAVELET_RGBA = 0x6, + IMG_FORMAT_WAVELET_RGB = 0x7, + IMG_FORMAT_WAVELET_LUMINANCE_ALPHA = 0x8, + IMG_FORMAT_WAVELET_LUMINANCE = 0x9, + IMG_FORMAT_WAVELET_ALPHA = 0xA, + IMG_FORMAT_DXT1 = 0xB, + IMG_FORMAT_DXT3 = 0xC, + IMG_FORMAT_DXT5 = 0xD, + IMG_FORMAT_DXN = 0xE, + IMG_FORMAT_DXT3A_AS_LUMINANCE = 0xF, + IMG_FORMAT_DXT5A_AS_LUMINANCE = 0x10, + IMG_FORMAT_DXT3A_AS_ALPHA = 0x11, + IMG_FORMAT_DXT5A_AS_ALPHA = 0x12, + IMG_FORMAT_DXT1_AS_LUMINANCE_ALPHA = 0x13, + IMG_FORMAT_DXN_AS_LUMINANCE_ALPHA = 0x14, + IMG_FORMAT_DXT1_AS_LUMINANCE = 0x15, + IMG_FORMAT_DXT1_AS_ALPHA = 0x16, + + IMG_FORMAT_COUNT + }; + + enum IwiFlags + { + IMG_FLAG_NOPICMIP = 1 << 0, + IMG_FLAG_NOMIPMAPS = 1 << 1, + IMG_FLAG_STREAMING = 1 << 2, + IMG_FLAG_LEGACY_NORMALS = 1 << 3, + IMG_FLAG_CLAMP_U = 1 << 4, + IMG_FLAG_CLAMP_V = 1 << 5, + IMG_FLAG_ALPHA_WEIGHTED_COLORS = 1 << 6, + IMG_FLAG_DXTC_APPROX_WEIGHTS = 1 << 7, + IMG_FLAG_GAMMA_NONE = 0, + IMG_FLAG_GAMMA_SRGB = 1 << 8, + IMG_FLAG_GAMMA_PWL = 1 << 9, + IMG_FLAG_GAMMA_2 = IMG_FLAG_GAMMA_SRGB | IMG_FLAG_GAMMA_PWL, + IMG_FLAG_MAPTYPE_2D = 0, + IMG_FLAG_MAPTYPE_CUBE = 1 << 16, + IMG_FLAG_MAPTYPE_3D = 1 << 17, + IMG_FLAG_MAPTYPE_1D = IMG_FLAG_MAPTYPE_CUBE | IMG_FLAG_MAPTYPE_3D, + IMG_FLAG_MAPTYPE_MASK = IMG_FLAG_MAPTYPE_2D | IMG_FLAG_MAPTYPE_CUBE | IMG_FLAG_MAPTYPE_3D | IMG_FLAG_MAPTYPE_1D, + IMG_FLAG_NORMALMAP = 1 << 18, + IMG_FLAG_INTENSITY_TO_ALPHA = 1 << 19, + IMG_FLAG_DYNAMIC = 1 << 24, + IMG_FLAG_RENDER_TARGET = 1 << 25, + IMG_FLAG_SYSTEMMEM = 1 << 26 + }; } // T5 diff --git a/src/ObjLoading/Game/IW4/ObjLoaderIW4.cpp b/src/ObjLoading/Game/IW4/ObjLoaderIW4.cpp new file mode 100644 index 00000000..31597d9f --- /dev/null +++ b/src/ObjLoading/Game/IW4/ObjLoaderIW4.cpp @@ -0,0 +1,109 @@ +#include "ObjLoaderIW4.h" +#include "Game/IW4/GameIW4.h" +#include "Game/IW4/GameAssetPoolIW4.h" +#include "ObjContainer/IPak/IPak.h" +#include "ObjLoading.h" +#include "Image/Texture.h" +#include "Image/IwiLoader.h" + +namespace IW4 +{ + bool ObjLoader::SupportsZone(Zone* zone) const + { + return zone->m_game == &g_GameIW4; + } + + bool ObjLoader::IsMpZone(Zone* zone) + { + return zone->m_name.compare(0, 3, "mp_") == 0 + || zone->m_name.compare(zone->m_name.length() - 3, 3, "_mp") == 0; + } + + bool ObjLoader::IsZmZone(Zone* zone) + { + return zone->m_name.compare(0, 3, "zm_") == 0 + || zone->m_name.compare(zone->m_name.length() - 3, 3, "_zm") == 0; + } + + void ObjLoader::LoadReferencedContainersForZone(ISearchPath* searchPath, Zone* zone) const + { + } + + void ObjLoader::UnloadContainersOfZone(Zone* zone) const + { + } + + void ObjLoader::LoadImageFromLoadDef(GfxImage* image, Zone* zone) + { + // TODO: Load Texture from LoadDef here + } + + void ObjLoader::LoadImageFromIwi(GfxImage* image, ISearchPath* searchPath, Zone* zone) + { + Texture* loadedTexture = nullptr; + IwiLoader loader(zone->GetMemory()); + + const std::string imageFileName = "images/" + std::string(image->name) + ".iwi"; + auto* filePathImage = searchPath->Open(imageFileName); + + if (filePathImage != nullptr) + { + loadedTexture = loader.LoadIwi(filePathImage); + + filePathImage->Close(); + delete filePathImage; + } + + if (loadedTexture != nullptr) + { + image->texture.texture = loadedTexture; + image->cardMemory.platform[0] = 0; + + const int textureMipCount = loadedTexture->GetMipMapCount(); + for (int mipLevel = 0; mipLevel < textureMipCount; mipLevel++) + image->cardMemory.platform[0] += static_cast(loadedTexture->GetSizeOfMipLevel(mipLevel) * loadedTexture->GetFaceCount()); + } + else + { + printf("Could not find data for image \"%s\"\n", image->name); + } + } + + void ObjLoader::LoadImageData(ISearchPath* searchPath, Zone* zone) + { + auto* assetPool = dynamic_cast(zone->GetPools()); + + if (assetPool && assetPool->m_image != nullptr) + { + for (auto* imageEntry : *assetPool->m_image) + { + auto* image = imageEntry->Asset(); + + if (image->cardMemory.platform[0] > 0) + { + continue; + } + + // Do not load linked assets + if (image->name && image->name[0] == ',') + { + continue; + } + + if (image->texture.loadDef && image->texture.loadDef->resourceSize > 0) + { + LoadImageFromLoadDef(image, zone); + } + else + { + LoadImageFromIwi(image, searchPath, zone); + } + } + } + } + + void ObjLoader::LoadObjDataForZone(ISearchPath* searchPath, Zone* zone) const + { + LoadImageData(searchPath, zone); + } +} diff --git a/src/ObjLoading/Game/IW4/ObjLoaderIW4.h b/src/ObjLoading/Game/IW4/ObjLoaderIW4.h new file mode 100644 index 00000000..59530c42 --- /dev/null +++ b/src/ObjLoading/Game/IW4/ObjLoaderIW4.h @@ -0,0 +1,26 @@ +#pragma once + +#include "IObjLoader.h" +#include "SearchPath/ISearchPath.h" +#include "Game/IW4/IW4.h" + +namespace IW4 +{ + class ObjLoader final : public IObjLoader + { + static void LoadImageFromIwi(GfxImage* image, ISearchPath* searchPath, Zone* zone); + static void LoadImageFromLoadDef(GfxImage* image, Zone* zone); + static void LoadImageData(ISearchPath* searchPath, Zone* zone); + + static bool IsMpZone(Zone* zone); + static bool IsZmZone(Zone* zone); + + public: + bool SupportsZone(Zone* zone) const override; + + void LoadReferencedContainersForZone(ISearchPath* searchPath, Zone* zone) const override; + void UnloadContainersOfZone(Zone* zone) const override; + + void LoadObjDataForZone(ISearchPath* searchPath, Zone* zone) const override; + }; +} diff --git a/src/ObjLoading/Game/T6/ObjLoaderT6.cpp b/src/ObjLoading/Game/T6/ObjLoaderT6.cpp index 6cb15f1d..a6af175e 100644 --- a/src/ObjLoading/Game/T6/ObjLoaderT6.cpp +++ b/src/ObjLoading/Game/T6/ObjLoaderT6.cpp @@ -7,226 +7,229 @@ #include "Image/IwiLoader.h" #include "Game/T6/CommonT6.h" -const int ObjLoaderT6::IPAK_READ_HASH = CommonT6::Com_HashKey("ipak_read", 64); -const int ObjLoaderT6::GLOBAL_HASH = CommonT6::Com_HashKey("GLOBAL", 64); - -bool ObjLoaderT6::SupportsZone(Zone* zone) const +namespace T6 { - return zone->m_game == &g_GameT6; -} + const int ObjLoader::IPAK_READ_HASH = CommonT6::Com_HashKey("ipak_read", 64); + const int ObjLoader::GLOBAL_HASH = CommonT6::Com_HashKey("GLOBAL", 64); -void ObjLoaderT6::LoadIPakForZone(ISearchPath* searchPath, const std::string& ipakName, Zone* zone) -{ - if(ObjLoading::Configuration.Verbose) - printf("Trying to load ipak '%s' for zone '%s'\n", ipakName.c_str(), zone->m_name.c_str()); - - IPak* existingIPak = IPak::Repository.GetContainerByName(ipakName); - if(existingIPak != nullptr) + bool ObjLoader::SupportsZone(Zone* zone) const { - if (ObjLoading::Configuration.Verbose) - printf("Referencing loaded ipak '%s'.\n", ipakName.c_str()); - - IPak::Repository.AddContainer(existingIPak, zone); - return; + return zone->m_game == &g_GameT6; } - const std::string ipakFilename = ipakName + ".ipak"; - - auto* file = searchPath->Open(ipakFilename); - if(file && file->IsOpen()) + void ObjLoader::LoadIPakForZone(ISearchPath* searchPath, const std::string& ipakName, Zone* zone) { - IPak* ipak = new IPak(ipakFilename, file); + if (ObjLoading::Configuration.Verbose) + printf("Trying to load ipak '%s' for zone '%s'\n", ipakName.c_str(), zone->m_name.c_str()); - if(ipak->Initialize()) + IPak* existingIPak = IPak::Repository.GetContainerByName(ipakName); + if (existingIPak != nullptr) { - IPak::Repository.AddContainer(ipak, zone); - if (ObjLoading::Configuration.Verbose) - printf("Found and loaded ipak '%s'.\n", ipakFilename.c_str()); + printf("Referencing loaded ipak '%s'.\n", ipakName.c_str()); + + IPak::Repository.AddContainer(existingIPak, zone); + return; } - else + + const std::string ipakFilename = ipakName + ".ipak"; + + auto* file = searchPath->Open(ipakFilename); + if (file && file->IsOpen()) { - delete ipak; - file->Close(); - delete file; + IPak* ipak = new IPak(ipakFilename, file); - printf("Failed to load ipak '%s'!\n", ipakFilename.c_str()); - } - } -} - -bool ObjLoaderT6::IsMpZone(Zone* zone) -{ - return zone->m_name.compare(0, 3, "mp_") == 0 - || zone->m_name.compare(zone->m_name.length() - 3, 3, "_mp") == 0; -} - -bool ObjLoaderT6::IsZmZone(Zone* zone) -{ - return zone->m_name.compare(0, 3, "zm_") == 0 - || zone->m_name.compare(zone->m_name.length() - 3, 3, "_zm") == 0; -} - -void ObjLoaderT6::LoadCommonIPaks(ISearchPath* searchPath, Zone* zone) -{ - if(ObjLoading::Configuration.Verbose) - printf("Loading common ipaks for zone \"%s\"\n", zone->m_name.c_str()); - - LoadIPakForZone(searchPath, "base", zone); - auto languagePrefixes = g_GameT6.GetLanguagePrefixes(); - for (const auto& languagePrefix : languagePrefixes) - { - LoadIPakForZone(searchPath, languagePrefix.m_prefix + "base", zone); - } - - if (IsMpZone(zone)) - { - if (ObjLoading::Configuration.Verbose) - printf("Loading multiplayer ipaks for zone \"%s\"\n", zone->m_name.c_str()); - - LoadIPakForZone(searchPath, "mp", zone); - LoadIPakForZone(searchPath, "so", zone); - } - else if (IsZmZone(zone)) - { - if (ObjLoading::Configuration.Verbose) - printf("Loading zombie ipak for zone \"%s\"\n", zone->m_name.c_str()); - - LoadIPakForZone(searchPath, "zm", zone); - } - else - { - if (ObjLoading::Configuration.Verbose) - printf("Loading singleplayer ipak for zone \"%s\"\n", zone->m_name.c_str()); - - LoadIPakForZone(searchPath, "sp", zone); - } -} - -void ObjLoaderT6::LoadReferencedContainersForZone(ISearchPath* searchPath, Zone* zone) const -{ - auto* assetPoolT6 = dynamic_cast(zone->GetPools()); - const int zoneNameHash = CommonT6::Com_HashKey(zone->m_name.c_str(), 64); - - LoadCommonIPaks(searchPath, zone); - - if(assetPoolT6->m_key_value_pairs != nullptr) - { - for(auto* keyValuePairsEntry : *assetPoolT6->m_key_value_pairs) - { - auto* keyValuePairs = keyValuePairsEntry->Asset(); - for(int variableIndex = 0; variableIndex < keyValuePairs->numVariables; variableIndex++) + if (ipak->Initialize()) { - T6::KeyValuePair* variable = &keyValuePairs->keyValuePairs[variableIndex]; - - if(variable->namespaceHash == zoneNameHash && variable->keyHash == IPAK_READ_HASH) - { - LoadIPakForZone(searchPath, variable->value, zone); - } - } - } - } -} + IPak::Repository.AddContainer(ipak, zone); -void ObjLoaderT6::UnloadContainersOfZone(Zone* zone) const -{ - IPak::Repository.RemoveContainerReferences(zone); -} - -void ObjLoaderT6::LoadImageFromLoadDef(T6::GfxImage* image, Zone* zone) -{ - // TODO: Load Texture from LoadDef here -} - -void ObjLoaderT6::LoadImageFromIwi(T6::GfxImage* image, ISearchPath* searchPath, Zone* zone) -{ - Texture* loadedTexture = nullptr; - IwiLoader loader(zone->GetMemory()); - - if (image->streamedPartCount > 0) - { - for (auto* ipak : IPak::Repository) - { - auto* ipakStream = ipak->GetEntryStream(image->hash, image->streamedParts[0].hash); - - if (ipakStream != nullptr) - { - loadedTexture = loader.LoadIwi(ipakStream); - - ipakStream->Close(); - delete ipakStream; - - if (loadedTexture != nullptr) - { - break; - } - } - } - } - - if(loadedTexture == nullptr) - { - const std::string imageFileName = "images/" + std::string(image->name) + ".iwi"; - auto* filePathImage = searchPath->Open(imageFileName); - - if (filePathImage != nullptr) - { - loadedTexture = loader.LoadIwi(filePathImage); - - filePathImage->Close(); - delete filePathImage; - } - } - - if(loadedTexture != nullptr) - { - image->texture.texture = loadedTexture; - image->loadedSize = 0; - - const int textureMipCount = loadedTexture->GetMipMapCount(); - for(int mipLevel = 0; mipLevel < textureMipCount; mipLevel++) - image->loadedSize += loadedTexture->GetSizeOfMipLevel(mipLevel) * loadedTexture->GetFaceCount(); - } - else - { - printf("Could not find data for image \"%s\"\n", image->name); - } -} - -void ObjLoaderT6::LoadImageData(ISearchPath* searchPath, Zone* zone) -{ - auto* assetPoolT6 = dynamic_cast(zone->GetPools()); - - if (assetPoolT6 && assetPoolT6->m_image != nullptr) - { - for (auto* imageEntry : *assetPoolT6->m_image) - { - auto* image = imageEntry->Asset(); - - if(image->loadedSize > 0) - { - continue; - } - - // Do not load linked assets - if(image->name && image->name[0] == ',') - { - continue; - } - - if(image->texture.loadDef && image->texture.loadDef->resourceSize > 0) - { - LoadImageFromLoadDef(image, zone); + if (ObjLoading::Configuration.Verbose) + printf("Found and loaded ipak '%s'.\n", ipakFilename.c_str()); } else { - LoadImageFromIwi(image, searchPath, zone); + delete ipak; + file->Close(); + delete file; + + printf("Failed to load ipak '%s'!\n", ipakFilename.c_str()); } } } -} -void ObjLoaderT6::LoadObjDataForZone(ISearchPath* searchPath, Zone* zone) const -{ - LoadImageData(searchPath, zone); + bool ObjLoader::IsMpZone(Zone* zone) + { + return zone->m_name.compare(0, 3, "mp_") == 0 + || zone->m_name.compare(zone->m_name.length() - 3, 3, "_mp") == 0; + } + + bool ObjLoader::IsZmZone(Zone* zone) + { + return zone->m_name.compare(0, 3, "zm_") == 0 + || zone->m_name.compare(zone->m_name.length() - 3, 3, "_zm") == 0; + } + + void ObjLoader::LoadCommonIPaks(ISearchPath* searchPath, Zone* zone) + { + if (ObjLoading::Configuration.Verbose) + printf("Loading common ipaks for zone \"%s\"\n", zone->m_name.c_str()); + + LoadIPakForZone(searchPath, "base", zone); + auto languagePrefixes = g_GameT6.GetLanguagePrefixes(); + for (const auto& languagePrefix : languagePrefixes) + { + LoadIPakForZone(searchPath, languagePrefix.m_prefix + "base", zone); + } + + if (IsMpZone(zone)) + { + if (ObjLoading::Configuration.Verbose) + printf("Loading multiplayer ipaks for zone \"%s\"\n", zone->m_name.c_str()); + + LoadIPakForZone(searchPath, "mp", zone); + LoadIPakForZone(searchPath, "so", zone); + } + else if (IsZmZone(zone)) + { + if (ObjLoading::Configuration.Verbose) + printf("Loading zombie ipak for zone \"%s\"\n", zone->m_name.c_str()); + + LoadIPakForZone(searchPath, "zm", zone); + } + else + { + if (ObjLoading::Configuration.Verbose) + printf("Loading singleplayer ipak for zone \"%s\"\n", zone->m_name.c_str()); + + LoadIPakForZone(searchPath, "sp", zone); + } + } + + void ObjLoader::LoadReferencedContainersForZone(ISearchPath* searchPath, Zone* zone) const + { + auto* assetPoolT6 = dynamic_cast(zone->GetPools()); + const int zoneNameHash = CommonT6::Com_HashKey(zone->m_name.c_str(), 64); + + LoadCommonIPaks(searchPath, zone); + + if (assetPoolT6->m_key_value_pairs != nullptr) + { + for (auto* keyValuePairsEntry : *assetPoolT6->m_key_value_pairs) + { + auto* keyValuePairs = keyValuePairsEntry->Asset(); + for (int variableIndex = 0; variableIndex < keyValuePairs->numVariables; variableIndex++) + { + KeyValuePair* variable = &keyValuePairs->keyValuePairs[variableIndex]; + + if (variable->namespaceHash == zoneNameHash && variable->keyHash == IPAK_READ_HASH) + { + LoadIPakForZone(searchPath, variable->value, zone); + } + } + } + } + } + + void ObjLoader::UnloadContainersOfZone(Zone* zone) const + { + IPak::Repository.RemoveContainerReferences(zone); + } + + void ObjLoader::LoadImageFromLoadDef(GfxImage* image, Zone* zone) + { + // TODO: Load Texture from LoadDef here + } + + void ObjLoader::LoadImageFromIwi(GfxImage* image, ISearchPath* searchPath, Zone* zone) + { + Texture* loadedTexture = nullptr; + IwiLoader loader(zone->GetMemory()); + + if (image->streamedPartCount > 0) + { + for (auto* ipak : IPak::Repository) + { + auto* ipakStream = ipak->GetEntryStream(image->hash, image->streamedParts[0].hash); + + if (ipakStream != nullptr) + { + loadedTexture = loader.LoadIwi(ipakStream); + + ipakStream->Close(); + delete ipakStream; + + if (loadedTexture != nullptr) + { + break; + } + } + } + } + + if (loadedTexture == nullptr) + { + const std::string imageFileName = "images/" + std::string(image->name) + ".iwi"; + auto* filePathImage = searchPath->Open(imageFileName); + + if (filePathImage != nullptr) + { + loadedTexture = loader.LoadIwi(filePathImage); + + filePathImage->Close(); + delete filePathImage; + } + } + + if (loadedTexture != nullptr) + { + image->texture.texture = loadedTexture; + image->loadedSize = 0; + + const int textureMipCount = loadedTexture->GetMipMapCount(); + for (int mipLevel = 0; mipLevel < textureMipCount; mipLevel++) + image->loadedSize += loadedTexture->GetSizeOfMipLevel(mipLevel) * loadedTexture->GetFaceCount(); + } + else + { + printf("Could not find data for image \"%s\"\n", image->name); + } + } + + void ObjLoader::LoadImageData(ISearchPath* searchPath, Zone* zone) + { + auto* assetPoolT6 = dynamic_cast(zone->GetPools()); + + if (assetPoolT6 && assetPoolT6->m_image != nullptr) + { + for (auto* imageEntry : *assetPoolT6->m_image) + { + auto* image = imageEntry->Asset(); + + if (image->loadedSize > 0) + { + continue; + } + + // Do not load linked assets + if (image->name && image->name[0] == ',') + { + continue; + } + + if (image->texture.loadDef && image->texture.loadDef->resourceSize > 0) + { + LoadImageFromLoadDef(image, zone); + } + else + { + LoadImageFromIwi(image, searchPath, zone); + } + } + } + } + + void ObjLoader::LoadObjDataForZone(ISearchPath* searchPath, Zone* zone) const + { + LoadImageData(searchPath, zone); + } } diff --git a/src/ObjLoading/Game/T6/ObjLoaderT6.h b/src/ObjLoading/Game/T6/ObjLoaderT6.h index 7972265f..5e977938 100644 --- a/src/ObjLoading/Game/T6/ObjLoaderT6.h +++ b/src/ObjLoading/Game/T6/ObjLoaderT6.h @@ -4,26 +4,29 @@ #include "SearchPath/ISearchPath.h" #include "Game/T6/T6.h" -class ObjLoaderT6 final : public IObjLoader +namespace T6 { - static const int IPAK_READ_HASH; - static const int GLOBAL_HASH; + class ObjLoader final : public IObjLoader + { + static const int IPAK_READ_HASH; + static const int GLOBAL_HASH; - static void LoadIPakForZone(ISearchPath* searchPath, const std::string& ipakName, Zone* zone); + static void LoadIPakForZone(ISearchPath* searchPath, const std::string& ipakName, Zone* zone); - static void LoadImageFromIwi(T6::GfxImage* image, ISearchPath* searchPath, Zone* zone); - static void LoadImageFromLoadDef(T6::GfxImage* image, Zone* zone); - static void LoadImageData(ISearchPath* searchPath, Zone* zone); + static void LoadImageFromIwi(GfxImage* image, ISearchPath* searchPath, Zone* zone); + static void LoadImageFromLoadDef(GfxImage* image, Zone* zone); + static void LoadImageData(ISearchPath* searchPath, Zone* zone); - static bool IsMpZone(Zone* zone); - static bool IsZmZone(Zone* zone); - static void LoadCommonIPaks(ISearchPath* searchPath, Zone* zone); + static bool IsMpZone(Zone* zone); + static bool IsZmZone(Zone* zone); + static void LoadCommonIPaks(ISearchPath* searchPath, Zone* zone); -public: - bool SupportsZone(Zone* zone) const override; + public: + bool SupportsZone(Zone* zone) const override; - void LoadReferencedContainersForZone(ISearchPath* searchPath, Zone* zone) const override; - void UnloadContainersOfZone(Zone* zone) const override; + void LoadReferencedContainersForZone(ISearchPath* searchPath, Zone* zone) const override; + void UnloadContainersOfZone(Zone* zone) const override; - void LoadObjDataForZone(ISearchPath* searchPath, Zone* zone) const override; -}; + void LoadObjDataForZone(ISearchPath* searchPath, Zone* zone) const override; + }; +} diff --git a/src/ObjLoading/Image/IwiLoader.cpp b/src/ObjLoading/Image/IwiLoader.cpp index 74e5ede2..2637303b 100644 --- a/src/ObjLoading/Image/IwiLoader.cpp +++ b/src/ObjLoading/Image/IwiLoader.cpp @@ -7,6 +7,120 @@ IwiLoader::IwiLoader(MemoryManager* memoryManager) m_memory_manager = memoryManager; } +const ImageFormat* IwiLoader::GetFormat8(int8_t format) +{ + switch (static_cast(format)) + { + case iwi8::IwiFormat::IMG_FORMAT_BITMAP_RGBA: + return &ImageFormat::FORMAT_R8_G8_B8_A8; + case iwi8::IwiFormat::IMG_FORMAT_BITMAP_RGB: + return &ImageFormat::FORMAT_R8_G8_B8; + case iwi8::IwiFormat::IMG_FORMAT_BITMAP_ALPHA: + return &ImageFormat::FORMAT_A8; + case iwi8::IwiFormat::IMG_FORMAT_DXT1: + return &ImageFormat::FORMAT_BC1; + case iwi8::IwiFormat::IMG_FORMAT_DXT3: + return &ImageFormat::FORMAT_BC2; + case iwi8::IwiFormat::IMG_FORMAT_DXT5: + return &ImageFormat::FORMAT_BC3; + case iwi8::IwiFormat::IMG_FORMAT_DXN: + return &ImageFormat::FORMAT_BC5; + case iwi8::IwiFormat::IMG_FORMAT_BITMAP_LUMINANCE_ALPHA: // used + case iwi8::IwiFormat::IMG_FORMAT_BITMAP_LUMINANCE: // used + case iwi8::IwiFormat::IMG_FORMAT_WAVELET_RGBA: // used + case iwi8::IwiFormat::IMG_FORMAT_WAVELET_RGB: // used + case iwi8::IwiFormat::IMG_FORMAT_WAVELET_LUMINANCE_ALPHA: + case iwi8::IwiFormat::IMG_FORMAT_WAVELET_LUMINANCE: + case iwi8::IwiFormat::IMG_FORMAT_WAVELET_ALPHA: + case iwi8::IwiFormat::IMG_FORMAT_DXT3A_AS_LUMINANCE: + case iwi8::IwiFormat::IMG_FORMAT_DXT5A_AS_LUMINANCE: + case iwi8::IwiFormat::IMG_FORMAT_DXT3A_AS_ALPHA: + case iwi8::IwiFormat::IMG_FORMAT_DXT5A_AS_ALPHA: + case iwi8::IwiFormat::IMG_FORMAT_DXT1_AS_LUMINANCE_ALPHA: + case iwi8::IwiFormat::IMG_FORMAT_DXN_AS_LUMINANCE_ALPHA: + case iwi8::IwiFormat::IMG_FORMAT_DXT1_AS_LUMINANCE: + case iwi8::IwiFormat::IMG_FORMAT_DXT1_AS_ALPHA: + printf("Unsupported IWI format: %i\n", format); + break; + default: + printf("Unknown IWI format: %i\n", format); + break; + } + + return nullptr; +} + +Texture* IwiLoader::LoadIwi8(FileAPI::IFile* file) +{ + iwi8::IwiHeader header{}; + + if (file->Read(&header, sizeof header, 1) != 1) + return nullptr; + + const ImageFormat* format = GetFormat8(header.format); + if (format == nullptr) + return nullptr; + + uint16_t width = header.dimensions[0]; + uint16_t height = header.dimensions[1]; + uint16_t depth = header.dimensions[2]; + bool hasMipMaps = !(header.flags & iwi8::IwiFlags::IMG_FLAG_NOMIPMAPS); + + Texture* texture; + if ((header.flags & iwi8::IwiFlags::IMG_FLAG_MAPTYPE_MASK) == iwi8::IwiFlags::IMG_FLAG_MAPTYPE_CUBE) + { + texture = m_memory_manager->Create(format, width, height, hasMipMaps); + } + else if ((header.flags & iwi8::IwiFlags::IMG_FLAG_MAPTYPE_MASK) == iwi8::IwiFlags::IMG_FLAG_MAPTYPE_3D) + { + texture = m_memory_manager->Create(format, width, height, depth, hasMipMaps); + } + else if ((header.flags & iwi8::IwiFlags::IMG_FLAG_MAPTYPE_MASK) == iwi8::IwiFlags::IMG_FLAG_MAPTYPE_2D) + { + texture = m_memory_manager->Create(format, width, height, hasMipMaps); + } + else if ((header.flags & iwi8::IwiFlags::IMG_FLAG_MAPTYPE_MASK) == iwi8::IwiFlags::IMG_FLAG_MAPTYPE_1D) + { + printf("Iwi has unsupported map type 1D\n"); + return nullptr; + } + else + { + printf("Iwi has unsupported map type\n"); + return nullptr; + } + + texture->Allocate(); + + size_t currentFileSize = sizeof iwi8::IwiHeader + sizeof IwiVersion; + const int mipMapCount = hasMipMaps ? texture->GetMipMapCount() : 1; + + for (int currentMipLevel = mipMapCount - 1; currentMipLevel >= 0; currentMipLevel--) + { + const size_t sizeOfMipLevel = texture->GetSizeOfMipLevel(currentMipLevel) * texture->GetFaceCount(); + currentFileSize += sizeOfMipLevel; + + if (currentMipLevel < static_cast(_countof(iwi8::IwiHeader::fileSizeForPicmip)) + && currentFileSize != header.fileSizeForPicmip[currentMipLevel]) + { + printf("Iwi has invalid file size for picmip %i\n", currentMipLevel); + + m_memory_manager->Delete(texture); + return nullptr; + } + + if (file->Read(texture->GetBufferForMipLevel(currentMipLevel), 1, sizeOfMipLevel) != sizeOfMipLevel) + { + printf("Unexpected eof of iwi in mip level %i\n", currentMipLevel); + + m_memory_manager->Delete(texture); + return nullptr; + } + } + + return texture; +} + const ImageFormat* IwiLoader::GetFormat27(int8_t format) { switch (static_cast(format)) @@ -65,11 +179,11 @@ Texture* IwiLoader::LoadIwi27(FileAPI::IFile* file) bool hasMipMaps = !(header.flags & iwi27::IwiFlags::IMG_FLAG_NOMIPMAPS); Texture* texture; - if(header.flags & iwi27::IwiFlags::IMG_FLAG_CUBEMAP) + if (header.flags & iwi27::IwiFlags::IMG_FLAG_CUBEMAP) { texture = m_memory_manager->Create(format, width, height, hasMipMaps); } - else if(header.flags & iwi27::IwiFlags::IMG_FLAG_VOLMAP) + else if (header.flags & iwi27::IwiFlags::IMG_FLAG_VOLMAP) { texture = m_memory_manager->Create(format, width, height, depth, hasMipMaps); } @@ -125,6 +239,9 @@ Texture* IwiLoader::LoadIwi(FileAPI::IFile* file) switch (iwiVersion.version) { + case 8: + return LoadIwi8(file); + case 27: return LoadIwi27(file); diff --git a/src/ObjLoading/Image/IwiLoader.h b/src/ObjLoading/Image/IwiLoader.h index 1021d583..b377f03e 100644 --- a/src/ObjLoading/Image/IwiLoader.h +++ b/src/ObjLoading/Image/IwiLoader.h @@ -8,6 +8,9 @@ class IwiLoader { MemoryManager* m_memory_manager; + static const ImageFormat* GetFormat8(int8_t format); + Texture* LoadIwi8(FileAPI::IFile* file); + static const ImageFormat* GetFormat27(int8_t format); Texture* LoadIwi27(FileAPI::IFile* file); diff --git a/src/ObjLoading/ObjContainer/IWD/IWD.cpp b/src/ObjLoading/ObjContainer/IWD/IWD.cpp index f80d97be..a34df1b8 100644 --- a/src/ObjLoading/ObjContainer/IWD/IWD.cpp +++ b/src/ObjLoading/ObjContainer/IWD/IWD.cpp @@ -53,7 +53,7 @@ public: { const auto result = unzReadCurrentFile(m_container, buffer, elementSize * elementCount); - return result >= 0 ? static_cast(result) : 0; + return result >= 0 ? static_cast(result) / elementSize : 0; } size_t Write(const void* data, size_t elementSize, size_t elementCount) override @@ -113,7 +113,7 @@ public: void Close() override { - unzClose(m_container); + unzCloseCurrentFile(m_container); m_open = false; m_parent->OnIWDFileClose(); @@ -232,6 +232,8 @@ public: { m_last_file = new IWDFile(this, m_unz_file, iwdEntry->m_size); } + + return m_last_file; } return nullptr; diff --git a/src/ObjLoading/ObjLoading.cpp b/src/ObjLoading/ObjLoading.cpp index 92627f36..85eaa2b6 100644 --- a/src/ObjLoading/ObjLoading.cpp +++ b/src/ObjLoading/ObjLoading.cpp @@ -1,5 +1,6 @@ #include "ObjLoading.h" #include "IObjLoader.h" +#include "Game/IW4/ObjLoaderIW4.h" #include "Game/T6/ObjLoaderT6.h" #include "ObjContainer/IWD/IWD.h" #include "SearchPath/SearchPaths.h" @@ -8,12 +9,13 @@ ObjLoading::Configuration_t ObjLoading::Configuration; const IObjLoader* const OBJ_LOADERS[] { - new ObjLoaderT6() + new IW4::ObjLoader(), + new T6::ObjLoader() }; void ObjLoading::LoadReferencedContainersForZone(ISearchPath* searchPath, Zone* zone) { - for (auto* loader : OBJ_LOADERS) + for (const auto* loader : OBJ_LOADERS) { if (loader->SupportsZone(zone)) { @@ -25,7 +27,7 @@ void ObjLoading::LoadReferencedContainersForZone(ISearchPath* searchPath, Zone* void ObjLoading::LoadObjDataForZone(ISearchPath* searchPath, Zone* zone) { - for (auto* loader : OBJ_LOADERS) + for (const auto* loader : OBJ_LOADERS) { if (loader->SupportsZone(zone)) { @@ -37,7 +39,7 @@ void ObjLoading::LoadObjDataForZone(ISearchPath* searchPath, Zone* zone) void ObjLoading::UnloadContainersOfZone(Zone* zone) { - for (auto* loader : OBJ_LOADERS) + for (const auto* loader : OBJ_LOADERS) { if (loader->SupportsZone(zone)) { @@ -56,7 +58,7 @@ void ObjLoading::LoadIWDsInSearchPath(ISearchPath* searchPath) if (file.IsOpen()) { - const auto fileP = new FileAPI::File(std::move(file)); + auto* fileP = new FileAPI::File(std::move(file)); IWD* iwd = new IWD(path, fileP); if (iwd->Initialize()) @@ -83,7 +85,7 @@ SearchPaths ObjLoading::GetIWDSearchPaths() { SearchPaths iwdPaths; - for(auto iwd : IWD::Repository) + for (auto* iwd : IWD::Repository) { iwdPaths.IncludeSearchPath(iwd); } diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperGfxImage.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperGfxImage.cpp index 74ea1d94..c21ed552 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperGfxImage.cpp +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperGfxImage.cpp @@ -3,7 +3,7 @@ #include #include "ObjWriting.h" -#include "Image/IwiWriter27.h" +#include "Image/IwiWriter8.h" #include "Image/DdsWriter.h" using namespace IW4; @@ -16,7 +16,7 @@ AssetDumperGfxImage::AssetDumperGfxImage() m_writer = new DdsWriter(); break; case ObjWriting::Configuration_t::ImageOutputFormat_e::IWI: - m_writer = new IwiWriter27(); + m_writer = new iwi8::IwiWriter(); break; default: assert(false); diff --git a/src/ObjWriting/Game/IW4/ZoneDumperIW4.cpp b/src/ObjWriting/Game/IW4/ZoneDumperIW4.cpp index acf6927b..889f3574 100644 --- a/src/ObjWriting/Game/IW4/ZoneDumperIW4.cpp +++ b/src/ObjWriting/Game/IW4/ZoneDumperIW4.cpp @@ -35,7 +35,7 @@ bool ZoneDumper::DumpZone(Zone* zone, const std::string& basePath) const // DUMP_ASSET_POOL(AssetDumperMaterialVertexShader, m_material_vertex_shader); // DUMP_ASSET_POOL(AssetDumperMaterialVertexDeclaration, m_material_vertex_decl); // DUMP_ASSET_POOL(AssetDumperMaterialTechniqueSet, m_technique_set); - // DUMP_ASSET_POOL(AssetDumperGfxImage, m_image); + DUMP_ASSET_POOL(AssetDumperGfxImage, m_image); // DUMP_ASSET_POOL(AssetDumpersnd_alias_list_t, m_sound); // DUMP_ASSET_POOL(AssetDumperSndCurve, m_sound_curve); // DUMP_ASSET_POOL(AssetDumperLoadedSound, m_loaded_sound); diff --git a/src/ObjWriting/Image/IwiWriter8.cpp b/src/ObjWriting/Image/IwiWriter8.cpp index e69de29b..312e6143 100644 --- a/src/ObjWriting/Image/IwiWriter8.cpp +++ b/src/ObjWriting/Image/IwiWriter8.cpp @@ -0,0 +1,134 @@ +#include "IwiWriter8.h" + +#include + +using namespace iwi8; + +IwiWriter::IwiWriter() += default; + +IwiWriter::~IwiWriter() += default; + +IwiFormat IwiWriter::GetIwiFormatForImageFormat(const ImageFormat* imageFormat) +{ + switch (imageFormat->GetId()) + { + case ImageFormatId::R8_G8_B8: + return IwiFormat::IMG_FORMAT_BITMAP_RGB; + + case ImageFormatId::R8_G8_B8_A8: + return IwiFormat::IMG_FORMAT_BITMAP_RGBA; + + case ImageFormatId::A8: + return IwiFormat::IMG_FORMAT_BITMAP_ALPHA; + + case ImageFormatId::BC1: + return IwiFormat::IMG_FORMAT_DXT1; + + case ImageFormatId::BC2: + return IwiFormat::IMG_FORMAT_DXT3; + + case ImageFormatId::BC3: + return IwiFormat::IMG_FORMAT_DXT5; + + case ImageFormatId::BC5: + return IwiFormat::IMG_FORMAT_DXN; + + default: + return IwiFormat::IMG_FORMAT_INVALID; + } +} + +void IwiWriter::WriteVersion(FileAPI::IFile* file) +{ + IwiVersion version{}; + version.tag[0] = 'I'; + version.tag[1] = 'W'; + version.tag[2] = 'i'; + version.version = 8; + + file->Write(&version, sizeof IwiVersion, 1); +} + +void IwiWriter::FillHeader2D(IwiHeader* header, Texture2D* texture) +{ + header->dimensions[0] = static_cast(texture->GetWidth()); + header->dimensions[1] = static_cast(texture->GetHeight()); + header->dimensions[2] = 1ui16; +} + +void IwiWriter::FillHeaderCube(IwiHeader* header, TextureCube* texture) +{ + header->dimensions[0] = static_cast(texture->GetWidth()); + header->dimensions[1] = static_cast(texture->GetHeight()); + header->dimensions[2] = 1ui16; + header->flags |= IMG_FLAG_MAPTYPE_CUBE; +} + +void IwiWriter::FillHeader3D(IwiHeader* header, Texture3D* texture) +{ + header->dimensions[0] = static_cast(texture->GetWidth()); + header->dimensions[1] = static_cast(texture->GetHeight()); + header->dimensions[2] = static_cast(texture->GetDepth()); + header->flags |= IMG_FLAG_MAPTYPE_3D; +} + +bool IwiWriter::SupportsImageFormat(const ImageFormat* imageFormat) +{ + return GetIwiFormatForImageFormat(imageFormat) != IwiFormat::IMG_FORMAT_INVALID; +} + +std::string IwiWriter::GetFileExtension() +{ + return ".iwi"; +} + +void IwiWriter::DumpImage(FileAPI::IFile* file, Texture* texture) +{ + assert(file != nullptr); + assert(texture != nullptr); + + WriteVersion(file); + + IwiHeader header{}; + header.flags = 0; + + header.format = static_cast(GetIwiFormatForImageFormat(texture->GetFormat())); + + if (!texture->HasMipMaps()) + header.flags |= IMG_FLAG_NOMIPMAPS; + + size_t currentFileSize = sizeof IwiVersion + sizeof IwiHeader; + + const int textureMipCount = texture->HasMipMaps() ? texture->GetMipMapCount() : 1; + for (int currentMipLevel = textureMipCount - 1; currentMipLevel >= 0; currentMipLevel--) + { + const size_t mipLevelSize = texture->GetSizeOfMipLevel(currentMipLevel) * texture->GetFaceCount(); + currentFileSize += mipLevelSize; + + if (currentMipLevel < static_cast(_countof(iwi27::IwiHeader::fileSizeForPicmip))) + header.fileSizeForPicmip[currentMipLevel] = currentFileSize; + } + + if (auto* texture2D = dynamic_cast(texture)) + { + FillHeader2D(&header, texture2D); + } + else if (auto* textureCube = dynamic_cast(texture)) + { + FillHeaderCube(&header, textureCube); + } + else if (auto* texture3D = dynamic_cast(texture)) + { + FillHeader3D(&header, texture3D); + } + + file->Write(&header, sizeof iwi27::IwiHeader, 1); + + for (int currentMipLevel = textureMipCount - 1; currentMipLevel >= 0; currentMipLevel--) + { + const size_t mipLevelSize = texture->GetSizeOfMipLevel(currentMipLevel) * texture->GetFaceCount(); + file->Write(texture->GetBufferForMipLevel(currentMipLevel), 1, mipLevelSize); + } +} diff --git a/src/Unlinker/Unlinker.cpp b/src/Unlinker/Unlinker.cpp index 752af671..16af34cb 100644 --- a/src/Unlinker/Unlinker.cpp +++ b/src/Unlinker/Unlinker.cpp @@ -99,6 +99,11 @@ class Unlinker::Impl LoadSearchPath(m_last_zone_search_path); } + for(auto* iwd : IWD::Repository) + { + searchPathsForZone.IncludeSearchPath(iwd); + } + return searchPathsForZone; }