2
0
mirror of https://github.com/Laupetin/OpenAssetTools.git synced 2026-05-16 23:11:42 +00:00

feat: add support for extracting ipaks to ImageConverter

This commit is contained in:
Jan Laupetin
2026-05-11 21:58:25 +02:00
parent a2ab44da39
commit 36b4ec7781
6 changed files with 98 additions and 60 deletions
+2
View File
@@ -39,12 +39,14 @@ function ImageConverter:project()
self:include(includes) self:include(includes)
Utils:include(includes) Utils:include(includes)
Common:include(includes) Common:include(includes)
ObjLoading:include(includes)
ObjImage:include(includes) ObjImage:include(includes)
Raw:use() Raw:use()
links:linkto(Utils) links:linkto(Utils)
links:linkto(Common) links:linkto(Common)
links:linkto(ObjLoading)
links:linkto(ObjImage) links:linkto(ObjImage)
links:linkall() links:linkall()
end end
+79 -27
View File
@@ -8,8 +8,8 @@
#include "Image/IwiWriter27.h" #include "Image/IwiWriter27.h"
#include "Image/IwiWriter6.h" #include "Image/IwiWriter6.h"
#include "Image/IwiWriter8.h" #include "Image/IwiWriter8.h"
#include "Image/Texture.h"
#include "ImageConverterArgs.h" #include "ImageConverterArgs.h"
#include "ObjContainer/IPak/IPak.h"
#include "Utils/Logging/Log.h" #include "Utils/Logging/Log.h"
#include "Utils/StringUtils.h" #include "Utils/StringUtils.h"
@@ -27,36 +27,17 @@ namespace
{ {
constexpr auto EXTENSION_IWI = ".iwi"; constexpr auto EXTENSION_IWI = ".iwi";
constexpr auto EXTENSION_DDS = ".dds"; constexpr auto EXTENSION_DDS = ".dds";
constexpr auto EXTENSION_IPAK = ".ipak";
class ImageConverterImpl final : public ImageConverter class ImageConverter
{ {
public: public:
ImageConverterImpl() explicit ImageConverter(const ImageConverterArgs& args)
: m_game_to_convert_to(std::nullopt) : m_game_to_convert_to(args.m_game_to_convert_to)
{ {
} }
bool Start(const int argc, const char** argv) override void HandleFile(const std::string& file)
{
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)
{ {
const fs::path filePath(file); const fs::path filePath(file);
auto extension = filePath.extension().string(); auto extension = filePath.extension().string();
@@ -66,10 +47,13 @@ namespace
ConvertIwi(filePath); ConvertIwi(filePath);
else if (extension == EXTENSION_DDS) else if (extension == EXTENSION_DDS)
ConvertDds(filePath); ConvertDds(filePath);
else if (extension == EXTENSION_IPAK)
ExtractIpak(filePath);
else else
con::error("Unsupported extension {}", extension); con::error("Unsupported extension {}", extension);
} }
private:
bool ConvertIwi(const fs::path& iwiPath) bool ConvertIwi(const fs::path& iwiPath)
{ {
std::ifstream file(iwiPath, std::ios::in | std::ios::binary); std::ifstream file(iwiPath, std::ios::in | std::ios::binary);
@@ -195,6 +179,60 @@ namespace
return true; return true;
} }
bool ExtractIpak(const fs::path& ipakPath)
{
auto file = std::make_unique<std::ifstream>(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; ImageConverterArgs m_args;
std::optional<GameId> m_game_to_convert_to; std::optional<GameId> m_game_to_convert_to;
DdsWriter m_dds_writer; DdsWriter m_dds_writer;
@@ -202,7 +240,21 @@ namespace
}; };
} // namespace } // namespace
std::unique_ptr<ImageConverter> ImageConverter::Create() bool RunImageConverter(const int argc, const char** argv)
{ {
return std::make_unique<ImageConverterImpl>(); 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;
} }
+1 -22
View File
@@ -1,24 +1,3 @@
#pragma once #pragma once
#include <memory>
class ImageConverter bool RunImageConverter(int argc, const char** argv);
{
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<ImageConverter> Create();
};
+1 -3
View File
@@ -2,7 +2,5 @@
int main(const int argc, const char** argv) int main(const int argc, const char** argv)
{ {
const auto imageConverter = ImageConverter::Create(); return RunImageConverter(argc, argv) ? 0 : 1;
return imageConverter->Start(argc, argv) ? 0 : 1;
} }
+8 -3
View File
@@ -55,7 +55,7 @@ namespace
return true; return true;
} }
[[nodiscard]] std::unique_ptr<iobjstream> GetEntryStream(const Hash nameHash, const Hash dataHash) const override [[nodiscard]] std::unique_ptr<iobjstream> GetEntryStream(const IPakHash nameHash, const IPakHash dataHash) const override
{ {
const IPakIndexEntryKey wantedKey{ const IPakIndexEntryKey wantedKey{
{.dataHash = dataHash, .nameHash = nameHash} {.dataHash = dataHash, .nameHash = nameHash}
@@ -77,6 +77,11 @@ namespace
return nullptr; return nullptr;
} }
[[nodiscard]] const std::vector<IPakIndexEntry>& GetIndexEntries() const override
{
return m_index_entries;
}
std::string GetName() override std::string GetName() override
{ {
return fs::path(m_path).filename().replace_extension("").string(); return fs::path(m_path).filename().replace_extension("").string();
@@ -203,12 +208,12 @@ std::unique_ptr<IIPak> IIPak::Create(std::string path, std::unique_ptr<std::istr
return std::make_unique<IPak>(std::move(path), std::move(stream)); return std::make_unique<IPak>(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); 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<const Bytef*>(data), static_cast<unsigned>(dataSize)); return crc32(0, static_cast<const Bytef*>(data), static_cast<unsigned>(dataSize));
} }
+7 -5
View File
@@ -1,5 +1,6 @@
#pragma once #pragma once
#include "ObjContainer/IPak/IPakTypes.h"
#include "ObjContainer/ObjContainerReferenceable.h" #include "ObjContainer/ObjContainerReferenceable.h"
#include "ObjContainer/ObjContainerRepository.h" #include "ObjContainer/ObjContainerRepository.h"
#include "Utils/ObjStream.h" #include "Utils/ObjStream.h"
@@ -14,19 +15,20 @@ class IIPak : public ObjContainerReferenceable
{ {
public: public:
static ObjContainerRepository<IIPak, Zone> Repository; static ObjContainerRepository<IIPak, Zone> Repository;
typedef std::uint32_t Hash;
IIPak() = default; IIPak() = default;
virtual ~IIPak() = default; ~IIPak() override = default;
IIPak(const IIPak& other) = default; IIPak(const IIPak& other) = default;
IIPak(IIPak&& other) noexcept = default; IIPak(IIPak&& other) noexcept = default;
IIPak& operator=(const IIPak& other) = default; IIPak& operator=(const IIPak& other) = default;
IIPak& operator=(IIPak&& other) noexcept = default; IIPak& operator=(IIPak&& other) noexcept = default;
virtual bool Initialize() = 0; virtual bool Initialize() = 0;
[[nodiscard]] virtual std::unique_ptr<iobjstream> GetEntryStream(Hash nameHash, Hash dataHash) const = 0; [[nodiscard]] virtual std::unique_ptr<iobjstream> GetEntryStream(IPakHash nameHash, IPakHash dataHash) const = 0;
[[nodiscard]] virtual const std::vector<IPakIndexEntry>& GetIndexEntries() const = 0;
static std::unique_ptr<IIPak> Create(std::string path, std::unique_ptr<std::istream> stream); static std::unique_ptr<IIPak> Create(std::string path, std::unique_ptr<std::istream> stream);
static Hash HashString(const std::string& str); static IPakHash HashString(const std::string& str);
static Hash HashData(const void* data, size_t dataSize); static IPakHash HashData(const void* data, size_t dataSize);
}; };