diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperRawFile.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperRawFile.cpp index 33947efd..41c71f29 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperRawFile.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperRawFile.cpp @@ -1,12 +1,87 @@ #include "AssetDumperRawFile.h" +#include +#include + using namespace T6; +namespace fs = std::filesystem; + bool AssetDumperRawFile::ShouldDump(XAssetInfo* asset) { return true; } +void AssetDumperRawFile::DumpAnimtree(AssetDumpingContext& context, XAssetInfo* asset, std::ostream& stream) +{ + const auto* rawFile = asset->Asset(); + + if (rawFile->len <= 4) + { + std::cout << "Invalid len of animtree file \"" << rawFile->name << "\"" << std::endl; + return; + } + + const auto outLen = reinterpret_cast(rawFile->buffer)[0]; + const auto inLen = rawFile->len; + + if (outLen > ANIMTREE_MAX_SIZE) + { + std::cout << "Invalid size of animtree file \"" << rawFile->name << "\": " << outLen << std::endl; + return; + } + + z_stream_s zs{}; + + zs.zalloc = Z_NULL; + zs.zfree = Z_NULL; + zs.opaque = Z_NULL; + zs.avail_in = 0; + zs.next_in = Z_NULL; + + int ret = inflateInit2(&zs, -13); + + if (ret != Z_OK) + { + throw std::runtime_error("Initializing inflate failed"); + } + + zs.next_in = reinterpret_cast(&rawFile->buffer[4]); + zs.avail_in = inLen - 4; + + Bytef buffer[0x1000]; + + size_t writtenSize = 0; + while (zs.avail_in > 0) + { + zs.next_out = buffer; + zs.avail_out = sizeof buffer; + ret = inflate(&zs, Z_SYNC_FLUSH); + + if (ret < 0) + { + std::cout << "Inflate failed for dumping animtree file \"" << rawFile->name << "\"" << std::endl; + inflateEnd(&zs); + return; + } + + const auto inflateOutSize = sizeof buffer - zs.avail_out; + + if (writtenSize + inflateOutSize >= outLen) + { + // Last byte is a \0 byte. Skip it. + stream.write(reinterpret_cast(buffer), inflateOutSize - 1); + } + else + { + stream.write(reinterpret_cast(buffer), inflateOutSize); + } + writtenSize += inflateOutSize; + } + + inflateEnd(&zs); +} + void AssetDumperRawFile::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) { const auto* rawFile = asset->Asset(); @@ -16,5 +91,15 @@ void AssetDumperRawFile::DumpAsset(AssetDumpingContext& context, XAssetInfobuffer, rawFile->len); + const fs::path rawFilePath(rawFile->name); + const auto extension = rawFilePath.extension().string(); + + if (extension == ".atr") + { + DumpAnimtree(context, asset, stream); + } + else + { + stream.write(rawFile->buffer, rawFile->len); + } } diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperRawFile.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperRawFile.h index 017d38b6..fcffaee6 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperRawFile.h +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperRawFile.h @@ -7,6 +7,9 @@ namespace T6 { class AssetDumperRawFile final : public AbstractAssetDumper { + constexpr static size_t ANIMTREE_MAX_SIZE = 0xC000000; + + void DumpAnimtree(AssetDumpingContext& context, XAssetInfo* asset, std::ostream& stream); protected: bool ShouldDump(XAssetInfo* asset) override; void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override;