@geheimgang188/fmod-service-api
Version:
FMOD service API
276 lines • 23.6 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FmodPlayer = void 0;
const fmod_types_1 = require("./fmod-types");
const tiny_typed_emitter_1 = require("tiny-typed-emitter");
class FmodPlayer extends tiny_typed_emitter_1.TypedEmitter {
constructor(api, bankDir, logger) {
super();
this._initCalled = false;
this._firstConnection = true;
this._loadedBanksByName = new Map();
this._localisedBanks = new Set();
this._languages = new Set();
this._eventsByName = new Map();
this._globalParamsByName = new Map();
this._api = api;
this._logger = logger;
this._banks = new fmod_types_1.FmodBank(bankDir);
}
/**
* Returns the current language, if localisation is enabled, and undefined otherwise.
*/
get currentLanguage() {
return this._currentLanguage;
}
/**
* Returns all events that have been registered for this player.
*/
get allEvents() {
return Array.from(this._eventsByName.values());
}
/**
* Returns all global parameters that have been registered for this player.
*/
get allGlobalParameters() {
return Array.from(this._globalParamsByName.values());
}
/**
* Initialise the player by connecting it to the FMOD service
* using the provided API.
*/
init() {
var _a, _b;
if (this._initCalled)
throw new Error('init() already called');
this._initCalled = true;
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.info('Initialising FMOD Player');
for (const event of this._eventsByName.values()) {
event.init(this._api, this);
}
for (const param of this._globalParamsByName.values()) {
param.init('global', this._api);
}
(_b = this._logger) === null || _b === void 0 ? void 0 : _b.info('Connecting to FMOD and loading banks');
this._api.connect();
this._api.once('connect', () => this.initBanks().catch(err => { var _a, _b; return (_a = this._logger) === null || _a === void 0 ? void 0 : _a.error(`Error initialising banks: ${(_b = err === null || err === void 0 ? void 0 : err.message) !== null && _b !== void 0 ? _b : err}`); }));
this._api.on('reconnect', () => this.handleReconnect().catch(err => { var _a, _b; return (_a = this._logger) === null || _a === void 0 ? void 0 : _a.error(`Error trying to reconnect: ${(_b = err === null || err === void 0 ? void 0 : err.message) !== null && _b !== void 0 ? _b : err}`); }));
this._api.on('disconnect', () => this.handleDisconnect());
}
/**
* Close the API connection.
*/
close() {
this._api.disconnect();
}
ensureBankLoaded(bankName) {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b;
if (this._loadedBanksByName.has(bankName)) {
return;
}
let bankToLoad = bankName;
const isLocalised = this.isLocalisedBank(bankName);
if (isLocalised) {
if (this._currentLanguage === undefined) {
throw new Error(`Language not initialised!`);
}
bankToLoad = this._banks.localisedBankName(bankName, this._currentLanguage);
}
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.info(`Bank ${bankName} not loaded, loading ${isLocalised ? 'localised ' : ''}bank ${bankToLoad} now`);
try {
yield this._api.loadBank(this._banks.bankPath(bankToLoad));
}
catch (err) {
if ((err === null || err === void 0 ? void 0 : err.message.indexOf('FMOD Exception 70')) >= 0) {
// No issue, bank is loaded already, nothing more to do.
(_b = this._logger) === null || _b === void 0 ? void 0 : _b.debug(`Bank ${bankName} is already loaded.`);
}
else {
throw err;
}
}
this._loadedBanksByName.set(bankName, { bankName, isLocalised, loadedLanguage: this._currentLanguage });
});
}
ensureBankUnloaded(bankName, force) {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b, _c, _d;
if (!this._loadedBanksByName.has(bankName) && !force) {
return;
}
const isLocalisedBank = this.isLocalisedBank(bankName);
if (isLocalisedBank) {
const bankInfo = this._loadedBanksByName.get(bankName);
// If we know what language is loaded, unload it.
// If not, any could be loaded, so unload all.
if ((bankInfo === null || bankInfo === void 0 ? void 0 : bankInfo.loadedLanguage) !== undefined) {
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.debug(`Unloading localised bank ${bankName} in language ${bankInfo.loadedLanguage}`);
yield this._api.unloadBank(this._banks.localisedBankPath(bankName, bankInfo.loadedLanguage));
}
else {
(_b = this._logger) === null || _b === void 0 ? void 0 : _b.debug(`Unloading all localised banks for ${bankName} because currently loaded language is unknown`);
for (const lang of this._languages) {
(_c = this._logger) === null || _c === void 0 ? void 0 : _c.debug(`Unloading localised bank ${bankName} in language ${lang}`);
yield this._api.unloadBank(this._banks.localisedBankPath(bankName, lang));
}
}
}
else {
(_d = this._logger) === null || _d === void 0 ? void 0 : _d.debug(`Unloading non-localised bank ${bankName}`);
yield this._api.unloadBank(this._banks.bankPath(bankName));
}
this._loadedBanksByName.delete(bankName);
});
}
/**
*
* @param bankNames Banks which are localised. If e.g. Voice is localised, the player will load
* Voice_en, Voice_de etc. according to the languages that have been configured.
* @param languages Languages supported by the localised banks. This is the Locale Code in FMOD,
* not the full name of the language.
* @param initialLanguage Default language to use when none has been configured.
*/
configureLocalisation(bankNames, languages, initialLanguage) {
if (this._initCalled)
throw new Error('Localisation must be configured before initialisation.');
if (initialLanguage === undefined)
throw new Error('Initial language must be provided.');
if (languages.indexOf(initialLanguage) < 0)
throw new Error('Initial language must be a valid language.');
bankNames.forEach((bankName) => this._localisedBanks.add(bankName));
languages.forEach(language => this._languages.add(language));
this._defaultLanguage = initialLanguage;
}
/**
* Set a new language. The language must have been configured in the localisation setup.
*
* Loading a new language causes localised banks to be unloaded in the previous language
* and reloaded in the new language.
*/
setLanguage(language) {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b;
if (!this._languages.has(language))
throw new Error(`Invalid language ${language}, not configured.`);
this._currentLanguage = language;
for (const [bankName, bankInfo] of this._loadedBanksByName.entries()) {
if (bankInfo.isLocalised) {
if (bankInfo.loadedLanguage !== language) {
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.debug(`Unloading bank ${bankName} because its language is ${(_b = bankInfo.loadedLanguage) !== null && _b !== void 0 ? _b : 'unknown'}`);
yield this.ensureBankUnloaded(bankName, true);
yield this.ensureBankLoaded(bankName);
}
}
}
});
}
resetAllParameters() {
return __awaiter(this, void 0, void 0, function* () {
for (const event of this._eventsByName.values()) {
for (const param of event.params) {
yield param.setDefaultValue();
}
}
for (const param of this.allGlobalParameters) {
yield param.setDefaultValue();
}
});
}
/**
* Stops **all** running events, including snapshots.
*/
stopAllEvents() {
return this._api.stopStartedEvents();
}
/**
* Retrieves an event by its name (event path).
*/
getEvent(eventName) {
const event = this._eventsByName.get(eventName);
if (event === undefined) {
throw new Error(`Event ${eventName} is not registered.`);
}
return event;
}
/**
* Retrieves a global parameter by name (parameter path).
*/
getGlobalParameter(paramName) {
const param = this._globalParamsByName.get(paramName);
if (param === undefined) {
throw new Error(`Parameter ${paramName} is not registered as global parameter.`);
}
return param;
}
/**
* Registers an event so it can later on be retrieved with the `getEvent()` function.
*/
registerEvent(event) {
if (this._eventsByName.has(event.eventName))
throw new Error(`Event ${event.eventName} is already registered.`);
this._eventsByName.set(event.eventName, event);
}
/**
* Registers a global parameter so it can later on be retrieved with the `getGlobalParameter()` function.
*/
registerGlobalParam(param) {
if (this._globalParamsByName.has(param.name))
throw new Error(`Global parameter ${param.name} is already registered.`);
this._globalParamsByName.set(param.name, param);
}
initBanks() {
return __awaiter(this, void 0, void 0, function* () {
this._loadedBanksByName.clear();
const loadedBanks = yield this._api.listLoadedBankPaths();
for (const bankName of loadedBanks) {
// If a bank is localised, we don't know which one is currently loaded
// as they all have the same path.
this._loadedBanksByName.set(bankName, {
bankName,
isLocalised: this.isLocalisedBank(bankName),
loadedLanguage: undefined,
});
}
// Master banks always have to be loaded.
yield this.ensureBankLoaded('Master');
yield this.ensureBankLoaded('Master.strings');
if (this._defaultLanguage !== undefined) {
yield this.setLanguage(this._defaultLanguage);
}
if (this._firstConnection) {
this._firstConnection = false;
setImmediate(() => this.emit('init'));
}
else {
setImmediate(() => this.emit('reconnect'));
}
});
}
handleReconnect() {
return __awaiter(this, void 0, void 0, function* () {
var _a;
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.warn(`Reconnected. Resetting loaded banks and initialising banks again.`);
this._currentLanguage = undefined;
yield this.initBanks();
});
}
handleDisconnect() {
var _a;
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.warn('FMOD API disconnected.');
}
isLocalisedBank(bankName) {
return this._localisedBanks.has(bankName);
}
}
exports.FmodPlayer = FmodPlayer;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZm1vZC1wbGF5ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYXBpLWdlbmVyYXRvci9mbW9kLXBsYXllci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7QUFBQSw2Q0FBdUQ7QUFDdkQsMkRBQWtEO0FBbUJsRCxNQUFzQixVQUE2RCxTQUFRLGlDQUE4QjtJQXdCckgsWUFBdUIsR0FBYSxFQUFFLE9BQWUsRUFBRSxNQUFnQjtRQUNuRSxLQUFLLEVBQUUsQ0FBQztRQWRKLGdCQUFXLEdBQUcsS0FBSyxDQUFDO1FBQ3BCLHFCQUFnQixHQUFHLElBQUksQ0FBQztRQUtmLHVCQUFrQixHQUFnQyxJQUFJLEdBQUcsRUFBRSxDQUFDO1FBRTVELG9CQUFlLEdBQWdCLElBQUksR0FBRyxFQUFFLENBQUM7UUFDekMsZUFBVSxHQUFnQixJQUFJLEdBQUcsRUFBRSxDQUFDO1FBQ3BDLGtCQUFhLEdBQTJCLElBQUksR0FBRyxFQUFFLENBQUM7UUFDbEQsd0JBQW1CLEdBQStCLElBQUksR0FBRyxFQUFFLENBQUM7UUFJekUsSUFBSSxDQUFDLElBQUksR0FBRyxHQUFHLENBQUM7UUFDaEIsSUFBSSxDQUFDLE9BQU8sR0FBRyxNQUFNLENBQUM7UUFDdEIsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLHFCQUFRLENBQUUsT0FBTyxDQUFFLENBQUM7SUFDMUMsQ0FBQztJQUVEOztPQUVHO0lBQ0gsSUFBSSxlQUFlO1FBQ2YsT0FBTyxJQUFJLENBQUMsZ0JBQWdCLENBQUM7SUFDakMsQ0FBQztJQUVEOztPQUVHO0lBQ0gsSUFBSSxTQUFTO1FBQ1QsT0FBTyxLQUFLLENBQUMsSUFBSSxDQUFFLElBQUksQ0FBQyxhQUFhLENBQUMsTUFBTSxFQUFFLENBQUUsQ0FBQztJQUNyRCxDQUFDO0lBRUQ7O09BRUc7SUFDSCxJQUFJLG1CQUFtQjtRQUNuQixPQUFPLEtBQUssQ0FBQyxJQUFJLENBQUUsSUFBSSxDQUFDLG1CQUFtQixDQUFDLE1BQU0sRUFBRSxDQUFFLENBQUM7SUFDM0QsQ0FBQztJQUVEOzs7T0FHRztJQUNILElBQUk7O1FBQ0EsSUFBSyxJQUFJLENBQUMsV0FBVztZQUFHLE1BQU0sSUFBSSxLQUFLLENBQUUsdUJBQXVCLENBQUUsQ0FBQztRQUNuRSxJQUFJLENBQUMsV0FBVyxHQUFHLElBQUksQ0FBQztRQUV4QixNQUFBLElBQUksQ0FBQyxPQUFPLDBDQUFFLElBQUksQ0FBRSwwQkFBMEIsQ0FBRSxDQUFDO1FBQ2pELEtBQU0sTUFBTSxLQUFLLElBQUksSUFBSSxDQUFDLGFBQWEsQ0FBQyxNQUFNLEVBQUUsRUFBRyxDQUFDO1lBQ2hELEtBQUssQ0FBQyxJQUFJLENBQUUsSUFBSSxDQUFDLElBQUksRUFBRSxJQUFJLENBQUUsQ0FBQztRQUNsQyxDQUFDO1FBRUQsS0FBTSxNQUFNLEtBQUssSUFBSSxJQUFJLENBQUMsbUJBQW1CLENBQUMsTUFBTSxFQUFFLEVBQUcsQ0FBQztZQUN0RCxLQUFLLENBQUMsSUFBSSxDQUFFLFFBQVEsRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFFLENBQUM7UUFDdEMsQ0FBQztRQUVELE1BQUEsSUFBSSxDQUFDLE9BQU8sMENBQUUsSUFBSSxDQUFFLHNDQUFzQyxDQUFFLENBQUM7UUFDN0QsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUVwQixJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBRSxTQUFTLEVBQUUsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxDQUFDLEtBQUssQ0FBRSxHQUFHLENBQUMsRUFBRSxlQUFDLE9BQUEsTUFBQSxJQUFJLENBQUMsT0FBTywwQ0FBRSxLQUFLLENBQUUsNkJBQTZCLE1BQUEsR0FBRyxhQUFILEdBQUcsdUJBQUgsR0FBRyxDQUFFLE9BQU8sbUNBQUksR0FBRyxFQUFFLENBQUUsQ0FBQSxFQUFBLENBQUUsQ0FBRSxDQUFDO1FBRTlJLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFFLFdBQVcsRUFBRSxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsZUFBZSxFQUFFLENBQUMsS0FBSyxDQUFFLEdBQUcsQ0FBQyxFQUFFLGVBQUMsT0FBQSxNQUFBLElBQUksQ0FBQyxPQUFPLDBDQUFFLEtBQUssQ0FBRSw4QkFBOEIsTUFBQSxHQUFHLGFBQUgsR0FBRyx1QkFBSCxHQUFHLENBQUUsT0FBTyxtQ0FBSSxHQUFHLEVBQUUsQ0FBRSxDQUFBLEVBQUEsQ0FBRSxDQUFFLENBQUM7UUFFckosSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUUsWUFBWSxFQUFFLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFFLENBQUM7SUFDaEUsQ0FBQztJQUVEOztPQUVHO0lBQ0gsS0FBSztRQUNELElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7SUFDM0IsQ0FBQztJQUVLLGdCQUFnQixDQUFFLFFBQWdCOzs7WUFDcEMsSUFBSyxJQUFJLENBQUMsa0JBQWtCLENBQUMsR0FBRyxDQUFFLFFBQVEsQ0FBRSxFQUFHLENBQUM7Z0JBQzVDLE9BQU87WUFDWCxDQUFDO1lBRUQsSUFBSSxVQUFVLEdBQUcsUUFBUSxDQUFDO1lBQzFCLE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxlQUFlLENBQUUsUUFBUSxDQUFFLENBQUM7WUFDckQsSUFBSyxXQUFXLEVBQUcsQ0FBQztnQkFDaEIsSUFBSyxJQUFJLENBQUMsZ0JBQWdCLEtBQUssU0FBUyxFQUFHLENBQUM7b0JBQ3hDLE1BQU0sSUFBSSxLQUFLLENBQUUsMkJBQTJCLENBQUUsQ0FBQztnQkFDbkQsQ0FBQztnQkFDRCxVQUFVLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxpQkFBaUIsQ0FBRSxRQUFRLEVBQUUsSUFBSSxDQUFDLGdCQUFnQixDQUFFLENBQUM7WUFDbEYsQ0FBQztZQUVELE1BQUEsSUFBSSxDQUFDLE9BQU8sMENBQUUsSUFBSSxDQUFFLFFBQVEsUUFBUSx3QkFBd0IsV0FBVyxDQUFDLENBQUMsQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLEVBQUUsUUFBUSxVQUFVLE1BQU0sQ0FBRSxDQUFDO1lBQ3RILElBQUksQ0FBQztnQkFDRCxNQUFNLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFFLFVBQVUsQ0FBRSxDQUFFLENBQUM7WUFDbkUsQ0FBQztZQUFDLE9BQVEsR0FBUSxFQUFHLENBQUM7Z0JBQ2xCLElBQUssQ0FBQSxHQUFHLGFBQUgsR0FBRyx1QkFBSCxHQUFHLENBQUUsT0FBTyxDQUFDLE9BQU8sQ0FBRSxtQkFBbUIsQ0FBRSxLQUFJLENBQUMsRUFBRyxDQUFDO29CQUNyRCx3REFBd0Q7b0JBQ3hELE1BQUEsSUFBSSxDQUFDLE9BQU8sMENBQUUsS0FBSyxDQUFFLFFBQVEsUUFBUSxxQkFBcUIsQ0FBRSxDQUFDO2dCQUNqRSxDQUFDO3FCQUFNLENBQUM7b0JBQ0osTUFBTSxHQUFHLENBQUM7Z0JBQ2QsQ0FBQztZQUNMLENBQUM7WUFFRCxJQUFJLENBQUMsa0JBQWtCLENBQUMsR0FBRyxDQUFFLFFBQVEsRUFBRSxFQUFFLFFBQVEsRUFBRSxXQUFXLEVBQUUsY0FBYyxFQUFFLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFFLENBQUM7UUFDOUcsQ0FBQztLQUFBO0lBRUssa0JBQWtCLENBQUUsUUFBZ0IsRUFBRSxLQUFjOzs7WUFDdEQsSUFBSyxDQUFDLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxHQUFHLENBQUUsUUFBUSxDQUFFLElBQUksQ0FBQyxLQUFLLEVBQUcsQ0FBQztnQkFDdkQsT0FBTztZQUNYLENBQUM7WUFFRCxNQUFNLGVBQWUsR0FBRyxJQUFJLENBQUMsZUFBZSxDQUFFLFFBQVEsQ0FBRSxDQUFDO1lBQ3pELElBQUssZUFBZSxFQUFHLENBQUM7Z0JBQ3BCLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxHQUFHLENBQUUsUUFBUSxDQUFFLENBQUM7Z0JBRXpELGlEQUFpRDtnQkFDakQsOENBQThDO2dCQUM5QyxJQUFLLENBQUEsUUFBUSxhQUFSLFFBQVEsdUJBQVIsUUFBUSxDQUFFLGNBQWMsTUFBSyxTQUFTLEVBQUcsQ0FBQztvQkFDM0MsTUFBQSxJQUFJLENBQUMsT0FBTywwQ0FBRSxLQUFLLENBQUUsNEJBQTRCLFFBQVEsZ0JBQWdCLFFBQVEsQ0FBQyxjQUFjLEVBQUUsQ0FBRSxDQUFDO29CQUNyRyxNQUFNLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsaUJBQWlCLENBQUUsUUFBUSxFQUFFLFFBQVEsQ0FBQyxjQUFjLENBQUUsQ0FBRSxDQUFDO2dCQUNyRyxDQUFDO3FCQUFNLENBQUM7b0JBQ0osTUFBQSxJQUFJLENBQUMsT0FBTywwQ0FBRSxLQUFLLENBQUUscUNBQXFDLFFBQVEsK0NBQStDLENBQUUsQ0FBQztvQkFDcEgsS0FBTSxNQUFNLElBQUksSUFBSSxJQUFJLENBQUMsVUFBVSxFQUFHLENBQUM7d0JBQ25DLE1BQUEsSUFBSSxDQUFDLE9BQU8sMENBQUUsS0FBSyxDQUFFLDRCQUE0QixRQUFRLGdCQUFnQixJQUFJLEVBQUUsQ0FBRSxDQUFDO3dCQUNsRixNQUFNLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsaUJBQWlCLENBQUUsUUFBUSxFQUFFLElBQUksQ0FBRSxDQUFFLENBQUM7b0JBQ2xGLENBQUM7Z0JBQ0wsQ0FBQztZQUNMLENBQUM7aUJBQU0sQ0FBQztnQkFDSixNQUFBLElBQUksQ0FBQyxPQUFPLDBDQUFFLEtBQUssQ0FBRSxnQ0FBZ0MsUUFBUSxFQUFFLENBQUUsQ0FBQztnQkFDbEUsTUFBTSxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBRSxJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBRSxRQUFRLENBQUUsQ0FBRSxDQUFDO1lBQ25FLENBQUM7WUFFRCxJQUFJLENBQUMsa0JBQWtCLENBQUMsTUFBTSxDQUFFLFFBQVEsQ0FBRSxDQUFDO1FBQy9DLENBQUM7S0FBQTtJQUVEOzs7Ozs7O09BT0c7SUFDSCxxQkFBcUIsQ0FBRSxTQUFtQixFQUFFLFNBQW1CLEVBQUUsZUFBdUI7UUFDcEYsSUFBSyxJQUFJLENBQUMsV0FBVztZQUFHLE1BQU0sSUFBSSxLQUFLLENBQUUsd0RBQXdELENBQUUsQ0FBQztRQUNwRyxJQUFLLGVBQWUsS0FBSyxTQUFTO1lBQUcsTUFBTSxJQUFJLEtBQUssQ0FBRSxvQ0FBb0MsQ0FBRSxDQUFDO1FBQzdGLElBQUssU0FBUyxDQUFDLE9BQU8sQ0FBRSxlQUFlLENBQUUsR0FBRyxDQUFDO1lBQUcsTUFBTSxJQUFJLEtBQUssQ0FBRSw0Q0FBNEMsQ0FBRSxDQUFDO1FBRWhILFNBQVMsQ0FBQyxPQUFPLENBQUUsQ0FBRSxRQUFRLEVBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxlQUFlLENBQUMsR0FBRyxDQUFFLFFBQVEsQ0FBRSxDQUFFLENBQUM7UUFDMUUsU0FBUyxDQUFDLE9BQU8sQ0FBRSxRQUFRLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFFLFFBQVEsQ0FBRSxDQUFFLENBQUM7UUFDakUsSUFBSSxDQUFDLGdCQUFnQixHQUFHLGVBQWUsQ0FBQztJQUM1QyxDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDRyxXQUFXLENBQUUsUUFBZ0I7OztZQUMvQixJQUFLLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUUsUUFBUSxDQUFFO2dCQUFHLE1BQU0sSUFBSSxLQUFLLENBQUUsb0JBQW9CLFFBQVEsbUJBQW1CLENBQUUsQ0FBQztZQUUzRyxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsUUFBUSxDQUFDO1lBRWpDLEtBQU0sTUFBTSxDQUFFLFFBQVEsRUFBRSxRQUFRLENBQUUsSUFBSSxJQUFJLENBQUMsa0JBQWtCLENBQUMsT0FBTyxFQUFFLEVBQUcsQ0FBQztnQkFDdkUsSUFBSyxRQUFRLENBQUMsV0FBVyxFQUFHLENBQUM7b0JBQ3pCLElBQUssUUFBUSxDQUFDLGNBQWMsS0FBSyxRQUFRLEVBQUcsQ0FBQzt3QkFDekMsTUFBQSxJQUFJLENBQUMsT0FBTywwQ0FBRSxLQUFLLENBQUUsa0JBQWtCLFFBQVEsNEJBQTRCLE1BQUEsUUFBUSxDQUFDLGNBQWMsbUNBQUksU0FBUyxFQUFFLENBQUUsQ0FBQzt3QkFDcEgsTUFBTSxJQUFJLENBQUMsa0JBQWtCLENBQUUsUUFBUSxFQUFFLElBQUksQ0FBRSxDQUFDO3dCQUVoRCxNQUFNLElBQUksQ0FBQyxnQkFBZ0IsQ0FBRSxRQUFRLENBQUUsQ0FBQztvQkFDNUMsQ0FBQztnQkFDTCxDQUFDO1lBQ0wsQ0FBQztRQUNMLENBQUM7S0FBQTtJQUVLLGtCQUFrQjs7WUFDcEIsS0FBTSxNQUFNLEtBQUssSUFBSSxJQUFJLENBQUMsYUFBYSxDQUFDLE1BQU0sRUFBRSxFQUFHLENBQUM7Z0JBQ2hELEtBQU0sTUFBTSxLQUFLLElBQUksS0FBSyxDQUFDLE1BQU0sRUFBRyxDQUFDO29CQUNqQyxNQUFNLEtBQUssQ0FBQyxlQUFlLEVBQUUsQ0FBQztnQkFDbEMsQ0FBQztZQUNMLENBQUM7WUFDRCxLQUFNLE1BQU0sS0FBSyxJQUFJLElBQUksQ0FBQyxtQkFBbUIsRUFBRyxDQUFDO2dCQUM3QyxNQUFNLEtBQUssQ0FBQyxlQUFlLEVBQUUsQ0FBQztZQUNsQyxDQUFDO1FBQ0wsQ0FBQztLQUFBO0lBRUQ7O09BRUc7SUFDSCxhQUFhO1FBQ1QsT0FBTyxJQUFJLENBQUMsSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUM7SUFDekMsQ0FBQztJQUVEOztPQUVHO0lBQ0gsUUFBUSxDQUFFLFNBQWlCO1FBQ3ZCLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMsR0FBRyxDQUFFLFNBQVMsQ0FBRSxDQUFDO1FBQ2xELElBQUssS0FBSyxLQUFLLFNBQVMsRUFBRyxDQUFDO1lBQ3hCLE1BQU0sSUFBSSxLQUFLLENBQUUsU0FBUyxTQUFTLHFCQUFxQixDQUFFLENBQUM7UUFDL0QsQ0FBQztRQUNELE9BQU8sS0FBSyxDQUFDO0lBQ2pCLENBQUM7SUFFRDs7T0FFRztJQUNILGtCQUFrQixDQUFFLFNBQWlCO1FBQ2pDLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxHQUFHLENBQUUsU0FBUyxDQUFFLENBQUM7UUFDeEQsSUFBSyxLQUFLLEtBQUssU0FBUyxFQUFHLENBQUM7WUFDeEIsTUFBTSxJQUFJLEtBQUssQ0FBRSxhQUFhLFNBQVMseUNBQXlDLENBQUUsQ0FBQztRQUN2RixDQUFDO1FBQ0QsT0FBTyxLQUFLLENBQUM7SUFDakIsQ0FBQztJQUVEOztPQUVHO0lBQ08sYUFBYSxDQUFFLEtBQWdCO1FBQ3JDLElBQUssSUFBSSxDQUFDLGFBQWEsQ0FBQyxHQUFHLENBQUUsS0FBSyxDQUFDLFNBQVMsQ0FBRTtZQUFHLE1BQU0sSUFBSSxLQUFLLENBQUUsU0FBUyxLQUFLLENBQUMsU0FBUyx5QkFBeUIsQ0FBRSxDQUFDO1FBRXRILElBQUksQ0FBQyxhQUFhLENBQUMsR0FBRyxDQUFFLEtBQUssQ0FBQyxTQUFTLEVBQUUsS0FBSyxDQUFFLENBQUM7SUFDckQsQ0FBQztJQUVEOztPQUVHO0lBQ08sbUJBQW1CLENBQUUsS0FBb0I7UUFDL0MsSUFBSyxJQUFJLENBQUMsbUJBQW1CLENBQUMsR0FBRyxDQUFFLEtBQUssQ0FBQyxJQUFJLENBQUU7WUFBRyxNQUFNLElBQUksS0FBSyxDQUFFLG9CQUFvQixLQUFLLENBQUMsSUFBSSx5QkFBeUIsQ0FBRSxDQUFDO1FBRTdILElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxHQUFHLENBQUUsS0FBSyxDQUFDLElBQUksRUFBRSxLQUFLLENBQUUsQ0FBQztJQUN0RCxDQUFDO0lBRWEsU0FBUzs7WUFDbkIsSUFBSSxDQUFDLGtCQUFrQixDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ2hDLE1BQU0sV0FBVyxHQUFHLE1BQU0sSUFBSSxDQUFDLElBQUksQ0FBQyxtQkFBbUIsRUFBRSxDQUFDO1lBRTFELEtBQU0sTUFBTSxRQUFRLElBQUksV0FBVyxFQUFHLENBQUM7Z0JBQ25DLHNFQUFzRTtnQkFDdEUsa0NBQWtDO2dCQUNsQyxJQUFJLENBQUMsa0JBQWtCLENBQUMsR0FBRyxDQUFFLFFBQVEsRUFBRTtvQkFDbkMsUUFBUTtvQkFDUixXQUFXLEVBQUUsSUFBSSxDQUFDLGVBQWUsQ0FBRSxRQUFRLENBQUU7b0JBQzdDLGNBQWMsRUFBRSxTQUFTO2lCQUM1QixDQUFFLENBQUM7WUFDUixDQUFDO1lBRUQseUNBQXlDO1lBQ3pDLE1BQU0sSUFBSSxDQUFDLGdCQUFnQixDQUFFLFFBQVEsQ0FBRSxDQUFDO1lBQ3hDLE1BQU0sSUFBSSxDQUFDLGdCQUFnQixDQUFFLGdCQUFnQixDQUFFLENBQUM7WUFFaEQsSUFBSyxJQUFJLENBQUMsZ0JBQWdCLEtBQUssU0FBUyxFQUFHLENBQUM7Z0JBQ3hDLE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBRSxJQUFJLENBQUMsZ0JBQWdCLENBQUUsQ0FBQztZQUNwRCxDQUFDO1lBRUQsSUFBSyxJQUFJLENBQUMsZ0JBQWdCLEVBQUcsQ0FBQztnQkFDMUIsSUFBSSxDQUFDLGdCQUFnQixHQUFHLEtBQUssQ0FBQztnQkFDOUIsWUFBWSxDQUFFLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUUsTUFBTSxDQUFFLENBQUUsQ0FBQztZQUM5QyxDQUFDO2lCQUFNLENBQUM7Z0JBQ0osWUFBWSxDQUFFLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUUsV0FBVyxDQUFFLENBQUUsQ0FBQztZQUNuRCxDQUFDO1FBQ0wsQ0FBQztLQUFBO0lBRWEsZUFBZTs7O1lBQ3pCLE1BQUEsSUFBSSxDQUFDLE9BQU8sMENBQUUsSUFBSSxDQUFFLG1FQUFtRSxDQUFFLENBQUM7WUFDMUYsSUFBSSxDQUFDLGdCQUFnQixHQUFHLFNBQVMsQ0FBQztZQUNsQyxNQUFNLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztRQUMzQixDQUFDO0tBQUE7SUFFTyxnQkFBZ0I7O1FBQ3BCLE1BQUEsSUFBSSxDQUFDLE9BQU8sMENBQUUsSUFBSSxDQUFFLHdCQUF3QixDQUFFLENBQUM7SUFDbkQsQ0FBQztJQUVPLGVBQWUsQ0FBRSxRQUFnQjtRQUNyQyxPQUFPLElBQUksQ0FBQyxlQUFlLENBQUMsR0FBRyxDQUFFLFFBQVEsQ0FBRSxDQUFDO0lBQ2hELENBQUM7Q0FDSjtBQTlSRCxnQ0E4UkMifQ==