From 6d138800bee207a8a02e31ce9829bb0a8df6458c Mon Sep 17 00:00:00 2001 From: chaoticgd <43898262+chaoticgd@users.noreply.github.com> Date: Tue, 30 Jun 2026 00:36:02 +0100 Subject: [PATCH 1/4] Add support for UYA japan bonus WAD --- src/assetmgr/asset_dispatch.h | 51 +++++++++++++++++++++++++++ src/iso/wad_identifier.cpp | 1 + src/wrenchbuild/globals/bonus_wad.cpp | 31 +++++++++++++--- 3 files changed, 79 insertions(+), 4 deletions(-) diff --git a/src/assetmgr/asset_dispatch.h b/src/assetmgr/asset_dispatch.h index d3795e62..cb4d8a99 100644 --- a/src/assetmgr/asset_dispatch.h +++ b/src/assetmgr/asset_dispatch.h @@ -89,6 +89,26 @@ AssetUnpackerFunc* wrap_wad_unpacker_func(UnpackerFunc func, bool error_fatal = }); } +// Try to unpack the WAD with one of two functions depending on the header. +template +AssetUnpackerFunc* wrap_wad_unpacker_func_2(UnpackerFunc1 func_1, UnpackerFunc2 func_2) +{ + return new AssetUnpackerFunc([func_1, func_2](Asset& dest, InputStream& src, const std::vector* header_src, BuildConfig config, const char* hint) { + verify(header_src, "No header passed to wad unpacker."); + + Buffer header_src_buffer(*header_src); + s32 header_size = header_src_buffer.read(0, "wad header"); + + if (header_size == sizeof(WadHeader1)) { + WadHeader1 header = header_src_buffer.read(0, "wad header"); + func_1(static_cast(dest), header, src, config); + } else if (header_size == sizeof(WadHeader2)) { + WadHeader2 header = header_src_buffer.read(0, "wad header"); + func_2(static_cast(dest), header, src, config); + } + }); +} + template AssetUnpackerFunc* wrap_iso_unpacker_func(UnpackerFunc func, AssetUnpackerFunc unpack) { @@ -142,6 +162,37 @@ AssetPackerFunc* wrap_wad_packer_func(PackerFunc func) }); } +// Try to pack the WAD with one function, and if it fails try another. +template +AssetPackerFunc* wrap_wad_packer_func_2(PackerFunc1 func_1, PackerFunc2 func_2) +{ + return new AssetPackerFunc([func_1, func_2](OutputStream& dest, std::vector* header_dest, fs::file_time_type* time_dest, const Asset& src, BuildConfig config, const char* hint) { + WadHeader1 header_1 = {}; + header_1.header_size = sizeof(WadHeader1); + dest.write(header_1); + dest.pad(SECTOR_SIZE, 0); + if (func_1(dest, header_1, static_cast(src), config)) { + dest.write(0, header_1); + if(header_dest) { + OutBuffer(*header_dest).write(0, header_1); + } + } else { + WadHeader2 header_2 = {}; + header_2.header_size = sizeof(WadHeader2); + dest.write(header_2); + dest.pad(SECTOR_SIZE, 0); + func_2(dest, header_2, static_cast(src), config); + dest.write(0, header_2); + if(header_dest) { + OutBuffer(*header_dest).write(0, header_2); + } + } + if(time_dest) { + *time_dest = fs::file_time_type::clock::now(); + } + }); +} + template AssetPackerFunc* wrap_wad_hint_packer_func(PackerFunc func) { diff --git a/src/iso/wad_identifier.cpp b/src/iso/wad_identifier.cpp index 818d82c8..b899cf6e 100644 --- a/src/iso/wad_identifier.cpp +++ b/src/iso/wad_identifier.cpp @@ -55,6 +55,7 @@ static WadFileDescription WAD_FILE_TYPES[] = { {"mpeg" , Game::UNKNOWN, WadType::MPEG , 0x0648}, {"misc" , Game::UYA , WadType::MISC , 0x0048}, {"bonus" , Game::UYA , WadType::BONUS , 0x0bf0}, + {"bonus" , Game::UYA , WadType::BONUS , 0x0c00}, // japan {"space" , Game::UYA , WadType::SPACE , 0x0c30}, {"armor" , Game::UYA , WadType::ARMOR , 0x0398}, {"audio" , Game::UYA , WadType::AUDIO , 0x2340}, diff --git a/src/wrenchbuild/globals/bonus_wad.cpp b/src/wrenchbuild/globals/bonus_wad.cpp index 55130175..66ab3e3b 100644 --- a/src/wrenchbuild/globals/bonus_wad.cpp +++ b/src/wrenchbuild/globals/bonus_wad.cpp @@ -80,6 +80,20 @@ packed_struct(UyaBonusWadHeader, /* 0xbe8 */ SectorRange trophy_image; ) +packed_struct(UyaJapanBonusWadHeader, + /* 0x000 */ s32 header_size; + /* 0x004 */ Sector32 sector; + /* 0x008 */ u64 unused_8[183]; + /* 0x5c0 */ SectorRange credits_text[8]; + /* 0x600 */ SectorRange credits_images[13]; + /* 0x668 */ u64 unused_658[115]; + /* 0xa00 */ SectorRange demo_menu[6]; + /* 0xa30 */ SectorRange demo_exit[6]; + /* 0xa60 */ SectorRange cheat_images[20]; + /* 0xb00 */ SectorRange skill_images[31]; + /* 0xbf8 */ SectorRange trophy_image; +) + packed_struct(DlBonusWadHeader, /* 0x000 */ s32 header_size; /* 0x004 */ Sector32 sector; @@ -107,7 +121,7 @@ template static void unpack_uya_dl_bonus_wad( BonusWadAsset& dest, const Header& header, InputStream& src, BuildConfig config); template -static void pack_uya_dl_bonus_wad( +static bool pack_uya_dl_bonus_wad( OutputStream& dest, Header& header, const BonusWadAsset& src, BuildConfig config); static void unpack_demo_images( CollectionAsset& dest, @@ -128,12 +142,12 @@ static void pack_demo_images( on_load(Bonus, []() { BonusWadAsset::funcs.unpack_rac1 = wrap_wad_unpacker_func(unpack_rac_gc_bonus_wad); BonusWadAsset::funcs.unpack_rac2 = wrap_wad_unpacker_func(unpack_rac_gc_bonus_wad); - BonusWadAsset::funcs.unpack_rac3 = wrap_wad_unpacker_func(unpack_uya_dl_bonus_wad); + BonusWadAsset::funcs.unpack_rac3 = wrap_wad_unpacker_func_2(unpack_uya_dl_bonus_wad, unpack_uya_dl_bonus_wad); BonusWadAsset::funcs.unpack_dl = wrap_wad_unpacker_func(unpack_uya_dl_bonus_wad); BonusWadAsset::funcs.pack_rac1 = wrap_wad_packer_func(pack_rac_gc_bonus_wad); BonusWadAsset::funcs.pack_rac2 = wrap_wad_packer_func(pack_rac_gc_bonus_wad); - BonusWadAsset::funcs.pack_rac3 = wrap_wad_packer_func(pack_uya_dl_bonus_wad); + BonusWadAsset::funcs.pack_rac3 = wrap_wad_packer_func_2(pack_uya_dl_bonus_wad, pack_uya_dl_bonus_wad); BonusWadAsset::funcs.pack_dl = wrap_wad_packer_func(pack_uya_dl_bonus_wad); }) @@ -263,9 +277,16 @@ static void unpack_uya_dl_bonus_wad( } template -void pack_uya_dl_bonus_wad( +bool pack_uya_dl_bonus_wad( OutputStream& dest, Header& header, const BonusWadAsset& src, BuildConfig config) { + // The Japanese version of UYA has a different header. First the packer will + // try to call the version of this function with the normal header, so bail + // out so it will try the version with the Japan header. + if (config.game() == Game::UYA && config.region() == Region::JAPAN && std::is_same_v) { + return false; + } + pack_assets_sa(dest, ARRAY_PAIR(header.credits_text), src.get_credits_text(), config); pack_assets_sa(dest, ARRAY_PAIR(header.credits_images), src.get_credits_images(), config, FMT_TEXTURE_RGBA); pack_demo_images(dest, ARRAY_PAIR(header.demo_menu), 30, src.get_demo_menu(), config, "demo_menu"); @@ -276,6 +297,8 @@ void pack_uya_dl_bonus_wad( if constexpr(std::is_same_v) { header.dige = pack_asset_sa(dest, src.get_dige(), config); } + + return true; } static void unpack_demo_images( From b522e453beb85438a4bb3792886eba0643009cab Mon Sep 17 00:00:00 2001 From: chaoticgd <43898262+chaoticgd@users.noreply.github.com> Date: Tue, 30 Jun 2026 00:52:01 +0100 Subject: [PATCH 2/4] Unpack Japanese subtitles as raw binary files --- src/wrenchbuild/globals/mpeg_wad.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/wrenchbuild/globals/mpeg_wad.cpp b/src/wrenchbuild/globals/mpeg_wad.cpp index 0c130d5e..d364f9bb 100644 --- a/src/wrenchbuild/globals/mpeg_wad.cpp +++ b/src/wrenchbuild/globals/mpeg_wad.cpp @@ -116,8 +116,15 @@ static void unpack_gc_uya_dl_mpeg_wad( } else { unpack_asset(mpeg.video_pal(), src, header.mpegs[i].video, config, FMT_BINARY_PSS); } - CollectionAsset& subtitles = mpeg.child("subtitles"); - unpack_asset(subtitles, src, header.mpegs[i].subtitles, config, FMT_COLLECTION_SUBTITLES); + if (config.region() == Region::JAPAN) { + // The Japanese subtitles are in a different format, so just + // unpack them as binaries. + BinaryAsset& subtitles = mpeg.child("subtitles"); + unpack_asset(subtitles, src, header.mpegs[i].subtitles, config, FMT_NO_HINT); + } else { + CollectionAsset& subtitles = mpeg.child("subtitles"); + unpack_asset(subtitles, src, header.mpegs[i].subtitles, config, FMT_COLLECTION_SUBTITLES); + } } } } From 96f83b9023dd923f23e4f6569ce62c3dc514045c Mon Sep 17 00:00:00 2001 From: chaoticgd <43898262+chaoticgd@users.noreply.github.com> Date: Tue, 30 Jun 2026 01:33:09 +0100 Subject: [PATCH 3/4] Fix some issues with unpacking missions --- src/wrenchbuild/level/level_wad.cpp | 54 +++++++++++++++++------------ 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/src/wrenchbuild/level/level_wad.cpp b/src/wrenchbuild/level/level_wad.cpp index a1900dd7..fb89e1d4 100644 --- a/src/wrenchbuild/level/level_wad.cpp +++ b/src/wrenchbuild/level/level_wad.cpp @@ -286,29 +286,39 @@ static void unpack_missions( if (!header.instances.empty() || !header.classes.empty() || !ranges.sound_banks[i].empty()) { std::string path = stringf("missions/%d/mission%d.asset", i, i); MissionAsset& mission = collection.foreign_child(path, false, i); - - // Horrible hack: There are some mission instance files that look - // more like a gameplay core with empty help message sections, for - // example level 4 (sarathos), mission 44. I'm not sure what these - // files are exactly. - file.seek(header.instances.bytes().offset); - std::vector compressed_instances = file.read_multiple(header.instances.bytes().size); - std::vector instances; - decompress_wad(instances, compressed_instances); - verify(instances.size() >= 4, "Bad mission instances file."); - if (*(s32*) instances.data() != 0x90) { - MemoryInputStream compressed_instances_stream(compressed_instances); - InstancesAsset& instances_asset = mission.instances(); - std::string hint = stringf("mission,%d", core_moby_count); - unpack_asset_impl(instances_asset, compressed_instances_stream, nullptr, config, hint.c_str()); - ReferenceAsset& reference = instances_asset.child("core"); - reference.set_asset(dest.get_gameplay().link_relative_to(dest)); - } else { - MemoryInputStream instances_stream(instances); - unpack_asset_impl(mission.instances(), instances_stream, nullptr, config, FMT_INSTANCES_MISSION); + + if (!header.instances.empty()) { + // Horrible hack: There are some mission instance files that look + // more like a gameplay core with empty help message sections, for + // example level 4 (sarathos), mission 44. These appear to be + // leftovers from development. + file.seek(header.instances.bytes().offset); + std::vector compressed_instances = file.read_multiple(header.instances.bytes().size); + std::vector instances; + verify(decompress_wad(instances, compressed_instances), "Corrupted mission instances file."); + verify(instances.size() >= 4, "Empty mission instances file."); + if (*(s32*) instances.data() != 0x90) { + MemoryInputStream compressed_instances_stream(compressed_instances); + InstancesAsset& instances_asset = mission.instances(); + std::string hint = stringf("mission,%d", core_moby_count); + unpack_asset_impl(instances_asset, compressed_instances_stream, nullptr, config, hint.c_str()); + ReferenceAsset& reference = instances_asset.child("core"); + reference.set_asset(dest.get_gameplay().link_relative_to(dest)); + } else { + MemoryInputStream instances_stream(instances); + unpack_asset_impl(mission.instances(), instances_stream, nullptr, config, FMT_INSTANCES_MISSION); + } + } + + // Level 26, mission 8 from the Japanese version of Deadlocked has + // an empty compressed file (LZ header only) here. + if (header.classes.size > 0x10) { + unpack_compressed_asset(mission.classes(), file, header.classes, config, FMT_COLLECTION_MISSION_CLASSES); + } + + if (!ranges.sound_banks[i].empty()) { + unpack_asset(mission.sound_bank(), file, ranges.sound_banks[i], config); } - unpack_compressed_asset(mission.classes(), file, header.classes, config, FMT_COLLECTION_MISSION_CLASSES); - unpack_asset(mission.sound_bank(), file, ranges.sound_banks[i], config); } } } From b5cbc8df7514d4c9ced2d2ea192ffdd6c29f26a8 Mon Sep 17 00:00:00 2001 From: chaoticgd <43898262+chaoticgd@users.noreply.github.com> Date: Tue, 30 Jun 2026 01:55:29 +0100 Subject: [PATCH 4/4] Add some special cases for unpacking the japanese versions --- src/iso/iso_packer.cpp | 30 ++++++++++----------- src/iso/wad_identifier.cpp | 12 ++++++++- src/iso/wad_identifier.h | 2 +- src/wrenchbuild/globals/armor_wad.cpp | 38 +++++++++++++++++++++------ src/wrenchbuild/globals/bonus_wad.cpp | 8 +++--- 5 files changed, 62 insertions(+), 28 deletions(-) diff --git a/src/iso/iso_packer.cpp b/src/iso/iso_packer.cpp index 68f7f707..fe2ac012 100644 --- a/src/iso/iso_packer.cpp +++ b/src/iso/iso_packer.cpp @@ -23,10 +23,10 @@ static void pack_ps2_logo( OutputStream& iso, const BuildAsset& build, BuildConfig config, AssetPackerFunc pack); -static std::vector enumerate_globals(const BuildAsset& build, Game game); +static std::vector enumerate_globals(const BuildAsset& build, Game game, Region region); static std::vector enumerate_levels( - const BuildAsset& build, Game game, const LevelAsset* single_level); -static LevelInfo enumerate_level(const LevelAsset& level, Game game); + const BuildAsset& build, Game game, Region region, const LevelAsset* single_level); +static LevelInfo enumerate_level(const LevelAsset& level, Game game, Region region); static IsoDirectory enumerate_files(const Asset& files); static void flatten_files(std::vector& dest, IsoDirectory& root_dir); static IsoFileRecord pack_system_cnf(OutputStream& iso, const BuildAsset& build, Game game); @@ -89,8 +89,8 @@ void pack_iso( pack_ps2_logo(iso, src, config, pack); table_of_contents toc; - toc.globals = enumerate_globals(src, config.game()); - toc.levels = enumerate_levels(src, config.game(), single_level); + toc.globals = enumerate_globals(src, config.game(), config.region()); + toc.levels = enumerate_levels(src, config.game(), config.region(), single_level); Sector32 toc_size = calculate_table_of_contents_size(toc, config.game()); @@ -210,7 +210,7 @@ static void pack_ps2_logo( iso.write_v(texture->data); } -static std::vector enumerate_globals(const BuildAsset& build, Game game) +static std::vector enumerate_globals(const BuildAsset& build, Game game, Region region) { std::vector order; switch (game) { @@ -280,7 +280,7 @@ static std::vector enumerate_globals(const BuildAsset& build, Gam case WadType::SPACE: global.asset = &build.get_space(); global.name = "space.wad"; break; default: verify_fatal(0); } - global.header.resize(header_size_of_wad(game, type)); + global.header.resize(header_size_of_wad(game, region, type)); verify(global.asset, "Failed to build ISO, missing global WAD asset."); globals.emplace_back(std::move(global)); } @@ -288,12 +288,12 @@ static std::vector enumerate_globals(const BuildAsset& build, Gam } static std::vector enumerate_levels( - const BuildAsset& build, Game game, const LevelAsset* single_level) + const BuildAsset& build, Game game, Region region, const LevelAsset* single_level) { std::vector levels; if (single_level) { - LevelInfo info = enumerate_level(*single_level, game); + LevelInfo info = enumerate_level(*single_level, game, region); // Construct a list of all the levels that exist so they can be filled in later. build.get_levels().for_each_logical_child_of_type([&](const LevelAsset& level) { @@ -311,7 +311,7 @@ static std::vector enumerate_levels( if (((s32) levels.size()) <= level_table_index) { levels.resize(level_table_index + 1); } - levels[level_table_index] = enumerate_level(level, game); + levels[level_table_index] = enumerate_level(level, game, region); }); verify(levels.size() >= 1, "No levels with a valid index."); @@ -320,24 +320,24 @@ static std::vector enumerate_levels( return levels; } -static LevelInfo enumerate_level(const LevelAsset& level, Game game) +static LevelInfo enumerate_level(const LevelAsset& level, Game game, Region region) { LevelInfo info; info.level_table_index = level.index(); if (level.has_level()) { LevelWadInfo& wad = info.level.emplace(); - wad.header.resize(header_size_of_wad(game, WadType::LEVEL)); + wad.header.resize(header_size_of_wad(game, region, WadType::LEVEL)); wad.asset = &level.get_level(); } if (level.has_audio()) { LevelWadInfo& wad = info.audio.emplace(); - wad.header.resize(header_size_of_wad(game, WadType::LEVEL_AUDIO)); + wad.header.resize(header_size_of_wad(game, region, WadType::LEVEL_AUDIO)); wad.asset = &level.get_audio(); } if (level.has_scene()) { LevelWadInfo& wad = info.scene.emplace(); - wad.header.resize(header_size_of_wad(game, WadType::LEVEL_SCENE)); + wad.header.resize(header_size_of_wad(game, region, WadType::LEVEL_SCENE)); wad.asset = &level.get_scene(); } @@ -542,7 +542,7 @@ static std::array pack_levels( IsoDirectory scenes_dir {"scenes"}; if (single_level) { // Only write out a single level, and point every level at it. - LevelInfo level = enumerate_level(*single_level, config.game()); + LevelInfo level = enumerate_level(*single_level, config.game(), config.region()); if (level.level) pack_level_wad_outer(iso, levels_dir, *level.level, "level", config, 0, pack); if (level.audio) pack_level_wad_outer(iso, audio_dir, *level.audio, "audio", config, 0, pack); if (level.scene) pack_level_wad_outer(iso, scenes_dir, *level.scene, "scene", config, 0, pack); diff --git a/src/iso/wad_identifier.cpp b/src/iso/wad_identifier.cpp index b899cf6e..007e0f38 100644 --- a/src/iso/wad_identifier.cpp +++ b/src/iso/wad_identifier.cpp @@ -68,6 +68,7 @@ static WadFileDescription WAD_FILE_TYPES[] = { {"online", Game::DL , WadType::ONLINE , 0x0068, 0xc, 0x75e, 0x1000, 0x14, 0x1}, // secondary: 0xc6a {"level" , Game::UNKNOWN, WadType::LEVEL , 0x0068}, {"armor" , Game::DL , WadType::ARMOR , 0x0228}, + {"armor" , Game::DL , WadType::ARMOR , 0x0248}, // japan {"audio" , Game::DL , WadType::AUDIO , 0xa870}, {"hud" , Game::DL , WadType::HUD , 0x0f88}, {"level" , Game::DL , WadType::LEVEL , 0x0c68}, @@ -102,8 +103,17 @@ std::tuple identify_wad(Buffer header) return {Game::UNKNOWN, WadType::UNKNOWN, "unknown"}; } -s32 header_size_of_wad(Game game, WadType type) +s32 header_size_of_wad(Game game, Region region, WadType type) { + // Some of the header sizes are different for the japanese versions. + if (region == Region::JAPAN) { + if (game == Game::UYA && type == WadType::BONUS) { + return 0x0c00; + } else if (game == Game::DL && type == WadType::ARMOR) { + return 0x0248; + } + } + for (WadFileDescription& desc : WAD_FILE_TYPES) { if (desc.game == game && desc.type == type) { return desc.header_size; diff --git a/src/iso/wad_identifier.h b/src/iso/wad_identifier.h index f2fde709..f12f5a63 100644 --- a/src/iso/wad_identifier.h +++ b/src/iso/wad_identifier.h @@ -26,6 +26,6 @@ #include std::tuple identify_wad(Buffer header); -s32 header_size_of_wad(Game game, WadType type); +s32 header_size_of_wad(Game game, Region region, WadType type); #endif diff --git a/src/wrenchbuild/globals/armor_wad.cpp b/src/wrenchbuild/globals/armor_wad.cpp index 8fe5b0e9..637b9087 100644 --- a/src/wrenchbuild/globals/armor_wad.cpp +++ b/src/wrenchbuild/globals/armor_wad.cpp @@ -50,6 +50,15 @@ packed_struct(DlArmorWadHeader, /* 0x1e8 */ SectorRange dropship_textures[8]; ) +packed_struct(DlJapanArmorWadHeader, + /* 0x000 */ s32 header_size; + /* 0x004 */ Sector32 sector; + /* 0x008 */ ArmorHeader armors[22]; + /* 0x148 */ SectorRange bot_textures[12]; + /* 0x1a8 */ SectorRange landstalker_textures[8]; + /* 0x1e8 */ SectorRange dropship_textures[8]; +) + static void unpack_gc_armor_wad( ArmorWadAsset& dest, const GcArmorWadHeader& header, InputStream& src, BuildConfig config); static void pack_gc_armor_wad( @@ -58,10 +67,12 @@ static void unpack_uya_armor_wad( ArmorWadAsset& dest, const UyaArmorWadHeader& header, InputStream& src, BuildConfig config); static void pack_uya_armor_wad( OutputStream& dest, UyaArmorWadHeader& header, const ArmorWadAsset& src, BuildConfig config); +template static void unpack_dl_armor_wad( - ArmorWadAsset& dest, const DlArmorWadHeader& header, InputStream& src, BuildConfig config); -static void pack_dl_armor_wad( - OutputStream& dest, DlArmorWadHeader& header, const ArmorWadAsset& src, BuildConfig config); + ArmorWadAsset& dest, const Header& header, InputStream& src, BuildConfig config); +template +static bool pack_dl_armor_wad( + OutputStream& dest, Header& header, const ArmorWadAsset& src, BuildConfig config); static void unpack_armors( CollectionAsset& dest, InputStream& src, @@ -80,11 +91,13 @@ static void pack_armors( on_load(Armor, []() { ArmorWadAsset::funcs.unpack_rac2 = wrap_wad_unpacker_func(unpack_gc_armor_wad); ArmorWadAsset::funcs.unpack_rac3 = wrap_wad_unpacker_func(unpack_uya_armor_wad); - ArmorWadAsset::funcs.unpack_dl = wrap_wad_unpacker_func(unpack_dl_armor_wad); + ArmorWadAsset::funcs.unpack_dl = wrap_wad_unpacker_func_2( + unpack_dl_armor_wad, unpack_dl_armor_wad); ArmorWadAsset::funcs.pack_rac2 = wrap_wad_packer_func(pack_gc_armor_wad); ArmorWadAsset::funcs.pack_rac3 = wrap_wad_packer_func(pack_uya_armor_wad); - ArmorWadAsset::funcs.pack_dl = wrap_wad_packer_func(pack_dl_armor_wad); + ArmorWadAsset::funcs.pack_dl = wrap_wad_packer_func_2( + pack_dl_armor_wad, pack_dl_armor_wad); }) static void unpack_gc_armor_wad( @@ -119,8 +132,9 @@ static void pack_uya_armor_wad( pack_assets_sa(dest, ARRAY_PAIR(header.clank_textures), src.get_clank_textures(), config, FMT_TEXTURE_PIF8); } +template static void unpack_dl_armor_wad( - ArmorWadAsset& dest, const DlArmorWadHeader& header, InputStream& src, BuildConfig config) + ArmorWadAsset& dest, const Header& header, InputStream& src, BuildConfig config) { unpack_armors(dest.armors(SWITCH_FILES), src, ARRAY_PAIR(header.armors), config, FMT_MOBY_CLASS_PHAT); unpack_assets(dest.bot_textures(SWITCH_FILES), src, ARRAY_PAIR(header.bot_textures), config, FMT_COLLECTION_PIF8, true); @@ -128,13 +142,21 @@ static void unpack_dl_armor_wad( unpack_assets(dest.dropship_textures(SWITCH_FILES), src, ARRAY_PAIR(header.dropship_textures), config, FMT_COLLECTION_PIF8, true); } -static void pack_dl_armor_wad( - OutputStream& dest, DlArmorWadHeader& header, const ArmorWadAsset& src, BuildConfig config) +template +static bool pack_dl_armor_wad( + OutputStream& dest, Header& header, const ArmorWadAsset& src, BuildConfig config) { + // The Japanese version of Deadlocked has a different header. + if (config.game() == Game::DL && config.region() == Region::JAPAN && std::is_same_v) { + return false; + } + pack_armors(dest, ARRAY_PAIR(header.armors), src.get_armors(), config, FMT_MOBY_CLASS_PHAT); pack_assets_sa(dest, ARRAY_PAIR(header.bot_textures), src.get_bot_textures(), config, FMT_COLLECTION_PIF8); pack_assets_sa(dest, ARRAY_PAIR(header.landstalker_textures), src.get_landstalker_textures(), config, FMT_COLLECTION_PIF8); pack_assets_sa(dest, ARRAY_PAIR(header.dropship_textures), src.get_dropship_textures(), config, FMT_COLLECTION_PIF8); + + return true; } packed_struct(ArmorMeshHeader, diff --git a/src/wrenchbuild/globals/bonus_wad.cpp b/src/wrenchbuild/globals/bonus_wad.cpp index 66ab3e3b..300ab920 100644 --- a/src/wrenchbuild/globals/bonus_wad.cpp +++ b/src/wrenchbuild/globals/bonus_wad.cpp @@ -142,12 +142,14 @@ static void pack_demo_images( on_load(Bonus, []() { BonusWadAsset::funcs.unpack_rac1 = wrap_wad_unpacker_func(unpack_rac_gc_bonus_wad); BonusWadAsset::funcs.unpack_rac2 = wrap_wad_unpacker_func(unpack_rac_gc_bonus_wad); - BonusWadAsset::funcs.unpack_rac3 = wrap_wad_unpacker_func_2(unpack_uya_dl_bonus_wad, unpack_uya_dl_bonus_wad); + BonusWadAsset::funcs.unpack_rac3 = wrap_wad_unpacker_func_2( + unpack_uya_dl_bonus_wad, unpack_uya_dl_bonus_wad); BonusWadAsset::funcs.unpack_dl = wrap_wad_unpacker_func(unpack_uya_dl_bonus_wad); BonusWadAsset::funcs.pack_rac1 = wrap_wad_packer_func(pack_rac_gc_bonus_wad); BonusWadAsset::funcs.pack_rac2 = wrap_wad_packer_func(pack_rac_gc_bonus_wad); - BonusWadAsset::funcs.pack_rac3 = wrap_wad_packer_func_2(pack_uya_dl_bonus_wad, pack_uya_dl_bonus_wad); + BonusWadAsset::funcs.pack_rac3 = wrap_wad_packer_func_2( + pack_uya_dl_bonus_wad, pack_uya_dl_bonus_wad); BonusWadAsset::funcs.pack_dl = wrap_wad_packer_func(pack_uya_dl_bonus_wad); }) @@ -283,7 +285,7 @@ bool pack_uya_dl_bonus_wad( // The Japanese version of UYA has a different header. First the packer will // try to call the version of this function with the normal header, so bail // out so it will try the version with the Japan header. - if (config.game() == Game::UYA && config.region() == Region::JAPAN && std::is_same_v) { + if (config.game() == Game::UYA && config.region() == Region::JAPAN && std::is_same_v) { return false; }