Skip to content

Commit

Permalink
t10 character dumper
Browse files Browse the repository at this point in the history
  • Loading branch information
ate47 committed Mar 3, 2025
1 parent 31964b6 commit 49f9197
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 70 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ See the wiki to know how to use the features
- Black Ops Cold War (DEC): `rawfile`, `rawfilepreproc`, `rawtextfile`, `stringtable`, `scriptparsetree`, `scriptbundle`.
- Modern Warfare 2019 (COR): `scriptfile`.
- Modern Warfare III (COR): `gscobj`, `scriptbundle`, `stringtable`, `localize`, `luafile`, `ddl`.
- Black Ops 6 (COR): `gscobj`, `scriptbundle`, `stringtable`, `localize`, `ddl`, `operator`, `operatorlist`, `operatorskin`, `luafile`, `rawfile`, `storagefile`, `project`, `projecttable`.
- Black Ops 6 (COR): `gscobj`, `scriptbundle`, `stringtable`, `localize`, `ddl`, `operator`, `operatorlist`, `operatorskin`, `luafile`, `rawfile`, `storagefile`, `project`, `projecttable`, `character`.

- **DEC**: Requires pre-decode
- **COR**: Using [Cordycep](https://github.com/Scobalula/Cordycep).
Expand Down
39 changes: 3 additions & 36 deletions src/acts/tools/cordycep/dumper_t10.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ namespace tool::cordycep::dump::t10 {
return tool::OK;
}

if (!opt.m_any_type && !opt.dumpScrStrings) {
if (!opt.m_any_type) {
opt.PrintHelp();
return tool::OK;
}
Expand Down Expand Up @@ -244,24 +244,6 @@ namespace tool::cordycep::dump::t10 {

UnlinkerContext uctx{ proc, opt, outDir, GetLocalized, cordycep };

if (opt.dumpScrStrings) {
uctx.GetScrString(0);

utils::OutFileCE stros{ dumpDir / "scrstr.txt" };

if (!stros) {
LOG_ERROR("Can't dump scr strings");
return tool::BASIC_ERROR;
}

const char* origin{ uctx.scrBuffer.get() };
const char* ss{ uctx.scrBuffer.get() };
while ((ss - origin) < cordycep.stringSize) {
stros << ss << "\n";
ss += std::strlen(ss) + 1;
}
}

for (auto& [type, unlinker] : GetUnlinkers()) {
HandlePool(type, outDir, [&uctx, &unlinker](const XAsset64& asset, size_t count) { return unlinker->Unlink(asset, uctx); });
}
Expand Down Expand Up @@ -402,9 +384,6 @@ namespace tool::cordycep::dump::t10 {
else if (!_strcmpi("--header", arg)) {
header = true;
}
else if (!_strcmpi("--dumpScrStrings", arg)) {
dumpScrStrings = true;
}
else if (!strcmp("-S", arg) || !_strcmpi("--strings", arg)) {
if (i + 1 == endIndex) {
std::cerr << "Missing value for param: " << arg << "!\n";
Expand Down Expand Up @@ -465,20 +444,8 @@ namespace tool::cordycep::dump::t10 {
return str;
}

const char* UnlinkerContext::GetScrString(ScrString str) {
if (!scrBufferSize) {
scrBufferSize = cordycep.stringSize;
scrBuffer = std::make_unique<char[]>(1 + cordycep.stringSize);
if (cordycep.stringSize) {
if (!proc.ReadMemory(scrBuffer.get(), cordycep.stringsAddress, cordycep.stringSize)) {
throw std::runtime_error("Can't read cordycep strings");
}
}
}
if (str >= scrBufferSize) {
return utils::va("<invalid:%d/%d>", str, scrBufferSize);
}
return &scrBuffer[str];
const char* UnlinkerContext::GetScrString(ScrString str) const {
return proc.ReadStringTmp(cordycep.stringsAddress + str);
}

std::unordered_map<bo6::T10RAssetType, Unlinker*>& GetUnlinkers() {
Expand Down
67 changes: 39 additions & 28 deletions src/acts/tools/cordycep/t10/dumper_10_character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@ namespace {
uint64_t unk18;
uint64_t unk20;
uint64_t unk28;
Dismemberment* unk30;
AIAnimSettings* unk38;
ScrString unk40;
Dismemberment* dismemberment;
AIAnimSettings* aiAnimSettings;
ScrString bodyScr;
ScrString unk44;
ScrString unk48;
ScrString headScr;
ScrString unk4c;
ScrString unk50;
ScrString hatScr;
ScrString unk54;
ScrString unk58;
ScrString unk5c;
ScrString classScr;
ScrString animtreeScr;
int32_t unk68_count;
const char** unk68;
uint64_t unk70;
Expand All @@ -46,40 +46,51 @@ namespace {
LOG_ERROR("Can't read {} header ", hashutils::ExtractTmp("hash", asset.ID));
return false;
}


std::filesystem::path loc{ ctx.outDir / "tables" / "character" / std::format("{}.json", hashutils::ExtractTmp("file", data.name)) };
if (opt.m_ignoreOld && std::filesystem::exists(loc)) return true;
std::filesystem::create_directories(loc.parent_path());
utils::raw_file_extractor::JsonWriter json{};

LOG_INFO("Dump {}", loc.string());

// Dump pool
json.BeginObject();

json.WriteFieldNameString("name");
json.WriteValueHash(data.name);
json.WriteFieldNameString("unk40"); json.WriteValueString(opt.AddString(ctx.GetScrString(data.unk40)));
json.WriteFieldNameString("unk44"); json.WriteValueString(opt.AddString(ctx.GetScrString(data.unk44)));
json.WriteFieldNameString("unk48"); json.WriteValueString(opt.AddString(ctx.GetScrString(data.unk48)));
json.WriteFieldNameString("unk4c"); json.WriteValueString(opt.AddString(ctx.GetScrString(data.unk4c)));
json.WriteFieldNameString("unk50"); json.WriteValueString(opt.AddString(ctx.GetScrString(data.unk50)));
json.WriteFieldNameString("unk54"); json.WriteValueString(opt.AddString(ctx.GetScrString(data.unk54)));
json.WriteFieldNameString("unk58"); json.WriteValueString(opt.AddString(ctx.GetScrString(data.unk58)));
json.WriteFieldNameString("unk5c"); json.WriteValueString(opt.AddString(ctx.GetScrString(data.unk5c)));
json.WriteFieldNameString("unk68");
json.BeginArray();
json.WriteFieldNameString("name"); json.WriteValueHash(data.name);
if (data.dismemberment) {
json.WriteFieldNameString("dismemberment"); json.WriteValueHash(proc.ReadMemory<uint64_t>(reinterpret_cast<uintptr_t>(data.dismemberment)));
}
if (data.aiAnimSettings) {
json.WriteFieldNameString("aiAnimSettings"); json.WriteValueHash(proc.ReadMemory<uint64_t>(reinterpret_cast<uintptr_t>(data.aiAnimSettings)));
}

json.WriteFieldNameString("hash08"); json.WriteValueHash(data.unk08);
json.WriteFieldNameString("hash10"); json.WriteValueHash(data.unk10);
json.WriteFieldNameString("hash18"); json.WriteValueHash(data.unk18);
json.WriteFieldNameString("hash20"); json.WriteValueHash(data.unk20);
json.WriteFieldNameString("hash28"); json.WriteValueHash(data.unk28);

if (data.bodyScr) { json.WriteFieldNameString("body"); json.WriteValueString(opt.AddString(ctx.GetScrString(data.bodyScr))); }
if (data.unk44) { json.WriteFieldNameString("unk44"); json.WriteValueString(opt.AddString(ctx.GetScrString(data.unk44))); }
if (data.headScr) { json.WriteFieldNameString("head"); json.WriteValueString(opt.AddString(ctx.GetScrString(data.headScr))); }
if (data.unk4c) { json.WriteFieldNameString("unk4c"); json.WriteValueString(opt.AddString(ctx.GetScrString(data.unk4c))); }
if (data.hatScr) { json.WriteFieldNameString("hat"); json.WriteValueString(opt.AddString(ctx.GetScrString(data.hatScr))); }
if (data.unk54) { json.WriteFieldNameString("unk54"); json.WriteValueString(opt.AddString(ctx.GetScrString(data.unk54))); }
if (data.classScr) { json.WriteFieldNameString("class"); json.WriteValueString(opt.AddString(ctx.GetScrString(data.classScr))); }
if (data.animtreeScr) { json.WriteFieldNameString("animtree"); json.WriteValueString(opt.AddString(ctx.GetScrString(data.animtreeScr))); }
if (data.unk68_count) {
json.WriteFieldNameString("unk68");
json.BeginArray();
auto unk68{ proc.ReadMemoryArrayEx<uintptr_t>(reinterpret_cast<uintptr_t>(data.unk68), data.unk68_count) };
for (size_t i = 0; i < data.unk68_count; i++) {
json.WriteValueString(opt.AddString(proc.ReadStringTmp(unk68[i])));
}
json.EndArray();
}
json.EndArray();
json.EndObject();


std::filesystem::path loc{ ctx.outDir / "tables" / "character" / std::format("{}.json", hashutils::ExtractTmp("file", data.name)) };

LOG_INFO("Dump {}", loc.string());
if (opt.m_ignoreOld && std::filesystem::exists(loc)) return true;
std::filesystem::create_directories(loc.parent_path());

if (!json.WriteToFile(loc)) {
LOG_ERROR("Can't write {}", loc.string());
}
Expand All @@ -88,5 +99,5 @@ namespace {

};

//utils::MapAdder<UnlinkerImpl, bo6::T10RAssetType, Unlinker> impl{ GetUnlinkers(), bo6::T10R_ASSET_CHARACTER };
utils::MapAdder<UnlinkerImpl, bo6::T10RAssetType, Unlinker> impl{ GetUnlinkers(), bo6::T10R_ASSET_CHARACTER };
}
6 changes: 1 addition & 5 deletions src/acts/tools/cordycep/t10/dumper_t10.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ namespace tool::cordycep::dump::t10 {
bool m_any_type = false;
bool m_dump_info = false;
bool m_dump_all_available = false;
bool dumpScrStrings{};
bool m_pools{};
bool m_cf_files{};
bool m_usev1{};
Expand Down Expand Up @@ -51,10 +50,7 @@ namespace tool::cordycep::dump::t10 {
std::function<const char* (uint64_t hash)> GetLocalized;
compatibility::scobalula::csi::CordycepProc& cordycep;

std::unique_ptr<char[]> scrBuffer{ nullptr };
size_t scrBufferSize{};

const char* GetScrString(ScrString str);
const char* GetScrString(ScrString str) const;
};

class Unlinker {
Expand Down

0 comments on commit 49f9197

Please # to comment.