decompiler: allow jak 3 texture and model extraction (#3080)

Added some hacks and stubs to allow extracting textures and models.
pull/3058/head
Hat Kid 2023-10-12 01:32:12 +02:00 committed by GitHub
parent 6285c61662
commit 5be46c9852
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 370 additions and 32 deletions

View File

@ -62,6 +62,21 @@ constexpr int LEVEL_MAX = 6;
constexpr int LEVEL_TOTAL = LEVEL_MAX + 1;
} // namespace jak2
// TODO copypaste from jak 2 for now
namespace jak3 {
// for now, we don't have the ability to extend the size of the symbol table
constexpr s32 GOAL_MAX_SYMBOLS = 0x4000;
constexpr s32 SYM_TABLE_MEM_SIZE = 0x30000;
// from the "off-by-one" symbol pointer
constexpr int SYM_TO_STRING_OFFSET = 0xff37;
constexpr int SYM_TO_HASH_OFFSET = 0x1fe6f;
// amount of levels in level heap
constexpr int LEVEL_MAX = 10;
// total amount of levels, including ones outside level heap (default-level)
constexpr int LEVEL_TOTAL = LEVEL_MAX + 1;
} // namespace jak3
constexpr s32 max_symbols(GameVersion version) {
switch (version) {
case GameVersion::Jak1:

View File

@ -42,8 +42,14 @@ std::vector<std::string> jak2_slots = {
"cas-toxic-slime-scroll-dest",
};
}
std::vector<std::string> jak3_slots = {};
} // namespace
const std::vector<std::string>& jak2_animated_texture_slots() {
return jak2_slots;
}
const std::vector<std::string>& jak3_animated_texture_slots() {
return jak3_slots;
}

View File

@ -4,3 +4,4 @@
#include <vector>
const std::vector<std::string>& jak2_animated_texture_slots();
const std::vector<std::string>& jak3_animated_texture_slots();

View File

@ -33,7 +33,9 @@ constexpr u32 TX_PAGE_VERSION = 8;
namespace jak3 {
constexpr u32 ART_FILE_VERSION = 8;
}
constexpr u32 LEVEL_FILE_VERSION = 36;
constexpr u32 TX_PAGE_VERSION = 8;
} // namespace jak3
} // namespace versions
@ -47,14 +49,15 @@ constexpr int IRX_VERSION_MINOR = 0;
enum class GameVersion { Jak1 = 1, Jak2 = 2, Jak3 = 3 };
// TODO: most usages of this are currently stubs for jak 3
template <typename T>
struct PerGameVersion {
constexpr PerGameVersion(T jak1, T jak2) : data{jak1, jak2} {}
constexpr PerGameVersion(T jak1, T jak2, T jak3) : data{jak1, jak2, jak3} {}
constexpr const T& operator[](GameVersion v) const { return data[(int)v - 1]; }
T data[2];
T data[3];
};
constexpr PerGameVersion<const char*> game_version_names = {"jak1", "jak2"};
constexpr PerGameVersion<const char*> game_version_names = {"jak1", "jak2", "jak3"};
GameVersion game_name_to_version(const std::string& name);
bool valid_game_version(const std::string& name);

View File

@ -732,6 +732,9 @@ std::string ObjectFileDB::process_tpages(TextureDB& tex_db,
case GameVersion::Jak2:
animated_slots = jak2_animated_texture_slots();
break;
case GameVersion::Jak3:
animated_slots = jak3_animated_texture_slots();
break;
default:
ASSERT_NOT_REACHED();
}

View File

@ -16,7 +16,7 @@
// set to true to generate plain .asm files with MIPS disassembly, with no fancy decompilation.
// this is fast and should succeed 100% of the time.
"disassemble_code": true,
"disassemble_code": false,
// Run the decompiler
"decompile_code": true,
@ -33,7 +33,7 @@
"disassemble_data": false,
// unpack textures to assets folder
"process_tpages": false,
"process_tpages": true,
// unpack game text to assets folder
"process_game_text": false,
// unpack game count to assets folder
@ -110,6 +110,8 @@
// these can be displayed in game, but makes the .fr3 files slightly larger
"extract_collision": false,
"save_texture_pngs": false,
////////////////////////////
// PATCHING OPTIONS
////////////////////////////

