Files
gtav-src/tools_ng/bin/audio/scripts/Build/audio_rpfs_ng.py
T
2025-09-29 00:52:08 +02:00

437 lines
15 KiB
Python
Executable File

import datetime
import hashlib
import os
import re
import sys
from audio_logger import Logger, LogSeverity
from p4_helper import P4Helper
DEFAULT_BRANCH = 'dev_ng'
# Runtime data
NOW_START = datetime.datetime.now()
NOW_START_STR = NOW_START.strftime('%y_%m_%d__%H_%M_%S')
RPF_ROOT_PATH = r'x:\gta5\audio\{branch}\STAGINGRPF'
LOG_DIR = os.path.join(r'C:\Temp\Logs\Audio RPFs', NOW_START_STR)
LOG_FILENAME = r'run_log_%s.txt'
PLATFORMS = (
'PS4',
'XBOXONE',
'PC',
'XBOXSERIES',
'PS5'
)
# Now that we have the data for the disk build, we no longer need
# to rebuild the RPFs for next-gen consoles, only audio.rpf and
# audio_rel.rpf need to be built for these
EXCLUDED_RPF_BUILD_PLATFORMS = (
'PS4',
'XBOXONE',
'PC'
)
DEV_IGNORE_PACKS_LIST = (
#'^PS_(.*)',
'(.*)DLC(.*)',
)
RELEASE_IGNORE_PACKS_LIST = {
'PS4' : (
'(.*)DLC(.*)',
'TEST(.*)',
'^CUTSCENE$',
'^CUTSCENE_MASTERED_TRIMMED$',
),
'XBOXONE' : (
'^PS_(.*)',
'(.*)DLC(.*)',
'TEST(.*)',
'^CUTSCENE$',
'^CUTSCENE_MASTERED_TRIMMED$',
),
'PC' : (
'(.*)DLC(.*)',
'TEST(.*)',
'^CUTSCENE$',
'^CUTSCENE_MASTERED_TRIMMED$',
),
'PS5' : (
'(.*)DLC(.*)',
'TEST(.*)',
'^CUTSCENE$',
'^CUTSCENE_MASTERED_TRIMMED$',
),
'XBOXSERIES' : (
'(.*)DLC(.*)',
'TEST(.*)',
'^CUTSCENE$',
'^CUTSCENE_MASTERED_TRIMMED$',
),
}
IGNORE_EXCEPTION_PACKS_LIST = (
'DLC_GTAO$',
'DLCTU$',
)
# Runtime args
audio_path = None
rage_builder_path = None
package_path_release = None
package_path_main = None
tools_root_path = None
packlist_path_main = None
packlist_path_release = None
input_path = None
audio_output_path = None
sfx_output_path = None
build_pack_path = None
working_path = None
rpf_source_path = None
logger = None
p4 = None
def generate_audio_rpfs(platform, rebuild, branch):
global logger
global p4
logger = Logger('Audio RPFs', LOG_DIR, LOG_FILENAME % platform)
if platform in EXCLUDED_RPF_BUILD_PLATFORMS:
logger.log("%s is set to be excluded from RPF builds" %platform, LogSeverity.INFO)
return 0
# Initialise paths to required dirs and files
if not init_paths(platform, branch):
logger.log("Failed to initialise paths", LogSeverity.ERROR)
failed()
return -1
# connect to p4
p4 = P4Helper(logger)
p4.connect()
cl_number = p4.create_cl("Daily Sync [GTA5] " + branch + " : Audio RPF Build - " + platform)
if cl_number == -1:
failed()
return -1
# build sfx rpfs
if not generate_rpfs(rebuild, platform, cl_number):
failed()
return -1
#we dont need to build audio.rpf and audio_rel.rpf anymore for V
#re-enabling audio(_rel).rpf building for gen9 platforms
if(platform.upper() != 'PS5' and platform.upper() != 'XBOXSERIES'):
return 0
# Generate dev audio RPF file
if not generate_audio_rpf(platform, cl_number):
failed()
return -1
# Generate release audio RPF file
if not generate_audio_rpf(platform, cl_number, True):
failed()
return -1
if not p4.submit_cl(cl_number):
p4.revert_cl(cl_number, ".rpf", True)
return -1
logger.save()
return 0
def init_paths(platform, branch):
global build_path
global audio_path
global rage_builder_path
global package_path_release
global package_path_main
global package_path_release_durango
global package_path_main_durango
global package_path_release_orbis
global package_path_main_orbis
global package_path_release_win64
global package_path_main_win64
global tools_root_path
global packlist_path_main
global packlist_path_release
global package_path_release
global input_path
global audio_output_path
global sfx_output_path
global build_pack_path
global rpf_source_path
global output_platform_name
#ragebuilder forces win32 if 'PC' is in output path so we will make sure to output to 'x64'
output_platform_name = platform
if platform.upper() == 'PC':
output_platform_name = 'X64'
# Init tools root path
tools_root_path = os.environ['RS_TOOLSROOT']
if not tools_root_path:
logger.log("'RS_TOOLSROOT' path not found in environment", LogSeverity.ERROR)
return False
ruby_path = os.environ['RUBYLIB']
if not ruby_path:
logger.log("'RUBYLIB' path not found in environment", LogSeverity.ERROR)
return False
# Init package paths
package_path_release_orbis = os.path.join(ruby_path, r'util\ragebuilder\package_audio_orbis_release.rbs')
package_path_main_orbis = os.path.join(ruby_path, r'util\ragebuilder\package_audio_orbis.rbs')
package_path_release_durango = os.path.join(ruby_path, r'util\ragebuilder\package_audio_durango_release.rbs')
package_path_main_durango = os.path.join(ruby_path, r'util\ragebuilder\package_audio_durango.rbs')
package_path_release_win64 = os.path.join(ruby_path, r'util\ragebuilder\package_audio_win64_release.rbs')
package_path_main_win64 = os.path.join(ruby_path, r'util\ragebuilder\package_audio_win64.rbs')
# Init rage builder path - use rage builder gen9 for gen9 platforms
if platform == 'XBOXSERIES' or platform == 'PS5':
rage_builder_path = os.path.join(tools_root_path, r'bin\RageBuilderGen9\ragebuilder_Release_gen9_SGA.exe')
else:
rage_builder_path = os.path.join(tools_root_path, r'bin\ragebuilder_x64_0378.exe')
proj_root = os.environ['RS_PROJROOT']
if not proj_root:
logger.log("'RS_PROJROOT' path not found in environment", LogSeverity.ERROR)
return False
# Init audio path
audio_path = os.path.join(proj_root, 'audio', branch, 'staging')
# Init config dir
config_dir = os.path.join(audio_path, platform, 'config')
if not os.path.exists(config_dir):
logger.log("Creating path: %s" % config_dir, LogSeverity.INFO)
os.makedirs(config_dir)
# Init packlist paths (file may not exist locally)
packlist_path_main = os.path.join(config_dir, 'packlist.txt')
logger.log("Using main packlist path: %s" % packlist_path_main, LogSeverity.INFO)
packlist_path_release = os.path.join(config_dir, 'packlist_rel.txt')
logger.log("Using release packlist path: %s" % packlist_path_release, LogSeverity.INFO)
# Init input path
input_path = os.path.join(audio_path, platform, 'sfx')
if not os.path.exists(input_path):
logger.log("Path does not exist: %s" % input_path, LogSeverity.ERROR)
return False
logger.log("Using input path: %s" % input_path, LogSeverity.INFO)
if branch is None:
rpf_root_path = RPF_ROOT_PATH.replace(r'{branch}', DEFAULT_BRANCH)
else:
rpf_root_path = RPF_ROOT_PATH.replace(r'{branch}', branch)
rpf_source_path = os.path.join(rpf_root_path, output_platform_name, 'audio')
if not os.path.exists(rpf_root_path):
try:
os.makedirs(rpf_root_path)
except:
logger.log("Failed to create directory: %s" % rpf_root_path, LogSeverity.ERROR)
return False
# Init output path
audio_output_path = os.path.join(rpf_root_path, output_platform_name, 'audio')
if not os.path.exists(audio_output_path):
logger.log("Path does not exist - creating directory: %s" % audio_output_path, LogSeverity.INFO)
try:
os.makedirs(audio_output_path)
except:
logger.log("Failed to create directory: %s" % audio_output_path, LogSeverity.ERROR)
return False
logger.log("Using audio output path: %s" % audio_output_path, LogSeverity.INFO)
sfx_output_path = os.path.join(audio_output_path, 'sfx')
if not os.path.exists(sfx_output_path):
logger.log("Path does not exist - creating directory: %s" % sfx_output_path, LogSeverity.INFO)
try:
os.makedirs(sfx_output_path)
except:
logger.log("Failed to create directory: %s" % sfx_output_path, LogSeverity.ERROR)
return False
logger.log("Using sfx output path: %s" % sfx_output_path, LogSeverity.INFO)
return True
def generate_rpfs(rebuild, platform, changeListNumber):
global input_path
global sfx_output_path
global package_path
global p4
if rebuild:
logger.log("Generating all audio RPF files..", LogSeverity.INFO)
else:
logger.log("Generating latest audio RPF files..", LogSeverity.INFO)
# Open pack list file for writing and overwrite previous data
pack_list_file_main = open(packlist_path_main, 'w+')
pack_list_file_release = open(packlist_path_release, 'w+')
# Generate RPFs for files in each pack (ie sub-dir) in input_path
for path, packs, files in os.walk(input_path):
for pack in packs:
# Add pack to dev file list (if required)
skip_for_dev = False
for except_pattern in IGNORE_EXCEPTION_PACKS_LIST:
if re.match(except_pattern, pack, re.IGNORECASE):
skip_for_dev = False
break
else:
for pattern in DEV_IGNORE_PACKS_LIST:
if re.match(pattern, pack, re.IGNORECASE):
logger.log("Skipping pack for dev pack list: %s" % pack, LogSeverity.INFO)
skip_for_dev = True
break
if not skip_for_dev:
pack_list_file_main.write("%s\n" % pack)
# Add pack to release file list (if required)
skip_for_rel = False
for except_pattern in IGNORE_EXCEPTION_PACKS_LIST:
if re.match(except_pattern, pack, re.IGNORECASE):
skip_for_rel = False
break
else:
for pattern in RELEASE_IGNORE_PACKS_LIST[platform.upper()]:
if re.match(pattern, pack, re.IGNORECASE):
logger.log("Skipping pack for release pack list: %s" % pack, LogSeverity.INFO)
skip_for_rel = True
break
if not skip_for_rel:
pack_list_file_release.write("%s\n" % pack)
if not rebuild and not pack_requires_build(os.path.join(path, pack), pack):
# Skip current pack if build not required and
# process not marked for full rebuild
logger.log("Skipping pack - build not required: %s" % pack, LogSeverity.INFO)
continue
# Init build params
logger.log("Building pack %s..." % pack, LogSeverity.INFO)
input_pack_path = os.path.join(input_path, pack) + '\\'
output_file = os.path.join(sfx_output_path, pack) + '.rpf'
if platform.upper() == 'PS4' or platform.upper() == 'PS5':
package_path = package_path_main_orbis
if platform.upper() == 'XBOXONE' or platform.upper() == 'XBOXSERIES':
package_path = package_path_main_durango
if platform.upper() == 'PC':
package_path = package_path_main_win64
# checkout output file
if not p4.checkout(output_file, changeListNumber):
return False
# Run build command
#cmd = r'%s %s -pack %s -rootpath %s -extignore .rpf -nameheapshift=1' % (rage_builder_path, package_path, output_file, input_pack_path)
cmd = r'%s %s -pack %s -rootpath %s -extignore .rpf' % (rage_builder_path, package_path, output_file, input_pack_path)
logger.log("Running cmd: %s" % cmd, LogSeverity.INFO)
if os.system(cmd) != 0:
logger.log("Failed to successfully run command: %s" % cmd, LogSeverity.ERROR)
return False
pack_list_file_main.close()
pack_list_file_release.close()
return True
def pack_requires_build(pack_path, pack_name):
global sfx_output_path
rpf_path = '%s.rpf' % os.path.join(sfx_output_path, pack_name)
# If output file doesn't exist, it needs to be built
if not os.path.exists(rpf_path):
return True
for file in os.listdir(pack_path):
# Only interested in AWC files
if file.lower().endswith('.awc'):
awc_path = os.path.join(pack_path, file)
# If AWC file is newer than RPF file, it needs to be built
if os.stat(awc_path).st_mtime > os.stat(rpf_path).st_mtime:
return True
return False
def generate_audio_rpf(platform, changeListNumber, release=False):
global audio_path
global audio_output_path
global rage_builder_path
global package_path
global p4
root_path = os.path.join(audio_path, platform)
if release:
output_file = 'audio_rel.rpf'
if platform.upper() == 'PS4' or platform.upper() == 'PS5':
package_path = package_path_release_orbis
if platform.upper() == 'XBOXONE' or platform.upper() == 'XBOXSERIES':
package_path = package_path_release_durango
if platform.upper() == 'PC':
package_path = package_path_release_win64
else:
output_file = 'audio.rpf'
if platform.upper() == 'PS4' or platform.upper() == 'PS5':
package_path = package_path_main_orbis
if platform.upper() == 'XBOXONE' or platform.upper() == 'XBOXSERIES':
package_path = package_path_main_durango
if platform.upper() == 'PC':
package_path = package_path_main_win64
pack = os.path.join(audio_output_path, output_file)
# checkout output file
if not p4.checkout(pack, changeListNumber):
return False
cmd = r'%s %s -pack %s -rootpath %s\ -extignore .awc' % (rage_builder_path, package_path, pack, root_path)
#cmd = r'%s %s -pack %s -rootpath %s\ -extignore .awc -nameheapshift=1' % (rage_builder_path, package_path, pack, root_path)
logger.log("Running cmd: %s" % cmd, LogSeverity.INFO)
if os.system(cmd) != 0:
logger.log("Failed to successfully run command: %s" % cmd, LogSeverity.ERROR)
return False
return True
def set_working_path(path):
global working_path
# Change working path if required
if working_path != path:
working_path = path
msg = "Setting working path as %s" % path
logger.log(msg, LogSeverity.INFO)
os.chdir(path)
def failed():
logger.save()