Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions src/assetmgr/asset_dispatch.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <typename ThisAsset, typename WadHeader1, typename WadHeader2, typename UnpackerFunc1, typename UnpackerFunc2>
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<u8>* 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<s32>(0, "wad header");

if (header_size == sizeof(WadHeader1)) {
WadHeader1 header = header_src_buffer.read<WadHeader1>(0, "wad header");
func_1(static_cast<ThisAsset&>(dest), header, src, config);
} else if (header_size == sizeof(WadHeader2)) {
WadHeader2 header = header_src_buffer.read<WadHeader2>(0, "wad header");
func_2(static_cast<ThisAsset&>(dest), header, src, config);
}
});
}

template <typename ThisAsset, typename UnpackerFunc>
AssetUnpackerFunc* wrap_iso_unpacker_func(UnpackerFunc func, AssetUnpackerFunc unpack)
{
Expand Down Expand Up @@ -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 <typename ThisAsset, typename WadHeader1, typename WadHeader2, typename PackerFunc1, typename PackerFunc2>
AssetPackerFunc* wrap_wad_packer_func_2(PackerFunc1 func_1, PackerFunc2 func_2)
{
return new AssetPackerFunc([func_1, func_2](OutputStream& dest, std::vector<u8>* 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<const ThisAsset&>(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<const ThisAsset&>(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 <typename ThisAsset, typename WadHeader, typename PackerFunc>
AssetPackerFunc* wrap_wad_hint_packer_func(PackerFunc func)
{
Expand Down
30 changes: 15 additions & 15 deletions src/iso/iso_packer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@

static void pack_ps2_logo(
OutputStream& iso, const BuildAsset& build, BuildConfig config, AssetPackerFunc pack);
static std::vector<GlobalWadInfo> enumerate_globals(const BuildAsset& build, Game game);
static std::vector<GlobalWadInfo> enumerate_globals(const BuildAsset& build, Game game, Region region);
static std::vector<LevelInfo> 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<IsoFileRecord*>& dest, IsoDirectory& root_dir);
static IsoFileRecord pack_system_cnf(OutputStream& iso, const BuildAsset& build, Game game);
Expand Down Expand Up @@ -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());

Expand Down Expand Up @@ -210,7 +210,7 @@ static void pack_ps2_logo(
iso.write_v(texture->data);
}

static std::vector<GlobalWadInfo> enumerate_globals(const BuildAsset& build, Game game)
static std::vector<GlobalWadInfo> enumerate_globals(const BuildAsset& build, Game game, Region region)
{
std::vector<WadType> order;
switch (game) {
Expand Down Expand Up @@ -280,20 +280,20 @@ static std::vector<GlobalWadInfo> 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));
}
return globals;
}

static std::vector<LevelInfo> enumerate_levels(
const BuildAsset& build, Game game, const LevelAsset* single_level)
const BuildAsset& build, Game game, Region region, const LevelAsset* single_level)
{
std::vector<LevelInfo> 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<LevelAsset>([&](const LevelAsset& level) {
Expand All @@ -311,7 +311,7 @@ static std::vector<LevelInfo> 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.");
Expand All @@ -320,24 +320,24 @@ static std::vector<LevelInfo> 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();
}

Expand Down Expand Up @@ -542,7 +542,7 @@ static std::array<IsoDirectory, 3> 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);
Expand Down
13 changes: 12 additions & 1 deletion src/iso/wad_identifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -67,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},
Expand Down Expand Up @@ -101,8 +103,17 @@ std::tuple<Game, WadType, const char*> 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;
Expand Down
2 changes: 1 addition & 1 deletion src/iso/wad_identifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@
#include <core/build_config.h>

std::tuple<Game, WadType, const char*> 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
38 changes: 30 additions & 8 deletions src/wrenchbuild/globals/armor_wad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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 <typename Header>
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 <typename Header>
static bool pack_dl_armor_wad(
OutputStream& dest, Header& header, const ArmorWadAsset& src, BuildConfig config);
static void unpack_armors(
CollectionAsset& dest,
InputStream& src,
Expand All @@ -80,11 +91,13 @@ static void pack_armors(
on_load(Armor, []() {
ArmorWadAsset::funcs.unpack_rac2 = wrap_wad_unpacker_func<ArmorWadAsset, GcArmorWadHeader>(unpack_gc_armor_wad);
ArmorWadAsset::funcs.unpack_rac3 = wrap_wad_unpacker_func<ArmorWadAsset, UyaArmorWadHeader>(unpack_uya_armor_wad);
ArmorWadAsset::funcs.unpack_dl = wrap_wad_unpacker_func<ArmorWadAsset, DlArmorWadHeader>(unpack_dl_armor_wad);
ArmorWadAsset::funcs.unpack_dl = wrap_wad_unpacker_func_2<ArmorWadAsset, DlArmorWadHeader, DlJapanArmorWadHeader>(
unpack_dl_armor_wad<DlArmorWadHeader>, unpack_dl_armor_wad<DlJapanArmorWadHeader>);

ArmorWadAsset::funcs.pack_rac2 = wrap_wad_packer_func<ArmorWadAsset, GcArmorWadHeader>(pack_gc_armor_wad);
ArmorWadAsset::funcs.pack_rac3 = wrap_wad_packer_func<ArmorWadAsset, UyaArmorWadHeader>(pack_uya_armor_wad);
ArmorWadAsset::funcs.pack_dl = wrap_wad_packer_func<ArmorWadAsset, DlArmorWadHeader>(pack_dl_armor_wad);
ArmorWadAsset::funcs.pack_dl = wrap_wad_packer_func_2<ArmorWadAsset, DlArmorWadHeader, DlJapanArmorWadHeader>(
pack_dl_armor_wad<DlArmorWadHeader>, pack_dl_armor_wad<DlJapanArmorWadHeader>);
})

static void unpack_gc_armor_wad(
Expand Down Expand Up @@ -119,22 +132,31 @@ static void pack_uya_armor_wad(
pack_assets_sa(dest, ARRAY_PAIR(header.clank_textures), src.get_clank_textures(), config, FMT_TEXTURE_PIF8);
}

template <typename Header>
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<CollectionAsset>(dest.bot_textures(SWITCH_FILES), src, ARRAY_PAIR(header.bot_textures), config, FMT_COLLECTION_PIF8, true);
unpack_assets<CollectionAsset>(dest.landstalker_textures(SWITCH_FILES), src, ARRAY_PAIR(header.landstalker_textures), config, FMT_COLLECTION_PIF8, true);
unpack_assets<CollectionAsset>(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 <typename Header>
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<Header, DlArmorWadHeader>) {
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,
Expand Down
33 changes: 29 additions & 4 deletions src/wrenchbuild/globals/bonus_wad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -107,7 +121,7 @@ template <typename Header>
static void unpack_uya_dl_bonus_wad(
BonusWadAsset& dest, const Header& header, InputStream& src, BuildConfig config);
template <typename Header>
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,
Expand All @@ -128,12 +142,14 @@ static void pack_demo_images(
on_load(Bonus, []() {
BonusWadAsset::funcs.unpack_rac1 = wrap_wad_unpacker_func<BonusWadAsset, RacBonusWadHeader>(unpack_rac_gc_bonus_wad<RacBonusWadHeader>);
BonusWadAsset::funcs.unpack_rac2 = wrap_wad_unpacker_func<BonusWadAsset, GcBonusWadHeader>(unpack_rac_gc_bonus_wad<GcBonusWadHeader>);
BonusWadAsset::funcs.unpack_rac3 = wrap_wad_unpacker_func<BonusWadAsset, UyaBonusWadHeader>(unpack_uya_dl_bonus_wad<UyaBonusWadHeader>);
BonusWadAsset::funcs.unpack_rac3 = wrap_wad_unpacker_func_2<BonusWadAsset, UyaBonusWadHeader, UyaJapanBonusWadHeader>(
unpack_uya_dl_bonus_wad<UyaBonusWadHeader>, unpack_uya_dl_bonus_wad<UyaJapanBonusWadHeader>);
BonusWadAsset::funcs.unpack_dl = wrap_wad_unpacker_func<BonusWadAsset, DlBonusWadHeader>(unpack_uya_dl_bonus_wad<DlBonusWadHeader>);

BonusWadAsset::funcs.pack_rac1 = wrap_wad_packer_func<BonusWadAsset, RacBonusWadHeader>(pack_rac_gc_bonus_wad<RacBonusWadHeader>);
BonusWadAsset::funcs.pack_rac2 = wrap_wad_packer_func<BonusWadAsset, GcBonusWadHeader>(pack_rac_gc_bonus_wad<GcBonusWadHeader>);
BonusWadAsset::funcs.pack_rac3 = wrap_wad_packer_func<BonusWadAsset, UyaBonusWadHeader>(pack_uya_dl_bonus_wad<UyaBonusWadHeader>);
BonusWadAsset::funcs.pack_rac3 = wrap_wad_packer_func_2<BonusWadAsset, UyaBonusWadHeader, UyaJapanBonusWadHeader>(
pack_uya_dl_bonus_wad<UyaBonusWadHeader>, pack_uya_dl_bonus_wad<UyaJapanBonusWadHeader>);
BonusWadAsset::funcs.pack_dl = wrap_wad_packer_func<BonusWadAsset, DlBonusWadHeader>(pack_uya_dl_bonus_wad<DlBonusWadHeader>);
})

Expand Down Expand Up @@ -263,9 +279,16 @@ static void unpack_uya_dl_bonus_wad(
}

template <typename Header>
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<Header, UyaBonusWadHeader>) {
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");
Expand All @@ -276,6 +299,8 @@ void pack_uya_dl_bonus_wad(
if constexpr(std::is_same_v<Header, DlBonusWadHeader>) {
header.dige = pack_asset_sa<SectorRange>(dest, src.get_dige(), config);
}

return true;
}

static void unpack_demo_images(
Expand Down
11 changes: 9 additions & 2 deletions src/wrenchbuild/globals/mpeg_wad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<CollectionAsset>("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<BinaryAsset>("subtitles");
unpack_asset(subtitles, src, header.mpegs[i].subtitles, config, FMT_NO_HINT);
} else {
CollectionAsset& subtitles = mpeg.child<CollectionAsset>("subtitles");
unpack_asset(subtitles, src, header.mpegs[i].subtitles, config, FMT_COLLECTION_SUBTITLES);
}
}
}
}
Expand Down
Loading
Loading