View File

@ -757,7 +757,7 @@
// there are some missing textures. I don't know what the game actually does here.
// the format for entries is [level, tpage, index]
"missing_textures": [["vinroom", 0, 0]],
"missing_textures": [["lfac", 0, 0], ["ltow", 0, 0], ["lcit", 0, 0], ["pow", 0, 0], ["wasintro", 0, 0], ["lfacctyb", 0, 0], ["intpfall", 0, 0]],
// some object files have garbage pad data at the end which makes the decompiler
// assume they must be different files, such as the art group for orb-cache-top.

View File

@ -312,5 +312,278 @@
"streamed_audio_file_names": [],
"levels_to_extract": []
"levels_to_extract": [
"LJKDMPK.DGO",
"LBBSDRP1.DGO",
"LTNJXHIP.DGO",
"MIC.DGO",
"OASISCST.DGO",
"CTYPEPA.DGO",
"LPRENME.DGO",
"LFREEOUT.DGO",
"LGUNNORM.DGO",
// "LTOWA.DGO",
"TEMA.DGO",
"CTA.DGO",
"LPRECC.DGO",
"LJKDXVIN.DGO",
"CTYPEPC.DGO",
"SEA.DGO",
"COMBE.DGO",
"CTYPESA.DGO",
"LBLOWCST.DGO",
"WSD.DGO",
"LBBRING3.DGO",
"LCTYPATK.DGO",
"WCB.DGO",
"DESRESC.DGO",
"LBBRING4.DGO",
"GRIDCST.DGO",
// "RAILX.DGO",
"SEJ.DGO",
"LJAKC.DGO",
"CTB.DGO",
"CTYCARC.DGO",
"LMECH.DGO",
"LBBSDRP2.DGO",
"NSA.DGO",
"LBBTCHA3.DGO",
"GUNGAME2.DGO",
"CTC.DGO",
"LVINCST.DGO",
// "COMBX.DGO",
"DESH.DGO",
"DESRACE2.DGO",
"RAILD.DGO",
"FACC.DGO",
"CTYPESC.DGO",
"LWASBBV.DGO",
"TOWB.DGO",
"HGA.DGO",
"SEH.DGO",
"MHCTYCST.DGO",
"GUNGAME1.DGO",
// "INTROCST.DGO",
"DESJUMP.DGO",
"SEM.DGO",
"SEI.DGO",
"DESG.DGO",
"DESW.DGO",
"LOUTRO3.DGO",
"LDAMKLEV.DGO",
"DESERROL.DGO",
"RAILB2.DGO",
"LERROL.DGO",
"IPF.DGO",
"RAILB.DGO",
"LCTYHIJK.DGO",
"CTYPEPB.DGO",
"PRECB.DGO",
"LFORM.DGO",
"WASLEAPR.DGO",
// "LKEIRA.DGO",
"LJAK.DGO",
"SLUMBSET.DGO",
"FACD.DGO",
// "LWASSIG.DGO",
"LBIPED.DGO",
"DESD.DGO",
"CFB.DGO",
"FREECAST.DGO",
"SEG.DGO",
"FACTORYA.DGO",
"LPATK.DGO",
"FRSTX.DGO",
"SEB.DGO",
// "DESBCST.DGO",
"DESE.DGO",
"DESOASIS.DGO",
"CTYCARA.DGO",
"LSIGKLV.DGO",
"CIB.DGO",
"LBBRING2.DGO",
"LTNFXHIP.DGO",
"MIA.DGO",
"MHCB.DGO",
"LNSTOBC.DGO",
"COMBD.DGO",
// "RBCT.DGO",
"LTORNJNX.DGO",
// "DESBATTL.DGO",
"SEK.DGO",
"LSNKWHLS.DGO",
"LMHCB.DGO",
// "LBOMBBOT.DGO",
"OUTCAST3.DGO",
"LBLOWTMH.DGO",
"TEMD.DGO",
"LTOWCITY.DGO",
// "OUTROCST.DGO",
"WASCAST.DGO",
"LFACRM2.DGO",
"WASPGAME.DGO",
"RAILE.DGO",
"CTYPESB.DGO",
"DESBOSS1.DGO",
"FREEHQ.DGO",
"LTORN.DGO",
"TOWERA.DGO",
"LSAMOS.DGO",
"LFORP.DGO",
"CFA.DGO",
"LJINX.DGO",
"SEO.DGO",
"PRECA.DGO",
"TOWERC.DGO",
"WCA.DGO",
"SEC.DGO",
"DESF.DGO",
"SEL.DGO",
"LCTYDEST.DGO",
"LTORNSAM.DGO",
"MUSEUM3B.DGO",
// "SEE.DGO",
"DESHUNT.DGO",
"RAILA.DGO",
"TITLE.DGO",
"RUBC.DGO",
// "DESB.DGO",
"LFACCAR.DGO",
"LNSTOA.DGO",
"MUSEUM3.DGO",
"ONINTENT.DGO",
"STA.DGO",
"WASSTADA.DGO",
"POWERGD.DGO",
"LKLEEVER.DGO",
"FACB.DGO",
"LCTYASS.DGO",
"MHCA.DGO",
"LTOWB.DGO",
"LNSTCST.DGO",
"DESRESCG.DGO",
"INTPALRF.DGO",
"LMHCA.DGO",
"TOWERCST.DGO",
"RAILF.DGO",
"CIA.DGO",
"CTYCARKG.DGO",
"WASCHASE.DGO",
"LFACO.DGO",
"WIN.DGO",
"TEMPLEE.DGO",
"LBBSPIRT.DGO",
"MUSEUM2.DGO",
"INTTITLE.DGO",
"STAA.DGO",
"MUSEUM4B.DGO",
// "PRECD.DGO",
"SEF.DGO",
"CTYCARB.DGO",
"WASDEFEN.DGO",
"LBLOWTKG.DGO",
"DESA.DGO",
"COMBB.DGO",
// "WASSTADC.DGO",
"DESC.DGO",
// "LDAMPECK.DGO",
// "LJAKSIG.DGO",
"HALFPIPE.DGO",
"DESRACE1.DGO",
"SEN.DGO",
"TEMP.DGO",
"SED.DGO",
"LFACB.DGO",
"LCTYSNPR.DGO",
"LBBSPID.DGO",
"FRSTA.DGO",
"LBBRING5.DGO",
"LBBSPRT3.DGO",
"HHG.DGO",
"LBBSPRT2.DGO",
"CGB.DGO",
"LDMPCKGN.DGO",
"LSEEMWCA.DGO",
// "HGB.DGO",
"LONINSIM.DGO",
"RUBA.DGO",
"DESRALLY.DGO",
"WWD.DGO",
"STB.DGO",
"MIB.DGO",
"LCTYBLOW.DGO",
"LWSTDPCK.DGO",
// "MUSEUM.DGO",
"LJAKCKLV.DGO",
"LBBRING1.DGO",
"MUSEUM4.DGO",
"LFACRM1.DGO",
"LJKCDMKL.DGO",
"LDAMSIG.DGO",
"DESTRACK.DGO",
"GGA.DGO",
"RAILC.DGO",
"LBBTCHA2.DGO",
"DESINTER.DGO",
// "NSB.DGO",
"LOUTRO.DGO",
"VIN.DGO",
"LDESGCST.DGO",
"WARPCAST.DGO",
"LBBRING6.DGO",
"FRSTB.DGO",
"TEMC.DGO",
// "COMBC.DGO",
"LTRTWHLS.DGO",
"PRECC.DGO",
"DESCHASE.DGO",
"CITYCAST.DGO",
// "CPO.DGO",
"LFACCITY.DGO",
"RAILCST.DGO",
"LJNDKLEV.DGO",
"CWI.DGO",
"MINEE.DGO",
"LFORRING.DGO",
"LASHELIN.DGO",
"LJAKKLEV.DGO",
"LCTYPALT.DGO",
"LNSTOBB.DGO",
"LJKFEET.DGO",
"DST.DGO",
"LBBTCHA1.DGO",
"LGUNRNC.DGO",
"COMBN.DGO",
"DESRESCC.DGO",
"LSIGJAKC.DGO",
"DESLIZ.DGO",
"WASPALA.DGO",
"LJAKNDAX.DGO",
// "WASSEEM.DGO",
"WASALL.DGO",
"WCASEEM.DGO",
// "LSIG.DGO",
"LFACTORY.DGO",
"LWLANDM.DGO",
"LPTRL.DGO",
"MINED.DGO",
"LDAMPKSM.DGO",
"RUBB.DGO",
"LCITYSML.DGO",
"RUBA2.DGO",
"LOUTRO2.DGO",
"VOCX.DGO",
// "TEMX.DGO",
"ARENACST.DGO",
"TEMB.DGO",
"COMBA.DGO",
"LBBSDRP3.DGO",
"LPATKCS.DGO",
"VOCA.DGO",
"WASSTADB.DGO",
"LDAX.DGO",
"LCTYPROT.DGO",
"DESHOVER.DGO",
"DESBOSS2.DGO"
]
}

