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:
@@ -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
|
||||||
|
|||||||
@@ -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,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();
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user