From e2703b45fcc77d97a5c0b296c159aa183cf3199a Mon Sep 17 00:00:00 2001 From: Michael Green <84688932+michael-j-green@users.noreply.github.com> Date: Wed, 17 Dec 2025 11:04:04 +1100 Subject: [PATCH] Refactor code structure for improved readability and maintainability --- data/src/cache.js | 7 +- data/src/compression.js | 6 +- data/src/emulator.js | 301 +++++++++++++++++++++++++++++++++++----- index.html | 1 + smb.zip | Bin 0 -> 30978 bytes 5 files changed, 278 insertions(+), 37 deletions(-) create mode 100644 smb.zip diff --git a/data/src/cache.js b/data/src/cache.js index e4e3c12..6108d01 100644 --- a/data/src/cache.js +++ b/data/src/cache.js @@ -322,7 +322,12 @@ class EJS_Cache { } } - return item ? new EJS_CacheItem(item.key, item.files, item.added, item.type, item.responseType, item.filename, item.url, item.cacheExpiry, item.lastAccessed) : null; + if (item) { + const cacheItem = new EJS_CacheItem(item.key, item.files, item.added, item.type, item.responseType, item.filename, item.url, item.cacheExpiry, item.lastAccessed); + cacheItem.source = "cache"; + return cacheItem; + } + return null; } /** diff --git a/data/src/compression.js b/data/src/compression.js index dda2a66..c7d3762 100644 --- a/data/src/compression.js +++ b/data/src/compression.js @@ -87,13 +87,13 @@ class EJSCompression { path = "compression/libunrar.js"; obj = "rar"; } - const res = await this.EJS.downloadFile(path, this.EJS.downloadType.support.name, null, false, { responseType: "text", method: "GET" }, false, true); + const res = await this.EJS.downloadFile(path, this.EJS.downloadType.support.name, null, false, { responseType: "text", method: "GET" }, false, this.EJS.downloadType.support.dontCache); if (res === -1) { this.EJS.startGameError(this.EJS.localization("Network Error")); return; } if (method === "rar") { - const res2 = await this.EJS.downloadFile("compression/libunrar.wasm", this.EJS.downloadType.support.name, null, false, { responseType: "arraybuffer", method: "GET" }, false, true); + const res2 = await this.EJS.downloadFile("compression/libunrar.wasm", this.EJS.downloadType.support.name, null, false, { responseType: "arraybuffer", method: "GET" }, false, this.EJS.downloadType.support.dontCache); if (res2 === -1) { this.EJS.startGameError(this.EJS.localization("Network Error")); return; @@ -151,7 +151,7 @@ class EJSCompression { }) resolve(blob); } else { - const blob = new Blob([res.data], { + const blob = new Blob([res.data.files[0].bytes], { type: "application/javascript" }) resolve(blob); diff --git a/data/src/emulator.js b/data/src/emulator.js index 5c28aa6..342da5f 100644 --- a/data/src/emulator.js +++ b/data/src/emulator.js @@ -51,7 +51,7 @@ class EmulatorJS { "patch": { "name": "Patch", "dontCache": false }, "reports": { "name": "Reports", "dontCache": false }, "states": { "name": "States", "dontCache": true }, - "support": { "name": "Support", "dontCache": false }, + "support": { "name": "Support", "dontCache": true }, "unknown": { "name": "Unknown", "dontCache": true } } requiresThreads(core) { @@ -112,7 +112,7 @@ class EmulatorJS { * @returns A promise that resolves with the downloaded file data. */ downloadFile(path, type, progress, notWithPath, opts, forceExtract = false, dontCache = false) { - if (this.debug) console.log("[EJS Download] Downloading " + path); + if (this.debug) console.log("[EJS " + type + "] Downloading " + path); return new Promise(async (resolve) => { // Handle data types (ArrayBuffer, Uint8Array, Blob) const data = this.toData(path); @@ -196,9 +196,9 @@ class EmulatorJS { // Extract the data from the cache item if (cacheItem && cacheItem.files && cacheItem.files.length > 0) { - // If forceExtract was used and there are multiple files, return the entire cache item + // If there are files, return the entire cache item // so the caller can access all extracted files - if (forceExtract && cacheItem.files.length > 1) { + if (cacheItem.files.length > 0) { resolve({ data: cacheItem, headers: { @@ -884,7 +884,149 @@ class EmulatorJS { }); }) } + + download(url, type) { + if (url === undefined || url === null || url === "") { + if (this.debug) console.log("[EJS " + type.name.toUpperCase() + "] No URL provided, skipping download."); + return new Promise((resolve) => { + resolve(url); + }); + } + + if (!this.compression) { + this.compression = new window.EJS_COMPRESSION(this); + } + + return new Promise(async (resolve, reject) => { + let returnData; + + // check if url is a file object, and if so convert it to an EJS_CacheItem + if (typeof url === "object" && url instanceof File) { + if (this.debug) console.log("[EJS " + type.name.toUpperCase() + "] Requested download for File object " + url.name); + + // Convert File to Uint8Array + const arrayBuffer = await url.arrayBuffer(); + const inData = new Uint8Array(arrayBuffer); + + // check cache + let key = this.storageCache.generateCacheKey(inData); + let cachedItem = await this.storageCache.get(key); + if (cachedItem) { + if (this.debug) console.log("[EJS " + type.name.toUpperCase() + "] Using cached content for " + url.name); + returnData = cachedItem; + } else { + // Not in cache - decompress + let files = []; + const decompressedData = await this.compression.decompress(inData, (m, appendMsg) => { + this.textElem.innerText = appendMsg ? (this.localization("Decompress Game Core") + m) : m; + }, (fileName, fileData) => { + // Use file callback to collect files during decompression + let bytes; + if (fileData instanceof Uint8Array) { + bytes = fileData; + } else if (fileData instanceof ArrayBuffer) { + bytes = new Uint8Array(fileData); + } else if (fileData && typeof fileData === 'object') { + // Handle case where it might be an object with numeric keys + bytes = new Uint8Array(Object.values(fileData)); + } else { + console.error("Unknown file data type:", typeof fileData, fileData); + return; + } + + if (fileName === "!!notCompressedData") { + files.push(new EJS_FileItem(url.name, bytes)); + } else if (!fileName.endsWith("/")) { + files.push(new EJS_FileItem(fileName, bytes)); + } + }); + + // construct EJS_CacheItem + let data = new EJS_CacheItem( + key, + files, + Date.now(), + type.name, + "arraybuffer", + url.name, + url.name, + Date.now() + 5 * 24 * 60 * 60 * 1000 // 5 days expiration + ); + + this.storageCache.put(data); + + returnData = data; + } + } else { + // download using a url + if (this.debug) console.log("[EJS " + type.name.toUpperCase() + "] Requested download for " + url); + // download the content + const data = await this.downloadFile( + url, + type.name, + (progress) => { + this.textElem.innerText = this.localization("Download Game Data") + progress; + }, + true, + { responseType: "arraybuffer", method: "GET" }, + false, + type.dontCache + ); + // check for error + if (data === -1) { + this.startGameError(this.localization("Network Error")); + return; + } + // check for content type + if (this.config.gameUrl instanceof File) { + this.config.gameUrl = this.config.gameUrl.name; + } else if (this.toData(this.config.gameUrl, true)) { + this.config.gameUrl = type.name.toLowerCase(); + } + + returnData = data.data; + } + + if (this.debug) console.log("[EJS " + type.name.toUpperCase() + "] Downloaded content:", returnData); + + const writeFilesToFS = (fileName, fileData) => { + if (fileName.includes("/")) { + const paths = fileName.split("/"); + let cp = ""; + for (let i = 0; i < paths.length - 1; i++) { + if (paths[i] === "") continue; + cp += `/${paths[i]}`; + if (!this.gameManager.FS.analyzePath(cp).exists) { + this.gameManager.FS.mkdir(cp); + } + } + } + if (fileName.endsWith("/")) { + this.gameManager.FS.mkdir(fileName); + return null; + } + this.gameManager.FS.writeFile(`/${fileName}`, fileData); + return fileName; + }; + + // extract to the file system + if (returnData && returnData.files) { + for (let i = 0; i < returnData.files.length; i++) { + writeFilesToFS(returnData.files[i].filename, returnData.files[i].bytes) + } + } + + resolve(returnData); + }); + } + downloadGameFile(assetUrl, type, progressMessage, decompressProgressMessage) { + if (assetUrl === undefined || assetUrl === null) { + return new Promise((resolve) => { + resolve(assetUrl); + }); + } + console.log("[EJS " + type.name.toUpperCase() + "] Requested download for " + assetUrl); return new Promise(async (resolve, reject) => { if ((typeof assetUrl !== "string" || !assetUrl.trim()) && !this.toData(assetUrl, true) && !(assetUrl instanceof File)) { return resolve(assetUrl); @@ -919,7 +1061,7 @@ class EmulatorJS { } } - console.log("[EJS " + type.toUpperCase() + "] Downloading " + assetUrl); + console.log("[EJS " + type.name.toUpperCase() + "] Downloading " + assetUrl); this.textElem.innerText = progressMessage; const res = await this.downloadFile(assetUrl, type.name, (progress) => { @@ -937,7 +1079,7 @@ class EmulatorJS { } await gotData(res.data); resolve(assetUrl); - console.log("[EJS " + type.toUpperCase() + "] Download and decompression complete"); + console.log("[EJS " + type.name.toUpperCase() + "] Download and decompression complete"); }); } downloadGamePatch() { @@ -1161,11 +1303,102 @@ class EmulatorJS { if (this.getCore() === "ppsspp") { await this.gameManager.loadPpssppAssets(); } - await this.downloadRom(); - await this.downloadBios(); + const romData = await this.download(this.config.gameUrl, this.downloadType.rom); + const biosData = await this.download(this.config.biosUrl, this.downloadType.bios); await this.downloadStartState(); - await this.downloadGameParent(); - await this.downloadGamePatch(); + const parentData = await this.download(this.config.gameParentUrl, this.downloadType.parent); + const patchData = await this.download(this.config.gamePatchUrl, this.downloadType.patch); + // await this.downloadRom(); + // await this.downloadBios(); + // await this.downloadStartState(); + // await this.downloadGameParent(); + // await this.downloadGamePatch(); + + // Select rom file for start up + // List of cores to generate a CUE file for, if it doesn't exist. + const cueGeneration = ["mednafen_psx_hw"]; + const prioritizeExtensions = ["cue", "ccd", "toc", "m3u"]; + + let createCueFile = cueGeneration.includes(this.getCore()); + if (this.config.disableCue === true) { + createCueFile = false; + } + const selectRomFile = (fileNames, coreName) => { + let isoFile = null; + let supportedFile = null; + let cueFile = null; + let selectedCueExt = null; + + const supportsExt = (ext) => { + const core = this.getCore(); + if (!this.extensions) return false; + return this.extensions.includes(ext); + }; + + fileNames.forEach(fileName => { + const ext = fileName.split(".").pop().toLowerCase(); + if (supportedFile === null && supportsExt(ext)) { + supportedFile = fileName; + } + if (isoFile === null && ["iso", "cso", "chd", "elf"].includes(ext)) { + isoFile = fileName; + } + if (prioritizeExtensions.includes(ext)) { + const currentCueExt = (cueFile === null) ? null : cueFile.split(".").pop().toLowerCase(); + if (coreName === "psx") { + // Always prefer m3u files for psx cores + if (currentCueExt !== "m3u") { + if (cueFile === null || ext === "m3u") { + cueFile = fileName; + } + } + } else { + const priority = ["cue", "ccd"] + // Prefer cue or ccd files over toc or m3u + if (!priority.includes(currentCueExt)) { + if (cueFile === null || priority.includes(ext)) { + cueFile = fileName; + } + } + } + } + }); + if (supportedFile !== null) { + this.fileName = supportedFile; + } else { + this.fileName = fileNames[0]; + } + if (isoFile !== null && supportsExt(isoFile.split(".").pop().toLowerCase())) { + this.fileName = isoFile; + } + if (cueFile !== null && supportsExt(cueFile.split(".").pop().toLowerCase())) { + this.fileName = cueFile; + } else if (createCueFile && supportsExt("m3u") && supportsExt("cue")) { + this.fileName = this.gameManager.createCueFile(fileNames); + } + if (this.getCore(true) === "dos" && !this.config.disableBatchBootup) { + this.fileName = this.gameManager.writeBootupBatchFile(); + } + }; + + let disableCue = false; + if (["pcsx_rearmed", "genesis_plus_gx", "picodrive", "mednafen_pce", "smsplus", "vice_x64", "vice_x64sc", "vice_x128", "vice_xvic", "vice_xpet", "puae"].includes(this.getCore()) && this.config.disableCue === undefined) { + disableCue = true; + console.log("DISABLING CUE!"); + } else { + disableCue = this.config.disableCue; + } + + const fileNames = []; + for (const file of romData.files) { + if (file.filename.endsWith("/")) { + continue; + } + fileNames.push(file.filename); + } + selectRomFile(fileNames, this.getCore(), disableCue); + + // start the game this.startGame(); })(); } @@ -1223,29 +1456,6 @@ class EmulatorJS { if (this.debug) args.push("-v"); args.push("/" + this.fileName); if (this.debug) console.log(args); - if (this.debug && this.gameManager && this.gameManager.FS) { - const FS = this.gameManager.FS; - console.log("File system directory"); - function listDir(path, indent = "") { - try { - const entries = FS.readdir(path); - for (const entry of entries) { - if (entry === "." || entry === "..") continue; - const fullPath = path === "/" ? `/${entry}` : `${path}/${entry}`; - const stat = FS.stat(fullPath); - if (FS.isDir(stat.mode)) { - console.log(`${indent}[DIR] ${fullPath}`); - listDir(fullPath, indent + " "); - } else { - console.log(`${indent}${fullPath}`); - } - } - } catch (e) { - console.warn("Error reading directory:", path, e); - } - } - listDir("/"); - } this.Module.callMain(args); if (typeof this.config.softLoad === "number" && this.config.softLoad > 0) { this.resetTimeout = setTimeout(() => { @@ -1289,6 +1499,31 @@ class EmulatorJS { //Safari is --- funny this.checkStarted(); } + + // debug list directory structure + if (this.debug && this.gameManager && this.gameManager.FS) { + const FS = this.gameManager.FS; + console.log("File system directory"); + function listDir(path, indent = "") { + try { + const entries = FS.readdir(path); + for (const entry of entries) { + if (entry === "." || entry === "..") continue; + const fullPath = path === "/" ? `/${entry}` : `${path}/${entry}`; + const stat = FS.stat(fullPath); + if (FS.isDir(stat.mode)) { + console.log(`${indent}[DIR] ${fullPath}`); + listDir(fullPath, indent + " "); + } else { + console.log(`${indent}${fullPath}`); + } + } + } catch (e) { + console.warn("Error reading directory:", path, e); + } + } + listDir("/"); + } } catch(e) { console.warn("Failed to start game", e); this.startGameError(this.localization("Failed to start game")); diff --git a/index.html b/index.html index 7b2f784..4166b6d 100644 --- a/index.html +++ b/index.html @@ -399,6 +399,7 @@ window.EJS_gameName = parts.shift(); window.EJS_biosUrl = biosUrl; window.EJS_gameUrl = url; + // window.EJS_gameUrl = "smb.zip"; window.EJS_core = core; window.EJS_pathtodata = "data/"; window.EJS_startOnLoaded = true; diff --git a/smb.zip b/smb.zip new file mode 100644 index 0000000000000000000000000000000000000000..0f892e0137469ab31afed33f4425cb7bc34f3b0a GIT binary patch literal 30978 zcmV(tK@GZG)b|A zKskQt0*xoys;E^!TRBA3dgGOw7+r z=ke-=)C1RBS`_C8&!@B5f6SjhfA-J(`7>!*acrR|2;@(=`W@Y^5f+)b-4L03XQl}M z7O^GXhO{G=GwS}yn1OWE6vJ<^R|DGIT5S#JQEdja`#^VT^J{fk6BuJ1te}7Laxkuj zEI<3YzxhPJusQXWo7B}cFI~3$xVGyDpZV|`+Sk{oQA)MHfz;v@?@byOH=-QIHQ*f| zz8p*&wz5hz?0$~?M?|I*Hr}sMri=VWKqHo7X8o< zqwXKavmzu6#(m%wRbmWcwSssoq58)AV4~NYGRnXz#ohAOVw>}n*`f~i(#;u^H5H!M zYyN7~fM#PxEidsa8~FEeQp$2WkghGukC<9x4WvaYJ1v;i#-)&^=HA9tU`b6=jCwcv5p}w1T6<~WX)wQEh$nu*u?BLd23BKVL&=;#D;!J!hzeobE;cc?#nvdiF z^{ghLPvuef=Of$XiEY#rBcy+u9I=PR_;xw;t?X&HhrY{{-^-(;qrL74AIig|{=GZi zA6eh&83>cz!;=0>kccS+^JgE*h{LaMI9FX2SkWP|%g~m>1SOsGtDJUQQaWJL=65>M z%qkp-fSIlNft}wO^ED?sMyA1PO4pi)r;G?#ExJdGfZ+#oKi!Q~Nx^a&*FvM9T??$d zv-zoxk@I0G&h_{tHGjbHg>M(>Ri<|vH&K!m+E$Py1At%OuF`sUijitQ;v40eK9(| zZ)y`8j~RvV;!-QmXai5XUuy$hXeWd0g$LNh4~*{{-PdaK8I52z7zJQ?(pj+@+|MMK zS!o(rx?O1Ybfv|>ZiA-XI2NpVDRXYhI0MpPW%VYdSVfY?TuEw5x};8T^0ephL)M7v zNLnKx!*-$-AaTHsZs8u7ycBKt9xz#Eg;*i(xDT{bxtb==0Irl1Q8?6_Z`OYBEJC6SKx0$*_m#=8pd!MEIR+EHV zu%1q52W)zF$Sn*eb{9+u8@>;B7t*_#CCO|PCR;(TRey^=-qnOKgtgZdS}L&0ty@2n zAI(eWT4m`uR!Q1s<)$50UOF4=teE_0Nm`8AM<(gkujDQEM2V301Z>-dP>F!;?RNI; zXNi0vNJTt9K@fE@D2hQ@M0p%TiMzXkf|v+*1#RLMyU$q0)wo{0LtWY3+I>rRsCN+G z@$P5TPt?P@XS(<4Y3!MGqImqQeIm5^Mb%gi4aN!3fV*}Apxavx>|3=B+ilovUeP0n zRKkP*^iyj8WxP+a98y`C%jLqiEU(g2R#jP0wtr>b@N%%^_{%D)YH}Hj5E>?2wPIL8 z?r9NSyIrR$iNCFeB$}FdiMW42BAjJXWFLQ>qxBkesJc5OQe6sBiU-C{*Lm7OBh+Us z0d!ut`&vc}oiKYZ)~sqPve6lzcp0BaaUwsun>fG4AsZb&m3L&$4LCO$x#2|PWPZem zb~1i+^UK{RGa~u6EJ;(V%vcHLt|(WMS~jB{%o4Ww8(qL9S+o}?;nCM~wJ_)uQFugj zyS5B`=n+_Kz$gXtm$4FXQ{<`3C@yz)W|Ll`$xKi3f~o=@Dq;Y`-$s%$#c?4jq$Sfz z9vJvaw>39+^C|g}%pF6dQS)c?Rmje#?_m3FJ$6newjr%sD^DgZ*NU!W;_N;uzWWMF<-3OuRerQ$Xl8y% zt~*zvB2jD#v4czqsjNJ_<>XZtUDV(hOiF>Aq&0n(@u^P_j@U&A_&Qxz_tNizUX|lH>s3Ic!=Z{sDb8;c z9IM5_E#yHtw4A1}TtPQWQJd+E3Qe(s3d*16sQYua-_O1Qg6$YR=L98nziUN*aUa#M%tHdb{K;RRu)H#74<0Wi3(( zF4WYXRD}q^?}~ChQGt7uL(v!$Q#81o>H>aVQ)r5&5GJ-k;N989W^K5#vN9j?YjUI+ z1mc2RmkaXwTZAu(W_iIp22bL)y<<&-@$}jdS0(D1A98-~jTUe?2IFt6{nzP#Q;t8s z7SE_b=3k&=k(;#XH4;n(roE0yhg5mD47g zq|!ybE{7_ij%`u&x6Dyt!{2I2Ma{Jn&uXgLHVcUZ)&(g|9JmfmD{;`epq(a`tqW$+ zsDx&VlY%mh+Jb}zZInA}8U%`ZbI>is=N>-0Z~gkdJ_Vr?0jquYE8o9dsm4}S6@J{( z3T1C)pCuK_lBE^1pd|6R=_cigC*w~&u{EA}a_dtG-`T5HpLxz%=l)r@wY9fbRGfF- z5zR-o9C;KzEk_@7{+iwQxZsitFTLopi!bk9*R#I2ueWcl*52N~al=5xEfu!}Zf$Sh zy#0$W6%KnT2S+0J-piZ34k|%x98z=2sY(VHs=o`BP+5sZrqt_)>hGw$vy#I~1b_JE zd!By!nP;we^2(>4eDbNMR@Bexjn|Jn_UPkS|L6HXQ2v1XhmDcbPCxDRXO(A_mr^gM zM_+yAwOx0ut<4P9EA<_nT`I5nck%Dx^8aful>?Lmm4lRJ*s80jtKblO@S%sVy5_2D zjy?Xk6ZjjwZ_D6wFFgO^i6@`bdfg4z-&jMKYkqFI>%c;ns|u~VGB(eIu|H8j-ulEPNcnWmoiRRSmG=y=2YDKKS0VqS-_k) z+gaqyFV66GDEUq{OZEV%Xqr<{QZ!e=Wr{udN;awJor~8*k#oK?rx-~y_YQFkwy9E^ zYD+yRsWLC%0+|>hcV|N>$n|whbl7hU%#w`vahR>mx%_~O8K=PTO0cML3}}U*m4mhzSamGSyn=7(#;Jhm0-bzSMz)p>!&Eouesw46{_;%xkBt*S zI}UK96|_~5iO~jw=Wei8=z_XLeH5209t%ie(sv3NEj(`OlVHT1*bu3Z7 zQG0=696vlCtU}{Nu;$12*r@%&)1V#Dq)h?et-E3PB+R{Zsuo8G>q_XB`gX(?Caffm zlA4rdKdyu7^qo+h$ESRx?`6{5@gX zc$9VkUlL~qcENSDUr`JPPp-qjmqruB&pVDM-#D)xv=dNx9G*@6s(FmK&{zdkMaHo_ z{XZ5Wa-tO)600E83T>fPXtdvWd}t$S**AqBAR8yM*w z>7Bmi&w8=~c)aK`Wvv1x%V9Ds8)4a7ibm}tRlZvYnVXxt3n9m>JeFiY5JfuM+Bkrt znUT6f5BmdiTTk!WAmNjC*&K~Vx7@ZVC?Ngj=oUnAMzb6^22p=9&%m8z{SX4qTu<)2 zlatXuM~8;)93tyE#@|V9?jjv35eY#tBpkHKAUlXX>qtW8xLTYoEo4q%ch6tY-cC5o zUfATH(B0F1+{!bt;SoIGad~DuLMfEGN^dR|$nR8H3Cs&Y6y9ogr>eBBDpl64l+;b! zchR`x7VEB?i2ICK>ZrSVDOFlsl!_~LrE%BUAda}>`jN;YaSPwHB+UMQyEF6V&G+WL zdGqFVZN>K$zZ%~ge^pG>Br>i^o`@E%VLJ!cu(utN+a;Ut#PDS<G@%KNR%6TS771&iR0vg9;Po`Lw#o8i+Qj1)zM%Hn>$K#mh zd1oQ~3QtT8#x~_68?1VD0V4+kQV~jb;d&8{DM(!Dy>=$sm$|3nd#dCOPd&dR%<3TK&Tv1=# z7qSs-qoUR6k!Wxvy)hz{f!#_D$8N7=L<7G!xRhI`1%200V#I#`zf6$lIN4P(p=u7IM&mHH4(xn)tbOm$y(v|p%IYKaS zxt6kEvXqOl1*vVm41=w~>}2@`LE}y?wZjJnsa-zGy12e{sgfm1m*SiGql~PgLip?n zffR)S)TWDF9e9(K037%29PDw-U*P=BAdnaJ82IHEobea|Ai5f<$G)AM3mOP3@@RY( zs0+M=2xB~~li7kNZ1$4?&4|+_n|Mn~x{E6CVtQE$C}1Y9 z{VVMlrIR}F3&5Q6Q%G&V@674br)f^_o!`+Rz!CL1?TWA#ayW_wBsFg9JDF2o{iz>* z2TzR|YC4;7}agrqf7L-@D07S^yr4yuRpR(5Ht{ATp2;o?SUJ6F3RIz|vSc4gn zh$!rCNUk*`dmEBd`vR$GATABh&jhuC$sa?^Y0@)5s_!v`6`PKH(gDXy=<#E^pQKso@CN3<;2}_aGp@Cmb zNRd;H9^iA@fhaII4GKhqUHlOT8IqGNgZK?bx4IEIv7M8R=`wPebUC?XIxTC{>12^~ zU+k)$vNC_tCAqzg5YFEa^8UrBXQ_K>D!}Veubg-Mx;Raa(Yb_Z?hEM-fyuJ0J$Fi{ z9tTA@MV)cZ!=T6^tzM#Wv0}yQ*RQXs-i9x^#od!w1e}qz+}(+Q6kh7k7ULuV5}(*2 zBzh9Q@Rm5faK!Z>sVC^_fv)FU#MdbEyV z4Ne4|9b4dz4#@Jb+@NO8<6NLLcn_sUtK%>nL0ON~0@s<(aA8^^vxZ6HhwhETTV%eH??I9ImQ!YIp@=Q^e*Tx+tj?3$1EtM!}DUHfV9 zwOx08l{vDmI%aatrv+P!Dbp1phbCbrY`&5oH5k2~I& zO;`F2HH6Na8;3Gx)%`B!rKZeKmq(|zIcC?1)m(S@mrmKwlriZ@W7BSv-J{dgG7Z+w zu7i#utGhX74STCSdB1FEhIzr@d*+r8{pRYSLA6&}FPV7Hydn1;rYp^_OuyMC|F9Bc89m^P8dqHT4M}0T+eFEWOGHwp-o+pX@xZ- z#%+SpFBk>6T4R%qz-9qOJu9UI2T_!1Z8ahRbr?xKX<-jSD%dcLMCys0?mFqm^Yqjo zclWt_KA-RB{dv7V$MJEmtLF)~J+a8f8{rZ0f2GPg3_Ny2kV+xJEnH;SHs6kby_QoU z&7^nY!hrAcph|;SQtBVN?2p#8DE~SY;#Y@uWDcE z+6*7c@VOSZ)=JOHIqf#Nbo&523E$pcBOQ=3Q(gj&a*}ICyabxhv2lD3MNO1LT+zs3 zlzp2{ClDxBnLB}k>i28t1q~1*j2o8*- zB8ET+Fb1Mox&W&pi%|@Z!+q}4$Wv{*w!>~keu@07{ESPweObE^W(cT?Ts1V;Ki5y7 zw%kKs5p@G=nHo3OAp}OUDjicw+4u#||eNn?(*eb|n+KPCSn&C=Q-JtMukl|WyX|G++nGZuGj$Y{=B zMMLvY8Ajpk)QnN5g~2J^h@(CzbVh?VNAwnBw=F324&p*D!B^-)!6~O97}DqhA&3wN zfk8)UH)C^j0gig_BYYBQ6AHNpk)yEATZ;R=Oon$|f(s{L zAFl8k@j-7Ck2-zM5EP>(LtX;w#0T-;@mlw8WCZ%i%XAKnQTqpnj9%f8p{95xfhqu7 zO*4nU8lWpAq-#W}Z!pUYULx9ROg)+^Mmo`|6HtWxN^x-Y& zx8W*O z$qLMl?7)VQ31AY7Van(8xc6X5bqBDeb#^3-fhQqMk5v~su`q^v?2sSY3k^f#5b6{_ z*@zwLLSq>Jz>(ac1!LS%Dh53mzHI!fK>=1`e%0J=9ztRe9~9wssEOW3AESSvlg5>J z0c5_pw!8=D$8|wN@kb174yQzA$6ir&(CzAPZkmREDEh?|wbj5v>cz3P z$`}-L@=>j|z2Yas>gZ$zJE5}}+PcM~;&WM9Jgv0=IrP*@rUx5c6+>SdlSv% z%ZaVz*NLY{!TuYPZ%kY*aFIKcc9F>qEc7huH7tInqQSOLlIHx=>3NbRXSU~{3Tu&uCP6;nOh+e7^4c~ z&y;It&OvunA1^uw^7(`&jQ+Idy55JXvs|7_kg`~!=4`+D97wkmi>+WK=n{Lxd&L29KNtp%iigF; zEtRN8GFkMiD;WnPsJElIbO&1IA+UucT2Ltp#(RZdF3$-(z6!uSMg+HWu zwD-OA%P-FF;gUvA@~)&0D6HXXa#_;a9Oq(1qckU!6a@Nm`vt3>+q(mEW?oNj zE_?sXN^T_^$yRbNIcHuqu~F+H8*=`R=gfk>EXy5I8fdokQ{3ivGOyIFU-`zWIrEa! zx9IQ7{iKNf5~wwItB$R_q`GBKm3fufZEA@hln!HjT4iI!O?KIkM3 z^OzJu-yQbgaJK#L^ZUO0ec!$3{(k@Q65ri&L2}4*#a*GjZ0TQSCUJCcNk4b3_|mNK zn}?aIjr(!~E0e?a(p{YJ#?FvW7>Czj;b@_IjS@gpi{g>)I|c|gG1F5VyfL9zEZ7-e zoBIiF<-2e%J60XDaZ#?=V?kaTyELSVIr(;X+?Y?|sLAKGw*LG}`Rk7#3%^4hS5Y^g z5;Kf4`Kjy^7pP}WJkfnepXD7sBK6uX_fslM11c6W+cE+Ql_h>ws#_-L3=X{4XTEH! zU$tWT_Ia{+rCPaCRw>(|_{1-`_FkVfS-pR+|7jmV=e_=kvULF)e99HIvY-3|J`ePJ zTJ~W;e|rTk)NpZWp+3+fvzkvd*MGe4pJ`=ygcek~*zsH5Isho!P?px}+E5rZz?xZf+ z1no{ncXPx-(Wr|4bl$o65iD$7@IW&6WJnHPNACXL z(&TS{p>b9d+cWDt^Wu#iO5gH8=Ck-)A>({_S#;QZx{8{d_hiH3kWyTUw#;e{QI*%n z|4Or0eG7CMmL%_{JJKu_80rfpyNyvs$?{~m%0fS*tWkN@MLqMHcT+TrCOjtbFI2q{Pvl8#^z3 z5_*-ngnpm3;L=CbVQQ*I_MWQcwTTT>3rF>V$sHk5*7Wz8)Bcw=Cvoq zl+RbJV=5G5D-#FJ+!slrO#w;T8;PUqs4Xi$qW0z;rcUOcr~XYmi@~i<%TbHJPqwno z%gpqLoly?V4&4l8m!_?JYwd6Hn2o8c7U$>Z|N2H+d5$12BsXcttPhfUS$lQjXkK_h zL_a@H2VFdehB;Bif>||Tm7C4XiKMR3bF0Vc4g1NI?W=mJ|K++&m~x!DxAi$HKmUyv zHWkzqoG($8wPJ@?m#}~Cc`Bw%MO9J#bFOEMQvfm~0G`H0G2i@)vjr*uP$nS!b5|B~!}Fn>y7bt~5u z(&KYVnqIxG zk=4=CB_XeKZ+lfy+e7N-&Fy&P_=fO1{N3Qa9KX9H{SCYwx8ZN` zO+2S;xe`KdM;wGPdyl$Q`8m~-Yai_A2hi9`(Z(nDzrX)5J(M{Nzr~)LlWU8`BJTwE zR<+1mLa$)L)}I5gO;yqRvOBVSX%a6VQ<9AVVJ~p+QxmlC8wvkf+Q;6K^83@K9J`sm;0=yF zCE{UvoQ^3^%6^h7nEhf{Vc0=sH+R9SrE-Fa8H`z7K$lXL^!wBZeTxD92m@Od6T!@3 zw8hQ$x$mxgZQYH%|(;VCIkT)nTD zoE^440M8uvbs!g>9uf7Ee1chcYaRVQe=Kdp`-yHz`HTMwNK0tynMe7Q)U?!`h0XL& z8Bo2EdS(u{Ao9UQscGG)&-#<^OH=Y zqA9aAt6gy*vn%J{ahUmO3$hwksKPluy2-OElOv12Cv3Y|MIFX!DPGu@gqoE6>`Ay&oD2-Wr<0ATn7Xwzsa0$vatF*ar?xw|#&uGl; zr!dSRpenD{|I*x?gN}^zgfg6AbrY?vv>J!C|HHQJ+6)(t&fjs_C=hS)@Cg3)=pGuX zUO#%u{fT!R!iVj+;{aaVfuA~n2e%k)4o2!@sYgH@07_7eh1_Z9*A;ilJsRFu_3K|jEkZ;cXo%@d#mfZDKYx;+>rjKy0u7500` z6?2hMbZ)-YjS?+|F1uMZC8ZflFmV4Sbu;cxGaado5<(lV?%vP0T;AWO$+V zRo;@)QrNP-WeRq`55Tv+&RG4cQ{X!XNMNSra~9x0T6M*W?``S3z&kTX3X#gc)v^rG zLenIsT8w>;YJH^IQpY;f&IjPB4sOb6y(a<4dqABwK~(?(;xn+I7wKg{j+PK?K0}v{ zbOA`W45?w&jS^t>K1?oZrHwYNlZR29gv$n11jp{wc#4{G!SV0$SpWiRmYj=#-%O+P<}NjxaPAd_W`4RbP=yQ4|6E=0Wh<` zB?X#+2yR1#pl#GozyToM6Z_B}`u$JukW_E83{c0O&Er)v z)WB~>CsC~ERII0YKLWgy2lU@Wfok~2{Uh06e4AUNA-%{Xgwh6f^9-=3i2)O+d|OO3 z7!jlcxzF%`Rubftb$a)xyjp_t`!s!OFnj|;OfRX*Ms60t6q8;Jwp`jk8!G@sXEjUr z5q7!Sl7<|&f#koq#ql|Kd`XMD#AQW}9#^Pp*y{8(dm$J}j7Gc=*eji2|99KSq2A2_ zAdBQK>vZ`VJ%ED%FfuXCxPSuVs9{w16Zx$+PItsQ{Vl#oGYNft5qQwY0ON9?;T4J6 zKQv%^U{GkDi3Z`o2F-d2#Er|tgf-*PJQP{aNala9Pn3& zDI{>`ZCQlQooLasE@Z%HLXRhr(4x{tIY^_twxS}ZP?=>OY>iF6)FWo~V(m--q#qsnHBojw*J zO1Rwj6%RP^RK0}*?g#V+UHq&9QxFtQ4FsR;zk?BfhOmhw13qy~!j0dcsL4r702fpw z=X3!;NHNw+Y3*i;EU?iABq9-@FAmBi!NwY$ngJGS{wYffHh^2wLCWs;Mv* zt^cx>M14A|k*jrEkQ(Eek&$BV_1D#Uy{E_c`1oIcO-xecuZ%i-%@r9W6%ExosY#l` zlf)R#P?+3{%*FRa2?>+vBPRmsNg*s(IRbJ`09PgFj5ZxY#y*{paBpT{$VAhua(xg99G6UM6rD_QdV%Z;=QPVER@h9Q zx=qNgXwzkKuvEy&0Bb3^O}B(=r0i*JI)=m;jg0b>DK1{ZpgA;BGaP72#t63h_c$a# zawEBDg9}HoFd?BQut;^%Mp}4Ty^L~A3dzYj0kT)wh)KcX4hmd!sViL?8p~$rXIccX zF)#?47?&2&NVD{-C0uN%WeKNQs>1D1n{fSZ?R9r;CMOkGGNIN%Nd@VXz=hLMNbP=2 zK%Yz$cFDnI=UV#sDEd=dm~%Esr5KW-bGkbU{7o2Qlre;vMX1ULX{$D(kv<;M;gr1E>c&r`AjpFK2Ja`@0J!(Z~h0qq}Y0Ya3bFI+2B8(M0B|1v>%oI`F_T?0u|Bh#wlom81Cni@0ad z@(ml+>*VpdGpY-~xHE*Qp;qN_{m4GDGa}m%rvI`L*`GL|+lCs^5jfY3J$_(U3}+h}rW_b@(QU(!dNNbtW|%z%>n+r$!*-KG zlDzdXOC@qXXt|BVV@UibfX?RINDDj|@#F$pvh{MjhZ9>tGeRtu#<3Z97a3P>cbhxTb}qZlW;(s!j#b*|%!zAyT0%Yxo+ z?=YP1r1jqJG~)rDD=^*E{lIs!PEX4Zyrv!^N`lCc>#yOpXUITeO9g^R<8&2hL;~C2 zTtfh?y|;Z0@B-{xDMO`wfHIsm*RoDlw~KwZ?gMtDmW3QLp^jAp@c$yzXyKT0F5T|( zCfl0c!}u*&@!OF)7K*_wQeirf9JqY*hI)EooIfmIvr!_KRD1LT7S}p})2ORubyLl; zJ`flYbi;J)y41*YI<3O}JUC2vH4m>8c_WgIUL(7MPWT6^3rG8(-hEg4L^Vq zC!8$)d;otqiWiOI`4{o|AvNB7$hM|#EoIy4A*idNYy-p6x*sUjD~F)s$S*9^7~uEI4s!WD!y~2ti2~(slxK1gMP%c=aA3ENZlCiKm{& zT?bV+54A;WT{CV)+iHS}-b98P+gc)_EgC{B9CBJTv>-Z9r9vL!Q9~1zL0_904m!-O zXd?NCZs9AgoRaSTn~Ey|Zla9B|L!KcNlV+bNUZ{8wOB<#gQ66zT6L_U#%nx?_j=&O z3+pH<<2Y6&wlY+r!#Y9DuwpZlb%+yZST#ayZF(eyHj7HJItn_{X;tbvDi##$_cry) zLbLmy?|=V0|9vmYv2%;d`I$Ngfdj{A$K-a1or&9|#BEaUZgU10z(=3qqD*=>$c zo2}PMsXw;M;szvaq<-hYQS&$bP8n|c>7m)iW1&Zk4WaeW&fAT>6sGbiZfin_WNqK< zh%JLKEGj4yt|_z(s(nDvdGHR&i&%|yYf-p{HuI9$GOHZgqy$k#OXb4@@?Bs1KieTc zJ|KUz4>!nY5zOQIK8@9k*5t~=Wx7WVKwZ}qVYpM(##jxKR4NK=Miln(D2NPXtT-xs zdO4E$$d2Rn3bj_ge;poUeDqr(9uuRZ!aJgTC>>(dfQy7r(#kj1>OCE-D_}XgZiNN9 zLo{6ogN{lQJH$kkbb3!DIwl}Ow}aPfUJ{YVx>iCUJ^2Sjz%V4ZEL6yv?{_S))VZL#`G-1BCam8xZpxb!N z2?)cBBQSjUaVN~pkJEMA>$ZQE{y4oay(dj~I$Nmo3!xB~`Iqn5;@mnRAZ%FqsOxs+ z`Wq-&=jK~(y-h0_8B1Hx1(Q|wG+gutA#_x}=tKQa%;N-6K;!~93jTNS*6|TUwS@Ti zJ7TUIiA{)}l&>Jv$=7UGj1AGB1l>{<^-leW8rT0&-_jZ2jb7j|9#fw%HmENddr66o zP_<&zTDLAJ^Tk1~?zh^kv*ZrqOFZ#!;kKZ?Z~RGq_b2(y$B$yRw2JV!3pLG=^q4ZPI^=biBKO>`da~ONRJ5s z|BiJ!L-PU89%am))V4=2c*|HpOdH~i6;1d+Uh{5?yyF}rTbfTZs7x-(;0&MYyU%!j zB~za_9$d+*4+f!15qw4D>!hK6PPnBIgO8Hv)Xq1mi3v5?zp+ze{>M8tZcG%5>gzXmfOAw~fgOf=YBz0yal{o!Z<Rs~ppvT?;_UZVUyO z$p#9>ZLU{*X*dE;o#lv_zkCY0<`Syv4sdqsacV7j3ayjwn1t3-s8NhEUs19m2s{4SEG5}d!f>g7 zS62{$NvSeBic%2-(f&x1mPK@BoDl;X!(i)Z5cgYW% znCf$}eMH3frBY&0v^)iy0Hjup2vC1*_ts_7JL9=2xWtv+6eVkwBG#)Y`_A}+3v;Ji zoRXM}vl8zDD?|%{PgtGi>;p4OQ7>#FcYq|Y%vK59O<-}2LF4M>;WC0Y;K zP%VY0aA9~cFd%KckGvheuqL^-8+vxRlGrJl?nFdDUT-nkK(to*fo{b=yFZJ;n&>ll zJDhw#jo2|1AvWw9$$vB9q5zU95a9H$uvs8VSQN98C2`n%w#^a!Oo%{&m1gLw&RE^2 zWPa+tICDSlR%cdce(P@S-`(GoIX@H1X#G~V)?c{Z$Gz2kOHbjtv4@`M+Sgx^y)b)e zwm2)`|K-^g+2y^;j_MTZrY|Y;OjeOgGycYm&cou#H$`8OVD->5EJbDV7mcIr7OpKd zBclfVH)YV6Y#62GYB0qXK@GsfO4Vl2O)vJ}k^wg!hd)q1ZnHx38u=*a@bKFH$m%@G z1lUrTrx6Xb=n{uQ_qKth$c>paepmu}3tdn{=%#S$-J*Pc#y`>z`@VMJnRW7s^J`}w zGpjPOK{D(2JCk)tDcvB!*so-{a_cmt1WoJ~6T3y^+t1zNpy1O1&tb2JHcKx@;Oe@J zF3X?w0`oAs7lseR=rMmuts+yl6{ajviEZRyHwYXdzJ*UkGAftFXTb9dgzN+Pjukk{ zERB=5ghP%9NrVW%*LXFMV_vRpW21gl)ogxn0#T>b;bP3=Ve;x;!i-T_H$FR_x~eDI zCZw+GQI{C`D!6ueqm{l|ja3wJizT;s8(O?qU)?M>v}nx@;b5s#ES)2JW>lY8b&N-m zeT0KI2ybx$G}X~Vm=K7fx$Ii@ElaMomk?r*gTDhHhEq97cXS;W=8Ame=CHyNq_i#} zW|Bc(bMaK92m8dqW>{fXbTGIORT`~!_LYL7O=U?a9$B9k z*ST9Zl`&)0rd7;n-PFox#7*xqBflxnlyFOPc#5N?{{k|J9d+}eqg#Ul?f)Wcu{^RH zL7t#UOMrR?K5PuYsfwqfy=vZ;I`obA#d~$S%`mOg*$$)WkLdY_U0M&*SxBnsMMg zu@}EZQqAJQsx^!IfH+3bq?Y#m>vjZmm#QfoPbpf^1m5t~vZ6H?71Qt!k)yj}zmU8~ zV*oYx7T_cC%u&+t0&L-{eQIUmOA+Fi4s*X0X+2UL$s!BC6fFsDB>!+<$(JIkQZM&` z9k5m#Wd8%**oQ7>vIKi!9U|CW=8sD3PdA(u-DSo#tYWDTx$eb2O@v&a1K;3UPD?J} zaofC>0ka_0Z@A-YN_bHmwmI8Ee z4mYOF^OL<@YFRK5)v@M#>);Gnjy8Iz1nf$`VepC!&;vns2uQsBbrY+Gg5tgsLnKL*R(pFKOLemtBl zT$knke+_3>=KgzKfor81+HrYmI6Lz32k#GOJKo!yRSFI!TuaF9UI9-9FCYE@d8kcY5crF;?Z8obgM++x8zAFzT&E!}YeSZr+#PBACaL4jam0&*hCDgxyB9+XWRE z82D<7fWPum^d$DWfU<(LBSzjkQ#+b#9%bR;K3j%zFjS9h)X)!_F~-i+mSA@fZEkGT7^cDTsZm{&^b)M*%`~qyAFoP~P?GfG zoPgTEpPxa$3S(5lk^5Y!Wxd5lw;48|0U%%+Tg8ahNmhlc+B_N79V}B>2mD#mMgoga!ZVPJeT!eCba4 zo}C(Jd!FUf3!7RCDGkj6*{{zG73xa11q`hzS=7T>!l8h2NK+!#k>P*>hyk{SBdUM( zi^gwk1n?n7;9n@%NVo!f0|YK>-Dy^=YYIC$E={^L9@v8ckJkHk8u_}TOB^^D;??}N z#x}DQ?6f-Yw;olD*1BKqr*V4nukWSBHY5-_#XmL0SWtcN05J@AGuW?t1 zPjhe{c!&|B6);UhhErmGKObIh1aNj=nLZZY2ybt}N&%L^yHK=SccGLzQt1J^6dm8y zSOEbZN%Pof%#}fG0Dp|LGeaa76GV4$NokpfIou_lnU%*@F{F4`9zS=U%%rl)IVYTS z@+r(+G5ds*7oOo|#k1xikauYvn^84?(b?x+(7;M(pK|7T7dQG?*}O${7dKsbH7h@H z@deATxgo#_JfoiT7oBsV@0y$Lno$L|Ccbi+j}lWCrP~DA^XY}3VyyRI_!}igzj-0XxJGBvv3%-YuIj<*;RfFpl5UJ$cR=)y4%Quf zTgcrQ2IHBtFmY}Y?=|^<`%Hr(AN(QW!@KMftfZZtQ;;apvZmX%ZQJhM*lpX~ZQHhO z+qP}nwr$@&=T6Mqyi8R@qbl*5DbjiAwKPhDfqD6~Y z#Es>d2D;ZC<77m%d7?r_f_I=h?2Y_c3^Ezc)Z0miBhT{9x_&}(cvYM&!YCgKk^<9pTnt7nB z+^4Uv2I&kVg1Sk%ES6qC(S%St4+kw%i9%n7F34HNIPluJ(1!24L2l~PfCX!k;4!c? zZ_!mOKw@2QyyM4SbVe1BqagsT;9pBdix4^gagHD-?MPTlW4GC!?sR+D&Pbu$tym^` z4$0$TvfWo|`7xIE&R_7kQQcC#g(xNCw~W$-!$lR+aKg&J)*2}BK81upDJL~g>8Tt6 z+Z%INxcc|`&1egY-xE9n#+D-lD*G4H_ZpybD~4`;hd`%pZh1l;a1&iLN`P%1i>cM% z*|#yoJ7`$%ftn?qUhc?;1s+VL6f>jDv8yx%36nf-v)Ue^L>Y4ej^ygFV6A%4G5}tc z6#wgUHv9_FZhLw4zb2(CZB7_jMD( zvWvv|sH)_wRohoZD6_gW89A=tazX0uD!}Zw7}Sg2pY>#|-M;JnA6R7h z5;B4&U(1#I@!qD+Pb+kmJ7RM@5E=;LZ)O!ocP?&j-t6h+iZ{QCx7Q^epi}LslJ58M z*@O;d_`JR{313DvW`lZTXL1W`^5Bm9qM`OBCq9(`yjvQU+d6lwVFGab*wYVNT4*vf zBPq7vP+=PI;=y3fX+zUf{#q*@6-0cEH4;Dhd?44N8SHw3)ZDBJOj&WCB9KA~z|isg zP~#HbJ~k;}2w*w_zh?iN2VZ>80PxDgw)?^5(+NyxL$8~EkQml=h|fOD$30EGNcLs6ypkmvxg#$iFJzIO+BcPD*}Wc38oP3 z`aHp;2@BIKFjmRqPy8Q+-(=7nfT+R^(-M@^2^GK?WElPU)QYbIz6|!rBkAG@ zi$;EqnST0>s6A5U1n>*siQx~h(#G)U%{jHfYMOG{u2vbCJ(ZK6JS`J= z@YbDF!3TG=QtjHzm1kC4dYj}D z0g@d7uwPxMRGroq@hcSOk2m=ayq7ACqlaF`bev?r9sp&>&nv1(qqMM5T-iFox~pnG z+vRehJkeXN=VSRGp3o%IeekRn*phydrV&AMZ4o*IDz}aX0mbQjw;uT<)NYaH7wpcl zI;i`ojPTKelJhwb;1Cn?W5Qkj&CKdk2Q&Ejh@k`$d&1j?`#L`!$Sw(t^8B?-jtooR zwIiL~|$Z{ll;KJGV#T5|zC~I|A$9sE7`pb8R*BKD5x)bgP%nea7I7@)RC53MNJ_J!IVYS*&zk0J@SHQ-=m8{+MP$)`~n z@m;)2t*)b$ouAyj=CIJ8guK}vKsDWn(=ye$jcybh*RT(ubdywl?*e{SD4YPGhO*wg zpG?gtOEH;b>0ny#YL!F{0vygS?x~=U#j~9tbcp->n@`#ud1q2DRuf&AM$iSiygf&P z;!4)QHK6^gut^@VdX;?9ftIMtezeVAjFJY^>ab&pghm4{$s~C*sz#GiR#Oz|G$RtK zfqa-$`qO!mIhMr|K#zT9#oKoT=O8T=WH=4IJYx=abuXYDxhf1SmVYj&V@xlO|ELFL zG!7-ZOU&f7=yTer){yniyD&akX$f_#!5xFBaQwrSjQLgp)5stf2fV#BsAM!fp(V)4 zh@&XF_gZ7!2gEuB5S^3r+!F#b%cm%)NJv~K1f-#J*bJ>ilUeJi!Cnh7%$A>*Y11UK zf?A`8DYTOOmYV{Dfupmina zH|3Y-9Pz&h)}D~GO@QjZMOg4S`?9SwKpiSp43Zqcvj5#A4oD{AK0p?>(jEA5CgahY z*wZJEz1dNke~eYaPsNgqFM=de3Dx5VuM*;5%G)plrdl2X9iTwgMPFUrbS@4hKdf2( z8ZfF51fSrmS=hR-s6a01Ewi5y0hE_=Ny*X5vTgnscs;KHO`O!(w$)>$>>Mm(6@~cg z+)!YK6u#CKRgUNq)5FHn6Ni|S<;OGbE>iXOcG?deR^3!y8%m{(*5TZ$P;KsS?*Lz~ z3mjav1043*H-AgeQO<#5mu%~C$)xGi@T1sjMT`^R3^x&i9nT9}>mHPHTi|MCOFVDB zbZQ5c4kXLNz&nsOYzl;pVTInlJAq<#p@afR@$!J2aX7?xuX_Ln`t!32AEn3~$%d_C zx1P6lAuzGk_%)WK5z1jPVtIHZVcmh?fJsw&2s-ux<}qfKh!aOD4Wt7HYNHv3Lz42` z4?om%0L-j|AO!5YPeMUCQHZZ2at#kAl7dW>*nujXQEdiFk)0urkg&i)AR z#W*m`$e}FNX&*3C2OFLR*G$VL%a2py87XUm3QFDx{i!$?)?BXD zrHrSu#q;)3pa8Lr7Mf`N8xun63@h2>0fFmS}YbW6jLHJ70E$R6ZSYJcIU(WTok&sY>ki(Uj8DQzYQo> z%n&fEq4h}6mfm$3S5;$O2=#yaY;9Jw_ug8>5aOVOe#yX$R_$W&e#9cqrMu9vdzKw+rhX~7USnq8Zoay@1 z&JlUfyxO184K{JNF zeo|bbeF~YEfMFzJ?TraJS-dKFF3hBueb3;C%MtwETQZI4eza>c0$}4A;i`oCoC*%J zHS=&YaGw{E-vxnXRwW4Sq~GmMdPZ=O5j>I57q2*CbQA5puR2;KDM+{CV`MGVZmBAf zXzBco1Mw47BG}1u(QgbvAR6c4{o-(G^mMUp|3W6$a7bYe>X{<7dKs)eNyexS#XAdf zQ};4S{q^WsH?4>>$*{J$5vgT3Up;&xT`kf^>%JF!BE`h`Gx!(Sd;10S!%v$v)UpE1 zoN{VhS3jD@zCXM#N5T?F*Kost`1Li3Dj`Ab1sb@}WFYp~gKjkiU#$evMhdqN_U5rp zfNAhNG8Xjx$jC3+e`1>URQ!+(EC#FueN-bi(niwUNaz^C?Iet>eVO6P0E*lq+72_H zEK5P4@tgwx4$(U#=Yuf^NGd zk=e@1-#@Y2gUp-?IDORzN6#J=rajl7;2-9N#mW=vFVqgeF-_9huV`l0dWKTOHcZSb zQ0HUq?;8(?u`G^qUtboHL8Oo)(k4-SVSd>Q!E-x}YlDCI9Kmm~X^M{|jkBRZ`6HM( zC;U(;F4+kzks|vJvHqp|DDZ~jgN>Qi%@7k={YNin`Q2CMJ>w6^t!&LH^WP74@w+3A zr2LK1GX>lscmRrxy9$t=J#?t%FF1+MqM!Wh6JmYvG!Ho#P9*S~V}KC5L2wXcoYDM0 zzAF7}GF}K*gV2cZj)dl{BhY8;AkjK8S5J%=M=fk@;>AE!{dwpCR6|;;V**iNxxHCR z#*bK`O1%K|1>%Ag_?7tZ2{z`RBJvX~t|Y<1=7L5AwwfWG9q;p{HpV)L(0!xGRM?-HCZo_^?`bh!93rvG(A zR;D>^PYSLHkW`;S%(UN)zWJtRJGQ5V2+f*k>WXt^Mz?Usy^e`fEvRADLGD01yC7O2 z&YIwjkjrb^e4y`qRiJx0{Ctx=wuhbJS%{se(r9vjDp%(FoZZNn=z4n2H>7)+-niL! zUzlpyOrO}Y9f)E-ws$z5eHzBLY4xjiI6kx=7$7F*t|2BK7&uV<+^8pg+6KRAX*sb! zbM*M&eer(z3P;uTUfbGQaQN}k<*j?X__*h%74hs9>I-fT2` zIQH|?l3D2fYHm0fm=tZ3{qyZ;w_=ioD_L*+9x>z4_MPMX=4NVov7DDD@Huhhn6=)x zf!WdZHc9Ml=hmfb*lR6#WU!6iqmySdQHL{AM>F$4QhqsY-x_7-c*HSwl+I&RPrb(c z9c}t(9VlmHkg(*+8F5z+gDbY3@36J(T>7bAdF(fZ+QCZvp$99WnJ!`u$+PJ4mZ<1P z)h|#5W+Iw8U3dvd(ci*3_^AEmN86Oq%X5{U8rk{=h|SaKx`mcz>KeOfZMF%dJ+2hb zD+u)XH-pNs-gk{qSByh`rJ3(o95tMB_ZG&Q_9pvg0m!4W}SAe@+Fv?c+&JZgS;2VeCBIRaYuA z9%J#%;d9Yqj$a2y1c$WtRzI4cGJWszI#}7lVob66bVjr7wi4pxkXCu1$5{VTpyVy( zKEKmd)@pLG^eKTr;4UYs$HQ1}XW*nRhx;DrHQH0^tx!D;z6`2y-dOm`cinp~iY>zm z0TuXw4;4HocTUC{f0bAUXlB!vB64eZ$SnnHTmx>DsFiHEMJA-ns-LA2)D%Con4KQ1 zQm=%QHet#8Z4V)vxZ(RPW-y$;G1rmH2s0@$YPv7BNV|56z{YeM9Q4^5 zpPv!#lcMhvAJ1XCPgj#V?vqdJw%Ay=^zNVp0fUWBXVyreJ8}Y44g84bX^Ta^)cZtK z>1G$=C)^gEWqhn;3BNjq=!qZZ0@2FM0gp-ByD7R#G}l20PV#AGjkw@X=#8J;;+^Ph z%PV^)`Ud5g8m=j0X>24t46PWHmFv<*9&7FCsxF~+#4v>EqzHCfcw`V`dMR*uBY&s3 z=O5lt?0>|HO>ffQ62k}t`(}8z0DRwQF-t7LCN^m^P>`T)?#R`{=fDh%9Js}<)IFmiX!&P>@n%*=Y2Zm^kiBb=_e577=f-Rr|OZ7nLtI{sHeluIMtI(9^F(c@(s9$BI{xY*Y)=u0ZYO^O3~lm`R_ zVKq^CD(pTFK-Oi^i(uaj&>(jdr*PF6nzU%SK*Vr=YV7_mkU7>db;QCY z3YYzhzp7ADC)dD>E;pJ25pd@-b4la?8G;g*F#@h7UgT}!zgVIkiCRTa7tcB( z>~*|lpQBuJV7F?V%NtcI?FS5uMU6@SvFlq8&v`k!po30G)&^v>BB|FzCIKQ+nL(Ym z>s7soe*iu^0MYHpeBtm4p{3Y1XJ0;Q+{Uiz+={{h^%`1<)_ARX^9*YhLhJgTw$3*yrVnOt8|K%q1ECNC3!rkU`)>9!gUIlR} zDiRLU#~MtH#g&wcBn>{CySM$j?$4-?xkEK%ngR@m-f+1oO)!CQA5}NMQxl2#bGp(N zs23LEO{W(xN-M+^-DeZ^70l=(|4DmlZP0V33c7<7kP}W7FyhzOE2mc>udqe^>Q{;v z(RSwt@l~8iy8zxmXX;TMx-5d1c+u?aLtyTK*#tm25tly(0BrjX;^T?{*J|FlB=^TscSGd~1mB-5ycRy~rd?NhuQI_^%s7jP&sA=iYij!nf#!XrSF_}vsm|v(+pUY{G zsa}$TytWoVUI5Sg<_Lz?Lg`?NheiX=8SNLAT{R}#{XWwKDnvI_lg`GN&xbVr@Y+g~AG;yBi(A#k!61cDRe? z^KNAk=@dreNcmSu9N@d_TrHpTAxYkj#aJy>bq|tv2P(_+9`L6Z1W|I_wS2&TuqF@C z9lKN^;n64STk%77Q~tZxI9drg$`DNtKA|u6MaT(l8c>fpj+S`f^V@^{AjSjDHT{Xq z?xq9_mLFlfOsm2 zahx7?+hCYWa02UZCh%}Z{y7Ot6+G&;k_GEc&HU%=pn#algb*oZ~ z1c)Op@P0RkE*tz^NAayv^G&4^N8|yX2^Iw;e&6+sD6v!G>-oTy=gzfr95~G&*2sH@ z(2&dNy96y>pSj1l$vW?JXZ@!so8iXXAfAy@x|>p}m*|ZIddC1ogtz@RH?jF{ zfVciOAk%1@Zp5PO%KDOuD?Ml3jw=iIrzyST%qV)i-O`IPT`L*WW6L9{VzRa1yBWmW z`-qWSh>9Zkux}C)zYC~NwsOC@dOx>uZwwFaS*Hhey?=5RMHlGB(Dl=|5|Ph)g3s84 zzsFx!7t$PrR`G3ta5nzNiaqtqF9}^c#0R6Bek3QS?UDBLZiI(at~6WQMyN{C)`&k1 zZ2b<}jDDvK#1yOgS{{g=58yg7;Us%6!|^fbWRj!%ZTPt0F2gcK*X!+{`yiLWu64&p z^U-52PEjzm!-3n#c@}c{SG00lfCaA2(IA%h;!YrjWe0?t>1i`(vdfV>wU3%iEtbru zhwv@X0OUR66HkMM&xZN;sCM)?fzMO5UGmwdSBFT_+AF)ul?`Xs&4y7G!dq6yb9L#_ z3+6s;#A`vwaJuYq4HgpB%Rvne(&PvErnO6^Q-e5r_3SIh#wZy!a#Ylyb&JbESLKF| zqM`%$wxmtX*=_=ztiRK5Y9L<1-N&`nbg`qs2MI1t^b6LGx&uSsII}bl`&c^`l2qKh z5@(BVkmYo~`Q^HOt@YA>@YV0U7EMfx5!sW0m~0Z(X0f>_cBVbC2tcYD%%|xc&~6|9 zGRXa@u3LEv58q5wuU>@yoz;UQwa{xPH*z6g7b1jo_g{B9thM1fKy$*o-wh4Kt7zh2 z#zf$!aKJGoBqU`2mWL(qsMJ;Lb(!>~m)To?vmIP(fTYvlZcF6yE8F;x=e3hOFZ@z< zchSwOU~5w`VOnfiqLVC)Bf513N#(3BW2L^QtK03$h@uP-FoGXed}bWddvRhGjm7MTjpYU za&--R5Ah*;eX|!YJCe0n^DBgWWqFIqV|`vcmDEX@>`AWl)nc;dG-;LuO!z6U(@@c4 zUi-xYbRPG)$FF};A+;em{q1vqkL!_*YnhF6!vh-D!;Yel~V5W~^T8e@d>nm-Wn616eJ|X`yZEY3Sr#ndt0N zlC7^F`F**qoo4LRvGOmkp#yB;@JsQ!PxUq`-pT`;(%CU9)~nzEMSg#qsv9R6h36Lb zJE!&>SqB*?7|AxU4@zzA6(fTrx3Yg#oMU&#Lcca{PHrA#p0^ETE}pCDy$xDg+Pmab zzUg5NuJnwIgQ+?+Ep``{A?>KCy5eF(#mc$?X(a6mBxaLpqDSiOmL0eEvl7`HR0Nd&qYHs&gi zTmz-{%f*ta(f8}ciTFp0s)l;^y0H@{C^kJul!_yiJ#Cs;=ry9Y61(I|eueRST1)Ck z+D0k^a}HgI?SzxfgY~rFikE)_+Xh?ZZB)_F2ts7}g)FJZyVzmqYB{I11+2?*H0bwNRG9@l6oo=`k|1hDQBub~k ze0iY4FtGn_Qs2kd%Ep>~4X`V}Rm%^-CK<&XkUNY*s;aXTB}%nojpYK#NSk~_0O(cA zSs8%KL`Ct3-PM&(pFk7vP-*f0f}aa^!yj2$7C7;GZ>LK{@yC6>76(c`%&bw}>MfPg zT~J`F1M6FRX9)Xq*DT=YTpB@^tN|CLi(Dc+4=NaBcG>o~6fth1Vs>N)JZh|PmRTw0 zo^Sl<=H|#(Bk_b2BAN=an;Vv0=l~ZoR8k%v;D1}awXG2$!xP=&YL3aG@bpp8Mw9oI z$edBB%p;Id2A4^n^z?yC7%w=rhEt9zeX6~e%l?9G4eGxw_1uAT^$*dH6-&NKWxFz| zrgzL(sK{TM!6EQq-cJfaSp-;{M1r5QLydD?FGq-`apHWcEHcOSRf$b^hw7$~vi9^c z6uw~1@DJr>0Oc;4AU`AtLZi*+t9%vB$eD{=PI$an$j@C>{QZ@b&1}wA+XJbvcmjTn zUi9(+sqG*4tO(jU=F~shmFpLTU;zmLVxo2pQKIcb290-SP3UqHhJu}b2qYP1Tqf4A z7H8h+co2HNKo}^08^LKXCO_lGK!E>|;5_d!G6GTvdorQyE55asMd)*jl0nbp-c;_q zJ{8+HXy3kh?zIr4_^uB@r7j*<+%>uK4jvO@WtPY_azdJ@a&)vMnj)Zv=LH2;9+2Ed zSJpqao)ZQLI?&3vvALQO-`uI~WTAa^L#_4!zf!egXrhk8amkVXv$su07x$F87URWoUu(#K_NV|iA27}uP*`Xhrs?vj1H z@Xx;aZTnm&iN7D#`?oJ$KxQ3w?bz+(Ks0!A`O%+naV-}50h4V*N#W7S?PLp@^t*kr{w3{tV^X4v|L{RV#mW+$G zs+_qkxGTNYSI*|MkKPk71wqU*=i*q4!0v+X+rGBkD0LSZltC52S0i32JFivcWcjRy?N;VSoY(&1js9BPcTT80hH??!Ak5=+P*$$>aJ0FBt3i4^ID=j zz^!R!GHa@L59-aXWic27g25t#PFi}XrqcKu6%X-;6C(F6hf5sQt}L%MKV>ATvLXlv zM3OEvTXm>MrLbg+HIRlwM@yEJiL2269oV(ETTU#ixt<&=yly;@#qMclOPV7JC&wsM zn$EHaK3gc55+n;*bxPxtj&m=dPS0m6(&gCQJ>=20x1}V1AErwlR9IG2JoFRn(uYUX^-MM&T1Wom%hItTn{KJPjw3Hi5=H|NEW@3>TjY1#ISw z4HREG%?^_!1A|IYw1WSqrwaBRhpWe`?)<`{V@WULb@yV$6H&a*0WaeYAR7BNoXueH zW5wocTQBz`%f;r&FaR(gk{})6KM!9vWqt-^1yHdxDIMRL6m;KjRTV4bao9m1gTIU! z6nHfAbGE&SXp`0e9+P$>Q>~l(Pufkk`i78xPSGJ25SYr#i?05E(|>l$qcXa^RnvWR zW;d{Oq>FIyU}QM+BER0SC^?Qs@E0g08M3&xEbdJ6b{K88J2?mS9%wZhDFg}R{Q2`C zEfWS)vM)Z&3mS!ZPJPR02_bXexl4N~6^?-ms@!2R%JPLKQqm($9lc*i z*WKe%0EQ}OKB}Fl=;8Hw0a$5NKuYD7|FQrU5)dSz^RocX;{g!`)RXzDf(Ei9g7w-G zp!yTcKB4yuo`1b4=E0Pq!Sf&_9M}g2S0JR+rb5$|e!)2$D@1mi3uVM+P9UtqKgIT* zhNgTc@>)`ds}Q{dtMNt~&z|w#s?a4pSAw5{@7j%UIsnR zgr8Z#^|g;jd3@XT!X-CRMMyW<%4eo!kYNEDfneX^VM{wb-Ky8?J8m@V+nvr@(|c@f z?#s*S5G;1v`3@rWd?}@rEY@SO%)^EF>s!F=l@#O(-eURnG~jgczOpu=26yXUJiX+!E}tW;u>8CcI`OK}Ibs2xIaRbb$#+4D}Il38h9Wu}n*qa7Kck z2Yr)abDV&CWx);}V3s&z?B$7eB<=zyOp=J-rL8M#>IUlFxD=_$U8`mm$Tf(MZAupCbMQ8dT}3kPk!~7m00F+bXPf+8!R*K=b@fPN)SxCA)Q|Y;v#A3? zb<}8pNtlw*FJu#v7pkMrrfWCV2fpzn1k8fJav3++e&<|D6{b$3`xb?*T2dg-7v&Kd zZ3GJzLCQCyXJdq?Ezh`qTB{kElw{r5{97l94XAynE-zn9E72d}&eBVZvhJ=eTE|X4 zDhqW8bd8G`4jPJQ0>NYdEbT|I zyh>rs8GKVZBEpDdCj7%uIB7G7=}3aA^)+(iLl?~Y1!kb`|EQVhExZ=?gsOsF4XGxz zV8kep^hOAYPpy_MRaxQ0(feZAV+;wD+I^D7Ovh_mj=N!$AdaA+85Yg&3Ty@n(pP9e z%LOet{++}pRqG$Gr+X=7e=>&)cdf@3V&dU}sBAm*&qk&I^!c<8;HWH!5?(?Y_CTGV z*LMiOqQmKlf@Ji0>r2>xGl1?cfFI2f$n5bB1q(8~XER z?C{0ort4s1>rIyPCy4txU2zwenbIHbo-g$UIREyuPyhQDdBAay8MWm`b~&ZE%-=ck z`20K#TI2U}&u^Ft|B~*R*T^#|8rfOX$j&pl;hEw|fE_j9NtZ-TEAs-X(6&vabIp3s zpcVJ2^GeiGLe%aQFQzk{B{X$Is?A8^;D$LrX+#aR}%cT z%zs|6?N&9wBQe3@+>)}hfG0?qu3Bhw1CGtSR>Oz&`z0ajSZEo)KM}rn_j#4vew4TQ zom)ZV#%U{O8}lfQPeUd1Q`*`+3tq->$t$Sa-O9_~n!1DatP%b?W8`KL=Pd?{es?O| z{m-p_>=-tm?-L&d_Q%DCq;won0fuH0yZEOfv(WEaCU?44(A=MCwm=B5QpD~WbM(Zf ziEs_O`1YTqfIfliy)ld4UJX4ZeaS_4zYnfKBY*@2hYgkN5Km6?*#k?7sSnf`O1D&B zx>WJRF&I!dm{^@;X*RQ(7yzTj3}J1=%H?bW&;CZFh5RMFn{-dSy;`Nwf_Yl?(wa5} znG3JGkUHeRY4LLEqO`o%kq($d5^M8fE>a!hkFROuR9#gZoVAarJi|y&m`c@?0I2#j z7=#j0(H0<3n=f&YyRRn#DybaQ>dAW1iU=jk2ZDM)exF{IkK|3yjxbCod&_YqJ#=%7 znH&oPR-tL5#~Ca`7s*skjHj4fuCIcvmQp~W-`so%WB(k}Kx9>!9>%R^Gs;qdY419z zEU>E-00k9MH-+#e!j4#5`eN-k!`*GRXGcCAm>*5W0wccw-d{jBA=tO%tuHL-Xi#W0 zF$G6q^6rT(KM=$o6w8#f`0OcFtvcM9_6240$;^o0z@l z{tI6%%No>yvG{Z~rc4@S0&)j;TCQMGY0M$H-XL?Oon`*ID2`@1OJe&>L9%v;AgO#e zA8~~~U5O8ZAf%}+{yd7b9txCy+Y$MCVjN1q4(mdlC?al_z&Zg`D2T#TfHLA@}qh12-kv%_rRcFabsUr-E{Y9ehjcnL05a zj(4$&sG(J~tx0k|@Q;K47I#=<=d#%wc+ra>RviGY5Z!&B1;Kiubm9Pc6yH@}!U@*_ z>PePoGu`-LyKdvC=0#?g+RA{DmGuT`eI0VvsOXIcukcqWv8~g1!^gFH-;}QYi2AG=GzGbHkkIej352 z(VRD`J1{zfGG;o3O3TGqufKj=FmW#8dQ(1?-B3B|zB)7O;k19tGl^IPt>5;PWY|qD zRBhI6uEq}#s7$MOt^ND$VQM-8*PwK|_$W8Sq=tch^gExzpY;rIIHf>ut-bI5-d=`U z2HrP&asLtElu}o#fp~h+gUMMMCuN2K#}@_G9TuxXv9BI0;*y$AvrNaU`M$vvWeuP2 z%I?Xo*DNNdcxB=+F{z}KwVKC>v?`YOElgc*wS>oO{ov>*z==riQkJJ02KC|5PK!~N zTbWT7ATy-Tg5ow+#uOcY_&CdFVVf^9QaDI(-fb1SlQTxsrc1e4;4qY!u1}rJ$cWbH zFN2f#*oII4kEQC0eP?^Se_t-i-k>P~R{k#xcyC>kEz@xxh;b*C{f~dJ@j_JtpTSin;r zOR&=>pk0Nm9_SUvKlEA$K4_yJ(HmROLYPaLEY-7K4&Lp^LH*t7!_ev(_@{rgK0HZT zqA2aU%OnE}5s^2G9cFl6!W{}7X0E^!v%LdLCet$v93>E9dIS}wd0W8eO0=(UbQ?r~ zeu1%73I(8CD0p(d?l7f_n2a&enLcIpk7Ia^F0PiwGO&sa6jzHPHe?1WeQB^L!}0Zz z;f}*h$hVcLm=|uheIE=p31%|n+VC)!r-05dFlw5B7yNxW;Xrw(vY(5vGBI>QS`nKX~@wfV?y3Fp< z_eUwZ);h#!pSXPws}NLNfv<3FAKxE0!=2TB;YHd2R*Eg}2sOb7%&-K%yv$zf5PZe6 z%F5YperCP#T+Dy*$%N&`VkV7s6elIH(^{^dIy~DEZx9UjUdmcJU?Q)YSZVm@LW%jM z&$<`KrNRH+aIiwxUt-CDKq#Vhhj^BXIex@GLM=qrO=h02)^zl)G@P=);mZQ%Jx|ri zrLioIy-QH7HsY?-H!?<#q}o|+I=fvN*&et-QC^vAKlj% zE}wGGXC^t`YcE$Nx=%$Gk`sS^_8;ikI{y3^KOAeMIDN%n|1NISKX`|&md?GnvtOF7 zea5vH!}of^-B+O0sa|(uemAFQeEM5mQPTte*m0xXgsXC!0e2-?(E?8KrQfUk0byOh z5-r=~C;%TT+q0?g;k@=wp9RJRkWMD33K;?Qehbr~8 z+BK0`Z1(Y|3AhKLje%{cQGIU1^@s-O&rdB2cUv8bYZ&q{Qr$|lAvJ!c?t0M O4*HEIzmtvu0R9KLj~}N1 literal 0 HcmV?d00001