diff --git a/api.cpp b/api.cpp index 31a67f5..331dc29 100644 --- a/api.cpp +++ b/api.cpp @@ -21,26 +21,26 @@ std::unordered_map channelNames = std::vector dataNames = { - "Detail Diffuse Transform", - "Detail Normal Transform", + "\"detail_diffuse_transform\"", + "\"detail_normal_transform\"", "\"spec_aa_transform\"", - "Primary Color", + "\"primary_albedo_tint\"", "\"primary_emissive_tint_color_and_intensity_bias\"", "\"primary_material_params\"", "\"primary_material_advanced_params\"", - "Primary Roughness Remap", - "Primary Wear Color", - "Primary Wear Remap", - "Primary Worn Roughness Remap", + "\"primary_roughness_remap\"", + "\"primary_worn_albedo_tint\"", + "\"primary_wear_remap\"", + "\"primary_worn_roughness_remap\"", "\"primary_worn_material_parameters\"", - "Secondary Color", + "\"secondary_albedo_tint\"", "\"secondary_emissive_tint_color_and_intensity_bias\"", "\"secondary_material_params\"", "\"secondary_material_advanced_params\"", - "Secondary Roughness Remap", - "Secondary Wear Color", - "Secondary Wear Remap", - "Secondary Worn Roughness Remap", + "\"secondary_roughness_remap\"", + "\"secondary_worn_albedo_tint\"", + "\"secondary_wear_remap\"", + "\"secondary_worn_roughness_remap\"", "\"secondary_worn_material_parameters\"", }; @@ -113,7 +113,7 @@ uint32_t getArtArrangementHash(uint32_t apiHash, std::string packagesPath) } -void getAPIShader(uint32_t apiHash, std::string outputPath, std::string packagesPath, std::unordered_map hash64Table) +void getAPIShader(uint32_t apiHash, std::string outputPath, std::string packagesPath, std::unordered_map hash64Table, std::uint32_t dyeManifestHashes[], std::uint32_t dyeManHashesSize) { File* dataTable = new File("26FCDD80", packagesPath); dataTable->getData(); @@ -125,49 +125,60 @@ void getAPIShader(uint32_t apiHash, std::string outputPath, std::string packages std::unordered_map defaultChannelDyeMap; std::unordered_map customChannelDyeMap; memcpy((char*)&tableCount, dataTable->data + 8, 4); - for (int i = tableOffset; i < tableOffset + tableCount * 0x20; i += 0x20) + if (!dyeManHashesSize > 0) { - memcpy((char*)&val, dataTable->data + i, 4); - if (val == apiHash) + for (int i = tableOffset; i < tableOffset + tableCount * 0x20; i += 0x20) { - memcpy((char*)&val, dataTable->data + i + 0x10, 4); - File dataFile = File(uint32ToHexStr(val), packagesPath); - dataFile.getData(); - memcpy((char*)&val, dataFile.data + 0x88, 4); - val += 0x88; - memcpy((char*)&val2, dataFile.data + val - 4, 4); - if (val2 != 2155901815) - { - printf("Given shader is not valid!\n"); - return; - } - uint32_t defaultDyeTableCount; - uint32_t defaultDyeTableOffset; - uint32_t customDyeTableCount; - uint32_t customDyeTableOffset; - memcpy((char*)&defaultDyeTableCount, dataFile.data + val + 0x28, 4); - memcpy((char*)&defaultDyeTableOffset, dataFile.data + val + 0x30, 4); - defaultDyeTableOffset += 0x10 + val + 0x30; - memcpy((char*)&customDyeTableCount, dataFile.data + val + 0x38, 4); - memcpy((char*)&customDyeTableOffset, dataFile.data + val + 0x40, 4); - customDyeTableOffset += 0x10 + val + 0x40; - uint16_t channelIndex; - uint16_t dyeIndex; - for (int j = defaultDyeTableOffset; j < defaultDyeTableOffset + defaultDyeTableCount * 4; j += 4) - { - memcpy((char*)&channelIndex, dataFile.data + j, 2); - memcpy((char*)&dyeIndex, dataFile.data + j + 2, 2); - defaultChannelDyeMap[channelIndex] = dyeIndex; - } - for (int j = customDyeTableOffset; j < customDyeTableOffset + customDyeTableCount * 4; j += 4) + memcpy((char*)&val, dataTable->data + i, 4); + if (val == apiHash) { - memcpy((char*)&channelIndex, dataFile.data + j, 2); - memcpy((char*)&dyeIndex, dataFile.data + j + 2, 2); - customChannelDyeMap[channelIndex] = dyeIndex; + memcpy((char*)&val, dataTable->data + i + 0x10, 4); + File dataFile = File(uint32ToHexStr(val), packagesPath); + dataFile.getData(); + memcpy((char*)&val, dataFile.data + 0x88, 4); + val += 0x88; + memcpy((char*)&val2, dataFile.data + val - 4, 4); + if (val2 != 2155901815) + { + printf("Given shader is not valid!\n"); + return; + } + uint32_t defaultDyeTableCount; + uint32_t defaultDyeTableOffset; + uint32_t customDyeTableCount; + uint32_t customDyeTableOffset; + memcpy((char*)&defaultDyeTableCount, dataFile.data + val + 0x28, 4); + memcpy((char*)&defaultDyeTableOffset, dataFile.data + val + 0x30, 4); + defaultDyeTableOffset += 0x10 + val + 0x30; + memcpy((char*)&customDyeTableCount, dataFile.data + val + 0x38, 4); + memcpy((char*)&customDyeTableOffset, dataFile.data + val + 0x40, 4); + customDyeTableOffset += 0x10 + val + 0x40; + uint16_t channelIndex; + uint16_t dyeIndex; + for (int j = defaultDyeTableOffset; j < defaultDyeTableOffset + defaultDyeTableCount * 4; j += 4) + { + memcpy((char*)&channelIndex, dataFile.data + j, 2); + memcpy((char*)&dyeIndex, dataFile.data + j + 2, 2); + defaultChannelDyeMap[channelIndex] = dyeIndex; + } + for (int j = customDyeTableOffset; j < customDyeTableOffset + customDyeTableCount * 4; j += 4) + { + memcpy((char*)&channelIndex, dataFile.data + j, 2); + memcpy((char*)&dyeIndex, dataFile.data + j + 2, 2); + customChannelDyeMap[channelIndex] = dyeIndex; + printf(std::to_string(channelIndex).c_str()); + printf("\n"); + } } } + if (defaultChannelDyeMap.size() == 0) return; + } + else + { + customChannelDyeMap[0] = 0; + customChannelDyeMap[1] = 1; + customChannelDyeMap[2] = 2; } - if (defaultChannelDyeMap.size() == 0) return; File* channelTable = new File("C92FCF80", packagesPath); channelTable->getData(); @@ -180,6 +191,7 @@ void getAPIShader(uint32_t apiHash, std::string outputPath, std::string packages std::string dyeFileHash; File* finalDyeFile = nullptr; std::string channelName; + std::string channelNameHash; // For each pair, find the channel hash to pair it with a name + find the dye file std::unordered_map>> defaultDyes; @@ -200,9 +212,13 @@ void getAPIShader(uint32_t apiHash, std::string outputPath, std::string packages // Get channel name memcpy((char*)&channelHash, channelTable->data + 0x30 + 4 * it.first, 4); channelName = channelNames[channelHash]; + channelNameHash = std::to_string(channelHash); // Get dye file - memcpy((char*)&dyeManifestHash, dyeManifestTable->data + 0x30 + 8 * it.second + 4, 4); + if (!dyeManHashesSize > 0) + memcpy((char*)&dyeManifestHash, dyeManifestTable->data + 0x30 + 8 * it.second + 4, 4); + else + dyeManifestHash = dyeManifestHashes[it.second]; tableOffset = 0x40; memcpy((char*)&tableCount, dyeFileTable->data + 8, 4); for (int i = tableOffset; i < tableOffset + tableCount * 0x18; i += 0x18) @@ -292,17 +308,17 @@ void getAPIShader(uint32_t apiHash, std::string outputPath, std::string packages if (q == 0) { - defaultDyes[channelName] = dyeData; + defaultDyes[channelNameHash] = dyeData; texData["Diffuse"] = diffuseName; texData["Normal"] = normalName; - defaultTextures[channelName] = texData; + defaultTextures[channelNameHash] = texData; } else { - customDyes[channelName] = dyeData; + customDyes[channelNameHash] = dyeData; texData["Diffuse"] = diffuseName; texData["Normal"] = normalName; - customTextures[channelName] = texData; + customTextures[channelNameHash] = texData; } } } @@ -316,29 +332,38 @@ void getAPIShader(uint32_t apiHash, std::string outputPath, std::string packages void writeShader(std::unordered_map>> dyes, std::unordered_map> textures, bool bCustom, std::string outputPath) { - std::string stringFactoryShader = ""; - if (!bCustom) stringFactoryShader += "Default dyes:\n"; - else stringFactoryShader += "Custom dyes:\n"; + std::string stringFactoryShader = "{\n"; + if (!bCustom) stringFactoryShader += " \"custom_dyes\": [],\n \"locked_dyes\": [],\n \"default_dyes\": [\n"; + else stringFactoryShader += " \"default_dyes\": [],\n \"locked_dyes\": [],\n \"custom_dyes\": [\n"; + std::string propertiesString = ""; for (auto& it : dyes) { - stringFactoryShader += " " + it.first + ":\n"; - if (it.first.find("Cloth", 0) != std::string::npos) stringFactoryShader += " Is cloth: True\n"; - else stringFactoryShader += " Is cloth: False\n"; - stringFactoryShader += " Properties:\n"; + //propertiesString += " " + it.first + ":\n"; + propertiesString += " {\n"; + propertiesString += " \"investment_hash\": " + it.first + ",\n"; + if (it.first.find("Cloth", 0) != std::string::npos) propertiesString += " \"cloth\": true,\n"; + else propertiesString += " \"cloth\": false,\n"; + propertiesString += " \"material_properties\": {\n"; + std::string valuesString = ""; for (auto& it2 : it.second) { - if (it.first.find("Diffuse", 0) != std::string::npos) break; + if (it.first.find("diffuse", 0) != std::string::npos) break; std::string floatString = "["; for (auto& flt : it2.second) floatString += std::to_string(flt) + ", "; - stringFactoryShader += " " + it2.first + ": " + floatString.substr(0, floatString.size()-2) + "]\n"; + //stringFactoryShader += " " + it2.first + ": " + floatString.substr(0, floatString.size()-2) + "],\n"; + valuesString += " " + it2.first + ": " + floatString.substr(0, floatString.size()-2) + "],\n"; } - stringFactoryShader += " Diffuse: " + textures[it.first]["Diffuse"] + "\n"; - stringFactoryShader += " Normal: " + textures[it.first]["Normal"] + "\n"; + propertiesString += valuesString.substr(0, valuesString.size()-2) + "\n },\n"; + propertiesString += " \"textures\": {\n"; + propertiesString += " \"diffuse\": {\n \"name\": \"" + textures[it.first]["Diffuse"] + "\"\n },\n"; + propertiesString += " \"normal\": {\n \"name\": \"" + textures[it.first]["Normal"] + "\"\n }\n"; + propertiesString += " }\n },\n"; } - stringFactoryShader += "\n"; + stringFactoryShader += propertiesString.substr(0, propertiesString.size()-2) + "\n ]\n}"; + //stringFactoryShader += "\n"; FILE* shaderFile; - std::string path = outputPath + "/shader.txt"; + std::string path = outputPath + "/shader.json"; fopen_s(&shaderFile, path.c_str(), "w"); fwrite(stringFactoryShader.c_str(), stringFactoryShader.size(), 1, shaderFile); fclose(shaderFile); diff --git a/api.h b/api.h index ca339a4..8eba4fd 100644 --- a/api.h +++ b/api.h @@ -8,5 +8,5 @@ std::vector getAPISingleHashes(uint32_t mHash, uint32_t fHash, std: std::vector getAPIMultiHashes(uint32_t tableOffset, File* modelTable, std::string packagesPath, std::unordered_map hash64Table); std::vector getHashesFromH64s(std::vector h64Files, std::string packagesPath, std::unordered_map hash64Table); uint32_t getArtArrangementHash(uint32_t apiHash, std::string packagesPath); -void getAPIShader(uint32_t apiHash, std::string outputPath, std::string packagesPath, std::unordered_map hash64Table); +void getAPIShader(uint32_t apiHash, std::string outputPath, std::string packagesPath, std::unordered_map hash64Table, std::uint32_t dyeManifestHashes[], std::uint32_t dyeManHashesSize); void writeShader(std::unordered_map>> dyes, std::unordered_map> textures, bool bCustom, std::string outputPath); diff --git a/main.cpp b/main.cpp index 5c93ba5..1b7d29c 100644 --- a/main.cpp +++ b/main.cpp @@ -30,6 +30,7 @@ int main(int argc, char** argv) sarge.setArgument("s", "skeloverride", "skeleton override", true); sarge.setArgument("c", "cbuffer", "enable cbuffer extraction", false); sarge.setArgument("h", "shader", "shader hash", true); + sarge.setArgument("d", "dyemanifesthashes", "shader by individual dyes", true); sarge.setDescription("Destiny 2 dynamic model extractor by Monteven."); sarge.setUsage("MontevenDynamicExtractor"); @@ -51,19 +52,36 @@ int main(int argc, char** argv) std::string batchPkg; std::string apiHashStr = ""; std::string shaderHashStr = ""; + std::string dyeHashesStr = ""; uint32_t apiHash = 0; uint32_t shaderHash = 0; + uint32_t dyeHashes[3] = {}; sarge.getFlag("pkgspath", pkgsPath); sarge.getFlag("outputpath", outputPath); sarge.getFlag("filename", fileName); sarge.getFlag("inputhash", modelHash); sarge.getFlag("api", apiHashStr); sarge.getFlag("shader", shaderHashStr); + sarge.getFlag("dyemanifesthashes", dyeHashesStr); sarge.getFlag("skeloverride", skeletonOverrideStr); if (skeletonOverrideStr != "") skeletonOverride = std::stol(skeletonOverrideStr); if (apiHashStr != "") apiHash = std::stoul(apiHashStr); if (shaderHashStr != "") shaderHash = std::stoul(shaderHashStr); + if (dyeHashesStr != "") + { + shaderHash = 1; + size_t pos = 0; + std::string token; + std::int8_t index = 0; + std::string delimiter = " "; + dyeHashesStr += " "; + while ((pos = dyeHashesStr.find(delimiter)) != std::string::npos) { + dyeHashes[index] = stoul(dyeHashesStr.substr(0, pos)); + dyeHashesStr.erase(0, pos + delimiter.length()); + index++; + } + } bTextures = sarge.exists("textures"); bCBuffer = sarge.exists("cbuffer"); sarge.getFlag("batch", batchPkg); @@ -119,9 +137,12 @@ int main(int argc, char** argv) if (shaderHash != 0) { printf("Shader flag found, getting shader data...\n"); - std::string savePath = outputPath + "/" + std::to_string(shaderHash); + std::string savePath = outputPath + "/" + fileName;//std::to_string(shaderHash); std::filesystem::create_directories(savePath); - getAPIShader(shaderHash, savePath, pkgsPath, hash64Table); + if (shaderHash != 1) + getAPIShader(shaderHash, savePath, pkgsPath, hash64Table, new std::uint32_t[0]{}, 0); + else + getAPIShader(shaderHash, savePath, pkgsPath, hash64Table, dyeHashes, sizeof(dyeHashes)); printf("Shader rip done!"); return 0;