From 0ebff73e8e06508b670fd2aca14b2971b6892b6b Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 5 Apr 2018 20:43:35 +0000 Subject: [PATCH] Add Linux AppImage packaging. Requires a system installation of mono >= 4.2 but bundles all other requirements. --- .travis.yml | 1 + README.md | 2 +- mod.config | 11 ++ packaging/linux/buildpackage.sh | 125 +++++++++++++++++++ packaging/linux/include/AppRun.in | 36 ++++++ packaging/linux/include/Eluant.dll.config | 3 + packaging/linux/include/OpenAL-CS.dll.config | 4 + packaging/linux/include/SDL2-CS.dll.config | 4 + packaging/linux/include/mod-mimeinfo.xml.in | 8 ++ packaging/linux/include/mod-server.in | 6 + packaging/linux/include/mod.desktop.in | 11 ++ packaging/linux/include/mod.in | 46 +++++++ packaging/linux/mod_128x128.png | Bin 0 -> 1852 bytes packaging/linux/mod_16x16.png | Bin 0 -> 265 bytes packaging/linux/mod_256x256.png | Bin 0 -> 4487 bytes packaging/linux/mod_32x32.png | Bin 0 -> 542 bytes packaging/linux/mod_48x48.png | Bin 0 -> 1672 bytes packaging/linux/mod_64x64.png | Bin 0 -> 952 bytes packaging/package-all.sh | 10 ++ 19 files changed, 266 insertions(+), 1 deletion(-) create mode 100755 packaging/linux/buildpackage.sh create mode 100755 packaging/linux/include/AppRun.in create mode 100644 packaging/linux/include/Eluant.dll.config create mode 100644 packaging/linux/include/OpenAL-CS.dll.config create mode 100644 packaging/linux/include/SDL2-CS.dll.config create mode 100644 packaging/linux/include/mod-mimeinfo.xml.in create mode 100755 packaging/linux/include/mod-server.in create mode 100644 packaging/linux/include/mod.desktop.in create mode 100755 packaging/linux/include/mod.in create mode 100644 packaging/linux/mod_128x128.png create mode 100644 packaging/linux/mod_16x16.png create mode 100644 packaging/linux/mod_256x256.png create mode 100644 packaging/linux/mod_32x32.png create mode 100644 packaging/linux/mod_48x48.png create mode 100644 packaging/linux/mod_64x64.png diff --git a/.travis.yml b/.travis.yml index 1b9efa0..61ea43c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,6 +30,7 @@ deploy: - build/${PACKAGING_INSTALLER_NAME}-${TRAVIS_TAG}.exe - build/${PACKAGING_INSTALLER_NAME}-${TRAVIS_TAG}-macOS.zip - build/${PACKAGING_INSTALLER_NAME}-${TRAVIS_TAG}-winportable.zip + - build/${PACKAGING_INSTALLER_NAME}-${TRAVIS_TAG}.AppImage skip_cleanup: true on: tags: true diff --git a/README.md b/README.md index 0e9a510..d55cd7c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ This repository contains a bare development environment for creating a new mod/game on the [OpenRA](https://github.com/OpenRA/OpenRA) engine. -These scripts and support files wrap and automatically manage a copy of the OpenRA game engine and common files, and provide entrypoints to run development versions of your project and to generate platform-specific installers for your players. +These scripts and support files wrap and automatically manage a copy of the OpenRA game engine and common files during development, and generates Windows installers, macOS .app bundles, and Linux [AppImages](https://appimage.org/) for distribution. The key scripts in this SDK are: diff --git a/mod.config b/mod.config index b95ead9..f4c362d 100644 --- a/mod.config +++ b/mod.config @@ -41,6 +41,7 @@ TRAVIS_TEST_PACKAGING="False" # The prefix used for the installer filenames. # - Windows installers will be named as {PACKAGING_INSTALLER_NAME}-{TAG}.exe # - macOS installers will be named as {PACKAGING_INSTALLER_NAME}-{TAG}.zip +# - Linux .appimages will be named as {PACKAGING_INSTALLER_NAME}-${TAG}.AppImage PACKAGING_INSTALLER_NAME="ExampleMod" # The human-readable name for this project. @@ -53,6 +54,7 @@ PACKAGING_INSTALLER_NAME="ExampleMod" # - Windows start menu # - Windows desktop shortcut # - Windows "Programs and Features" list +# - Linux launcher shortcut PACKAGING_DISPLAY_NAME="Example Mod" # The URL for the project homepage. @@ -81,6 +83,9 @@ PACKAGING_WINDOWS_INSTALL_DIR_NAME="OpenRA Example Mod" # This should not contain spaces or special characters. PACKAGING_WINDOWS_REGISTRY_KEY="OpenRAExampleMod" +# The git tag to use for the AppImage dependencies. +PACKAGING_APPIMAGE_DEPENDENCIES_TAG="20180408" + ############################################################################## # Advanced Configuration # @@ -105,3 +110,9 @@ PACKAGING_OSX_LAUNCHER_SOURCE="https://github.com/OpenRA/OpenRALauncherOSX/relea # Temporary file name used when downloading the OpenRA macOS launcher files. PACKAGING_OSX_LAUNCHER_TEMP_ARCHIVE_NAME="launcher.zip" + +# The url to download the OpenRA AppImage dependencies. +PACKAGING_APPIMAGE_DEPENDENCIES_SOURCE="https://github.com/OpenRA/AppImageSupport/releases/download/${PACKAGING_APPIMAGE_DEPENDENCIES_TAG}/libs.tar.bz2" + +# Temporary file name used when downloading the OpenRA AppImage dependencies. +PACKAGING_APPIMAGE_DEPENDENCIES_TEMP_ARCHIVE_NAME="libs.tar.bz2" diff --git a/packaging/linux/buildpackage.sh b/packaging/linux/buildpackage.sh new file mode 100755 index 0000000..d4efa9a --- /dev/null +++ b/packaging/linux/buildpackage.sh @@ -0,0 +1,125 @@ +#!/bin/bash +# OpenRA packaging script for Linux (AppImage) +set -e + +command -v make >/dev/null 2>&1 || { echo >&2 "The OpenRA mod template requires make."; exit 1; } +command -v python >/dev/null 2>&1 || { echo >&2 "The OpenRA mod template requires python."; exit 1; } +command -v tar >/dev/null 2>&1 || { echo >&2 "The OpenRA mod template requires tar."; exit 1; } +command -v curl >/dev/null 2>&1 || { echo >&2 "The OpenRA mod template requires curl."; exit 1; } + +if [ $# -eq "0" ]; then + echo "Usage: `basename $0` version [outputdir]" + exit 1 +fi + +PACKAGING_DIR=$(python -c "import os; print(os.path.dirname(os.path.realpath('$0')))") +TEMPLATE_ROOT="${PACKAGING_DIR}/../../" + +# shellcheck source=mod.config +. "${TEMPLATE_ROOT}/mod.config" + +if [ -f "${TEMPLATE_ROOT}/user.config" ]; then + # shellcheck source=user.config + . "${TEMPLATE_ROOT}/user.config" +fi + +if [ "${INCLUDE_DEFAULT_MODS}" = "True" ]; then + echo "Cannot generate installers while INCLUDE_DEFAULT_MODS is enabled." + echo "Make sure that this setting is disabled in both your mod.config and user.config." + exit 1 +fi + +TAG="$1" +if [ $# -eq "1" ]; then + OUTPUTDIR=$(python -c "import os; print(os.path.realpath('.'))") +else + OUTPUTDIR=$(python -c "import os; print(os.path.realpath('$2'))") +fi + +BUILTDIR="${PACKAGING_DIR}/${PACKAGING_INSTALLER_NAME}.appdir" + +# Set the working dir to the location of this script +cd "${PACKAGING_DIR}" + +pushd "${TEMPLATE_ROOT}" > /dev/null + +if [ ! -f "${ENGINE_DIRECTORY}/Makefile" ]; then + echo "Required engine files not found." + echo "Run \`make\` in the mod directory to fetch and build the required files, then try again."; + exit 1 +fi + +if [ ! -d "${OUTPUTDIR}" ]; then + echo "Output directory '${OUTPUTDIR}' does not exist."; + exit 1 +fi + +echo "Building core files" + +make version VERSION="${TAG}" + +pushd "${ENGINE_DIRECTORY}" > /dev/null +make linux-dependencies +make core SDK="-sdk:4.5" +make install-engine prefix="usr" DESTDIR="${BUILTDIR}/" +make install-common-mod-files prefix="usr" DESTDIR="${BUILTDIR}/" + +popd > /dev/null +popd > /dev/null + +# Add native libraries +echo "Downloading dependencies" +curl -s -L -o "${PACKAGING_APPIMAGE_DEPENDENCIES_TEMP_ARCHIVE_NAME}" -O "${PACKAGING_APPIMAGE_DEPENDENCIES_SOURCE}" || exit 3 +tar xf "${PACKAGING_APPIMAGE_DEPENDENCIES_TEMP_ARCHIVE_NAME}" + +curl -s -L -O https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage +chmod a+x appimagetool-x86_64.AppImage + +echo "Building AppImage" + +# Add mod files +cp -r "${TEMPLATE_ROOT}/mods/"* "${BUILTDIR}/usr/lib/openra/mods" + +install -Dm 0755 libSDL2.so "${BUILTDIR}/usr/lib/openra/" +install -Dm 0644 include/SDL2-CS.dll.config "${BUILTDIR}/usr/lib/openra/" +install -Dm 0755 libopenal.so "${BUILTDIR}/usr/lib/openra/" +install -Dm 0644 include/OpenAL-CS.dll.config "${BUILTDIR}/usr/lib/openra/" +install -Dm 0755 liblua.so "${BUILTDIR}/usr/lib/openra/" +install -Dm 0644 include/Eluant.dll.config "${BUILTDIR}/usr/lib/openra/" + +# Add launcher and icons +sed "s/{MODID}/${MOD_ID}/g" include/AppRun.in | sed "s/{MODNAME}/${PACKAGING_DISPLAY_NAME}/g" > AppRun.temp +install -m 0755 AppRun.temp "${BUILTDIR}/AppRun" + +sed "s/{MODID}/${MOD_ID}/g" include/mod.desktop.in | sed "s/{MODNAME}/${PACKAGING_DISPLAY_NAME}/g" | sed "s/{TAG}/${TAG}/g" > temp.desktop +install -Dm 0755 temp.desktop "${BUILTDIR}/usr/share/applications/openra-${MOD_ID}.desktop" +install -m 0755 temp.desktop "${BUILTDIR}/openra-${MOD_ID}.desktop" + +sed "s/{MODID}/${MOD_ID}/g" include/mod-mimeinfo.xml.in | sed "s/{TAG}/${TAG}/g" > temp.xml +install -Dm 0755 temp.xml "${BUILTDIR}/usr/share/mime/packages/openra-${MOD_ID}.xml" + +if [ -f "${PACKAGING_DIR}/mod_scalable.svg" ]; then + install -Dm644 "${PACKAGING_DIR}/mod_scalable.svg" "${BUILTDIR}/usr/share/icons/hicolor/scalable/apps/openra-${MOD_ID}.svg" +fi + +for i in 16x16 32x32 48x48 64x64 128x128 256x256 512x512 1024x1024; do + if [ -f "${PACKAGING_DIR}/mod_${i}.png" ]; then + install -Dm644 "${PACKAGING_DIR}/mod_${i}.png" "${BUILTDIR}/usr/share/icons/hicolor/${i}/apps/openra-${MOD_ID}.png" + install -m644 "${PACKAGING_DIR}/mod_${i}.png" "${BUILTDIR}/openra-${MOD_ID}.png" + fi +done + +install -d "${BUILTDIR}/usr/bin" + +sed "s/{MODID}/${MOD_ID}/g" include/mod.in | sed "s/{TAG}/${TAG}/g" | sed "s/{MODNAME}/${PACKAGING_DISPLAY_NAME}/g" | sed "s/{MODINSTALLERNAME}/${PACKAGING_INSTALLER_NAME}/g" | sed "s|{MODFAQURL}|${PACKAGING_FAQ_URL}|g" > openra-mod.temp +install -m 0755 openra-mod.temp "${BUILTDIR}/usr/bin/openra-${MOD_ID}" + +sed "s/{MODID}/${MOD_ID}/g" include/mod-server.in > openra-mod-server.temp +install -m 0755 openra-mod-server.temp "${BUILTDIR}/usr/bin/openra-${MOD_ID}-server" + +# travis-ci doesn't support mounting FUSE filesystems so extract and run the contents manually +./appimagetool-x86_64.AppImage --appimage-extract +ARCH=x86_64 ./squashfs-root/AppRun "${BUILTDIR}" "${OUTPUTDIR}/${PACKAGING_INSTALLER_NAME}-${TAG}.AppImage" + +# Clean up +rm -rf openra-mod.temp openra-mod-server.temp temp.desktop temp.xml AppRun.temp libSDL2.so libopenal.so liblua.so appimagetool-x86_64.AppImage squashfs-root "${PACKAGING_APPIMAGE_DEPENDENCIES_TEMP_ARCHIVE_NAME}" "${BUILTDIR}" diff --git a/packaging/linux/include/AppRun.in b/packaging/linux/include/AppRun.in new file mode 100755 index 0000000..778e706 --- /dev/null +++ b/packaging/linux/include/AppRun.in @@ -0,0 +1,36 @@ +#!/bin/sh + +# Make sure the user has a sufficiently recent version of mono on the PATH +MINIMUM_MONO_VERSION="4.2" + +make_version() { + echo "$1" | tr '.' '\n' | head -n 4 | xargs printf "%03d%03d%03d%03d"; +} + +mono_missing_or_old() { + command -v mono >/dev/null 2>&1 || return 0 + MONO_VERSION=$(mono --version | head -n1 | cut -d' ' -f5) + [ "$(make_version "${MONO_VERSION}")" -lt "$(make_version "${MINIMUM_MONO_VERSION}")" ] && return 0 + return 1 +} + +if mono_missing_or_old; then + if command -v zenity > /dev/null + then + zenity --no-wrap --error --title "{MODNAME}" --text "{MODNAME} requires Mono ${MINIMUM_MONO_VERSION} or greater.\nPlease install Mono using your system package manager." + else + printf "{MODNAME} requires Mono %s or greater.\nPlease install Mono using your system package manager.\n" "${MINIMUM_MONO_VERSION}" + fi + exit 1 +fi + +# Run the game or server +HERE="$(dirname "$(readlink -f "${0}")")" +export PATH="${HERE}"/usr/bin/:"${PATH}" +export XDG_DATA_DIRS="${HERE}"/usr/share/:"${XDG_DATA_DIRS}" + +if [ -n "$1" ] && [ "$1" = "--server" ]; then + exec "openra-{MODID}-server" "$@" +else + exec "openra-{MODID}" "$@" +fi diff --git a/packaging/linux/include/Eluant.dll.config b/packaging/linux/include/Eluant.dll.config new file mode 100644 index 0000000..561d8e9 --- /dev/null +++ b/packaging/linux/include/Eluant.dll.config @@ -0,0 +1,3 @@ + + + diff --git a/packaging/linux/include/OpenAL-CS.dll.config b/packaging/linux/include/OpenAL-CS.dll.config new file mode 100644 index 0000000..43cedc7 --- /dev/null +++ b/packaging/linux/include/OpenAL-CS.dll.config @@ -0,0 +1,4 @@ + + + + diff --git a/packaging/linux/include/SDL2-CS.dll.config b/packaging/linux/include/SDL2-CS.dll.config new file mode 100644 index 0000000..a2aa928 --- /dev/null +++ b/packaging/linux/include/SDL2-CS.dll.config @@ -0,0 +1,4 @@ + + + + diff --git a/packaging/linux/include/mod-mimeinfo.xml.in b/packaging/linux/include/mod-mimeinfo.xml.in new file mode 100644 index 0000000..a1e8c19 --- /dev/null +++ b/packaging/linux/include/mod-mimeinfo.xml.in @@ -0,0 +1,8 @@ + + + + + Join OpenRA server + + + diff --git a/packaging/linux/include/mod-server.in b/packaging/linux/include/mod-server.in new file mode 100755 index 0000000..a2798c2 --- /dev/null +++ b/packaging/linux/include/mod-server.in @@ -0,0 +1,6 @@ +#!/bin/sh + +HERE="$(dirname "$(readlink -f "${0}")")" +cd "${HERE}/../lib/openra" || exit 1 + +mono --debug OpenRA.Server.exe Game.Mod={MODID} "$@" diff --git a/packaging/linux/include/mod.desktop.in b/packaging/linux/include/mod.desktop.in new file mode 100644 index 0000000..05f6885 --- /dev/null +++ b/packaging/linux/include/mod.desktop.in @@ -0,0 +1,11 @@ +[Desktop Entry] +Type=Application +Version=1.0 +Name=OpenRA - {MODNAME} +GenericName=Real Time Strategy Game +Icon=openra-{MODID} +Exec=openra-{MODID} %u +Terminal=false +Categories=Game;StrategyGame; +StartupWMClass=openra-{MODID} +MimeType=x-scheme-handler/openra-{MODID}-{TAG}; diff --git a/packaging/linux/include/mod.in b/packaging/linux/include/mod.in new file mode 100755 index 0000000..1e3785e --- /dev/null +++ b/packaging/linux/include/mod.in @@ -0,0 +1,46 @@ +#!/bin/sh + +LAUNCHER=$(readlink -f "${0}") +HERE="$(dirname "${LAUNCHER}")" +cd "${HERE}/../lib/openra" || exit 1 + +# APPIMAGE is an environment variable set by the runtime +# defining the absolute path to the .AppImage file +if [ ! -z "${APPIMAGE}" ]; then + LAUNCHER=${APPIMAGE} + + # appimaged doesn't update the mime cache database when registering AppImages. + # Run update-desktop-database ourselves if we detect that the desktop file has + # been installed but the handler is not yet present in the cache + if command -v update-desktop-database > /dev/null; then + APPIMAGEID=$(printf "file://%s" "${APPIMAGE}" | md5sum | cut -d' ' -f1) + LAUNCHER_NAME="appimagekit_${APPIMAGEID}-openra-{MODID}.desktop" + LAUNCHER_PATH="${HOME}/.local/share/applications/${LAUNCHER_NAME}" + MIMECACHE_PATH="${HOME}/.local/share/applications/mimeinfo.cache" + SCHEME="x-scheme-handler/openra-{MODID}-{TAG}" + if [ -f "${LAUNCHER_PATH}" ] && ! grep -qs "${SCHEME}=" "${MIMECACHE_PATH}"; then + update-desktop-database "${HOME}/.local/share/applications" + fi + fi +fi + +# Search for server connection +PROTOCOL_PREFIX="openra-{MODID}-{TAG}://" +JOIN_SERVER="" +if [ "${1#${PROTOCOL_PREFIX}}" != "${1}" ]; then + JOIN_SERVER="Launch.Connect=${1#${PROTOCOL_PREFIX}}" +fi + +# Run the game +export SDL_VIDEO_X11_WMCLASS="openra-{MODID}" +mono --debug OpenRA.Game.exe Game.Mod={MODID} Engine.LaunchPath="${LAUNCHER}" "${JOIN_SERVER}" "$@" + +# Show a crash dialog if something went wrong +if [ $? != 0 ] && [ $? != 1 ]; then + if command -v zenity > /dev/null; then + zenity --no-wrap --question --title "{MODNAME}" --text "{MODNAME} has encountered a fatal error.\nLog Files are available in ~/.openra." --ok-label "Quit" --cancel-label "View FAQ" || xdg-open "{MODFAQURL}" + else + printf "{MODNAME} has encountered a fatal error.\n -> Log Files are available in ~/.openra\n -> FAQ is available at {MODFAQURL}\n" + fi + exit 1 +fi diff --git a/packaging/linux/mod_128x128.png b/packaging/linux/mod_128x128.png new file mode 100644 index 0000000000000000000000000000000000000000..ed1b45814844596ec65165f9bf5d84430dad50eb GIT binary patch literal 1852 zcmcJQ`#;kQ1INFc88){~NNzn(u5nzFTytL@m*lu4O>N>(xnvP=t$~nm8#Sn5# zb4!KfGUjp)wsc%_+h)q7n1n5Bc|3o_dA&ZL&-=H}=f_X3uaCPN>=+CH06CBUxcMED z_ z{0%{Mg;BiUH`l3hm~ZMXS}G3FlpI3Qwhz6_5ESZjc=C(j%u#IY+?E) zIV}5QJ0KltIqo^?zBO0E2rIUjWbq&o)V!|4@|_ z1=ZiswrR-jbM(rU$68L-TmCDPVK7%?j(Qm_jGbvStC&Tn-bPRAYpWB zE)}{^eErP8NaE^f)<;IfUtp|CEHPL{Gh{3%MR?mJ6hYm)c6JweQ%X9!@Mw%OqiHr+ zXdZ9)xhB&&xr?KD@ETm@tkieq{Z#zvAzO(!*bDxU;8iTyurd!(1TlML$+i>OBeM3- zwc+GE0N3|cVHTz|)GofBLk}(Oe5peW?X~U}XiyF2nqNj`|BJg&0MzrM39lEy@w)SY zf}NesFFggo9Yz`RwCRx37r26rY9BOa@fVN?%0M# zpPoduurmp~%562Rf*xIvtlCocL`^vFMH}Wh3S8>Bbn5$xC;7FG>U29h#XpoQG6H#v zrDu|q3mp}qeD#MpreY-}3LLAdvtgP4USuyAvCdNs^tb{fok0JZgkv4FEXSW3=NaQ7 z1Lj5;^~S^}*6C5YO!k(I`tUQ^h$a1S@d1Bo*O3AvUT_gnIXzK284guPl83Fs>oAd+ zdIMtF=shNkHM?xJzj&7)LVLQVJ|uP`fjc$-{Ii>-u&%#cm=wgfLys!El1(Wx9NF-! z`&Xd3cf`EJANUjT_{`C1EA$A?$BO%$RZ##4&$It%jV=gT=(R}2SBy_Pq6Iei)d5?y zAEIA2oXFh$rEq25#G<25irce-_=*z7efKF2fW#TCbt5aAo^Efj%yUGw?w`wT%hEmv zz`$E+td?~c%gY|pUq=7?l|g1NykC%uxyl?d%zqre7tNjjanvDxSWRcaYRd91*x<4j z!!nK+E){ztRbu!9#A|Ngk-h3MGRswjJgU7(z1)est{I(y!2UE}qVDkE3_21hDOhMg z|C~W$dRE5d*!8(Y2bJ7ycy9~4&zx>%#7yr70==JpT!ccIY_(^Q{tsdICGycWA=;9h z?(L++Uv3U*&Q{MMX%WJ46ZyZrqqHTrvfRNARC7=aytU}|z8=8#->UH|`pfPslJ4J= zsMkrwueF}t*8$k&W~92@+b5uH5XEi1=Woq-Q9$a-n$>>+LL>6YO|tN3ad3WGhZCMBLM^T`g6XYM# cK8oY$aqe|X+VkVahj$2gpnTk#U9g$|1G=bJdH?_b literal 0 HcmV?d00001 diff --git a/packaging/linux/mod_16x16.png b/packaging/linux/mod_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..c31b0fc1ede7a516b92e25aff207321f28ed7d42 GIT binary patch literal 265 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|4tu&dhFAzD zCrEs15Io6lCltrX$Bsvq&cNyRwQ0`0)A0 zFrIkM(rMVp!SujBpiWqT-SKKG^UK}gTe~DWM4fI9OZ6 literal 0 HcmV?d00001 diff --git a/packaging/linux/mod_256x256.png b/packaging/linux/mod_256x256.png new file mode 100644 index 0000000000000000000000000000000000000000..e05a6f8b7142ecc0e249760d5cf5ee9fef7d0052 GIT binary patch literal 4487 zcmeHLc|6qJ_y5c^7%6Eg9^2T)QbI+MbtYsf(!$sVk9`@+Hg=7MC}BbrF_R2h?Aya+ zDA^er`<69~UDm9>d47NY{`*wxS_Z|pj=e)~t{@$G7o*gZAFDvKWZN=brC42T?2QpPgMp-hqDXC|xF4&(~eq|k$ zq<16i@f5stX5?tNNM^0U(=Lf%}EYyY61@d~Zr^rd9s&SG-CyP3PXoTj~ut z%k}O7Vm0gekiIqm7M4bW=K?t<;1GA1D5xhp_8LUh7!T=MHc?Nu*P`|eT zCwWRZGK44301q)qk{{xkQvtz{O={3>QIjMVQwmci*5wUG;Nt^$mQiFW7SpKORhv8o z(FcTAL`MinBNj8w@5fVQ6Q>{33wi3nDSb!5Q}4zF5+K_m+k)ap9`k zajiS%=qO<(#QuAbM7wjLgKbmG4;RXMUXq#2o7!fp5VGF0QN9b4=5(6%^3a)7!(A7X zZ5t*QCcDpHa}a-qZ)*J+zt#MjmH{xjG7>gD>09y@DgS3b%Fk>eyty=Z$6FfS_kyQJ0GxX%>1m7K5zpZ^JCL@dd9zGgiRyg zWse&29U3C3(BG$^+`GXX* zW-he$WFB&d2-y1{(tMPKnuk!Hw2{jAgPJT@mf|F<+HbQj4&fJ^m`HlYVWHp8#pQQ}G{;BHSG_v3`B`sMUJUd=&$=8mtVKhm zsLgc;<81bgOlyQOBkL-9(+lvM(ka)aIYrM{M}L${R=`QzQhn{4n`)TLg>DSWWX6R5 za_H5KgP5Fq=rAc_cB_gI{?AWIhWmOTezu&u_QAzZEi&CaqM+Nyq{cgETW9~*dRNNi z_LrgJhXGnaLhRN=QPy;hs!nD(q4F@94siN4uNv%V+k1eEeUI4RB+qHB9jZs}Fsrwm=z)9719>5#5Uc$j9U|Au$z#Q;&t zW@a+ogOuujuO$@S!#)L4d_@hlE&qn46?1fNsUC6-Bd57b|ynjOkTa}y7Y zo%wo`&we_>3@$k3ELa#52jq>Pj1lP##+GL!ri$5!>BV~{E5vw(^D`vAS*{F+u+)t& zTYH7C`UL2QMv+3CWGvoK12k|0>+!G}ykyvI})S;41h80XuPxHq~&73(c(RCHG%PR0omH4@} zj|-bTiAj{rda$h&?Sd8H%dKjQ##TxUK%#*sBoz0T)fY^+~kun`kZyuk1>Nw zR}v6*ghy(FG7Y$pLSj&vhX6-B!xcR{@fQ*ul5-S37{a!dI#{n|7>mJ{nO|#l-YHId z*vVi=_Z(GdDm-GO1@cz_t$l_+Dgbt_eHW#P@@tw-qN*LUUb~|nwMT`FG&Li&{kJGq zGQGx=B?YL#S0&z{VMU*~b1w+aH&LD7O9Q4cg>OHe4sT;+ITH77V> z{VwxjGNl3M|5$3hOF8aT&g9G7>+C#L+q+X#462gUZj*A?a?xIbI{C<0eB-*&%BxU? zH~X0vNLJ#=(GQ$OY$g>?pMPPyWG;>+2j^4nSc~oQhFRLLb=8=vrr61xV?DN3UWO(D z7#6Rwp1EwkG?SUoyzIj$rOMq>&`8m#e2(E7&9(2ewHnIBH^wwgO)hueNwSW zzN`&_?;1t-2C2axQLrNsZxw4XN@cftU3o}}0H>Yd#r=Pt;AgefoefXup}q*SKnM4a zp1Xg*bq~If4ki4p!|nTSrDJ0Z7z*sl8Cv9ghgd5~2(7L@A}+IkHQ#8(z>tO2rIwd) ze|E2yFxh?RD-~l8(IUpy#RS>2P#y8rE7z$}rS%o2-sPpv(CaW{jMul8a}aEh2HKee zq7j>afL=eV;}Hs81i$FSGTeo7@xIsA^gcKYCG&C;0n11cGIJ;ZIy4xi0e&=>AB({M z#Cdw#v5e?3^(>N7+e@uIvDS;8qM&#XWd1X!e=og_bbh!jVrOaK+u)T(-D5@3>2_Rp zuc2dcbw5fH%Y%(E1?OZ6LhN;rjPi(SMigjS6JPmWi)U zOea>i^=$2PuX}sPg$eV$h*3FHbZ}VeXr;0cFHwn9BuOaR%ADk}<06BaQE4jH;VGYM zG#20jkrsi<2EYWhOA?=vZQnV$F7#c}a;7a_{-wW3BnHooYlToZ?lpK7{xq~*cokc3 zGw#O0b1U|bz2@3Y^1Z~;9v3b+={T(7x5lmyoUthkb%a zuhsQA9<`KVRFq`2x^$kIS#FafWJrgs0_5=adzocXJ&{xjK`vmImzkZufKy$=NiRuL z)&=+}U{kt?tW4xuOXMNXV7wP9Iz60UG8RP3Z3piUJos5hLuzM{_<2@ zIKeWYSjQZ!HNLOT6qSEN4~m%ej$b16l+xVfgkVUIIYG}r+j6rj?&Yn4(h?`kVhf zcqe?I_>*?~3bC=&Sn~j->Y=iuR18Dl!vhYK&)KNP+{#Jt*4aF#S4*3p>;G=>64)L7 z6(UnKSGH`%_x4E)*PUMR2^m-LW#O7n^#J4-)%8DPrW+_dLJ09pU8VwD}}n8EI* z$1Sx+vO5DI1}wy@9`Idq-e1b9k z>ngfu4zJ;}tEBXSj$*GYO2Eg{Rvx(om4NCGSLcQPgk&UjmRFijJJ%lPBx@ZogW_wL zVeVA3iPIKdC58*Vs?I-1l9JCNA;dbUx|`lyVKU{ISin&H$66{I?@GY$u}PZsOi*7I zn^1?60oe7a?~)lUo85Ss^$Mh5)5iJ>$Ln*u(^2u>A|so2=^F`@W#Orq5URr-xv9k@Om zV)lM&9AN8$>GQ|dsdsqp6~XzK1EAt#j(3`~&W7YngkMoxwP1nS`-YMY%u`C5L$5UG zMO5q?-SG#5X2uBaBsxqwU&`q)uaP$1L&jkJvXabP`Ej7oYack$e_h3a7LlXK+KuiC zadAF1XmIi8p!-@c2N$+w)|0{f?snXaJ9)dj}Lg>R8c}(&OT@S6a$cqTE0qA`sMMXuNAv49(>WU ztXLVTw3lVbI#NQ}H@8;=$#*BkSneS!M1v6cCCd6g*dn`>Q28W!sD*9Y&Et!33(ohb z(MGT6WyVq+iC0~%>Gqmf(Fn@9KuL3&>;S{P?>uH~iAkHl~5P|$smt(6n5Ax^W;SU)R(fv&Mm!4117{{v74jPU>f literal 0 HcmV?d00001 diff --git a/packaging/linux/mod_32x32.png b/packaging/linux/mod_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..155dafd27917b353eee46282493406534787b4e9 GIT binary patch literal 542 zcmV+(0^$9MP)Px$*hxe|R9Fe^l}#&!K@`W4*9egmb~YAlC>Bal>||#p;RCQ^N7>of_yXQmlqf5< zBqCN;bi2wzSs)AV{Qfe`z2?q+E;lyMsb6#EoHJ+6Ju~y%jEqpi4ul=}=MH2#_HV;h z>9EI}q8Rq#jp>+gkmNN03+dP_+mmDq-?qnp=-s z+Yzs)>lN5}E0_#fhCcL!`y_N&2~ts&Zyu^a&+-I1U=#w&Z;oJ$UDSGQTmnsS2ali+ zQ6Twg0nKE;hX-h}b{aufQD;zY*%PX!fO8`DJuF)p%Fv^rjfO3|RK2uSkWZuLUNHq| zfe*cRi4y9(6}$d1XNZwfW)Vb%7 literal 0 HcmV?d00001 diff --git a/packaging/linux/mod_48x48.png b/packaging/linux/mod_48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..e5cc191c7c34a5532a0cb7f82e1f718a515c1a85 GIT binary patch literal 1672 zcmY*Z4Ls9n9RCxSP32{nmrL6$Q|v`Ku{JEqT+PcGGjbu)Vxw)?$*`GEUW!y7N^?|1 zNtc&!B{EYfp_e;S7iFE0V^>+C+<(<|chBeZynUbV@B4jz&+~kq9DiT$#!gL%m0xph!A4A ze4EcA|LAx{^LO#s2?BOJ7opLOh>TAZkWna&(bw{Mp91#of1SAeFKK}Z;x#LHM;rnF zRT~T?X;7jcj~xvLn|*~~zbo8If~wUHJT*{m#S3nb3;7NQ{sQDWHWvBU0xt>Njc+=F>6YPDfd z|EhjxthZf9uALFmzsgP$WbEiz>2zb+dg7U=N9x!6)$=FkpSC8aPjBeS?$|gZQa|DM zxvFLSKGC##a-3a#&SSgytTs)z1iDaQe`&4J{N7q|<^9r1OWc95wRPMDd~w-r@*QW7 zB&NB^H&WI!EiD#iQ%o&kO{+~0ME1O0PPS>jyJm{&v9+V|Rf?wq-5#k7zvJ9uBz499gcB;twqr6%2J%jtF7XZHVnQ10rVV|Qo=o{j_L)NV79N$B zCR*$?`fcg&_Ll9&t=5WKWYIN;?osV5Yk05T%I<56KRH3ZO|@p#Q8p->`>od1yAPdm zcTtTGRgIzJL^nz)9Um{W?{U~Vl6UL)-Vt<`OZV|b|D+WYaFv@;bA9G;_2qL^^(A5U zU}QQrLKRTtQG3STC3gewSL>o)1IbbILvb>l`E;xCREu$!rlwn4I22va&9jh~FmtVA zMm)(vh&gPqKi=T{{;h*HhKQfXrnGC{HjRwr=cM~)T!eUHagVPG&vnw%F4+u=ABQA$ zP2_iWtft&l&czxJ4Gce@&%9q#D0>Ufe3BGVVqIh-&NQ{+r*LZvx;8?Fa?xo8QQitq zZhKsiaERq#tCe306OtVkDpc50zWQYJwLE#BM3lpUX{m^5j23j9*`PtWL9hN}o$4O% zCb~)i@bm*J`$CTVX&v2Fx3pQ&V7vo9m|0mDl=(E4yx)> zC<#_mW_>kg6GG^TA^8KJF08W5n30)y!#8-Q<@y{wpQ_AVT>WvpoQF6vY_hoR@YNS` zC-Sk?hQnfIq3w>1*<+QXBNoXzR?PQ;#*b%&?H;Yx4{Hw8>+}ZIV3@KC_KVU+j1DFN z72Hq-du6&9ThU1tr#^q=K$>#FEQ2|VTd1K@KV4^e;eg@~Jy#+lHu&8S2m%iKMm!Uh zH#z>x1=Zc_qp+2NIovDLHluRnGs#SjM{ApBuJaA>$!D#=;SLR&>q4!CKZMgn$-#E-pPNkh s-^0zaZnoWHukB})fBqQM$+{nx;LbX3FJYV=)cp58n|-}5ZVH$D4Z)Pf7ytkO literal 0 HcmV?d00001 diff --git a/packaging/linux/mod_64x64.png b/packaging/linux/mod_64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..d780c5aeb914dd5d76ab8d1dda11600e2f190a43 GIT binary patch literal 952 zcmV;p14sOcP)Px&a!Eu%RCodHSWBo*Q53$OdBwn$N0b;SCQNlBHxijBB%%zMDq=vnC1M~Yuc(_L zDT6y29gaF=I}@6Z=HEU^Z@18R-k4)bGO%LHHyB6z40cn*DH zY-Fv*Sx`TMC4g6<=Q-`RuB5aw0pQjPzzwiW z2l@?V^Hy~QNThw_b2%d!55Fg1xd)RUC+5$D{6YCJ@e#r&Rjl(tsDa2gfWL_h=0iT& z)LFe3!SX$S2Yoa64>%ZARRK`~{4YXQ0p+dWBg)RCSAdJkLVrCsWcPDA%PSl&mYD$b(Q)%o8y146G|5(r;lb_j@llsdEpIaejrue!!EV zCN{xMV6XRnr~tsEc_Ms4LD6aG{InKEb$A7s+!=6G^78i!?DObQ6x9KMbx2Motz|Y= z-4ui2Q8fT_kjZBS0MGqw#uA7V&;sd*dzGEn;I-i3R3!k%k&R5A ze{L*pX2?59pBn!JeKt5KRRzFmWWlE@0bneWra*ZGmOl5PvvPt_6#&Fta;_nxxX<yChVgH#p(Are@Ls97-*gad<_cnW0zz~;uQtPK1;^cfy8A64rRknW^)_^xCTkC