diff --git a/src/ImageConverter.lua b/src/ImageConverter.lua index a34fa239..5d1f540e 100644 --- a/src/ImageConverter.lua +++ b/src/ImageConverter.lua @@ -39,12 +39,14 @@ function ImageConverter:project() self:include(includes) Utils:include(includes) Common:include(includes) + ObjLoading:include(includes) ObjImage:include(includes) Raw:use() links:linkto(Utils) links:linkto(Common) + links:linkto(ObjLoading) links:linkto(ObjImage) links:linkall() end diff --git a/src/ImageConverter/ImageConverter.cpp b/src/ImageConverter/ImageConverter.cpp index 89cde7f4..5b5694cb 100644 --- a/src/ImageConverter/ImageConverter.cpp +++ b/src/ImageConverter/ImageConverter.cpp @@ -8,8 +8,8 @@ #include "Image/IwiWriter27.h" #include "Image/IwiWriter6.h" #include "Image/IwiWriter8.h" -#include "Image/Texture.h" #include "ImageConverterArgs.h" +#include "ObjContainer/IPak/IPak.h" #include "Utils/Logging/Log.h" #include "Utils/StringUtils.h" @@ -27,36 +27,17 @@ namespace { constexpr auto EXTENSION_IWI = ".iwi"; constexpr auto EXTENSION_DDS = ".dds"; + constexpr auto EXTENSION_IPAK = ".ipak"; - class ImageConverterImpl final : public ImageConverter + class ImageConverter { public: - ImageConverterImpl() - : m_game_to_convert_to(std::nullopt) + explicit ImageConverter(const ImageConverterArgs& args) + : m_game_to_convert_to(args.m_game_to_convert_to) { } - bool Start(const int argc, const char** argv) override - { - con::init(); - - auto shouldContinue = true; - if (!m_args.ParseArgs(argc, argv, shouldContinue)) - return false; - - if (!shouldContinue) - return true; - - m_game_to_convert_to = m_args.m_game_to_convert_to; - - for (const auto& file : m_args.m_files_to_convert) - Convert(file); - - return true; - } - - private: - void Convert(const std::string& file) + void HandleFile(const std::string& file) { const fs::path filePath(file); auto extension = filePath.extension().string(); @@ -66,10 +47,13 @@ namespace ConvertIwi(filePath); else if (extension == EXTENSION_DDS) ConvertDds(filePath); + else if (extension == EXTENSION_IPAK) + ExtractIpak(filePath); else con::error("Unsupported extension {}", extension); } + private: bool ConvertIwi(const fs::path& iwiPath) { std::ifstream file(iwiPath, std::ios::in | std::ios::binary); @@ -195,6 +179,60 @@ namespace return true; } + bool ExtractIpak(const fs::path& ipakPath) + { + auto file = std::make_unique(ipakPath, std::ios::in | std::ios::binary); + if (!file->is_open()) + { + con::error("Failed to open ipak {}", ipakPath.string()); + return false; + } + + auto ipak = IIPak::Create(ipakPath.string(), std::move(file)); + if (!ipak->Initialize()) + { + con::error("Failed to read ipak {}", ipakPath.string()); + return false; + } + + const auto outDir = fs::absolute(ipakPath).parent_path() / ipakPath.filename().replace_extension(); + fs::create_directories(outDir); + + for (const auto& indexEntry : ipak->GetIndexEntries()) + { + const auto fileName = std::format("{:6x}_{:6x}.iwi", indexEntry.key.dataHash, indexEntry.key.nameHash); + std::ofstream outFile(outDir / fileName, std::ios::out | std::ios::binary); + if (!outFile.is_open()) + { + con::error("Failed to open ipak file {}", fileName); + return false; + } + + auto entryStream = ipak->GetEntryStream(indexEntry.key.nameHash, indexEntry.key.dataHash); + if (!entryStream) + { + con::error("Failed to open entry stream for {}", fileName); + return false; + } + + char buffer[0x2000]; + entryStream->read(buffer, 0x2000); + auto readCount = entryStream->gcount(); + while (readCount > 0) + { + outFile.write(buffer, readCount); + + entryStream->read(buffer, 0x2000); + readCount = entryStream->gcount(); + } + + entryStream->close(); + outFile.close(); + } + + return true; + } + ImageConverterArgs m_args; std::optional m_game_to_convert_to; DdsWriter m_dds_writer; @@ -202,7 +240,21 @@ namespace }; } // namespace -std::unique_ptr ImageConverter::Create() +bool RunImageConverter(const int argc, const char** argv) { - return std::make_unique(); + con::init(); + + auto shouldContinue = true; + ImageConverterArgs args; + if (!args.ParseArgs(argc, argv, shouldContinue)) + return false; + + if (!shouldContinue) + return true; + + ImageConverter imageConverter(args); + for (const auto& file : args.m_files_to_convert) + imageConverter.HandleFile(file); + + return true; } diff --git a/src/ImageConverter/ImageConverter.h b/src/ImageConverter/ImageConverter.h index d0e37a87..2e6cfc71 100644 --- a/src/ImageConverter/ImageConverter.h +++ b/src/ImageConverter/ImageConverter.h @@ -1,24 +1,3 @@ #pragma once -#include -class ImageConverter -{ -public: - ImageConverter() = default; - virtual ~ImageConverter() = default; - - ImageConverter(const ImageConverter& other) = delete; - ImageConverter(ImageConverter&& other) noexcept = delete; - ImageConverter& operator=(const ImageConverter& other) = delete; - ImageConverter& operator=(ImageConverter&& other) noexcept = delete; - - /** - * \brief Starts the ImageConverter application logic. - * \param argc The amount of command line arguments specified. - * \param argv The command line arguments. - * \return \c true if the application was successful or \c false if an error occurred. - */ - virtual bool Start(int argc, const char** argv) = 0; - - static std::unique_ptr Create(); -}; +bool RunImageConverter(int argc, const char** argv); diff --git a/src/ImageConverter/main.cpp b/src/ImageConverter/main.cpp index 23145c36..fea7281d 100644 --- a/src/ImageConverter/main.cpp +++ b/src/ImageConverter/main.cpp @@ -2,7 +2,5 @@ int main(const int argc, const char** argv) { - const auto imageConverter = ImageConverter::Create(); - - return imageConverter->Start(argc, argv) ? 0 : 1; + return RunImageConverter(argc, argv) ? 0 : 1; } diff --git a/src/ObjLoading/ObjContainer/IPak/IPak.cpp b/src/ObjLoading/ObjContainer/IPak/IPak.cpp index f61fb95a..0585fd96 100644 --- a/src/ObjLoading/ObjContainer/IPak/IPak.cpp +++ b/src/ObjLoading/ObjContainer/IPak/IPak.cpp @@ -55,7 +55,7 @@ namespace return true; } - [[nodiscard]] std::unique_ptr GetEntryStream(const Hash nameHash, const Hash dataHash) const override + [[nodiscard]] std::unique_ptr GetEntryStream(const IPakHash nameHash, const IPakHash dataHash) const override { const IPakIndexEntryKey wantedKey{ {.dataHash = dataHash, .nameHash = nameHash} @@ -77,6 +77,11 @@ namespace return nullptr; } + [[nodiscard]] const std::vector& GetIndexEntries() const override + { + return m_index_entries; + } + std::string GetName() override { return fs::path(m_path).filename().replace_extension("").string(); @@ -203,12 +208,12 @@ std::unique_ptr IIPak::Create(std::string path, std::unique_ptr(std::move(path), std::move(stream)); } -IIPak::Hash IIPak::HashString(const std::string& str) +IPakHash IIPak::HashString(const std::string& str) { return R_HashString(str.c_str(), 0); } -IIPak::Hash IIPak::HashData(const void* data, const size_t dataSize) +IPakHash IIPak::HashData(const void* data, const size_t dataSize) { return crc32(0, static_cast(data), static_cast(dataSize)); } diff --git a/src/ObjLoading/ObjContainer/IPak/IPak.h b/src/ObjLoading/ObjContainer/IPak/IPak.h index 308513a5..b735807a 100644 --- a/src/ObjLoading/ObjContainer/IPak/IPak.h +++ b/src/ObjLoading/ObjContainer/IPak/IPak.h @@ -1,5 +1,6 @@ #pragma once +#include "ObjContainer/IPak/IPakTypes.h" #include "ObjContainer/ObjContainerReferenceable.h" #include "ObjContainer/ObjContainerRepository.h" #include "Utils/ObjStream.h" @@ -14,19 +15,20 @@ class IIPak : public ObjContainerReferenceable { public: static ObjContainerRepository Repository; - typedef std::uint32_t Hash; IIPak() = default; - virtual ~IIPak() = default; + ~IIPak() override = default; IIPak(const IIPak& other) = default; IIPak(IIPak&& other) noexcept = default; IIPak& operator=(const IIPak& other) = default; IIPak& operator=(IIPak&& other) noexcept = default; virtual bool Initialize() = 0; - [[nodiscard]] virtual std::unique_ptr GetEntryStream(Hash nameHash, Hash dataHash) const = 0; + [[nodiscard]] virtual std::unique_ptr GetEntryStream(IPakHash nameHash, IPakHash dataHash) const = 0; + + [[nodiscard]] virtual const std::vector& GetIndexEntries() const = 0; static std::unique_ptr Create(std::string path, std::unique_ptr stream); - static Hash HashString(const std::string& str); - static Hash HashData(const void* data, size_t dataSize); + static IPakHash HashString(const std::string& str); + static IPakHash HashData(const void* data, size_t dataSize); };