Skip to content

Commit

Permalink
fine t7 decompiler
Browse files Browse the repository at this point in the history
  • Loading branch information
ate47 committed May 22, 2024
1 parent e734bd6 commit f60dba4
Show file tree
Hide file tree
Showing 9 changed files with 308 additions and 69 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ My set of tools. The code is more important than the features, so feel free to r

| Name | Revision | Decompiler | Compiler | PS4 support |
| -------------------------------------- | -------- | --------------------------- | -------------- | -------------- |
| Call of Duty: Black Ops 3 (T7) | 1C ||||
| Call of Duty: Black Ops 4 (T8) | 36 ||| With extension |
| Call of Duty: Black Ops Cold War (T9) | 37 | With pre-decode & extension | With extension | With extension |
| Call of Duty: Black Ops Cold War (T9) | 38 | With pre-decode || With extension |
Expand Down Expand Up @@ -45,6 +46,7 @@ You can download the latest release here:
## Related repositories

- [ate47/t8-atian-menu](https://github.com/ate47/t8-atian-menu/tree/master/docs/notes) : All the notes and dump generated by this tool. (All games)
- [ate47/bo3-source](https://github.com/ate47/bo3-source) : Black Ops 3 Dump
- [ate47/bo4-source](https://github.com/ate47/bo4-source) : Black Ops 4 Dump
- [ate47/bocw-source](https://github.com/ate47/bocw-source) : Black Ops Cold War Dump
- [ate47/BOHashTool](https://github.com/ate47/BOHashTool) : Tool to test hashes with error (en/de)coder for Black Ops games
Expand Down
1 change: 1 addition & 0 deletions src/acts/hashutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ int hashutils::LoadMap(const char* file, bool ignoreCol, bool iw) {
g_hashMap[Hash32("self")] = "self";
g_hashMap[Hash32("size")] = "size";
g_hashMap[Hash32("nextarray")] = "nextarray";
g_hashMap[Hash32("_")] = "_";

// class special things
Add("__constructor", true, iw);
Expand Down
122 changes: 113 additions & 9 deletions src/acts/tools/gsc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ using namespace tool::gsc::opcode;

constexpr uint32_t g_constructorName = hashutils::Hash32("__constructor");
constexpr uint32_t g_destructorName = hashutils::Hash32("__destructor");
constexpr uint32_t g_constructorNameT7 = hashutils::HashT7("__constructor");
constexpr uint32_t g_destructorNameT7 = hashutils::HashT7("__destructor");

GscInfoOption::GscInfoOption() {
// set default formatter
Expand Down Expand Up @@ -510,6 +512,43 @@ void GSCOBJHandler::PatchCode(T8GSCOBJContext& ctx) {
}
str_location += sizeof(*str) + sizeof(*strings) * str->num_address;
}

if (GetAnimTreeDoubleOffset()) {
uintptr_t animt_location = reinterpret_cast<uintptr_t>(file) + GetAnimTreeDoubleOffset();
auto anims_count = (int)GetAnimTreeDoubleCount();
for (size_t i = 0; i < anims_count; i++) {
const auto* animt = reinterpret_cast<T7GscAnimTree*>(animt_location);

auto* s1 = Ptr<char>(animt->name);

hashutils::Add(s1, true, true);
uint32_t ref1 = ctx.AddStringValue(s1);

const uint32_t* vars = reinterpret_cast<const uint32_t*>(&animt[1]);
for (size_t j = 0; j < animt->num_tree_address; j++) {
uint32_t rloc = *(vars++);
uint32_t* loc = Ptr<uint32_t>(rloc);
// use strings to link them
ctx.m_animTreeLocations[rloc] = ref1;
*loc = 666;
}
const uint64_t* vars2 = reinterpret_cast<const uint64_t*>(vars);
for (size_t j = 0; j < animt->num_node_address; j++) {
if (vars2[0] < fileSize) {
const char* v = Ptr<const char>(vars2[0]);
uint32_t ref2 = ctx.AddStringValue(v);

uint32_t rloc = (uint32_t)vars2[1];
uint32_t* loc = Ptr<uint32_t>(rloc);
loc[0] = ref1;
loc[1] = ref2;

}
vars2 += 2;
}
animt_location += sizeof(*animt) + sizeof(*vars) * (animt->num_tree_address + (size_t)animt->num_node_address * 2);
}
}
}

void tool::gsc::GSCOBJHandler::DumpExperimental(std::ostream& asmout, const GscInfoOption& opt) {
Expand Down Expand Up @@ -991,6 +1030,8 @@ int GscInfoHandleData(byte* data, size_t size, const char* path, const GscInfoOp
}
}

std::vector<const char*> animtreeUsings{};

if (opt.m_imports) {
uintptr_t import_location = reinterpret_cast<uintptr_t>(scriptfile->Ptr(scriptfile->GetImportsOffset()));

Expand Down Expand Up @@ -1077,8 +1118,60 @@ int GscInfoHandleData(byte* data, size_t size, const char* path, const GscInfoOp
if (scriptfile->GetImportsCount()) {
asmout << "\n";
}

if (vm == VM_T7 && scriptfile->GetAnimTreeDoubleOffset()) {
uintptr_t animt_location = reinterpret_cast<uintptr_t>(scriptfile->file) + scriptfile->GetAnimTreeDoubleOffset();
auto anims_count = (int)scriptfile->GetAnimTreeDoubleCount();
for (size_t i = 0; i < anims_count; i++) {
const auto* animt = reinterpret_cast<T7GscAnimTree*>(animt_location);

if (animt->name >= scriptfile->fileSize) {
asmout << std::hex << "invalid animtree name 0x" << animt->name << "\n";
}
else {
char* s = scriptfile->Ptr<char>(animt->name);

animtreeUsings.emplace_back(s);

asmout << std::hex << "animtree " << (s ? s : "<err>") << "\n";

asmout << "tree address:";
const uint32_t* vars = reinterpret_cast<const uint32_t*>(&animt[1]);
for (size_t j = 0; j < animt->num_tree_address; j++) {
asmout << " " << flocName(*(vars++));
}
asmout << std::endl;
asmout << "node address:";
const uint64_t* vars2 = reinterpret_cast<const uint64_t*>(vars);
for (size_t j = 0; j < animt->num_node_address; j++) {

if (vars2[0] >= scriptfile->fileSize) {
asmout << std::hex << "invalid animtree 2nd name 0x" << animt->name << "\n";
}
else {
char* v = scriptfile->Ptr<char>(vars2[0]);
// why u64?
asmout << " " << flocName((uint32_t)vars2[1]) << ":" << v;
}

vars2 += 2;
}
asmout << std::endl;
}

animt_location += sizeof(*animt) + sizeof(uint32_t) * (animt->num_tree_address + (size_t)animt->num_node_address * 2);
}
}
}

if (opt.m_includes && animtreeUsings.size()) {
for (const char* animTree : animtreeUsings) {
asmout << "#using_animtree(\"" << animTree << "\");\n";
}
asmout << "\n";
}


if (opt.m_func) {
actslib::profiler::ProfiledSection ps{ profiler, "decompiling" };
// current namespace
Expand Down Expand Up @@ -1345,8 +1438,8 @@ int GscInfoHandleData(byte* data, size_t size, const char* path, const GscInfoOp
}

// handle first the constructor/destructor
handleMethod(g_constructorName, "constructor", true);
handleMethod(g_destructorName, "destructor", true);
handleMethod(vm < VM_T8 ? g_constructorNameT7 : g_constructorName, "constructor", true);
handleMethod(vm < VM_T8 ? g_destructorNameT7 : g_destructorName, "destructor", true);

for (const auto& method : cls.m_methods) {
handleMethod(method, nullptr, false);
Expand Down Expand Up @@ -1843,16 +1936,21 @@ int tool::gsc::DumpVTable(GSCExportReader& exp, std::ostream& out, GSCOBJHandler
return -1;
}

ctx.m_bcl += 2 + 1;
ctx.m_bcl += 2 + 1; // call
ctx.Aligned<uint64_t>() += 8; // assume that we have a spawnstruct

ctx.Aligned<uint16_t>() += 2; // GetZero


if (gscFile.GetVM() != VM_T8) {
if (gscFile.GetVM() > VM_T8) {
ctx.Aligned<uint16_t>() += 2; // EvalFieldVariableFromGlobalObject
ctx.Aligned<uint16_t>() += 2; // - classes
}
if (gscFile.GetVM() < VM_T8) {
ctx.Aligned<uint16_t>() += 2; // GetClassesObject

ctx.Aligned<uint16_t>() += 2; // EvalFieldVariableRef className
}
else {

ctx.Aligned<uint16_t>() += 2; // GetGlobalObject
Expand All @@ -1870,7 +1968,7 @@ int tool::gsc::DumpVTable(GSCExportReader& exp, std::ostream& out, GSCOBJHandler

clsName += 4;

if (gscFile.GetVM() != VM_T8) {
if (gscFile.GetVM() > VM_T8) {
ctx.Aligned<uint16_t>() += 2; // SetVariableFieldFromEvalArrayRef
}
else {
Expand Down Expand Up @@ -1974,15 +2072,21 @@ int tool::gsc::DumpVTable(GSCExportReader& exp, std::ostream& out, GSCOBJHandler

ctx.Aligned<uint16_t>() += 2; // GetZero

ctx.Aligned<uint16_t>() += 2; // EvalGlobalObjectFieldVariable
ctx.Aligned<uint16_t>() += 2; // - gvar
if (gscFile.GetVM() >= VM_T8) {
ctx.Aligned<uint16_t>() += 2; // EvalGlobalObjectFieldVariable
ctx.Aligned<uint16_t>() += 2; // - gvar
}
else {
ctx.Aligned<uint16_t>() += 2; // GetClassesObject
ctx.Aligned<uint16_t>() += 2; // EvalFieldVariable
}
ctx.Aligned<uint32_t>() += 4; // - ref
ctx.Aligned<uint16_t>() += 2; // EvalArray
ctx.Aligned<uint16_t>() += 2; // CastFieldObject
ctx.Aligned<uint16_t>() += 2; // EvalFieldVariableRef
ctx.Aligned<uint32_t>() += 4; // - ref

if (gscFile.GetVM() != VM_T8) {
if (gscFile.GetVM() > VM_T8) {
ctx.Aligned<uint16_t>() += 2; // SetVariableFieldFromEvalArrayRef
}
else {
Expand Down Expand Up @@ -2138,7 +2242,7 @@ void tool::gsc::DumpFunctionHeader(GSCExportReader& exp, std::ostream& asmout, G
else {

bool specialClassMember = !ctx.m_opt.m_dasm && classMember &&
((remapedFlags & T8GSCExportFlags::CLASS_DESTRUCTOR) || g_constructorName == exp.GetName());
((remapedFlags & T8GSCExportFlags::CLASS_DESTRUCTOR) || (g_constructorName == exp.GetName() || g_constructorNameT7 == exp.GetName()));

utils::Padding(asmout, padding);

Expand Down
8 changes: 8 additions & 0 deletions src/acts/tools/gsc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,8 @@ namespace tool::gsc {
std::unordered_map<uint32_t, const char*> m_stringRefs{};
public:
std::vector<IW23GSCImport> m_linkedImports{};
// getnumber hack
std::unordered_map<uint32_t, uint32_t> m_animTreeLocations{};
GsicInfo m_gsicInfo{};
opcode::VmInfo* m_vmInfo{};
std::unordered_map<uint64_t, gscclass> m_classes{};
Expand Down Expand Up @@ -943,6 +945,12 @@ namespace tool::gsc {
uint32_t address_str2;
};

struct T7GscAnimTree {
uint32_t name;
uint16_t num_tree_address;
uint16_t num_node_address;
};

struct T8GSCString {
uint32_t string;
uint8_t num_address;
Expand Down
27 changes: 22 additions & 5 deletions src/acts/tools/gsc_opcode_nodes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,21 +197,38 @@ namespace tool::gsc::opcode {
}
};

inline bool ASMContextNodeStringHasSpace(const char* str) {
while (*str) {
if (isspace(*(str++))) {
return true;
}
}
return false;
}

class ASMContextNodeString : public ASMContextNode {
public:
const char* m_value;
ASMContextNodeString(const char* value) : ASMContextNode(PRIORITY_VALUE), m_value(value) {
const char* m_prefix;
bool m_allowNoQuote;
ASMContextNodeString(const char* value, const char* prefix = nullptr, bool allowNoQuote = false)
: ASMContextNode(PRIORITY_VALUE), m_value(value), m_prefix(prefix), m_allowNoQuote(allowNoQuote) {
}

void Dump(std::ostream& out, DecompContext& ctx) const override {
out << "\"";
bool quotes{ !m_allowNoQuote || ASMContextNodeStringHasSpace(m_value) };
if (m_prefix) {
out << m_prefix;
}

if (quotes) out << "\"";
PrintFormattedString(out, m_value);
out << "\""
<< std::flush;
if (quotes) out << "\"";
out << std::flush;
}

ASMContextNode* Clone() const override {
return new ASMContextNodeString(m_value);
return new ASMContextNodeString(m_value, m_prefix, m_allowNoQuote);
}
};

Expand Down
Loading

0 comments on commit f60dba4

Please sign in to comment.