From 2a6fc2cf2de99eb3e2be23ded6c0b40eca1d1837 Mon Sep 17 00:00:00 2001 From: Cameron Thompson <50184035+iamromulan@users.noreply.github.com> Date: Fri, 1 Aug 2025 23:56:24 -0400 Subject: [PATCH] Add custom section for quick custom firmware prep --- custom/README.md | 7 + .../sdxpinn-firstboot/CONTROL/control | 6 + .../sdxpinn-firstboot/CONTROL/postinst | 5 + custom/ipk-source/sdxpinn-firstboot/build-ipk | 74 ++++++ .../root/etc/init.d/first-boot | 31 +++ .../ipk-source/sdxpinn-patch/CONTROL/control | 6 + .../ipk-source/sdxpinn-patch/CONTROL/postinst | 5 + .../ipk-source/sdxpinn-patch/CONTROL/preinst | 211 ++++++++++++++++++ custom/ipk-source/sdxpinn-patch/CONTROL/prerm | 5 + custom/ipk-source/sdxpinn-patch/build-ipk | 74 ++++++ .../sdxpinn-patch/root/tmp/sdxpinn.patched | 1 + custom/ipk/sdxpinn-firstboot_1.0_sdxpinn.ipk | Bin 0 -> 1133 bytes custom/ipk/sdxpinn-patch_2.5_all.ipk | Bin 0 -> 3053 bytes 13 files changed, 425 insertions(+) create mode 100644 custom/README.md create mode 100755 custom/ipk-source/sdxpinn-firstboot/CONTROL/control create mode 100755 custom/ipk-source/sdxpinn-firstboot/CONTROL/postinst create mode 100755 custom/ipk-source/sdxpinn-firstboot/build-ipk create mode 100755 custom/ipk-source/sdxpinn-firstboot/root/etc/init.d/first-boot create mode 100755 custom/ipk-source/sdxpinn-patch/CONTROL/control create mode 100755 custom/ipk-source/sdxpinn-patch/CONTROL/postinst create mode 100755 custom/ipk-source/sdxpinn-patch/CONTROL/preinst create mode 100755 custom/ipk-source/sdxpinn-patch/CONTROL/prerm create mode 100755 custom/ipk-source/sdxpinn-patch/build-ipk create mode 100644 custom/ipk-source/sdxpinn-patch/root/tmp/sdxpinn.patched create mode 100644 custom/ipk/sdxpinn-firstboot_1.0_sdxpinn.ipk create mode 100644 custom/ipk/sdxpinn-patch_2.5_all.ipk diff --git a/custom/README.md b/custom/README.md new file mode 100644 index 0000000..1d1170e --- /dev/null +++ b/custom/README.md @@ -0,0 +1,7 @@ +# For preping a firmare before capture of the mtd block + +This directory is for custom firmware development. It contains special versions of ipks intended to be installed as apart of a custom firmware. + +This is mostly here to help me simplify the firmware creation process + +~iamromulan diff --git a/custom/ipk-source/sdxpinn-firstboot/CONTROL/control b/custom/ipk-source/sdxpinn-firstboot/CONTROL/control new file mode 100755 index 0000000..04e62a7 --- /dev/null +++ b/custom/ipk-source/sdxpinn-firstboot/CONTROL/control @@ -0,0 +1,6 @@ +Package: sdxpinn-firstboot +Version: 1.0 +Architecture: sdxpinn +Maintainer: Cameron Thompson iamromulan@github.com +Source: github.com/iamromulan +Description: A first boot init after a flash or factory reset diff --git a/custom/ipk-source/sdxpinn-firstboot/CONTROL/postinst b/custom/ipk-source/sdxpinn-firstboot/CONTROL/postinst new file mode 100755 index 0000000..8448a97 --- /dev/null +++ b/custom/ipk-source/sdxpinn-firstboot/CONTROL/postinst @@ -0,0 +1,5 @@ +#!/bin/ash + +service first-boot enable + +exit 0 diff --git a/custom/ipk-source/sdxpinn-firstboot/build-ipk b/custom/ipk-source/sdxpinn-firstboot/build-ipk new file mode 100755 index 0000000..eb83cf6 --- /dev/null +++ b/custom/ipk-source/sdxpinn-firstboot/build-ipk @@ -0,0 +1,74 @@ +#!/bin/sh + +# Script for building OpenWRT .ipk packages using tar by iamromulan +# Works with SDXPPINN OpenWRT - iamromulan +# This script accepts an optional path to the directory containing the `CONTROL` and `root` directories. +# Usage: ./build-ipk.sh [path] +# If no path is provided, the script will look in the current directory for `CONTROL` and `root` directories. +# This will spit out an ipk in the current directory + +# Check if the script is run as root. If not, rerun with sudo. +if [ "$(id -u)" -ne 0 ]; then + echo "Script is not running as root. Re-executing with sudo..." + exec sudo "$0" "$@" +fi + +# Set the default build path to the current directory +build_path="." + +# Check if a path is provided as the first argument +if [ "$1" ]; then + build_path="$1" +fi + +# Check if the required directories are present in the specified path +if [ ! -d "${build_path}/CONTROL" ] || [ ! -d "${build_path}/root" ]; then + echo "Error: CONTROL and root directories must be present in the specified path (${build_path})." + exit 1 +fi + +# Extract values from the CONTROL/control file in the specified path +pkgname=$(grep -i '^Package:' "${build_path}/CONTROL/control" | awk '{print $2}') +version=$(grep -i '^Version:' "${build_path}/CONTROL/control" | awk '{print $2}') +architecture=$(grep -i '^Architecture:' "${build_path}/CONTROL/control" | awk '{print $2}') + +# Check if values are extracted correctly +if [ -z "$pkgname" ] || [ -z "$version" ] || [ -z "$architecture" ]; then + echo "Error: Failed to extract Package, Version, or Architecture from ${build_path}/CONTROL/control." + exit 1 +fi + +# Set the final IPK name based on the extracted values +ipkname="${pkgname}_${version}_${architecture}.ipk" + +# Ensure all CONTROL scripts are executable +echo "Setting permissions for CONTROL scripts..." +chmod +x "${build_path}/CONTROL"/* + +# Set ownership for CONTROL and root files +echo "Setting ownership for all package files..." +chown -R root:root "${build_path}/CONTROL"/* +chown -R root:root "${build_path}/root"/* + +# Create control.tar.gz from the CONTROL directory +echo "Creating control.tar.gz..." +tar -czvf control.tar.gz -C "${build_path}/CONTROL" . + +# Create data.tar.gz from the root directory +echo "Creating data.tar.gz..." +tar -czvf data.tar.gz -C "${build_path}/root" . + +# Create debian-binary file (must contain exactly "2.0" without a newline) +echo -n "2.0" > debian-binary +chown -R root:root debian-binary + +# Combine the components into the final .ipk file using tar +echo "Packaging ${ipkname}..." +tar -czvf "$ipkname" debian-binary control.tar.gz data.tar.gz + +# Clean up intermediate files +echo "Cleaning up temporary files..." +rm -f control.tar.gz data.tar.gz debian-binary + +echo "IPK package ${ipkname} created successfully using tar." + diff --git a/custom/ipk-source/sdxpinn-firstboot/root/etc/init.d/first-boot b/custom/ipk-source/sdxpinn-firstboot/root/etc/init.d/first-boot new file mode 100755 index 0000000..7d68c0c --- /dev/null +++ b/custom/ipk-source/sdxpinn-firstboot/root/etc/init.d/first-boot @@ -0,0 +1,31 @@ +#!/bin/sh /etc/rc.common +START=99 +STOP=15 +USE_PROCD=1 +start_service() { + # Create log file + touch /usrdata/first-boot.log + + # Redirect all output to log file + exec >>/usrdata/first-boot.log 2>&1 + + echo "Starting first boot script: $(date)" + + echo "Remounting the real rootfs as rw" + mount -o remount,rw /real_rootfs + + echo "Sending AT Commands" + atcmd 'AT+QCFG="usbnet",1' + atcmd 'AT+QCFG="pcie/mode",1' + + echo "Disabling first-boot service" + service first-boot disable + + echo "Restarting modem" + atcmd 'AT+CFUN=1,1' + + echo "Removing first-boot service" + rm /real_rootfs/etc/init.d/first-boot + + echo "First boot script completed: $(date)" +} diff --git a/custom/ipk-source/sdxpinn-patch/CONTROL/control b/custom/ipk-source/sdxpinn-patch/CONTROL/control new file mode 100755 index 0000000..5bf7511 --- /dev/null +++ b/custom/ipk-source/sdxpinn-patch/CONTROL/control @@ -0,0 +1,6 @@ +Package: sdxpinn-patch +Version: 2.5 +Architecture: all +Maintainer: Cameron Thompson iamromulan@github.com +Source: github.com/iamromulan +Description: A general first system patch for SDXPINN OpenWRT diff --git a/custom/ipk-source/sdxpinn-patch/CONTROL/postinst b/custom/ipk-source/sdxpinn-patch/CONTROL/postinst new file mode 100755 index 0000000..44547bc --- /dev/null +++ b/custom/ipk-source/sdxpinn-patch/CONTROL/postinst @@ -0,0 +1,5 @@ +#!/bin/ash + +echo "Complete" + +exit 0 diff --git a/custom/ipk-source/sdxpinn-patch/CONTROL/preinst b/custom/ipk-source/sdxpinn-patch/CONTROL/preinst new file mode 100755 index 0000000..1815504 --- /dev/null +++ b/custom/ipk-source/sdxpinn-patch/CONTROL/preinst @@ -0,0 +1,211 @@ +#!/bin/ash + +clean_distfeeds() { + # Define the list of items to comment out + items_to_comment=$(cat < "$TEMP_FILE" # Clear/create the temp file + + # Process each line in the input file + while IFS= read -r line || [ -n "$line" ]; do + # Skip empty lines + [ -z "$line" ] && continue + + # Skip malformed lines + echo "$line" | grep -qE '^(#[[:space:]]*)?src/gz ' || continue + + # Remove any existing comment marks for comparison + clean_line=$(echo "$line" | sed 's/^#[[:space:]]*//') + + # Check if line should be commented + if echo "$items_to_comment" | grep -Fxq "$clean_line"; then + echo "# $clean_line" >> "$TEMP_FILE" + continue + fi + + # Check if line should be kept uncommented + if echo "$items_to_keep" | grep -Fxq "$clean_line"; then + echo "$clean_line" >> "$TEMP_FILE" + continue + fi + + # Preserve other lines as-is + echo "$line" >> "$TEMP_FILE" + done < "$INPUT_FILE" + + # Add any missing items_to_keep entries + echo "$items_to_keep" | while IFS= read -r keep_item; do + [ -z "$keep_item" ] && continue + if ! grep -Fxq "$keep_item" "$TEMP_FILE"; then + echo "$keep_item" >> "$TEMP_FILE" + fi + done + + # Replace the original file with the modified one + mv "$TEMP_FILE" "$INPUT_FILE" + + echo "Processed '$INPUT_FILE'. Backup saved as '${INPUT_FILE}.bak'." +} + + +repo_iamromulanSDXPINN_check() { + # Define the repository and key details + STABLE_REPO_LINE="src/gz iamromulan-SDXPINN-repo https://raw.githubusercontent.com/iamromulan/quectel-rgmii-toolkit/SDXPINN/opkg-feed" + DEV_REPO_LINE="src/gz iamromulan-SDXPINN-repo https://raw.githubusercontent.com/iamromulan/quectel-rgmii-toolkit/development-SDXPINN/opkg-feed" + FEEDS_FILE="/etc/opkg/customfeeds.conf" + PUBLIC_KEY_URL="https://raw.githubusercontent.com/iamromulan/quectel-rgmii-toolkit/SDXPINN/opkg-feed/iamromulan-SDXPINN-repo.key" + TMP_KEY="/tmp/iamromulan-SDXPINN-repo.key" + KEYS_DIR="/etc/opkg/keys" + + # Remove duplicate and development repo lines + if grep -q "iamromulan-SDXPINN-repo" "$FEEDS_FILE"; then + echo "Cleaning up duplicate or development repo entries in $FEEDS_FILE." + sed -i "/iamromulan-SDXPINN-repo/d" "$FEEDS_FILE" + fi + + # Add the stable repository to customfeeds.conf + echo "Adding stable repository to $FEEDS_FILE." + echo "$STABLE_REPO_LINE" >> "$FEEDS_FILE" || { + echo "Error: Could not add repository to $FEEDS_FILE." + return 1 + } + + # Download the key temporarily for verification + curl -fsSL "$PUBLIC_KEY_URL" -o "$TMP_KEY" || { + echo "Error: Failed to download public key from $PUBLIC_KEY_URL." + return 1 + } + + # Check if the key already exists in /etc/opkg/keys/ + local key_installed=0 + for key in "$KEYS_DIR"/*; do + if cmp -s "$TMP_KEY" "$key"; then + echo "Public key already installed: $(basename "$key")." + key_installed=1 + break + fi + done + + # If the key is not installed, add it + if [ "$key_installed" -eq 0 ]; then + echo "Installing public key..." + opkg-key add "$TMP_KEY" || { + echo "Error: Failed to add public key." + rm -f "$TMP_KEY" + return 1 + } + echo "Public key installed successfully." + fi + + # Clean up temporary key file + rm -f "$TMP_KEY" + + return 0 +} + +noprerm_mount_fix() { + + if [ -f /real_rootfs/usr/lib/opkg/info/sdxpinn-mount-fix.prerm ]; then + mount -o remount,rw /real_rootfs + sleep 1 + rm /real_rootfs/usr/lib/opkg/info/sdxpinn-mount-fix.prerm + sleep 1 + mount -o remount,ro /real_rootfs + fi + + if [ -f /usr/lib/opkg/info/sdxpinn-mount-fix.prerm ]; then + rm /usr/lib/opkg/info/sdxpinn-mount-fix.prerm + fi +} + +opkg_arch_patch() { +if [ ! -f /etc/opkg/arch.conf ]; then + cat > /etc/opkg/arch.conf << 'EOF' +arch all 1 +arch noarch 1 +arch sdxpinn 2 +arch aarch64_cortex-a53 10 +arch aarch64_generic 20 +EOF +else + for arch in "all 1" "noarch 1" "sdxpinn 2" "aarch64_cortex-a53 10" "aarch64_generic 20"; do + if ! grep -q "^arch $arch$" /etc/opkg/arch.conf; then + echo "arch $arch" >> /etc/opkg/arch.conf + fi + done +fi +} + + +# Begin Patch Process +echo -e "\e[92mBegining sdxpinn-patch process...\e[0m" +echo -e "\e[92mEnsuring distfeeds is clean of any non-working sources and has stock OpenWRT ones...\e[0m" +clean_distfeeds +echo -e "\e[92mEnsuring iamromulan's SDXPINN repo is listed in customfeeds and public key is installed...\e[0m" +repo_iamromulanSDXPINN_check +echo -e "\e[92miamromulan's SDXPINN repo source and key is installed.\e[0m" +echo -e "\e[92mEnsuring mount-fix does not have a prerm\e[0m" +noprerm_mount_fix +echo -e "\e[92mEnsuring opkg reconizes needed archs\e[0m" +opkg_arch_patch +echo -e "\e[92msdxpinn-patch process complete.\e[0m" +exit 0 diff --git a/custom/ipk-source/sdxpinn-patch/CONTROL/prerm b/custom/ipk-source/sdxpinn-patch/CONTROL/prerm new file mode 100755 index 0000000..59f2ec6 --- /dev/null +++ b/custom/ipk-source/sdxpinn-patch/CONTROL/prerm @@ -0,0 +1,5 @@ +#!/bin/ash + +echo -e "\e[38;5;196mINFO: This package can not be removed by opkg\e[0m" +echo -e "\e[38;5;40mINFO: This package can be upgraded...\e[0m" +exit 0 \ No newline at end of file diff --git a/custom/ipk-source/sdxpinn-patch/build-ipk b/custom/ipk-source/sdxpinn-patch/build-ipk new file mode 100755 index 0000000..eb83cf6 --- /dev/null +++ b/custom/ipk-source/sdxpinn-patch/build-ipk @@ -0,0 +1,74 @@ +#!/bin/sh + +# Script for building OpenWRT .ipk packages using tar by iamromulan +# Works with SDXPPINN OpenWRT - iamromulan +# This script accepts an optional path to the directory containing the `CONTROL` and `root` directories. +# Usage: ./build-ipk.sh [path] +# If no path is provided, the script will look in the current directory for `CONTROL` and `root` directories. +# This will spit out an ipk in the current directory + +# Check if the script is run as root. If not, rerun with sudo. +if [ "$(id -u)" -ne 0 ]; then + echo "Script is not running as root. Re-executing with sudo..." + exec sudo "$0" "$@" +fi + +# Set the default build path to the current directory +build_path="." + +# Check if a path is provided as the first argument +if [ "$1" ]; then + build_path="$1" +fi + +# Check if the required directories are present in the specified path +if [ ! -d "${build_path}/CONTROL" ] || [ ! -d "${build_path}/root" ]; then + echo "Error: CONTROL and root directories must be present in the specified path (${build_path})." + exit 1 +fi + +# Extract values from the CONTROL/control file in the specified path +pkgname=$(grep -i '^Package:' "${build_path}/CONTROL/control" | awk '{print $2}') +version=$(grep -i '^Version:' "${build_path}/CONTROL/control" | awk '{print $2}') +architecture=$(grep -i '^Architecture:' "${build_path}/CONTROL/control" | awk '{print $2}') + +# Check if values are extracted correctly +if [ -z "$pkgname" ] || [ -z "$version" ] || [ -z "$architecture" ]; then + echo "Error: Failed to extract Package, Version, or Architecture from ${build_path}/CONTROL/control." + exit 1 +fi + +# Set the final IPK name based on the extracted values +ipkname="${pkgname}_${version}_${architecture}.ipk" + +# Ensure all CONTROL scripts are executable +echo "Setting permissions for CONTROL scripts..." +chmod +x "${build_path}/CONTROL"/* + +# Set ownership for CONTROL and root files +echo "Setting ownership for all package files..." +chown -R root:root "${build_path}/CONTROL"/* +chown -R root:root "${build_path}/root"/* + +# Create control.tar.gz from the CONTROL directory +echo "Creating control.tar.gz..." +tar -czvf control.tar.gz -C "${build_path}/CONTROL" . + +# Create data.tar.gz from the root directory +echo "Creating data.tar.gz..." +tar -czvf data.tar.gz -C "${build_path}/root" . + +# Create debian-binary file (must contain exactly "2.0" without a newline) +echo -n "2.0" > debian-binary +chown -R root:root debian-binary + +# Combine the components into the final .ipk file using tar +echo "Packaging ${ipkname}..." +tar -czvf "$ipkname" debian-binary control.tar.gz data.tar.gz + +# Clean up intermediate files +echo "Cleaning up temporary files..." +rm -f control.tar.gz data.tar.gz debian-binary + +echo "IPK package ${ipkname} created successfully using tar." + diff --git a/custom/ipk-source/sdxpinn-patch/root/tmp/sdxpinn.patched b/custom/ipk-source/sdxpinn-patch/root/tmp/sdxpinn.patched new file mode 100644 index 0000000..0cfbf08 --- /dev/null +++ b/custom/ipk-source/sdxpinn-patch/root/tmp/sdxpinn.patched @@ -0,0 +1 @@ +2 diff --git a/custom/ipk/sdxpinn-firstboot_1.0_sdxpinn.ipk b/custom/ipk/sdxpinn-firstboot_1.0_sdxpinn.ipk new file mode 100644 index 0000000000000000000000000000000000000000..c38ba5a38a869859f2c346b21d82ba42018df813 GIT binary patch literal 1133 zcmV-z1d{t7iwFP!0000014~Iw%1q4DP0GwmEUILn4KM%#GZPaq4Fu>k5EwHUni`lG z8ylDz7#lMf7#bOx8!{*u(8xU~ptQIou}DFIp(sDU1fM!W@Cq*WN5LQk$@zID zMfo{;z)YZ*UPT)_2<3lMQ&aT(Z)|2dn*ZqmzmlG zFP6$PjP+XPRJMAr)8*E}yQV#}8}H^Fh!A!Nb5!#bY%zkc5PPx_nYeEKh~r$6b^jg$`IiI-}2=UxBBzVxk;?E6Z#tbfzv@@=mF<==g*e!a9?{+s^I3d|Ha3!@@#Th4by(<1dE1+DIvxlNA)8=9cd7NYahtG@O8aW!u1%)Dbgk!oFgdMjfa{5`Xh7Bi)iYH;tF-bD zY`qn^v^I$IwD9aAgVu$aMrVaXRTo*E6S9!<+5bnTZ{PP>t+Sr{nERi+cvFq1G<>;;re|Oid zWShCRH0$^L3u)IbE^ACkE1ha|cG9e=^-}L^mkY2sVxoJ^`^ER*C-eTqhJ(_ zf>AIEM!_f;1*2dTjDk@x3P!;w7zLwX6pVsVFbYP&C>RB!00jU5!4C1i04M+e_WM`x literal 0 HcmV?d00001 diff --git a/custom/ipk/sdxpinn-patch_2.5_all.ipk b/custom/ipk/sdxpinn-patch_2.5_all.ipk new file mode 100644 index 0000000000000000000000000000000000000000..6c1b422ae7c31c81a6b2be35dfa641e4b87240a6 GIT binary patch literal 3053 zcmViSY;dFA0T6 z{XYL;QqrXS|1)rfQvYiJTr>WT9+Nq@{^9tN!Rz7j(2BWXC+8&j_0e}^{#}&{(d*;s zLu{o=V}SzNJh_5PGuXxpI4>hiVbG@xgR_zEpdB0Rv^pg`3S zx2nU@t`Xreol1a{&5^rmZSipcm8-gdA-1`Cbl}&Ox9zPgE-#Wrt-H4Sr=1_|G;}-8 zQj`F5L?W@DJ;jp+bOyAeo`hE}xE>c9rhN&)0k`}1a=(yO8(^--Zt{f2VnySPRLUA! zk9w{|1Oi*jit_VO3z6!o#acE!3+4)BqkHYipxmc*Q8=QKt;+oU$gi!~RB)r0vS|o> ze`rK`L6v<=2;aa=(@8#(g+_;;Oij>7*FQU{KT^HPd6HQ#b=(;8xz_f*!GjUZ$dU}W zA|SUbD^X{;^Auf24lP-=D(w=7f66NX0Wh3VUMP~@2f-vn-ywhYORd_{L=Ezn{Q8gHu*SI&)Gix5i@jz%MecRND(_(1qdGI|9Pwu8K0Pu#aC&j~@cAuS<-+N> z^+#iY0K<;i%WnpA0cGKNAY;KQ=4AaB^=n5U#X*Q9q?)2Kz)Fhl+DV%*y@wtJoxjwf zlHZksbcd}Q*2*?jOK@u{5eH}9r~31oydm~6z^zUW#EJd(KnfZJZeF)6(G&q1TKZ$u zS_SATje_oKx*|n@f>(+Ew96elo94GVqYPY|>cSmRT6LDt^hE0QnAch70kGo{fekPtVTA#D ze1Hq0P(wEQahi`(WXfA+wqI^sTq@2_D1fA3z;t* z1$d(;!D^cqyf)Xzi|9@q>Kspfnox3A@jWKPQy<2d21q~9;#ci&5YxMRnZ<{3)F0u| zs}cMzXW|uTHapt%9Mfdf;CKJq_=>N^>H!3e<$UU-Ha)xs*i$?~`)+seRlXiZU@To1 z48>t$rs$o0{~NT>DcjBRtvCPVbJL!fEQP z8c=I8Vr@_+^evA*iw(<3^Im^Vlx#oy%9tEw57&H%gRyzh^Q|1^1v4*24ztNKk@;wa= zWAB_~tLV5>70{l(R|aW&vI6ZZfCbOtnU>>cBQ+ve5qC#mMf6(x%T=>9)s7)fGJc)N zmBhsId*P}v^_bBq@oAk7BSs#X3Dn1X;$LvXX!sP1iU}d%0WXKH zb|cr+K;H)wvc{g63s#0ucG?w}_cI+$^Z~cjMp}<`>hvFarw#Titg_s<<_J#u!0%Br zSQI33%7=E6fY-;dljBhdbOTy4G$4++YQoERDs*3VyDm^ngZZj0va=IhTD~Iq0cJ~d!Qjpm zx15q?(%sidRrO2k8F4lGRK^UT_E4&*7dBl?(6|)cvOO&7_K>B!I_`5||3$eQOSS_Q z%6)6Bss76mjnDGPUGO*MKGRrcljoy#(pj8Lm>UXlTjLqR4ZcK~(4u}0rYFiR4LY)* z_~p~SUeC&}x>qRLC^n1Nh?sYKV0Q9LyJCdQt+t8QIRR!<%*v4PV^+tbu0xA*zMK8% zwHyAcv^dTA#|#%}cgQGr`DOU#q2s3+69fa!#6gRl%a-PFKmew+xtw^l+Yu$?ceeuC3)E!-ywWc_2wS8>oK9>$air? zz6QQ z52S_b_Pyid(GHC(Echg0buFGV!IbAyEq5;k9h^s@dB-{FoMviX#x2Wg(V58|18u!* z(-4n$ODGk;WtN!i5~?o5%(?RNEz!WmVOZu+G)fFyLvxYxaf>`JIG&uVpvgU}S>&zb zh1@>Losm#gX3ZSu_e`^ET)x2VA~E7|(I)#`)!OliBV1(sgmZNxV)T3C7s&>+J^Hsr(H4KgmTGnvR8ZZN={EQTaR>l zO6fC%J)SMS9N{98HTGDNr_f}xAU0#u!k91=T9myZma?JC6jC~}Pj`lR-8f=i2|E{d zfH|owZ2esS@@tCM?#8yUj_$pIeMi~U?YPNBV9sFAj&$bnGgw08qd7UjBTuaJ{w~#T vPrd(!lhQ8Z3gF^jab+YVBqSsxBqSsxBqSsxBqSsxe;fY*P7Oc|04M+eOeGD8 literal 0 HcmV?d00001