[jak2] Add static textures for the progress menu (#2838)

The progress menu loads its icon textures from a .STR file that we were
previously ignoring.

This change:
- updates the decompiler so it can process a .STR file containing a
texture
- adds a feature to force an entire page to always be loaded in the PC
renderer by putting all textures in the GAME.FR3 file.
- regenerates the texture offset map file for jak 2 with these new
textures

For now, I've just put the icon textures in GAME.FR3. The downside is
that these will always stay on the GPU, using up VRAM even when they
aren't needed. But the entire GAME.FR3 file is under 3 MB so I think
it's ok.


![image](https://github.com/open-goal/jak-project/assets/48171810/39f075b5-7cc5-4168-872a-33026342afab)
This commit is contained in:
water111
2023-07-23 12:35:59 -04:00
committed by GitHub
parent 673d2a13ae
commit 50230e05fa
12 changed files with 109 additions and 11 deletions
+14
View File
@@ -119,6 +119,7 @@ ObjectFileDB::ObjectFileDB(const std::vector<fs::path>& _dgos,
const fs::path& obj_file_name_map_file,
const std::vector<fs::path>& object_files,
const std::vector<fs::path>& str_files,
const std::vector<fs::path>& str_tex_files,
const Config& config)
: dts(config.game_version), m_version(config.game_version) {
Timer timer;
@@ -223,6 +224,19 @@ ObjectFileDB::ObjectFileDB(const std::vector<fs::path>& _dgos,
}
}
if (!str_tex_files.empty()) {
lg::info("-Loading {} streaming texture files...", str_tex_files.size());
for (auto& obj : str_tex_files) {
StrFileReader reader(obj, version());
// name from the file name
std::string base_name = obj_filename_to_name(obj.string());
ASSERT(reader.chunk_count() == 1);
auto name = reader.get_texture_name();
add_obj_from_dgo(name, name, reader.get_chunk(0).data(), reader.get_chunk(0).size(),
"TEXSPOOL", config, name);
}
}
lg::info("ObjectFileDB Initialized");
if (obj_files_by_name.empty()) {
lg::error(
+1
View File
@@ -163,6 +163,7 @@ class ObjectFileDB {
const fs::path& obj_file_name_map_file,
const std::vector<fs::path>& object_files,
const std::vector<fs::path>& str_files,
const std::vector<fs::path>& str_tex_files,
const Config& config);
std::string generate_dgo_listing();
std::string generate_obj_listing(const std::unordered_set<std::string>& merged_objs);
+10
View File
@@ -38,6 +38,12 @@ Config make_config_via_json(nlohmann::json& json) {
config.dgo_names = inputs_json.at("dgo_names").get<std::vector<std::string>>();
config.object_file_names = inputs_json.at("object_file_names").get<std::vector<std::string>>();
config.str_file_names = inputs_json.at("str_file_names").get<std::vector<std::string>>();
if (inputs_json.contains("str_texture_file_names")) {
config.str_texture_file_names =
inputs_json.at("str_texture_file_names").get<std::vector<std::string>>();
}
config.audio_dir_file_name = inputs_json.at("audio_dir_file_name").get<std::string>();
config.streamed_audio_file_names =
inputs_json.at("streamed_audio_file_names").get<std::vector<std::string>>();
@@ -272,6 +278,10 @@ Config make_config_via_json(nlohmann::json& json) {
inputs_json.at("animated_textures").get<std::unordered_set<std::string>>();
}
if (inputs_json.contains("common_tpages")) {
config.common_tpages = inputs_json.at("common_tpages").get<std::unordered_set<int>>();
}
auto art_info_json = read_json_file_from_config(json, "art_info_file");
config.art_groups_by_file =
art_info_json.at("files").get<std::unordered_map<std::string, std::string>>();
+2
View File
@@ -97,6 +97,7 @@ struct Config {
std::vector<std::string> dgo_names;
std::vector<std::string> object_file_names;
std::vector<std::string> str_file_names;
std::vector<std::string> str_texture_file_names;
std::string audio_dir_file_name;
std::vector<std::string> streamed_audio_file_names;
@@ -160,6 +161,7 @@ struct Config {
std::unordered_map<std::string, int> bad_format_strings;
std::unordered_set<std::string> animated_textures;
std::unordered_set<int> common_tpages;
std::vector<std::string> levels_to_extract;
bool levels_extract;
@@ -182,6 +182,12 @@
// "DGO/WASALL.DGO"
],
// STR files containing a texture that should be used by
// FR3 creation.
"str_texture_file_names" : [
"STR/PRMINIMA.STR"
],
// some objects are part of STR files (streaming data).
"str_file_names": [
"STR/AT1INT.STR",
@@ -443,6 +449,11 @@
"streamed_audio_file_names": [],
// tpages that should always be possible to access.
"common_tpages": [
3219 // PRMINIMA progress-minimap
],
// Textures that should be extracted in a special way for use in animated textures.
// these will be present in the common FR3, and stored as index textures.
"animated_textures": [
+22 -1
View File
@@ -196,7 +196,7 @@ std::string StrFileReader::get_full_name(const std::string& short_name) const {
bool done_first = false;
// this string is part of the file info struct and the stuff after it is the file name.
const auto& file_info_string = get_file_info_string();
const auto& file_info_string = get_art_group_file_info_string();
// it should occur in each chunk.
int chunk_id = 0;
@@ -241,4 +241,25 @@ std::string StrFileReader::get_full_name(const std::string& short_name) const {
return result;
}
std::string StrFileReader::get_texture_name() const {
ASSERT(m_chunks.size() == 1);
const auto& chunk = m_chunks[0];
auto find_string = get_texture_page_file_info_string();
int offset;
if (find_string_in_data(chunk.data(), int(chunk.size()), find_string, &offset)) {
offset += find_string.length();
} else {
ASSERT_MSG(false, fmt::format("did not find string '{}'", find_string));
}
for (int i = 0; i < 128; i++) {
if (chunk[offset + i] == '.') {
std::string result;
result.assign((const char*)&chunk[offset], i);
return result;
}
}
ASSERT_NOT_REACHED();
}
} // namespace decompiler
+11 -2
View File
@@ -20,20 +20,29 @@ class StrFileReader {
int chunk_count() const;
const std::vector<u8>& get_chunk(int idx) const;
std::string get_full_name(const std::string& short_name) const;
std::string get_texture_name() const;
private:
void init_jak1(const fs::path& file_path);
void init_jak2(const fs::path& file_path);
GameVersion m_version;
const std::string get_file_info_string() const {
std::string get_art_group_file_info_string() const {
switch (m_version) {
case GameVersion::Jak1:
return "/src/next/data/art-group6/";
break;
case GameVersion::Jak2:
return "/src/jak2/final/art-group7/";
default:
ASSERT_MSG(false, "NYI get_file_info_string version");
break;
}
}
std::string get_texture_page_file_info_string() const {
switch (m_version) {
case GameVersion::Jak2:
return "/src/jak2/final/texture-page8/";
default:
ASSERT_MSG(false, "NYI get_file_info_string version");
break;
+6 -2
View File
@@ -109,7 +109,7 @@ void decompile(const fs::path& iso_data_path, const std::string& data_subfolder)
fmt::format("{}_config.jsonc", version_info.game_name),
version_info.decomp_config_version);
std::vector<fs::path> dgos, objs;
std::vector<fs::path> dgos, objs, tex_strs;
// grab all DGOS we need (level + common)
// TODO - Jak 2 - jak 1 specific code?
@@ -133,8 +133,12 @@ void decompile(const fs::path& iso_data_path, const std::string& data_subfolder)
}
}
for (const auto& str_name : config.str_texture_file_names) {
tex_strs.push_back(iso_data_path / str_name);
}
// set up objects
ObjectFileDB db(dgos, fs::path(config.obj_file_name_map_file), objs, {}, config);
ObjectFileDB db(dgos, fs::path(config.obj_file_name_map_file), objs, {}, tex_strs, config);
// save object files
auto out_folder = file_util::get_jak_project_dir() / "decompiler_out" / data_subfolder;
+11 -1
View File
@@ -260,11 +260,21 @@ void extract_common(const ObjectFileDB& db,
textures_we_have.insert(t.debug_name);
}
for (const auto& [id, normal_texture] : tex_db.textures) {
if (config.common_tpages.count(normal_texture.page) &&
!textures_we_have.count(normal_texture.name)) {
lg::info("Adding common texture {}", normal_texture.name);
textures_we_have.insert(normal_texture.name);
tfrag_level.textures.push_back(
make_texture(id, normal_texture, tex_db.tpage_names.at(normal_texture.page), true));
}
}
// add animated textures that are missing.
for (const auto& [id, normal_texture] : tex_db.textures) {
if (config.animated_textures.count(normal_texture.name) &&
!textures_we_have.count(normal_texture.name)) {
lg::warn("adding anim normal texture {} ", normal_texture.name);
lg::info("Adding anim texture {}", normal_texture.name);
textures_we_have.insert(normal_texture.name);
tfrag_level.textures.push_back(
make_texture(id, normal_texture, tex_db.tpage_names.at(normal_texture.page), false));
+6 -2
View File
@@ -128,7 +128,7 @@ int main(int argc, char** argv) {
mem_log("After init: {} MB\n", get_peak_rss() / (1024 * 1024));
std::vector<fs::path> dgos, objs, strs;
std::vector<fs::path> dgos, objs, strs, tex_strs;
for (const auto& dgo_name : config.dgo_names) {
dgos.push_back(in_folder / dgo_name);
}
@@ -141,11 +141,15 @@ int main(int argc, char** argv) {
strs.push_back(in_folder / str_name);
}
for (const auto& str_name : config.str_texture_file_names) {
tex_strs.push_back(in_folder / str_name);
}
mem_log("After config read: {} MB", get_peak_rss() / (1024 * 1024));
// build file database
lg::info("Setting up object file DB...");
ObjectFileDB db(dgos, fs::path(config.obj_file_name_map_file), objs, strs, config);
ObjectFileDB db(dgos, fs::path(config.obj_file_name_map_file), objs, strs, tex_strs, config);
// Explicitly fail if a file in the 'allowed_objects' list wasn't found in the DB
// as this is another silent error that can be confusing
+12
View File
@@ -1355,6 +1355,18 @@ const std::unordered_map<int, std::vector<std::pair<int, int>>> data = {
{{64, 1}, {67, 2}, {31, 1}, {32, 2}, {27, 1}, {69, 1}, {61, 1}, {62, 2}, {65, 3}, {94, 1},
{90, 1}, {91, 2}, {93, 3}, {86, 1}, {88, 2}, {83, 1}, {84, 2}, {92, 3}, {97, 1}, {99, 2}}},
{3214, {{8, 1}, {9, 2}, {13, 1}, {6, 1}}},
{3219,
{{12, 1},
{16, 1},
{17, 2},
{19, 3},
{8, 2},
{15, 3},
{5, 1},
{6, 2},
{10, 1},
{13, 2},
{23, 3}}},
{3221,
{{26, 2}, {72, 3}, {79, 4}, {99, 5}, {49, 1}, {55, 1}, {62, 1}, {63, 2}, {91, 1},
{16, 1}, {15, 2}, {17, 3}, {18, 4}, {105, 4}, {56, 1}, {106, 2}, {19, 1}, {27, 2},
+3 -3
View File
@@ -48,9 +48,9 @@ OfflineTestDecompiler setup_decompiler(const OfflineTestWorkGroup& work,
dgo_paths.push_back(iso_data_path / x);
}
dc.db = std::make_unique<decompiler::ObjectFileDB>(dgo_paths, dc.config->obj_file_name_map_file,
std::vector<fs::path>{},
std::vector<fs::path>{}, *dc.config);
dc.db = std::make_unique<decompiler::ObjectFileDB>(
dgo_paths, dc.config->obj_file_name_map_file, std::vector<fs::path>{},
std::vector<fs::path>{}, std::vector<fs::path>{}, *dc.config);
dc.db->dts.art_group_info = dc.config->art_group_info_dump;
std::unordered_set<std::string> db_files;