You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
8.4 KiB
Python
211 lines
8.4 KiB
Python
"""
|
|
Copyright (c) Contributors to the Open 3D Engine Project.
|
|
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
|
|
SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
"""
|
|
|
|
from argparse import ArgumentParser
|
|
import glob
|
|
import json
|
|
import os
|
|
import sys
|
|
from xml.etree import ElementTree
|
|
|
|
|
|
__version__ = '0.1.0'
|
|
__copyright__ = 'Copyright (c) Contributors to the Open 3D Engine Project.\nFor complete copyright and license terms please see the LICENSE at the root of this distribution.'
|
|
|
|
metadata_file_extension = '.bankdeps'
|
|
metadata_version = '1.0'
|
|
init_bank_path = 'Init.bnk'
|
|
no_info_xml_error = 'SoundBanksInfo.xml does not exist, and there is more than ' \
|
|
'one bank (aside from Init.bnk), so complete dependency info of banks cannot be ' \
|
|
'determined. Please ensure "Project > Project Settings > SoundBanks > Generate ' \
|
|
'Metadata File" is enabled in your Wwise project to generate complete ' \
|
|
'dependencies. Limited dependency info will be generated.'
|
|
no_init_bank_error = 'There is no Init.bnk that exists at path {}. Init.bnk is ' \
|
|
'necessary for other soundbanks to work properly. Please regenerate soundbanks ' \
|
|
'from the Wwise project.'
|
|
|
|
|
|
class Bank:
|
|
def __init__(self, name):
|
|
self.path = name
|
|
self.embedded_media = [] # set of short ids for embedded media
|
|
self.streamed_media = [] # set of short ids for media being streamed
|
|
self.excluded_media = [] # set of short ids for media that is embedded in other banks
|
|
self.included_events = [] # set of names of events that are included in the bank.
|
|
self.metadata_object = {} # object that will be serialized to JSON for this bank
|
|
self.metadata_path = "" # path to serialize the metadata object to
|
|
|
|
|
|
def parse_args():
|
|
parser = ArgumentParser(description='Generate metadata files for all banks referenced in a SoundBankInfo.xml')
|
|
parser.add_argument('soundbank_info_path', help='Path to a SoundBankInfo.xml to parse')
|
|
parser.add_argument('output_path', help='Path that the banks have been output to, where these metadata files will live as well.')
|
|
options = parser.parse_args()
|
|
|
|
if not os.path.exists(options.output_path):
|
|
sys.exit('Output path {} does not exist'.format(options.output_path))
|
|
|
|
return options
|
|
|
|
|
|
def parse_bank_info_xml(root):
|
|
sound_banks_element = root.find('SoundBanks')
|
|
banks = []
|
|
for bank_element in sound_banks_element.iter('SoundBank'):
|
|
bank_path = bank_element.find('Path').text
|
|
# If this is the init bank, then skip as it doesn't need an entry, as the init bank does not need metadata
|
|
if bank_path == init_bank_path:
|
|
continue
|
|
|
|
bank = Bank(bank_path)
|
|
for embedded_file in bank_element.findall('IncludedMemoryFiles/File'):
|
|
bank.embedded_media.append(embedded_file.get('Id'))
|
|
for streamed_file in bank_element.findall('ReferencedStreamedFiles/File'):
|
|
bank.streamed_media.append(streamed_file.get('Id'))
|
|
for excluded_file in bank_element.findall('ExcludedMemoryFiles/File'):
|
|
bank.excluded_media.append(excluded_file.get('Id'))
|
|
for event_name in bank_element.findall('IncludedEvents/Event'):
|
|
bank.included_events.append(event_name.get('Name'))
|
|
|
|
for embedded_file in event_name.findall('IncludedMemoryFiles/File'):
|
|
bank.embedded_media.append(embedded_file.get('Id'))
|
|
|
|
for streamed_file in event_name.findall('ReferencedStreamedFiles/File'):
|
|
bank.streamed_media.append(streamed_file.get('Id'))
|
|
|
|
for excluded_file in event_name.findall('ExcludedMemoryFiles/File'):
|
|
bank.excluded_media.append(excluded_file.get('Id'))
|
|
|
|
banks.append(bank)
|
|
return banks
|
|
|
|
|
|
def make_banks_from_file_paths(bank_paths):
|
|
return [Bank(bank) for bank in bank_paths]
|
|
|
|
|
|
def build_media_to_bank_dictionary(banks):
|
|
media_to_banks = {}
|
|
for bank in banks:
|
|
for short_id in bank.embedded_media:
|
|
media_to_banks[short_id] = bank
|
|
|
|
return media_to_banks
|
|
|
|
|
|
def serialize_metadata_list(banks):
|
|
for bank in banks:
|
|
# generate metadata json file
|
|
with open(bank.metadata_path, 'w') as metadata_file:
|
|
json.dump(bank.metadata_object, metadata_file, indent=4)
|
|
|
|
|
|
def generate_default_metadata_path_and_object(bank_path, output_path):
|
|
metadata_file_path = os.path.join(output_path, bank_path)
|
|
metadata_file_path = os.path.splitext(metadata_file_path)[0] + metadata_file_extension
|
|
|
|
metadata = dict()
|
|
metadata['version'] = metadata_version
|
|
metadata['bankName'] = bank_path
|
|
return metadata_file_path, metadata
|
|
|
|
|
|
def generate_bank_metadata(banks, media_dictionary, output_path):
|
|
for bank in banks:
|
|
# Determine path for metadata file.
|
|
metadata_file_path, metadata = generate_default_metadata_path_and_object(bank.path, output_path)
|
|
|
|
# Determine paths for each of the streamed files.
|
|
dependencies = set()
|
|
for short_id in bank.streamed_media:
|
|
dependencies.add(str.format("{}.wem", short_id))
|
|
|
|
# Any media that has been excluded from this bank and embedded in another, add that bank as a dependency
|
|
for short_id in bank.excluded_media:
|
|
dependencies.add(media_dictionary[short_id].path)
|
|
|
|
# Force a dependency on the init bank.
|
|
dependencies.add(init_bank_path)
|
|
metadata['dependencies'] = list(dependencies)
|
|
|
|
metadata['includedEvents'] = bank.included_events
|
|
|
|
# Push the data generated bank into the bank to be used later (by tests or by serializer).
|
|
bank.metadata_object = metadata
|
|
bank.metadata_path = metadata_file_path
|
|
|
|
return banks
|
|
|
|
|
|
def register_wems_as_streamed_file_dependencies(bank, output_path):
|
|
for wem_file in glob.glob1(output_path, '*.wem'):
|
|
bank.streamed_media.append(os.path.splitext(wem_file)[0])
|
|
|
|
|
|
def generate_metadata(soundbank_info_path, output_path):
|
|
bank_paths = glob.glob1(output_path, '*.bnk')
|
|
soundbank_xml_exists = os.path.exists(soundbank_info_path)
|
|
error_code = 0
|
|
banks_with_metadata = dict()
|
|
init_bank_exists = init_bank_path in bank_paths
|
|
|
|
if init_bank_exists:
|
|
bank_paths.remove(init_bank_path)
|
|
else:
|
|
print(str.format(no_init_bank_error, output_path))
|
|
error_code = max(error_code, 1)
|
|
|
|
# Check to see if the soundbankinfo file exists. If it doesn't then there are no streamed files.
|
|
if soundbank_xml_exists:
|
|
root = ElementTree.parse(soundbank_info_path).getroot()
|
|
banks = parse_bank_info_xml(root)
|
|
media_dictionary = build_media_to_bank_dictionary(banks)
|
|
banks_with_metadata = generate_bank_metadata(banks, media_dictionary, output_path)
|
|
|
|
# If there are more than two content banks in the directory, then there is
|
|
# no way to generate dependencies properly without the XML.
|
|
# Just generate the dependency on the init bank and generate a warning.
|
|
elif len(bank_paths) > 1:
|
|
print(no_info_xml_error)
|
|
error_code = max(error_code, 1)
|
|
banks = make_banks_from_file_paths(bank_paths)
|
|
media_dictionary = build_media_to_bank_dictionary(banks)
|
|
banks_with_metadata = generate_bank_metadata(banks, media_dictionary, output_path)
|
|
|
|
# There is one content bank, so this bank depends on the init bank and all wem files in the output path
|
|
elif len(bank_paths) == 1:
|
|
banks = make_banks_from_file_paths(bank_paths)
|
|
# populate bank streamed file dependencies with all the wems in the folder.
|
|
register_wems_as_streamed_file_dependencies(banks[0], output_path)
|
|
media_dictionary = build_media_to_bank_dictionary(banks)
|
|
banks_with_metadata = generate_bank_metadata(banks, media_dictionary, output_path)
|
|
|
|
# There were no banks in the directory, and no metadata xml, then we can't generate any dependencies
|
|
elif not init_bank_exists:
|
|
print(str.format(no_init_bank_error, output_path))
|
|
error_code = max(error_code, 2)
|
|
|
|
return banks_with_metadata, error_code
|
|
|
|
|
|
def main():
|
|
print('Wwise Bank Info Parser v{}'.format(__version__))
|
|
print(__copyright__)
|
|
print()
|
|
|
|
args = parse_args()
|
|
banks, error_code = generate_metadata(args.soundbank_info_path, args.output_path)
|
|
|
|
if banks is not None:
|
|
serialize_metadata_list(banks)
|
|
|
|
return error_code
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|