diff --git a/doc/WWISER.md b/doc/WWISER.md index 7607000..735f779 100644 --- a/doc/WWISER.md +++ b/doc/WWISER.md @@ -644,6 +644,16 @@ Since only the bank version is needed, you can use `00000000 00000000 XXXXXXXX 0 Note that devs could instead encrypt the whole `.bnk` using standard encryption like AES, this is unrelated to Wwise and not covered by this program. +## LOADING REPEATED BANKS +By default *wwiser* allows loading the same bank ID+lang in different dirs multiple times, or the same bank in the same dir with different names (same bank in the same dir is ignored). Real *Wwise* games probably don't do that, plus the engine should ignore repeated IDs, but *wwiser* has some leniency for usability or odd games. + +The most notable case is game updates, where newer `.bnk` may be put in a separate dir or `.pck`. Manually loading newer banks is time consuming, so when loading multiple dirs *wwiser* just accepts everything. This works fine when dumping contents or wwnames (dupes are taken into account), but affects TXTP generation. + +Repeated banks may have the same IDs but different contents. For example a *music-switch* object in v1.0 may have 10 cases (10 `.txtp`), while v1.1 has 12 cases (12 `.txtp`), but the other way around could be possible. *wwiser* can only use either v1.0 or v1.1, but can't guess which *music-switch* is the newer/better one. + +By default it favors bigger banks first (usually v1.1) while still allowing both. This can be overridden to ignore repeated banks (first only, last only, biggest, etc) for fine tuning. In comple cases you may need to experiment and see if other mores make more `.txtp`. There is nothing stopping tricky devs of reusing a bank ID with completely different contents though, so use with care. + + ## KNOWN ISSUES Bank format may change a bit between major Wwise SDK versions, adding new features or moving fields around. *wwiser* should handle almost all but there are bugs left: - earliest versions used in *Shadowrun (X360)* and *Too Human (X360)* partially supported diff --git a/wwiser/parser/wio.py b/wwiser/parser/wio.py index 2300ea9..dc44cc5 100644 --- a/wwiser/parser/wio.py +++ b/wwiser/parser/wio.py @@ -195,8 +195,8 @@ def skip(self, bytes): def current(self): return self.file.tell() - #def size(self): - # return self.size + def get_size(self): + return self.size def guess_endian32(self, offset): current = self.file.tell() diff --git a/wwiser/parser/wparser.py b/wwiser/parser/wparser.py index 91421ef..ee73cde 100644 --- a/wwiser/parser/wparser.py +++ b/wwiser/parser/wparser.py @@ -3795,6 +3795,26 @@ def parse_chunk(obj): # ############################################################################# class Parser(object): + # when loading multiple banks + MULTIBANK_AUTO = 'auto' + MULTIBANK_MANUAL = 'manual-all' + MULTIBANK_FIRST = 'first' + MULTIBANK_LAST = 'last' + MULTIBANK_BIGGEST = 'biggest' + MULTIBANK_BIGGEST_LAST = 'biggest+last' + MULTIBANK_SMALLEST = 'smallest' + + #MULTIBANK_NEWEST = 6 # latest timestamp (useful?) + MULTIBANK_MODES = [ + MULTIBANK_AUTO, + MULTIBANK_MANUAL, + MULTIBANK_FIRST, + MULTIBANK_LAST, + MULTIBANK_BIGGEST, + MULTIBANK_BIGGEST_LAST, + MULTIBANK_SMALLEST, + ] + def __init__(self): #self._ignore_version = ignore_version self._banks = {} @@ -3958,12 +3978,79 @@ def _process(self, r, filename): if self._names: bank.set_names(self._names) - self._banks[filename] = bank + root = bank.get_root() + sid = root.get_id() + lang = root.get_lang() + size = r.get_size() + + self._banks[filename] = (bank, sid, lang, size) return None + def get_banks(self, mode=None): + if not mode: + mode = self.MULTIBANK_AUTO + + if mode not in self.MULTIBANK_MODES: + logging.warning("parser: WARNING, unknown repeat mode '%s'" % (mode)) + + # as loaded (MULTIBANK_ALLOW_MANUAL) + items = self._banks.values() + + done = {} + banks = [] + for bank, version, lang, size in items: + + key = (version, lang) + if key not in done: + # not a dupe = always include + done[key] = (bank, size) + banks.append(bank) + continue + + # handle dupe + old_bank, old_size = done.get(key) + index = banks.index(old_bank) + + + # allow as loaded + if mode == self.MULTIBANK_MANUAL: + banks.append(bank) + + # allow, bigger first + if mode == self.MULTIBANK_AUTO: + if size > old_size: + banks.insert(index, bank) #before + else: + banks.append(bank) #after (tail) + + # ignore current + if mode == self.MULTIBANK_FIRST: + pass + + # overwrite old + if mode == self.MULTIBANK_LAST: + banks[index] = bank + done[key] = (bank, size) + + # favor bigger + if mode == self.MULTIBANK_BIGGEST: + if size > old_size: + banks[index] = bank #overwrite + done[key] = (bank, size) + + # favor bigger, or last (same size = overwrite) + # (generally useless except when fine-tuning which clone banks are preferred, for -fc + multiple updates) + if mode == self.MULTIBANK_BIGGEST_LAST: + if size >= old_size: + banks[index] = bank #overwrite + done[key] = (bank, size) + + # favor smaller + if mode == self.MULTIBANK_SMALLEST: + if size < old_size: + banks[index] = bank #overwrite + done[key] = (bank, size) - def get_banks(self): - banks = list(self._banks.values()) return banks def get_filenames(self): @@ -3971,7 +4058,8 @@ def get_filenames(self): def set_names(self, names): self._names = names - for bank in self._banks.values(): + for items in self._banks.values(): + bank = items[0] bank.set_names(names) #def set_ignore_version(self, value): @@ -3979,7 +4067,7 @@ def set_names(self, names): def unload_bank(self, filename): if filename not in self._banks: - logging.warn("parser: can't unload " + filename) + logging.warning("parser: can't unload " + filename) return logging.info("parser: unloading " + filename) diff --git a/wwiser/wcli.py b/wwiser/wcli.py index e7d90f9..59c1bee 100644 --- a/wwiser/wcli.py +++ b/wwiser/wcli.py @@ -51,6 +51,7 @@ def _parse_init(self): p.add_argument('-vp', '--viewer-port', help="Set the viewer port", metavar='PORT', default=wview.DEFAULT_PORT) #p.add_argument('-iv', '--ignore-version', help="Ignore bank version check", action='store_true') p.add_argument('-sl', '--save-lst', help="Clean wwnames.txt and include missing hashnames\n(needs dump set)", action='store_true') + p.add_argument('-br', '--bank-repeat', help="Override repeated banks handling:\n manual / first / last / smallest / biggest / biggest+last") p = parser.add_argument_group('txtp options') p.add_argument('-g', '--txtp', help="Generate TXTP", action='store_true') @@ -268,7 +269,7 @@ def _execute(self, args, filenames): parser = wparser.Parser() #parser.set_ignore_version(args.ignore_version) parser.parse_banks(filenames) - banks = parser.get_banks() + banks = parser.get_banks(args.bank_repeat) # load names diff --git a/wwiser/wgui.py b/wwiser/wgui.py index 2263ea1..1369d50 100644 --- a/wwiser/wgui.py +++ b/wwiser/wgui.py @@ -322,6 +322,15 @@ def _setup_options(self): frame.grid_columnconfigure(0, minsize=80) #weight 2 = x2 of others row = 0 + cmb = self._cmb('bank_repeat', frame, 'Repeats:', 'How different banks with same ID + language (such as updates) are handled') + cmb[0].grid(row=row, column=0, sticky=tk.E) + cmb[1].grid(row=row, column=1, sticky=tk.W) + cmb[2].grid(row=row, column=2, sticky=tk.W) + + items = self._fields["bank_repeat"] + items['values'] = [''] + wparser.Parser.MULTIBANK_MODES #self.parser.MULTIBANK_MODES + + row += 1 cmb = self._cmb('txtp_lang', frame, 'Language:', 'Current language (select when loading multiple localized banks or set to SFX to skip all localized banks)') cmb[0].grid(row=row, column=0, sticky=tk.E) @@ -855,8 +864,11 @@ def _process(self, **args): #raise def _process_main(self, make_txtp=False, make_tags=False, make_clean=False): + repeat_mode = self._get_list('bank_repeat') + if repeat_mode: #TODO: better way to handle + repeat_mode = repeat_mode[0] - banks = self.parser.get_banks() + banks = self.parser.get_banks(repeat_mode) if not banks: messagebox.showerror('Error', 'Load one or more banks') return