2
0
mirror of https://github.com/Laupetin/OpenAssetTools.git synced 2026-01-10 02:31:49 +00:00

Merge pull request #641 from Laupetin/feature/feature-parity-image

feat: Feature parity for games
This commit is contained in:
Jan
2026-01-05 11:21:48 +01:00
committed by GitHub
88 changed files with 3044 additions and 2094 deletions

View File

@@ -1515,7 +1515,8 @@ namespace IW3
MAPTYPE_2D = 0x3, MAPTYPE_2D = 0x3,
MAPTYPE_3D = 0x4, MAPTYPE_3D = 0x4,
MAPTYPE_CUBE = 0x5, MAPTYPE_CUBE = 0x5,
MAPTYPE_COUNT = 0x6,
MAPTYPE_COUNT
}; };
enum TextureSemantic enum TextureSemantic

View File

@@ -1181,6 +1181,18 @@ namespace IW4
int platform[2]; int platform[2];
}; };
enum MapType
{
MAPTYPE_NONE = 0x0,
MAPTYPE_INVALID1 = 0x1,
MAPTYPE_1D = 0x2,
MAPTYPE_2D = 0x3,
MAPTYPE_3D = 0x4,
MAPTYPE_CUBE = 0x5,
MAPTYPE_COUNT
};
enum TextureSemantic enum TextureSemantic
{ {
TS_2D = 0x0, TS_2D = 0x0,
@@ -1197,6 +1209,19 @@ namespace IW4
TS_WATER_MAP = 0xB, TS_WATER_MAP = 0xB,
}; };
enum ImageCategory
{
IMG_CATEGORY_UNKNOWN = 0x0,
IMG_CATEGORY_AUTO_GENERATED = 0x1,
IMG_CATEGORY_LIGHTMAP = 0x2,
IMG_CATEGORY_LOAD_FROM_FILE = 0x3,
IMG_CATEGORY_RAW = 0x4,
IMG_CATEGORY_FIRST_UNMANAGED = 0x5,
IMG_CATEGORY_WATER = 0x5,
IMG_CATEGORY_RENDERTARGET = 0x6,
IMG_CATEGORY_TEMP = 0x7,
};
struct GfxImage struct GfxImage
{ {
GfxTexture texture; GfxTexture texture;

View File

@@ -732,23 +732,6 @@ namespace IW5
GfxImage* image; GfxImage* image;
}; };
enum TextureSemantic
{
TS_2D = 0x0,
TS_FUNCTION = 0x1,
TS_COLOR_MAP = 0x2,
TS_DETAIL_MAP = 0x3,
TS_UNUSED_2 = 0x4,
TS_NORMAL_MAP = 0x5,
TS_UNUSED_3 = 0x6,
TS_UNUSED_4 = 0x7,
TS_SPECULAR_MAP = 0x8,
TS_UNUSED_5 = 0x9,
TS_UNUSED_6 = 0xA,
TS_WATER_MAP = 0xB,
TS_DISPLACEMENT_MAP = 0xC
};
union MaterialTextureDefInfo union MaterialTextureDefInfo
{ {
GfxImage* image; GfxImage* image;
@@ -1157,6 +1140,48 @@ namespace IW5
int platform[2]; int platform[2];
}; };
enum MapType
{
MAPTYPE_NONE = 0x0,
MAPTYPE_INVALID1 = 0x1,
MAPTYPE_1D = 0x2,
MAPTYPE_2D = 0x3,
MAPTYPE_3D = 0x4,
MAPTYPE_CUBE = 0x5,
MAPTYPE_COUNT
};
enum TextureSemantic
{
TS_2D = 0x0,
TS_FUNCTION = 0x1,
TS_COLOR_MAP = 0x2,
TS_DETAIL_MAP = 0x3,
TS_UNUSED_2 = 0x4,
TS_NORMAL_MAP = 0x5,
TS_UNUSED_3 = 0x6,
TS_UNUSED_4 = 0x7,
TS_SPECULAR_MAP = 0x8,
TS_UNUSED_5 = 0x9,
TS_UNUSED_6 = 0xA,
TS_WATER_MAP = 0xB,
TS_DISPLACEMENT_MAP = 0xC
};
enum ImageCategory
{
IMG_CATEGORY_UNKNOWN = 0x0,
IMG_CATEGORY_AUTO_GENERATED = 0x1,
IMG_CATEGORY_LIGHTMAP = 0x2,
IMG_CATEGORY_LOAD_FROM_FILE = 0x3,
IMG_CATEGORY_RAW = 0x4,
IMG_CATEGORY_FIRST_UNMANAGED = 0x5,
IMG_CATEGORY_WATER = 0x5,
IMG_CATEGORY_RENDERTARGET = 0x6,
IMG_CATEGORY_TEMP = 0x7,
};
struct GfxImage struct GfxImage
{ {
GfxTexture texture; GfxTexture texture;

View File

@@ -1242,6 +1242,18 @@ namespace T5
int platform[2]; int platform[2];
}; };
enum MapType
{
MAPTYPE_NONE = 0x0,
MAPTYPE_INVALID1 = 0x1,
MAPTYPE_INVALID2 = 0x2,
MAPTYPE_2D = 0x3,
MAPTYPE_3D = 0x4,
MAPTYPE_CUBE = 0x5,
MAPTYPE_COUNT
};
enum TextureSemantic enum TextureSemantic
{ {
TS_2D = 0x0, TS_2D = 0x0,

View File

@@ -879,6 +879,18 @@ namespace T6
uint32_t valid : 1; uint32_t valid : 1;
}; };
enum MapType
{
MAPTYPE_NONE = 0x0,
MAPTYPE_INVALID1 = 0x1,
MAPTYPE_INVALID2 = 0x2,
MAPTYPE_2D = 0x3,
MAPTYPE_3D = 0x4,
MAPTYPE_CUBE = 0x5,
MAPTYPE_COUNT
};
enum TextureSemantic enum TextureSemantic
{ {
TS_2D = 0x0, TS_2D = 0x0,

View File

@@ -38,11 +38,13 @@ function ImageConverter:project()
self:include(includes) self:include(includes)
Utils:include(includes) Utils:include(includes)
Common:include(includes)
ObjImage:include(includes) ObjImage:include(includes)
Raw:use() Raw:use()
links:linkto(Utils) links:linkto(Utils)
links:linkto(Common)
links:linkto(ObjImage) links:linkto(ObjImage)
links:linkall() links:linkall()
end end

View File

@@ -1,5 +1,6 @@
#include "ImageConverter.h" #include "ImageConverter.h"
#include "Game/IGame.h"
#include "Image/DdsLoader.h" #include "Image/DdsLoader.h"
#include "Image/DdsWriter.h" #include "Image/DdsWriter.h"
#include "Image/IwiLoader.h" #include "Image/IwiLoader.h"
@@ -17,10 +18,12 @@
#include <format> #include <format>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <optional>
namespace fs = std::filesystem; namespace fs = std::filesystem;
using namespace image;
namespace image_converter namespace
{ {
constexpr auto EXTENSION_IWI = ".iwi"; constexpr auto EXTENSION_IWI = ".iwi";
constexpr auto EXTENSION_DDS = ".dds"; constexpr auto EXTENSION_DDS = ".dds";
@@ -29,7 +32,7 @@ namespace image_converter
{ {
public: public:
ImageConverterImpl() ImageConverterImpl()
: m_game_to_convert_to(image_converter::Game::UNKNOWN) : m_game_to_convert_to(std::nullopt)
{ {
} }
@@ -76,8 +79,8 @@ namespace image_converter
return false; return false;
} }
const auto texture = iwi::LoadIwi(file); const auto loadResult = image::LoadIwi(file);
if (!texture) if (!loadResult)
return false; return false;
auto outPath = iwiPath; auto outPath = iwiPath;
@@ -90,7 +93,7 @@ namespace image_converter
return false; return false;
} }
m_dds_writer.DumpImage(outFile, texture.get()); m_dds_writer.DumpImage(outFile, loadResult->m_texture.get());
return true; return true;
} }
@@ -103,7 +106,7 @@ namespace image_converter
return false; return false;
} }
const auto texture = dds::LoadDds(file); const auto texture = image::LoadDds(file);
if (!texture) if (!texture)
return false; return false;
@@ -129,23 +132,23 @@ namespace image_converter
if (m_iwi_writer) if (m_iwi_writer)
return true; return true;
if (m_game_to_convert_to == Game::UNKNOWN && !ShowGameTui()) if (!m_game_to_convert_to && !ShowGameTui())
return false; return false;
switch (m_game_to_convert_to) switch (*m_game_to_convert_to)
{ {
case Game::IW3: case GameId::IW3:
m_iwi_writer = std::make_unique<iwi6::IwiWriter>(); m_iwi_writer = std::make_unique<image::iwi6::IwiWriter>();
break; break;
case Game::IW4: case GameId::IW4:
case Game::IW5: case GameId::IW5:
m_iwi_writer = std::make_unique<iwi8::IwiWriter>(); m_iwi_writer = std::make_unique<image::iwi8::IwiWriter>();
break; break;
case Game::T5: case GameId::T5:
m_iwi_writer = std::make_unique<iwi13::IwiWriter>(); m_iwi_writer = std::make_unique<image::iwi13::IwiWriter>();
break; break;
case Game::T6: case GameId::T6:
m_iwi_writer = std::make_unique<iwi27::IwiWriter>(); m_iwi_writer = std::make_unique<image::iwi27::IwiWriter>();
break; break;
default: default:
assert(false); assert(false);
@@ -170,19 +173,19 @@ namespace image_converter
switch (num) switch (num)
{ {
case 1: case 1:
m_game_to_convert_to = Game::IW3; m_game_to_convert_to = GameId::IW3;
break; break;
case 2: case 2:
m_game_to_convert_to = Game::IW4; m_game_to_convert_to = GameId::IW4;
break; break;
case 3: case 3:
m_game_to_convert_to = Game::IW5; m_game_to_convert_to = GameId::IW5;
break; break;
case 4: case 4:
m_game_to_convert_to = Game::T5; m_game_to_convert_to = GameId::T5;
break; break;
case 5: case 5:
m_game_to_convert_to = Game::T6; m_game_to_convert_to = GameId::T6;
break; break;
default: default:
con::error("Invalid input"); con::error("Invalid input");
@@ -193,13 +196,13 @@ namespace image_converter
} }
ImageConverterArgs m_args; ImageConverterArgs m_args;
image_converter::Game m_game_to_convert_to; std::optional<GameId> m_game_to_convert_to;
DdsWriter m_dds_writer; DdsWriter m_dds_writer;
std::unique_ptr<IImageWriter> m_iwi_writer; std::unique_ptr<ImageWriter> m_iwi_writer;
}; };
} // namespace image_converter } // namespace
std::unique_ptr<ImageConverter> ImageConverter::Create() std::unique_ptr<ImageConverter> ImageConverter::Create()
{ {
return std::make_unique<image_converter::ImageConverterImpl>(); return std::make_unique<ImageConverterImpl>();
} }

View File

@@ -86,7 +86,7 @@ const CommandLineOption* const COMMAND_LINE_OPTIONS[]{
}; };
ImageConverterArgs::ImageConverterArgs() ImageConverterArgs::ImageConverterArgs()
: m_game_to_convert_to(image_converter::Game::UNKNOWN), : m_game_to_convert_to(std::nullopt),
m_argument_parser(COMMAND_LINE_OPTIONS, std::extent_v<decltype(COMMAND_LINE_OPTIONS)>) m_argument_parser(COMMAND_LINE_OPTIONS, std::extent_v<decltype(COMMAND_LINE_OPTIONS)>)
{ {
} }

View File

