From 7a3d15c48b3cde1655574e6405e4ab6e8a1c1d77 Mon Sep 17 00:00:00 2001 From: Aurora Date: Thu, 22 Sep 2016 14:48:28 +0200 Subject: [PATCH] Got rid of CakeHax, patched CakeBrah to load arm9loaderhax.bin directly (lifting size restrictions in the process), got rid of the pathchanger (to have a custom path you can now enable the option and write it in a /luma/path.txt file, it must start with a / (this path is also picked up by the patched CakeBrah loader), fixed loading SafeA9LHInstaller and other payloads which need the OTP hash if having a PIN, fixed writing a file smaller than the existing one not removing the extra size, slightly changed the PIN format, added support for the alternate framebuffer and made the splash screen use it (it is now displayed all at once), fixed screen issues from CakeBrah --- .gitmodules | 3 - CakeHax | 1 - Makefile | 29 +---- diffs/1.diff | 116 +++++++++++++++++ menuhax/menuhax.diff => diffs/2.diff | 2 +- injector/source/patcher.h | 3 +- patches/reboot.s | 54 +++----- pathchanger/pathchanger.c | 97 -------------- pathchanger/pathchanger.py | 40 ------ pathchanger/prebuilt/Linux/pathchanger | Bin 13339 -> 0 bytes pathchanger/prebuilt/Mac OS X/pathchanger | Bin 9308 -> 0 bytes pathchanger/prebuilt/Windows/pathchanger.exe | Bin 71578 -> 0 bytes source/config.c | 7 +- source/config.h | 5 +- source/crypto.c | 15 ++- source/crypto.h | 3 +- source/draw.c | 9 +- source/draw.h | 6 - source/firm.c | 4 +- source/fs.c | 3 + source/patches.c | 27 ++++ source/pin.c | 29 +++-- source/pin.h | 5 +- source/screen.c | 128 ++++++++++++------- source/screen.h | 11 +- source/utils.c | 4 +- 26 files changed, 318 insertions(+), 283 deletions(-) delete mode 160000 CakeHax create mode 100644 diffs/1.diff rename menuhax/menuhax.diff => diffs/2.diff (78%) delete mode 100644 pathchanger/pathchanger.c delete mode 100644 pathchanger/pathchanger.py delete mode 100755 pathchanger/prebuilt/Linux/pathchanger delete mode 100755 pathchanger/prebuilt/Mac OS X/pathchanger delete mode 100755 pathchanger/prebuilt/Windows/pathchanger.exe diff --git a/.gitmodules b/.gitmodules index 41f0849..154c12f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ [submodule "CakeBrah"] path = CakeBrah url = https://github.com/mid-kid/CakeBrah -[submodule "CakeHax"] - path = CakeHax - url = https://github.com/mid-kid/CakeHax diff --git a/CakeHax b/CakeHax deleted file mode 160000 index 5245c7b..0000000 --- a/CakeHax +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5245c7b9dc232956a8578a36468f9024d8cf7001 diff --git a/Makefile b/Makefile index 9e56076..4ff4dba 100644 --- a/Makefile +++ b/Makefile @@ -22,10 +22,8 @@ dir_injector := injector dir_exceptions := exceptions dir_arm9_exceptions := $(dir_exceptions)/arm9 dir_arm11_exceptions := $(dir_exceptions)/arm11 -dir_mset := CakeHax dir_ninjhax := CakeBrah -dir_menuhax := menuhax -dir_pathchanger := pathchanger +dir_diffs := diffs dir_build := build dir_out := out @@ -42,10 +40,7 @@ bundled = $(dir_build)/rebootpatch.h $(dir_build)/emunandpatch.h $(dir_build)/sv $(dir_build)/k11modulespatch.h $(dir_build)/arm9_exceptions.h $(dir_build)/arm11_exceptions.h .PHONY: all -all: launcher a9lh ninjhax menuhax - -.PHONY: launcher -launcher: $(dir_out)/$(name).dat +all: a9lh ninjhax menuhax .PHONY: a9lh a9lh: $(dir_out)/arm9loaderhax.bin @@ -61,7 +56,6 @@ release: $(dir_out)/$(name)$(revision).7z .PHONY: clean clean: - @$(MAKE) $(FLAGS) -C $(dir_mset) clean @$(MAKE) $(FLAGS) -C $(dir_ninjhax) clean @$(MAKE) -C $(dir_loader) clean @$(MAKE) -C $(dir_arm9_exceptions) clean @@ -72,30 +66,21 @@ clean: $(dir_out): @mkdir -p "$(dir_out)" -$(dir_out)/$(name).dat: $(dir_build)/main.bin $(dir_out) - @$(MAKE) $(FLAGS) -C $(dir_mset) launcher - @dd if=$(dir_build)/main.bin of=$@ bs=512 seek=144 - -$(dir_out)/menuhax/boot.3dsx: $(dir_menuhax)/menuhax.diff $(dir_out) +$(dir_out)/menuhax/boot.3dsx: $(dir_diffs) $(dir_out) @mkdir -p "$(@D)" - @cd $(dir_ninjhax); patch -p1 < ../$(dir_menuhax)/menuhax.diff; $(MAKE) $(FLAGS); git reset --hard + @cd $(dir_ninjhax); patch -p1 < ../$(dir_diffs)/1.diff; patch -p1 < ../$(dir_diffs)/2.diff; $(MAKE) $(FLAGS); git reset --hard @mv $(dir_out)/$(name).3dsx $@ @rm $(dir_out)/$(name).smdh $(dir_out)/arm9loaderhax.bin: $(dir_build)/main.bin $(dir_out) @cp -a $(dir_build)/main.bin $@ -$(dir_out)/3ds/$(name): $(dir_out) +$(dir_out)/3ds/$(name): $(dir_diffs) $(dir_out) @mkdir -p "$@" - @$(MAKE) $(FLAGS) -C $(dir_ninjhax) + @cd $(dir_ninjhax); patch -p1 < ../$(dir_diffs)/1.diff; $(MAKE) $(FLAGS); git reset --hard @mv $(dir_out)/$(name).3dsx $(dir_out)/$(name).smdh $@ -$(dir_out)/pathchanger: $(dir_pathchanger)/pathchanger.py $(dir_pathchanger)/prebuilt $(dir_out) - @mkdir -p "$@" - @cp $(dir_pathchanger)/pathchanger.py $@ - @cp -rfT $(dir_pathchanger)/prebuilt $@ - -$(dir_out)/$(name)$(revision).7z: all $(dir_out)/pathchanger +$(dir_out)/$(name)$(revision).7z: all @7z a -mx $@ ./$(@D)/* ./$(dir_exceptions)/exception_dump_parser.py $(dir_build)/main.bin: $(dir_build)/main.elf diff --git a/diffs/1.diff b/diffs/1.diff new file mode 100644 index 0000000..7326b25 --- /dev/null +++ b/diffs/1.diff @@ -0,0 +1,116 @@ +diff -uNr a/include/brahma.h b/include/brahma.h +--- a/include/brahma.h 2016-09-21 16:18:56.246840000 +0200 ++++ b/include/brahma.h 2016-09-21 16:20:28.975957322 +0200 +@@ -4,7 +4,7 @@ + + u32 brahma_init (void); + u32 brahma_exit (void); +-s32 load_arm9_payload_offset (char *filename, u32 offset, u32 max_psize); ++s32 load_arm9_payload_offset (void); + s32 load_arm9_payload_from_mem (u8* data, u32 dsize); + void redirect_codeflow (u32 *dst_addr, u32 *src_addr); + s32 map_arm9_payload (void); +@@ -13,8 +13,6 @@ + s32 get_exploit_data (struct exploit_data *data); + s32 firm_reboot (); + +-#define load_arm9_payload(filename) load_arm9_payload_offset(filename, 0, 0) +- + #define BRAHMA_NETWORK_PORT 80 + + #define ARM_JUMPOUT 0xE51FF004 // LDR PC, [PC, -#04] +diff -uNr a/source/brahma.c b/source/brahma.c +--- a/source/brahma.c 2016-09-21 16:18:56.246840000 +0200 ++++ b/source/brahma.c 2016-09-21 16:21:33.240730777 +0200 +@@ -179,39 +179,56 @@ + return g_ext_arm9_loaded; + } + +-/* reads ARM9 payload from a given path. +- filename: full path of payload +- offset: offset of the payload in the file +- max_psize: the maximum size of the payload that should be loaded (if 0, ARM9_MAX_PAYLOAD_SIZE. Should be smaller than ARM9_MAX_PAYLOAD_SIZE) ++/* reads Luma payload + returns: 0 on failure, 1 on success */ +-s32 load_arm9_payload_offset (char *filename, u32 offset, u32 max_psize) { ++s32 load_arm9_payload_offset (void) { + s32 result = 0; + u32 fsize = 0; + u32 psize = 0; ++ bool use_default = true; ++ FILE *f; + +- if (max_psize == 0 || max_psize > ARM9_PAYLOAD_MAX_SIZE) +- max_psize = ARM9_PAYLOAD_MAX_SIZE; ++ FILE *p = fopen("/luma/path.txt", "r"); + +- if (!filename) +- return result; ++ if (p) { ++ fseek(p , 0, SEEK_END); ++ psize = ftell(p); ++ if (psize < 39 && psize > 5) { ++ char path[psize + 1]; ++ ++ fseek(p, 0, SEEK_SET); ++ u32 bytes_read = fread(path, 1, psize, p); ++ ++ if (bytes_read == psize) { ++ if (path[psize - 1] == 0xA) psize--; ++ if (path[psize - 1] == 0xD) psize--; ++ if (psize > 5 && path[0] == '/' && memcmp(&path[psize - 4], ".bin", 4)) { ++ path[psize] = 0; ++ f = fopen(path, "rb"); ++ use_default = false; ++ } ++ } ++ } ++ fclose(p); ++ } ++ ++ if (use_default) f = fopen("/arm9loaderhax.bin", "rb"); ++ ++ u32 max_size = ARM9_PAYLOAD_MAX_SIZE; + +- FILE *f = fopen(filename, "rb"); + if (f) { +- fseek(f , 0, SEEK_END); ++ fseek(f, 0, SEEK_END); + fsize = ftell(f); + +- if (offset < fsize) { +- psize = fsize - offset; +- if (psize > max_psize) +- psize = max_psize; +- +- g_ext_arm9_size = psize; +- +- fseek(f, offset, SEEK_SET); +- if (psize >= 8) { +- u32 bytes_read = fread(g_ext_arm9_buf, 1, psize, f); +- result = (g_ext_arm9_loaded = (bytes_read == psize)); +- } ++ if (fsize > max_size) ++ fsize = max_size; ++ ++ g_ext_arm9_size = fsize; ++ ++ fseek(f, 0, SEEK_SET); ++ if (fsize >= 8) { ++ u32 bytes_read = fread(g_ext_arm9_buf, 1, fsize, f); ++ result = (g_ext_arm9_loaded = (bytes_read == fsize)); + } + fclose(f); + } +diff -uNr a/source/main.c b/source/main.c +--- a/source/main.c 2016-09-21 16:18:56.246840000 +0200 ++++ b/source/main.c 2016-09-21 16:20:28.979957377 +0200 +@@ -10,7 +10,7 @@ + + int main (void) { + if (brahma_init()) { +- if (load_arm9_payload_offset("/" LAUNCHER_PATH, 0x12000, 0x10000) != 1) ++ if (load_arm9_payload_offset() != 1) + goto error; + firm_reboot(); + brahma_exit(); diff --git a/menuhax/menuhax.diff b/diffs/2.diff similarity index 78% rename from menuhax/menuhax.diff rename to diffs/2.diff index af9f93c..47ffe5f 100644 --- a/menuhax/menuhax.diff +++ b/diffs/2.diff @@ -7,5 +7,5 @@ diff -uNr a/source/main.c b/source/main.c int main (void) { + svcSleepThread(2500 * 1000000ULL); if (brahma_init()) { - if (load_arm9_payload_offset("/" LAUNCHER_PATH, 0x12000, 0x10000) != 1) + if (load_arm9_payload_offset() != 1) goto error; diff --git a/injector/source/patcher.h b/injector/source/patcher.h index 498c7aa..3520ed7 100644 --- a/injector/source/patcher.h +++ b/injector/source/patcher.h @@ -26,7 +26,8 @@ enum singleOptions { AUTOBOOTSYS = 0, USESYSFIRM, - SDFIRMSANDMODULES, + LOADSDFIRMSANDMODULES, + USECUSTOMPATH, USELANGEMUANDCODE, PATCHVERSTRING, SHOWGBABOOT, diff --git a/patches/reboot.s b/patches/reboot.s index 9174a23..75756e6 100644 --- a/patches/reboot.s +++ b/patches/reboot.s @@ -1,7 +1,7 @@ .arm.little payload_addr equ 0x23F00000 ; Brahma payload address. -payload_maxsize equ 0x10000 ; Maximum size for the payload (maximum that CakeBrah supports). +payload_maxsize equ 0x100000 ; Maximum size for the payload (maximum that CakeBrah supports). .create "build/reboot.bin", 0 .arm @@ -25,38 +25,22 @@ payload_maxsize equ 0x10000 ; Maximum size for the payload (maximum that CakeB cmp r0, r2 bne pxi_wait_recv - mov r4, #0 - adr r1, bin_fname - b open_payload + ; Open file + add r0, r7, #8 + adr r1, fname + mov r2, #1 + ldr r6, [fopen] + orr r6, 1 + blx r6 - fallback: - mov r4, #1 - adr r1, dat_fname - - open_payload: - ; Open file - add r0, r7, #8 - mov r2, #1 - ldr r6, [fopen] - orr r6, 1 - blx r6 - cmp r0, #0 - bne fallback ; If the .bin is not found, try the .dat. - - read_payload: - ; Read file - mov r0, r7 - adr r1, bytes_read - ldr r2, =payload_addr - cmp r4, #0 - movne r3, #0x12000 ; Skip the first 0x12000 bytes. - moveq r3, payload_maxsize - ldr r6, [r7] - ldr r6, [r6, #0x28] - blx r6 - cmp r4, #0 - movne r4, #0 - bne read_payload ; Go read the real payload. + ; Read file + mov r0, r7 + adr r1, bytes_read + ldr r2, =payload_addr + mov r3, payload_maxsize + ldr r6, [r7] + ldr r6, [r6, #0x28] + blx r6 ; Copy the low TID (in UTF-16) of the wanted firm to the 5th byte of the payload add r0, r8, 0x1A @@ -87,10 +71,8 @@ payload_maxsize equ 0x10000 ; Maximum size for the payload (maximum that CakeB bytes_read: .word 0 fopen: .ascii "OPEN" .pool -bin_fname: .dcw "sdmc:/arm9loaderhax.bin" - .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -dat_fname: .dcw "sdmc:/Luma3DS.dat" - .word 0 +fname: .dcw "sdmc:/arm9loaderhax.bin" + .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 .align 4 kernelcode_start: diff --git a/pathchanger/pathchanger.c b/pathchanger/pathchanger.c deleted file mode 100644 index 3a3a9b9..0000000 --- a/pathchanger/pathchanger.c +++ /dev/null @@ -1,97 +0,0 @@ -#include -#include -#include -#include - -typedef uint8_t u8; - -static u8 *memsearch(u8 *startPos, const void *pattern, int size, int patternSize) -{ - const u8 *patternc = (const u8 *)pattern; - int table[256]; - - //Preprocessing - int i; - for(i = 0; i < 256; i++) - table[i] = patternSize; - for(i = 0; i < patternSize - 1; i++) - table[patternc[i]] = patternSize - i - 1; - - //Searching - int j = 0; - while(j <= size - patternSize) - { - u8 c = startPos[j + patternSize - 1]; - if(patternc[patternSize - 1] == c && memcmp(pattern, startPos + j, patternSize - 1) == 0) - return startPos + j; - j += table[c]; - } - - return NULL; -} - -static int fsize(FILE *fp) -{ - fseek(fp, 0, SEEK_END); - int size = ftell(fp); - rewind(fp); - - return size; -} - -static void error(FILE *payload, const char *message) -{ - fclose(payload); - printf("%s, are you sure you're using a Luma3DS payload?\n", message); - exit(0); -} - -int main(int argc, char **argv) -{ - if(argc == 1) - { - printf("Usage: %s \n", argv[0]); - exit(0); - } - - FILE *payload; - size_t size; - - payload = fopen(argv[1], "rb+"); - size = fsize(payload); - if(size > 0x20000) - error(payload, "The input file is too large"); - - u8 *buffer = (u8 *)malloc(size); - fread(buffer, 1, size, payload); - - u8 pattern[] = {'s', 0, 'd', 0, 'm', 0, 'c', 0, ':', 0, '/', 0}; - - u8 *found = memsearch(buffer, pattern, size, sizeof(pattern)); - - if(found == NULL) - { - free(buffer); - error(payload, "Pattern not found"); - } - - u8 input[38] = {0}; - u8 payloadname[2 * (sizeof(input) - 1)] = {0}; - - printf("Enter the payload's path (37 characters max): "); - scanf("%37s", input); - - unsigned int i; - for (i = 0; i < sizeof(input) - 1; i++) - payloadname[2 * i] = input[i]; - - memcpy(found + 12, payloadname, sizeof(payloadname)); - - rewind(payload); - fwrite(buffer, 1, size, payload); - - free(buffer); - fclose(payload); - - exit(0); -} \ No newline at end of file diff --git a/pathchanger/pathchanger.py b/pathchanger/pathchanger.py deleted file mode 100644 index cd4ea6c..0000000 --- a/pathchanger/pathchanger.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python -# Requires Python >= 3.2 or >= 2.7 - -# This is part of Luma3DS - -__author__ = "TuxSH" -__copyright__ = "Copyright (c) 2016 TuxSH" -__license__ = "GPLv3" -__version__ = "v1.0" - -import argparse - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Changes the path to Luma3DS for reboot patches") - parser.add_argument("payload", help="Path to the Luma3DS payload") - parser.add_argument("new_path", help="New Luma3DS payload path") - args = parser.parse_args() - data = b"" - - if len(args.new_path) > 37: - raise SystemExit("The new payload path is too large (37 characters max.)") - - with open(args.payload, "rb") as f: data = bytearray(f.read()) - - if len(data) == 0: raise SystemExit("Could not read {0}".format(args.payload)) - - if len(data) > 0x20000: - raise SystemExit("The input file is too large, are you sure you're using a Luma3DS payload?") - - found_index = data.find("sdmc:/".encode("utf-16-le")) - - if found_index == -1: - raise SystemExit("The pattern was not found, are you sure you're usinga a Luma3DS payload?") - - namebuf = args.new_path.encode("utf-16-le") - namebuf += b'\x00' * (74 - len(namebuf)) - - data[found_index + 12 : found_index + 12 + 74] = namebuf - - with open(args.payload, "wb+") as f: f.write(data) diff --git a/pathchanger/prebuilt/Linux/pathchanger b/pathchanger/prebuilt/Linux/pathchanger deleted file mode 100755 index 10f3e6530ec424ce452d98dc0730ae8179a24f3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13339 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 diff --git a/pathchanger/prebuilt/Windows/pathchanger.exe b/pathchanger/prebuilt/Windows/pathchanger.exe deleted file mode 100755 index 232a81d1153d4fe07b448a18d08f9251b0910b74..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 71578 zcmeFadtg&lwm*K7rjWLwCrZ$wb%;(-s(`KHNVICO4XsMC3Kf)AK%v5jfHVPAN?T6? z-5!G-A2ZG!odMA+a|dU*cbI~Tm_BHEIaWX@Pb;9FLn@Ysv=k)Y&)VmlHpO!1`}=56=QDGrO&{K`U!qG# zo$nQdF(#{!7Th~Q_zyui3qo3izHvz=^FZNw0R20@HbGc^IZ`{mRrnHhbZyq?HsO-! z=N~)naFcbNiT6tooEj`upgQ>IiFVT>2XCi|MBO;}O!s!{=#$E!3Cg_*(HT z$2aEV5vF*CPoFWl7}7a900nFCy#n9L7$95%pJ(_JB_))bo`|0&y88L2T$mCbK2vL# z^RNXl)b1a9!gBhu9v)A*oL6nQpni~SV?G?tH;cFHHMk1` zhy)p`#*dH@mg-X`)mntm+Ux25uGfLZH}J0CQZnYQ*O2)4e@uoE=@ar@FJ$bGK~B1( z++rYKfxBSX_W@d^f@stFC_a%t-E|Ei`E5)+B2MX?$BFWd0YL#jAW@GB^m^(vWEEHG zT`)2qBslbr2r_#2pM+?0RK2xl8E@nbz3fVqRT2F$l{xi300j4QO2xp53(+XccG;9Z zU>mq>Q@=uv#;DMS8_=duz`lV994nkFJk;4#;3GU7FR4C=qT6Vb)M}$^yGKw)5{FtX zN>c*5{TCRhQr$-oRydnDihJd2TEF%PD=k$X0gYSaEIYHfl$9WigJ^l_l=n){ynM=g z0eQ-A@goo580^YS5zQTKQ@)^<0#rhHtt*5kt$`I5xy&hD~ zMWd4Uw5g)>G=_g+@wW!Z^>V>!+1qMr5~~HVI!t$YjR_T~2rk?C|NQ&uB@9DD7 z8@=S%g-9{-DYqRB&3ao}%aGaBv&wvOr4HNUR$GNZNvrl8$0DH{p?SFprAX<&?J? z_#QAL&B<4EZeJdK0K*Eg`apz}p}fu2iVl zz!Mr5=U+*^+lZPmpqT*0ErC+aVH&=w7<@1@0{~1S6l7hGv-oy&3m1-Rl&fbsK^^?oUEmlf>*8 zVlsc?U4I!s(gqhLg8%xM^1w5ikoWau!)@dR#=9Q-mjvXkDa6V@Nyv2Drj(!Y%AJT-k*ixFWdE) zt|(WpK;8?;YYe#FB8u^e5YcoKatNU@uE+E{(`Hy#WwygEX{9D~lAv7kn*hOk9kvkx zAK#B$RwI3F0$s^sbz-Ge3Y$Qk6fsdYbxjo4B^EQ}_vm0F+kNUic9L~`2n+%{BzqY>0gjK3BOGiw>{l30pwIi|8VMV*7exwPXKHaT`|59xZYruvUMPlj#)p^_+ zcGH3D;wtT*Za=MK&cxI3U={-(nPEn6gL`ax7(}oJ#R~XPws4u$(xz;{Ae2@g!f?WB znYUgWQ@ls1YmMF}E!j?q)Z#&iGBC`%n=`@{(>Dv@Xtct++3o@2f?ZOBNvwW6DsJ2c zzP(U>?jAAFA7&uF(u%6(=WY-KM{rwy?nW_ij^|t_27iSdR`p6XlnZv5b{1Db z0`7ec_eKl-Kdv%~LBuyO|0dKYwMQ!%VOF$@n##y)YV>Xg3V}E3m$fh;2*vSU2PO^~s=whK5xQF+(%-6sC+$8_Qnk1Z` z60JUX-=^RW@r_!xhvKRc$fTY(#n8z*13SdvO6u>~GBNOPxCrH%DpSz)yj|+6Xh$yH zMU@%}LnpzZlbw1ey-o1(k20G}`OQr7_r5$iZ+mcyKfzsEFG@6mqT>yH$wmCE@1^xD z)1~Zn@USy)vx$}NLibQbP_H(^fE=yom4j3(vZ@+{GT4ne$%x;y&f3MmMDnqauCIhk zwufB(iI6EYU??H&V&x46EB^)`MM&RAswA}oMrS+d$q1`e$d|EAl2Eu-|%XBCEZPX07>ezcumgjHW-+wHSC0of?-B7T+Nr{th?mNj%~^ zXBtetzEVFI{b}YOgqqQj2lxJX6yJ=_&ul=^sws zuUvfRE=uNKLoMSjxMSgUyrRdKSiuRPx!R;tb=plifFli6dI_pTc2EcRAL$vOtgu!= zQ%UZF5aAYAMVoj|DBmJ`ENlRkLO)cg-gc^v!ahD^}i7Q=SS3Y(4|2U@lUZu^+ykOh9%72@por7HQDuCvv|bU>%%59}RzFJlzv-Bd-&^ zh%e=Amy3cysUyy3=WLhiQ)_JkkN@TIJYux~$cfm6M;Q~rhZwjX@jhaOR)=)gG&Eu+ z#J9^(reD~t7Gxp1tx9J{anK3n} zGr3RsTYFRjZVH0<*bdXNa{AyXHjpHigHd`soDuFR5BOsu(GD(VgGMFuDH!}UnYhG^ z1Khqzj$=UcUNOjRwZJ(?a0Ln3Fs4}LEDIaqiZ;oUtik>MFAW@TYQUlC9g`l1FIsy& z`f9XK?PZ*y7FT%C2r%^n9Bbo{`uxfUaNr`LDD~5+IacTMAV$+0U~v>NW?7V1(9jB^ zm2rw$mK8J_R9uci)`kq7j*T$GXynyMKN54-2lSAHI5BdOmFUR`%SGoAob&mufz1VM z@&JP`JA7BlW_PncF_JA?-Ftn5Sgm|7RVHoZe0&{rqir3RoNHzGb6u&Q#{0%$=4vpJ zeW<&VccUq?|L644jNNM^J@L*XrSvBltb39x&6Yaj{xN<5NdLDw9gXqCW%?+;)?3@A zL;D&PnJ47z9X`?IaIJ2_R5D84`C~)Cxo(h zJE`|ZA}p_k<%OY`v6wbJK?Pyyti|_N>8!{1XAHwKuWAKOLKuT(A+Vsn(K5Qs!)+oS zjL2L8^ZJZN6s}mt{Irl;fv>NwZ|hs$_Xxx2N9yR?7*C}npv5gT>u7aK9ZBM{L&%VC zBbeYBG58YQU2~M~_KU$qbk|S8-N&tzwM7i9Mb^gzEyF&riW9sK7^ON}#W~6pB?{cd z>Ty=N#3Cn2o%YZYT5^*SKB6=rqg3j2ih+~-!YT&-h6|dup-=CkPmmAwP*6@lGpcw) z8s?H)pilBR%ZC=KS!WHs$nm~Pzj5O_fFc!UJD;X zqV|ySzF%A=t^@ZxLNR*2ZufJ2Dz;)FAey%ILo?c_I7UUL&MwR}P^*`trdVx9%u;<4 z75)kMXvJ}YWG7LPxEoVt5mn)Iegd=ze(BIF6d@FP%tU$(e0iS5Sf z+PBWoIGO|8s;N!z3~TqtBk=Nh;cl*%&fepDNIH9?Zwx@sP7+@avz@5PtN3?jL>XN0 zI@L#xfZz?mnww9ko6SV*+KU?Nk5JLyU1g+tT>@nhxZi0MQWR@WcB0|J~k83H&^dAFBlR~3S z6vr1=Au_g!)uT-Si|j)GD6NUf<#!aPyE`cw+GEH?=12SEAK?OPsHx< zx2k;!R?}|{RX+7K(n9!=ZUY5KIaiRXg8XzMuik{;}^jlA%7HV zA_nFHy!=%4vAXik8~sTL&&2Bfxb*#) z5f1F|9gr8f>=mEG%?ObMh^l;XRPZ&jvzVhdD>m>JhO7XOu7~2rf+(~nkVG9RGhORj z1iI}?3499zp+?R6#A*@xu9{K1yfg|oy_5GT@}1OKCI(4h;>HOksnJY&NFGp4!Vl!j zcBdUuZ=-8X{^CXlxNmAV)u z9}Y*0#Z~GwfQlRYv^T4?`Yl%Hn87Ls^jM5MLi$t(_4eH$R>x%)k;40r!>K2T)mbK# zM82koYrOLq9A22!`qJbVEC@N}EL-S7lc~|PnLU7T_#=57^!r7k zpbIZ9^zeRi-9_;oWN+hT`uLB<<~DUfw|tC9$ie4K@k%R-Fpjg6N-e*_;OOg%yIr`W z=x7?K!^B|H<2pWyr)WTtT4%9>?JVzI3U}Ur6eAMZyq%U<9zBZ@l$Ka*(0ePQ77M{_ z0}Q24_d)ccEx0i6dd$t0vzj7UjW`GE7P5D}axc)(Nd1@@)>5#YmM<(WIqzMRmH)F` zDFK1#>{b$eCWeVg@DG_7CB`9@{p&w%THz7+D1PeS#o*n@Z1AouJmcMNXGP&gFS%ss z<*jfe2>7tZL+lU(&(eL~_C{|#8Wq^*(sveC`X>9l;ya-n_oYo$QfWP$0?p8@+;0Fbave%edN5gZ2Fo|_GNrMs};)yY4sW5&?1*DxP_Ij z7K7gd6P6!L6cK6cKSE92PlRB(qjLg^HA(#qm1X~G2n*o^+Zqx1dK6qrAY6HG)OR(F zb7lTkeY^lpqsadaMsKAVy3P)0`5sBIdT%hx?p(mb4sob3=AMLrms;K!uTOF z_ARmJ$Rk~Bc@r~#xnMom0;aOFk$zGQt(1Z(qc~H{8dL7&%ctE%U<%k<2ZLd!Sq(Qr z8XX2BvI`Y7v&~8yq^RcNRu_Ia5p?)B&}u67FJ$iq<#9M=$-5bZZkBV-6In+=mfV;4 z_#&^^LYbdarnkP!V4%MKwXEPRXdKOiGCAjzM*$^PXO95#w=xTTw`Jb!%K)5MowpLB zUf5J=yItKyNDHj2O{pW|z6D31{Lo)P-G}SqD%rbMrLdRygqA-kfJ1L~*NM{W5GxM` zU~{FoF7GV#UjzUBEYJ5u$tJ_VLpDj)#%MRkz0ZF{toB4%LAcVpvb=O6=(B7$TRxOTzsGWGj7$ zL7I~#(+1^HzWlgc>mOPa)YrBCm2O~hs27Z&lgzt8J%Y5+OLH7SrB*aj)qJ>{jQtr~ zREcwJI|i^7r@4a)ehkjh!2YfBr}14nG#-UrcGZtehXyXPs#|F6;#Q@NS(r?2)*X&I z2sD8!VA(NTk;mfWEE(I=Fkh0?X=Pw1iK7K|IFUqnWx-0YN2R?>gf<$d)VFb0rTJ@G zh4MSF9wC?Vg_ib4n1JBBt>W$-D?Ea)ALL&GrEly*x3D}p&yG!vW+fR-VO5FM_^yPq zvCTX*X&g;vCq!z=ex>j1zM~8-box;mF5Vv3#es)Ufy}%XZbcT>8M$1W55#Z+{w0XXVZF&qG$rt@qw)`6;t3K>44VkGZ{vRtY^S z@fJf=sTP|Wm(z&*-3fif?Z>=Fzj)@T>^*A2sMNw5`4B*>7-;xW)XCUV%xpQBlre7; zpu}|+p_qC1iAzobZL!o*=u5=!&0;W-tg^I)l^&2wj~3sVeGhh49t79Kb>4l&G{|)l zK*0gFC*q(QKBu_uUUTSvGYWmbVp|E9|66gLnP1qjW2RsGPIK_%1eZ*8*dDExf2*8# zz+fz zJphIkON#{a9b)iD*cLLe|ElGS>#_w4aZ=rn61Vq&7`PpT6Uq5z&S(cUMi|8)4NmBf zxXxNEoh>AWoF)HOb5mZ6bT(dG_MbXsHN_YYuf+x%(N@eJy5M`VSQ=RvT4?r76xZdO zSqA|8Um>sK}E97KdS%^D|+2}rUklJuflsfG5FK--e zO^L-)T!oozH%{=I=o`m&_C}vd0&K++Ta-2U?hkbnkku;zwQ&$-T#H(6K^6P(ET6yJ+{z<$lJK5^__~=p+JOvU=I@V6;B~8A1Pqf4HpQi_$ejM zxN~H^8N3Q1j)q5dF?t4K;{LziT6_Tz2vBk_Wh@?9>j}H}y z(_f~tRVgTw>ddqxOFoCzDvpf-S5GoUNrMPTXiMlQsjqZBCD05DYinHMz%_juF$)66 z>xof(P_0qyz^6VB?bj}Qb`m^93153u(m;!*of+ZC2IVS#OG7elKTwkR?G@VXAK~$o z6(9^nuk!w5Da0xly6k)_Mj~=Baf1oGMwg2(lO>`SGmd%~H#{^Z>QcRRQk}9ENE(F% z4Q(p~t*M^TC6^V^jnT?n*|6^Wz)n%974Vm$AI??@x>9wCj{f59f9jAaSXl_-}}pRqGo zr|f>f=$%H=v99x8d_O|Q76h2)44zpyih45vvOrL9v-B}>E9&nbF?zmFk@-~3*I(wX zBaC%7Fn(EfK$}M^GkIeqAuXfF0xu#p%$$dkqXC`#FdI@AnF&%S18YeLSm4t3-H1LH zN=AUbbSzMov;Z z$EWL5N8j6^3P#Ye2K6-PZj#4grKdF)1muH6C(`jqiJ*x{hafH7KMQ1|OMAch!&fZ_ zo6ppnd{>>Rw}^q?5bA8#Ncb&;71#%bk~&c0FVyd-klM#CaXExo=$gU$W5GNVw56h} z(|F&&7|>^{^3Wba3bLmlbpUK6bpa{gR#lE7Zj$PAC)J&)_xNrk%;ds&L+k54#a_U_ zXOb9In3Z_nIH_i+k^OlhQZ=0us=n<-*RS?({Gg@OL0Gz zy4YU+jop8xe3g8EN~I+UnY12-U)oEqk}1|C%0RqDx#B+b3pYK1de{RNkg#{P4SQE$ zVoGS1D_z%*u5gx|&u!NMQ5M##7Fyj6;wuzkU>1$b(95_?YlDSnSr;ds$%+T#gVYrt zc1s{{NGKIE@9`3ln+?p!OcB?4E&wwcwJ3Y8CGWTR=8!L5U_VHWCUIj<6b2WCr*pUa zk4v*$siw$Uu0Q+MJAq!I(GnD3&WEPQSc?oahb$`?7gb-t57r4l7SpTzXU{7B z?^Log)}Y>k#Mm#y`kXwAOh`z%pyoEO#hJHX<`QM5E0_1!Jc zv|tGA*8mG228P?DZ=4xt#Ol;rq$6J?L@HBn$0Cb#AUfQMoY5aC+tFD6LqKf4Ue#%mG6i zSyMs>+v)#@T+}R&vjums_QeAcQm3_4uzWdMy4{^#K$VSG6CK_6Y4?F@H0rc4`(W^#fQq6a(9F+0Zu~Rn;0?8umu>XKnvn z)0T`KQpX(e^;+;xI^2l^S{Yj?o?Hq+iXN zel>@(%_%(;?1m$xU|1DP@*ZH*Tg1Tq)VJ!qX4WEB7d1&=J1g@JLFS=a^aN}f$P;k$~R#Ati%zR>;YGlF=8!1Jp zRi13aQslDt=nktBHKuKJ`&-nF9!-@{YX!l1t@2ze>$u6=0*|`v&*)oP z12VC9Iv08(l{U}u^~3!cPrOwOl5tXviPTPI0wIQm6Jw4NB$gYxVK)L54&)?gl`Xz! z?<{SZKbS$A{4K1g1+i?Y6$doMjd83~`3+V;a8Bn3*qL0^f@%Y?rop`}o#8yGJd1Li zQVNF4jv2~C>vz_T5ImN&$RkG^J(~BOgKI4wE;`B@Vvr2&Mk43Ip1H_E&qlA^oYoL} zJjxHgNypd@#5FupyExTGIx{@$gj z@7KsRU4YWL+X;pLN1aYiv$_SPoM(+TtB+7V2?z^YV&Ea% z=~_HmFM#fOtrb%&QCjTwwx|~{)$9?!c8#!^;5OFKy}PiNBQAR$I_RzuLw7)eX`5LN z+=*B@jD$PE=B^b(ctA`@!+JeR#AT_tfhSq^0V-k8(Q*TQ2)bYw?V0gqj0reLNA5=q z{sq6~-}VuMN1y=_8|&0Y&A$?jIr^wM0iz~tf+kR#$(%aZ4@087L@*PqaDDydoLFWC zP7o1LW;?f@E7qq29Q%cR;622Rwa}mkQ5Cx4KP>0ad6#8$Zc{3=|0D+J2nUut99U2g zg9vei^}~6SN-C!as_eT~ZAO+@-3#7!S}@PCX@mU)TiL#D{cFtO3a9lWI1;)6qT%Dj z;4Nr!l}8MuBd@FfK*YC)zFW}yisAJ}0)$7O$$T(mv7ABz~ES`1`^0#h?X2e6;n z$=Rm30E?One^7sk(r%H`Lp|~Yi+T^Vi}e3Y4(a1T+;K(**WpShQR%oFwqJ0RqCFbg z>|md^C>`=1Mii*zBc3Lr|44jYS8%wPO>nRrySe~4C|`!AQUsG@RV$745zu!Nz?Hy{ z;M9)R>+T3&t?aC6ft5mQC6ScA>pTD+_n2Uf{Cih? zrtM{B=IZPZkf|oQU{}$|(>a#KLYRt0HQowo|Fe4I;?;Lio zQU~4>=*L~4++XXPuO3dYSot=p{ohbbb1!9NPl_9Tle#HBK${KSaeQ|?97oIh4_GlY zz-5-eRnaO=GAGtUp|P%CM*pajd~L6*gNGq0s2oe7SJSJ_-WD8m^rx{%BEq?fbsidQXIoJ#AvtAK@M zYxkGuo={P~&YF4@` z^0=J0O1=+)DPp6dRq#3N0UP#m#nnxgnqwC14(F{A1FJ|Vd248QHkyEn;6MYbmaQlCPtHe%W0JCU8sI15jdu`^+te;JMzxbs#o z_yt-%GYUc)fcDS~5%MU;zr~kn@lxLE(4!_+w5Gu{95_yx628FR*w`p2GqHc-0f1xD zDO18ug(5MILp$F4d0;{Tc&;p(c>S~CR$5-!S zCqEqA@!XjYlaWu&r0xCUSk2dF=3TR%Rw`J*+Ke6Id$l`Tw0O8SBW&`nxvXHVjQ`#> zkap!Xn7POip%UOx@}Y>TC{Ei8RN@CsJCC8Z7@(ntHD&BjDcqv=NnGAA+ns==&pogs zLKgtK4@sODF9|K%W(q?@Ao2t|r2YamW$(Lg?`nua3_J{A^3~oo7?%(id6$dJ76B2W zDDl%XvUl}O-glXQ6~Y;_sYxzaQ*oI4^G?&i9U-5+4-OH0MB_&LFVmAR1IKUTfn`+E(_3|2y&1I_7=L?O(q5G1QV+NQ@8P2W=s7^N_5Nt-7sVSZ23)WH~r%{aTq2YUm2g;s1(O2HuKYSHQi1JxBE zndE~Z?>WHTS@fqLh98q&2AGNLUU=>k02kEFo0L^14THi<6X_R4m z{VOW7AuHtF&Ajg-BF(WZz7?j@;N3kuUA~HGuDJr{1Q-MtVac@LJ03w^?J7qPAn~Dq zn2RWN1gN1W+9^_}l%AiYZLjoxgNeuP;bOl`E#aO22y`g=vgz}P^mP= zm2P1tFikSrQXc_4j~)*x|5PEcv(OYorVge^$IMcvNnFwwW2KxTZNdXGc<>KCiXwrB zZ$Vm%lUYNdDed8=M zajjp75@jY?&06RsAD>WP+U#6q;X1+>j`lwTM7I4{Q#?rZlr&5L)PN4J^{Yb+ltT~b z2K~JOmvSklY8K8Cg9L2sVMP3e&KC>&QVcAin-0GiXeAPfutl&OA{8Oq7vvXG$vAt` zGyn{^n5F?VvudNsOi$S4USJatD66y|*GqqCdFf)@X^8aH*LC;`=wmsE<^2`|%ON$r z*SVxK;CTVD&WUlAcOKU+G4O!S9Ny_g#2!*7s2BR|Qr43e-%1T=`cU5aD>1MT=*l~9 zMN|eu`?Od!Z0Xm1tidqdIO`i?W_Fh1p z1WNmHkSI47O~ts0aJxE4E=@C&*S`hqoF;jZD}~`1YI0ZV zFT45&q1fRe9s*OlOiwepDEvOw_cU8-BsmKZl#bvq3blAuD}SGtqcy#}wyPY?2BW@} zX8;#32AfE)A(m1LBn@JjS*)CFwEYtoD;;PM)CwTr@ka-_Hs!~o;EWS_8a+5!3#Z51 zw05;QmfAGR`=gwErAH?yL&qlI(DkWd9fM{tC zR#5*01%N*CHVuMLsQUoe#WzklRREs^^-~m;KWDxLPa;uECqB9rxhU;1P^fPM3dfEl zc)(G(gqy%6pj-eJ$U~^Dqz_)o*6oN)NZkf?2bdjtAgFPmjf5`!kj_Tx9rh5mJXEt{ zcN$A2PV9iV)IS->ta8D5!iPZ2r7i|CR5Rl!5O6w(e8SDU(b2)XF+vQ8S~q^BmGKc$|0N&V5*hg|Ak(9w@ORse4bF$Zrbz|11} zd*C%RxxLUeg1c>TBh`+nI|p^eYIxtSCULD!qbB!An=CcaL2RK#eHCsWOpB9h;sD17 zXQUd_>Ux$@ce~2oM;Yn#`oYx6L&{Z$p>SSg0%9bzHp+e?5#o;P6Rg#bLSsjqpXZTU z)8!%?PF#{6^CjavJMFRD3<=47w6h<0cLZ7@{%`& zA&`9sQ7AvAYoox&9nZlexBf2^oxM>S+wxQI}F9IsAKqXgCg3Wtw) zSY|vQ3up|*`5*;{OYv3;DkrSnuBd?X`Fc$3hIf`Cx&3#e+vldhh;#@SPlSI z1Kk{hb9ldk?&kudgM1PlkkA(XvSFY$l9plyrBeL}dQzg$H9Yc?=AS-Vi10B|*R|fL z?FVN9Ss`f5M=U-bo_`{VXB4<}F^$iFCdxBt%+v0<=7CaCr}PZ6K|~7gY48kRWlq%& zOUcQkd0(QBV8ydojtFd_{-WKC@7W>HFg_y6Nv2IwgV|K${(&XUrzOu+I-Y>#M<8|J zHU+7jXhv#d9ccrJf>spM1j#Pc#f8pKf)2B#jV|K;Ht;- zc3@Va4=h*z3*rENyz@Y69ZBQ-a7_)kaS)xD0+HD$i8u6Q@v-YKQiIPEg^%pN03;Rn?W%i z?uCN%#cP*9^#{sn^eV=F(8OBg2(;b`NT@+NL^1*@^0>w(8W?(5%MrLk$}=>v4)<8z z>b917z{hIEb!Q-D8-z#?SHn20N&;+C(w@UDu7db~5qS%OG1eO=Xcn7kv`1E9h-S`3 zZ9a(mm=*I!>z|(7FA-`H0RZ;&IpeSR1>56A=APb*4?_FW`x} z9B4f0-F%w*(nGl9J#FH;K14SwjC zM`@7>oAy1)6!a&R{e+gUe9)W zSYw*Jd?h?%z*P?FGj_-$rKodpGG7kEiXL9F(pc6G4O12j#EA2qni!I!p!#Gs64bH z_`R=H{3QD|I~!-OIULWvl-dTRZGs4tlb{fd2B=Yl6o^G(+F}sLg$4Nm8cu_&s0fg& zh&=7BLyfdhMK^CDfl68F7V7ji`pJcwiaj@}epZ)%>Z(VZ0x|W-m7(76je18xjZyDX zb*x&QDx=Ptm^vZNdGa?78ZoZrR7{EqSI7SToCIGZ82t1*W7mVJQ>0L-;MsORS6scESJ!s62!k zCNjEg7uh}5q<~?As|pF`P8D#=`W+6#hcCn3YF>sXtM&XiF>o1hh|5x_ZLKCn#%~ z<)}yJ2(xjZQL_>3y3rafRu%BFtjdabRQ(oPMvvkmu!HMA9vG(P0FgxAB>04OHv+`b z31wY=zJ7ZZo$KHA!{$jC&NrpGra8V9;v!|JnB{`3v4>qLGBB8nfrrelCmlzS$cQ7=gnA_o8EN)XzbT5;JKm@#Ss&50PQG1^q z)xyw_o3TSPr)KmKn7N)IrIQ0uYKf=XYxAfI?@gggV=;Onh2w}*>I(Suz2V*jjoiU) zKu-3pF&nIYIDvDiMKiRe)RTxE9dC{#(l1goexWX8t;yoDufSDw$U{!H4#2u)n8anT z0TW76puK#>?PV*R6#~!nv_{G%qZ~j|^6?|bh46epMK(Hw>{R(Jye2x}1T38v{RIvm zT?N%ib)R6H7n@mI8UWK8@Jl95&&8)>=WZ;N)`MAH;2@+Dj4r+fwVhz5g&{=Rh4arC z`RJMuE7Pb2KJeO==Kh>mJELJVJq-_a{0SIznv_I>B&st(Nir7RP~V~i2w$VoVTgna zTwL}F90b9BG6?;_h@6D&5Al;(OYPE+gKomHRnRy@`raaaGstv|DLDBz%T>7ea-sx> zOO%aQ0xMBb7^n5TtG#!rgs2z+-UTlwz0=%~SzLA}SdS9o>MJ*QUk4k(Q_u_IXr$oL z6zU1eMD$ndyM;9>k0IVe&1JvWgo$@N(!#kdGEW;{!7MH<1Se?2aL7h;hpCdLhc(I| znq4+&Xo#2~9c60n*R5unUP*)XmNH0{okW`6Ca}6Pb|5o_a^XN4gbZRhX6wqWPFp?TJ zMq9q(1YVS`UI|qEk_u9(Kvg=T(fxqtvLGM!0y6k;sy>S=@Pi_Af_HNG=gzGK4d81*l4WR@8N}XM;{u+eQNX8%D!;+@SlcXm&w_H>_Yg56fy7`M5(!S z8hRo&UQ#OtoZ8+Y0=olK>dhjp?kbQrA{vZCoYtD;!6yvOU z@3z80BHsgT4Ge@>LzdyNatB(Xg)6RDH#(qLxCU43@+^8NLky~9<&XrtODfpn1h)!_ zA&9GosJrRHp}Cn-&E-2;P14Iertqgg?3I8xV>0<|=<87OSOqXp;H^x7eI~8d0PXTx z4X_UmK}4ZCpz)P0Xc$N5hv2zoxEvA``U^@^z{NtmXb;6O0+7>X@$j;OQVcTOKU|#6 zU|MzB6GlolAaMNtXCRHrja0G;jTV~FP^#`Yl)nQpcdl?<4N^lox=vwWx{^rzngvhW zz`sT^UD>kgFxx2VIC34f{o2_TnTA zaoGs?mY@LLqFFqbFi9O+kP;cJ%`bYvuXG^9;{0NDL=qwnKq)Um-?iiCcf#kFH2|v} z_6&DI%J&WFIW63$kdt@;>u$@oK^esAu~9VViVV=_&-8wve}|&ty*xPUg@ea9?CU#* z__C5#1$Y$Vgv50{S2vPh`U%^KGiWt6;3`L%?4h^gxC4BuBQ6_@60TDCNu?lKTi?E& zdm)3o3(#(|B5-9W)bTpa@!HfVjxF;24a6_xpvVaLZUO*Pk`1QuV3*5Z73r8+ZASE_ z@em=zXu|U~s)_|o@+;(L4%m2PX;dYC>pn(v4|})r8AU|Y_}{!$>uTY}0UexuA{yY+ zl0zzFfatCg%!C}>P5#XX_aPkQq3qt;=9(^VAugp}96Ag?rA;<$AT1{F6JN@Me8gZ5 zenBE%Dv9(lgpp3dIEf}k8vOq?=OS?q(0tjn+o!AjNG9ZzzJ@uEr7&@%82BEnja;R9 ztjOhBiqXJoT)|9Skz{w17@))4M$4KSz?NKwD$s%oA4UyQz9R3w)>V#LX+My$;dMi3 zvQQptN$YfP6+;`fse{uP0@Cn228U3KAm!$;9;VYdWIYU~;p0w(qSOiM0X0c+5cNd; zUW*v`XyrsE#&X_SExb@&AcyGvFau>!y^*E{bGkZ;u)(Wkgm1zO1^DGac3R@(B7c zJYgu;MrZ8p%;pL?g2V_MDhO-u-Jz)X5d>ers`U=Xd5+rvu}S@cWZh{94jgu>7U~Yk zN(|5=091xsyPHcG(q(Pa>&E1Oh|NjS*elMJl2PFY0RaSbD($ zLv7`&T2M4hy?0XGZuTqrM4dK2fF3*yK^UxG_9o;5_N7bV45^czbhrDa8O@Uv%K8v` zpOx}R;|&cVGKoBGRPG|~b|UTpdU;pyMtEeZg-U`t3JPJMP&a@jtc@_BzaP4ZSYRUW z-ypcv%gB=xhkW~CEp~F$oEeco7q%xMORQ2s@}zw#xh{617tZ>AkJ(ZQ&6*4(QcVUz zPkSu0c`x{wVmOd_P#y4u#L&|OFvQbp3#iN1A|(u5ip_f2RC_5-fUk?f`dV@MZhy1nE zLa1huEj%i60xS(Zmwyp@#KLNJM)0P+C99Fij)PS;h{;O+Mm94HHnbwSvqh}7xe}V$ z==1Vp=Lvqn;hUgsfV72j&ebS*{4t1~XO`lN!y0CqQqy6MJU~P?dWb=!f4D^K1lHEu z_47gQMs{reKETVl=V{rq5zC$n+5l>4Z!&cdQcDnHlK^DYXRKn5w)_5J)MrX$nI1^! z>*3>2)hg%OL-~$S>Pzl@B}3#~M@ASc*hz>vuwRl$Z`eaiQrPLVu@(|vZByIB2b6l=tR{gqew+wdf%nOz~pyeLe^ zN1WgR3Gqj<>^#hJEi9jw+AV4u=+M@y>4excc#A1=K3<)6=LEa>%5Kf#r-5rzq_6|= zC%hxpj5BuNBE6=?Y-$WW?!aRki$Lo=$jij8SD|Yt-ksKd0-0DPWt)&a0tYilc#E)* zcL-|i#0{ylOHa9IanI$7=f4^)Olg1Wd>kPAW1K(~8+*RwJ@he?b|?IIKs-Ycq$?J@Y2-S2{`ue; z|JCp_>5;xwathvMl!zic*Ws?2*9K2X`*Bmqm9mAj2R97}X)-j0K{CT&`500v-$ie5 zh7kt|P0;6|6#Rp6Bf$p;Y7z}2wD=4Z;;;f<06q46#*Vbj1NYqo@~*~dG-*>e={xa8 ze|I~&hzCB;muyF4L!l1oA;+8Y9R~fOd(VSeg)ni zKyQxO|4rL*S z#kpGlL&$2OEPBrxs^hkUZXfTuwLgd3EB_5{Kc4DhD94<0QpmE2su71@04Q2PW$c;Z z{ezej1n0vZy5S@LBu8WHXK8a+!jOR;q0sGL8E;04s-xj{bNt=8gm_n}X zv9q6Qei{Ump#4HmXd-QJXOop`yDtveBExnZ;@{fZr(s|}Y3jJ_7!I%efc>$CBEpOLHb@hYm)T$d18`BO;9$tem#aq&h1T z?~w4Oh*D|<#8qL97amN6k1jr+ zvENj4+0HmTX4aqfP_e*3k1n{ig%4{8L}QCH0{| zOVb#dN(X@Ox;Bggabl33@T)mzW=*iyvGQHzFcaFP{u0?|>P-Ir__GnW$+rj36j%A~ zX6?xGea{17tXdTuRdO|LY4DZdX?BLCmvvZk`QjWq>EDL8>KVu7*IrK?=b;u=T8a2n zz8w`XG(V)$PF z(*7U_rw+5S4>Xl%V10duHIV~hq^gUGYlsToU!o-HyFsT!3|h#CYVMe5SxYN;oEDW{ zeTNb#n4tWG`e?t=_X|W`&%!$cM%V3?#6_*m$e$bI>5e}5gBcTmx=S80*6)LvH_G&v zA?)l_&AE6}b7%(5&QkE$hkMiFek5;g-+Pm`?_H*~4#gA$8-P2wA7RDxgVfo-A8N_N&a4-BX~sSlA!AgrePF@ zQKWARJ#X2G*CQg9NI1o=lINb^iI-6?IEeE+RC6Bwzy>aGOx%pt95`rzXGN(^2Ge{G zl;Ry*y0c;Dx&zWW9*{=1LrLj<%;R9R{JBVZlwE$zp4N;}(p7T=M++Sx%fQh1s~i!E zN>Zd_76c*kRqhmD+rY52*@WPd_(M@feK3xUQlT8SA-Lz>4P8Yrt6>udog$eFn@nI0haUu zM9ANP=ik&MFx2L|kL&LzIBF>1@J5G@3BkU)Q=wPtx80L~$I{$~;6N?&M$z`H)kMZgZGo!I#eQxndzTn9=b(mBkgabg#HiW5^Qcp+dX}X>c)zcw*>e5rEo~G()ik>?3)UKyCJ+j z_h~&<^z^8nw&-cIp6=4q?RvUiPuJ?{8a-XDr>pezT|Hf{r*G-$n|k`Xo>uAU%X(U= zrvW`J*V9FMx=>GjdOBB6XX)t-o@(=D2h?T+)M*I35j}sBj#rVuWE8g5KisV@D8%8G zZT=)a3TN!VbhwTwNKaux+=1U?hB;$Aa35cAo%qQi>C_^hpFUbuN-o@1kN zmtYdwAaq;%2p2Mlp55)=xOd}4*k8`O84;~z-e7VQ7`BenUjje{NCB0PCp6)`IJ(m! zdGGe`!DOc9CJcI44|PSh-igsxeDQKHSiJ^6(ZTh)zJ)b?%|G&@wpEy7ZCV~g!YNdw~q3+XYG<=V?@rYZ2Rs{av=YK8- zX3c!!sVASDTrzpuf?I}V44*b@mN0qpyy;KPEb&dB$Ny6N^vN@47tawm;ID^e+^WIc zKY#MWI#kxIsU;=TOHgmx^xkR;gG+`wr_P)1d~S~4S>o3&Zb0HMnK}C@=Tzqy|1(o> z%^v5RJN3C)bEZza2TMUhLCMsorr+TlT;j~Us2E_r8FwcN^Pad#7(ZjWbLQ;1exI{= z<}6&6IDK>GIA=|r_tbPDf2t2%nC+ZB2c>iTv!@B(*~oDEP)Bd+h7yj;Z{{=8oyAjU&O#^dNEDpTM`q6R z`KQi$$UlAFbG%^sG_*C(Ie41$iRXONOPo`E&Z*O;&6{3Q;v77ew8-f!nEmwZISXbx z=ax+OPn+YMH+|NeC#U*m&Oy$+Ilehh&Y9($4=N~Q@U-E?i~p|96El6zl9|s>H(-Yg zPtBV11SFY0>=`2a)?296;q(JJ2*TK@V}<)>7Yjp#*@9n~g@0)%88X{HYgU?&|L}cd z$B&-uotXdNSnt^Jle6#3xo`Y9A^j07tQKfryM8Ui@9Ow+8owdqfQW+o?XvW7{;YDIc<#IeVt>IWDMlL8zSK5$Yyu-_y7b<0}Z^ zd!}a1dGh_GJMQ`W<|*=!mm2Ol_uQZI`)BXG$I@B)_7%fk&%EWaoC3NRgmQeHqu}IG zu?64dgaCQk2b=Z6KzvK}``(i_9p2^9${*EI&nsG5S*NAH?%-*8U^v`%v(G4XPF`-l7pXEo_jXF(dHt$qkb1s(EBzg$Rllg;up8-!>wkROmL-(8-2PnM zs4QW5b8y0l`B}o|u}?iy^Or2)uHQ^KI`Qi)&&&T{+6%e+3H zx0PoDFaK;{&A5bHwnG!?`tHl$Swu|2Oh_tNTAWuQ$H@PwwykZr^(2%O&$JrI$v&L%%RR|FB?M zWHnuFvsfxkFjC~x#%assXtV_P!l-1cWX?)_`Zz0cvyuL{3 zLvU;G&5X&r6#YayeVu>}A?>2g%r>Emyet#hRVatE=`LT)i-muMcNOZLh^=S-z6ajt zaCn6G+u+Fy8eZaAA-->-JUzBPu8H1-_^w9T$T(aWb;m4?TVj#oFGUmegX*ruE3K1a z>y6gx#XV|DtQ*xZs&N$2Fe)jor04ym@Dfh?&YBsG9zZG-kq(&Oh4AmCt4q}*IZ-{w zucJ{Shd!5rrF??zKpR5|aBQ-@5PIUoREtzEe<6t0&an67#5Jn-mAgX^F|U(RI-KhGa`t z($hWeG`%Fbtwr5`)n6Cjh>kY_`vlT18GDz+Nfz@2k`qaZ`c8vb3*ahZ;YKZq8!cHL zr#@7+O8%|Imqwtvo6TLiu?Yh$!+~ba?yqdC_Q|PE#!ZU@&FLc67c=8k#Ow zq0SoAnc7u{%Mi3k7B5B?FJu^7Yg7`7V0JNRxXm{MPc!gP+)5wP;a21wMcy5h!EHB- zY!_%}qrPssC?Y*f0WUfL`!@=H4Ii7O`AYMnJyb7lSX_^LE{}<*KVxAux)0U6>g&GN zJh}(Gxq>_1EV3m|`|GG{drlwwQ2kQ=is&RA6#)AaITeFWqn*FTv@<#>uGpMdmu0wk zZ_*|4dVq@Xu38k0zJ;_4FZ{}Vl4T*ss$0HhlysAobf4jvP=)wC1z$^PH2N9RE*$rA zANF(%4#9)sm+@T%*grr<8f>@za(fw_95%`5x)I){Go@xpvRh6qFJMkS5yzPXgVDWBvL2ELmJAB^ZJj7*n;r}ho_ z`2-)^e!AJyqy4Au|8Pchf@F7=_Px*Fdm)PJAwp#D--Y^r(d+BuGYNQp4_%qjtt(_lG;WN+=D|U|UhKF_mEXQ- z+|5mz*>fO9J;JjPc#Z;(A9QH4^cXjB+SI zj(fop;}vtjZqB>YsI&gnXmkdSooRCI)dZ|)~6=~DJL2B^sXn*I=t;!mb` z>x&l0t?2TB-Xv{AAvfwx8rzkR@pVSmjhDx5++N=9j>DEvOhW#|V}8DejL_T+eBZ)% zF5sW(YX4{b$~PZ^PFx&^AZC2Dmu!r;z2xR3InfT;uj$vARYv9-{CbF&^1dIqEIKcOmLM&VPlt^S(jc#Ky#il)3=e=V>if{;(deH@kuLIqy6%;m;+(xH zpO;L42a7%9vfgv!ZtL4CG@8AX4v#nJTN{mD-V5(^`dsGPUNC1RJ=aSvA7e@8K73CD z@7+i>eVGe~jiEC~OQ4dLOVLF8l!>q7ANpJl<=yj*7rGr7 zVC?vkt&9Y$6DfVD?jn4%k$3LW{hku{gX!n|N4-nYO=A|(-C7O*gjAC^)b%M1JJubr;1fQ*_&y%uPceyu{D7fa{L8virpwvEYj_UJnu=Klv`cM$`&TF_Q>P06xPg20k^bfHGP4y8L zOhx=cc+-)ei~Na~=5>BtZf_GG6!eiXy^#HF%?cN%55I$o_hvA;suAyNA!X0JZX-< zHzWbPRNfx~uf1+(XqhG(&VsA&*;ik^XtDDgs&hx zk8lp*62eskI+mVyoSPBocnAK!6JayLZiGRE0|*ZxJc95z!Y2@pBb-2(LO6x+0>U|j z^9Yv^eui)r;hKMkUI;fK+>FqS(1x%EVKYK6LLb5&get;=2!{|JMR*+H(+HCYPa!;q z@I1mFWPr7(E(Nm98&$wVv3tDg`r^<~s*u@|D?sEb zAzMV|T;SazHn;<(;g%nawfjS!Nl5JrS+<%fLyoISn?pJ;QbJifcvDtH0+Fo^)) z^!Iw14WpP!`9(^HAiB-#rtv@ zZ#`xvIMqcvz3ROMBnPc1VXs7ff~8<{#un|&dtS-;@S@&)1?s*hl^^z;XBMgX3DCX@ zzdFvBG$Lj!IL>z#X@cIOVRd>Ow^A)QE|I!2F7evliDw z{cd`=>|(-%<7~xs$#uQLNUn?wbhgXu>`*b|IZhf^6|d^1N+q|tzXaqj9XIVEFCFJ^ z9oH+D3q?FJj+@0y1e)RDTpM$9#eV#-HRcuzLhy+gB_Y$hC!XprmaERTc&aK>>A_hs zhfR&*C`Rx<#wg?RjvLRWMG$y-@ZUob5yTMXedHq*IuoF%^fglEC?L zypkIzr1Gf8;>eoxAj;}#aYi}wbvnz#EawODYB68jhZ1oyK9bcezlcK>aeq+*&ZD^P0SKP@=K9Eg`vtwx?GiLGp_$Hg`n;*>Hpn9bDRN+Gxzs&h*}x`*m` zjgVQaFRlNFaMcHa1|f5Ubx&WQb0ZG&Q$uVDe}79{>pv|t`4ktV<<(amXCRi|KR~lD z--#x#AG2f&E$EFn&L1I6A&7Jwx7fc27Y^wqG^7jXBM40h!Y%^DNDiqdd`K<{Ii3PG zu4NqKLxGUaFW|m@dz8`V<3poiY*`4moU<~8JJLV9a7NnuM@!kyFsKB5(*DiB8RbQF zhP-ImlP;6E?;q{?Cy=c4Jarvqr@JhjF48Car-e5Xp{2#m(i~dH`8rIHC7gPs^hVGK zeH21HU!>Eekht-)OquLDjQI2>?jQ5CE#2$S&p*^pe6U*E@V zlJ*Z>HsJVKYe*`6vOLo_Hv4Ed45M5TKK9m=$q5Z8SXpZ@mRij}nVd4i*$cJm@ z;KN;T8JU$fl{V^v~|?|%qD0L5^z0X3#cpdSkD|hwgOI)9-E}pthBUUr5nk1;<9dw zi}~boA7aQF3GNFyNaxH9Ixn<78SqJccu67kNT1Pp>GKYXW*zhK&mdw-eXJzJg|_|3 z0_DX_TtrB=KRko&B;(POBJ@dwmJN~_flMo^e(}T+;QTR=uA71g&AFYhkdFg7vn(j{ zcR&)DQ0%8e_2syQd=JQ^g}e&nvW3uF$HXl%9o)q6CLm)8jc7khyQs|n6phK-g{1zo zV0o%q&oky(hBHGB%8Q9v@`8FY=ghNw6sGB~(td)slkEv*X~?rol4~Gi%7*E)$ynqK zn(GXQ299Xn;7^>c+0Gkpz~qpFyg{p3K@sQ^-lrvRjG=KSmWJ3a{N?6uCT}!JZ-mus zh~(T4nXb1g83LRkAdPQRJ`~7@&d_12=w7PYRDu)-1=g9zmkmpa%!SiR0=MN*r#Vnz3kT>v13IFhn5%L#; zGNv5LSmce{A!SEhC~puwS$b{`mX=rLRH08X04$M95Nnx)gnV zN>3gOZRzDXSUO=W?ST@u=SeHE?s!Sj^fm+V8o`Of88ra5usW_XDE`U7P9R%utL5BK4_ z2_`8_DH++1`VeAuZPVV+w##&g>wbt`YbiY++Ycfp2txCe5+&Qa=3x6q6k%!m%aj^L zH3vp%1~S!RYJ9u0BBcCiyQB>1Lo>%=cHmejyL69;z8uNtbPz1$1^Xb|d*@&~?Rg_@ zpFj+5+(#(iGxcpJ)}xT=@mMa4SmcfOv1Av@8^lhQ4$i^S^TyJ{h#|`-#n;CdVa2$% zf@v(Jz6_Z&T1G(s3?z}&wiEOnAYBGAWkbXwZ+s5ab{Sh}ZycP1Hx4%Hd_Mu5sRY=^ zPsj=st(n^HE08&fwgCSri>^VhVcJ4yw6!s((h(H50qIkS%!PI!)R*wnxeG`F(sW5rv2>#rN(~dj&Mkn8o!tWXMV4 ziNmSmd|^%AQ4@DWL`jn z*xlEmZKA}?!OIWjr(J|~dpd?Y!iTYOc-%g8-C`__ZL||n;`GFg&!LPZ(S;yQ8e&~= z94gtsq3s$MmXc?+6-|(m`L17#!kQ5uw**udz@|iu}JJo89V*j!Qx8x zGWO>g`(z~cM#fHGYHW6z?QbC+^!?Pf{X``8C5%18*y*c}&HfZ)pNho3n6dXUc6wfI zcKY6J+kP?68lSxeG6l!y{aw$bUet|FGpghQ3xyL_Kl3a zm9f)&Bo#~9ekBt7MaJIE*jF(2J&c`R5g|6uHtqsr?_%uqu4ehCqmQvKj>LYRvC|v| ziz`Xrn=GV*-iH;UJl`0JeVVa%GWG;x-^|#TMPjE>KFhB5Bx7IB*w--jL?rgJjJ=hy z(@Zc+!;Ut_-W-YjMaI5@u`gxp&5XS@68jm(PU|{YmL?k+`^}8KGZOo0#@@u(moWC5 z7<*SFb{a*uENxFP_Qj0-dd9vn68m$EeK})a#MrN4>^+g#rx^P(#!gkJ<)023V^xT% zZFWUsf10r`W$ZLEWV8Q_vG+w{Kf%}=8T%E+eu=RUMq;P0W|m#;OBnl0jQu=gFGXUX zWbBI>`(?&X-!zrZD&J}PIf}|>GGk1=+7?NxTkx7y?d#(s*i zABx0&l(D~D`+OzOGxn(kVxM5_R~Y*=W2cQ3m48&ek4Lio2xEVVv7ck?#~J(KNbH9h zJAIc?cBy#;E0(40G_Sxy zlBXH_LyY}cB=$p$eVVbKWb6kR`(z~cF~)wDu}?Af-HiQ2B=&=h{YA$9G-Kb)*ry_~ zk23Z%jGacoA?A-LcHFr@>{Z5onz290*xzB;2Meho5BGhUY;|zBfHCYqI;uWAsr>_n z+JlX+hRLX3V3`ruFd11#wKao4cr3OQXEk3mR6XYFtjB48r8*}w+Vf$vD5cKr2X!l4 zUuJca9*>G)Fuvx{Bikk~<~6dtg_V{QT4z1hotyxpT~@Ec{R&Yf;fyW`^J3q~*q>zV zM;QAh#(p*udpBdBWbB6-`#Huw9f`e*u^(sbk1_V=8T*Au>}weNF~&a5*uTQqFGphU zWb8*7`@@W#_OwPssxwuoyAp{#$=D|t`ys~uC}YRw7!lqAS{eHh#{LjvKfu@*M`B;W z*bg)IF~*){?2VDwn;H9KjQt>E?_uoABC$6y_Ho8O%Gi^PJrRjL!Pp;W>?qEQjQv^0zAF;@BF0`~?7JBI zBxCQ3#7>VA{`o!p_B>^+Qq%x72Y6`Vi~Ab&NVP3@)E zg218Q6#R7bn?is~wbUhT1+=I=7Wz$r(ifhe;Z4EUS-&X+bXK-wIrh4?>mJB2(XQlC zV;2;wIkXi|>s+4UZK6k)%-HK{QjwQG!F_2LW+;W_7t2$zYNnOeLggtXO7nk?;tdEz z5p#Pm$tUEXcDuS%&;OxbJ6HS)>>9&Vb}`e5-!`p`lp(GjCbiIZotT4NOWvUEqIV%% zC0!2B?2FPD&FGS#-}Ty`L-GLqS}S=RR~FK7Gm4)=R7p4+iM@}pPcimmjD4wL*Y%Gt zqwAkQI!eQ7t)cBcq>eQx)Y2nOI7bDDd?(aC*xax%8Clb|p%_a4%uxUMI_oF*SE_Sz z(&${jZaJxQXNJ0EQ0HmbNQJ6b-wX=2298(A=*3tX@Jt-jWnN^%>w%cu5y&k-WS%C% z^Xr$G@1zjbKSt-+8eG{0gsn>x;zLR&Y3!nMNWXHUTF*Y#L1qyMiJ?KHV{?%-)tRRu zSFg_0^#;k(F~9kc59wSs^~O0}HZ+oa4ymeILcPljBHtxKjN74|?u%j`tXk+%j&;+_ zSULtEBgb|$asbHmXSy8Pg;>gb6o{TPAd$}ixuEnx%4#Xb1YWNES+7*@${w{&`4o_s zOe|vmS0Hpysxsvt0a>CvkCf$tkj_Rch#qy6$m=nG zSHG=Gay8H$CB_elF^KWeA}0c*N(2hGk7}?JrhIvXEn)~ z9K@=Cv}~_4X0dODjGWJ}y>TCqgkg7SSzRoqD`#VBnG9scj0{;wtL7}l|GtKQ0>o}* zJ_$t5CD!SD9>`f^yX#`XW0YsATl_qof(#v~OjW+01|ml@bUNP#qUUu>+kXs%JVsX% z`8kj&%l2#V^M)StRJv+OUpdE~QkL~b3MuGvD3OfN#jn|Jq@PKc7LBCe3}mszb-Vg0 zg{+@l7mL@pTYEE*^)3B9$h4w<@sWFgBrKVoKzc3D_oA9Pvw?S{ho;wbrqI z7r#8CSbllF4M?Yjv;*m~ko7>w3ZH8ikS#`r7WSsat(%SQ5OxM2qeo64iengvd>hsJ z{2`F#xFeqeTK+pjwGEzbg{bAK&aHq?pBWfF3#M>Yg|924U zu!X!82pR9E{QE!%^q&G+#RvHlka;fTFp8_dav985%&I-hm z^@b~#RChY9SZ{&MGE>))YIgx?GO-BR4rICIjr)NdwYbKB$b&3>15Rs}ALEMyrFz4nnz=dD2K@SpBG zflQ=TJ`f3Wc2%>)cG4g zTA{PbWx3?OQ?k5q2{O|b@(K{UZdraK60ve831rM@=&I%H<*z^Vv%D2DyNu4{yY~UH zJvIhJkBds3KLR9yl%-c)v3WvQ@`9z|UqgoKOux)O1>~e9^8yeQd&hsezXxQZLZwP; zco9h6SV0A-;lPK!5)W|8Itfn&o)_wF5Qw{CFPZMbLC zHq_w2Hm~2|ZrQZ6S6&UI({34S8CS61gddO4j8}`JZ&vYA*rouJIIu3ZpcF< z+Al&Fx-TVN+_Gxr%2sUMk*f&jx>z3>8CY=SRjxq!V=km`v6qUxOHDT#J$WJXSd*_b zBE_J;^aKIScng$s-m>0!cLg+r;rfCG4O+Y7&5uC zn8^lS+`hr3n>%>KTYgN61*I(xfU_A`C`hF5~#;vhA?1&A*1-ca*dd_=Bnm;$25V zTEii1Dn!+lGAGy8uTSZYJ}(vbwfonl)^_-|W0j~_^OieMG3Yp%TsEuE``Zjd#%G|4 z#E1`PawD0XaQ!g$@=>R<_1Unk+i!c+h26Ng)DXz8c`e9%(H^#l#-XL7y?s?X?9M?o z*WL@NtJ87qy^y+AqQJQBj(fNDZbGxyvvb3}-P_$A8@6m;?>BlPCG~2g()ITz3ZanH zW|JZ`X;@(ZRUy=rW_9;&545Qwd9GH=J}+`8f#j5~xL)&4IlHMi> zLU4e5byZs56WHad3$5JF`g=)59m0+ETzRX2~=39An=x)V>Tg?@)dz)PUofz25vC=DOPO;D9H%KOR*T(*0_z4vC5~S5o6V>aH5C!QFt>U`JUl zR(K&++rX&EX0J5Yg(EW}-^6BNE;XLHq11rq_NOZH0TDKeS4?`d5L*SJA;#WlL)7uZ zBNVV-$jYG{*WI+X*XV3$Oz{9w{p?1n=p@T0Py55FVomkuxEuV<2}RiyI#BaR`?RU{ zKtEX4ZhE3M5gQqbcm4OP4+yNhI?L+Grk&o)rpzB)A_y%gGYPNxd?xtO-eLbE- z%c8a`L?f;@L-ZRBQP~9B44H#f-!xcC56wC}GqA~TOsIj;<&g$<)Rh|cR#)|EhLGNf zPC$;NtN8XFkyIl{@6xkrHC{rKn#aXz11iHs-2}|o*rNys#ubcGw4bBWX|~=D8^I7+ zga)%t2Sm2gvStdl(0DmQW{WCT_5g&9dPhe2ZAHD=;xQKcSSk(mE27LFIZ#T^=HT8+ zb+0=_uOqtB+KX2oFORVwsvU!mW1tAD`4*xDn7KbeojJeOkySP~OxU3Y$+T0=_L3&! z?W)Pj%M8Y8RCC^2)BO`^*X^*)Trt#buAqvkuj^Orx*bPP!S^ciUX6*7(z2>Vzb!B} hL)dV|LxoYVr=@CD4Bv>aA#Sa&>QouZlj>{i{{o-H1vCHv diff --git a/source/config.c b/source/config.c index 6a6bd1e..eae8907 100644 --- a/source/config.c +++ b/source/config.c @@ -77,6 +77,7 @@ void configMenu(bool oldPinStatus, u32 oldPinMode) const char *singleOptionsText[] = { "( ) Autoboot SysNAND", "( ) Use SysNAND FIRM if booting with R (A9LH)", "( ) Enable FIRMs and modules loading from SD", + "( ) Use custom path", "( ) Enable region/language emu. and ext. .code", "( ) Show NAND or user string in System Settings", "( ) Show GBA boot screen in patched AGB_FIRM", @@ -144,6 +145,10 @@ void configMenu(bool oldPinStatus, u32 oldPinMode) "system modules from the SD card.\n\n" "This isn't needed in most cases.", + "Use a custom path for the\n" + "Luma3DS payload.\n\n" + "Refer to the wiki for instructions.", + "Enable overriding the region and\n" "language configuration and the usage\n" "of patched code binaries for specific\n" @@ -299,7 +304,7 @@ void configMenu(bool oldPinStatus, u32 oldPinMode) drawString(singleOptionsText[singleSelected], true, 10, singleOptions[singleSelected].posY, COLOR_RED); } - clearScreens(false, true); + clearScreens(false, true, false); drawString(optionsDescription[selectedOption], false, 10, 10, COLOR_WHITE); } else diff --git a/source/config.h b/source/config.h index c32bf0f..3aa814b 100644 --- a/source/config.h +++ b/source/config.h @@ -30,7 +30,7 @@ #define CONFIG_PATH "/luma/config.bin" #define CONFIG_VERSIONMAJOR 1 -#define CONFIG_VERSIONMINOR 5 +#define CONFIG_VERSIONMINOR 6 #define BOOTCFG_NAND BOOTCONFIG(0, 7) #define BOOTCFG_FIRM BOOTCONFIG(3, 7) @@ -52,7 +52,8 @@ enum singleOptions { AUTOBOOTSYS = 0, USESYSFIRM, - SDFIRMSANDMODULES, + LOADSDFIRMSANDMODULES, + USECUSTOMPATH, USELANGEMUANDCODE, PATCHVERSTRING, SHOWGBABOOT, diff --git a/source/crypto.c b/source/crypto.c index 7262078..7ad5957 100755 --- a/source/crypto.c +++ b/source/crypto.c @@ -282,7 +282,7 @@ static void sha(void *res, const void *src, u32 size, u32 mode) while(*REG_SHA_CNT & SHA_FINAL_ROUND); sha_wait_idle(); - + u32 hashSize = SHA_256_HASH_SIZE; if(mode == SHA_224_MODE) hashSize = SHA_224_HASH_SIZE; @@ -297,6 +297,8 @@ static void sha(void *res, const void *src, u32 size, u32 mode) static u8 __attribute__((aligned(4))) nandCtr[AES_BLOCK_SIZE]; static u8 nandSlot; static u32 fatStart; +static bool didShaHashBackup = false; +static u8 __attribute__((aligned(4))) shaHashBackup[SHA_256_HASH_SIZE]; void ctrNandInit(void) { @@ -482,8 +484,19 @@ void computePinHash(u8 *outbuf, const u8 *inbuf) u8 __attribute__((aligned(4))) cid[AES_BLOCK_SIZE]; u8 __attribute__((aligned(4))) cipherText[AES_BLOCK_SIZE]; + if(!didShaHashBackup) + { + didShaHashBackup = true; + memcpy(shaHashBackup, (void *)REG_SHA_HASH, sizeof(shaHashBackup)); + } + sdmmc_get_cid(1, (u32 *)cid); aes_use_keyslot(4); //Console-unique keyslot whose keys are set by the ARM9 bootROM aes(cipherText, inbuf, 1, cid, AES_CBC_ENCRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); sha(outbuf, cipherText, sizeof(cipherText), SHA_256_MODE); +} + +void restoreShaHashBackup(void) +{ + if(didShaHashBackup) memcpy((void *)REG_SHA_HASH, shaHashBackup, sizeof(shaHashBackup)); } \ No newline at end of file diff --git a/source/crypto.h b/source/crypto.h index 7ea191a..133d6bc 100755 --- a/source/crypto.h +++ b/source/crypto.h @@ -111,4 +111,5 @@ void set6x7xKeys(void); void decryptExeFs(u8 *inbuf); void decryptNusFirm(const u8 *inbuf, u8 *outbuf, u32 ncchSize); void kernel9Loader(u8 *arm9Section); -void computePinHash(u8 *outbuf, const u8 *inbuf); \ No newline at end of file +void computePinHash(u8 *outbuf, const u8 *inbuf); +void restoreShaHashBackup(void); \ No newline at end of file diff --git a/source/draw.c b/source/draw.c index 6f000f0..568a4b4 100644 --- a/source/draw.c +++ b/source/draw.c @@ -45,9 +45,12 @@ bool loadSplash(void) return false; initScreens(); + clearScreens(true, true, true); - if(isTopSplashValid) fileRead(fb->top_left, topSplashPath, 0); - if(isBottomSplashValid) fileRead(fb->bottom, bottomSplashPath, 0); + if(isTopSplashValid) fileRead(fbs[1].top_left, topSplashPath, 0); + if(isBottomSplashValid) fileRead(fbs[1].bottom, bottomSplashPath, 0); + + swapFramebuffers(true); chrono(3); @@ -56,7 +59,7 @@ bool loadSplash(void) void drawCharacter(char character, bool isTopScreen, u32 posX, u32 posY, u32 color) { - u8 *select = isTopScreen ? fb->top_left : fb->bottom; + u8 *select = isTopScreen ? fbs[0].top_left : fbs[0].bottom; for(u32 y = 0; y < 8; y++) { diff --git a/source/draw.h b/source/draw.h index 7199060..9929f78 100644 --- a/source/draw.h +++ b/source/draw.h @@ -29,12 +29,6 @@ #include "types.h" -#define SCREEN_TOP_WIDTH 400 -#define SCREEN_BOTTOM_WIDTH 320 -#define SCREEN_HEIGHT 240 -#define SCREEN_TOP_FBSIZE (3 * SCREEN_TOP_WIDTH * SCREEN_HEIGHT) -#define SCREEN_BOTTOM_FBSIZE (3 * SCREEN_BOTTOM_WIDTH * SCREEN_HEIGHT) - #define SPACING_Y 10 #define SPACING_X 8 diff --git a/source/firm.c b/source/firm.c index 60c159d..e106ac9 100755 --- a/source/firm.c +++ b/source/firm.c @@ -247,7 +247,7 @@ void main(void) writeConfig(needConfig, configTemp); } - bool loadFromSd = CONFIG(SDFIRMSANDMODULES); + bool loadFromSd = CONFIG(LOADSDFIRMSANDMODULES); u32 firmVersion = loadFirm(&firmType, firmSource, loadFromSd); switch(firmType) @@ -316,7 +316,7 @@ static inline u32 loadFirm(FirmwareType *firmType, FirmwareSource firmSource, bo { u8 cetk[0xA50]; - if(fileRead(cetk, *firmType == NATIVE_FIRM1X2X ? cetkFiles[0] : cetkFiles[(u32)*firmType], sizeof(cetk))) + if(fileRead(cetk, *firmType == NATIVE_FIRM1X2X ? cetkFiles[0] : cetkFiles[(u32)*firmType], sizeof(cetk)) == sizeof(cetk)) decryptNusFirm(cetk, (u8 *)firm, firmSize); } diff --git a/source/fs.c b/source/fs.c index 21e8fb7..bb4fcf2 100644 --- a/source/fs.c +++ b/source/fs.c @@ -23,6 +23,7 @@ #include "fs.h" #include "memory.h" #include "strings.h" +#include "crypto.h" #include "cache.h" #include "screen.h" #include "fatfs/ff.h" @@ -70,6 +71,7 @@ bool fileWrite(const void *buffer, const char *path, u32 size) { unsigned int written; f_write(&file, buffer, size, &written); + f_truncate(&file); f_close(&file); return true; @@ -137,6 +139,7 @@ void loadPayload(u32 pressed) { loaderAddress[1] = payloadSize; + restoreShaHashBackup(); initScreens(); flushDCacheRange(loaderAddress, loader_size); diff --git a/source/patches.c b/source/patches.c index 81e768f..cf00192 100644 --- a/source/patches.c +++ b/source/patches.c @@ -25,6 +25,7 @@ */ #include "patches.h" +#include "fs.h" #include "memory.h" #include "config.h" #include "../build/rebootpatch.h" @@ -94,6 +95,32 @@ void patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr) //Put the fOpen offset in the right location u32 *pos_fopen = (u32 *)memsearch(off, "OPEN", reboot_size, 4); *pos_fopen = fOpenOffset; + + if(CONFIG(USECUSTOMPATH)) + { + const char pathPath[] = "/luma/path.txt"; + + u32 pathSize = getFileSize(pathPath); + + if(pathSize > 5 && pathSize < 39) + { + u8 path[pathSize]; + fileRead(path, pathPath, 0); + if(path[pathSize - 1] == 0xA) pathSize--; + if(path[pathSize - 1] == 0xD) pathSize--; + + if(pathSize > 5 && path[0] == '/' && memcmp(&path[pathSize - 4], ".bin", 4) == 0) + { + u16 finalPath[pathSize + 1]; + for(u32 i = 0; i < pathSize; i++) + finalPath[i] = (u16)path[i]; + finalPath[pathSize] = 0; + + u8 *pos_path = memsearch(off, u"sd", reboot_size, 4) + 0xA; + memcpy(pos_path, finalPath, pathSize); + } + } + } } void patchFirmWrites(u8 *pos, u32 size) diff --git a/source/pin.c b/source/pin.c index 1e6c3ab..36f5fe5 100644 --- a/source/pin.c +++ b/source/pin.c @@ -46,7 +46,7 @@ static char pinKeyToLetter(u32 pressed) void newPin(bool allowSkipping, u32 pinMode) { - clearScreens(true, true); + clearScreens(true, true, false); u8 length = 4 + 2 * (pinMode - 1); @@ -89,13 +89,13 @@ void newPin(bool allowSkipping, u32 pinMode) memcpy(pin.magic, "PINF", 4); pin.formatVersionMajor = PIN_VERSIONMAJOR; pin.formatVersionMinor = PIN_VERSIONMINOR; - pin.length = length; u8 __attribute__((aligned(4))) tmp[SHA_256_HASH_SIZE]; - u8 __attribute__((aligned(4))) zeroes[AES_BLOCK_SIZE] = {0}; + u8 __attribute__((aligned(4))) lengthBlock[AES_BLOCK_SIZE] = {0}; + lengthBlock[0] = length; - computePinHash(tmp, zeroes); - memcpy(pin.testHash, tmp, sizeof(tmp)); + computePinHash(tmp, lengthBlock); + memcpy(pin.lengthHash, tmp, sizeof(tmp)); computePinHash(tmp, enteredPassword); memcpy(pin.hash, tmp, sizeof(tmp)); @@ -111,17 +111,17 @@ bool verifyPin(u32 pinMode) if(fileRead(&pin, PIN_PATH, sizeof(PinData)) != sizeof(PinData) || memcmp(pin.magic, "PINF", 4) != 0 || pin.formatVersionMajor != PIN_VERSIONMAJOR || - pin.formatVersionMinor != PIN_VERSIONMINOR || - pin.length != 4 + 2 * (pinMode - 1)) + pin.formatVersionMinor != PIN_VERSIONMINOR) return false; - u8 __attribute__((aligned(4))) zeroes[AES_BLOCK_SIZE] = {0}; u8 __attribute__((aligned(4))) tmp[SHA_256_HASH_SIZE]; + u8 __attribute__((aligned(4))) lengthBlock[AES_BLOCK_SIZE] = {0}; + lengthBlock[0] = 4 + 2 * (pinMode - 1); - computePinHash(tmp, zeroes); + computePinHash(tmp, lengthBlock); - //Test vector verification (SD card has, or hasn't been used on another console) - if(memcmp(pin.testHash, tmp, sizeof(tmp)) != 0) return false; + //Test vector verification (check if SD card has been used on another console or PIN length changed) + if(memcmp(pin.lengthHash, tmp, sizeof(tmp)) != 0) return false; initScreens(); @@ -135,6 +135,7 @@ bool verifyPin(u32 pinMode) const char messagePath[] = "/luma/pinmessage.txt"; u32 messageSize = getFileSize(messagePath); + if(messageSize > 0 && messageSize <= 800) { char message[messageSize + 1]; @@ -147,7 +148,7 @@ bool verifyPin(u32 pinMode) { drawString("Press START to shutdown or enter PIN to proceed", true, 10, 10, COLOR_TITLE); drawString("PIN ( digits): ", true, 10, 10 + 2 * SPACING_Y, COLOR_WHITE); - drawCharacter('0' + pin.length, true, 10 + 5 * SPACING_X, 10 + 2 * SPACING_Y, COLOR_WHITE); + drawCharacter('0' + lengthBlock[0], true, 10 + 5 * SPACING_X, 10 + 2 * SPACING_Y, COLOR_WHITE); u32 pressed; do @@ -169,7 +170,7 @@ bool verifyPin(u32 pinMode) drawCharacter(key, true, 10 + charDrawPos, 10 + 2 * SPACING_Y, COLOR_WHITE); charDrawPos += 2 * SPACING_X; - if(cnt >= pin.length) + if(cnt >= lengthBlock[0]) { computePinHash(tmp, enteredPassword); unlock = memcmp(pin.hash, tmp, sizeof(tmp)) == 0; @@ -179,7 +180,7 @@ bool verifyPin(u32 pinMode) charDrawPos = 16 * SPACING_X; cnt = 0; - clearScreens(true, false); + clearScreens(true, false, false); drawString("Wrong PIN, try again", true, 10, 10 + 4 * SPACING_Y, COLOR_RED); } diff --git a/source/pin.h b/source/pin.h index 69390f8..f323452 100644 --- a/source/pin.h +++ b/source/pin.h @@ -30,15 +30,14 @@ #define PIN_PATH "/luma/pin.bin" #define PIN_VERSIONMAJOR 1 -#define PIN_VERSIONMINOR 2 +#define PIN_VERSIONMINOR 3 typedef struct __attribute__((packed)) { char magic[4]; u16 formatVersionMajor, formatVersionMinor; - u8 length; - u8 testHash[32]; + u8 lengthHash[32]; u8 hash[32]; } PinData; diff --git a/source/screen.c b/source/screen.c index 52eec27..09716b9 100644 --- a/source/screen.c +++ b/source/screen.c @@ -25,11 +25,20 @@ * Screen deinit code by tiniVi */ +/* +* About cache coherency: +* +* Flushing the data cache for all memory regions read from/written to by both processors is mandatory on the ARM9 processor. +* Thus, we make sure there'll be a cache miss on the ARM9 next time it's read. +* Otherwise the ARM9 won't see the changes made and things will break. +* +* On the ARM11, in the environment we're in, the MMU isn't enabled and nothing is cached. +*/ + #include "screen.h" #include "config.h" #include "memory.h" #include "cache.h" -#include "draw.h" #include "i2c.h" vu32 *const arm11Entry = (vu32 *)BRAHMA_ARM11_ENTRY; @@ -46,16 +55,6 @@ void __attribute__((naked)) arm11Stub(void) //Jump to it ((void (*)())*arm11Entry)(); } - -/* -About cache coherency: - - Flushing the data cache for **ALL** memory regions read from/written to by _both_ processors is mandatory on the arm9 processor. - Thus, we make sure there'll be a cache miss on the arm9 next time it's read. - Otherwise the arm9 won't see the changes made and things will break. - - On the arm11, in the environment we're in, the MMU isn't enabled and nothing is cached. -*/ static void invokeArm11Function(void (*func)()) { @@ -111,12 +110,34 @@ void updateBrightness(u32 brightnessIndex) invokeArm11Function(ARM11); } -void clearScreens(bool clearTop, bool clearBottom) +void swapFramebuffers(bool isAlternate) +{ + static u32 isAlternateTmp; + isAlternateTmp = isAlternate ? 1 : 0; + + void __attribute__((naked)) ARM11(void) + { + //Disable interrupts + __asm(".word 0xF10C01C0"); + + *(vu32 *)0x10400478 = (*(vu32 *)0x10400478 & 0xFFFFFFFE) | isAlternateTmp; + *(vu32 *)0x10400578 = (*(vu32 *)0x10400478 & 0xFFFFFFFE) | isAlternateTmp; + + WAIT_FOR_ARM9(); + } + + flushDCacheRange(&isAlternateTmp, 4); + invokeArm11Function(ARM11); +} + +void clearScreens(bool clearTop, bool clearBottom, bool clearAlternate) { static bool clearTopTmp, clearBottomTmp; + static volatile struct fb *fbTmp; clearTopTmp = clearTop; clearBottomTmp = clearBottom; + fbTmp = clearAlternate ? &fbs[1] : &fbs[0]; void __attribute__((naked)) ARM11(void) { @@ -130,26 +151,26 @@ void clearScreens(bool clearTop, bool clearBottom) if(clearTopTmp) { - REGs_PSC0[0] = (u32)fb->top_left >> 3; //Start address - REGs_PSC0[1] = (u32)(fb->top_left + SCREEN_TOP_FBSIZE) >> 3; //End address + REGs_PSC0[0] = (u32)fbTmp->top_left >> 3; //Start address + REGs_PSC0[1] = (u32)(fbTmp->top_left + SCREEN_TOP_FBSIZE) >> 3; //End address REGs_PSC0[2] = 0; //Fill value REGs_PSC0[3] = (2 << 8) | 1; //32-bit pattern; start } if(clearBottomTmp) { - REGs_PSC1[0] = (u32)fb->bottom >> 3; //Start address - REGs_PSC1[1] = (u32)(fb->bottom + SCREEN_BOTTOM_FBSIZE) >> 3; //End address + REGs_PSC1[0] = (u32)fbTmp->bottom >> 3; //Start address + REGs_PSC1[1] = (u32)(fbTmp->bottom + SCREEN_BOTTOM_FBSIZE) >> 3; //End address REGs_PSC1[2] = 0; //Fill value REGs_PSC1[3] = (2 << 8) | 1; //32-bit pattern; start } while(!((!clearTopTmp || (REGs_PSC0[3] & 2)) && (!clearBottomTmp || (REGs_PSC1[3] & 2)))); - if(fb->top_right != fb->top_left && clearTopTmp) + if(fbTmp->top_right != fbTmp->top_left && clearTopTmp) { - REGs_PSC0[0] = (u32)fb->top_right >> 3; //Start address - REGs_PSC0[1] = (u32)(fb->top_right + SCREEN_TOP_FBSIZE) >> 3; //End address + REGs_PSC0[0] = (u32)fbTmp->top_right >> 3; //Start address + REGs_PSC0[1] = (u32)(fbTmp->top_right + SCREEN_TOP_FBSIZE) >> 3; //End address REGs_PSC0[2] = 0; //Fill value REGs_PSC0[3] = (2 << 8) | 1; //32-bit pattern; start @@ -161,13 +182,14 @@ void clearScreens(bool clearTop, bool clearBottom) flushDCacheRange(&clearTopTmp, 1); flushDCacheRange(&clearBottomTmp, 1); - flushDCacheRange((void *)fb, sizeof(struct fb)); + flushDCacheRange((void *)fbTmp, sizeof(struct fb)); + flushDCacheRange(&fbTmp, 4); invokeArm11Function(ARM11); } void initScreens(void) { - void __attribute__((naked)) ARM11(void) + void __attribute__((naked)) initSequence(void) { //Disable interrupts __asm(".word 0xF10C01C0"); @@ -250,35 +272,51 @@ void initScreens(void) for(u32 i = 0; i < 256; i++) *(vu32 *)0x10400584 = 0x10101 * i; - *(vu32 *)0x10400468 = 0x18300000; - *(vu32 *)0x1040046c = 0x18300000; - *(vu32 *)0x10400494 = 0x18300000; - *(vu32 *)0x10400498 = 0x18300000; - *(vu32 *)0x10400568 = 0x18346500; - *(vu32 *)0x1040056c = 0x18346500; + WAIT_FOR_ARM9(); + } - //Set CakeBrah framebuffers - fb->top_left = (u8 *)0x18300000; - fb->top_right = (u8 *)0x18300000; - fb->bottom = (u8 *)0x18346500; + //Set CakeBrah framebuffers + void __attribute__((naked)) setupFramebuffers(void) + { + //Disable interrupts + __asm(".word 0xF10C01C0"); + + fbs[0].top_left = (u8 *)0x18300000; + fbs[1].top_left = (u8 *)0x18400000; + fbs[0].top_right = (u8 *)0x18300000; + fbs[1].top_right = (u8 *)0x18400000; + fbs[0].bottom = (u8 *)0x18346500; + fbs[1].bottom = (u8 *)0x18446500; + + *(vu32 *)0x10400468 = (u32)fbs[0].top_left; + *(vu32 *)0x1040046c = (u32)fbs[1].top_left; + *(vu32 *)0x10400494 = (u32)fbs[0].top_right; + *(vu32 *)0x10400498 = (u32)fbs[1].top_right; + *(vu32 *)0x10400568 = (u32)fbs[0].bottom; + *(vu32 *)0x1040056c = (u32)fbs[1].bottom; WAIT_FOR_ARM9(); } - if(PDN_GPU_CNT == 1) - { - flushDCacheRange(&configData, sizeof(CfgData)); - flushDCacheRange((void *)fb, sizeof(struct fb)); - invokeArm11Function(ARM11); + static bool needToSetup = true; - clearScreens(true, true); - - //Turn on backlight - i2cWriteRegister(I2C_DEV_MCU, 0x22, 0x2A); - } - else + if(needToSetup) { - clearScreens(true, true); - updateBrightness(MULTICONFIG(BRIGHTNESS)); + if(PDN_GPU_CNT == 1) + { + flushDCacheRange(&configData, sizeof(CfgData)); + invokeArm11Function(initSequence); + + //Turn on backlight + i2cWriteRegister(I2C_DEV_MCU, 0x22, 0x2A); + } + else updateBrightness(MULTICONFIG(BRIGHTNESS)); + + flushDCacheRange((void *)fbs, 2 * sizeof(struct fb)); + invokeArm11Function(setupFramebuffers); + needToSetup = false; } -} + + swapFramebuffers(false); + clearScreens(true, true, false); +} \ No newline at end of file diff --git a/source/screen.h b/source/screen.h index 9e78d62..d7e5818 100644 --- a/source/screen.h +++ b/source/screen.h @@ -34,13 +34,20 @@ #define ARM11_STUB_ADDRESS (0x25000000 - 0x30) //It's currently only 0x28 bytes large. We're putting 0x30 just to be sure here #define WAIT_FOR_ARM9() *arm11Entry = 0; while(!*arm11Entry); ((void (*)())*arm11Entry)(); +#define SCREEN_TOP_WIDTH 400 +#define SCREEN_BOTTOM_WIDTH 320 +#define SCREEN_HEIGHT 240 +#define SCREEN_TOP_FBSIZE (3 * SCREEN_TOP_WIDTH * SCREEN_HEIGHT) +#define SCREEN_BOTTOM_FBSIZE (3 * SCREEN_BOTTOM_WIDTH * SCREEN_HEIGHT) + static volatile struct fb { u8 *top_left; u8 *top_right; u8 *bottom; -} *const fb = (volatile struct fb *)0x23FFFE00; +} *const fbs = (volatile struct fb *)0x23FFFE00; void deinitScreens(void); +void swapFramebuffers(bool isAlternate); void updateBrightness(u32 brightnessIndex); -void clearScreens(bool clearTop, bool clearBottom); +void clearScreens(bool clearTop, bool clearBottom, bool clearAlternate); void initScreens(void); \ No newline at end of file diff --git a/source/utils.c b/source/utils.c index 3addfc8..017fc82 100644 --- a/source/utils.c +++ b/source/utils.c @@ -56,7 +56,7 @@ u32 waitInput(void) void mcuReboot(void) { - if(!isFirmlaunch && PDN_GPU_CNT != 1) clearScreens(true, true); + if(!isFirmlaunch && PDN_GPU_CNT != 1) clearScreens(true, true, false); //Ensure that all memory transfers have completed and that the data cache has been flushed flushEntireDCache(); @@ -67,7 +67,7 @@ void mcuReboot(void) void mcuPowerOff(void) { - if(!isFirmlaunch && PDN_GPU_CNT != 1) clearScreens(true, true); + if(!isFirmlaunch && PDN_GPU_CNT != 1) clearScreens(true, true, false); //Ensure that all memory transfers have completed and that the data cache has been flushed flushEntireDCache();