Files
gtav-src/tools_ng/techart/dcc/motionbuilder2014/python/RS/Utils/ContentUpdate.py
T
2025-09-29 00:52:08 +02:00

163 lines
7.1 KiB
Python
Executable File

import os
import copy
from subprocess import Popen, PIPE
from xml.etree import ElementTree as ET
from xml.dom import minidom
import pyfbsdk as mobu
import RS.Perforce as P4
def RunTerminalCommand(cmdTxt):
process = Popen(cmdTxt, shell=True, stdout=PIPE)
output = process.communicate()[0]
return output
def WriteCleanXml(rootNode, xmlPath):
outputString = ET.tostring(rootNode, encoding="utf-8")
xmlDoc = minidom.parseString(outputString)
prettyXml = '\n'.join([line for line in xmlDoc.toprettyxml(indent=' '*2, encoding='utf-8').split('\n') if line.strip()])
with open(xmlPath, "wb") as fileHandle:
fileHandle.write(prettyXml)
def SetupGameContent(changeListNumber=None):
# Get Project Root and Name
projectRoot = mobu.FBApplication().FBXFileName.split("{0}art{0}".format(os.path.sep))[0]
projectName = projectRoot.split(os.path.sep)[1].upper()
# Get Mission Name
fbxName = os.path.split(mobu.FBApplication().FBXFileName)[1][:-4].upper()
missionText = fbxName.split("_")[0].lower()
if projectName == "GTA5_DLC":
missionText = "_".join(fbxName.split("_")[:2]).lower()
# Create List of Xml Paths
xmlList = []
if projectName == "RDR3":
xmlList.append(os.path.join(projectRoot, "build", "dev", "common", "data", "common_cutscene.meta"))
xmlList.append(os.path.join(projectRoot, "build", "dev", "common", "data", "anim", "anim.meta"))
if projectName == "GTA5_DLC":
xmlList.append(os.path.join(projectRoot, "build", "dev_ng", "content.xml"))
# Loop Through Each Xml Path - and set search text
for xmlPath in xmlList:
searchText = "/anim/animscenes/cuts@" if xmlPath.endswith("anim.meta") else "/anim/cutscene/cuts_"
# Sync Latest Xml, rootNode, and dataFilesNode
P4.Sync(xmlPath, force=True)
tree = ET.parse(xmlPath)
rootNode = tree.getroot()
if rootNode.tag == "dataFiles":
dataFilesNode = rootNode
else:
dataFilesNode = tree.find("dataFiles")
# Search For Cutscene RPFs for Matching Nodes
rpfNodes = [node for node in dataFilesNode.findall(".//Item/filename/..") if searchText in node.find("filename").text.lower()]
matchedNode = [node for node in rpfNodes if node.find("filename").text.lower().endswith("{0}{1}.rpf".format(searchText, missionText))]
# Scene RPF Not Found - copy and add a new node
if rpfNodes and matchedNode == []:
platformText = rpfNodes[0].find("filename").text.lower().split(searchText)[0]
rpfText = "{0}{1}{2}.rpf".format(platformText, searchText, missionText)
newNode = copy.deepcopy(rpfNodes[0])
filenameNode = newNode.find("filename")
filenameNode.text = rpfText
dataFilesNode.append(newNode)
# RDR Specific - Sort And Cleanup Nodes
if projectName == "RDR3":
# Sort And Cleanup Nodes
container = dataFilesNode
data = []
keyList = []
for elem in container:
nameNode = elem.find("filename")
key = nameNode.text.lower()
nameNode.text = key
if key not in keyList:
keyList.append(key)
data.append((key, elem))
data.sort()
container[:] = [item[-1] for item in data]
# GTA5 DLC Specific - Add extra fileToEnable node
else:
dlcPackName = rpfText.split("dlc_")[1].split(":")[0].upper()
changeSetText = "{}_AUTOGEN".format(dlcPackName)
enableFilesNode = rootNode.find(".//*[changeSetName='{}']/filesToEnable".format(changeSetText))
if enableFilesNode != []:
itemNodes = [node for node in enableFilesNode.findall("Item") if "/anim/cutscene/cuts_" in node.text]
matchedNode = [node for node in enableFilesNode.findall("Item") if node.text.lower() == rpfText.lower()]
if rpfNodes and matchedNode == []:
newNode = copy.deepcopy(itemNodes[0])
newNode.text = rpfText
enableFilesNode.append(newNode)
# Write Out Updated Xml - and add to changelist if provided
P4.Edit(xmlPath)
# tree.write(xmlPath, encoding="UTF-8")
WriteCleanXml(rootNode, xmlPath)
if changeListNumber:
os.chdir(projectRoot)
cmdTxt = "p4 reopen -c {0} {1}".format(changeListNumber, xmlPath)
RunTerminalCommand(cmdTxt)
def SetupToolsContent(changeListNumber=None):
# Get Project Paths and Name
projectRoot = os.environ.get("RS_PROJROOT")
toolsRoot = os.environ.get("RS_TOOLSROOT")
assetRoot = os.path.join(projectRoot, "assets")
projectName = os.path.basename(projectRoot).upper()
# Get Content Files
if projectName == "RDR3":
contentFiles = [os.path.join(toolsRoot, "etc", "content", "content_animation_cutscene.xml"),
os.path.join(toolsRoot, "etc", "content", "content_animation_cutscene_fbx.xml")]
contentToolPath = os.path.join(assetRoot, "metadata", "content", "generator", "GenerateCutsceneContent.bat")
cmdTxt = "{} < nul".format(contentToolPath)
elif projectName == "GTA5_DLC":
contentFiles = [os.path.join(projectRoot, "content_animation_cutscene.xml"),
os.path.join(projectRoot, "content_animation_cutscene_fbx.xml")]
contentToolPath = os.path.join(toolsRoot, "bin", "Cutscene", "content", "cutscene_content_generator.exe")
cmdTxt = "{0} --fbx --target={1}".format(contentToolPath, projectRoot.split(os.path.sep)[-1])
else:
errorMsg = "Error: This FBX is from a project the content update tool doesn't support."
print errorMsg
return errorMsg
# Sync Latest Content Xml Files
P4.Revert(contentFiles)
P4.Sync(contentFiles, force=True)
# Run Content Tool Command
os.chdir(projectRoot)
RunTerminalCommand(cmdTxt)
# Move Files to Our ChangeList - if they were updated
for eachXml in contentFiles:
eachP4Obj = P4.GetFileState(eachXml)
if P4.IsOpenForEdit(eachP4Obj):
cmdTxt = "p4 diff -ds {}".format(eachXml)
output = RunTerminalCommand(cmdTxt)
diffText = output.split("====\r")[1]
# File Identical - we revert it
if len(diffText) < 5:
P4.Revert(eachXml)
# File Updated and ChangeList Provided - move to our changelist
elif changeListNumber:
cmdTxt = "p4 reopen -c {0} {1}".format(changeListNumber, eachXml)
RunTerminalCommand(cmdTxt)
def Run():
fbxPath = mobu.FBApplication().FBXFileName.lower()
animationFolder = "{0}art{0}animation{0}".format(os.path.sep)
if animationFolder in fbxPath and os.path.splitext(fbxPath)[1] == ".fbx":
SetupGameContent()
SetupToolsContent()