@@ -1,24 +1,13 @@
#pragma once #pragma once
#include "Game/IGame.h"
#include "Utils/Arguments/ArgumentParser.h" #include "Utils/Arguments/ArgumentParser.h"
#include <cstdint> #include <cstdint>
#include <optional>
#include <string> #include <string>
#include <vector> #include <vector>
namespace image_converter
{
enum class Game : std::uint8_t
{
UNKNOWN,
IW3,
IW4,
IW5,
T5,
T6
};
} // namespace image_converter
class ImageConverterArgs class ImageConverterArgs
{ {
public: public:
@@ -26,7 +15,7 @@ public:
bool ParseArgs(int argc, const char** argv, bool& shouldContinue); bool ParseArgs(int argc, const char** argv, bool& shouldContinue);
std::vector<std::string> m_files_to_convert; std::vector<std::string> m_files_to_convert;
image_converter::Game m_game_to_convert_to; std::optional<GameId> m_game_to_convert_to;
private: private:
/** /**

View File

@@ -9,7 +9,9 @@
#include <iostream> #include <iostream>
#include <memory> #include <memory>
namespace dds using namespace image;
namespace
{ {
class DdsLoaderInternal class DdsLoaderInternal
{ {
@@ -193,7 +195,7 @@ namespace dds
return ReadPixelFormatUnsigned(pf); return ReadPixelFormatUnsigned(pf);
} }
_NODISCARD bool ReadHeader() [[nodiscard]] bool ReadHeader()
{ {
DDS_HEADER header{}; DDS_HEADER header{};
m_stream.read(reinterpret_cast<char*>(&header), sizeof(header)); m_stream.read(reinterpret_cast<char*>(&header), sizeof(header));
@@ -277,10 +279,13 @@ namespace dds
unsigned m_depth; unsigned m_depth;
const ImageFormat* m_format; const ImageFormat* m_format;
}; };
} // namespace
namespace image
{
std::unique_ptr<Texture> LoadDds(std::istream& stream) std::unique_ptr<Texture> LoadDds(std::istream& stream)
{ {
DdsLoaderInternal internal(stream); DdsLoaderInternal internal(stream);
return internal.LoadDds(); return internal.LoadDds();
} }
} // namespace dds } // namespace image

View File

@@ -5,7 +5,7 @@
#include <istream> #include <istream>
#include <memory> #include <memory>
namespace dds namespace image
{ {
std::unique_ptr<Texture> LoadDds(std::istream& stream); std::unique_ptr<Texture> LoadDds(std::istream& stream);
} }

View File

@@ -2,6 +2,8 @@
#include <cstdint> #include <cstdint>
namespace image
{
constexpr uint32_t MakeFourCc(const char ch0, const char ch1, const char ch2, const char ch3) constexpr uint32_t MakeFourCc(const char ch0, const char ch1, const char ch2, const char ch3)
{ {
return static_cast<uint32_t>(ch0) | static_cast<uint32_t>(ch1) << 8 | static_cast<uint32_t>(ch2) << 16 | static_cast<uint32_t>(ch3) << 24; return static_cast<uint32_t>(ch0) | static_cast<uint32_t>(ch1) << 8 | static_cast<uint32_t>(ch2) << 16 | static_cast<uint32_t>(ch3) << 24;
@@ -109,3 +111,5 @@ struct DDS_HEADER_DXT10
uint32_t arraySize; uint32_t arraySize;
uint32_t miscFlags2; uint32_t miscFlags2;
}; };
} // namespace image

View File

@@ -7,6 +7,10 @@
#include <map> #include <map>
#include <memory> #include <memory>
using namespace image;
namespace
{
const std::map<ImageFormatId, ImageFormatId> DDS_CONVERSION_TABLE{ const std::map<ImageFormatId, ImageFormatId> DDS_CONVERSION_TABLE{
{ImageFormatId::R8_G8_B8, ImageFormatId::B8_G8_R8_X8}, {ImageFormatId::R8_G8_B8, ImageFormatId::B8_G8_R8_X8},
}; };
@@ -220,7 +224,10 @@ public:
std::unique_ptr<Texture> m_converted_texture; std::unique_ptr<Texture> m_converted_texture;
bool m_use_dx10_extension; bool m_use_dx10_extension;
}; };
} // namespace
namespace image
{
DdsWriter::~DdsWriter() = default; DdsWriter::~DdsWriter() = default;
bool DdsWriter::SupportsImageFormat(const ImageFormat* imageFormat) bool DdsWriter::SupportsImageFormat(const ImageFormat* imageFormat)
@@ -238,3 +245,4 @@ void DdsWriter::DumpImage(std::ostream& stream, const Texture* texture)
DdsWriterInternal internal(stream, texture); DdsWriterInternal internal(stream, texture);
internal.DumpImage(); internal.DumpImage();
} }
} // namespace image

View File

@@ -1,7 +1,10 @@
#pragma once #pragma once
#include "IImageWriter.h"
class DdsWriter final : public IImageWriter #include "ImageWriter.h"
namespace image
{
class DdsWriter final : public ImageWriter
{ {
public: public:
~DdsWriter() override; ~DdsWriter() override;
@@ -10,3 +13,4 @@ public:
std::string GetFileExtension() override; std::string GetFileExtension() override;
void DumpImage(std::ostream& stream, const Texture* texture) override; void DumpImage(std::ostream& stream, const Texture* texture) override;
}; };
} // namespace image

View File

@@ -2,6 +2,8 @@
#include <cstring> #include <cstring>
namespace image
{
Dx12TextureLoader::Dx12TextureLoader() Dx12TextureLoader::Dx12TextureLoader()
: m_format(oat::DXGI_FORMAT_UNKNOWN), : m_format(oat::DXGI_FORMAT_UNKNOWN),
m_type(TextureType::T_2D), m_type(TextureType::T_2D),
@@ -102,3 +104,4 @@ std::unique_ptr<Texture> Dx12TextureLoader::LoadTexture(const void* data)
return texture; return texture;
} }
} // namespace image

View File

@@ -6,6 +6,8 @@
#include <memory> #include <memory>
#include <unordered_map> #include <unordered_map>
namespace image
{
class Dx12TextureLoader class Dx12TextureLoader
{ {
public: public:
@@ -32,3 +34,4 @@ private:
unsigned m_height; unsigned m_height;
unsigned m_depth; unsigned m_depth;
}; };
} // namespace image

View File

@@ -2,6 +2,8 @@
#include <cstring> #include <cstring>
namespace image
{
Dx9TextureLoader::Dx9TextureLoader() Dx9TextureLoader::Dx9TextureLoader()
: m_format(oat::D3DFMT_UNKNOWN), : m_format(oat::D3DFMT_UNKNOWN),
m_type(TextureType::T_2D), m_type(TextureType::T_2D),
@@ -102,3 +104,4 @@ std::unique_ptr<Texture> Dx9TextureLoader::LoadTexture(const void* data)
return texture; return texture;
} }
} // namespace image

View File

@@ -5,6 +5,8 @@
#include <memory> #include <memory>
namespace image
{
class Dx9TextureLoader class Dx9TextureLoader
{ {
public: public:
@@ -29,3 +31,4 @@ private:
unsigned m_height; unsigned m_height;
unsigned m_depth; unsigned m_depth;
}; };
} // namespace image

View File

@@ -1,21 +0,0 @@
#pragma once
#include "Image/Texture.h"
#include <ostream>
#include <string>
class IImageWriter
{
public:
IImageWriter() = default;
virtual ~IImageWriter() = default;
IImageWriter(const IImageWriter& other) = default;
IImageWriter(IImageWriter&& other) noexcept = default;
IImageWriter& operator=(const IImageWriter& other) = default;
IImageWriter& operator=(IImageWriter&& other) noexcept = default;
virtual bool SupportsImageFormat(const ImageFormat* imageFormat) = 0;
virtual std::string GetFileExtension() = 0;
virtual void DumpImage(std::ostream& stream, const Texture* texture) = 0;
};

View File

@@ -1,5 +1,7 @@
#include "ImageFormat.h" #include "ImageFormat.h"
namespace image
{
ImageFormat::ImageFormat(const ImageFormatId id, const oat::D3DFORMAT d3dFormat, const oat::DXGI_FORMAT dxgiFormat) ImageFormat::ImageFormat(const ImageFormatId id, const oat::D3DFORMAT d3dFormat, const oat::DXGI_FORMAT dxgiFormat)
: m_id(id), : m_id(id),
m_d3d_format(d3dFormat), m_d3d_format(d3dFormat),
@@ -173,3 +175,4 @@ const ImageFormat* const ImageFormat::ALL_FORMATS[static_cast<unsigned>(ImageFor
&FORMAT_BC4, &FORMAT_BC4,
&FORMAT_BC5, &FORMAT_BC5,
}; };
} // namespace image

View File

@@ -6,6 +6,8 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
namespace image
{
enum class ImageFormatId enum class ImageFormatId
{ {
UNKNOWN = -1, UNKNOWN = -1,
@@ -120,3 +122,4 @@ public:
[[nodiscard]] size_t GetPitch(unsigned mipLevel, unsigned width) const override; [[nodiscard]] size_t GetPitch(unsigned mipLevel, unsigned width) const override;
[[nodiscard]] size_t GetSizeOfMipLevel(unsigned mipLevel, unsigned width, unsigned height, unsigned depth) const override; [[nodiscard]] size_t GetSizeOfMipLevel(unsigned mipLevel, unsigned width, unsigned height, unsigned depth) const override;
}; };
} // namespace image

View File

@@ -0,0 +1,24 @@
#pragma once
#include "Image/Texture.h"
#include <ostream>
#include <string>
namespace image
{
class ImageWriter
{
public:
ImageWriter() = default;
virtual ~ImageWriter() = default;
ImageWriter(const ImageWriter& other) = default;
ImageWriter(ImageWriter&& other) noexcept = default;
ImageWriter& operator=(const ImageWriter& other) = default;
ImageWriter& operator=(ImageWriter&& other) noexcept = default;
virtual bool SupportsImageFormat(const ImageFormat* imageFormat) = 0;
virtual std::string GetFileExtension() = 0;
virtual void DumpImage(std::ostream& stream, const Texture* texture) = 0;
};
} // namespace image

View File

@@ -8,7 +8,9 @@
#include <iostream> #include <iostream>
#include <type_traits> #include <type_traits>
namespace iwi using namespace image;
namespace
{ {
const ImageFormat* GetFormat6(int8_t format) const ImageFormat* GetFormat6(int8_t format)
{ {
@@ -47,17 +49,20 @@ namespace iwi
return nullptr; return nullptr;
} }
std::unique_ptr<Texture> LoadIwi6(std::istream& stream) std::optional<IwiLoaderResult> LoadIwi6(std::istream& stream)
{ {
iwi6::IwiHeader header{}; iwi6::IwiHeader header{};
stream.read(reinterpret_cast<char*>(&header), sizeof(header)); stream.read(reinterpret_cast<char*>(&header), sizeof(header));
if (stream.gcount() != sizeof(header)) if (stream.gcount() != sizeof(header))
return nullptr; {
con::error("IWI header corrupted");
return std::nullopt;
}
const auto* format = GetFormat6(header.format); const auto* format = GetFormat6(header.format);
if (format == nullptr) if (format == nullptr)
return nullptr; return std::nullopt;
auto width = header.dimensions[0]; auto width = header.dimensions[0];
auto height = header.dimensions[1]; auto height = header.dimensions[1];
@@ -74,7 +79,7 @@ namespace iwi
texture->Allocate(); texture->Allocate();
auto currentFileSize = sizeof(iwi6::IwiHeader) + sizeof(IwiVersion); auto currentFileSize = sizeof(iwi6::IwiHeader) + sizeof(IwiVersionHeader);
const auto mipMapCount = hasMipMaps ? texture->GetMipMapCount() : 1; const auto mipMapCount = hasMipMaps ? texture->GetMipMapCount() : 1;
for (auto currentMipLevel = mipMapCount - 1; currentMipLevel >= 0; currentMipLevel--) for (auto currentMipLevel = mipMapCount - 1; currentMipLevel >= 0; currentMipLevel--)
@@ -86,18 +91,30 @@ namespace iwi
&& currentFileSize != header.fileSizeForPicmip[currentMipLevel]) && currentFileSize != header.fileSizeForPicmip[currentMipLevel])
{ {
con::error("Iwi has invalid file size for picmip {}", currentMipLevel); con::error("Iwi has invalid file size for picmip {}", currentMipLevel);
return nullptr; return std::nullopt;
} }
stream.read(reinterpret_cast<char*>(texture->GetBufferForMipLevel(currentMipLevel)), sizeOfMipLevel); stream.read(reinterpret_cast<char*>(texture->GetBufferForMipLevel(currentMipLevel)), sizeOfMipLevel);
if (stream.gcount() != sizeOfMipLevel) if (stream.gcount() != sizeOfMipLevel)
{ {
con::error("Unexpected eof of iwi in mip level {}", currentMipLevel); con::error("Unexpected eof of iwi in mip level {}", currentMipLevel);
return nullptr; return std::nullopt;
} }
} }
return texture; CommonIwiMetaData meta{
.m_no_picmip = (header.flags & iwi6::IwiFlags::IMG_FLAG_NOPICMIP) != 0,
.m_streaming = (header.flags & iwi6::IwiFlags::IMG_FLAG_STREAMING) != 0,
.m_clamp_u = (header.flags & iwi6::IwiFlags::IMG_FLAG_CLAMP_U) != 0,
.m_clamp_v = (header.flags & iwi6::IwiFlags::IMG_FLAG_CLAMP_V) != 0,
.m_dynamic = (header.flags & iwi6::IwiFlags::IMG_FLAG_DYNAMIC) != 0,
};
return IwiLoaderResult{
.m_version = IwiVersion::IWI_6,
.m_meta = meta,
.m_texture = std::move(texture),
};
} }
const ImageFormat* GetFormat8(int8_t format) const ImageFormat* GetFormat8(int8_t format)
@@ -145,17 +162,20 @@ namespace iwi
return nullptr; return nullptr;
} }
std::unique_ptr<Texture> LoadIwi8(std::istream& stream) std::optional<IwiLoaderResult> LoadIwi8(std::istream& stream)
{ {
iwi8::IwiHeader header{}; iwi8::IwiHeader header{};
stream.read(reinterpret_cast<char*>(&header), sizeof(header)); stream.read(reinterpret_cast<char*>(&header), sizeof(header));
if (stream.gcount() != sizeof(header)) if (stream.gcount() != sizeof(header))
return nullptr; {
con::error("IWI header corrupted");
return std::nullopt;
}
const auto* format = GetFormat8(header.format); const auto* format = GetFormat8(header.format);
if (format == nullptr) if (format == nullptr)
return nullptr; return std::nullopt;
auto width = header.dimensions[0]; auto width = header.dimensions[0];
auto height = header.dimensions[1]; auto height = header.dimensions[1];
@@ -178,17 +198,17 @@ namespace iwi
else if ((header.flags & iwi8::IwiFlags::IMG_FLAG_MAPTYPE_MASK) == iwi8::IwiFlags::IMG_FLAG_MAPTYPE_1D) else if ((header.flags & iwi8::IwiFlags::IMG_FLAG_MAPTYPE_MASK) == iwi8::IwiFlags::IMG_FLAG_MAPTYPE_1D)
{ {
con::error("Iwi has unsupported map type 1D"); con::error("Iwi has unsupported map type 1D");
return nullptr; return std::nullopt;
} }
else else
{ {
con::error("Iwi has unsupported map type"); con::error("Iwi has unsupported map type");
return nullptr; return std::nullopt;
} }
texture->Allocate(); texture->Allocate();
auto currentFileSize = sizeof(iwi8::IwiHeader) + sizeof(IwiVersion); auto currentFileSize = sizeof(iwi8::IwiHeader) + sizeof(IwiVersionHeader);
const auto mipMapCount = hasMipMaps ? texture->GetMipMapCount() : 1; const auto mipMapCount = hasMipMaps ? texture->GetMipMapCount() : 1;
for (auto currentMipLevel = mipMapCount - 1; currentMipLevel >= 0; currentMipLevel--) for (auto currentMipLevel = mipMapCount - 1; currentMipLevel >= 0; currentMipLevel--)
@@ -200,18 +220,30 @@ namespace iwi
&& currentFileSize != header.fileSizeForPicmip[currentMipLevel]) && currentFileSize != header.fileSizeForPicmip[currentMipLevel])
{ {
con::error("Iwi has invalid file size for picmip {}", currentMipLevel); con::error("Iwi has invalid file size for picmip {}", currentMipLevel);
return nullptr; return std::nullopt;
} }
stream.read(reinterpret_cast<char*>(texture->GetBufferForMipLevel(currentMipLevel)), sizeOfMipLevel); stream.read(reinterpret_cast<char*>(texture->GetBufferForMipLevel(currentMipLevel)), sizeOfMipLevel);
if (stream.gcount() != sizeOfMipLevel) if (stream.gcount() != sizeOfMipLevel)
{ {
con::error("Unexpected eof of iwi in mip level {}", currentMipLevel); con::error("Unexpected eof of iwi in mip level {}", currentMipLevel);
return nullptr; return std::nullopt;
} }
} }
return texture; CommonIwiMetaData meta{
.m_no_picmip = (header.flags & iwi8::IwiFlags::IMG_FLAG_NOPICMIP) != 0,
.m_streaming = (header.flags & iwi8::IwiFlags::IMG_FLAG_STREAMING) != 0,
.m_clamp_u = (header.flags & iwi8::IwiFlags::IMG_FLAG_CLAMP_U) != 0,
.m_clamp_v = (header.flags & iwi8::IwiFlags::IMG_FLAG_CLAMP_V) != 0,
.m_dynamic = (header.flags & iwi8::IwiFlags::IMG_FLAG_DYNAMIC) != 0,
};
return IwiLoaderResult{
.m_version = IwiVersion::IWI_8,
.m_meta = meta,
.m_texture = std::move(texture),
};
} }
const ImageFormat* GetFormat13(int8_t format) const ImageFormat* GetFormat13(int8_t format)
@@ -256,17 +288,20 @@ namespace iwi
return nullptr; return nullptr;
} }
std::unique_ptr<Texture> LoadIwi13(std::istream& stream) std::optional<IwiLoaderResult> LoadIwi13(std::istream& stream)
{ {
iwi13::IwiHeader header{}; iwi13::IwiHeader header{};
stream.read(reinterpret_cast<char*>(&header), sizeof(header)); stream.read(reinterpret_cast<char*>(&header), sizeof(header));
if (stream.gcount() != sizeof(header)) if (stream.gcount() != sizeof(header))
return nullptr; {
con::error("IWI header corrupted");
return std::nullopt;
}
const auto* format = GetFormat6(header.format); const auto* format = GetFormat6(header.format);
if (format == nullptr) if (format == nullptr)
return nullptr; return std::nullopt;
auto width = header.dimensions[0]; auto width = header.dimensions[0];
auto height = header.dimensions[1]; auto height = header.dimensions[1];
@@ -283,7 +318,7 @@ namespace iwi
texture->Allocate(); texture->Allocate();
auto currentFileSize = sizeof(iwi13::IwiHeader) + sizeof(IwiVersion); auto currentFileSize = sizeof(iwi13::IwiHeader) + sizeof(IwiVersionHeader);
const auto mipMapCount = hasMipMaps ? texture->GetMipMapCount() : 1; const auto mipMapCount = hasMipMaps ? texture->GetMipMapCount() : 1;
for (auto currentMipLevel = mipMapCount - 1; currentMipLevel >= 0; currentMipLevel--) for (auto currentMipLevel = mipMapCount - 1; currentMipLevel >= 0; currentMipLevel--)
@@ -295,18 +330,31 @@ namespace iwi
&& currentFileSize != header.fileSizeForPicmip[currentMipLevel]) && currentFileSize != header.fileSizeForPicmip[currentMipLevel])
{ {
con::error("Iwi has invalid file size for picmip {}", currentMipLevel); con::error("Iwi has invalid file size for picmip {}", currentMipLevel);
return nullptr; return std::nullopt;
} }
stream.read(reinterpret_cast<char*>(texture->GetBufferForMipLevel(currentMipLevel)), sizeOfMipLevel); stream.read(reinterpret_cast<char*>(texture->GetBufferForMipLevel(currentMipLevel)), sizeOfMipLevel);
if (stream.gcount() != sizeOfMipLevel) if (stream.gcount() != sizeOfMipLevel)
{ {
con::error("Unexpected eof of iwi in mip level {}", currentMipLevel); con::error("Unexpected eof of iwi in mip level {}", currentMipLevel);
return nullptr; return std::nullopt;
} }
} }
return texture; CommonIwiMetaData meta{
.m_no_picmip = (header.flags & iwi13::IwiFlags::IMG_FLAG_NOPICMIP) != 0,
.m_streaming = (header.flags & iwi13::IwiFlags::IMG_FLAG_STREAMING) != 0,
.m_clamp_u = (header.flags & iwi13::IwiFlags::IMG_FLAG_CLAMP_U) != 0,
.m_clamp_v = (header.flags & iwi13::IwiFlags::IMG_FLAG_CLAMP_V) != 0,
.m_dynamic = (header.flags & iwi13::IwiFlags::IMG_FLAG_DYNAMIC) != 0,
.m_gamma = header.gamma,
};
return IwiLoaderResult{
.m_version = IwiVersion::IWI_13,
.m_meta = meta,
.m_texture = std::move(texture),
};
} }
const ImageFormat* GetFormat27(int8_t format) const ImageFormat* GetFormat27(int8_t format)
@@ -353,17 +401,20 @@ namespace iwi
return nullptr; return nullptr;
} }
std::unique_ptr<Texture> LoadIwi27(std::istream& stream) std::optional<IwiLoaderResult> LoadIwi27(std::istream& stream)
{ {
iwi27::IwiHeader header{}; iwi27::IwiHeader header{};
stream.read(reinterpret_cast<char*>(&header), sizeof(header)); stream.read(reinterpret_cast<char*>(&header), sizeof(header));
if (stream.gcount() != sizeof(header)) if (stream.gcount() != sizeof(header))
return nullptr; {
con::error("IWI header corrupted");
return std::nullopt;
}
const auto* format = GetFormat27(header.format); const auto* format = GetFormat27(header.format);
if (format == nullptr) if (format == nullptr)
return nullptr; return std::nullopt;
auto width = header.dimensions[0]; auto width = header.dimensions[0];
auto height = header.dimensions[1]; auto height = header.dimensions[1];
@@ -380,7 +431,7 @@ namespace iwi
texture->Allocate(); texture->Allocate();
auto currentFileSize = sizeof(iwi27::IwiHeader) + sizeof(IwiVersion); auto currentFileSize = sizeof(iwi27::IwiHeader) + sizeof(IwiVersionHeader);
const auto mipMapCount = hasMipMaps ? texture->GetMipMapCount() : 1; const auto mipMapCount = hasMipMaps ? texture->GetMipMapCount() : 1;
for (auto currentMipLevel = mipMapCount - 1; currentMipLevel >= 0; currentMipLevel--) for (auto currentMipLevel = mipMapCount - 1; currentMipLevel >= 0; currentMipLevel--)
@@ -392,35 +443,54 @@ namespace iwi
&& currentFileSize != header.fileSizeForPicmip[currentMipLevel]) && currentFileSize != header.fileSizeForPicmip[currentMipLevel])
{ {
con::error("Iwi has invalid file size for picmip {}", currentMipLevel); con::error("Iwi has invalid file size for picmip {}", currentMipLevel);
return nullptr; return std::nullopt;
} }
stream.read(reinterpret_cast<char*>(texture->GetBufferForMipLevel(currentMipLevel)), sizeOfMipLevel); stream.read(reinterpret_cast<char*>(texture->GetBufferForMipLevel(currentMipLevel)), sizeOfMipLevel);
if (stream.gcount() != sizeOfMipLevel) if (stream.gcount() != sizeOfMipLevel)
{ {
con::error("Unexpected eof of iwi in mip level {}", currentMipLevel); con::error("Unexpected eof of iwi in mip level {}", currentMipLevel);
return nullptr; return std::nullopt;
} }
} }
return texture; CommonIwiMetaData meta{
} .m_no_picmip = (header.flags & iwi27::IwiFlags::IMG_FLAG_NOPICMIP) != 0,
.m_streaming = (header.flags & iwi27::IwiFlags::IMG_FLAG_STREAMING) != 0,
.m_clamp_u = (header.flags & iwi27::IwiFlags::IMG_FLAG_CLAMP_U) != 0,
.m_clamp_v = (header.flags & iwi27::IwiFlags::IMG_FLAG_CLAMP_V) != 0,
.m_dynamic = (header.flags & iwi27::IwiFlags::IMG_FLAG_DYNAMIC) != 0,
.m_gamma = header.gamma,
};
std::unique_ptr<Texture> LoadIwi(std::istream& stream) return IwiLoaderResult{
.m_version = IwiVersion::IWI_27,
.m_meta = meta,
.m_texture = std::move(texture),
};
}
} // namespace
namespace image
{ {
IwiVersion iwiVersion{}; std::optional<IwiLoaderResult> LoadIwi(std::istream& stream)
{
IwiVersionHeader iwiVersionHeader{};
stream.read(reinterpret_cast<char*>(&iwiVersion), sizeof(iwiVersion)); stream.read(reinterpret_cast<char*>(&iwiVersionHeader), sizeof(iwiVersionHeader));
if (stream.gcount() != sizeof(iwiVersion)) if (stream.gcount() != sizeof(iwiVersionHeader))
return nullptr; {
con::error("IWI version header corrupted");
return std::nullopt;
}
if (iwiVersion.tag[0] != 'I' || iwiVersion.tag[1] != 'W' || iwiVersion.tag[2] != 'i') if (iwiVersionHeader.tag[0] != 'I' || iwiVersionHeader.tag[1] != 'W' || iwiVersionHeader.tag[2] != 'i')
{ {
con::error("Invalid IWI magic"); con::error("Invalid IWI magic");
return nullptr; return std::nullopt;
} }
switch (iwiVersion.version) switch (iwiVersionHeader.version)
{ {
case 6: case 6:
return LoadIwi6(stream); return LoadIwi6(stream);
@@ -438,7 +508,7 @@ namespace iwi
break; break;
} }
con::error("Unknown IWI version {}", iwiVersion.version); con::error("Unknown IWI version {}", iwiVersionHeader.version);
return nullptr; return std::nullopt;
} }
} // namespace iwi } // namespace image

View File

@@ -1,11 +1,20 @@
#pragma once #pragma once
#include "Image/IwiTypes.h"
#include "Image/Texture.h" #include "Image/Texture.h"
#include <istream> #include <istream>
#include <memory> #include <memory>
#include <optional>
namespace iwi namespace image
{ {
std::unique_ptr<Texture> LoadIwi(std::istream& stream); struct IwiLoaderResult
}; // namespace iwi {
IwiVersion m_version;
CommonIwiMetaData m_meta;
std::unique_ptr<Texture> m_texture;
};
std::optional<IwiLoaderResult> LoadIwi(std::istream& stream);
}; // namespace image

View File

@@ -2,13 +2,38 @@
#include <cstdint> #include <cstdint>
struct IwiVersion namespace image
{
enum class IwiVersion : std::uint8_t
{
// IW3
IWI_6 = 6,
// IW4, IW5
IWI_8 = 8,
// T5
IWI_13 = 13,
// T6
IWI_27 = 27
};
struct CommonIwiMetaData
{
// Always high resolution
bool m_no_picmip;
bool m_streaming;
bool m_clamp_u;
bool m_clamp_v;
bool m_dynamic;
float m_gamma;
};
struct IwiVersionHeader
{ {
char tag[3]; char tag[3];
char version; char version;
}; };
// IW3
namespace iwi6 namespace iwi6
{ {
struct IwiHeader struct IwiHeader
@@ -56,7 +81,6 @@ namespace iwi6
}; };
} // namespace iwi6 } // namespace iwi6
// IW4
namespace iwi8 namespace iwi8
{ {
struct IwiHeader struct IwiHeader
@@ -124,7 +148,6 @@ namespace iwi8
}; };
} // namespace iwi8 } // namespace iwi8
// T5
namespace iwi13 namespace iwi13
{ {
struct IwiHeader struct IwiHeader
@@ -179,7 +202,6 @@ namespace iwi13
} // namespace iwi13 } // namespace iwi13
// T6
namespace iwi27 namespace iwi27
{ {
struct IwiHeader struct IwiHeader
@@ -233,3 +255,4 @@ namespace iwi27
}; };
} // namespace iwi27 } // namespace iwi27
} // namespace image

View File

@@ -3,7 +3,7 @@
#include <cassert> #include <cassert>
#include <ostream> #include <ostream>
using namespace iwi13; using namespace image::iwi13;
IwiWriter::IwiWriter() = default; IwiWriter::IwiWriter() = default;
@@ -54,13 +54,13 @@ std::string IwiWriter::GetFileExtension()
void IwiWriter::WriteVersion(std::ostream& stream) void IwiWriter::WriteVersion(std::ostream& stream)
{ {
IwiVersion version{}; IwiVersionHeader version{};
version.tag[0] = 'I'; version.tag[0] = 'I';
version.tag[1] = 'W'; version.tag[1] = 'W';
version.tag[2] = 'i'; version.tag[2] = 'i';
version.version = 13; version.version = 13;
stream.write(reinterpret_cast<char*>(&version), sizeof(IwiVersion)); stream.write(reinterpret_cast<char*>(&version), sizeof(IwiVersionHeader));
} }
void IwiWriter::FillHeader2D(IwiHeader& header, const Texture2D& texture) void IwiWriter::FillHeader2D(IwiHeader& header, const Texture2D& texture)
@@ -101,7 +101,7 @@ void IwiWriter::DumpImage(std::ostream& stream, const Texture* texture)
if (!texture->HasMipMaps()) if (!texture->HasMipMaps())
header.flags |= IMG_FLAG_NOMIPMAPS; header.flags |= IMG_FLAG_NOMIPMAPS;
auto currentFileSize = sizeof(IwiVersion) + sizeof(IwiHeader); auto currentFileSize = sizeof(IwiVersionHeader) + sizeof(IwiHeader);
const auto textureMipCount = texture->HasMipMaps() ? texture->GetMipMapCount() : 1; const auto textureMipCount = texture->HasMipMaps() ? texture->GetMipMapCount() : 1;
for (auto currentMipLevel = textureMipCount - 1; currentMipLevel >= 0; currentMipLevel--) for (auto currentMipLevel = textureMipCount - 1; currentMipLevel >= 0; currentMipLevel--)

View File

@@ -1,11 +1,11 @@
#pragma once #pragma once
#include "IImageWriter.h"
#include "Image/IwiTypes.h" #include "Image/IwiTypes.h"
#include "ImageWriter.h"
namespace iwi13 namespace image::iwi13
{ {
class IwiWriter final : public IImageWriter class IwiWriter final : public ImageWriter
{ {
static IwiFormat GetIwiFormatForImageFormat(const ImageFormat* imageFormat); static IwiFormat GetIwiFormatForImageFormat(const ImageFormat* imageFormat);
@@ -27,4 +27,4 @@ namespace iwi13
std::string GetFileExtension() override; std::string GetFileExtension() override;
void DumpImage(std::ostream& stream, const Texture* texture) override; void DumpImage(std::ostream& stream, const Texture* texture) override;
}; };
} // namespace iwi13 } // namespace image::iwi13

View File

@@ -3,7 +3,7 @@
#include <cassert> #include <cassert>
#include <ostream> #include <ostream>
using namespace iwi27; using namespace image::iwi27;
IwiWriter::IwiWriter() = default; IwiWriter::IwiWriter() = default;
@@ -54,13 +54,13 @@ std::string IwiWriter::GetFileExtension()
void IwiWriter::WriteVersion(std::ostream& stream) void IwiWriter::WriteVersion(std::ostream& stream)
{ {
IwiVersion version{}; IwiVersionHeader version{};
version.tag[0] = 'I'; version.tag[0] = 'I';
version.tag[1] = 'W'; version.tag[1] = 'W';
version.tag[2] = 'i'; version.tag[2] = 'i';
version.version = 27; version.version = 27;
stream.write(reinterpret_cast<char*>(&version), sizeof(IwiVersion)); stream.write(reinterpret_cast<char*>(&version), sizeof(IwiVersionHeader));
} }
void IwiWriter::FillHeader2D(IwiHeader& header, const Texture2D& texture) void IwiWriter::FillHeader2D(IwiHeader& header, const Texture2D& texture)
@@ -104,7 +104,7 @@ void IwiWriter::DumpImage(std::ostream& stream, const Texture* texture)
for (auto& i : header.maxGlossForMip) for (auto& i : header.maxGlossForMip)
i = 0; i = 0;
auto currentFileSize = sizeof(IwiVersion) + sizeof(IwiHeader); auto currentFileSize = sizeof(IwiVersionHeader) + sizeof(IwiHeader);
const auto textureMipCount = texture->HasMipMaps() ? texture->GetMipMapCount() : 1; const auto textureMipCount = texture->HasMipMaps() ? texture->GetMipMapCount() : 1;
for (auto currentMipLevel = textureMipCount - 1; currentMipLevel >= 0; currentMipLevel--) for (auto currentMipLevel = textureMipCount - 1; currentMipLevel >= 0; currentMipLevel--)

View File

@@ -1,11 +1,11 @@
#pragma once #pragma once
#include "IImageWriter.h"
#include "Image/IwiTypes.h" #include "Image/IwiTypes.h"
#include "ImageWriter.h"
namespace iwi27 namespace image::iwi27
{ {
class IwiWriter final : public IImageWriter class IwiWriter final : public ImageWriter
{ {
static IwiFormat GetIwiFormatForImageFormat(const ImageFormat* imageFormat); static IwiFormat GetIwiFormatForImageFormat(const ImageFormat* imageFormat);
@@ -27,4 +27,4 @@ namespace iwi27
std::string GetFileExtension() override; std::string GetFileExtension() override;
void DumpImage(std::ostream& stream, const Texture* texture) override; void DumpImage(std::ostream& stream, const Texture* texture) override;
}; };
} // namespace iwi27 } // namespace image::iwi27

View File

@@ -2,7 +2,7 @@
#include <cassert> #include <cassert>
using namespace iwi6; using namespace image::iwi6;
IwiWriter::IwiWriter() = default; IwiWriter::IwiWriter() = default;
@@ -40,13 +40,13 @@ IwiFormat IwiWriter::GetIwiFormatForImageFormat(const ImageFormat* imageFormat)
void IwiWriter::WriteVersion(std::ostream& stream) void IwiWriter::WriteVersion(std::ostream& stream)
{ {
IwiVersion version{}; IwiVersionHeader version{};
version.tag[0] = 'I'; version.tag[0] = 'I';
version.tag[1] = 'W'; version.tag[1] = 'W';
version.tag[2] = 'i'; version.tag[2] = 'i';
version.version = 6; version.version = 6;
stream.write(reinterpret_cast<char*>(&version), sizeof(IwiVersion)); stream.write(reinterpret_cast<char*>(&version), sizeof(IwiVersionHeader));
} }
void IwiWriter::FillHeader2D(IwiHeader& header, const Texture2D& texture) void IwiWriter::FillHeader2D(IwiHeader& header, const Texture2D& texture)
@@ -96,7 +96,7 @@ void IwiWriter::DumpImage(std::ostream& stream, const Texture* texture)
if (!texture->HasMipMaps()) if (!texture->HasMipMaps())
header.flags |= IMG_FLAG_NOMIPMAPS; header.flags |= IMG_FLAG_NOMIPMAPS;
auto currentFileSize = sizeof(IwiVersion) + sizeof(IwiHeader); auto currentFileSize = sizeof(IwiVersionHeader) + sizeof(IwiHeader);
const auto textureMipCount = texture->HasMipMaps() ? texture->GetMipMapCount() : 1; const auto textureMipCount = texture->HasMipMaps() ? texture->GetMipMapCount() : 1;
for (auto currentMipLevel = textureMipCount - 1; currentMipLevel >= 0; currentMipLevel--) for (auto currentMipLevel = textureMipCount - 1; currentMipLevel >= 0; currentMipLevel--)

View File

@@ -1,11 +1,11 @@
#pragma once #pragma once
#include "IImageWriter.h"
#include "Image/IwiTypes.h" #include "Image/IwiTypes.h"
#include "ImageWriter.h"
namespace iwi6 namespace image::iwi6
{ {
class IwiWriter final : public IImageWriter class IwiWriter final : public ImageWriter
{ {
static IwiFormat GetIwiFormatForImageFormat(const ImageFormat* imageFormat); static IwiFormat GetIwiFormatForImageFormat(const ImageFormat* imageFormat);
@@ -27,4 +27,4 @@ namespace iwi6
std::string GetFileExtension() override; std::string GetFileExtension() override;
void DumpImage(std::ostream& stream, const Texture* texture) override; void DumpImage(std::ostream& stream, const Texture* texture) override;
}; };
} // namespace iwi6 } // namespace image::iwi6

View File

@@ -2,7 +2,7 @@
#include <cassert> #include <cassert>
using namespace iwi8; using namespace image::iwi8;
IwiWriter::IwiWriter() = default; IwiWriter::IwiWriter() = default;
@@ -40,13 +40,13 @@ IwiFormat IwiWriter::GetIwiFormatForImageFormat(const ImageFormat* imageFormat)
void IwiWriter::WriteVersion(std::ostream& stream) void IwiWriter::WriteVersion(std::ostream& stream)
{ {
IwiVersion version{}; IwiVersionHeader version{};
version.tag[0] = 'I'; version.tag[0] = 'I';
version.tag[1] = 'W'; version.tag[1] = 'W';
version.tag[2] = 'i'; version.tag[2] = 'i';
version.version = 8; version.version = 8;
stream.write(reinterpret_cast<char*>(&version), sizeof(IwiVersion)); stream.write(reinterpret_cast<char*>(&version), sizeof(IwiVersionHeader));
} }
void IwiWriter::FillHeader2D(IwiHeader& header, const Texture2D& texture) void IwiWriter::FillHeader2D(IwiHeader& header, const Texture2D& texture)
@@ -96,7 +96,7 @@ void IwiWriter::DumpImage(std::ostream& stream, const Texture* texture)
if (!texture->HasMipMaps()) if (!texture->HasMipMaps())
header.flags |= IMG_FLAG_NOMIPMAPS; header.flags |= IMG_FLAG_NOMIPMAPS;
auto currentFileSize = sizeof(IwiVersion) + sizeof(IwiHeader); auto currentFileSize = sizeof(IwiVersionHeader) + sizeof(IwiHeader);
const auto textureMipCount = texture->HasMipMaps() ? texture->GetMipMapCount() : 1; const auto textureMipCount = texture->HasMipMaps() ? texture->GetMipMapCount() : 1;
for (auto currentMipLevel = textureMipCount - 1; currentMipLevel >= 0; currentMipLevel--) for (auto currentMipLevel = textureMipCount - 1; currentMipLevel >= 0; currentMipLevel--)

View File

@@ -1,11 +1,11 @@
#pragma once #pragma once
#include "IImageWriter.h"
#include "Image/IwiTypes.h" #include "Image/IwiTypes.h"
#include "ImageWriter.h"
namespace iwi8 namespace image::iwi8
{ {
class IwiWriter final : public IImageWriter class IwiWriter final : public ImageWriter
{ {
static IwiFormat GetIwiFormatForImageFormat(const ImageFormat* imageFormat); static IwiFormat GetIwiFormatForImageFormat(const ImageFormat* imageFormat);
@@ -27,4 +27,4 @@ namespace iwi8
std::string GetFileExtension() override; std::string GetFileExtension() override;
void DumpImage(std::ostream& stream, const Texture* texture) override; void DumpImage(std::ostream& stream, const Texture* texture) override;
}; };
} // namespace iwi8 } // namespace image::iwi8

View File

@@ -4,6 +4,8 @@
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
namespace image
{
// ============================================== // ==============================================
// ================= Texture ==================== // ================= Texture ====================
// ============================================== // ==============================================
@@ -441,3 +443,4 @@ const uint8_t* Texture3D::GetBufferForMipLevel(const int mipLevel, const int fac
return &m_data[bufferOffset]; return &m_data[bufferOffset];
} }
} // namespace image

View File

@@ -3,6 +3,8 @@
#include <cstdint> #include <cstdint>
namespace image
{
enum class TextureType : std::uint8_t enum class TextureType : std::uint8_t
{ {
T_2D, T_2D,
@@ -130,3 +132,4 @@ public:
[[nodiscard]] int GetMipMapCount() const override; [[nodiscard]] int GetMipMapCount() const override;
}; };
} // namespace image

View File

@@ -2,6 +2,8 @@
#include <cassert> #include <cassert>
namespace image
{
constexpr uint64_t TextureConverter::Mask1(const unsigned length) constexpr uint64_t TextureConverter::Mask1(const unsigned length)
{ {
if (length >= sizeof(uint64_t) * 8) if (length >= sizeof(uint64_t) * 8)
@@ -192,8 +194,8 @@ void TextureConverter::ConvertUnsignedToUnsigned()
SetPixelFunctions(inputFormat->m_bits_per_pixel, outputFormat->m_bits_per_pixel); SetPixelFunctions(inputFormat->m_bits_per_pixel, outputFormat->m_bits_per_pixel);
if (inputFormat->m_r_size == outputFormat->m_r_size && inputFormat->m_g_size == outputFormat->m_g_size && inputFormat->m_b_size == outputFormat->m_b_size if (inputFormat->m_r_size == outputFormat->m_r_size && inputFormat->m_g_size == outputFormat->m_g_size
&& inputFormat->m_a_size == outputFormat->m_a_size) && inputFormat->m_b_size == outputFormat->m_b_size && inputFormat->m_a_size == outputFormat->m_a_size)
{ {
ReorderUnsignedToUnsigned(); ReorderUnsignedToUnsigned();
} }
@@ -220,3 +222,4 @@ std::unique_ptr<Texture> TextureConverter::Convert()
return std::move(m_output_texture); return std::move(m_output_texture);
} }
} // namespace image

View File

@@ -5,6 +5,8 @@
#include <functional> #include <functional>
#include <memory> #include <memory>
namespace image
{
class TextureConverter class TextureConverter
{ {
public: public:
@@ -29,3 +31,4 @@ private:
const ImageFormat* m_input_format; const ImageFormat* m_input_format;
const ImageFormat* m_output_format; const ImageFormat* m_output_format;
}; };
} // namespace image

View File

@@ -1,13 +0,0 @@
#pragma once
#include "Asset/IAssetCreator.h"
#include "Game/IW3/IW3.h"
#include "SearchPath/ISearchPath.h"
#include "Utils/MemoryManager.h"
#include <memory>
namespace image
{
std::unique_ptr<AssetCreator<IW3::AssetImage>> CreateLoaderIW3(MemoryManager& memory, ISearchPath& searchPath);
} // namespace image

View File

@@ -4,8 +4,9 @@
#include "Game/IW3/AssetMarkerIW3.h" #include "Game/IW3/AssetMarkerIW3.h"
#include "Game/IW3/GameIW3.h" #include "Game/IW3/GameIW3.h"
#include "Game/IW3/IW3.h" #include "Game/IW3/IW3.h"
#include "Game/IW3/Image/ImageLoaderEmbeddedIW3.h"
#include "Game/IW3/Image/ImageLoaderExternalIW3.h"
#include "Game/IW3/XModel/LoaderXModelIW3.h" #include "Game/IW3/XModel/LoaderXModelIW3.h"
#include "Image/AssetLoaderImageIW3.h"
#include "Localize/AssetLoaderLocalizeIW3.h" #include "Localize/AssetLoaderLocalizeIW3.h"
#include "Material/LoaderMaterialIW3.h" #include "Material/LoaderMaterialIW3.h"
#include "ObjLoading.h" #include "ObjLoading.h"
@@ -95,7 +96,8 @@ namespace
collection.AddAssetCreator(xmodel::CreateLoaderIW3(memory, searchPath, zone)); collection.AddAssetCreator(xmodel::CreateLoaderIW3(memory, searchPath, zone));
collection.AddAssetCreator(material::CreateLoaderIW3(memory, searchPath)); collection.AddAssetCreator(material::CreateLoaderIW3(memory, searchPath));
// collection.AddAssetCreator(std::make_unique<AssetLoaderTechniqueSet>(memory)); // collection.AddAssetCreator(std::make_unique<AssetLoaderTechniqueSet>(memory));
collection.AddAssetCreator(image::CreateLoaderIW3(memory, searchPath)); collection.AddAssetCreator(image::CreateLoaderEmbeddedIW3(memory, searchPath));
collection.AddAssetCreator(image::CreateLoaderExternalIW3(memory, searchPath));
// collection.AddAssetCreator(std::make_unique<AssetLoaderSound>(memory)); // collection.AddAssetCreator(std::make_unique<AssetLoaderSound>(memory));
// collection.AddAssetCreator(std::make_unique<AssetLoaderSoundCurve>(memory)); // collection.AddAssetCreator(std::make_unique<AssetLoaderSoundCurve>(memory));
// collection.AddAssetCreator(std::make_unique<AssetLoaderLoadedSound>(memory)); // collection.AddAssetCreator(std::make_unique<AssetLoaderLoadedSound>(memory));

View File

@@ -4,6 +4,8 @@
#include "Game/IW4/AssetMarkerIW4.h" #include "Game/IW4/AssetMarkerIW4.h"
#include "Game/IW4/GameIW4.h" #include "Game/IW4/GameIW4.h"
#include "Game/IW4/IW4.h" #include "Game/IW4/IW4.h"
#include "Game/IW4/Image/ImageLoaderEmbeddedIW4.h"
#include "Game/IW4/Image/ImageLoaderExternalIW4.h"
#include "Game/IW4/XModel/LoaderXModelIW4.h" #include "Game/IW4/XModel/LoaderXModelIW4.h"
#include "Leaderboard/LoaderLeaderboardIW4.h" #include "Leaderboard/LoaderLeaderboardIW4.h"
#include "LightDef/LightDefLoaderIW4.h" #include "LightDef/LightDefLoaderIW4.h"
@@ -130,7 +132,8 @@ namespace
collection.AddAssetCreator(shader::CreatePixelShaderLoaderIW4(memory, searchPath)); collection.AddAssetCreator(shader::CreatePixelShaderLoaderIW4(memory, searchPath));
collection.AddAssetCreator(shader::CreateVertexShaderLoaderIW4(memory, searchPath)); collection.AddAssetCreator(shader::CreateVertexShaderLoaderIW4(memory, searchPath));
// collection.AddAssetCreator(std::make_unique<AssetLoaderTechset>(memory)); // collection.AddAssetCreator(std::make_unique<AssetLoaderTechset>(memory));
// collection.AddAssetCreator(std::make_unique<AssetLoaderImage>(memory)); collection.AddAssetCreator(image::CreateLoaderEmbeddedIW4(memory, searchPath));
collection.AddAssetCreator(image::CreateLoaderExternalIW4(memory, searchPath));
// collection.AddAssetCreator(std::make_unique<AssetLoaderSound>(memory)); // collection.AddAssetCreator(std::make_unique<AssetLoaderSound>(memory));
collection.AddAssetCreator(sound_curve::CreateLoaderIW4(memory, searchPath)); collection.AddAssetCreator(sound_curve::CreateLoaderIW4(memory, searchPath));
// collection.AddAssetCreator(std::make_unique<AssetLoaderLoadedSound>(memory)); // collection.AddAssetCreator(std::make_unique<AssetLoaderLoadedSound>(memory));

View File

@@ -1,71 +0,0 @@
#include "LoaderImageIW5.h"
#include "Game/IW5/IW5.h"
#include "Image/ImageCommon.h"
#include "Image/IwiLoader.h"
#include "Utils/Logging/Log.h"
#include <cstring>
#include <format>
#include <iostream>
#include <sstream>
using namespace IW5;
namespace
{
constexpr auto MAX_IMAGE_NAME_SIZE = 0x800;
class ImageLoader final : public AssetCreator<AssetImage>
{
public:
ImageLoader(MemoryManager& memory, ISearchPath& searchPath)
: m_memory(memory),
m_search_path(searchPath)
{
}
AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override
{
const auto fileName = image::GetFileNameForAsset(assetName, ".iwi");
const auto file = m_search_path.Open(fileName);
if (!file.IsOpen())
return AssetCreationResult::NoAction();
const auto fileSize = static_cast<size_t>(file.m_length);
const auto fileData = std::make_unique<char[]>(fileSize);
file.m_stream->read(fileData.get(), fileSize);
std::istringstream ss(std::string(fileData.get(), fileSize));
const auto texture = iwi::LoadIwi(ss);
if (!texture)
{
con::error("Failed to load texture from: {}", fileName);
return AssetCreationResult::Failure();
}
auto* image = m_memory.Alloc<GfxImage>();
image->name = m_memory.Dup(assetName.c_str());
image->noPicmip = !texture->HasMipMaps();
image->width = static_cast<uint16_t>(texture->GetWidth());
image->height = static_cast<uint16_t>(texture->GetHeight());
image->depth = static_cast<uint16_t>(texture->GetDepth());
image->texture.loadDef = m_memory.Alloc<GfxImageLoadDef>();
return AssetCreationResult::Success(context.AddAsset<AssetImage>(assetName, image));
}
private:
MemoryManager& m_memory;
ISearchPath& m_search_path;
};
} // namespace
namespace image
{
std::unique_ptr<AssetCreator<AssetImage>> CreateLoaderIW5(MemoryManager& memory, ISearchPath& searchPath)
{
return std::make_unique<ImageLoader>(memory, searchPath);
}
} // namespace image

View File

@@ -1,13 +0,0 @@
#pragma once
#include "Asset/IAssetCreator.h"
#include "Game/IW5/IW5.h"
#include "SearchPath/ISearchPath.h"
#include "Utils/MemoryManager.h"
#include <memory>
namespace image
{
std::unique_ptr<AssetCreator<IW5::AssetImage>> CreateLoaderIW5(MemoryManager& memory, ISearchPath& searchPath);
} // namespace image

View File

@@ -4,8 +4,9 @@
#include "Game/IW5/AssetMarkerIW5.h" #include "Game/IW5/AssetMarkerIW5.h"
#include "Game/IW5/GameIW5.h" #include "Game/IW5/GameIW5.h"
#include "Game/IW5/IW5.h" #include "Game/IW5/IW5.h"
#include "Game/IW5/Image/ImageLoaderEmbeddedIW5.h"
#include "Game/IW5/Image/ImageLoaderExternalIW5.h"
#include "Game/IW5/XModel/LoaderXModelIW5.h" #include "Game/IW5/XModel/LoaderXModelIW5.h"
#include "Image/LoaderImageIW5.h"
#include "Leaderboard/LoaderLeaderboardIW5.h" #include "Leaderboard/LoaderLeaderboardIW5.h"
#include "Localize/LoaderLocalizeIW5.h" #include "Localize/LoaderLocalizeIW5.h"
#include "Material/LoaderMaterialIW5.h" #include "Material/LoaderMaterialIW5.h"
@@ -132,7 +133,8 @@ namespace
// collection.AddAssetCreator(std::make_unique<AssetLoaderVertexShader>(memory)); // collection.AddAssetCreator(std::make_unique<AssetLoaderVertexShader>(memory));
// collection.AddAssetCreator(std::make_unique<AssetLoaderVertexDecl>(memory)); // collection.AddAssetCreator(std::make_unique<AssetLoaderVertexDecl>(memory));
// collection.AddAssetCreator(std::make_unique<AssetLoaderTechniqueSet>(memory)); // collection.AddAssetCreator(std::make_unique<AssetLoaderTechniqueSet>(memory));
collection.AddAssetCreator(image::CreateLoaderIW5(memory, searchPath)); collection.AddAssetCreator(image::CreateLoaderEmbeddedIW5(memory, searchPath));
collection.AddAssetCreator(image::CreateLoaderExternalIW5(memory, searchPath));
// collection.AddAssetCreator(std::make_unique<AssetLoaderSound>(memory)); // collection.AddAssetCreator(std::make_unique<AssetLoaderSound>(memory));
// collection.AddAssetCreator(std::make_unique<AssetLoaderSoundCurve>(memory)); // collection.AddAssetCreator(std::make_unique<AssetLoaderSoundCurve>(memory));
// collection.AddAssetCreator(std::make_unique<AssetLoaderLoadedSound>(memory)); // collection.AddAssetCreator(std::make_unique<AssetLoaderLoadedSound>(memory));

View File

@@ -3,6 +3,8 @@
#include "Asset/GlobalAssetPoolsLoader.h" #include "Asset/GlobalAssetPoolsLoader.h"
#include "Game/T5/AssetMarkerT5.h" #include "Game/T5/AssetMarkerT5.h"
#include "Game/T5/GameT5.h" #include "Game/T5/GameT5.h"
#include "Game/T5/Image/ImageLoaderEmbeddedT5.h"
#include "Game/T5/Image/ImageLoaderExternalT5.h"
#include "Game/T5/T5.h" #include "Game/T5/T5.h"
#include "Game/T5/XModel/LoaderXModelT5.h" #include "Game/T5/XModel/LoaderXModelT5.h"
#include "Localize/LoaderLocalizeT5.h" #include "Localize/LoaderLocalizeT5.h"
@@ -108,7 +110,8 @@ namespace
collection.AddAssetCreator(xmodel::CreateLoaderT5(memory, searchPath, zone)); collection.AddAssetCreator(xmodel::CreateLoaderT5(memory, searchPath, zone));
collection.AddAssetCreator(material::CreateLoaderT5(memory, searchPath)); collection.AddAssetCreator(material::CreateLoaderT5(memory, searchPath));
// collection.AddAssetCreator(std::make_unique<AssetLoaderTechniqueSet>(memory)); // collection.AddAssetCreator(std::make_unique<AssetLoaderTechniqueSet>(memory));
// collection.AddAssetCreator(std::make_unique<AssetLoaderImage>(memory)); collection.AddAssetCreator(image::CreateLoaderEmbeddedT5(memory, searchPath));
collection.AddAssetCreator(image::CreateLoaderExternalT5(memory, searchPath));
// collection.AddAssetCreator(std::make_unique<AssetLoaderSoundBank>(memory)); // collection.AddAssetCreator(std::make_unique<AssetLoaderSoundBank>(memory));
// collection.AddAssetCreator(std::make_unique<AssetLoaderSoundPatch>(memory)); // collection.AddAssetCreator(std::make_unique<AssetLoaderSoundPatch>(memory));
// collection.AddAssetCreator(std::make_unique<AssetLoaderClipMapPvs>(memory)); // collection.AddAssetCreator(std::make_unique<AssetLoaderClipMapPvs>(memory));

View File

@@ -1,79 +0,0 @@
#include "LoaderImageT6.h"
#include "Game/T6/CommonT6.h"
#include "Game/T6/T6.h"
#include "Image/ImageCommon.h"
#include "Image/IwiLoader.h"
#include "Utils/Logging/Log.h"
#include <cstring>
#include <format>
#include <iostream>
#include <sstream>
#include <zlib.h>
using namespace T6;
namespace
{
class ImageLoader final : public AssetCreator<AssetImage>
{
public:
ImageLoader(MemoryManager& memory, ISearchPath& searchPath)
: m_memory(memory),
m_search_path(searchPath)
{
}
AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override
{
const auto fileName = image::GetFileNameForAsset(assetName, ".iwi");
const auto file = m_search_path.Open(fileName);
if (!file.IsOpen())
return AssetCreationResult::NoAction();
const auto fileSize = static_cast<size_t>(file.m_length);
const auto fileData = std::make_unique<char[]>(fileSize);
file.m_stream->read(fileData.get(), static_cast<std::streamsize>(fileSize));
const auto dataHash = static_cast<unsigned>(crc32(0u, reinterpret_cast<const Bytef*>(fileData.get()), static_cast<unsigned>(fileSize)));
std::istringstream ss(std::string(fileData.get(), fileSize));
const auto texture = iwi::LoadIwi(ss);
if (!texture)
{
con::error("Failed to load texture from: {}", fileName);
return AssetCreationResult::Failure();
}
auto* image = m_memory.Alloc<GfxImage>();
image->name = m_memory.Dup(assetName.c_str());
image->hash = Common::R_HashString(image->name, 0);
image->delayLoadPixels = true;
image->noPicmip = !texture->HasMipMaps();
image->width = static_cast<uint16_t>(texture->GetWidth());
image->height = static_cast<uint16_t>(texture->GetHeight());
image->depth = static_cast<uint16_t>(texture->GetDepth());
image->streaming = 1;
image->streamedParts[0].levelCount = 1;
image->streamedParts[0].levelSize = static_cast<uint32_t>(fileSize);
image->streamedParts[0].hash = dataHash & 0x1FFFFFFF;
image->streamedPartCount = 1;
return AssetCreationResult::Success(context.AddAsset<AssetImage>(assetName, image));
}
private:
MemoryManager& m_memory;
ISearchPath& m_search_path;
};
} // namespace
namespace image
{
std::unique_ptr<AssetCreator<AssetImage>> CreateLoaderT6(MemoryManager& memory, ISearchPath& searchPath)
{
return std::make_unique<ImageLoader>(memory, searchPath);
}
} // namespace image

View File

@@ -1,13 +0,0 @@
#pragma once
#include "Asset/IAssetCreator.h"
#include "Game/T6/T6.h"
#include "SearchPath/ISearchPath.h"
#include "Utils/MemoryManager.h"
#include <memory>
namespace image
{
std::unique_ptr<AssetCreator<T6::AssetImage>> CreateLoaderT6(MemoryManager& memory, ISearchPath& searchPath);
} // namespace image

View File

@@ -7,12 +7,13 @@
#include "Game/T6/CommonT6.h" #include "Game/T6/CommonT6.h"
#include "Game/T6/GameAssetPoolT6.h" #include "Game/T6/GameAssetPoolT6.h"
#include "Game/T6/GameT6.h" #include "Game/T6/GameT6.h"
#include "Game/T6/Image/ImageLoaderEmbeddedT6.h"
#include "Game/T6/Image/ImageLoaderExternalT6.h"
#include "Game/T6/T6.h" #include "Game/T6/T6.h"
#include "Game/T6/XModel/LoaderXModelT6.h" #include "Game/T6/XModel/LoaderXModelT6.h"
#include "Image/Dx12TextureLoader.h" #include "Image/Dx12TextureLoader.h"
#include "Image/IwiLoader.h" #include "Image/IwiLoader.h"
#include "Image/IwiTypes.h" #include "Image/IwiTypes.h"
#include "Image/LoaderImageT6.h"
#include "Image/Texture.h" #include "Image/Texture.h"
#include "Leaderboard/JsonLoaderLeaderboardT6.h" #include "Leaderboard/JsonLoaderLeaderboardT6.h"
#include "Localize/LocalizeLoaderT6.h" #include "Localize/LocalizeLoaderT6.h"
@@ -394,7 +395,8 @@ namespace T6
collection.AddAssetCreator(xmodel::CreateLoaderT6(memory, searchPath, zone)); collection.AddAssetCreator(xmodel::CreateLoaderT6(memory, searchPath, zone));
collection.AddAssetCreator(material::CreateLoaderT6(memory, searchPath)); collection.AddAssetCreator(material::CreateLoaderT6(memory, searchPath));
// collection.AddAssetCreator(std::make_unique<AssetLoaderTechniqueSet>(memory)); // collection.AddAssetCreator(std::make_unique<AssetLoaderTechniqueSet>(memory));
collection.AddAssetCreator(image::CreateLoaderT6(memory, searchPath)); collection.AddAssetCreator(image::CreateLoaderEmbeddedT6(memory, searchPath));
collection.AddAssetCreator(image::CreateLoaderExternalT6(memory, searchPath));
collection.AddAssetCreator(sound::CreateSoundBankLoaderT6(memory, searchPath)); collection.AddAssetCreator(sound::CreateSoundBankLoaderT6(memory, searchPath));
// collection.AddAssetCreator(std::make_unique<AssetLoaderSoundPatch>(memory)); // collection.AddAssetCreator(std::make_unique<AssetLoaderSoundPatch>(memory));
// collection.AddAssetCreator(std::make_unique<AssetLoaderClipMapPvs>(memory)); // collection.AddAssetCreator(std::make_unique<AssetLoaderClipMapPvs>(memory));

View File

@@ -0,0 +1,89 @@
#include "ImageLoaderCommon.h"
#include "Image/ImageCommon.h"
#include "Image/IwiLoader.h"
#include "Image/Texture.h"
#include "Utils/Logging/Log.h"
#include <sstream>
#include <zlib.h>
using namespace image;
namespace
{
CommonImageLoaderResult Success(size_t iwiSize, CommonIwiMetaData meta, std::unique_ptr<Texture> texture, CommonImageLoaderHash hash)
{
return CommonImageLoaderResult{
.m_failure = false,
.m_iwi_size = iwiSize,
.m_meta = meta,
.m_texture = std::move(texture),
.m_hash = hash,
};
}
CommonImageLoaderResult NoAction()
{
return CommonImageLoaderResult{
.m_failure = false,
};
}
CommonImageLoaderResult Failure()
{
return CommonImageLoaderResult{
.m_failure = true,
};
}
} // namespace
namespace image
{
std::optional<AssetCreationResult> CommonImageLoaderResult::GetResultIfCancelled() const
{
if (m_failure)
return AssetCreationResult::Failure();
if (!m_texture)
return AssetCreationResult::NoAction();
return std::nullopt;
}
CommonImageLoaderResult
LoadImageCommon(const std::string& imageName, ISearchPath& searchPath, IwiVersion expectedIwiVersion, CommonImageLoaderHashType hashType)
{
const auto fileName = image::GetFileNameForAsset(imageName, ".iwi");
const auto file = searchPath.Open(fileName);
if (!file.IsOpen())
return NoAction();
const auto fileSize = static_cast<size_t>(file.m_length);
std::optional<IwiLoaderResult> loaderResult;
CommonImageLoaderHash hash{};
if (hashType == CommonImageLoaderHashType::NONE)
{
loaderResult = image::LoadIwi(*file.m_stream);
}
else
{
const auto fileData = std::make_unique<char[]>(fileSize);
file.m_stream->read(fileData.get(), static_cast<std::streamsize>(fileSize));
hash.crc32 = static_cast<unsigned>(crc32(0u, reinterpret_cast<const Bytef*>(fileData.get()), static_cast<unsigned>(fileSize)));
std::istringstream inMemory(std::string(fileData.get(), fileSize));
loaderResult = image::LoadIwi(inMemory);
}
if (!loaderResult)
{
con::error("Failed to load texture from: {}", fileName);
return Failure();
}
return Success(fileSize, loaderResult->m_meta, std::move(loaderResult->m_texture), hash);
}
} // namespace image

View File

@@ -0,0 +1,39 @@
#pragma once
#include "Asset/AssetCreationResult.h"
#include "Image/IwiTypes.h"
#include "Image/Texture.h"
#include "SearchPath/ISearchPath.h"
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
namespace image
{
enum class CommonImageLoaderHashType
{
NONE,
CRC32
};
union CommonImageLoaderHash
{
uint32_t crc32;
};
struct CommonImageLoaderResult
{
std::optional<AssetCreationResult> GetResultIfCancelled() const;
bool m_failure;
size_t m_iwi_size;
CommonIwiMetaData m_meta;
std::unique_ptr<Texture> m_texture;
CommonImageLoaderHash m_hash;
};
CommonImageLoaderResult
LoadImageCommon(const std::string& imageName, ISearchPath& searchPath, IwiVersion expectedIwiVersion, CommonImageLoaderHashType hashType);
} // namespace image

View File

@@ -1,24 +1,64 @@
#include "AssetLoaderImageIW3.h" #options GAME (IW3, IW4, IW5, T5, T6)
#include "Game/IW3/IW3.h" #filename "Game/" + GAME + "/Image/ImageLoaderEmbedded" + GAME + ".cpp"
#if GAME == "IW3"
#define FEATURE_IW3
#elif GAME == "IW4"
#define FEATURE_IW4
#elif GAME == "IW5"
#define FEATURE_IW5
#elif GAME == "T5"
#define FEATURE_T5
#elif GAME == "T6"
#define FEATURE_T6
#endif
#if defined(FEATURE_IW3)
#define IWI_NS iwi6
#define FLAG_CUBE IMG_FLAG_CUBEMAP
#define FLAG_3D IMG_FLAG_VOLMAP
#elif defined(FEATURE_IW4) || defined(FEATURE_IW5)
#define IWI_NS iwi8
#define FLAG_CUBE IMG_FLAG_MAPTYPE_CUBE
#define FLAG_3D IMG_FLAG_MAPTYPE_3D
#elif defined(FEATURE_T5)
#define IWI_NS iwi13
#define FLAG_CUBE IMG_FLAG_CUBEMAP
#define FLAG_3D IMG_FLAG_VOLMAP
#elif defined(FEATURE_T6)
#define IWI_NS iwi27
#define FLAG_CUBE IMG_FLAG_CUBEMAP
#define FLAG_3D IMG_FLAG_VOLMAP
#endif
// This file was templated.
// See ImageLoaderEmbedded.cpp.template.
// Do not modify, changes will be lost.
#set LOADER_HEADER "\"ImageLoaderEmbedded" + GAME + ".h\""
#include LOADER_HEADER
#set COMMON_HEADER "\"Game/" + GAME + "/Common" + GAME + ".h\""
#include COMMON_HEADER
#include "Image/DdsLoader.h" #include "Image/DdsLoader.h"
#include "Image/ImageCommon.h"
#include "Image/IwiTypes.h" #include "Image/IwiTypes.h"
#include "Pool/GlobalAssetPool.h"
#include "Utils/Logging/Log.h" #include "Utils/Logging/Log.h"
#include <algorithm>
#include <cstring> #include <cstring>
#include <format>
#include <iostream>
using namespace IW3; using namespace GAME;
using namespace image;
namespace namespace
{ {
class ImageLoader final : public AssetCreator<AssetImage> #set LOADER_CLASS "ImageLoader" + GAME
class LOADER_CLASS final : public AssetCreator<AssetImage>
{ {
public: public:
ImageLoader(MemoryManager& memory, ISearchPath& searchPath) LOADER_CLASS(MemoryManager& memory, ISearchPath& searchPath)
: m_memory(memory), : m_memory(memory),
m_search_path(searchPath) m_search_path(searchPath)
{ {
@@ -31,14 +71,11 @@ namespace
if (assetName.empty() || assetName[0] != '*') if (assetName.empty() || assetName[0] != '*')
return AssetCreationResult::NoAction(); return AssetCreationResult::NoAction();
std::string safeAssetName = assetName; const auto file = m_search_path.Open(image::GetFileNameForAsset(assetName, ".dds"));
std::ranges::replace(safeAssetName, '*', '_');
const auto file = m_search_path.Open(std::format("images/{}.dds", safeAssetName));
if (!file.IsOpen()) if (!file.IsOpen())
return AssetCreationResult::NoAction(); return AssetCreationResult::NoAction();
const auto texture = dds::LoadDds(*file.m_stream); const auto texture = image::LoadDds(*file.m_stream);
if (!texture) if (!texture)
{ {
con::error("Failed to load dds file for image asset \"{}\"", assetName); con::error("Failed to load dds file for image asset \"{}\"", assetName);
@@ -58,7 +95,9 @@ namespace
image->height = static_cast<uint16_t>(texture->GetHeight()); image->height = static_cast<uint16_t>(texture->GetHeight());
image->depth = static_cast<uint16_t>(texture->GetDepth()); image->depth = static_cast<uint16_t>(texture->GetDepth());
image->category = IMG_CATEGORY_AUTO_GENERATED; image->category = IMG_CATEGORY_AUTO_GENERATED;
#ifndef FEATURE_IW5
image->delayLoadPixels = false; image->delayLoadPixels = false;
#endif
switch (texture->GetTextureType()) switch (texture->GetTextureType())
{ {
@@ -91,15 +130,23 @@ namespace
loadDef->levelCount = static_cast<char>(mipCount); loadDef->levelCount = static_cast<char>(mipCount);
loadDef->flags = 0; loadDef->flags = 0;
if (!texture->HasMipMaps()) if (!texture->HasMipMaps())
loadDef->flags |= iwi6::IMG_FLAG_NOMIPMAPS; loadDef->flags |= IWI_NS::IMG_FLAG_NOMIPMAPS;
if (texture->GetTextureType() == TextureType::T_CUBE) if (texture->GetTextureType() == TextureType::T_CUBE)
loadDef->flags |= iwi6::IMG_FLAG_CUBEMAP; loadDef->flags |= IWI_NS::FLAG_CUBE;
if (texture->GetTextureType() == TextureType::T_3D) if (texture->GetTextureType() == TextureType::T_3D)
loadDef->flags |= iwi6::IMG_FLAG_VOLMAP; loadDef->flags |= IWI_NS::FLAG_3D;
#if defined(FEATURE_IW3)
loadDef->dimensions[0] = image->width; loadDef->dimensions[0] = image->width;
loadDef->dimensions[1] = image->height; loadDef->dimensions[1] = image->height;
loadDef->dimensions[2] = image->depth; loadDef->dimensions[2] = image->depth;
#endif
#if defined(FEATURE_T6)
loadDef->format = static_cast<int>(texture->GetFormat()->GetDxgiFormat());
#else
loadDef->format = static_cast<int>(texture->GetFormat()->GetD3DFormat()); loadDef->format = static_cast<int>(texture->GetFormat()->GetD3DFormat());
#endif
loadDef->resourceSize = static_cast<unsigned>(dataSize); loadDef->resourceSize = static_cast<unsigned>(dataSize);
char* currentDataBuffer = loadDef->data; char* currentDataBuffer = loadDef->data;
@@ -117,7 +164,6 @@ namespace
return AssetCreationResult::Success(context.AddAsset<AssetImage>(assetName, image)); return AssetCreationResult::Success(context.AddAsset<AssetImage>(assetName, image));
} }
private:
MemoryManager& m_memory; MemoryManager& m_memory;
ISearchPath& m_search_path; ISearchPath& m_search_path;
}; };
@@ -125,8 +171,9 @@ namespace
namespace image namespace image
{ {
std::unique_ptr<AssetCreator<AssetImage>> CreateLoaderIW3(MemoryManager& memory, ISearchPath& searchPath) #set LOADER_METHOD "CreateLoaderEmbedded" + GAME
std::unique_ptr<AssetCreator<AssetImage>> LOADER_METHOD(MemoryManager& memory, ISearchPath& searchPath)
{ {
return std::make_unique<ImageLoader>(memory, searchPath); return std::make_unique<LOADER_CLASS>(memory, searchPath);
} }
} // namespace image } // namespace image

View File

@@ -0,0 +1,23 @@
#options GAME (IW3, IW4, IW5, T5, T6)
#filename "Game/" + GAME + "/Image/ImageLoaderEmbedded" + GAME + ".h"
// This file was templated.
// See ImageLoaderEmbedded.h.template.
// Do not modify, changes will be lost.
#pragma once
#include "Asset/IAssetCreator.h"
#set GAME_HEADER "\"Game/" + GAME + "/" + GAME + ".h\""
#include GAME_HEADER
#include "SearchPath/ISearchPath.h"
#include "Utils/MemoryManager.h"
#include <memory>
namespace image
{
#set LOADER_METHOD "CreateLoaderEmbedded" + GAME
std::unique_ptr<AssetCreator<GAME::AssetImage>> LOADER_METHOD(MemoryManager& memory, ISearchPath& searchPath);
} // namespace image

View File

@@ -0,0 +1,111 @@
#options GAME (IW3, IW4, IW5, T5, T6)
#filename "Game/" + GAME + "/Image/ImageLoaderExternal" + GAME + ".cpp"
#if GAME == "IW3"
#define FEATURE_IW3
#elif GAME == "IW4"
#define FEATURE_IW4
#elif GAME == "IW5"
#define FEATURE_IW5
#elif GAME == "T5"
#define FEATURE_T5
#elif GAME == "T6"
#define FEATURE_T6
#endif
#if defined(FEATURE_IW3)
#define IWI_VERSION IWI_6
#elif defined(FEATURE_IW4) || defined(FEATURE_IW5)
#define IWI_VERSION IWI_8
#elif defined(FEATURE_T5)
#define IWI_VERSION IWI_13
#elif defined(FEATURE_T6)
#define IWI_VERSION IWI_27
#endif
#if defined(FEATURE_T6)
#define HASH_TYPE CRC32
#else
#define HASH_TYPE NONE
#endif
// This file was templated.
// See ImageLoaderExternal.cpp.template.
// Do not modify, changes will be lost.
#set LOADER_HEADER "\"ImageLoaderExternal" + GAME + ".h\""
#include LOADER_HEADER
#set COMMON_HEADER "\"Game/" + GAME + "/Common" + GAME + ".h\""
#include COMMON_HEADER
#include "Image/ImageLoaderCommon.h"
#include "Utils/Logging/Log.h"
#include <cstring>
using namespace GAME;
using namespace image;
namespace
{
#set LOADER_CLASS "ImageLoader" + GAME
class LOADER_CLASS final : public AssetCreator<AssetImage>
{
public:
LOADER_CLASS(MemoryManager& memory, ISearchPath& searchPath)
: m_memory(memory),
m_search_path(searchPath)
{
}
AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override
{
const auto loadingResult = LoadImageCommon(assetName, m_search_path, IwiVersion::IWI_VERSION, CommonImageLoaderHashType::HASH_TYPE);
const auto earlyReturn = loadingResult.GetResultIfCancelled();
if (earlyReturn)
return *earlyReturn;
const auto* texture = loadingResult.m_texture.get();
auto* image = m_memory.Alloc<GfxImage>();
image->name = m_memory.Dup(assetName.c_str());
#ifdef FEATURE_t6
image->hash = Common::R_HashString(image->name, 0);
#endif
#ifndef FEATURE_IW5
image->delayLoadPixels = true;
#endif
image->noPicmip = loadingResult.m_meta.m_no_picmip;
image->width = static_cast<uint16_t>(texture->GetWidth());
image->height = static_cast<uint16_t>(texture->GetHeight());
image->depth = static_cast<uint16_t>(texture->GetDepth());
#ifdef FEATURE_T6
image->streaming = 1;
image->streamedParts[0].levelCount = 1;
image->streamedParts[0].levelSize = static_cast<uint32_t>(loadingResult.m_iwi_size);
image->streamedParts[0].hash = loadingResult.m_hash.crc32 & 0x1FFFFFFF;
image->streamedPartCount = 1;
#endif
image->texture.loadDef = m_memory.Alloc<GfxImageLoadDef>();
return AssetCreationResult::Success(context.AddAsset<AssetImage>(assetName, image));
}
MemoryManager& m_memory;
ISearchPath& m_search_path;
};
} // namespace
namespace image
{
#set LOADER_METHOD "CreateLoaderExternal" + GAME
std::unique_ptr<AssetCreator<AssetImage>> LOADER_METHOD(MemoryManager& memory, ISearchPath& searchPath)
{
return std::make_unique<LOADER_CLASS>(memory, searchPath);
}
} // namespace image

View File

@@ -0,0 +1,23 @@
#options GAME (IW3, IW4, IW5, T5, T6)
#filename "Game/" + GAME + "/Image/ImageLoaderExternal" + GAME + ".h"
// This file was templated.
// See ImageLoaderExternal.h.template.
// Do not modify, changes will be lost.
#pragma once
#include "Asset/IAssetCreator.h"
#set GAME_HEADER "\"Game/" + GAME + "/" + GAME + ".h\""
#include GAME_HEADER
#include "SearchPath/ISearchPath.h"
#include "Utils/MemoryManager.h"
#include <memory>
namespace image
{
#set LOADER_METHOD "CreateLoaderExternal" + GAME
std::unique_ptr<AssetCreator<GAME::AssetImage>> LOADER_METHOD(MemoryManager& memory, ISearchPath& searchPath);
} // namespace image

View File

@@ -16,6 +16,7 @@
#include <format> #include <format>
using namespace IW3; using namespace IW3;
using namespace image;
namespace namespace
{ {
@@ -26,15 +27,15 @@ namespace
const auto& loadDef = *image.texture.loadDef; const auto& loadDef = *image.texture.loadDef;
textureLoader.Width(loadDef.dimensions[0]).Height(loadDef.dimensions[1]).Depth(loadDef.dimensions[2]); textureLoader.Width(loadDef.dimensions[0]).Height(loadDef.dimensions[1]).Depth(loadDef.dimensions[2]);
if (loadDef.flags & iwi6::IMG_FLAG_VOLMAP) if (loadDef.flags & image::iwi6::IMG_FLAG_VOLMAP)
textureLoader.Type(TextureType::T_3D); textureLoader.Type(TextureType::T_3D);
else if (loadDef.flags & iwi6::IMG_FLAG_CUBEMAP) else if (loadDef.flags & image::iwi6::IMG_FLAG_CUBEMAP)
textureLoader.Type(TextureType::T_CUBE); textureLoader.Type(TextureType::T_CUBE);
else else
textureLoader.Type(TextureType::T_2D); textureLoader.Type(TextureType::T_2D);
textureLoader.Format(static_cast<oat::D3DFORMAT>(loadDef.format)); textureLoader.Format(static_cast<oat::D3DFORMAT>(loadDef.format));
textureLoader.HasMipMaps(!(loadDef.flags & iwi6::IMG_FLAG_NOMIPMAPS)); textureLoader.HasMipMaps(!(loadDef.flags & image::iwi6::IMG_FLAG_NOMIPMAPS));
return textureLoader.LoadTexture(loadDef.data); return textureLoader.LoadTexture(loadDef.data);
} }
@@ -48,7 +49,8 @@ namespace
return nullptr; return nullptr;
} }
return iwi::LoadIwi(*filePathImage.m_stream); auto loadResult = image::LoadIwi(*filePathImage.m_stream);
return loadResult ? std::move(loadResult->m_texture) : nullptr;
} }
std::unique_ptr<Texture> LoadImageData(ISearchPath& searchPath, const GfxImage& image) std::unique_ptr<Texture> LoadImageData(ISearchPath& searchPath, const GfxImage& image)

View File

@@ -2,7 +2,7 @@
#include "Dumping/AbstractAssetDumper.h" #include "Dumping/AbstractAssetDumper.h"
#include "Game/IW3/IW3.h" #include "Game/IW3/IW3.h"
#include "Image/IImageWriter.h" #include "Image/ImageWriter.h"
#include <memory> #include <memory>
@@ -17,6 +17,6 @@ namespace image
void DumpAsset(AssetDumpingContext& context, const XAssetInfo<IW3::AssetImage::Type>& asset) override; void DumpAsset(AssetDumpingContext& context, const XAssetInfo<IW3::AssetImage::Type>& asset) override;
private: private:
std::unique_ptr<IImageWriter> m_writer; std::unique_ptr<ImageWriter> m_writer;
}; };
} // namespace image } // namespace image

View File

@@ -13,6 +13,7 @@
#include <format> #include <format>
using namespace IW4; using namespace IW4;
using namespace image;
namespace namespace
{ {
@@ -23,15 +24,15 @@ namespace
const auto& loadDef = *image.texture.loadDef; const auto& loadDef = *image.texture.loadDef;
textureLoader.Width(image.width).Height(image.height).Depth(image.depth); textureLoader.Width(image.width).Height(image.height).Depth(image.depth);
if ((loadDef.flags & iwi8::IMG_FLAG_MAPTYPE_MASK) == iwi8::IMG_FLAG_MAPTYPE_3D) if ((loadDef.flags & image::iwi8::IMG_FLAG_MAPTYPE_MASK) == image::iwi8::IMG_FLAG_MAPTYPE_3D)
textureLoader.Type(TextureType::T_3D); textureLoader.Type(TextureType::T_3D);
else if ((loadDef.flags & iwi8::IMG_FLAG_MAPTYPE_MASK) == iwi8::IMG_FLAG_MAPTYPE_CUBE) else if ((loadDef.flags & image::iwi8::IMG_FLAG_MAPTYPE_MASK) == image::iwi8::IMG_FLAG_MAPTYPE_CUBE)
textureLoader.Type(TextureType::T_CUBE); textureLoader.Type(TextureType::T_CUBE);
else else
textureLoader.Type(TextureType::T_2D); textureLoader.Type(TextureType::T_2D);
textureLoader.Format(static_cast<oat::D3DFORMAT>(loadDef.format)); textureLoader.Format(static_cast<oat::D3DFORMAT>(loadDef.format));
textureLoader.HasMipMaps(!(loadDef.flags & iwi8::IMG_FLAG_NOMIPMAPS)); textureLoader.HasMipMaps(!(loadDef.flags & image::iwi8::IMG_FLAG_NOMIPMAPS));
return textureLoader.LoadTexture(loadDef.data); return textureLoader.LoadTexture(loadDef.data);
} }
@@ -45,7 +46,8 @@ namespace
return nullptr; return nullptr;
} }
return iwi::LoadIwi(*filePathImage.m_stream); auto loadResult = image::LoadIwi(*filePathImage.m_stream);
return loadResult ? std::move(loadResult->m_texture) : nullptr;
} }
std::unique_ptr<Texture> LoadImageData(ISearchPath& searchPath, const GfxImage& image) std::unique_ptr<Texture> LoadImageData(ISearchPath& searchPath, const GfxImage& image)

View File

@@ -2,7 +2,7 @@
#include "Dumping/AbstractAssetDumper.h" #include "Dumping/AbstractAssetDumper.h"
#include "Game/IW4/IW4.h" #include "Game/IW4/IW4.h"
#include "Image/IImageWriter.h" #include "Image/ImageWriter.h"
#include <memory> #include <memory>
@@ -17,6 +17,6 @@ namespace image
void DumpAsset(AssetDumpingContext& context, const XAssetInfo<IW4::AssetImage::Type>& asset) override; void DumpAsset(AssetDumpingContext& context, const XAssetInfo<IW4::AssetImage::Type>& asset) override;
private: private:
std::unique_ptr<IImageWriter> m_writer; std::unique_ptr<ImageWriter> m_writer;
}; };
} // namespace image } // namespace image

View File

@@ -13,6 +13,7 @@
#include <format> #include <format>
using namespace IW5; using namespace IW5;
using namespace image;
namespace namespace
{ {
@@ -24,15 +25,15 @@ namespace
textureLoader.Width(image.width).Height(image.height).Depth(image.depth); textureLoader.Width(image.width).Height(image.height).Depth(image.depth);
if ((loadDef.flags & iwi8::IMG_FLAG_MAPTYPE_MASK) == iwi8::IMG_FLAG_MAPTYPE_3D) if ((loadDef.flags & image::iwi8::IMG_FLAG_MAPTYPE_MASK) == image::iwi8::IMG_FLAG_MAPTYPE_3D)
textureLoader.Type(TextureType::T_3D); textureLoader.Type(TextureType::T_3D);
else if ((loadDef.flags & iwi8::IMG_FLAG_MAPTYPE_MASK) == iwi8::IMG_FLAG_MAPTYPE_CUBE) else if ((loadDef.flags & image::iwi8::IMG_FLAG_MAPTYPE_MASK) == image::iwi8::IMG_FLAG_MAPTYPE_CUBE)
textureLoader.Type(TextureType::T_CUBE); textureLoader.Type(TextureType::T_CUBE);
else else
textureLoader.Type(TextureType::T_2D); textureLoader.Type(TextureType::T_2D);
textureLoader.Format(static_cast<oat::D3DFORMAT>(loadDef.format)); textureLoader.Format(static_cast<oat::D3DFORMAT>(loadDef.format));
textureLoader.HasMipMaps(!(loadDef.flags & iwi8::IMG_FLAG_NOMIPMAPS)); textureLoader.HasMipMaps(!(loadDef.flags & image::iwi8::IMG_FLAG_NOMIPMAPS));
return textureLoader.LoadTexture(loadDef.data); return textureLoader.LoadTexture(loadDef.data);
} }
@@ -46,7 +47,8 @@ namespace
return nullptr; return nullptr;
} }
return iwi::LoadIwi(*filePathImage.m_stream); auto loadResult = image::LoadIwi(*filePathImage.m_stream);
return loadResult ? std::move(loadResult->m_texture) : nullptr;
} }
std::unique_ptr<Texture> LoadImageData(ISearchPath& searchPath, const GfxImage& image) std::unique_ptr<Texture> LoadImageData(ISearchPath& searchPath, const GfxImage& image)

View File

@@ -2,7 +2,7 @@
#include "Dumping/AbstractAssetDumper.h" #include "Dumping/AbstractAssetDumper.h"
#include "Game/IW5/IW5.h" #include "Game/IW5/IW5.h"
#include "Image/IImageWriter.h" #include "Image/ImageWriter.h"
#include <memory> #include <memory>
@@ -17,6 +17,6 @@ namespace image
void DumpAsset(AssetDumpingContext& context, const XAssetInfo<IW5::AssetImage::Type>& asset) override; void DumpAsset(AssetDumpingContext& context, const XAssetInfo<IW5::AssetImage::Type>& asset) override;
private: private:
std::unique_ptr<IImageWriter> m_writer; std::unique_ptr<ImageWriter> m_writer;
}; };
} // namespace image } // namespace image

View File

@@ -13,6 +13,7 @@
#include <format> #include <format>
using namespace T5; using namespace T5;
using namespace image;
namespace namespace
{ {
@@ -23,15 +24,15 @@ namespace
const auto& loadDef = *image.texture.loadDef; const auto& loadDef = *image.texture.loadDef;
textureLoader.Width(image.width).Height(image.height).Depth(image.depth); textureLoader.Width(image.width).Height(image.height).Depth(image.depth);
if (loadDef.flags & iwi13::IMG_FLAG_VOLMAP) if (loadDef.flags & image::iwi13::IMG_FLAG_VOLMAP)
textureLoader.Type(TextureType::T_3D); textureLoader.Type(TextureType::T_3D);
else if (loadDef.flags & iwi13::IMG_FLAG_CUBEMAP) else if (loadDef.flags & image::iwi13::IMG_FLAG_CUBEMAP)
textureLoader.Type(TextureType::T_CUBE); textureLoader.Type(TextureType::T_CUBE);
else else
textureLoader.Type(TextureType::T_2D); textureLoader.Type(TextureType::T_2D);
textureLoader.Format(static_cast<oat::D3DFORMAT>(loadDef.format)); textureLoader.Format(static_cast<oat::D3DFORMAT>(loadDef.format));
textureLoader.HasMipMaps(!(loadDef.flags & iwi13::IMG_FLAG_NOMIPMAPS)); textureLoader.HasMipMaps(!(loadDef.flags & image::iwi13::IMG_FLAG_NOMIPMAPS));
return textureLoader.LoadTexture(loadDef.data); return textureLoader.LoadTexture(loadDef.data);
} }
@@ -45,7 +46,8 @@ namespace
return nullptr; return nullptr;
} }
return iwi::LoadIwi(*filePathImage.m_stream); auto loadResult = image::LoadIwi(*filePathImage.m_stream);
return loadResult ? std::move(loadResult->m_texture) : nullptr;
} }
std::unique_ptr<Texture> LoadImageData(ISearchPath& searchPath, const GfxImage& image) std::unique_ptr<Texture> LoadImageData(ISearchPath& searchPath, const GfxImage& image)

View File

@@ -2,7 +2,7 @@
#include "Dumping/AbstractAssetDumper.h" #include "Dumping/AbstractAssetDumper.h"
#include "Game/T5/T5.h" #include "Game/T5/T5.h"
#include "Image/IImageWriter.h" #include "Image/ImageWriter.h"
#include <memory> #include <memory>
@@ -17,6 +17,6 @@ namespace image
void DumpAsset(AssetDumpingContext& context, const XAssetInfo<T5::AssetImage::Type>& asset) override; void DumpAsset(AssetDumpingContext& context, const XAssetInfo<T5::AssetImage::Type>& asset) override;
private: private:
std::unique_ptr<IImageWriter> m_writer; std::unique_ptr<ImageWriter> m_writer;
}; };
} // namespace image } // namespace image

View File

@@ -14,6 +14,7 @@
#include <format> #include <format>
using namespace T6; using namespace T6;
using namespace image;
namespace namespace
{ {
@@ -24,15 +25,15 @@ namespace
const auto& loadDef = *image.texture.loadDef; const auto& loadDef = *image.texture.loadDef;
textureLoader.Width(image.width).Height(image.height).Depth(image.depth); textureLoader.Width(image.width).Height(image.height).Depth(image.depth);
if (loadDef.flags & iwi27::IMG_FLAG_VOLMAP) if (loadDef.flags & image::iwi27::IMG_FLAG_VOLMAP)
textureLoader.Type(TextureType::T_3D); textureLoader.Type(TextureType::T_3D);
else if (loadDef.flags & iwi27::IMG_FLAG_CUBEMAP) else if (loadDef.flags & image::iwi27::IMG_FLAG_CUBEMAP)
textureLoader.Type(TextureType::T_CUBE); textureLoader.Type(TextureType::T_CUBE);
else else
textureLoader.Type(TextureType::T_2D); textureLoader.Type(TextureType::T_2D);
textureLoader.Format(static_cast<oat::DXGI_FORMAT>(loadDef.format)); textureLoader.Format(static_cast<oat::DXGI_FORMAT>(loadDef.format));
textureLoader.HasMipMaps(!(loadDef.flags & iwi27::IMG_FLAG_NOMIPMAPS)); textureLoader.HasMipMaps(!(loadDef.flags & image::iwi27::IMG_FLAG_NOMIPMAPS));
return textureLoader.LoadTexture(loadDef.data); return textureLoader.LoadTexture(loadDef.data);
} }
@@ -46,11 +47,11 @@ namespace
if (ipakStream) if (ipakStream)
{ {
auto loadedTexture = iwi::LoadIwi(*ipakStream); auto loadResult = image::LoadIwi(*ipakStream);
ipakStream->close(); ipakStream->close();
if (loadedTexture != nullptr) if (loadResult)
return loadedTexture; return std::move(loadResult->m_texture);
} }
} }
} }
@@ -63,7 +64,8 @@ namespace
return nullptr; return nullptr;
} }
return iwi::LoadIwi(*filePathImage.m_stream); auto loadResult = image::LoadIwi(*filePathImage.m_stream);
return loadResult ? std::move(loadResult->m_texture) : nullptr;
} }
std::unique_ptr<Texture> LoadImageData(ISearchPath& searchPath, const GfxImage& image) std::unique_ptr<Texture> LoadImageData(ISearchPath& searchPath, const GfxImage& image)

View File

@@ -2,7 +2,7 @@
#include "Dumping/AbstractAssetDumper.h" #include "Dumping/AbstractAssetDumper.h"
#include "Game/T6/T6.h" #include "Game/T6/T6.h"
#include "Image/IImageWriter.h" #include "Image/ImageWriter.h"
#include <memory> #include <memory>
@@ -17,6 +17,6 @@ namespace image
void DumpAsset(AssetDumpingContext& context, const XAssetInfo<T6::AssetImage::Type>& asset) override; void DumpAsset(AssetDumpingContext& context, const XAssetInfo<T6::AssetImage::Type>& asset) override;
private: private:
std::unique_ptr<IImageWriter> m_writer; std::unique_ptr<ImageWriter> m_writer;
}; };
} // namespace image } // namespace image

View File

@@ -3,6 +3,7 @@ ObjLoadingTests = {}
function ObjLoadingTests:include(includes) function ObjLoadingTests:include(includes)
if includes:handle(self:name()) then if includes:handle(self:name()) then
includedirs { includedirs {
"%{wks.location}/src/ObjLoading",
path.join(TestFolder(), "ObjLoadingTests") path.join(TestFolder(), "ObjLoadingTests")
} }
end end

View File

@@ -0,0 +1,57 @@
#include "Game/IW3/Image/ImageLoaderEmbeddedIW3.h"
#include "Game/IW3/GameIW3.h"
#include "OatTestPaths.h"
#include "SearchPath/MockSearchPath.h"
#include "Utils/MemoryManager.h"
#include <catch2/catch_test_macros.hpp>
#include <filesystem>
#include <fstream>
#include <memory>
#include <string>
namespace fs = std::filesystem;
using namespace IW3;
using namespace std::literals;
namespace
{
TEST_CASE("ImageLoaderEmbeddedIW3: Can parse dds", "[iw3][image]")
{
MockSearchPath searchPath;
const auto filePath = oat::paths::GetTestDirectory() / "ObjLoadingTests/Game/IW3/Image/TestImage.dds";
const auto fileSize = static_cast<size_t>(fs::file_size(filePath));
std::ifstream file(filePath, std::ios::binary);
REQUIRE(file.is_open());
const auto data = std::make_unique<char[]>(fileSize);
file.read(data.get(), fileSize);
searchPath.AddFileData("images/_testimage.dds", std::string(data.get(), fileSize));
Zone zone("MockZone", 0, GameId::IW3, GamePlatform::PC);
MemoryManager memory;
AssetCreatorCollection creatorCollection(zone);
IgnoredAssetLookup ignoredAssetLookup;
AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup);
auto loader = image::CreateLoaderEmbeddedIW3(memory, searchPath);
auto result = loader->CreateAsset("*testimage", context);
REQUIRE(result.HasBeenSuccessful());
const auto* assetInfo = reinterpret_cast<XAssetInfo<GfxImage>*>(result.GetAssetInfo());
const auto* image = assetInfo->Asset();
REQUIRE(image->name == "*testimage"s);
REQUIRE(image->width == 64);
REQUIRE(image->height == 64);
REQUIRE(image->depth == 1);
REQUIRE(image->texture.loadDef);
REQUIRE(image->texture.loadDef->resourceSize > 0);
}
} // namespace

View File

@@ -0,0 +1,54 @@
#include "Game/IW3/Image/ImageLoaderExternalIW3.h"
#include "Game/IW3/GameIW3.h"
#include "OatTestPaths.h"
#include "SearchPath/MockSearchPath.h"
#include "Utils/MemoryManager.h"
#include <catch2/catch_test_macros.hpp>
#include <filesystem>
#include <fstream>
#include <memory>
#include <string>
namespace fs = std::filesystem;
using namespace IW3;
using namespace std::literals;
namespace
{
TEST_CASE("ImageLoaderExternalIW3: Can parse iwi", "[iw3][image]")
{
MockSearchPath searchPath;
const auto filePath = oat::paths::GetTestDirectory() / "ObjLoadingTests/Game/IW3/Image/TestImage.iwi";
const auto fileSize = static_cast<size_t>(fs::file_size(filePath));
std::ifstream file(filePath, std::ios::binary);
REQUIRE(file.is_open());
const auto data = std::make_unique<char[]>(fileSize);
file.read(data.get(), fileSize);
searchPath.AddFileData("images/testimage.iwi", std::string(data.get(), fileSize));
Zone zone("MockZone", 0, GameId::IW3, GamePlatform::PC);
MemoryManager memory;
AssetCreatorCollection creatorCollection(zone);
IgnoredAssetLookup ignoredAssetLookup;
AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup);
auto loader = image::CreateLoaderExternalIW3(memory, searchPath);
auto result = loader->CreateAsset("testimage", context);
REQUIRE(result.HasBeenSuccessful());
const auto* assetInfo = reinterpret_cast<XAssetInfo<GfxImage>*>(result.GetAssetInfo());
const auto* image = assetInfo->Asset();
REQUIRE(image->name == "testimage"s);
REQUIRE(image->width == 64);
REQUIRE(image->height == 64);
REQUIRE(image->depth == 1);
}
} // namespace

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,57 @@
#include "Game/IW4/Image/ImageLoaderEmbeddedIW4.h"
#include "Game/IW4/GameIW4.h"
#include "OatTestPaths.h"
#include "SearchPath/MockSearchPath.h"
#include "Utils/MemoryManager.h"
#include <catch2/catch_test_macros.hpp>
#include <filesystem>
#include <fstream>
#include <memory>
#include <string>
namespace fs = std::filesystem;
using namespace IW4;
using namespace std::literals;
namespace
{
TEST_CASE("ImageLoaderEmbeddedIW4: Can parse dds", "[iw4][image]")
{
MockSearchPath searchPath;
const auto filePath = oat::paths::GetTestDirectory() / "ObjLoadingTests/Game/IW4/Image/TestImage.dds";
const auto fileSize = static_cast<size_t>(fs::file_size(filePath));
std::ifstream file(filePath, std::ios::binary);
REQUIRE(file.is_open());
const auto data = std::make_unique<char[]>(fileSize);
file.read(data.get(), fileSize);
searchPath.AddFileData("images/_testimage.dds", std::string(data.get(), fileSize));
Zone zone("MockZone", 0, GameId::IW4, GamePlatform::PC);
MemoryManager memory;
AssetCreatorCollection creatorCollection(zone);
IgnoredAssetLookup ignoredAssetLookup;
AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup);
auto loader = image::CreateLoaderEmbeddedIW4(memory, searchPath);
auto result = loader->CreateAsset("*testimage", context);
REQUIRE(result.HasBeenSuccessful());
const auto* assetInfo = reinterpret_cast<XAssetInfo<GfxImage>*>(result.GetAssetInfo());
const auto* image = assetInfo->Asset();
REQUIRE(image->name == "*testimage"s);
REQUIRE(image->width == 64);
REQUIRE(image->height == 64);
REQUIRE(image->depth == 1);
REQUIRE(image->texture.loadDef);
REQUIRE(image->texture.loadDef->resourceSize > 0);
}
} // namespace

View File

@@ -0,0 +1,54 @@
#include "Game/IW4/Image/ImageLoaderExternalIW4.h"
#include "Game/IW4/GameIW4.h"
#include "OatTestPaths.h"
#include "SearchPath/MockSearchPath.h"
#include "Utils/MemoryManager.h"
#include <catch2/catch_test_macros.hpp>
#include <filesystem>
#include <fstream>
#include <memory>
#include <string>
namespace fs = std::filesystem;
using namespace IW4;
using namespace std::literals;
namespace
{
TEST_CASE("ImageLoaderExternalIW4: Can parse iwi", "[iw4][image]")
{
MockSearchPath searchPath;
const auto filePath = oat::paths::GetTestDirectory() / "ObjLoadingTests/Game/IW4/Image/TestImage.iwi";
const auto fileSize = static_cast<size_t>(fs::file_size(filePath));
std::ifstream file(filePath, std::ios::binary);
REQUIRE(file.is_open());
const auto data = std::make_unique<char[]>(fileSize);
file.read(data.get(), fileSize);
searchPath.AddFileData("images/testimage.iwi", std::string(data.get(), fileSize));
Zone zone("MockZone", 0, GameId::IW4, GamePlatform::PC);
MemoryManager memory;
AssetCreatorCollection creatorCollection(zone);
IgnoredAssetLookup ignoredAssetLookup;
AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup);
auto loader = image::CreateLoaderExternalIW4(memory, searchPath);
auto result = loader->CreateAsset("testimage", context);
REQUIRE(result.HasBeenSuccessful());
const auto* assetInfo = reinterpret_cast<XAssetInfo<GfxImage>*>(result.GetAssetInfo());
const auto* image = assetInfo->Asset();
REQUIRE(image->name == "testimage"s);
REQUIRE(image->width == 64);
REQUIRE(image->height == 64);
REQUIRE(image->depth == 1);
}
} // namespace

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,57 @@
#include "Game/IW5/Image/ImageLoaderEmbeddedIW5.h"
#include "Game/IW5/GameIW5.h"
#include "OatTestPaths.h"
#include "SearchPath/MockSearchPath.h"
#include "Utils/MemoryManager.h"
#include <catch2/catch_test_macros.hpp>
#include <filesystem>
#include <fstream>
#include <memory>
#include <string>
namespace fs = std::filesystem;
using namespace IW5;
using namespace std::literals;
namespace
{
TEST_CASE("ImageLoaderEmbeddedIW5: Can parse dds", "[iw5][image]")
{
MockSearchPath searchPath;
const auto filePath = oat::paths::GetTestDirectory() / "ObjLoadingTests/Game/IW5/Image/TestImage.dds";
const auto fileSize = static_cast<size_t>(fs::file_size(filePath));
std::ifstream file(filePath, std::ios::binary);
REQUIRE(file.is_open());
const auto data = std::make_unique<char[]>(fileSize);
file.read(data.get(), fileSize);
searchPath.AddFileData("images/_testimage.dds", std::string(data.get(), fileSize));
Zone zone("MockZone", 0, GameId::IW5, GamePlatform::PC);
MemoryManager memory;
AssetCreatorCollection creatorCollection(zone);
IgnoredAssetLookup ignoredAssetLookup;
AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup);
auto loader = image::CreateLoaderEmbeddedIW5(memory, searchPath);
auto result = loader->CreateAsset("*testimage", context);
REQUIRE(result.HasBeenSuccessful());
const auto* assetInfo = reinterpret_cast<XAssetInfo<GfxImage>*>(result.GetAssetInfo());
const auto* image = assetInfo->Asset();
REQUIRE(image->name == "*testimage"s);
REQUIRE(image->width == 64);
REQUIRE(image->height == 64);
REQUIRE(image->depth == 1);
REQUIRE(image->texture.loadDef);
REQUIRE(image->texture.loadDef->resourceSize > 0);
}
} // namespace

View File

@@ -0,0 +1,54 @@
#include "Game/IW5/Image/ImageLoaderExternalIW5.h"
#include "Game/IW5/GameIW5.h"
#include "OatTestPaths.h"
#include "SearchPath/MockSearchPath.h"
#include "Utils/MemoryManager.h"
#include <catch2/catch_test_macros.hpp>
#include <filesystem>
#include <fstream>
#include <memory>
#include <string>
namespace fs = std::filesystem;
using namespace IW5;
using namespace std::literals;
namespace
{
TEST_CASE("ImageLoaderExternalIW5: Can parse iwi", "[iw5][image]")
{
MockSearchPath searchPath;
const auto filePath = oat::paths::GetTestDirectory() / "ObjLoadingTests/Game/IW5/Image/TestImage.iwi";
const auto fileSize = static_cast<size_t>(fs::file_size(filePath));
std::ifstream file(filePath, std::ios::binary);
REQUIRE(file.is_open());
const auto data = std::make_unique<char[]>(fileSize);
file.read(data.get(), fileSize);
searchPath.AddFileData("images/testimage.iwi", std::string(data.get(), fileSize));
Zone zone("MockZone", 0, GameId::IW5, GamePlatform::PC);
MemoryManager memory;
AssetCreatorCollection creatorCollection(zone);
IgnoredAssetLookup ignoredAssetLookup;
AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup);
auto loader = image::CreateLoaderExternalIW5(memory, searchPath);
auto result = loader->CreateAsset("testimage", context);
REQUIRE(result.HasBeenSuccessful());
const auto* assetInfo = reinterpret_cast<XAssetInfo<GfxImage>*>(result.GetAssetInfo());
const auto* image = assetInfo->Asset();
REQUIRE(image->name == "testimage"s);
REQUIRE(image->width == 64);
REQUIRE(image->height == 64);
REQUIRE(image->depth == 1);
}
} // namespace

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,57 @@
#include "Game/T5/Image/ImageLoaderEmbeddedT5.h"
#include "Game/T5/GameT5.h"
#include "OatTestPaths.h"
#include "SearchPath/MockSearchPath.h"
#include "Utils/MemoryManager.h"
#include <catch2/catch_test_macros.hpp>
#include <filesystem>
#include <fstream>
#include <memory>
#include <string>
namespace fs = std::filesystem;
using namespace T5;
using namespace std::literals;
namespace
{
TEST_CASE("ImageLoaderEmbeddedT5: Can parse dds", "[t5][image]")
{
MockSearchPath searchPath;
const auto filePath = oat::paths::GetTestDirectory() / "ObjLoadingTests/Game/T5/Image/TestImage.dds";
const auto fileSize = static_cast<size_t>(fs::file_size(filePath));
std::ifstream file(filePath, std::ios::binary);
REQUIRE(file.is_open());
const auto data = std::make_unique<char[]>(fileSize);
file.read(data.get(), fileSize);
searchPath.AddFileData("images/_testimage.dds", std::string(data.get(), fileSize));
Zone zone("MockZone", 0, GameId::T5, GamePlatform::PC);
MemoryManager memory;
AssetCreatorCollection creatorCollection(zone);
IgnoredAssetLookup ignoredAssetLookup;
AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup);
auto loader = image::CreateLoaderEmbeddedT5(memory, searchPath);
auto result = loader->CreateAsset("*testimage", context);
REQUIRE(result.HasBeenSuccessful());
const auto* assetInfo = reinterpret_cast<XAssetInfo<GfxImage>*>(result.GetAssetInfo());
const auto* image = assetInfo->Asset();
REQUIRE(image->name == "*testimage"s);
REQUIRE(image->width == 64);
REQUIRE(image->height == 64);
REQUIRE(image->depth == 1);
REQUIRE(image->texture.loadDef);
REQUIRE(image->texture.loadDef->resourceSize > 0);
}
} // namespace

View File

@@ -0,0 +1,54 @@
#include "Game/T5/Image/ImageLoaderExternalT5.h"
#include "Game/T5/GameT5.h"
#include "OatTestPaths.h"
#include "SearchPath/MockSearchPath.h"
#include "Utils/MemoryManager.h"
#include <catch2/catch_test_macros.hpp>
#include <filesystem>
#include <fstream>
#include <memory>
#include <string>
namespace fs = std::filesystem;
using namespace T5;
using namespace std::literals;
namespace
{
TEST_CASE("ImageLoaderExternalT5: Can parse iwi", "[t5][image]")
{
MockSearchPath searchPath;
const auto filePath = oat::paths::GetTestDirectory() / "ObjLoadingTests/Game/T5/Image/TestImage.iwi";
const auto fileSize = static_cast<size_t>(fs::file_size(filePath));
std::ifstream file(filePath, std::ios::binary);
REQUIRE(file.is_open());
const auto data = std::make_unique<char[]>(fileSize);
file.read(data.get(), fileSize);
searchPath.AddFileData("images/testimage.iwi", std::string(data.get(), fileSize));
Zone zone("MockZone", 0, GameId::T5, GamePlatform::PC);
MemoryManager memory;
AssetCreatorCollection creatorCollection(zone);
IgnoredAssetLookup ignoredAssetLookup;
AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup);
auto loader = image::CreateLoaderExternalT5(memory, searchPath);
auto result = loader->CreateAsset("testimage", context);
REQUIRE(result.HasBeenSuccessful());
const auto* assetInfo = reinterpret_cast<XAssetInfo<GfxImage>*>(result.GetAssetInfo());
const auto* image = assetInfo->Asset();
REQUIRE(image->name == "testimage"s);
REQUIRE(image->width == 64);
REQUIRE(image->height == 64);
REQUIRE(image->depth == 1);
}
} // namespace

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,58 @@
#include "Game/T6/Image/ImageLoaderEmbeddedT6.h"
#include "Game/T6/GameT6.h"
#include "OatTestPaths.h"
#include "SearchPath/MockSearchPath.h"
#include "Utils/MemoryManager.h"
#include <catch2/catch_test_macros.hpp>
#include <filesystem>
#include <fstream>
#include <memory>
#include <string>
namespace fs = std::filesystem;
using namespace T6;
using namespace std::literals;
namespace
{
TEST_CASE("ImageLoaderEmbeddedT6: Can parse dds", "[t6][image]")
{
MockSearchPath searchPath;
const auto filePath = oat::paths::GetTestDirectory() / "ObjLoadingTests/Game/T6/Image/TestImage.dds";
const auto fileSize = static_cast<size_t>(fs::file_size(filePath));
std::ifstream file(filePath, std::ios::binary);
REQUIRE(file.is_open());
const auto data = std::make_unique<char[]>(fileSize);
file.read(data.get(), fileSize);
searchPath.AddFileData("images/_testimage.dds", std::string(data.get(), fileSize));
Zone zone("MockZone", 0, GameId::T6, GamePlatform::PC);
MemoryManager memory;
AssetCreatorCollection creatorCollection(zone);
IgnoredAssetLookup ignoredAssetLookup;
AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup);
auto loader = image::CreateLoaderEmbeddedT6(memory, searchPath);
auto result = loader->CreateAsset("*testimage", context);
REQUIRE(result.HasBeenSuccessful());
const auto* assetInfo = reinterpret_cast<XAssetInfo<GfxImage>*>(result.GetAssetInfo());
const auto* image = assetInfo->Asset();
REQUIRE(image->name == "*testimage"s);
REQUIRE(image->width == 64);
REQUIRE(image->height == 64);
REQUIRE(image->depth == 1);
REQUIRE(image->streamedPartCount == 0);
REQUIRE(image->texture.loadDef);
REQUIRE(image->texture.loadDef->resourceSize > 0);
}
} // namespace

View File

@@ -0,0 +1,57 @@
#include "Game/T6/Image/ImageLoaderExternalT6.h"
#include "Game/T6/GameT6.h"
#include "OatTestPaths.h"
#include "SearchPath/MockSearchPath.h"
#include "Utils/MemoryManager.h"
#include <catch2/catch_test_macros.hpp>
#include <filesystem>
#include <fstream>
#include <memory>
#include <string>
namespace fs = std::filesystem;
using namespace T6;
using namespace std::literals;
namespace
{
TEST_CASE("ImageLoaderExternalT6: Can parse iwi", "[t6][image]")
{
MockSearchPath searchPath;
const auto filePath = oat::paths::GetTestDirectory() / "ObjLoadingTests/Game/T6/Image/TestImage.iwi";
const auto fileSize = static_cast<size_t>(fs::file_size(filePath));
std::ifstream file(filePath, std::ios::binary);
REQUIRE(file.is_open());
const auto data = std::make_unique<char[]>(fileSize);
file.read(data.get(), fileSize);
searchPath.AddFileData("images/testimage.iwi", std::string(data.get(), fileSize));
Zone zone("MockZone", 0, GameId::T6, GamePlatform::PC);
MemoryManager memory;
AssetCreatorCollection creatorCollection(zone);
IgnoredAssetLookup ignoredAssetLookup;
AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup);
auto loader = image::CreateLoaderExternalT6(memory, searchPath);
auto result = loader->CreateAsset("testimage", context);
REQUIRE(result.HasBeenSuccessful());
const auto* assetInfo = reinterpret_cast<XAssetInfo<GfxImage>*>(result.GetAssetInfo());
const auto* image = assetInfo->Asset();
REQUIRE(image->name == "testimage"s);
REQUIRE(image->width == 64);
REQUIRE(image->height == 64);
REQUIRE(image->depth == 1);
REQUIRE(image->streamedPartCount == 1);
REQUIRE(image->streamedParts[0].levelSize == 2112);
REQUIRE(image->streamedParts[0].hash == (0x386422F2 & 0x1FFFFFFF));
}
} // namespace

Binary file not shown.

Binary file not shown.