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