mirror of
https://github.com/open-goal/jak-project
synced 2026-07-08 06:30:44 -04:00
support loading streaming data in decompiler
This commit is contained in:
+144
-15
@@ -21,7 +21,8 @@
|
||||
#include <cstring>
|
||||
#endif
|
||||
|
||||
std::string file_util::get_project_path() {
|
||||
namespace file_util {
|
||||
std::string get_project_path() {
|
||||
#ifdef _WIN32
|
||||
char buffer[FILENAME_MAX];
|
||||
GetModuleFileNameA(NULL, buffer, FILENAME_MAX);
|
||||
@@ -34,7 +35,7 @@ std::string file_util::get_project_path() {
|
||||
char buffer[FILENAME_MAX + 1];
|
||||
auto len = readlink("/proc/self/exe", buffer,
|
||||
FILENAME_MAX); // /proc/self acts like a "virtual folder" containing
|
||||
// information about the current process
|
||||
// information about the current process
|
||||
buffer[len] = '\0';
|
||||
std::string::size_type pos =
|
||||
std::string(buffer).rfind("jak-project"); // Strip file path down to /jak-project/ directory
|
||||
@@ -43,7 +44,7 @@ std::string file_util::get_project_path() {
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string file_util::get_file_path(const std::vector<std::string>& input) {
|
||||
std::string get_file_path(const std::vector<std::string>& input) {
|
||||
std::string currentPath = file_util::get_project_path();
|
||||
char dirSeparator;
|
||||
|
||||
@@ -61,7 +62,7 @@ std::string file_util::get_file_path(const std::vector<std::string>& input) {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
bool file_util::create_dir_if_needed(const std::string& path) {
|
||||
bool create_dir_if_needed(const std::string& path) {
|
||||
if (!std::filesystem::is_directory(path)) {
|
||||
std::filesystem::create_directories(path);
|
||||
return true;
|
||||
@@ -69,7 +70,7 @@ bool file_util::create_dir_if_needed(const std::string& path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void file_util::write_binary_file(const std::string& name, void* data, size_t size) {
|
||||
void write_binary_file(const std::string& name, void* data, size_t size) {
|
||||
FILE* fp = fopen(name.c_str(), "wb");
|
||||
if (!fp) {
|
||||
throw std::runtime_error("couldn't open file " + name);
|
||||
@@ -82,7 +83,7 @@ void file_util::write_binary_file(const std::string& name, void* data, size_t si
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
void file_util::write_rgba_png(const std::string& name, void* data, int w, int h) {
|
||||
void write_rgba_png(const std::string& name, void* data, int w, int h) {
|
||||
FILE* fp = fopen(name.c_str(), "wb");
|
||||
if (!fp) {
|
||||
throw std::runtime_error("couldn't open file " + name);
|
||||
@@ -93,7 +94,7 @@ void file_util::write_rgba_png(const std::string& name, void* data, int w, int h
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
void file_util::write_text_file(const std::string& file_name, const std::string& text) {
|
||||
void write_text_file(const std::string& file_name, const std::string& text) {
|
||||
FILE* fp = fopen(file_name.c_str(), "w");
|
||||
if (!fp) {
|
||||
printf("Failed to fopen %s\n", file_name.c_str());
|
||||
@@ -103,7 +104,7 @@ void file_util::write_text_file(const std::string& file_name, const std::string&
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> file_util::read_binary_file(const std::string& filename) {
|
||||
std::vector<uint8_t> read_binary_file(const std::string& filename) {
|
||||
auto fp = fopen(filename.c_str(), "rb");
|
||||
if (!fp)
|
||||
throw std::runtime_error("File " + filename +
|
||||
@@ -123,7 +124,7 @@ std::vector<uint8_t> file_util::read_binary_file(const std::string& filename) {
|
||||
return data;
|
||||
}
|
||||
|
||||
std::string file_util::read_text_file(const std::string& path) {
|
||||
std::string read_text_file(const std::string& path) {
|
||||
std::ifstream file(path);
|
||||
if (!file.good()) {
|
||||
throw std::runtime_error("couldn't open " + path);
|
||||
@@ -133,15 +134,15 @@ std::string file_util::read_text_file(const std::string& path) {
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
bool file_util::is_printable_char(char c) {
|
||||
bool is_printable_char(char c) {
|
||||
return c >= ' ' && c <= '~';
|
||||
}
|
||||
|
||||
std::string file_util::combine_path(const std::string& parent, const std::string& child) {
|
||||
std::string combine_path(const std::string& parent, const std::string& child) {
|
||||
return parent + "/" + child;
|
||||
}
|
||||
|
||||
std::string file_util::base_name(const std::string& filename) {
|
||||
std::string base_name(const std::string& filename) {
|
||||
size_t pos = 0;
|
||||
assert(!filename.empty());
|
||||
for (size_t i = filename.size() - 1; i-- > 0;) {
|
||||
@@ -157,7 +158,7 @@ std::string file_util::base_name(const std::string& filename) {
|
||||
static bool sInitCrc = false;
|
||||
static uint32_t crc_table[0x100];
|
||||
|
||||
void file_util::init_crc() {
|
||||
void init_crc() {
|
||||
for (uint32_t i = 0; i < 0x100; i++) {
|
||||
uint32_t n = i << 24u;
|
||||
for (uint32_t j = 0; j < 8; j++)
|
||||
@@ -167,7 +168,7 @@ void file_util::init_crc() {
|
||||
sInitCrc = true;
|
||||
}
|
||||
|
||||
uint32_t file_util::crc32(const uint8_t* data, size_t size) {
|
||||
uint32_t crc32(const uint8_t* data, size_t size) {
|
||||
assert(sInitCrc);
|
||||
uint32_t crc = 0;
|
||||
for (size_t i = size; i != 0; i--, data++) {
|
||||
@@ -176,6 +177,134 @@ uint32_t file_util::crc32(const uint8_t* data, size_t size) {
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
uint32_t file_util::crc32(const std::vector<uint8_t>& data) {
|
||||
uint32_t crc32(const std::vector<uint8_t>& data) {
|
||||
return crc32(data.data(), data.size());
|
||||
}
|
||||
|
||||
void ISONameFromAnimationName(char* dst, const char* src) {
|
||||
// The Animation Name is a bunch of words separated by dashes
|
||||
|
||||
// copy first two chars of the first word exactly
|
||||
dst[0] = src[0];
|
||||
dst[1] = src[1];
|
||||
s32 i = 2; // 2 chars added to dst.
|
||||
|
||||
// skip ahead to the first dash (or \0 if there's no dashes)
|
||||
const char* src_ptr = src;
|
||||
while (*src_ptr && *src_ptr != '-') {
|
||||
src_ptr++;
|
||||
}
|
||||
|
||||
// the points to the next dash (or \0 if there's none).
|
||||
const char* next_ptr = src_ptr;
|
||||
if (*src_ptr) {
|
||||
// loop over words (next_ptr points to dash before word, i counts chars in dest)
|
||||
while (src_ptr = next_ptr + 1, i < 8) {
|
||||
// scan next_ptr forward to next dash
|
||||
next_ptr = src_ptr;
|
||||
while (*next_ptr && *next_ptr != '-') {
|
||||
next_ptr++;
|
||||
}
|
||||
|
||||
// there's no next word, so break (the current word will be handled there)
|
||||
if (!*next_ptr)
|
||||
break;
|
||||
|
||||
// add a char for the current word:
|
||||
char char_to_add;
|
||||
if (next_ptr[-1] < '0' || next_ptr[-1] > '9') {
|
||||
// word doesn't end in a number.
|
||||
|
||||
// some special case words map to special letters (likely to avoid animation name conflicts)
|
||||
if (next_ptr - src_ptr == 10 && !memcmp(src_ptr, "resolution", 10)) {
|
||||
char_to_add = 'z';
|
||||
} else if (next_ptr - src_ptr == 6 && !memcmp(src_ptr, "accept", 6)) {
|
||||
char_to_add = 'y';
|
||||
} else if (next_ptr - src_ptr == 6 && !memcmp(src_ptr, "reject", 6)) {
|
||||
char_to_add = 'n';
|
||||
} else {
|
||||
// not a special case, just take the first letter.
|
||||
char_to_add = *src_ptr;
|
||||
}
|
||||
} else {
|
||||
// the current word ends in a number, just use this number (I think usually the whole word
|
||||
// is just a number)
|
||||
char_to_add = next_ptr[-1];
|
||||
}
|
||||
|
||||
dst[i++] = char_to_add;
|
||||
}
|
||||
|
||||
// here we ran out of room in dest, or words in source.
|
||||
// if there's still room in dest and chars in source, just add them
|
||||
while (*src_ptr && (i < 8)) {
|
||||
dst[i] = *src_ptr;
|
||||
src_ptr++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// pad with spaces (for ISO Name)
|
||||
while (i < 8) {
|
||||
dst[i++] = ' ';
|
||||
}
|
||||
|
||||
// upper case
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (dst[i] > '`' && dst[i] < '{') {
|
||||
dst[i] -= 0x20;
|
||||
}
|
||||
}
|
||||
|
||||
// append file extension
|
||||
strcpy(dst + 8, "STR");
|
||||
}
|
||||
|
||||
void MakeISOName(char* dst, const char* src) {
|
||||
int i = 0;
|
||||
const char* src_ptr = src;
|
||||
char* dst_ptr = dst;
|
||||
|
||||
// copy name and upper case
|
||||
while ((i < 8) && (*src_ptr) && (*src_ptr != '.')) {
|
||||
char c = *src_ptr;
|
||||
src_ptr++;
|
||||
if (('`' < c) && (c < '{')) { // lower case
|
||||
c -= 0x20;
|
||||
}
|
||||
*dst_ptr = c;
|
||||
dst_ptr++;
|
||||
i++;
|
||||
}
|
||||
|
||||
// pad out name with spaces
|
||||
while (i < 8) {
|
||||
*dst_ptr = ' ';
|
||||
dst_ptr++;
|
||||
i++;
|
||||
}
|
||||
|
||||
// increment past period
|
||||
if (*src_ptr == '.')
|
||||
src_ptr++;
|
||||
|
||||
// same for extension
|
||||
while (i < 11 && (*src_ptr)) {
|
||||
char c = *src_ptr;
|
||||
src_ptr++;
|
||||
if (('`' < c) && (c < '{')) { // lower case
|
||||
c -= 0x20;
|
||||
}
|
||||
*dst_ptr = c;
|
||||
dst_ptr++;
|
||||
i++;
|
||||
}
|
||||
|
||||
while (i < 11) {
|
||||
*dst_ptr = ' ';
|
||||
dst_ptr++;
|
||||
i++;
|
||||
}
|
||||
*dst_ptr = 0;
|
||||
}
|
||||
} // namespace file_util
|
||||
|
||||
@@ -23,4 +23,6 @@ std::string base_name(const std::string& filename);
|
||||
void init_crc();
|
||||
uint32_t crc32(const uint8_t* data, size_t size);
|
||||
uint32_t crc32(const std::vector<uint8_t>& data);
|
||||
void MakeISOName(char* dst, const char* src);
|
||||
void ISONameFromAnimationName(char* dst, const char* src);
|
||||
} // namespace file_util
|
||||
|
||||
@@ -20,7 +20,8 @@ add_executable(decompiler
|
||||
IR/IR_TypeAnalysis.cpp
|
||||
Function/TypeInspector.cpp
|
||||
data/tpage.cpp
|
||||
data/game_text.cpp)
|
||||
data/game_text.cpp
|
||||
data/StrFileReader.cpp)
|
||||
|
||||
target_link_libraries(decompiler
|
||||
goos
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <map>
|
||||
#include "decompiler/data/tpage.h"
|
||||
#include "decompiler/data/game_text.h"
|
||||
#include "decompiler/data/StrFileReader.h"
|
||||
#include "LinkedObjectFileCreation.h"
|
||||
#include "decompiler/config.h"
|
||||
#include "third-party/minilzo/minilzo.h"
|
||||
@@ -106,7 +107,8 @@ ObjectFileData& ObjectFileDB::lookup_record(const ObjectFileRecord& rec) {
|
||||
*/
|
||||
ObjectFileDB::ObjectFileDB(const std::vector<std::string>& _dgos,
|
||||
const std::string& obj_file_name_map_file,
|
||||
const std::vector<std::string>& object_files) {
|
||||
const std::vector<std::string>& object_files,
|
||||
const std::vector<std::string>& str_files) {
|
||||
Timer timer;
|
||||
|
||||
spdlog::info("-Loading types...");
|
||||
@@ -122,17 +124,33 @@ ObjectFileDB::ObjectFileDB(const std::vector<std::string>& _dgos,
|
||||
"consistent naming when doing a partial decompilation.");
|
||||
}
|
||||
|
||||
spdlog::info("-Initializing ObjectFileDB...");
|
||||
spdlog::info("-Loading DGOs...");
|
||||
for (auto& dgo : _dgos) {
|
||||
get_objs_from_dgo(dgo);
|
||||
}
|
||||
|
||||
spdlog::info("-Loading plain object files...");
|
||||
for (auto& obj : object_files) {
|
||||
auto data = file_util::read_binary_file(obj);
|
||||
auto name = obj_filename_to_name(obj);
|
||||
add_obj_from_dgo(name, name, data.data(), data.size(), "NO-XGO");
|
||||
}
|
||||
|
||||
spdlog::info("-Loading streaming object files...");
|
||||
for (auto& obj : str_files) {
|
||||
StrFileReader reader(obj);
|
||||
// name from the file name
|
||||
std::string base_name = obj_filename_to_name(obj);
|
||||
// name from inside the file (this does a lot of sanity checking)
|
||||
auto obj_name = reader.get_full_name(base_name + ".STR");
|
||||
for (int i = 0; i < reader.chunk_count(); i++) {
|
||||
// append the chunk ID to the full name
|
||||
std::string name = obj_name + fmt::format("+{}", i);
|
||||
auto& data = reader.get_chunk(i);
|
||||
add_obj_from_dgo(name, name, data.data(), data.size(), "NO-XGO");
|
||||
}
|
||||
}
|
||||
|
||||
spdlog::info("ObjectFileDB Initialized:");
|
||||
spdlog::info("Total DGOs: {}", int(_dgos.size()));
|
||||
spdlog::info("Total data: {} bytes", stats.total_dgo_bytes);
|
||||
@@ -320,12 +338,12 @@ void ObjectFileDB::get_objs_from_dgo(const std::string& filename) {
|
||||
*/
|
||||
void ObjectFileDB::add_obj_from_dgo(const std::string& obj_name,
|
||||
const std::string& name_in_dgo,
|
||||
uint8_t* obj_data,
|
||||
const uint8_t* obj_data,
|
||||
uint32_t obj_size,
|
||||
const std::string& dgo_name) {
|
||||
stats.total_obj_files++;
|
||||
assert(obj_size > 128);
|
||||
uint16_t version = *(uint16_t*)(obj_data + 8);
|
||||
uint16_t version = *(const uint16_t*)(obj_data + 8);
|
||||
auto hash = file_util::crc32(obj_data, obj_size);
|
||||
|
||||
bool duplicated = false;
|
||||
@@ -442,8 +460,8 @@ std::string ObjectFileDB::generate_obj_listing() {
|
||||
dgos.pop_back();
|
||||
dgos.pop_back();
|
||||
dgos += "]";
|
||||
result += "[\"" + pad_string(x.to_unique_name() + "\", ", 40) + "\"" +
|
||||
pad_string(x.name_in_dgo + "\", ", 30) + std::to_string(x.obj_version) + ", " +
|
||||
result += "[\"" + pad_string(x.to_unique_name() + "\", ", 50) + "\"" +
|
||||
pad_string(x.name_in_dgo + "\", ", 50) + std::to_string(x.obj_version) + ", " +
|
||||
dgos + ", \"\"],\n";
|
||||
unique_count++;
|
||||
all_unique_names.insert(x.to_unique_name());
|
||||
|
||||
@@ -47,7 +47,8 @@ class ObjectFileDB {
|
||||
public:
|
||||
ObjectFileDB(const std::vector<std::string>& _dgos,
|
||||
const std::string& obj_file_name_map_file,
|
||||
const std::vector<std::string>& object_files);
|
||||
const std::vector<std::string>& object_files,
|
||||
const std::vector<std::string>& str_files);
|
||||
std::string generate_dgo_listing();
|
||||
std::string generate_obj_listing();
|
||||
void process_link_data();
|
||||
@@ -70,7 +71,7 @@ class ObjectFileDB {
|
||||
void get_objs_from_dgo(const std::string& filename);
|
||||
void add_obj_from_dgo(const std::string& obj_name,
|
||||
const std::string& name_in_dgo,
|
||||
uint8_t* obj_data,
|
||||
const uint8_t* obj_data,
|
||||
uint32_t obj_size,
|
||||
const std::string& dgo_name);
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ void set_config(const std::string& path_to_config_file) {
|
||||
gConfig.game_version = cfg.at("game_version").get<int>();
|
||||
gConfig.dgo_names = cfg.at("dgo_names").get<std::vector<std::string>>();
|
||||
gConfig.object_file_names = cfg.at("object_file_names").get<std::vector<std::string>>();
|
||||
gConfig.str_file_names = cfg.at("str_file_names").get<std::vector<std::string>>();
|
||||
if (cfg.contains("obj_file_name_map_file")) {
|
||||
gConfig.obj_file_name_map_file = cfg.at("obj_file_name_map_file").get<std::string>();
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ struct Config {
|
||||
int game_version = -1;
|
||||
std::vector<std::string> dgo_names;
|
||||
std::vector<std::string> object_file_names;
|
||||
std::vector<std::string> str_file_names;
|
||||
std::unordered_set<std::string> bad_inspect_types;
|
||||
std::string obj_file_name_map_file;
|
||||
bool write_disassembly = false;
|
||||
|
||||
@@ -15,6 +15,41 @@
|
||||
"object_file_names":["TEXT/0COMMON.TXT", "TEXT/1COMMON.TXT", "TEXT/2COMMON.TXT", "TEXT/3COMMON.TXT", "TEXT/4COMMON.TXT",
|
||||
"TEXT/5COMMON.TXT", "TEXT/6COMMON.TXT"],
|
||||
|
||||
"str_file_names":["STR/BAFCELL.STR", "STR/SWTE4.STR", "STR/SWTE3.STR", "STR/SWTE2.STR", "STR/SWTE1.STR",
|
||||
"STR/SNRBSBFC.STR", "STR/SNRBIPFC.STR", "STR/SNRBICFC.STR", "STR/ORR3.STR", "STR/ORR2.STR", "STR/MICANNON.STR",
|
||||
"STR/BECANNON.STR", "STR/SWTS4.STR", "STR/SWTS3.STR", "STR/SWTS2.STR", "STR/SW4.STR", "STR/SW3.STR", "STR/SW2.STR",
|
||||
"STR/SWTS1.STR", "STR/ORREYE.STR", "STR/ORLEYE.STR", "STR/SW1.STR", "STR/MAGFCELL.STR", "STR/GNFCELL.STR",
|
||||
"STR/ORRE3.STR","STR/ORRE2.STR","STR/ORRE1.STR","STR/ORR1.STR","STR/ORLE3.STR","STR/ORLE2.STR","STR/ORI3.STR",
|
||||
"STR/ORI2.STR","STR/DE0202.STR","STR/RARSANIM.STR","STR/RARANIM.STR","STR/EIFISH.STR","STR/ORLE1.STR",
|
||||
"STR/SWTEF4.STR","STR/SWTEF3.STR","STR/SWTEF2.STR","STR/SWTEF1.STR","STR/ORI1.STR","STR/EIICE.STR","STR/EIA3.STR",
|
||||
"STR/DE0191.STR","STR/DE0186.STR","STR/DE0187.STR","STR/EIA4.STR","STR/EIPOLE.STR","STR/RARASECO.STR",
|
||||
"STR/RARA2.STR","STR/DE0184.STR","STR/DE0181.STR","STR/PESEXT.STR","STR/DE0195.STR","STR/EIA2.STR","STR/FIR1.STR",
|
||||
"STR/DE0182.STR","STR/BIR1.STR","STR/HAPOPEN.STR","STR/EITUBE.STR","STR/SCR1.STR","STR/DE0197.STR",
|
||||
"STR/DE0193.STR","STR/EIA1.STR","STR/FAR2.STR","STR/FAR1.STR","STR/DE0199.STR","STR/GERMONEY.STR",
|
||||
"STR/BIRESOLU.STR","STR/GARMONEY.STR","STR/BIADVENT.STR","STR/FUCRV1.STR","STR/BIREJECT.STR","STR/WAR1.STR",
|
||||
"STR/BIACCEPT.STR","STR/SA3R1DEC.STR","STR/ASR1GENE.STR","STR/FIREJECT.STR","STR/GARRACE.STR","STR/GEZMONEY.STR",
|
||||
"STR/LRFALLIN.STR","STR/EXR2.STR","STR/GERMOLES.STR","STR/FUCVICTO.STR","STR/MIR1ORBS.STR","STR/SA3R1RAM.STR",
|
||||
"STR/AS2R1FLU.STR","STR/FUCV2.STR","STR/MIR1GNAW.STR","STR/GAZMONEY.STR","STR/AS3REMIN.STR","STR/SIHISA.STR",
|
||||
"STR/FIACCEPT.STR","STR/FIWECO.STR","STR/FARESOLU.STR","STR/ASR1RBIK.STR","STR/MARDONAT.STR","STR/GAZRACE.STR",
|
||||
"STR/FUCFV1.STR","STR/FUCV5.STR","STR/SABR1CDU.STR","STR/FLLINTRO.STR","STR/SAR1ECOR.STR","STR/AS2R1ROB.STR",
|
||||
"STR/MIR2ORBS.STR","STR/MARBEAMS.STR","STR/LOI2.STR","STR/SAR1GENE.STR","STR/BILR1.STR","STR/AS2R1ROO.STR",
|
||||
"STR/ASR1BESW.STR","STR/LOLOOP.STR","STR/FAINTROD.STR","STR/GEZMOLES.STR","STR/V1IN.STR","STR/FUCV4.STR",
|
||||
"STR/SAIECORO.STR","STR/MIR1SWIT.STR","STR/LOINTRO.STR","STR/SAR2GENE.STR","STR/MUVICTOR.STR","STR/SAR1MCAN.STR",
|
||||
"STR/FUCV7.STR","STR/MIZ1ORBS.STR","STR/FUCV8.STR","STR/BILR2.STR","STR/FUCV6.STR","STR/FUCV3.STR",
|
||||
"STR/PLLBLOWU.STR","STR/PLBMAIN.STR","STR/WARESOLU.STR","STR/EIRACER.STR","STR/MAZDONAT.STR","STR/MAZBEAMS.STR",
|
||||
"STR/MIISWITC.STR","STR/FIBRTVIL.STR","STR/FIBRTMIS.STR","STR/SABR1PAR.STR","STR/NDINTRO.STR","STR/GORDOWN.STR",
|
||||
"STR/GORUP.STR","STR/SA3IRAMS.STR","STR/YERESOLU.STR","STR/EIFLUT.STR","STR/GRSDSACR.STR","STR/EXR1.STR",
|
||||
"STR/SCRESOLU.STR","STR/FIRESOLU.STR","STR/SIHITEST.STR","STR/GAI1.STR","STR/EXRESOLU.STR","STR/MIZ2ORBS.STR",
|
||||
"STR/ASIRBIKE.STR","STR/GRSOBBEC.STR","STR/BIINTROD.STR","STR/GRSOBBNC.STR","STR/AS2IROBB.STR","STR/GRSOBFIN.STR",
|
||||
"STR/RERESOLU.STR","STR/BLRESOLU.STR","STR/SABIPARM.STR","STR/EVMEND.STR","STR/AS2RESOL.STR","STR/SAIMCANN.STR",
|
||||
"STR/MIIGNAWE.STR","STR/GRSOBBA.STR","STR/GRSINTRO.STR","STR/SAISE.STR","STR/SA3IDECO.STR","STR/ASFRESOL.STR",
|
||||
"STR/EXINTROD.STR","STR/BILINTRO.STR","STR/FIINTROD.STR","STR/MAINTROD.STR","STR/SCINTROD.STR","STR/AS2IFLUT.STR",
|
||||
"STR/ASLERESO.STR","STR/ASLSRESO.STR","STR/AS2IROOM.STR","STR/GRSRESOL.STR","STR/SABICDUS.STR","STR/SIHISB.STR",
|
||||
"STR/ASIBESWI.STR","STR/BILBRESO.STR","STR/FIBRT1AL.STR","STR/AS2INTRO.STR","STR/GEINTROD.STR","STR/SAISD1.STR",
|
||||
"STR/SAISA.STR","STR/SIHISC.STR","STR/MIIORBS.STR","STR/WAINTROD.STR","STR/SAISD2.STR","STR/GRSOPREB.STR",
|
||||
"STR/GRSOBBB.STR","STR/SA3INTRO.STR"
|
||||
],
|
||||
|
||||
|
||||
"analyze_functions":true,
|
||||
"write_disassembly":true,
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
/*!
|
||||
* @file StrFileReader.cpp
|
||||
* Utility class to read a .STR file and extract the full file name.
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "game/overlord/isocommon.h"
|
||||
#include "StrFileReader.h"
|
||||
|
||||
// up to 64 chunks per STR file.
|
||||
constexpr int SECTOR_TABLE_SIZE = 64;
|
||||
|
||||
// there is a 1 sector long header
|
||||
struct StrFileHeader {
|
||||
u32 sectors[SECTOR_TABLE_SIZE]; // start of chunk, in sectors. including this sector.
|
||||
u32 sizes[SECTOR_TABLE_SIZE]; // size of chunk, in bytes. always an integer number of sectors.
|
||||
u32 pad[512 - 128]; // all zero
|
||||
};
|
||||
static_assert(sizeof(StrFileHeader) == SECTOR_SIZE, "Sector header size");
|
||||
|
||||
StrFileReader::StrFileReader(const std::string& file_path) {
|
||||
auto data = file_util::read_binary_file(file_path);
|
||||
assert(data.size() >= SECTOR_SIZE); // must have at least the header sector
|
||||
assert(data.size() % SECTOR_SIZE == 0); // should be multiple of the sector size.
|
||||
int end_sector = int(data.size()) / SECTOR_SIZE;
|
||||
|
||||
auto* header = (StrFileHeader*)data.data();
|
||||
|
||||
bool got_zero = false;
|
||||
for (int i = 0; i < SECTOR_TABLE_SIZE; i++) {
|
||||
// the chunk is from sector to next_sector
|
||||
int sector = header->sectors[i];
|
||||
// assume this chunk continues to the end...
|
||||
int next_sector = end_sector;
|
||||
// unless there's another chunk.
|
||||
if (i + 1 < SECTOR_TABLE_SIZE && header->sectors[i + 1]) {
|
||||
next_sector = header->sectors[i + 1];
|
||||
}
|
||||
if (sector) {
|
||||
assert(!got_zero); // shouldn't have a non-zero after a zero!
|
||||
assert(next_sector > sector); // should have a positive size.
|
||||
assert(next_sector * SECTOR_SIZE <= int(data.size())); // check for overflowing the file
|
||||
// get chunk data.
|
||||
std::vector<u8> chunk;
|
||||
chunk.insert(chunk.end(), data.begin() + sector * SECTOR_SIZE,
|
||||
data.begin() + next_sector * SECTOR_SIZE);
|
||||
m_chunks.emplace_back(std::move(chunk));
|
||||
} else {
|
||||
got_zero = true;
|
||||
}
|
||||
}
|
||||
|
||||
// check our sizes are accurate. Will make sure that we include all data, as our m_chunks
|
||||
// are sized assuming they are packed in order and dense (sectors);
|
||||
for (int i = 0; i < SECTOR_TABLE_SIZE; i++) {
|
||||
if (header->sectors[i]) {
|
||||
assert(header->sizes[i] == m_chunks.at(i).size());
|
||||
} else {
|
||||
assert(header->sizes[i] == 0);
|
||||
}
|
||||
}
|
||||
|
||||
// check nothing stored in the padding.
|
||||
for (auto x : header->pad) {
|
||||
assert(x == 0);
|
||||
}
|
||||
}
|
||||
|
||||
int StrFileReader::chunk_count() const {
|
||||
return m_chunks.size();
|
||||
}
|
||||
|
||||
const std::vector<u8>& StrFileReader::get_chunk(int idx) const {
|
||||
return m_chunks.at(idx);
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool find_string_in_data(const u8* data, int data_size, const std::string& str, int* result) {
|
||||
for (int i = 0; i < data_size - int(str.length()); i++) {
|
||||
if (std::memcmp(data + i, str.c_str(), str.length()) == 0) {
|
||||
*result = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string get_string_of_max_length(const char* data, int max_length) {
|
||||
std::string result;
|
||||
for (int i = 0; i < max_length; i++) {
|
||||
if (data[i]) {
|
||||
result.push_back(data[i]);
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
return "";
|
||||
}
|
||||
|
||||
struct FullName {
|
||||
std::string name;
|
||||
int chunk_idx = -1;
|
||||
};
|
||||
|
||||
FullName extract_name(const std::string& file_info_name) {
|
||||
FullName name;
|
||||
name.name = file_info_name;
|
||||
assert(name.name.length() > 10);
|
||||
assert(name.name.substr(name.name.length() - 6, 6) == "-ag.go");
|
||||
name.name = name.name.substr(0, name.name.length() - 6);
|
||||
int chunk_id = 0;
|
||||
int place = 0;
|
||||
for (int i = 2; i-- > 0;) {
|
||||
char c = name.name.back();
|
||||
if (c >= '0' && c <= '9') {
|
||||
int val = (c - '0');
|
||||
for (int j = 0; j < place; j++) {
|
||||
val *= 10;
|
||||
}
|
||||
chunk_id += val;
|
||||
name.name.pop_back();
|
||||
place++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(name.name.back() == '+');
|
||||
name.name.pop_back();
|
||||
name.chunk_idx = chunk_id;
|
||||
return name;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
/*!
|
||||
* Look inside the chunks to determine the source file name.
|
||||
* Does a lot of checking, might not work in future versions without some updating.
|
||||
*/
|
||||
std::string StrFileReader::get_full_name(const std::string& short_name) const {
|
||||
std::string result;
|
||||
bool done_first = false;
|
||||
|
||||
// this string is part of the file info struct and the stuff after it is the file name.
|
||||
const std::string file_info_string = "/src/next/data/art-group6/";
|
||||
|
||||
// it should occur in each chunk.
|
||||
int chunk_id = 0;
|
||||
for (const auto& chunk : m_chunks) {
|
||||
std::string chunk_long_name;
|
||||
|
||||
// find the file info string in the chunk.
|
||||
int offset;
|
||||
if (find_string_in_data(chunk.data(), int(chunk.size()), file_info_string, &offset)) {
|
||||
offset += file_info_string.length();
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// extract the name info as a "name" + "chunk id" + "-ag.go" format.
|
||||
auto full_name =
|
||||
extract_name(get_string_of_max_length((const char*)(chunk.data() + offset), 128));
|
||||
|
||||
// make sure it matches previous chunks for the name
|
||||
if (!done_first) {
|
||||
result = full_name.name;
|
||||
} else {
|
||||
assert(result == full_name.name);
|
||||
}
|
||||
|
||||
// make sure the index is right.
|
||||
assert(full_name.chunk_idx == chunk_id);
|
||||
|
||||
done_first = true;
|
||||
chunk_id++;
|
||||
}
|
||||
|
||||
// convert to ISO names in two ways.
|
||||
char iso_name_2[256];
|
||||
char iso_name_1[256];
|
||||
|
||||
// first, using the file name to ISO name
|
||||
file_util::MakeISOName(iso_name_1, short_name.c_str());
|
||||
// second, using the full name.
|
||||
file_util::ISONameFromAnimationName(iso_name_2, result.c_str());
|
||||
assert(strcmp(iso_name_1, iso_name_2) == 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
/*!
|
||||
* @file StrFileReader.h
|
||||
* Utility class to read a .STR file and extract the full file name.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
|
||||
class StrFileReader {
|
||||
public:
|
||||
explicit StrFileReader(const std::string& file_path);
|
||||
int chunk_count() const;
|
||||
const std::vector<u8>& get_chunk(int idx) const;
|
||||
std::string get_full_name(const std::string& short_name) const;
|
||||
|
||||
private:
|
||||
std::vector<std::vector<u8>> m_chunks;
|
||||
};
|
||||
+6
-2
@@ -27,7 +27,7 @@ int main(int argc, char** argv) {
|
||||
std::string in_folder = argv[2];
|
||||
std::string out_folder = argv[3];
|
||||
|
||||
std::vector<std::string> dgos, objs;
|
||||
std::vector<std::string> dgos, objs, strs;
|
||||
for (const auto& dgo_name : get_config().dgo_names) {
|
||||
dgos.push_back(file_util::combine_path(in_folder, dgo_name));
|
||||
}
|
||||
@@ -36,7 +36,11 @@ int main(int argc, char** argv) {
|
||||
objs.push_back(file_util::combine_path(in_folder, obj_name));
|
||||
}
|
||||
|
||||
ObjectFileDB db(dgos, get_config().obj_file_name_map_file, objs);
|
||||
for (const auto& str_name : get_config().str_file_names) {
|
||||
strs.push_back(file_util::combine_path(in_folder, str_name));
|
||||
}
|
||||
|
||||
ObjectFileDB db(dgos, get_config().obj_file_name_map_file, objs, strs);
|
||||
file_util::write_text_file(file_util::combine_path(out_folder, "dgo.txt"),
|
||||
db.generate_dgo_listing());
|
||||
file_util::write_text_file(file_util::combine_path(out_folder, "obj.txt"),
|
||||
|
||||
+1042
-2
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user