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()