add object graph parser

This commit is contained in:
Martin Michelsen
2022-11-03 00:40:04 -07:00
parent dedea228b1
commit 51ccecf1bd
4 changed files with 161 additions and 0 deletions
+1
View File
@@ -71,6 +71,7 @@ add_executable(newserv
src/ProxyCommands.cc
src/ProxyServer.cc
src/PSOEncryption.cc
src/PSOGCObjectGraph.cc
src/PSOProtocol.cc
src/Quest.cc
src/RareItemSet.cc
+15
View File
@@ -19,6 +19,7 @@
#include "Loggers.hh"
#include "NetworkAddresses.hh"
#include "ProxyServer.hh"
#include "PSOGCObjectGraph.hh"
#include "ReplaySession.hh"
#include "SendCommands.hh"
#include "Server.hh"
@@ -326,6 +327,7 @@ enum class Behavior {
DECODE_SJIS,
EXTRACT_GSL,
SHOW_EP3_DATA,
PARSE_OBJECT_GRAPH,
REPLAY_LOG,
CAT_CLIENT,
};
@@ -340,6 +342,7 @@ static bool behavior_takes_input_filename(Behavior b) {
(b == Behavior::DECODE_QUEST_FILE) ||
(b == Behavior::DECODE_SJIS) ||
(b == Behavior::EXTRACT_GSL) ||
(b == Behavior::PARSE_OBJECT_GRAPH) ||
(b == Behavior::REPLAY_LOG);
}
@@ -377,6 +380,7 @@ int main(int argc, char** argv) {
const char* output_filename = nullptr;
const char* replay_required_access_key = "";
const char* replay_required_password = "";
uint32_t root_object_address = 0;
struct sockaddr_storage cat_client_remote;
for (int x = 1; x < argc; x++) {
if (!strcmp(argv[x], "--help")) {
@@ -442,6 +446,8 @@ int main(int argc, char** argv) {
skip_big_endian = true;
} else if (!strcmp(argv[x], "--show-ep3-data")) {
behavior = Behavior::SHOW_EP3_DATA;
} else if (!strcmp(argv[x], "--parse-object-graph")) {
behavior = Behavior::PARSE_OBJECT_GRAPH;
} else if (!strcmp(argv[x], "--replay-log")) {
behavior = Behavior::REPLAY_LOG;
} else if (!strcmp(argv[x], "--extract-gsl")) {
@@ -450,6 +456,8 @@ int main(int argc, char** argv) {
replay_required_password = &argv[x][19];
} else if (!strncmp(argv[x], "--require-access-key=", 21)) {
replay_required_access_key = &argv[x][21];
} else if (!strncmp(argv[x], "--root-addr=", 12)) {
root_object_address = strtoul(&argv[x][12], nullptr, 16);
} else if (!strncmp(argv[x], "--config=", 9)) {
config_filename = &argv[x][9];
} else if (!strcmp(argv[x], "-") || argv[x][0] != '-') {
@@ -742,6 +750,13 @@ int main(int argc, char** argv) {
break;
}
case Behavior::PARSE_OBJECT_GRAPH: {
string data = read_input_data();
PSOGCObjectGraph g(data, root_object_address);
g.print(stdout);
break;
}
case Behavior::REPLAY_LOG:
case Behavior::RUN_SERVER: {
signal(SIGPIPE, SIG_IGN);
+101
View File
@@ -0,0 +1,101 @@
#include "PSOGCObjectGraph.hh"
#include "Text.hh"
using namespace std;
struct TObjectVTable {
be_uint32_t unused_a1;
be_uint32_t unused_a2;
be_uint32_t destroy;
be_uint32_t update;
be_uint32_t render;
be_uint32_t render_shadow;
} __attribute__((packed));
struct TObject {
be_uint32_t type_name_addr;
be_uint16_t flags;
parray<uint8_t, 2> unused;
be_uint32_t prev_sibling_addr;
be_uint32_t next_sibling_addr;
be_uint32_t parent_addr;
be_uint32_t children_head_addr;
be_uint32_t vtable_addr;
} __attribute__((packed));
PSOGCObjectGraph::PSOGCObjectGraph(
const string& memory_data, uint32_t root_address) {
StringReader r(memory_data);
this->root = this->parse_object_memo(r, root_address);
}
shared_ptr<PSOGCObjectGraph::VTable> PSOGCObjectGraph::parse_vtable_memo(
StringReader& r, uint32_t addr) {
try {
return this->all_vtables.at(addr);
} catch (const out_of_range&) { }
const auto& vt = r.pget<TObjectVTable>(addr & 0x01FFFFFF);
auto ret = this->all_vtables.emplace(addr, new VTable()).first->second;
ret->address = addr;
ret->destroy_addr = vt.destroy;
ret->update_addr = vt.update;
ret->render_addr = vt.render;
ret->render_shadow_addr = vt.render_shadow;
return ret;
}
shared_ptr<PSOGCObjectGraph::Object> PSOGCObjectGraph::parse_object_memo(
StringReader& r, uint32_t addr) {
try {
return this->all_objects.at(addr);
} catch (const out_of_range&) { }
const auto& obj = r.pget<TObject>(addr & 0x01FFFFFF);
string type_name = r.pget_cstr(obj.type_name_addr & 0x01FFFFFF);
auto ret = this->all_objects.emplace(addr, new Object()).first->second;
ret->address = addr;
ret->flags = obj.flags;
ret->type_name = move(type_name);
ret->vtable = this->parse_vtable_memo(r, obj.vtable_addr);
if (obj.parent_addr) {
ret->parent = this->parse_object_memo(r, obj.parent_addr);
}
if (obj.children_head_addr) {
uint32_t child_addr = obj.children_head_addr;
while (child_addr) {
ret->children.emplace_back(this->parse_object_memo(r, child_addr));
child_addr = r.pget<TObject>(child_addr & 0x01FFFFFF).next_sibling_addr;
}
}
return ret;
}
void PSOGCObjectGraph::print(FILE* stream) const {
this->root->print(stream);
}
void PSOGCObjectGraph::Object::print(FILE* stream, size_t indent_level) const {
for (size_t z = 0; z < indent_level; z++) {
fputc(' ', stream);
fputc(' ', stream);
}
fprintf(stream, "%s +%04hX @ %08" PRIX32 " (VT %08" PRIX32 ": destroy=%08" PRIX32 " update=%08" PRIX32 " render=%08" PRIX32 " render_shadow=%08" PRIX32 ")\n",
this->type_name.c_str(),
this->flags,
this->address,
this->vtable->address,
this->vtable->destroy_addr,
this->vtable->update_addr,
this->vtable->render_addr,
this->vtable->render_shadow_addr);
for (const auto& child : this->children) {
child->print(stream, indent_level + 1);
}
}
+44
View File
@@ -0,0 +1,44 @@
#pragma once
#include <inttypes.h>
#include <memory>
#include <phosg/Encoding.hh>
#include <phosg/Strings.hh>
#include <string>
#include <unordered_map>
#include <vector>
struct PSOGCObjectGraph {
PSOGCObjectGraph(const std::string& memory_data, uint32_t root_address);
void print(FILE* stream) const;
struct VTable {
uint32_t address;
uint32_t destroy_addr;
uint32_t update_addr;
uint32_t render_addr;
uint32_t render_shadow_addr;
};
struct Object {
uint32_t address;
uint16_t flags;
std::string type_name;
std::shared_ptr<VTable> vtable;
std::shared_ptr<Object> parent;
std::vector<std::shared_ptr<Object>> children;
void print(FILE* stream, size_t indent_level = 0) const;
};
std::shared_ptr<Object> root;
std::unordered_map<uint32_t, std::shared_ptr<Object>> all_objects;
std::unordered_map<uint32_t, std::shared_ptr<VTable>> all_vtables;
std::shared_ptr<Object> parse_object_memo(StringReader& r, uint32_t addr);
std::shared_ptr<VTable> parse_vtable_memo(StringReader& r, uint32_t addr);
};