From 07fb470466c4afbb90b5c37d37f829c12ad4d31c Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 15 Apr 2021 13:42:19 +0200 Subject: [PATCH] add iw3 asset dumping basics --- .../IW3/AssetDumpers/AssetDumperGfxImage.cpp | 48 ++++++ .../IW3/AssetDumpers/AssetDumperGfxImage.h | 24 +++ .../AssetDumpers/AssetDumperLoadedSound.cpp | 76 ++++++++++ .../IW3/AssetDumpers/AssetDumperLoadedSound.h | 17 +++ .../AssetDumpers/AssetDumperLocalizeEntry.cpp | 53 +++++++ .../AssetDumpers/AssetDumperLocalizeEntry.h | 13 ++ .../IW3/AssetDumpers/AssetDumperRawFile.cpp | 24 +++ .../IW3/AssetDumpers/AssetDumperRawFile.h | 16 ++ .../AssetDumpers/AssetDumperStringTable.cpp | 36 +++++ .../IW3/AssetDumpers/AssetDumperStringTable.h | 17 +++ src/ObjWriting/Game/IW3/ZoneDumperIW3.cpp | 66 +++++++++ src/ObjWriting/Game/IW3/ZoneDumperIW3.h | 12 ++ src/ObjWriting/Image/IwiWriter6.cpp | 138 ++++++++++++++++++ src/ObjWriting/Image/IwiWriter6.h | 30 ++++ src/ObjWriting/ObjWriting.cpp | 2 + src/Unlinker/Unlinker.cpp | 2 + 16 files changed, 574 insertions(+) create mode 100644 src/ObjWriting/Image/IwiWriter6.cpp create mode 100644 src/ObjWriting/Image/IwiWriter6.h diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperGfxImage.cpp b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperGfxImage.cpp index e69de29b..988c67ca 100644 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperGfxImage.cpp +++ b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperGfxImage.cpp @@ -0,0 +1,48 @@ +#include "AssetDumperGfxImage.h" + +#include + +#include "ObjWriting.h" +#include "Image/IwiWriter6.h" +#include "Image/DdsWriter.h" + +using namespace IW3; + +AssetDumperGfxImage::AssetDumperGfxImage() +{ + switch (ObjWriting::Configuration.ImageOutputFormat) + { + case ObjWriting::Configuration_t::ImageOutputFormat_e::DDS: + m_writer = std::make_unique(); + break; + case ObjWriting::Configuration_t::ImageOutputFormat_e::IWI: + m_writer = std::make_unique(); + break; + default: + assert(false); + m_writer = nullptr; + break; + } +} + +bool AssetDumperGfxImage::ShouldDump(XAssetInfo* asset) +{ + const auto* image = asset->Asset(); + return image->cardMemory.platform[0] > 0; +} + +bool AssetDumperGfxImage::CanDumpAsRaw() +{ + return true; +} + +std::string AssetDumperGfxImage::GetFileNameForAsset(Zone* zone, XAssetInfo* asset) +{ + return "images/" + asset->m_name + m_writer->GetFileExtension(); +} + +void AssetDumperGfxImage::DumpRaw(AssetDumpingContext& context, XAssetInfo* asset, std::ostream& stream) +{ + const auto* image = asset->Asset(); + m_writer->DumpImage(stream, image->texture.texture); +} diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperGfxImage.h b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperGfxImage.h index e69de29b..af62f01c 100644 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperGfxImage.h +++ b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperGfxImage.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW3/IW3.h" +#include "Image/IImageWriter.h" + +namespace IW3 +{ + class AssetDumperGfxImage final : public AbstractAssetDumper + { + std::unique_ptr m_writer; + + protected: + bool ShouldDump(XAssetInfo* asset) override; + bool CanDumpAsRaw() override; + std::string GetFileNameForAsset(Zone* zone, XAssetInfo* asset) override; + void DumpRaw(AssetDumpingContext& context, XAssetInfo* asset, std::ostream& stream) override; + + public: + AssetDumperGfxImage(); + }; +} diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperLoadedSound.cpp b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperLoadedSound.cpp index e69de29b..b0e33cab 100644 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperLoadedSound.cpp +++ b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperLoadedSound.cpp @@ -0,0 +1,76 @@ +#include "AssetDumperLoadedSound.h" + +#include "Sound/WavTypes.h" + +using namespace IW3; + +bool AssetDumperLoadedSound::ShouldDump(XAssetInfo* asset) +{ + return true; +} + +bool AssetDumperLoadedSound::CanDumpAsRaw() +{ + return true; +} + +std::string AssetDumperLoadedSound::GetFileNameForAsset(Zone* zone, XAssetInfo* asset) +{ + return "sound/" + asset->m_name; +} + +void AssetDumperLoadedSound::DumpWavPcm(AssetDumpingContext& context, const LoadedSound* asset, std::ostream& stream) +{ + const auto riffMasterChunkSize = sizeof(WAV_CHUNK_ID_RIFF) + + sizeof(uint32_t) + + sizeof(WAV_WAVE_ID) + + sizeof(WavChunkHeader) + + sizeof(WavFormatChunkPcm) + + sizeof(WavChunkHeader) + + sizeof(asset->sound.info.data_len); + + stream.write(reinterpret_cast(&WAV_CHUNK_ID_RIFF), sizeof(WAV_CHUNK_ID_RIFF)); + stream.write(reinterpret_cast(&riffMasterChunkSize), sizeof(riffMasterChunkSize)); + stream.write(reinterpret_cast(&WAV_WAVE_ID), sizeof(WAV_WAVE_ID)); + + const WavChunkHeader formatChunkHeader + { + WAV_CHUNK_ID_FMT, + sizeof(WavFormatChunkPcm) + }; + stream.write(reinterpret_cast(&formatChunkHeader), sizeof(formatChunkHeader)); + + WavFormatChunkPcm formatChunk + { + WavFormat::PCM, + static_cast(asset->sound.info.channels), + asset->sound.info.rate, + asset->sound.info.rate * asset->sound.info.channels * asset->sound.info.bits / 8, + static_cast(asset->sound.info.block_size), + static_cast(asset->sound.info.bits) + }; + stream.write(reinterpret_cast(&formatChunk), sizeof(formatChunk)); + + const WavChunkHeader dataChunkHeader + { + WAV_CHUNK_ID_DATA, + asset->sound.info.data_len + }; + stream.write(reinterpret_cast(&dataChunkHeader), sizeof(dataChunkHeader)); + stream.write(asset->sound.data, asset->sound.info.data_len); +} + +void AssetDumperLoadedSound::DumpRaw(AssetDumpingContext& context, XAssetInfo* asset, std::ostream& stream) +{ + const auto* loadedSound = asset->Asset(); + switch (static_cast(loadedSound->sound.info.format)) + { + case WavFormat::PCM: + DumpWavPcm(context, loadedSound, stream); + break; + + default: + printf("Unknown format %i for loaded sound: %s\n", loadedSound->sound.info.format, loadedSound->name); + break; + } +} diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperLoadedSound.h b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperLoadedSound.h index e69de29b..5e36aaaf 100644 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperLoadedSound.h +++ b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperLoadedSound.h @@ -0,0 +1,17 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW3/IW3.h" + +namespace IW3 +{ + class AssetDumperLoadedSound final : public AbstractAssetDumper + { + static void DumpWavPcm(AssetDumpingContext& context, const LoadedSound* asset, std::ostream& stream); + protected: + bool ShouldDump(XAssetInfo* asset) override; + bool CanDumpAsRaw() override; + std::string GetFileNameForAsset(Zone* zone, XAssetInfo* asset) override; + void DumpRaw(AssetDumpingContext& context, XAssetInfo* asset, std::ostream& stream) override; + }; +} diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperLocalizeEntry.cpp b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperLocalizeEntry.cpp index e69de29b..d6ccf34d 100644 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperLocalizeEntry.cpp +++ b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperLocalizeEntry.cpp @@ -0,0 +1,53 @@ +#include "AssetDumperLocalizeEntry.h" + +#include +#include + +#include "Localize/LocalizeCommon.h" +#include "Dumping/Localize/StringFileDumper.h" + +using namespace IW3; +namespace fs = std::filesystem; + +void AssetDumperLocalizeEntry::DumpPool(AssetDumpingContext& context, AssetPool* pool) +{ + if (pool->m_asset_lookup.empty()) + return; + + const auto language = LocalizeCommon::GetNameOfLanguage(context.m_zone->m_language); + fs::path stringsPath(context.m_base_path); + stringsPath.append(language); + stringsPath.append("localizedstrings"); + + create_directories(stringsPath); + + auto stringFilePath(stringsPath); + stringFilePath.append(context.m_zone->m_name + ".str"); + + std::ofstream stringFile(stringFilePath, std::fstream::out | std::ofstream::binary); + + if (stringFile.is_open()) + { + StringFileDumper stringFileDumper(context.m_zone, stringFile); + + stringFileDumper.SetLanguageName(language); + + // Magic string. Original string files do have this config file. The purpose of the config file is unknown though. + stringFileDumper.SetConfigFile(R"(C:\trees\cod3\cod3\bin\StringEd.cfg)"); + + stringFileDumper.SetNotes(""); + + for (auto* localizeEntry : *pool) + { + stringFileDumper.WriteLocalizeEntry(localizeEntry->m_name, localizeEntry->Asset()->value); + } + + stringFileDumper.Finalize(); + + stringFile.close(); + } + else + { + printf("Could not create string file for dumping localized strings of zone '%s'\n", context.m_zone->m_name.c_str()); + } +} \ No newline at end of file diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperLocalizeEntry.h b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperLocalizeEntry.h index e69de29b..fdb1c273 100644 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperLocalizeEntry.h +++ b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperLocalizeEntry.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW3/IW3.h" + +namespace IW3 +{ + class AssetDumperLocalizeEntry final : public IAssetDumper + { + public: + void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; + }; +} diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperRawFile.cpp b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperRawFile.cpp index e69de29b..12a35ae0 100644 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperRawFile.cpp +++ b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperRawFile.cpp @@ -0,0 +1,24 @@ +#include "AssetDumperRawFile.h" + +using namespace IW3; + +bool AssetDumperRawFile::ShouldDump(XAssetInfo* asset) +{ + return true; +} + +bool AssetDumperRawFile::CanDumpAsRaw() +{ + return true; +} + +std::string AssetDumperRawFile::GetFileNameForAsset(Zone* zone, XAssetInfo* asset) +{ + return asset->m_name; +} + +void AssetDumperRawFile::DumpRaw(AssetDumpingContext& context, XAssetInfo* asset, std::ostream& stream) +{ + const auto* rawFile = asset->Asset(); + stream.write(rawFile->buffer, rawFile->len); +} diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperRawFile.h b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperRawFile.h index e69de29b..537bfe68 100644 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperRawFile.h +++ b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperRawFile.h @@ -0,0 +1,16 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW3/IW3.h" + +namespace IW3 +{ + class AssetDumperRawFile final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + bool CanDumpAsRaw() override; + std::string GetFileNameForAsset(Zone* zone, XAssetInfo* asset) override; + void DumpRaw(AssetDumpingContext& context, XAssetInfo* asset, std::ostream& stream) override; + }; +} diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperStringTable.cpp b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperStringTable.cpp index e69de29b..1b12b56f 100644 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperStringTable.cpp +++ b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperStringTable.cpp @@ -0,0 +1,36 @@ +#include "AssetDumperStringTable.h" + +#include "Csv/CsvStream.h" + +using namespace IW3; + +bool AssetDumperStringTable::ShouldDump(XAssetInfo* asset) +{ + return true; +} + +bool AssetDumperStringTable::CanDumpAsRaw() +{ + return true; +} + +std::string AssetDumperStringTable::GetFileNameForAsset(Zone* zone, XAssetInfo* asset) +{ + return asset->m_name; +} + +void AssetDumperStringTable::DumpRaw(AssetDumpingContext& context, XAssetInfo* asset, std::ostream& stream) +{ + const auto* stringTable = asset->Asset(); + CsvOutputStream csv(stream); + + for (auto row = 0; row < stringTable->rowCount; row++) + { + for (auto column = 0; column < stringTable->columnCount; column++) + { + csv.WriteColumn(stringTable->values[column + row * stringTable->columnCount]); + } + + csv.NextRow(); + } +} diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperStringTable.h b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperStringTable.h index e69de29b..4dd9c69e 100644 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperStringTable.h +++ b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperStringTable.h @@ -0,0 +1,17 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW3/IW3.h" + +namespace IW3 +{ + class AssetDumperStringTable final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + bool CanDumpAsRaw() override; + + std::string GetFileNameForAsset(Zone* zone, XAssetInfo* asset) override; + void DumpRaw(AssetDumpingContext& context, XAssetInfo* asset, std::ostream& stream) override; + }; +} diff --git a/src/ObjWriting/Game/IW3/ZoneDumperIW3.cpp b/src/ObjWriting/Game/IW3/ZoneDumperIW3.cpp index e69de29b..2495fc52 100644 --- a/src/ObjWriting/Game/IW3/ZoneDumperIW3.cpp +++ b/src/ObjWriting/Game/IW3/ZoneDumperIW3.cpp @@ -0,0 +1,66 @@ +#include "ZoneDumperIW3.h" + +#include "Game/IW3/GameIW3.h" +#include "Game/IW3/GameAssetPoolIW3.h" + +#include "AssetDumpers/AssetDumperGfxImage.h" +#include "AssetDumpers/AssetDumperLoadedSound.h" +#include "AssetDumpers/AssetDumperLocalizeEntry.h" +#include "AssetDumpers/AssetDumperRawFile.h" +#include "AssetDumpers/AssetDumperStringTable.h" +#include "AssetDumpers/AssetDumperWeapon.h" + +using namespace IW3; + +bool ZoneDumper::CanHandleZone(AssetDumpingContext& context) const +{ + return context.m_zone->m_game == &g_GameIW3; +} + +bool ZoneDumper::DumpZone(AssetDumpingContext& context) const +{ +#define DUMP_ASSET_POOL(dumperType, poolName) \ + if(assetPools->poolName) \ + { \ + dumperType dumper; \ + dumper.DumpPool(context, assetPools->poolName.get()); \ + } + + const auto* assetPools = dynamic_cast(context.m_zone->m_pools.get()); + + // DUMP_ASSET_POOL(AssetDumperPhysPreset, m_phys_preset) + // DUMP_ASSET_POOL(AssetDumperPhysCollmap, m_phys_collmap) + // DUMP_ASSET_POOL(AssetDumperXAnimParts, m_xanim_parts) + // DUMP_ASSET_POOL(AssetDumperXModel, m_xmodel) + // DUMP_ASSET_POOL(AssetDumperMaterial, m_material) + // DUMP_ASSET_POOL(AssetDumperMaterialPixelShader, m_material_pixel_shader) + // 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(AssetDumpersnd_alias_list_t, m_sound) + // DUMP_ASSET_POOL(AssetDumperSndCurve, m_sound_curve) + DUMP_ASSET_POOL(AssetDumperLoadedSound, m_loaded_sound) + // DUMP_ASSET_POOL(AssetDumperclipMap_t, m_clip_map) + // DUMP_ASSET_POOL(AssetDumperComWorld, m_com_world) + // DUMP_ASSET_POOL(AssetDumperGameWorldSp, m_game_world_sp) + // DUMP_ASSET_POOL(AssetDumperGameWorldMp, m_game_world_mp) + // DUMP_ASSET_POOL(AssetDumperMapEnts, m_map_ents) + // DUMP_ASSET_POOL(AssetDumperFxWorld, m_fx_world) + // DUMP_ASSET_POOL(AssetDumperGfxWorld, m_gfx_world) + // DUMP_ASSET_POOL(AssetDumperGfxLightDef, m_gfx_light_def) + // DUMP_ASSET_POOL(AssetDumperFont_s, m_font) + // DUMP_ASSET_POOL(AssetDumperMenuList, m_menu_list) + // DUMP_ASSET_POOL(AssetDumpermenuDef_t, m_menu_def) + DUMP_ASSET_POOL(AssetDumperLocalizeEntry, m_localize) + // DUMP_ASSET_POOL(AssetDumperWeapon, m_weapon) + // DUMP_ASSET_POOL(AssetDumperSndDriverGlobals, m_snd_driver_globals) + // DUMP_ASSET_POOL(AssetDumperFxEffectDef, m_fx) + // DUMP_ASSET_POOL(AssetDumperFxImpactTable, m_fx_impact_table) + DUMP_ASSET_POOL(AssetDumperRawFile, m_raw_file) + DUMP_ASSET_POOL(AssetDumperStringTable, m_string_table) + + return true; + +#undef DUMP_ASSET_POOL +} diff --git a/src/ObjWriting/Game/IW3/ZoneDumperIW3.h b/src/ObjWriting/Game/IW3/ZoneDumperIW3.h index e69de29b..4de44f6b 100644 --- a/src/ObjWriting/Game/IW3/ZoneDumperIW3.h +++ b/src/ObjWriting/Game/IW3/ZoneDumperIW3.h @@ -0,0 +1,12 @@ +#pragma once +#include "Dumping/IZoneDumper.h" + +namespace IW3 +{ + class ZoneDumper final : public IZoneDumper + { + public: + bool CanHandleZone(AssetDumpingContext& context) const override; + bool DumpZone(AssetDumpingContext& context) const override; + }; +} diff --git a/src/ObjWriting/Image/IwiWriter6.cpp b/src/ObjWriting/Image/IwiWriter6.cpp new file mode 100644 index 00000000..adc12f5e --- /dev/null +++ b/src/ObjWriting/Image/IwiWriter6.cpp @@ -0,0 +1,138 @@ +#include "IwiWriter6.h" + +#include + +using namespace iwi6; + +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(std::ostream & stream) +{ + IwiVersion version{}; + version.tag[0] = 'I'; + version.tag[1] = 'W'; + version.tag[2] = 'i'; + version.version = 6; + + stream.write(reinterpret_cast(&version), sizeof(IwiVersion)); +} + +void IwiWriter::FillHeader2D(IwiHeader * header, Texture2D * texture) +{ + header->dimensions[0] = static_cast(texture->GetWidth()); + header->dimensions[1] = static_cast(texture->GetHeight()); + header->dimensions[2] = 1u; +} + +void IwiWriter::FillHeaderCube(IwiHeader * header, TextureCube * texture) +{ + header->dimensions[0] = static_cast(texture->GetWidth()); + header->dimensions[1] = static_cast(texture->GetHeight()); + header->dimensions[2] = 1u; + header->flags |= IMG_FLAG_CUBEMAP; +} + +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_VOLMAP; +} + +bool IwiWriter::SupportsImageFormat(const ImageFormat * imageFormat) +{ + return GetIwiFormatForImageFormat(imageFormat) != IwiFormat::IMG_FORMAT_INVALID; +} + +std::string IwiWriter::GetFileExtension() +{ + return ".iwi"; +} + +void IwiWriter::DumpImage(std::ostream & stream, Texture * texture) +{ + assert(texture != nullptr); + + WriteVersion(stream); + + IwiHeader header{}; + header.flags = 0; + + header.format = static_cast(GetIwiFormatForImageFormat(texture->GetFormat())); + + if (!texture->HasMipMaps()) + header.flags |= IMG_FLAG_NOMIPMAPS; + + auto currentFileSize = sizeof(IwiVersion) + sizeof(IwiHeader); + + const auto textureMipCount = texture->HasMipMaps() ? texture->GetMipMapCount() : 1; + for (auto currentMipLevel = textureMipCount - 1; currentMipLevel >= 0; currentMipLevel--) + { + const auto mipLevelSize = texture->GetSizeOfMipLevel(currentMipLevel) * texture->GetFaceCount(); + currentFileSize += mipLevelSize; + + if (currentMipLevel < static_cast(std::extent::value)) + 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); + } + else + { + assert(false); + return; + } + + stream.write(reinterpret_cast(&header), sizeof(IwiHeader)); + + for (auto currentMipLevel = textureMipCount - 1; currentMipLevel >= 0; currentMipLevel--) + { + const auto mipLevelSize = texture->GetSizeOfMipLevel(currentMipLevel) * texture->GetFaceCount(); + stream.write(reinterpret_cast(texture->GetBufferForMipLevel(currentMipLevel)), mipLevelSize); + } +} diff --git a/src/ObjWriting/Image/IwiWriter6.h b/src/ObjWriting/Image/IwiWriter6.h new file mode 100644 index 00000000..2ca178d1 --- /dev/null +++ b/src/ObjWriting/Image/IwiWriter6.h @@ -0,0 +1,30 @@ +#pragma once + +#include "IImageWriter.h" +#include "Image/IwiTypes.h" + +namespace iwi6 +{ + class IwiWriter final : public IImageWriter + { + static IwiFormat GetIwiFormatForImageFormat(const ImageFormat* imageFormat); + + static void WriteVersion(std::ostream& stream); + static void FillHeader2D(IwiHeader* header, Texture2D* texture); + static void FillHeaderCube(IwiHeader* header, TextureCube* texture); + static void FillHeader3D(IwiHeader* header, Texture3D* texture); + + public: + IwiWriter(); + IwiWriter(const IwiWriter& other) = delete; + IwiWriter(IwiWriter&& other) noexcept = delete; + ~IwiWriter() override; + + IwiWriter& operator=(const IwiWriter& other) = delete; + IwiWriter& operator=(IwiWriter&& other) noexcept = delete; + + bool SupportsImageFormat(const ImageFormat* imageFormat) override; + std::string GetFileExtension() override; + void DumpImage(std::ostream& stream, Texture* texture) override; + }; +} diff --git a/src/ObjWriting/ObjWriting.cpp b/src/ObjWriting/ObjWriting.cpp index 500fa858..e40d76e3 100644 --- a/src/ObjWriting/ObjWriting.cpp +++ b/src/ObjWriting/ObjWriting.cpp @@ -1,5 +1,6 @@ #include "ObjWriting.h" #include "Dumping/IZoneDumper.h" +#include "Game/IW3/ZoneDumperIW3.h" #include "Game/IW4/ZoneDumperIW4.h" #include "Game/T6/ZoneDumperT6.h" @@ -7,6 +8,7 @@ ObjWriting::Configuration_t ObjWriting::Configuration; const IZoneDumper* const ZONE_DUMPER[] { + new IW3::ZoneDumper(), new IW4::ZoneDumper(), new T6::ZoneDumper() }; diff --git a/src/Unlinker/Unlinker.cpp b/src/Unlinker/Unlinker.cpp index af6b6a48..ebe6f28a 100644 --- a/src/Unlinker/Unlinker.cpp +++ b/src/Unlinker/Unlinker.cpp @@ -16,6 +16,7 @@ #include "ContentLister/ZoneDefWriter.h" #include "ObjContainer/IWD/IWD.h" #include "UnlinkerArgs.h" +#include "Game/IW3/ZoneDefWriterIW3.h" #include "Game/IW4/ZoneDefWriterIW4.h" #include "Game/T6/ZoneDefWriterT6.h" @@ -25,6 +26,7 @@ namespace fs = std::filesystem; const IZoneDefWriter* const ZONE_DEF_WRITERS[] { + new IW3::ZoneDefWriter(), new IW4::ZoneDefWriter(), new T6::ZoneDefWriter() };