View File

@ -45,7 +45,9 @@ DirTpageResult process_dir_tpages(ObjectFileData& data) {
word_idx++;
}
word_idx = ((word_idx + 3) / 4) * 4;
if (data.linked_data.version != GameVersion::Jak3) {
word_idx = ((word_idx + 3) / 4) * 4;
}
ASSERT(word_idx == (int)words.size());
return result;

View File

@ -352,6 +352,9 @@ TexturePage read_texture_page(ObjectFileData& data,
case GameVersion::Jak2:
ASSERT(tpage.info.major_version == versions::jak2::TX_PAGE_VERSION);
break;
case GameVersion::Jak3:
ASSERT(tpage.info.major_version == versions::jak3::TX_PAGE_VERSION);
break;
default:
ASSERT(false);
}

View File

@ -465,7 +465,8 @@ void TieFragment::read_from_file(TypedRef ref,
num_tris = read_plain_data_field<u16>(ref, "num-tris", dts);
num_dverts = read_plain_data_field<u16>(ref, "num-dverts", dts);
break;
case GameVersion::Jak2: {
case GameVersion::Jak2:
case GameVersion::Jak3: {
auto debug_data_ref = TypedRef(deref_label(get_field_ref(ref, "debug", dts)),
dts.ts.lookup_type("tie-fragment-debug"));
num_tris = read_plain_data_field<u16>(debug_data_ref, "num-tris", dts);
@ -950,6 +951,7 @@ void PrototypeBucketTie::read_from_file(TypedRef ref,
ASSERT(flags == 0 || flags == 2);
break;
case GameVersion::Jak2:
case GameVersion::Jak3:
flags = read_plain_data_field<u16>(ref, "flags", dts);
break;
default:
@ -1629,6 +1631,7 @@ void PrototypeBucketShrub::read_from_file(TypedRef ref,
ASSERT(flags == 0 || flags == 2);
break;
case GameVersion::Jak2:
case GameVersion::Jak3:
flags = read_plain_data_field<u16>(ref, "flags", dts);
break;
default:
@ -2066,6 +2069,7 @@ void BspHeader::read_from_file(const decompiler::LinkedObjectFile& file,
visible_list_length = read_plain_data_field<s32>(ref, "visible-list-length", dts);
break;
case GameVersion::Jak2:
case GameVersion::Jak3:
visible_list_length = read_plain_data_field<s16>(ref, "visible-list-length", dts);
break;
default:

View File

@ -856,7 +856,8 @@ ConvertedMercEffect convert_merc_effect(const MercEffect& input_effect,
u32 tidx = (env >> 8) & 0b1111'1111'1111;
tex_combo = (((u32)tpage) << 16) | tidx;
} break;
case GameVersion::Jak2: {
case GameVersion::Jak2:
case GameVersion::Jak3: {
u32 tpage = 0x1f;
u32 tidx = 2;
tex_combo = (((u32)tpage) << 16) | tidx;
@ -891,7 +892,8 @@ ConvertedMercEffect convert_merc_effect(const MercEffect& input_effect,
bool use_alpha_blend = false;
bool depth_write = true;
if (version == GameVersion::Jak2) {
// TODO check jak 3
if (version >= GameVersion::Jak2) {
constexpr int kWaterTexture = 4;
constexpr int kAlphaTexture = 3;
if (input_effect.texture_index == kAlphaTexture) {

View File

@ -1456,7 +1456,7 @@ struct NrmDebug {
int ip2 = 0;
};
void emulate_tie_instance_program(std::vector<TieProtoInfo>& protos) {
void emulate_tie_instance_program(std::vector<TieProtoInfo>& protos, GameVersion version) {
for (auto& proto : protos) {
// bool first_instance = true;
// for (auto& instance : proto.instances) {
@ -1622,7 +1622,10 @@ void emulate_tie_instance_program(std::vector<TieProtoInfo>& protos) {
vertex_info.nrm = frag.get_normal_if_present(normal_table_offset++);
bool inserted = frag.vertex_by_dest_addr.insert({(u32)dest_ptr, vertex_info}).second;
ASSERT(inserted);
// TODO hack
if (version != GameVersion::Jak3) {
ASSERT(inserted);
}
nd.bp1++;
if (reached_target) {
@ -2542,6 +2545,7 @@ void add_vertices_and_static_draw(tfrag3::TieTree& tree,
info = get_jak1_tie_category(proto.proto_flag);
break;
case GameVersion::Jak2:
case GameVersion::Jak3:
info = get_jak2_tie_category(proto.proto_flag);
break;
default:
@ -2763,12 +2767,12 @@ void extract_tie(const level_tools::DrawableTreeInstanceTie* tree,
auto info =
collect_instance_info(as_instance_array, &tree->prototypes.prototype_array_tie.data, geo);
update_proto_info(&info, tex_map, tree->prototypes.prototype_array_tie.data, geo, version);
if (version != GameVersion::Jak2) {
if (version < GameVersion::Jak2) {
check_wind_vectors_zero(info, tree->prototypes.wind_vectors);
}
// determine draws from VU program
emulate_tie_prototype_program(info);
emulate_tie_instance_program(info);
emulate_tie_instance_program(info, version);
emulate_kicks(info);
// debug save to .obj

View File

@ -12,8 +12,8 @@ namespace decompiler {
*/
bool allowable_base_type_for_symbol_to_string(const TypeSpec& ts);
constexpr PerGameVersion<int> SYMBOL_TO_STRING_MEM_OFFSET_DECOMP = {8167 * 8,
jak2::SYM_TO_STRING_OFFSET};
constexpr PerGameVersion<int> SYMBOL_TO_STRING_MEM_OFFSET_DECOMP = {
8167 * 8, jak2::SYM_TO_STRING_OFFSET, jak3::SYM_TO_STRING_OFFSET};
constexpr PerGameVersion<int> OFFSET_OF_NEXT_STATE_STORE = {72, 64};
constexpr PerGameVersion<int> OFFSET_OF_NEXT_STATE_STORE = {72, 64, 68};
} // namespace decompiler

View File

@ -8,7 +8,8 @@
#include "common/common_types.h"
#include "common/versions/versions.h"
constexpr PerGameVersion<int> DGO_RPC_ID(0xdeb4, 0xfab3);
// TODO: jak 3 stub
constexpr PerGameVersion<int> DGO_RPC_ID(0xdeb4, 0xfab3, 0x0);
constexpr int DGO_RPC_CHANNEL = 3;
constexpr int DGO_RPC_LOAD_FNO = 0;
constexpr int DGO_RPC_LOAD_NEXT_FNO = 1;

View File

@ -7,5 +7,6 @@
#include "common/versions/versions.h"
constexpr PerGameVersion<int> LOADER_RPC_ID(0xdeb2, 0xfab1);
// TODO: jak 3 stub
constexpr PerGameVersion<int> LOADER_RPC_ID(0xdeb2, 0xfab1, 0x0);
constexpr int LOADER_RPC_CHANNEL = 1;

View File

@ -7,5 +7,6 @@
*/
#include "common/versions/versions.h"
constexpr PerGameVersion<int> PLAY_RPC_ID(0xdeb6, 0xfab5);
// TODO: jak 3 stub
constexpr PerGameVersion<int> PLAY_RPC_ID(0xdeb6, 0xfab5, 0x0);
constexpr int PLAY_RPC_CHANNEL = 5;

View File

@ -7,5 +7,6 @@
*/
#include "common/versions/versions.h"
constexpr PerGameVersion<int> PLAYER_RPC_ID(0xdeb1, 0xfab0);
// TODO: jak 3 stub
constexpr PerGameVersion<int> PLAYER_RPC_ID(0xdeb1, 0xfab0, 0x0);
constexpr int PLAYER_RPC_CHANNEL = 0;

View File

@ -8,7 +8,8 @@
#include "common/common_types.h"
#include "common/versions/versions.h"
constexpr PerGameVersion<int> RAMDISK_RPC_ID(0xdeb3, 0xfab2);
// TODO: jak 3 stub
constexpr PerGameVersion<int> RAMDISK_RPC_ID(0xdeb3, 0xfab2, 0x0);
constexpr int RAMDISK_RPC_CHANNEL = 2;
constexpr int RAMDISK_GET_DATA_FNO = 0;
constexpr int RAMDISK_RESET_AND_LOAD_FNO = 1;

View File

@ -5,7 +5,8 @@
#include "game/common/overlord_common.h"
constexpr PerGameVersion<int> STR_RPC_ID(0xdeb5, 0xfab4);
// TODO: jak 3 stub
constexpr PerGameVersion<int> STR_RPC_ID(0xdeb5, 0xfab4, 0x0);
constexpr int STR_RPC_CHANNEL = 4;
/*

View File

@ -11,7 +11,7 @@
DirectRenderer::ScissorState DirectRenderer::m_scissor;
constexpr PerGameVersion<int> game_height(448, 416);
constexpr PerGameVersion<int> game_height(448, 416, 416);
DirectRenderer::DirectRenderer(const std::string& name, int my_id, int batch_size)
: BucketRenderer(name, my_id), m_prim_buffer(batch_size) {
@ -1048,7 +1048,7 @@ void DirectRenderer::handle_xyz2_packed(const u8* data,
handle_xyzf2_common(x << 16, y << 16, z, 0, render_state, prof, !adc);
}
PerGameVersion<u32> normal_zbp = {448, 304};
PerGameVersion<u32> normal_zbp = {448, 304, 304};
void DirectRenderer::handle_zbuf1(u64 val,
SharedRenderState* render_state,
ScopedProfilerNode& prof) {

View File

@ -43,7 +43,9 @@
constexpr bool run_dma_copy = false;
constexpr PerGameVersion<int> fr3_level_count(jak1::LEVEL_TOTAL, jak2::LEVEL_TOTAL);
constexpr PerGameVersion<int> fr3_level_count(jak1::LEVEL_TOTAL,
jak2::LEVEL_TOTAL,
jak3::LEVEL_TOTAL);
struct GraphicsData {
// vsync

View File

@ -11,8 +11,9 @@
void kmemcard_init_globals();
constexpr PerGameVersion<s32> SAVE_SIZE(692, 1204); // 691 for jak 1 v1
constexpr PerGameVersion<s32> BANK_SIZE(0x10000, 0x20000);
// TODO: jak 3 stubs
constexpr PerGameVersion<s32> SAVE_SIZE(692, 1204, 0); // 691 for jak 1 v1
constexpr PerGameVersion<s32> BANK_SIZE(0x10000, 0x20000, 0x0);
// each card can be in one of these states:
enum class MemoryCardState : u32 {

View File

@ -423,7 +423,9 @@ PerGameVersion<std::unordered_map<std::string, std::vector<void (*)()>>> gMips2C
jak2::shadow_find_facing_double_tris::link, jak2::shadow_find_single_edges::link,
jak2::shadow_find_facing_single_tris::link, jak2::shadow_init_vars::link,
jak2::shadow_scissor_top::link, jak2::shadow_scissor_edges::link,
jak2::shadow_calc_dual_verts::link, jak2::shadow_xform_verts::link}}}};
jak2::shadow_calc_dual_verts::link, jak2::shadow_xform_verts::link}}},
/////////// JAK 3
{}};
void LinkedFunctionTable::reg(const std::string& name, u64 (*exec)(void*), u32 stack_size) {
const auto& it = m_executes.insert({name, {exec, Ptr<u8>()}});

3
out/jak3/fr3/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*
!.gitignore
!hash.md5

3
out/jak3/iso/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*
!.gitignore
!hash.md5

3
out/jak3/obj/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*
!.gitignore
!hash.md5