CHANGELOG0000644000000000000000000024603213120060140010760 0ustar rootroot2.30.0 * Deprecation notices: * Game scripts targeting a compatibility level ≥ 2.30 should not set dependencies using "PKG_xxx_DEPS". * Support for the legacy archive naming convention "ARCHIVE_xxx" is dropped. All game scripts must now use the naming convention introduced with ./play.it 2.13 for the main archives: "ARCHIVE_BASE_xxx". * Support for the following obsolete compatibility wrappers is dropped: - context_archive - icons_get_from_workdir - icons_move_to - launcher_desktop_exec - launcher_write * Support for the following obsolete variable is dropped: - PKG_xxx_PROVIDE * Support for setting distribution-specific dependencies using the following variables is dropped: - PKG_xxx_DEPS_ARCH - PKG_xxx_DEPS_DEB - PKG_xxx_DEPS_GENTOO The "PKG_DEPENDENCIES_xxx" variables should be used instead. * Increased verbosity: * Each of these functions now displays a message when it starts its actions: - content_inclusion_icons - content_inclusion_default / content_inclusion - launchers_generation - packages_generation * Performance improvements: * A default package is set early, to be used by the context system when no current package is explicitly set. * Improvements to files inclusion: * The functions called from "content_inclusion" have been reworked to reduce the number of calls to external commands (find, cp, rm). A slight improvement of the time spent on files inclusion can be noticed. * Support for the following legacy variables is restored: - ARCHIVE_GAME_xxx_FILES - ARCHIVE_DOC_xxx_FILES Setting any of these in a game script targeting a compatibility level ≥ 2.18 now triggers a deprecation warning, instead of being silently ignored (that could cause empty packages to be generated). * Improvements to launchers generation: * All checks that should be run prior to a launcher generation are now run early, before taking any real action, and only once. * New dependency system: sibling packages: * A "sibling" package is a package that is built from the current game script, in contrast to packages available from the distribution repositories. This old style dependencies list: PKG_BIN_DEPS="$PKG_L10N_ID $PKG_DATA_ID" should be converted to: PKG_BIN_DEPENDENCIES_SIBLINGS=' PKG_L10N PKG_DATA' 2.29.2 * Ensure that the commands required for icons extraction are available. 2.29.1 * Throw an error when trying to include an icon for an application with no icon set. * Prevent packages_generation from running twice, when called with no argument. * Unity3D games: Improve the automatic detection of the game binary. * Mono games: Prevent a failure on unknown Mono libraries set as dependencies. * Mono games: Add support for setting dependencies on extra Mono libraries: - System.Configuration.Install.dll 2.29.0 * Deprecation notices: * The obsolete function package_architecture_string has been dropped. * Support for the legacy PKG_xxx_PATH variables is dropped. Game scripts should rely on the package_path function instead. * The special behaviour of APP_xxx_PRERUN / APP_xxx_POSTRUN for DOSBox is dropped. The actions listed in these variables are now always executed before/after calling DOSBox. * The following variables are no longer exported: - PATH_BIN - PATH_DESK - PATH_DOC - PATH_GAME - PATH_ICON_BASE The following functions should be used instead: - path_binaries - path_xdg_desktop - path_documentation - path_game_data - path_icons * The following compatibility wrappers are no longer in use by game scripts, and have been removed: - archive_get_type - context_archive_suffix - context_package - packages_get_list * Codebase improvements: * APPLICATIONS_LIST can now be omitted if APP_xxx_TYPE is set. * hacks_inclusion_default no longer relies on the package context. It loops over the full list of packages, then build and include the hacks for all relevant packages. * launcher_write_desktop is updated to no longer rely on the package context. The launcher_write_desktop function now expects two arguments: USAGE: launcher_write_desktop $package $application * Some tar decompression options are explicitly set based on the archive type. This prevents tar from failing to extract the content of archives using a non-standard extension. The following archive types are included: - "tar.bz2" → add "--bunzip2" - "tar.gz" → add "--gzip" - "tar.xz" → add "--xz" * A new function is provided to clean-up snippets: snippet_clean. The following operation is done on the function input: - convert the series of 4 spaces to tabulations. * A new function is provided to clean-up lists: list_clean. The following operations are done on the function input: - remove leading and trailing spaces (including tabs); - sort the list and merge duplicate entries; - remove empty lines. * Optional icons archives: * An optional archive providing icons for the current game can be included in a game script by setting the following variables: - ARCHIVE_OPTIONAL_ICONS_NAME - ARCHIVE_OPTIONAL_ICONS_MD5 - ARCHIVE_OPTIONAL_ICONS_URL - CONTENT_ICONS_PATH - CONTENT_ICONS_FILES The inclusion is done from the archive, if provided, when the icons inclusion function is called: content_inclusion_icons. If the archive is not provided, the inclusion is done from the icon shipped in the game installer (if such an icon is available). * Better integration of shipped fonts: * A new function is provided to print the install path for TrueType fonts: path_fonts_ttf. * A list of TTF font files to include can be set using CONTENT_FONTS_xxx_PATH + CONTENT_FONTS_xxx_FILES. * Improved Ren'Py support: * Game scripts can rely on system-provided Ren'Py by setting: APP_xxx_TYPE='renpy' Such game scripts do not rely on APP_xxx_EXE. The game content should be available at the root of the game install path. Shipped binaries and libraries should all be excluded from the package. * Improved support for custom launchers: * Custom launchers can be generated by setting: APP_xxx_TYPE='custom' and a "custom_launcher" function. This function should output the full content of the launcher script that is to be included in the package. * More supported dependencies: * Support is added for "audioconvert" GStreamer decoder. * Changes specific to DOSBox games: * The detection of DOS binaries is improved to include .com files in addition to .exe ones. * Changes specific to WINE games: * Game scripts can require the installation of Mono in the WINE prefix by setting: WINE_WINEPREFIX_TWEAKS='mono' When Mono is to be installed, the "wine-mono-8.0.0-x86.msi" archive downloadable from the following URL is required: https://dl.winehq.org/wine/wine-mono/8.0.0/ * Changes specific to Debian: * The generation of the following metadata files has been reworked: - DEBIAN/control - DEBIAN/postinst - DEBIAN/prerm * The fields in DEBIAN/control are now all filled using dedicated functions. * Changes specific to Arch Linux: * The generation of the following metadata files has been reworked: - .PKGINFO - .INSTALL * The fields in .PKGINFO are now all filled using dedicated functions. * Changes specific to Gentoo: * The generation of the ebuild has been reworked, and most of it is now filled using dedicated functions. This change has been done for both the "gentoo" variant (binary packages) and the "egentoo" variant (source packages). 2.28.1 * Check the validity of game scripts compatibility level declaration. * Fix typos in several messages. * Display an explicit error when no .ico file is extracted from a given .exe. * Fix icons inclusion using legacy functions. The following functions should no longer trigger an error: - icons_get_from_package - icons_get_from_workdir 2.28.0 * Deprecation notices: * The function icons_inclusion is deprecated, content_inclusion_icons should be used instead. cf. "Icons system" below. * The functions launchers_write and launcher_write are deprecated, launchers_generation should be used instead. cf. "Launchers system" below. * The function launcher_desktop_exec is deprecated, desktop_field_exec should be used instead. cf. "Launchers system" below. * Support for the following deprecated archive types is dropped: - mojosetup_unzip - zip_unclean * Support for the following deprecated functions is dropped: - archive_find_path - get_context_specific_value - launcher_native_libraries_paths - launcher_write_script_headers - package_get_path - packages_get_version * Support for the following deprecated variables is dropped: - APP_xxx_ICON_ID - APP_xxx_LIBS - OPTION_xxx * Declaring dependencies on the following native libraries is no longer supported: - libavcodec.so.58 - libavformat.so.58 - libavutil.so.56 * Codebase improvements: * A package-specific contextual value can now be set for GAME_ID. * The environment language detection now honours LC_ALL and LC_MESSAGES, in addition to LANG. * Icons system: * A new function is provided to include game icons: content_inclusion_icons USAGE: content_inclusion_icons [$package [$application…]] * Launchers system: * The game execution command-line can now be overridden from game scripts by redefining the function game_exec_line. * New functions are provided to easily fetch or override the value of fields in XDG desktop files: - desktop_field_exec - desktop_field_icon * A new function is provided to generate launchers: launchers_generation USAGE: launchers_generation [$package [$application…]] * Dependencies system: * Support for the following native library is updated to rely on a downloadable archive: - libgconf-2.so.4 * Changes specific to game engines: * Unity3D: A more targeted list of libraries is included for Windows builds. * ScummVM: Hyphen-minus is now allowed in ScummVM ids, like in the following valid example: "ags:gobliiins5-1". * WINE: A virtual desktop can be set from game scripts, using the following variable: WINE_VIRTUAL_DESKTOP. WINE_VIRTUAL_DESKTOP can take the following values: - none (default if no value is set) - auto (use the current screen resolution when the game is launched for the first time) - some specific resolution (example: 1280x1024) * Changes specific to Debian: * dpkg-deb ≥ 1.19.0 is required. * fakeroot is no longer required. * Changes specific to Gentoo: * Several use flags are added to scummvm. 2.27.4 * An explicit error message is shown if the compilation of a preload shim failed. * Support for dependencies on several native libraries is added: - libboost_locale.so.1.74.0 - libdbus-glib-1.so.2 - libnotify.so.4 - libtheoraenc.so.1 2.27.3 * Prevent the current umask value to mess with permissions on packaged paths. * Ensure the package context is always set when fetching the path to install libraries into. * Debian - Drop the chmod calls made obsolete by the explicit umask setting. * Gentoo - Drop an obsolete check that would always fail. * Gentoo - Prevent a call to egentoo_package_name when using the gentoo variant. * Gentoo - Fix copying of symbolic links during installation. 2.27.2 * application_type - Ensure errors always stop the execution. * error_archive_not_found - Improve the message shown when a required archive is missing. * gentoo_package_build_single - Display an explicit error if the `ebuild (…) manifest` call failed. * package_archlinux_create_mtree - Fix the message severity level. * path_libraries - Ensure the value is always coherent with the package architecture. * temporary_directory_checks - Skip all checks if no file operation is going to take place. * Add support for dependencies on several commands: - corsix-th - sed - setxkbmap * Add support for dependencies on several native libraries: - libEGL.so.1 - libfribidi.so.0 - libminizip.so.1 - libtcmalloc_minimal.so.4 - libwayland-client.so.0 2.27.1 * Ensure that a missing required extra archive stops the script execution. * error_icon_path_empty - Fix showing the error message when LANG is not set to "fr_*" or "en_*". * launcher_target_presence_check - Ensure that the script execution stops if the binary path is not set. * unity3d_icon_path - Throw an error if no application type is found. * unity3d_application_exe_default - Throw an error if no binary could be found. 2.27.0 * Deprecation notices: * Support for the following deprecated functions is dropped: - context_specific_value - icons_linking_postinst - organize_data - use_archive_specific_value * Support for the following message functions is dropped: - print_warning - print_error See "New wrapper for messages display" for more details. * archive_get_type is deprecated, archive_type should be used instead. See "Changes related to archives" for more details. * The legacy global variables ARCHIVE and PKG should no longer be used. See "Improvements of the context system" for more details. * The functions context_archive and context_package are deprecated. See "Improvements of the context system" for more details. * The variable APP_xxx_TYPE_VARIANT is no longer supported, GAME_ENGINE should be used instead. It can usually be omitted, like with APP_xxx_TYPE_VARIANT. * The packages_get_list function is deprecated, packages_list should be used instead. * New wrapper for messages display: * A new function is provided to display all messages: print_message It expects a priority level as its first argument: - `print_message 'error' $message` replaces `print_error ; printf $message` - `print_message 'warning' $message` replaces `print_warning ; printf $message` - `print_message 'info' $message` replaces `printf $message` * Changes related to archives: * The presence of an optional archive can be checked using a dedicated function: archive_is_available * The type of an archive is retrieved using a new function: archive_type Unlike the previous function (archive_get_type), this will not trigger an error if no type is set for the given archive. * Improvements related to icons extraction: * A new function is provided to get the full path to an icon file: icon_full_path * Most functions related to icons extraction now take an icon identifier instead of the path to an icon file. * Reliance on the global variable WRESTOOL_OPTIONS is dropped. * Improvements of the context system: * A new function is provided to set the current archive: set_current_archive * A new function is provided to set the current package: set_current_package * The functions used to get the current context have been renamed: - context_archive → current_archive - context_package → current_package * Support for Visionaire engine: * A game script can rely on Visionaire engine support by setting GAME_ENGINE='visionaire' or by setting a value to VISIONAIRE_NAME. Since the engine value falls back to "visionaire" when VISIONAIRE_NAME is set, GAME_ENGINE can usually be omitted. * Default values are set for multiple variables: - APPLICATIONS_LIST - APP_xxx_EXE - CONTENT_LIBS_BIN_PATH - CONTENT_LIBS_BIN_FILES - CONTENT_GAME_BIN_FILES - CONTENT_GAME_DATA_FILES - CONTENT_DOC_DATA_PATH - CONTENT_DOC_DATA_FILES - PACKAGES_LIST - PKG_DATA_ID - PKG_DATA_DESCRIPTION - PKG_BIN_DEPS - PKG_BIN_DEPENDENCIES_LIBRARIES * For native Linux games, the used of system SDL is forced. * For WINE games, SDL_VIDEODRIVER is prevented from taking the value "wayland". * Improvements specific to WINE games: * A new compatibility link is added in the WINE user directory: "Local Settings/Application Data" → "AppData/Local". * Improvements specific to Unity3D games: * For native Linux games, the use of system SDL is forced. 2.26.3 * archive_path - Ensure that the output is always empty when the archive is not set. * icon_application - Print an explicit error if no application identifier could be found for the given icon. * launcher_target_presence_check - Do not display an error when no application type is set. * launcher_write_script - Check for the binary presence early. * Arch Linux: Fix .INSTALL metadata file generation when post-installation messages are set. 2.26.2 * archive_dependencies_check - Ensure that a failure to get the archive type triggers a fatal error. * archive_name - Add ability to compute the archive name from ARCHIVE_xxx_PATH. * archive_path - Ensure that the output is always empty when the archive is not set. * archives_integrity_check_md5 - Prevent an unexpected hashsum mismatch error for archives with no expected MD5 hash set. * icon_application - Prevent a mix up between applications sharing a similar prefix. * Arch Linux - Fix broken .PKGINFO "conflict" / "provides" fields. 2.26.1 * Fix archive integrity check when using extra archives providing native libraries. * Always use an absolute path for the archive path. * Store a cached value of the archive path the first time it is computed. * Ensure that archive content extraction using unzip overwrites files. * Debian - Ensure that dpkg-deb follows the custom TMPDIR value. 2.26.0 * Deprecation notices: * ./play.it can no longer be run by the root account, unless PLAYIT_OPTION_RUN_AS_ROOT=1 is set. * ${PLAYIT_WORKDIR}/gamedata is deleted automatically at the end of the content_inclusion_default execution for game scripts targeting a compatibility level ≥ 2.26, so a manual deletion attempt after that would fail. * target_version should no longer be set for game scripts targeting ./play.it ≥ 2.26. See "New way to set the compatibility level from a game script". * The legacy variable SOURCE_ARCHIVE should no longer be used from game scripts. See "Improvements to archives content extraction". * The legacy variable ARCHIVE_xxx should no longer be used to set neither the archive name nor the archive path. See "New archives properties". * New way to set the compatibility level from a game script: * Setting the compatibility level should be done using a new variable: PLAYIT_COMPATIBILITY_LEVEL. * Setting a compatibility level is no longer required. It is still very strongly recommended. Game scripts with no compatibility level set will break. * New functions are provided to work with the compatibility level: - compatibility_level: Get the compatibility level that has been requested. - compatibility_level_is_at_least: Check the compatibility level against a given version. * Improvements to archives content extraction: * A new function is provided to extract content from the current archive: archive_extraction_default. Unlike archive_extraction, it does not take any argument and will automatically pick the current archive (the one passed on the command line or found in the current directory). * New archives properties: * A new variable is exposed to set an archive name: ARCHIVE_xxx_NAME. * A new variable is exposed to set an archive path: ARCHIVE_xxx_PATH. * The presence of archive extra parts is checked automatically up to ARCHIVE_xxx_PART99. * Support for obsolete native libraries provided through downloadable archives: * Support for the following native libraries is added: - libcurl.so.4+CURL_OPENSSL_3 (libcurl.so.3 and libcurl.so.4 including the CURL_OPENSSL_3 symbol) - libFLAC.so.8 - libidn.so.11 - libpng12.so.0 - libssl.so.1.0.0 - libssl.so.1.1 * Adding a dependency on one of these libraries is done by including it in PKG_xxx_DEPENDENCIES_LIBRARIES, like it is done for system-provided packages. * The extra archive is required only when building packages for a system that does not provided the expected library from its repositories. * To be able to use these dependencies, the new function "archive_extraction_default" must be used. See "Improvements to archives content extraction". * Provide the ability to use a fake $HOME path, to prevent cluttering of the real $HOME: * The fake $HOME path is automatically enabled if FAKE_HOME_PERSISTENT_DIRECTORIES is set by the game script. Paths listed in this variable are diverted to persistent storage. The default fake $HOME path is ${XDG_CACHE_HOME}/play.it/home/${GAME_ID}, this path can be overridden at runtime by exporting the variable PLAYIT_FAKE_HOME_PATH. * XDG basedir paths from the fake $HOME are automatically diverted to the same paths in the real $HOME. * Support for LD_PRELOAD hacks: * A list of hacks can be provided by PRELOAD_HACKS_LIST, one per line. * Each hack must set the following properties: - HACK_xxx_NAME - HACK_xxx_DESCRIPTION - HACK_xxx_PACKAGE (can be omitted, probably not a good idea) - HACK_xxx_SOURCE * The following new function should be called from the game script to build and include the hacks: hacks_inclusion_default. * Improvements to the handling of packages metadata: * The "package_description" function now only returns the value of PKG_xxx_DESCRIPTION, without extra formatting. In addition, it triggers an error if this description includes line breaks. * Expansion of the context system: * The following variables got support for contextual values: - APPLICATIONS_PREFIX_TYPE - APP_xxx_PREFIX_TYPE - APP_xxx_PRERUN - APP_xxx_POSTRUN - USER_PERSISTENT_FILES - USER_PERSISTENT_DIRECTORIES * Rework packages post-installation and pre-removal actions: * A new variable can be set to a list of warnings that are displayed post-installation, one per line: PKG_xxx_POSTINST_WARNINGS. 2.25.6 * content_inclusion_chunk_single - Fix behaviour when PACKAGES_LIST has a contextual value set. * error_package_does_not_exist - Fix French translation. * Gentoo - Fix the definition of the postinst/prerm package scripts. 2.25.5 * Gentoo - Fix the function call used to fill the DESCRIPTION ebuild field. * Gentoo - Fix the function call used to fill the RDEPEND ebuild field. * Gentoo - Drop unwanted "$" at the beginning of the version string. * Gentoo - Fix a typo in the variable setting the compression command. * Gentoo - Fix ebuild settings through environment variables. * Gentoo - Drop broken support for --compression none. * Gentoo - Move the generated packages to the output directory, instead of deleting them. 2.25.4 * Add support for extra native libraries. * Trigger a warning if APP_xxx_LIBS is set from a game script targeting a compatibility level ≥ 2.19. * content_path - Prevent a failure if CONTENT_PATH_DEFAULT is empty. * games_find_script_for_archive - Prevent a broken pipe non-blocking error. 2.25.3 * application_type_guess_from_file - Prevent failure when APP_xxx_EXE is not set. * archive_extraction_makeself - Decrease the time spent on files extraction. * archive_extraction_mojosetup - Decrease the time spent on files extraction. * content_path_default - Throw an explicit error if no path is set. 2.25.2 * persistent_list_directories / persistent_list_files - Reduce reliance on variable_is_empty. 2.25.1 * archive_get_type - Fix archive type detection using file headers. * debian_package_build_single - Fix usage of $dpkg_options variable. 2.25.0 * Deprecation notices: * Support for the legacy compression options is dropped. See "Compression methods rework" in 2.22.0 release notes for more details. * The variable APP_WINETRICKS is deprecated, and will be ignored for game scripts targeting ./play.it ≥ 2.26. See "Improvements to support for WINE games" below for more details. * New command line options: * --list-available-scripts — Print the list of game scripts available on this system. * --list-supported-games — Print the list of supported games. The output of this option can take up to several minutes to be generated, depending on the number of available game scripts. * New make actions: * make shunit2 - Run a series of unit tests based on shUnit2. * make shunit2-coverage - Display a coverage report for shUnit2 tests. * make check - This command is extended to include the shUnit2 tests and their coverage report. * New dependencies system for commands required at runtime: * A new variable is exposed to game scripts: PKG_xxx_DEPENDENCIES_COMMANDS It can be set to a list of commands that are required by the game at runtime, one per line. * Improvements to support for WINE games: * Add ability to set the default value for WINEDLLOVERRIDES, using a new variables: WINE_DLLOVERRIDES_DEFAULT. If this variable is not set, the generic default value is used: WINEDLLOVERRIDES='winemenubuilder.exe,mscoree,mshtml=' * A new variable is exposed for listing required winetricks verbs: WINE_WINETRICKS_VERBS. The old variable name APP_WINETRICKS is still supported for game scripts targeting ./play.it < 2.26. * Improved support for Unity3D games, the following variables can be omitted and will fall back on default values: - APP_MAIN_EXE (including for Windows games) - CONTENT_GAME_BIN_FILES - CONTENT_GAME_BIN32_FILES - CONTENT_GAME_BIN64_FILES - CONTENT_GAME_DATA_FILES * Improved support for Unreal Engine 4, the following variables can be omitted and will fall back on default values: - APP_MAIN_ICON_WRESTOOL_OPTIONS - CONTENT_GAME_BIN_FILES - CONTENT_GAME_DATA_FILES - PKG_BIN_DEPENDENCIES_GSTREAMER_PLUGINS - WINE_DIRECT3D_RENDERER - WINE_PERSISTENT_DIRECTORIES - WINE_WINETRICKS_VERBS 2.24.1 * application_options - Fix test preventing line breaks in application options string. * archive_guess_type_from_name - Ensure that an empty string is returned if no type could be guessed. * content_inclusion - Check that the given package identifier is valid. * content_inclusion_chunks - Drop declaration of unused variable. * Prevent incompatibility between old .deb format 0.939000 and xz compression. * Do not tweak shell options from inside functions. * Add support for extra native libraries. 2.24.0 * Deprecation notices: * The following functions are deprecated: - launcher_native_libraries_paths - launcher_write_script_headers * The following functions are deprecated for game scripts targeting ./play.it ≥ 2.14: - write_metadata - build_pkg See the section "Changes related to packages generation" below for more details. * Support for the following application types is dropped: - renpy - residualvm * Ignoring errors during calls to archive_extraction can no longer be done using `set +o errexit`, because `set -o errexit` is forced after the call to external tools used to handle the archives. The recommended snippet to use instead is: archive_extraction 'SOURCE_ARCHIVE' 2>/dev/null || true * Support for the unused variable PREFIX_PREPARE is dropped. * DOSBox games: The "userdir_toupper_files" function is no longer included in launchers. * Mono games: Support for "APP_xxx_MONO_OPTIONS" is dropped. * Improved support for game expansions: * The expansion id is appended to the package id by default. Explicit PKG_xxx_ID declaration is no longer required for expansions. * Changes related to packages generation: * A new function is provided for packages generation: packages_generation It replaces the two following functions: - write_metadata - build_pkg * Changes related to files extraction from archives: * An extraction log is stored at: ${PLAYIT_WORKDIR}/logs/archive-extraction.log * Minimal permissions are always applied on extracted files. * MojoSetup archives: unzip is used instead of unar to handle the inner archive. * Changes specific to ScummVM games: * Allow omitting APPLICATION_xxx_TYPE for ScummVM applications. * Changes specific to WINE games: * Throw an explicit error if a required registry script could not be loaded. 2.23.6 * Fix error displayed when an unkown archive type is set. * WINE: Fix automatic dependencies addition based on WineD3D renderer. 2.23.5 * MojoSetup archives: Fix the extraction of the inner .zip archive. 2.23.4 * Prevent output redirection from leaking after error messages. * Add support for dependencies on extra native libraries. 2.23.3 * Add support for extra native libraries. * Unity3D: Prevent plugins inclusion failure if no architecture-specific directory is shipped. * Unity3D: Prevent a crash on Linux 6.1 by disabling the MAP_32BIT flag with some Unity3D builds. 2.23.2 * Fix warning displayed on unsupported GStreamer media format. * Fix content inclusion when using legacy ARCHIVE_xxx_FILES variables. * Do not generate an empty list of unsupported GStreamer decoders. * Add support for extra GStreamer decoders. * Add support for extra native libraries. * Avoid querying unset variables. 2.23.1 * Display an explicit error on package building failure. * Fix install path for native libraries. * Add support for more native libraries dependencies. * Arch Linux: Fix filling "conflict" and "provides" in package metadata. * Gentoo: Fix setting the list of dependencies on native libraries. 2.23.0 * Deprecation notices: * Game scripts targeting ./play.it ≥ 2.23 can no longer use the legacy compression values, see "Compression methods rework" in the 2.22.0 section for more details. * Trying to expand an unset variable triggers an error, for game scripts targeting ./play.it ≥ 2.23. * APP_WINE_LINK_DIRS is deprecated, setting it from a game script targeting ./play.it ≥ 2.23 triggers a warning. For games scripts targeting ./play.it ≥ 2.24, it will by ignored silently. See "Improved support for WINE games" below for more details. * PKG_xxx_PROVIDE is deprecated, setting it from a game script targeting ./play.it ≥ 2.23 triggers a warning. For games scripts targeting ./play.it ≥ 2.24, it will by ignored silently. See "Packages metadata improvements" below for more details. * Dependencies handling improvements: * The generation of a launcher for the following application types automatically adds the required dependency to the current package: - dosbox - java - mono - renpy - residualvm - scummvm - wine * Dependencies on media formats decoded by GStreamer can be listed using a new variable: PKG_xxx_DEPENDENCIES_GSTREAMER_PLUGINS. The following formats are supported: - avidemux - decodebin - deinterlace - audio/x-wma, wmaversion=(int)1 - video/quicktime, variant=(string)iso - video/x-ms-asf - video/x-msvideo - video/x-wmv, wmvversion=(int)1 * Application type improvements: * The application type can be guessed from the game binary even if it is not in the current package. * Packages generation improvements: * A package can now provide/conflict with multiple package names, using a new variable PKG_xxx_PROVIDES One provided name should be written on each line, using line breaks as the list separator. * If all packages are already built, the execution stops early to avoid unnecessary resources and time usage. * Debian: Automatically use old .deb format (0.939000) to avoid size limits. * Improved support for Unity3D games: * Shipped plugins for Unity3D games can now be listed using a new dedicated variable: UNITY3D_PLUGINS. If it is set to a non-empty value, it is used during the call to "content_inclusion_default" to include the listed plugins into the path dedicated to shipped libraries. Plugins that are not listed are removed to ensure they are not included later by some "CONTENT_xxx_FILES" greedy pattern. * Improved support for WINE games: * A new variable is used to list the paths that should be diverted from the WINE prefix: WINE_PERSISTENT_DIRECTORIES Its value is a list of paths relative to $WINEPREFIX/drive_c, one per line. * Improved handling of huge files (>9GB): * Files that are bigger than 9GB should be listed using a dedicated variable: HUGE_FILES_xxx "xxx" is the suffix of the package that includes these files by default, so a list for PKG_DATA would be named HUGE_FILES_DATA. * The files split is done only when building .deb packages, as Arch Linux and Gentoo packages have no size limit. * Improved support for Makeself and MojoSetup archives: * The type declaration can be omitted for MojoSetup archives. * Support is added for Makeself archives. 2.22.5 * Use a single function to handle the copy of the game binary into the prefix. * Fix permissions on the manual pages. * Add support for more native libraries dependencies: - libc++.so.1 - libc++abi.so.1 - libpcre.so.3 * Arch Linux: Improve listing of packages providing generic dependencies. * Arch Linux: Prevent querying of unset variables. 2.22.4 * Arch Linux: Fix fetching the maintainer name from /etc/makepkg.conf. * Arch Linux: Fix adding multiple dependencies for a single generic keyword. * Gentoo: Fix support for PKG_xxx_PROVIDE. * Gentoo: Prevent querying of unset variables. 2.22.3 * Ensure function return codes are never hidden behind cat calls. * Ensure package architecture is always fetched using the dedicated function. * Run a late requirements check for icons extraction. * Add support for missing native libraries dependencies: - libutil.so.1 - libXmu.so.6 * Arch Linux: Rework packages metadata generation to no longer rely on a variable leak. * Arch Linux: Fix libvulkan.so.1 dependency. 2.22.2 * Improve automatic detection of DOS executables. * Display a list of unknown Mono libraries if some have been required. * Prevent functions failures to be hidden by while loops. 2.22.1 * Fix inclusion of pre-run/post-run actions in launchers for WINE games. * Improve handling of pre-run/post-run actions in launchers for native games. * Add support for libbz2.so.1 native library dependency. 2.22.0 * Deprecation notices: * Multiple options are dropped, here are the replacements that should be used: - --icons yes (no replacement, this is the default behaviour) - --icons no → --no-icons - --skip-free-space-check → --no-free-space-check - -c → --config-file - -h → --help - -v → --version * Functions used to fetch packages metadata have been renamed: - packages_get_maintainer → package_maintainer - packages_get_version → package_version (a compatibility alias is provided) - package_get_architecture → package_architecture - package_get_architecture_string → package_architecture_string - package_get_description → package_description - package_get_id → package_id * "launcher_write_script_wine_run" is obsolete, "wine_launcher_run" should be used instead. A compatibility wrapper is provided for game scripts targeting ./play.it < 2.22. * Legacy environment variables (usually "OPTION_xxx") are still exported only for game scripts targeting ./play.it ≤ 2.22. See "Options system rework" below for more details. * The legacy values for --compression are still valid only for game scripts targeting ./play.it ≤ 2.22. See "Compression methods rework" below for more details. * "unity3d" is no longer a full blown application type, but a variant of the application type "native". See "New application type variant system" below for more details. * The internal test "variable_is_set" is dropped. "get_value" no longer throws an error when trying to query an unset variable, it returns an empty value instead. * Options system rework: * A new function is provided to fetch the current value of a given option: option_value $option_name As an example, instead of a direct use of the value of the deprecated variable $OPTION_COMPRESSION, the following function call should be used: option_value 'compression' * The game archive is no longer the first mandatory argument when calling the "play.it" command. It can now be provided anywhere on the command line. * All options can be set using environment variables, here is the full list of command line arguments and for each one the equivalent environment variable: - --checksum → PLAYIT_OPTION_CHECKSUM - --compression → PLAYIT_OPTION_COMPRESSION - --config-file → PLAYIT_OPTION_CONFIG_FILE - --debug → PLAYIT_OPTION_DEBUG - --help → PLAYIT_OPTION_HELP - --list-packages → PLAYIT_OPTION_LIST_PACKAGES - --list-requirements → PLAYIT_OPTION_LIST_REQUIREMENTS - --no-free-space-check → PLAYIT_OPTION_FREE_SPACE_CHECK - --no-icons → PLAYIT_OPTION_ICONS - --no-mtree → PLAYIT_OPTION_MTREE - --overwrite → PLAYIT_OPTION_OVERWRITE - --output-dir → PLAYIT_OPTION_OUTPUT_DIR - --package → PLAYIT_OPTION_PACKAGE - --prefix → PLAYIT_OPTION_PREFIX - --show-game-script → PLAYIT_OPTION_SHOW_GAME_SCRIPT - --tmpdir → PLAYIT_OPTION_TMPDIR - --version → PLAYIT_OPTION_VERSION If a same option is set using both an environment variable and a command line argument, the command line argument is ignored. * Compression methods rework: * Compression options are no longer specific to the target package format, the same four values are available: - "none": no compression (default value) - "speed": compression method focusing on compression speed - "size": compression method focusing on size reduction - "auto": use the current defaults for the packages generation tool (not supported when building packages for Arch Linux) The legacy values are still available for game scripts targeting ./play.it ≤ 2.22, but their use triggers a deprecation warning. For game scripts targeting ./play.it > 2.22, an error is thrown. * New application type variant system: * The application type variant can be set from a game script using the variable "APP_xxx_TYPE_VARIANT". There is only one accepted value for now: "unity3d". * Unity3D games for Windows are no longer wrongly identified as native Linux games if they set "UNITY3D_NAME" but not "APP_xxx_TYPE". * A dedicated per-session log file is used for Unity3D games running through WINE. * Changes related to packages: * Environment variables set for Debian tools are followed when trying to get the maintainer name and e-mail. * Changes specific to Debian: * The generation of the DEBIAN/control file is moved to a dedicated function. * The package metadata generation is updated to no longer rely on implicit variable inheritance. * Dead code removals: * Support is dropped for unused compatibility wrappers: - package_get_current * Support is dropped for unused messages: - error_archive_unset - error_context_invalid - error_no_valid_temp_dir_found - error_obsolete_function - print_ok - warning_missing_library * Support is dropped for unused generic dependency keywords: - bzip2 - gconf - libcurl - libcurl-gnutls - sdl1.2 - sdl2_image - sdl2_mixer - theora - vorbis - wine-staging / wine32-staging / wine64-staging - xft 2.21.2 * Display an explicit error when a game script seems to support no archive. * Use dpkg for version strings comparison. * Ensure the pre-run and post-run actions strings always end with a line break. * Add missing native library dependency: libIL.so.1 * Gentoo: Add the opengl USE flag to relevant SDL packages. 2.21.1 * Fix Make rule used to build the library. * Run syntax checks from the Makefile. * Add ability to build release tarballs from Make. * Add support for dependencies on more native libraries: - libaudio.so.2 - libcrypt.so.1 - libFAudio.so.0 - libgomp.so.1 - liblcms2.so.2 - libpixman-1.so.0 - libSDL_kitchensink.so.1 - libSDL_sound-1.0.so.1 - libsigc-2.0.so.0 - libvorbisenc.so.2 - libX11-xcb.so.1 - libxcb.so.1 - libxcb-randr.so.0 * Add support for dependencies on more Mono libraries: - OpenTK.dll - OpenTK.Compatibility.dll - OpenTK.GLControl.dll * Prevent non-fatal errors when listing package dependencies. * Fix the error displayed when no supported archive is found. * Add support for "image/x-xpixmap" MIME type for icons. 2.21.0 * Deprecation notices: * Support is dropped for --icons auto. The only valid values are --icon yes|no, defaulting to --icons yes. * Support for application type "native_no-prefix" is dropped. * For game scripts targeting ./play.it ≥ 2.21, the main archives identifiers must start with "ARCHIVE_BASE". Game scripts targeting an older version can still use the prefix "ARCHIVE" instead. * The following functions are deprecated, but still available through compatibility aliases: - context_specific_value - get_context_specific_value - package_get_current * The following old functions are dropped, with no compatibility alias: - get_context_suffix_archive - get_context_suffix_package - context_specific_name - test_var * Up to ./play.it 2.20, all of these content identifiers were tested when calling "content_inclusion_default" (or "prepare_package_layout" for older game scripts): - LIBS_xxx - LIBS0_xxx - (…) - LIBS9_xxx - GAME_xxx - GAME0_xxx - (…) - GAME9_xxx - DOC_xxx - DOC0_xxx - (…) - DOC9_xxx Starting with ./play.it 2.21, the search stops at the first unset numbered identifier. So if "GAME1_xxx" is not set, "GAME2_xxx" up to "GAME9_xxx" will be skipped. No legacy behaviour is provided for old game scripts. * For WINE games, winecfg launchers are no longer generated. To spawn winecfg using the prefix for a given name, you can run the following command: WINEPREFIX=~/.cache/play.it/wine/${game_id} winecfg * New options: * --list-packages: List the packages that would be built from the given archive. * --list-requirements: List the game script requirements for the given archive. * Improvements of file type detection based on MIME: * Exclude charset information from "file" output. * Add support for "application/x-sharedlib" MIME type. * Improve detection of PE32 executables. * Reworked context system * The following commonly used variables now have context support: - APP_xxx_ID - APP_xxx_NAME - APP_xxx_ICONS_LIST - APP_xxx_SCUMMID * New functions are provided: - context_value: Print the context-sensitive value for the given variable. - context_name: Print the name of the variable containing the context-specific value of the given variable. - context_archive: Print the identifier of the current archive. - context_package: Print the identifier of the current package. * Changes related to launchers: * Provide a new function printing the path to a launcher script: launcher_path. * Changes related to icons: * Rework "icons_list_all" to include implicit icons (like the ones from Unity3D or WINE games). * Changes specific to Mono games: * Dependencies on a list of Mono libraries can be declared with a new dedicated variable: PKG_xxx_DEPENDENCIES_MONO_LIBRARIES. * Changes specific to Gentoo: * Use PKG_xxx_DEPS_GENTOO with no transformation, allowing to pass more complex dependency constraints. * Other changes: * Provide new warnings and errors to be used when deprecated/obsolete functions are called: - warning_deprecated_function - error_obsolete_function * If EXPANSION_NAME is set, it is automatically included in the output of game_name. 2.20.3 * Fix usage of pkg_set_deps_gentoo. 2.20.2 * Display an error when a DOSBox image disk was not found. * Gentoo (egentoo variant): Install data files only if present. * Gentoo: Use correct documentation paths. * Add support for more native libraries. * New generic dependencies: - residualvm - scummvm 2.20.1 * Drop duplicate declaration of message function "error_variable_not_set". * Update documentation with the "--config-file" option. * application_icons_list - Fix support for archive context. * applications_list - When parsing the game script, avoid printing duplicates. * debian_dependencies_full_list - Fix listing when both PKG_xxx_DEPS_DEB and PKG_xxx_DEPENDENCIES_LIBRARIES are set. * Add support for MIME type "application/vnd.microsoft.portable-executable". 2.20.0 * Deprecation warnings: * APP_xxx_PRERUN/APP_xxx_POSTRUN behaviour is changed for DOSBox game scripts targeting ./play.it ≥ 2.20. See "Changes specific to DOSBox" below for more details. * "package_get_path" is now a compatibility wrapper around "package_path". See "Changes related to packages" below for more details * Codebase improvements: * Provide new variable manipulation helpers: - variable_is_set - variable_is_empty * Rework multiple functions to avoid querying unset variables. * Changes related to packages: * A new function "package_name" is provided, returning the file name of a given package. * Packages are prepared in "${PLAYIT_WORKDIR}/packages" instead of the root of "$PLAYIT_WORKDIR". * The function returning the path to the directory where a given package is prepared is renamed from "package_get_path" to "package_path". * Changes specific to DOSBox: * Drop special behaviour when using APP_xxx_PRERUN/APP_xxx_POSTRUN. Game scripts targeting ./play.it < 2.20 are unaffected, commands set using APP_xxx_PRERUN/APP_xxx_POSTRUN are still run from inside DOSBox. For games scripts targeting ./play.it ≥ 2.20, commands set using APP_xxx_PRERUN are run before spawning DOSBox, and commands set using APP_xxx_POSTRUN are run after exiting DOSBox. Two new variables are provided to set commands that should be run inside DOSBox: - APP_xxx_DOSBOX_PRERUN - APP_xxx_DOSBOX_POSTRUN * Changes specific to Mono: * Add ability to run games without a local user prefix. * Changes specific to Gentoo: * Change egentoo output paths. Instead of making packages in data, amd64 and x86 directories, ebuilds are now created in an overlay/games-playit directory and packages in a packages directory. * With egentoo variant, generate only one ebuild and one tar archive, with a bit more complexity in the ebuild, in order to decide which content to install. 2.19.1 * Ensure errors during calls to "game_id" are always blocking. * Gentoo: Fix dependencies handling. * Gentoo: Ensure libstdc++.so.6 is provided by the correct package. 2.19.0 * Deprecation warnings: * The legacy variables for content inclusion are no longer supported for game scripts targeting ./play.it ≥ 2.19: - ARCHIVE_xxx_PATH - ARCHIVE_xxx_FILES The new variables introduced with ./play.it 2.18 must be used instead: - CONTENT_xxx_PATH - CONTENT_xxx_FILES The legacy ones can still be used only from game scripts targeting ./play.it ≤ 2.18. * The following global variables are still available, but are deprecated: - PATH_BIN - PATH_DESK - PATH_DOC - PATH_GAME - PATH_ICON_BASE * "APP_xxx_LIBS" is deprecated, game scripts should no longer rely on it. Libraries installed under /usr/lib/games/${GAME_ID} are automatically added to LD_LIBRARY_PATH. * The following internal functions are dropped: - launcher_write_script_dosbox_application_variables - launcher_write_script_dosbox_run - launcher_write_script_java_application_variables - launcher_write_script_java_run - launcher_write_script_mono_application_variables - launcher_write_script_mono_run - launcher_write_script_native_application_variables - launcher_write_script_native_run - launcher_write_script_nativenoprefix_run - launcher_write_script_native_run_common - launcher_write_script_renpy_run - launcher_write_script_residualvm_application_variables - launcher_write_script_residualvm_run - launcher_write_script_scummvm_application_variables - launcher_write_script_scummvm_run * "application_type" no longer fails if no type could be found. Support for the special fallback value "unknown" is dropped, as its only purpose was to avoid the error on empty application type. * "package_get_current" no longer falls back on "PKG_MAIN", see "Changes related to packages" below for more details. * Improved integration of shipped libraries * In addition to standard system paths, libraries are loaded from the following paths, ordered from lower priority to higher priority: - APP_xxx_LIBS (deprecated) - /usr/lib/games/${GAME_ID} (/usr is replaced by the custom install prefix) - ${HOME}/.local/lib/games/${GAME_ID} * In addition to game data files and documentation files, "content_inclusion_default" now automatically fetches listed native libraries and put them into a specific install path. The native libraries can be listed using global variables similar to the ones already in use: - CONTENT_LIBS_xxx_PATH - CONTENT_LIBS_xxx_FILES As with game data files and documentation files, as single-digit number can be appended to "LIBS": - CONTENT_LIBS0_xxx_FILES - CONTENT_LIBS1_xxx_FILES - (…) - CONTENT_LIBS9_xxx_FILES * New dedicated functions printing install paths are available: - path_binaries - path_xdg_desktop - path_documentation - path_game_data - path_icons - path_libraries These should be used instead of the deprecated "PATH_xxx" variables. * Rework launchers generation * Game execution failure no longer prevents the execution of post-run actions. * The late extra eval step of JAVA_OPTIONS and MONO_OPTIONS is dropped, no current game script was relying on these. * APP_xxx_OPTIONS support is added to ResidualVM and ScummVM launchers. * Changes related to icons integration: * Rework icons requirement check to no longer rely on applications. * Add support for archive context to application_icons_list. * If no icon is set for a WINE application, try to extract one from the game binary. * Changes related to applications: * Update applications_list to follow the archive context. * Add automatic application type detection based on file type, APP_xxx_TYPE declaration is now optional in more cases. * Changes related to packages: * Extend the list of supported native libraries for packages dependencies declarations. * package_get_current returns the first package of the list of packages to build if no value is explicitly set, instead of the previously hardcoded value "PKG_MAIN". * Other changes: * Drop system-specific install prefix. The default install prefix is now the same for all supported systems: /usr * Do not run a full dependencies check when initializing an archive, only the presence of the dependencies required to handle the current archive is tested. * Set working directory path as soon as the archive is found. * Automatically add dependency on winetricks if APP_WINETRICKS is set. 2.18.3 * content_path_default - Do not throw an error if no default path is set. * Extend the list of supported native libraries for packages dependencies declarations. * Add a format check for package id. * WINE: Fix APP_WINE_LINK_DIRS failure on non-empty target. 2.18.2 * Add ability to get the name of a context-specific variable, using a new dedicated function: context_specific_name. This new function should not be used from game scripts before ./play.it 2.19 release. * Fix dependencies addition using dedicated functions. * Extend the list of supported native libraries for packages dependencies declarations. 2.18.1 * Identify SteamOS as an Arch Linux derivative. * Extend the list of supported native libraries for packages dependencies declarations. * archive_dependencies_check - Include archive extra parts during extraction dependencies check. * icons_inclusion - Fail explicitly if called with no argument and no applications list can be guessed. * launcher_write_script - Throw an explicit error if called with an empty argument. * print_instructions - Clear the list of unknown libraries after it has been displayed once. 2.18.0 * Changes related to archive contents inclusion: * CONTENT_xxx_PATH is a new variable replacing ARCHIVE_xxx_PATH. * CONTENT_xxx_FILES is a new variable replacing ARCHIVE_xxx_FILES. * content_inclusion is a new function replacing organize_data. * content_inclusion_default is a new function replacing prepare_package_layout. * Changes related to packages: * Provide a new dependency system for native libraries, using a new variable: PKG_xxx_DEPENDENCIES_LIBRARIES * Support is added for extra native libraries. * Changes specific to WINE: * WINEDLLOVERRIDES can be overridden at runtime. * Provide persistent storage for registry keys, using a new dedicated variable: WINE_REGEDIT_PERSISTENT_KEYS * Add ability to set the Direct3D renderer using WINE_DIRECT3D_RENDERER. * Other changes: * Drop messages on completion of long tasks. * Codebase improvements: * Rework prefix generation for WINE games. 2.17.2 * Prefixes - Fix handling of symbolic links in read-only game data * icons_move_to (deprecated) - Drop declaration of unused variable * Improve automatic package format setting * Identify Artix as an Arch Linux derivative 2.17.1 * icons_get_from_legacy_path - Fix clean-up step, to avoid an error from rmdir. 2.17.0 * Changes related to launchers: * Prefix type can be set at the application level (using APP_xxx_PREFIX_TYPE) or for all applications (using APPLICATIONS_PREFIX_TYPE), with the following valid values: - symlinks: generate our usual symbolic links farm, the default for most application types - none: the game is run from the read-only system directory, the default for ScummVM and ResidualVM * Prefix path can be overridden for the current game session by setting PLAYIT_PREFIX_PATH The default value is: ~/.cache/play.it/prefixes/${GAME_ID} * WINE prefix path follows WINEPREFIX if it is set. If the variable is unset, it defaults to: ~/.cache/play.it/wine/${GAME_ID} * A single directory is used for persistent storage of user data. It can be set using PLAYIT_PERSISTENT_USER_PATH, and defaults to: ~/.local/share/games/${GAME_ID} * The undocumented runtime variable PREFIX_ID is no longer supported. * Changes related to archives: * New archive type "tar.bz2". * For multi-part archives, type is optional for extra parts. * Allow use of unzip for data extraction from mojosetup archives, if bsdtar is not available. * Force the use of a specific tool for content extraction: - An archive extractor can be set with ARCHIVE_xxx_EXTRACTOR - An options strings can be set with ARCHIVE_xxx_EXTRACTOR_OPTIONS * Changes related to icons: * Icons inclusion is now done using a single function, icons_inclusion. * The following functions are deprecated: - icons_get_from_package - icons_get_from_workdir - icons_move_to * Other changes: * Drop option to do partial runs with no actual disk write. (--dry-run option) * New option provided to change ./play.it working path: --tmpdir The default value is $TMPDIR, falling back on /tmp * Codebase improvements: * New layout for source files. * Use a dedicated check to detect unexpected empty variables. 2.16.2 * applications_list - Throw an explicit error on unexpected empty list. * prepare_package_layout - Prevent PKG value changes to leak outside of the current function call. 2.16.1 * icons_list_dependencies - Rely on application_icons_list to get the list of icons. * launcher_write_desktop - Drop requirement on APP_xxx_TYPE. * get_context_specific_value: - Do not try to guess a package identifier if none is set. - Do not look for an archive-specific value before an archive is set. * Check for dependencies only after the main archive is set. 2.16.0 * Major changes: * Game scripts are no longer provided by this repository, they should instead be fetched from dedicated collections. The main official game scripts collection is available from https://forge.dotslashplay.it/play.it/games * Drop support for selective architecture building (--architecture option) * Disable debug support by default, its inclusion is now done through a build flag. * A new extended man page is provided, available in both English and French. * Codebase improvements * Rewrite the code used to generate and update the volatile game prefixes. * Rework the code handling data extraction from archives. * Launchers: Escape single quotes in binary file name. * Applications: Allow setting package-specific versions of several application properties. * Changes specific to WINE: * Add ability to set a custom path to `wine` command, using a new environment variable: PLAYIT_WINE_CMD * Changes specific to ScummVM: * Improve ScummVM ID validation. * Changes specific to Debian: * Drop support for installation instructions relying on dpkg (useful only for apt versions older than 1.1) * Changes specific to Arch Linux: * Restore the generation of the .MTREE package metadata file. Its generation can be skipped with --no-mtree. * Changes specific to Gentoo: * Ensure the directory used to prepare packages allows files execution. * Append script revision to package version number. 2.15.1 * WINE: Set compatibility links to legacy user paths * Drop direct calls to PACKAGES_LIST environment variable * Fix behaviour of archive_find_path when the archive variable contains an absolute path 2.15.0 * New features: * All command line options can now be set through a configuration file, its path can be set with --config-file some_path and defaults to: $XDG_CONFIG_HOME/play.it/config * --show-game-script - display the path to the game script that would be used for a given archive, but take no action * General improvements: * New functions used to fetch game-related values: * game_id - print the id of the current game * game_name - print the display name of the current game * Provide a new variable CONTENT_PATH_DEFAULT, used as a default value for ARCHIVE_xxx_PATH * Setting the current package to a value that is not included in the list of packages to build triggers an error * Rely on MIME type for icon type detection, instead of file extension * application_type can now be passed a fallback value * Changes specific to Debian: * Improve handling of dependency to WINE * Changes specific to Gentoo: * Use unpacker eclass when using a non-standard compression option * Drop support for lzop compression * Replace spaces by tabulations and linefeeds in pkg_deps * Refactor compression handling 2.14.4 * Fix ambiguous quoting when using prefix/suffix pattern removal 2.14.3 * Fix ability to call the wrapper with --help * WINE: Fix a failure to get the path to current package when generating the .desktop launcher for winecfg * Gentoo: Update icon cache after installation and removal * Gentoo: Fix "provides" fields for ebuild generation 2.14.2 * Ensure all errors related to fetching the path where a package is prepared are blocking * Fix fetching current package when getting the path where a .desktop file should be written * Fall back on guessed value for game binary only if no value has been explicitely set * Improve handling of failure state on errors 2.14.1 * Only use application type guessing if none is explicitely set * Drop ShellCheck "disable" directives for non-POSIX local usage * Ensure invalid application type always throws blocking errors * Fix fetching application type * Show an explicit error when tolower/toupper is given an invalid target 2.14.0 * New features: * Archives: Add support for tar.xz archives * Engines: Improve support for Unity3D games, with a new dedicated application type * Launchers: Provide new functions used to display localized messages * Launchers: Add a new hook for commands to run before the initialization of the game prefix * Launchers: Provide a new function used to convert filenames case * Icons: Add support for X PixMap icons (.xpm) * General improvements: * Improve the validity tests run when setting temporary directories * Packages-related improvements: * Always use the archive-specific list of packages when available * Add several new dependency keywords * Installation: * The default path for installing the "play.it" command as a non root user is now "$HOME/.local/bin", instead of "$HOME/bin" * The path for installing game scripts is now $(datadir)/play.it/games/50_core, where $(datadir) defaults to: - /usr/local/share/games for installation as root - $XDG_DATA_HOME for installation as a non-privileged user * Code maintenance: * A new source file is used for functions dedicated to variable manipulations * A new function is provided for getting context-specific variable values * Provide new helper functions easing the manipulation of applications and icons * Launchers: Rework the generation of the XDG desktop files * Deprecation notices: * get_archive_specific_value is no longer available * use_archive_specific_value and use_package_specific_value are now compatibility aliases around the new function get_context_specific_value 2.13.3 * Redirect all debug messages to /dev/stderr * Gentoo: Stop emerge on errors * Arch Linux: Drop generation of .MTREE metadata file 2.13.2 * Fix getting icon path for pre-2.8 game scripts with globs expanding to spaces * Improve the search for a proper working directory * Improve function descriptions in the icons source file * Gentoo: Use fakeroot (≥1.25.1) instead of fakeroot-ng 2.13.1 * Fix data migration from volatile prefix to persistent paths * Fix filenames case conversion failure on non-ascii characters * Fix wrapper installation path * Gentoo: Ensure sdl2-image is built with png and jpeg support 2.13.0 * New features: * New option --version (or -v), displaying the library version * Debug notices, disabled by default, enabled with --debug * Archives: Add support for InstallShield installers * Engines: Add ability to use system-provided Ren'Py * Packages: Add support for extra compression methods: lz4, lzip, lzop and zstd * Packages: Add an alternative output format targeting Gentoo * The installation process GNU Make has been improved to allow targeted actions: install-library, install-games, install-wrapper and install-manpage * Archive-related fixes and improvements: * Use a new archives naming convention, more robust, see our documentation for details: https://forge.dotslashplay.it/play.it/doc/-/wikis/dev/scripts/variables#base-archives * Rewrite functions related to archives initialization * .deb archives: Extract data without relying on dpkg-deb only * Fix the error shown when an archive type is not set and could not be guessed * Fix the error shown when an unknown archive type is used * WINE-specific improvements: * Do not override WINEDEBUG if it is already set by the user * Provide a standard method to divert arbitrary files from the WINE prefix to persistent user-writable paths * Packages-related changes: * Provide wrapper functions to get package-related informations, including packages metadata * New generic dependency keywords * Improve handling of supported compression methods based on the target package format * Debian: Update dependency on libgdk_pixbuf-2.0.so.0 * Gentoo: Change default installation prefix to /usr from /usr/local * Environment checks: * Run early filesystem checks, allowing to detect failure that would happen only at the packages building step * New function version_is_at_least comparing two version strings using the "x.y.z" format * Deprecation notices: * icons_linking_postinst is now a compatibility wrapper around standard icons extraction functions 2.12.2 * Archives-related improvements: * Improve compatibility check for InnoSetup archives * Icons-related fixes: * Avoid a potential infinite loop in the icons resolution guessing function * Fix compatibility with GraphicksMagic compatibility wrapper for ImageMagick * Package-related fixes and improvements: * Arch Linux: Add support for EndeavourOS as an Arch Linux derivative * Fix getting hostname on systems without /etc/hostname * Errors handling: * Fix error message displayed when passing an invalid value to a supported option * Improve handling of error cases related to empty variables * Fix error message shown when there was not enough disk space in tested temporary directory candidates * Codebase improvements: * Use grep --quiet instead of output redirection 2.12.1 * Wrapper fixes: * Fix script detection from archive name * Archive-related fixes: * .zip archives: Drop selected files extraction using unzip * Package-related improvements: * Arch Linux: Use multithreaded xz compression by default * Arch Linux: Fix packages building failure when using --output-dir with a relative path * Arch Linux: Generate a .MTREE metadata file * Debian: Improve APT version detection * Dependencies-related improvements: * Show a more explicit message if icotool or wrestool is required but missing * Launchers-related fixes * Avoid broken .desktop Exec field when using spaces in the install prefix * Icons-related fixes: * Fix icons_move_to failure when targeting a non-empty directory * Codebase improvements: * Drop the dependency on hostname * Unset variables that we do not want to import from the user environment 2.12.0 * New options: * --output-dir: Set the output directory for generated packages * --overwrite: Replace packages if they already exist * --icons: Allow including icons only if dependencies are present * Wrapper changes: * Drop $XDG_RUNTIME_DIR from the candidates for temporary directories * Prevent scan of unneeded directories * Drop script identification by MD5 hash * Archive-related changes: * Only extract needed files when using unzip * Allow to use renamed installers * Add support for LHA archives extraction * Engines-related changes: * New engine: ResidualVM * New engine: System-provided Mono runtime * DOSBox: Use PLAYIT_DOSBOX_BINARY in launchers if defined * Packages-related changes: * Add ability to set variables for package-specific postinst and prerm scripts * Arch Linux: Improve consistence of 32-bit packages naming * New helper functions: * version_target_is_older_than: Check if the game script target version is older than a given one * toupper: Convert files name to upper case * New generic dependency keywords: * libgdk_pixbuf-2.0.so.0 * libglib-2.0.so.0 / libgobject-2.0.so.0 * libmbedtls.so.12 * libpng16.so.16 * libopenal.so.1 (alias for "openal") * libSDL2-2.0.so.0 (alias for "sdl2") * libturbojpeg.so.0 * libuv.so.1 * libvorbisfile.so.3 (alias for "vorbis") * libz.so.1 * Codebase clean-up and improvements: * Massive rework of all message-related functions * Drop hardcoded paths for icons and .desktop launchers * Use system-specific default installation prefix for generated packages * Forcefully set errexit setting on library initialization * Use dirname/basename instead of built-in shell patterns 2.11.4 * Throw an explicit error when trying to write a launcher for a missing binary * Use safer `find | while read` constructs in prefix functions * Drop unrequired spawning of subshell by organize_data * Drop unrequired spawning of subshell by move_icons_to * Ensure $PLAYIT_WORKDIR is always an absolute path * Arch Linux: Fix bugs in dependencies handling * Debian: Fix APT version detection with APT ≥ 2.0.0 * Debian: Enforce correct permissions for packages metadata * Gentoo: Update download link for quickunpkg 2.11.3 * Fix cdrom type (file or directory) detection for DOSBox games * Use -eq instead of = for arithmetic comparisons * Update link to issues tracker * Improve handling of 7z archives extraction * Improve error messages shown when a required script dependency is missing * Spawn a terminal when calling winetricks, so its actions are no longer hidden from users not running their games from a terminal * Use convmv when available for converting file names to lower case, as it has better performances than our custom-made function * Fix argument usage for check_option_validity, and move it outside of play.it-2/src/99_init.sh * Arch Linux: Advertise ./play.it in the generated packages metadata * Arch Linux: Fix builddate entry in the generated packages metadata * Arch Linux: Improve support for libarchive implementation of tar ("bsdtar") when building packages * Debian: Improve handling of WINE dependency, thanks to Jens Reyer for the help on debian-wine mailing list * Gentoo: Fix default compression method for generated packages * Gentoo: Drop abi_x86_32 abi_x86_32 USE flag for app-emulation/winetricks dependency 2.11.2 * Do not throw an error when a script has been called with --dry-run and an icon file is not found * Move all compatibility aliases to a dedicated source file * glx meta-dependency do not install a transitional package anymore on recent Debian versions * Allow to skip control of InnoSetup version used in archive passed to innoextract * New test: Run a single game script in dry mode once for each supported archive * Check for file existence when an archive is given as argument to a game-specific script * Display an error when an unknown application type is used * When extracting multiple icons at once, ensure that they won’t override the ones extracted before them * Fix get_archive_specific_value / get_package_specific_value on corner cases, by dropping avoidable calls to eval * Use cabextract’s -L option to extract files as lowercase * Gentoo packages: bump EAPI version to 7 2.11.1 * Fix apt version detection on old versions of apt 2.11.0 * Add ability to generate packages for Gentoo, using --package=gentoo * Rewrite of launchers-related functions. Compatibility wrappers still exist for old functions * archive_guess_type: Automatically set ARCHIVE_TYPE for 7-zip archives * Nixstaller archives: Guess header length instead of using hardcoded value * Improve support for Java games * pkg_write_*: Reliably use archive-specific dependencies list when available * New option --skip-free-space-check: Bypass free space check, create temporary directories under $TMPDIR, defaults to /tmp * Fix issue with pre-2.8 compatibility code in icons_linking_postinst * move_icons_to: Fix crash when --prefix is set to a custom value 2.10.5 * select_package_architecture: Fix bug when using --architecture=64 (or --architecture=auto on 64-bit systems) on an archive providing 32-bit binaries only 2.10.4 * Improve ShellCheck tests coverage * Do not let empty dirs lingering after uninstallation using make * main script: Fix wrong usage of 'return' instead of 'exit' * main script: Update search paths for library and game scripts * help: Fix usage instructions when called from main script * packages_guess_format: Fix falling back on default package format value when host OS couldn’t be guessed * icons_get_from_path: Fail with an explicit error message if called with an empty icon path * print_instructions: Improve message readability * write_bin - init_prefix_dirs: Fix use of globbing when setting the paths to user directories 2.10.3 * write_bin - init_prefix_files: Do not ignore symbolic links in $CONFIG_PATH / $DATA_PATH when generating or updating prefix * pkg_write_deb: Always set "Multi-Arch: foreign" in packages meta-data, fixes issues when trying to install DLC for 32-bit games on 64-bit systems 2.10.2 * write_bin_run_native: Restore pre-2.10.1 version, as the 2.10.1 fix has unexpected side effects 2.10.1 * Drop pre-built library from repository * make install: Use different default paths if not run as root * Arch Linux: Add missing package to generic dependency 'gcc32' * archive_extraction_innosetup: Hide innoextract warnings during extraction * write_bin_build_wine: Always sleep 1 second after using winetricks, as some tweaks won’t apply directly if the game is called to quickly * Set input field separator to default value (space, tab, newline) * write_bin_run_native: Remove unneeded duplicated code 2.10.0 * get_tmp_dir: New function allowing to set a temporary working directory by setting $TMPDIR * New archive type 'iso', using bsdtar for data extraction * get_value: New helper function allowing to get the value of a variable using a dynamically generated name * New meta-dependencies 'libudev1' and 'theora' respectively providing libudev.so.1 and libtheora.so.0 * New archive type 'innosetup1.7', allowing to ensure that InnoExtract >= 1.7 is available * icon_file_not_found_error: New function displaying an error if some function should work on an icon file that could not be found * write_bin_run_dosbox: Improve handling of CD-ROM images for DOSBox games * Add support for bzip2 compression * Set all application-specific variables even for ScummVM games 2.9.2 * set_temp_directories: Improve the generation of the temporary work directory, dropping "mktemp --dry-run" usage 2.9.1 * extract_data_from: Do not print "OK" after innosetup variants extraction (progress already has a visual feedback) * Better handling of game updates and DLC (un)installation by automatically updating the user prefix on each game launch * meta-script: if called without argument, show usage instructions * icon_get_resolution_from_file: Fix a bug with pre 2.8 scripts where the resolution value of the first analyzed file would be kept for all files 2.9.0 * Update license file to keep track of all authors * New generic dependency: libcurl * Remove shebang from library, as it is always meant to be sourced, never directly executed * Makefile: Add ability to choose the installation path, now defaulting to /usr/local/share/games/play.it for library and scripts, and /usr/local/games for meta-script * Makefile: Do not install play.it 1.x scripts and library anymore * meta-script: Add manpage 2.8.3 * Fix typo in help(), that led to suboptimal wording when called on a script with a single supported archive (previous 2.8.2 fix was incomplete) * Add automated shellcheck tests based on GitLab CI * Improve syntax based on shellcheck 0.5.0 report 2.8.2 * icon_extract_ico_from_exe: Suppress wrestool error output * icon_get_resolution_from_file: Fix compatibility with scripts targeting library version 2.7 or older in a more robust way than what has been done in 2.8.1 * Fix typo in help(), that led to suboptimal wording when called on a script with a single supported archive 2.8.1 * Fix icon_get_resolution_from_file and icons_linking_postinst compatibility with scripts targeting library version 2.7 or older * WINE: Do not remove links to $HOME for scripts targeting library version 2.7 or older 2.8.0 * Greatly reduce time taken by play.it meta-script to identify the correct script for an archive, by using file name before falling back on MD5 hash sum * WINE: Remove most links pointing outside of the WINE prefix, to reduce $HOME clutter * Rework most icon-related code for easier understanding and maintenance * Use ImageMagick to extract .png files from .ico containers * Update dependencies automatic detection based on new icon extraction methods 2.7.5 * Fix sort_icons behaviour when icon extraction produced a single file * meta-script: Fix broken support for ./play.it 1 scripts 2.7.4 * Fix write_bin_winecfg breaking init_prefix_dirs in winecfg launcher 2.7.3 * archive_extraction_innosetup: Fix InnoSetup version test * Fix archives_get_list not detecting archives named ARCHIVE_(…)_OLD * Fix error message displayed by write_metadata when called on an unknown package 2.7.2 * Fix an inverted test in archive_get_infos that broke the MD5 integrity check again, the 2.6.2 fix having been accidentally reverted on 2.7.0 release 2.7.1 * Fix variable leak during multi-parts archive automatic handling 2.7.0 * InnoSetup archives: Check ability of available innoextract version to extract the target archive before trying to proceed with extraction * Try to guess the value of ARCHIVES_LIST if it is not set by the script * Use 'APP_ICON' as a fallback value for APP_ICONS_LIST if it is not set * Add automatic detection of multi-parts archives * New function get_package_version allowing a reliable way to use different version values for multiple packages generated from a single archive 2.6.2 * Fix an inverted test in archive_get_infos that broke the MD5 integrity check 2.6.1 * Fix postinst_icons_linking not working on more than one single app * Rework most archive-related code for easier understanding and maintenance 2.6.0 * Add ability to build only packages for a given architecture * Add automatic architecture handling to print_instructions * New function prepare_package_layout providing a wrapper for organize_data * Improve sort_icons so it can be used on single .png file produced by convert * Add --dry-run switch, running tests but not extracting data nor building packages * Use a dedicated function to guess package format to build from host OS 2.5.3 * When no supported archive is provided, add download URL to archives list * Fix variable leaking function scope when calling print_instructions * Follow symblic links when copying native game binary in user prefix 2.5.2 * Fix init_prefix_files crashing when some files exist in PATH_DATA or PATH_CONFIG with no equivalent in PATH_PREFIX 2.5.1 * Add a patch allowing to chose wether or not the desktop files should include the full path to the launcher script (default includes it and does not use $PATH, so custom values for installation prefix can be used without further tweaking) * Improve patches syntax following ShellCheck feedback * Display an error when using an invalid value for PKG * Fix error displayed if calling extract_icon_from on an unsupported file type * Improve user prefix generation * Improve library syntax following ShellCheck feedback 2.5.0 * Add support for games requiring wine-staging patches * Add support for 64-bit WINE games * Add support for Windows MSI installers * Add support for Microsoft cabinet archives * New archive type 'nullsoft-installer' * New archive type value 'innosetup_nolowercase' allowing to skip files names conversion to lower case * New archive type 'zip_unclean' * New function use_package_specific_value allowing to get package-specific value for a given variable name (if such value exists) * New function use_archive_specific_value allowing to get archive-specific value for a given variable name (if such value exists) * Add 'xrandr' and 'xgamma' to the generic dependencies * Add automatic dependencies detection for archive types 'mojosetup_unzip' and 'tar' 2.4.2 * Fix handling by init_prefix_files of files created after the first game launch 2.4.1 * Fix messed up icon path when using get_icon_from_temp_dir 2.4.0 * Add option to load regedit files on WINE prefix initialization * New function get_icon_from_temp_dir, to get an icon in .png format directly from the source archive 2.3.2 * Fix launchers broken in 2.3.1 2.3.1 * Use full path to game binary in launchers, to avoid issues when using non-default install path * Do not store temporary files outside of prefix * Drop post-run user directories clean-up, redundant with another function 2.3.0 * Improve handling of MojoSetup extraction with unzip ending with an error code * Improve handling of user directories * Add new packages to the list of generic dependencies * Add a patch allowing to change the default package compression method * Remove the need to manually clean-up package scripts * Preserve symbolic links when copying files 2.2.0 * Automatically detect archive type for *.rar files * Allow to use multiple fallback values for $ARCHIVE_PATH and $ARCHIVE_FILES * Make /tmp/play.it world-writable to allow using ./play.it more easily on shared systems * Allow to set package-specific values for $APP_OPTIONS, $APP_PRERUN and $APP_POSTRUN * Add $APP_POSTRUN support to WINE launchers * Skip building already existing packages * New distro-agnostic method to declare dependencies 2.1.1 * Fails gracefully if organize_data() is called before setting $PKG * Fix a bug where postinst_icons_linking() would erase the prerm/postinst prior content instead of appending to it * Fix a bug with wrestool, where calling it once with the --name option would have this option been carried out to all subsequent invocations 2.1.0 * First fully installable version of ./play.it * New script 'play.it' automatically loading the adequate game-specific script for the archive given in argument * Display an error when calling a script with unsupported arguments * New function easing the management of icons provided by post-installation links * New application type allowing to run native games without using a ./play.it prefix * The library can now be loaded without implying that it has been called by a ./play.it script, making it easier to use by third-party projects 2.0.3 * Fix error displayed when building .deb packages on systems without apt * Better handling of spaces in directories names when displaying installation instructions * Work around WINE bug 41639 2.0.2 * Fix a bug with the copy of the game binary in the user prefix for some native games * Work around WINE bug 29661 for WINE versions prior to 1.9.20 2.0.1 * Test the validity of options values early in the script execution to throw an error before any potentially long task * Print 'OK' on potentially long tasks completion without a visual progression indicator * If host OS auto-detection failed, display a warning before falling back on deb format * On all distributions providing apt >= 1.1, installation instructions show apt usage instead of dpkg + apt-get LICENSE0000644000000000000000000000351013120060140010543 0ustar rootrootCopyright © 2015 Antoine Le Gonidec Copyright © 2016 Mopi Copyright © 2017 Jacek Szafarkiewicz Copyright © 2017 HS-157 Copyright © 2017 Phil Morrell Copyright © 2017 Quentin Barbe Copyright © 2018 Janeene "dawnmist" Beeforth Copyright © 2018 VA Copyright © 2018 BetaRays Copyright © 2018 Julien Noblet Copyright © 2018 Andrey Butirsky Copyright © 2019 Emmanuel Gil Peyrot Copyright © 2019 Luc Didry Copyright © 2020 macaron Copyright © 2020 Hoël Bézier Copyright © 2020 quinao Copyright © 2021 Gael Le Mignot Copyright © 2022 JashinYoda All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. This software is provided by the copyright holders and contributors "as is" and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright holder or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage. README.md0000644000000000000000000001074013120060140011020 0ustar rootroot# ./play.it: Installer for DRM-free commercial games The codebase is maintained at https://git.dotslashplay.it/scripts/ Bug reports are tracked at https://forge.dotslashplay.it/play.it/scripts/-/issues Mirrors of the codebase can be found at: - https://forge.dotslashplay.it/play.it/scripts - https://git.sr.ht/~vv221/play.it - https://framagit.org/vv221/play.it ## Description ./play.it is a free software building native packages from installers for Windows or Linux, mainly those sold by stores focusing on DRM-free games distribution. The goal is that a game installed via ./play.it is indistinguishable from a game installed via the official repositories of your favourite distribution. The games are installed globally on multi-user systems, avoiding unnecessary duplication. The locations of save games, settings, mods, temporary files and backups are standardized with XDG Base Directory support. Packaging the games simplifies future updates, uninstalls and handling of any necessary dependencies, including integrated obsolete dependencies if specific versions are needed. ## Installation ### Distributions providing ./play.it The following distributions provide installation instructions in their official documentation: - [Debian] - [Gentoo] - [Ubuntu] (French article) [Debian]: https://wiki.debian.org/Games/PlayIt#Installation [Gentoo]: https://wiki.gentoo.org/wiki/Play.it#Installation [Ubuntu]: https://doc.ubuntu-fr.org/play.it#installation In most cases, these instructions should work in the same way for derivatives of these distributions. ### Installation from git If your distribution does not already have a package for ./play.it, you can install it from this git repository: ``` git clone --branch main --depth 1 https://git.dotslashplay.it/scripts play.it.git cd play.it.git make make install ``` ## Game scripts Starting with ./play.it 2.16 release, game scripts are no longer provided in this repository. You need to install a collection of game scripts in addition to the core library and wrapper to add support for some game installers. The following games collections are available: - [The community-maintained main collection](https://git.dotslashplay.it/games-community/about/) - [vv221ʼs games collection](https://git.vv221.fr/play.it-vv221/about/) - [Hoëlʼs games](https://git.dotslashplay.it/hoel/games-hoel/) - [ahubʼs My Game Collection](https://git.dotslashplay.it/berru/games-berru/about/) - [Caliban’s games collection](https://git.dotslashplay.it/caliban/games-caliban/) - [ArRay’s game collection](https://git.dotslashplay.it/array/game-collection/) ## Usage Once ./play.it is installed, you can call it providing a supported game installer as the first argument to generate the packages. An example could be: ``` play.it ~/Downloads/setup_sid_meiers_alpha_centauri_2.0.2.23.exe ``` The building process can take from a couple seconds to several minutes, depending mostly on the game size, and ends with the command to run as root to install the generated packages. On Debian, this could be something like: ``` apt install \ /home/user/Downloads/alpha-centauri_6.0b-gog2.0.2.23+20221005.2_i386.deb \ /home/user/Downloads/alpha-centauri-movies_6.0b-gog2.0.2.23+20221005.2_all.deb \ /home/user/Downloads/alpha-centauri-data_6.0b-gog2.0.2.23+20221005.2_all.deb ``` ## Contributing There is [some documentation] on how to add support for new games, but the best bet is to find a similar game and copy its script. Youʼll likely need to visit our IRC channel to ask for more help. It can also be useful to upload your attempts to [pastebin] for commentary, or feel free to raise a WIP [Merge Request]. [some documentation]: https://forge.dotslashplay.it/play.it/scripts/-/wikis/home [pastebin]: https://paste.debian.net/ [Merge Request]: https://forge.dotslashplay.it/play.it/scripts/-/merge_requests/new ## Contact informations ### IRC channel Some ./play.it developers and users can be reached on IRC, channel is `#play.it` on network `irc.oftc.net`. The main language on this IRC channel is English, but most of us can speak French too. ### E-mail A contact e-mail for feedback can usually be found in each ./play.it game script, as well as in the library. Open one of these files with any text editor to see the contact e-mail. ### Fediverse ./play.it has an account on the Fediverse, you can follow it for news about the development or use it to contact us: [@playit@fediverse.dotslashplay.it] [@playit@fediverse.dotslashplay.it]: https://fediverse.dotslashplay.it/snac/playit Makefile0000644000000000000000000000761213120060140011205 0ustar rootroot# Build the library .PHONY: all clean ## The debug code is omitted unless DEBUG is set to 1. DEBUG := 0 all: lib/libplayit2.sh lib/libplayit2.sh: src/*/*.sh mkdir --parents lib ifeq ($(DEBUG),1) find src -type f -name '*.sh' -print0 | LC_ALL=C sort -z | xargs -0 cat > lib/libplayit2.sh sed -i -e 's/%%DEBUG_DISABLED%%/0/' lib/libplayit2.sh else find src -type f -name '*.sh' \! -path 'src/80_debug/*' -print0 | LC_ALL=C sort -z | xargs -0 cat > lib/libplayit2.sh sed -i -e '/^\s*debug_/d' -e 's/%%DEBUG_DISABLED%%/1/' lib/libplayit2.sh endif clean: rm --force lib/libplayit2.sh # Install the library, main script, and man pages .PHONY: install uninstall ## Set the default install paths UID := $(shell id --user) ifeq ($(UID),0) prefix = /usr/local bindir = $(prefix)/games datadir = $(prefix)/share/games mandir = $(prefix)/share/man else ifeq ($(XDG_DATA_HOME),) XDG_DATA_HOME := $(HOME)/.local/share endif prefix = $(XDG_DATA_HOME) bindir = $(HOME)/.local/bin datadir = $(prefix) mandir = $(prefix)/man endif install: all install -D --mode=644 lib/libplayit2.sh $(DESTDIR)$(datadir)/play.it/libplayit2.sh install -D --mode=755 play.it $(DESTDIR)$(bindir)/play.it install -D --mode=644 man/man6/play.it.6 $(DESTDIR)$(mandir)/man6/play.it.6 install -D --mode=644 man/fr/man6/play.it.6 $(DESTDIR)$(mandir)/fr/man6/play.it.6 uninstall: rm --force $(DESTDIR)$(bindir)/play.it $(DESTDIR)$(datadir)/play.it/libplayit2.sh $(DESTDIR)$(mandir)/man6/play.it.6 $(DESTDIR)$(mandir)/fr/man6/play.it.6 rmdir -p --ignore-fail-on-non-empty $(DESTDIR)$(bindir) $(DESTDIR)$(datadir)/play.it $(DESTDIR)$(mandir)/man6 $(DESTDIR)$(mandir)/fr/man6 # Release preparation .PHONY: dist ## The generated tarball is signed with gpg by default, ## NO_SIGN should be set to a non-0 value to skip the signature. NO_SIGN := 0 dist: VERSION := $(shell head --lines=1 CHANGELOG) dist: TARBALL := play.it-$(VERSION).tar.gz dist: TAR_OPTIONS := --sort=name --mtime=2017-06-14 --owner=root:0 --group=root:0 --use-compress-program='gzip --no-name' dist: CHANGELOG LICENSE README.md Makefile play.it man/man6/play.it.6 man/*/man6/play.it.6 src/*/*.sh tests/shunit2/coverage.sh tests/shunit2/*/*.sh mkdir --parents dist LC_ALL=C tar cf dist/$(TARBALL) $(TAR_OPTIONS) CHANGELOG LICENSE README.md Makefile play.it man/man6/play.it.6 man/*/man6/play.it.6 src/*/*.sh tests/shunit2/coverage.sh tests/shunit2/*/*.sh ifeq ($(NO_SIGN),0) rm --force dist/$(TARBALL).asc gpg --armor --detach-sign dist/$(TARBALL) endif # Run tests, including: # - syntax checks, relying on ShellCheck # - unit tests, relying on shUnit2 # - man page syntax check .PHONY: check shellcheck-library shellcheck-wrapper shunit2-coverage shunit2 man-syntax check: shellcheck-library shellcheck-wrapper shunit2-coverage shunit2 man-syntax ## This is a unicode quote. Delete and retype it (or ignore/doublequote for literal). shellcheck-library: SHELLCHECK_EXCLUDE += --exclude=SC1112 ## Expressions don't expand in single quotes, use double quotes for that. shellcheck-library: SHELLCHECK_EXCLUDE += --exclude=SC2016 ## Double quote to prevent globbing and word splitting. shellcheck-library: SHELLCHECK_EXCLUDE += --exclude=SC2086 ## In POSIX sh, local is undefined. shellcheck-library: SHELLCHECK_EXCLUDE += --exclude=SC3043 shellcheck-library: lib/libplayit2.sh shellcheck --shell=sh $(SHELLCHECK_EXCLUDE) lib/libplayit2.sh shellcheck-wrapper: play.it shellcheck --external-sources --shell=sh play.it shunit2-coverage: lib/libplayit2.sh ./tests/shunit2/coverage.sh shunit2: lib/libplayit2.sh find tests/shunit2/*/ -type f -name '*.sh' -print0 | LC_ALL=C sort -z | xargs -0 -n1 shunit2 man-syntax: man/man6/play.it.6 man/fr/man6/play.it.6 man --warnings --encoding=UTF-8 --local-file --troff-device=utf8 --ditroff man/man6/play.it.6 >/dev/null man --warnings --encoding=UTF-8 --local-file --troff-device=utf8 --ditroff man/fr/man6/play.it.6 >/dev/null play.it0000755000000000000000000000524413120060140011052 0ustar rootroot#!/bin/sh set -o errexit LIB_ONLY=1 PLAYIT_LIB_PATHS=" $PWD $(dirname "$0")/lib ${XDG_DATA_HOME:="${HOME}/.local/share"}/play.it /usr/local/share/games/play.it /usr/local/share/play.it /usr/share/games/play.it /usr/share/play.it" if [ -z "$PLAYIT_LIB2" ]; then for playit_lib_path in $PLAYIT_LIB_PATHS; do if [ -e "${playit_lib_path}/libplayit2.sh" ]; then PLAYIT_LIB2="${playit_lib_path}/libplayit2.sh" break fi done fi if [ -z "$PLAYIT_LIB2" ]; then printf '\n\033[1;31mError:\033[0m\n' printf 'libplayit2.sh not found.\n' exit 1 fi # shellcheck source=lib/libplayit2.sh . "$PLAYIT_LIB2" export PLAYIT_LIB2 # Exit if the current process has been spawned by the root user ## This check can be skipped by setting the following environment variable: ## PLAYIT_OPTION_RUN_AS_ROOT=1 if \ [ "${PLAYIT_OPTION_RUN_AS_ROOT:-0}" -eq 0 ] && \ check_is_running_as_root then error_run_as_root exit 1 fi # Set default options ## Set hardcoded defaults options_init_default ## Load defaults from the configuration file config_file_path=$(find_configuration_file "$@") load_configuration_file "$config_file_path" # Display the help message, # if called with no argument. if [ $# -eq 0 ]; then help exit 0 fi # Set options from the command-line parse_arguments "$@" # Display the help message, # if called with --help. option_help=$(option_value 'help') if [ "${option_help:-0}" -eq 1 ]; then help exit 0 fi unset option_help # Display the version string, # if called with --version. option_version=$(option_value 'version') if [ "$option_version" -eq 1 ]; then printf '%s\n' "$LIBRARY_VERSION" exit 0 fi unset option_version # Display the list of available game scripts, # if called with --list-available-scripts. option_list_available_scripts=$(option_value 'list-available-scripts') if [ "$option_list_available_scripts" -eq 1 ]; then games_list_scripts_all exit 0 fi unset option_list_available_scripts # Display the full list of supported-games, # if called with --list-supported-games. option_list_supported_games=$(option_value 'list-supported-games') if [ "$option_list_supported_games" -eq 1 ]; then games_list_supported_all exit 0 fi unset option_list_supported_games # Exit with an error if no archive is provided if [ -z "${SOURCE_ARCHIVE_PATH:-}" ]; then error_archive_missing_from_arguments exit 1 fi # Locate game script by file name FILE_NAME="$(archive_name 'SOURCE_ARCHIVE' | sed 's/\[/\\\[/g;s/\]/\\\]/g')" GAME_SCRIPT=$(games_find_script_for_archive "$FILE_NAME") # Exit with an error if the given archive is not supported if [ -z "$GAME_SCRIPT" ]; then error_no_script_found_for_archive "$FILE_NAME" exit 1 fi # Load the game script exec "$GAME_SCRIPT" "$@" man/man6/play.it.60000644000000000000000000001275313120060140012632 0ustar rootroot.Dd $Mdocdate$ .Dt ./play.it 6 .Os .Sh NAME .Nm ./play.it .Nd Installer for drm-free commercial games .Sh SYNOPSIS .Nm .Ar archive .Op Fl \-checksum Cm md5 | Cm none .Op Fl \-compression Cm none | Cm speed | Cm size | Cm auto .Op Fl \-prefix Ar path .Op Fl \-package Cm arch | Cm deb | Cm gentoo | Cm egentoo .Op Fl \-no\-icons .Op Fl \-overwrite .Op Fl \-output\-dir Ar path .Op Fl \-debug Op Ar N .Op Fl \-no\-mtree .Op Fl \-tmpdir Ar path .Op Fl \-no\-free\-space\-check .Op Fl \-show\-game\-script .Op Fl \-config\-file Ar path .Op Fl \-list\-packages .Op Fl \-list\-requirements .Op Fl \-list\-available\-scripts .Op Fl \-list\-supported\-games .Sh DESCRIPTION .Nm is a free software building native packages from installers for Windows or Linux, mainly those sold by stores focusing on DRM-free games distribution. The goal is that a game installed via .Nm is indistinguishable from a game installed via the official repositories of your favorite distribution. .Pp The games are installed globally on multi-user systems, avoiding unnecessary duplication. The locations of save games, settings, mods, temporary files and backups are standardized with XDG Base Directory support. .Pp Packaging the games simplifies future updates, uninstalls and handling of any necessary dependencies, including integrated obsolete dependencies if specific versions are needed. .Ss Options .Bl -tag -width DS .It Fl \-checksum Cm md5 | Cm none Archive integrity verification method selection. .Bl -tag -width indent -compact .It Cm md5 md5sum verification .It Cm none no verification .El .It Fl \-compression Cm none | Cm speed | Cm size | Cm auto Generated packages compression method selection. .Bl -tag -width indent -compact .It Cm none no compression .It Cm speed compression method focusing on compression speed .It Cm size compression method focusing on size reduction .It Cm auto default compression method on the current system .El .It Fl \-prefix Ar path Game installation .Ar path setting. .Pp This option accepts an absolute .Ar path only. .It Fl \-package Cm arch | Cm deb | Cm gentoo | Cm egentoo Generated package type selection. .Bl -tag -width indent-two -compact .It Cm arch .No .pkg.tar package (Arch Linux) .It Cm deb .No .deb package (Debian, Ubuntu) .It Cm gentoo .No .tbz2 package (Gentoo) .It Cm egentoo .No .tar package with an associated .ebuild (Gentoo) .El .It Fl \-icons Do not include game icons. .It Fl \-overwrite Replace packages if they already exist. .It Fl \-output\-dir Ar path Set the output directory for generated packages. .It Fl \-debug Op Ar N Set the debug level. If .Ar N is unspecified, defaults to 1. .It Fl \-no\-mtree Do not create .MTREE files in Arch Linux packages. As these files contain a hash of every file in the package, they may be quite long to create when dealing with big games. .It Fl \-tmpdir Ar path Set the directory used for temporary files storage. Default value is: .Ev TMPDIR .It Fl \-no\-free\-space\-check Do not check for free space. .It Fl \-show\-game\-script Only display the name of the game script to use, without running it. .It Fl \-config\-file Ar path Set the configuration file path. Default path is .Ar $XDG_CONFIG_HOME/play.it/config or .Ar $HOME/config/play.it/config if .Ev XDG_CONFIG_HOME is unset. Configuration file contains list of additional command-line parameters that will be passed to .Nm as if they were given on the command-line. .It Fl \-list\-packages Print the list of packages to build. .It Fl \-list\-requirements Print the list of commands required to build packages from the given archive. .It Fl \-list\-available\-scripts Print the list of game scripts available on this system. .It Fl \-list\-supported\-games Print the list of supported games. Warning: this operation can take several minutes. .El .Sh ENVIRONMENT .Bl -tag -width DS .It Ev PLAYIT_LIB2 If set, overrides the provided version of .Pa libplayit2.sh (and its supported games) to a local copy for development. (default: /usr/share/games/play.it/libplayit2.sh) .It Ev TMPDIR Default place where temporary files are processed. (default: /tmp) .It Ev XDG_CONFIG_HOME Used in the configuration file default path. .El .Sh FILES .Bl -tag -width DS .It Ar $XDG_CONFIG_HOME/play.it/config Default path to the configuration file. This file contains a list of additional command-line parameters that will be passed to .Nm as if they were given on the command-line. .El .Sh COMPRESSION .Ss Arch Linux When building packages for Arch Linux (with \-\-package arch): .Bl -tag -compact .It \-\-compression speed Use zstd compression with the flag \-\-fast=1. .It \-\-compression size Use zstd compression with the flag \-19. .It \-\-compression auto Unsupported option. .El .Ss Debian When building packages for Debian (with \-\-package deb): .Bl -tag -compact .It \-\-compression speed Use gzip compression. .It \-\-compression size Use xz compression. .It \-\-compression auto Rely on the default dpkg-deb behaviour. This behaviour can be controlled using the environment variables DPKG_DEB_THREADS_MAX, DPKG_DEB_COMPRESSOR_TYPE and DPKG_DEB_COMPRESSOR_LEVEL. See dpkg-deb(1) for more details on how these can be used. .El .Ss Gentoo When building packages for Gentoo (with \-\-package gentoo or \-\-package egentoo) .Bl -tag -compact .It \-\-compression speed Use gzip compression. .It \-\-compression size Use bzip2 compression. .It \-\-compression auto Rely on the default ebuild behaviour. This behaviour can be controlled using the environment variables BINPKG_COMPRESS and BINPKG_COMPRESS_FLAGS. See make.conf(5) for more details on how these can be used. .El man/fr/man6/play.it.60000644000000000000000000001542413120060140013237 0ustar rootroot.Dd $Mdocdate$ .Dt ./play.it 6 .Os .\" La section .Sh NAME est obligatoire pour la mise en page correcte du .\" manuel. Super pratique pour les traductions… >< .Sh NAME .Nm ./play.it .Nd Installateur de jeux commerciaux sans DRM .Sh SYNOPSIS .Nm .Ar archive .Op Fl \-checksum Cm md5 | Cm none .Op Fl \-compression Cm none | Cm speed | Cm size | Cm auto .Op Fl \-prefix Ar chemin .Op Fl \-package Cm arch | Cm deb | Cm gentoo | Cm egentoo .Op Fl \-no\-icons .Op Fl \-overwrite .Op Fl \-output\-dir Ar chemin .Op Fl \-debug Op Ar N .Op Fl \-no\-mtree .Op Fl \-tmpdir Ar chemin .Op Fl \-no\-free\-space\-check .Op Fl \-show\-game\-script .Op Fl \-config\-file Ar chemin .Op Fl \-list\-packages .Op Fl \-list\-requirements .Op Fl \-list\-available\-scripts .Op Fl \-list\-supported\-games .Sh DESCRIPTION .Nm est un logiciel libre qui automatise la construction de paquets natifs pour plusieurs familles de distributions à partir d’installateurs sans DRM pour une collection de jeux commerciaux. Les paquets ainsi générés s’installent ensuite en utilisant les outils standard fournis par la distribution. .Pp Des jeux natifs pour Linux sont gérés, mais aussi des jeux initialement développés pour d’autres plate-formes grâce à des outils comme .Xr wine 1 , Xr dosbox 1 et Xr scummvm 1 . .Pp Les jeux sont installés globalement, ce qui évite la duplication sur les systèmes ayant plusieurs utilisateurs. Les emplacements des sauvegardes, des paramètres, des mods, des fichiers temporaires et des backups sont standardisés selon les directives de l’XDG Base Directory. .Pp Empaqueter les jeux simplifie les mises-à-jour, les désinstallations et la gestion des dépendances, y compris celle des dépendances obsolètes, dans le cas où un jeu nécessite une version particulière de l’une d’elles. .Ss Options .Bl -tag -width DS .It Fl \-checksum Cm md5 | Cm none Détermine la méthode de vérification de l’intégrité de l’archive. .Bl -tag -width indent -compact .It Cm md5 vérification de la somme md5 .It Cm none pas de vérification .El .It Fl \-compression Cm none | Cm speed | Cm size | Cm auto Détermine la méthode de compression des paquets générés. .Bl -tag -width indent -compact .It Cm none pas de compression .It Cm speed méthode de compression mettant lʼaccent sur la rapidité .It Cm size méthode de compression mettant lʼaccent sur la réduction de taille .It Cm auto méthode de compression par défaut du système actuel .El .It Fl \-prefix Ar chemin Détermine le .Ar chemin d’installation du jeu. .Pp Cette option n’accepte qu’un .Ar chemin absolu. .It Fl \-package Cm arch | Cm deb | Cm gentoo | Cm egentoo Détermine le type de paquets générés. .Bl -tag -width indent-two -compact .It Cm arch paquets .pkg.tar (Arch Linux) .It Cm deb paquets .deb (Debian, Ubuntu) .It Cm gentoo paquets .tbz2 (Gentoo) .It Cm egentoo paquets .tar accompagné d’un .ebuild (Gentoo) .El .It Fl \-no\-icons Ne pas inclure les icônes du jeu. .It Fl \-overwrite Remplace les paquets s’ils existent déjà. .It Fl \-output\-dir Ar chemin Détermine le dossier dans lequel seront placés les paquets générés. .It Fl \-debug Op Ar N Détermine le niveau de débuggage. .Ar N a la valeur 1 par défaut s’il n’est pas précisé. .It Fl \-no\-mtree Ne crée pas de fichier .MTREE lors de la création de paquets pour Arch Linux. Ces fichiers contenant un hash de tous les autres fichiers du paquet, ils peuvent être relativement long à calculer lors du traitement de jeux volumineux. .It Fl \-tmpdir Ar chemin Définit le répertoire utilisé pour le stockage des fichiers temporaire. La valeur par défaut est : .Ev TMPDIR .It Fl \-no\-free\-space\-check Ne pas tester l’espace libre disponible. .It Fl \-show\-game\-script Affiche seulement le nom du script du jeu à utiliser sans l’exécuter. .It Fl \-config\-file Ar chemin Définit le fichier de configuration à utiliser. Le chemin par défaut est .Ar $XDG_CONFIG_HOME/play.it/config ou .Ar $HOME/.config/play.it/config si .Ev XDG_CONFIG_HOME n’est pas définie. Le fichier de configuration contient une liste de paramètres qui seront passés à .Nm comme s’ils lui avaient été donnés sur la ligne de commande. .It Fl \-list\-packages Affiche la liste des paquets à construire. .It Fl \-list\-requirements Affiche la liste des commandes nécessaire à la construction de paquets à partir de lʼarchive donnée. .It Fl \-list\-available\-scripts Affiche la liste des scripts de prise en charge de jeux disponibles sur ce système. .It Fl \-list\-supported\-games Affiche la liste des jeux pris en charge. Attention : cette opération peut prendre plusieurs minutes. .El .Sh ENVIRONNEMENT .Bl -tag -width DS .It Ev PLAYIT_LIB2 Remplace la version fournie de .Pa libplayit2.sh (et des jeux qu’elle supporte) par une version de développement locale. (défaut : /usr/share/games/play.it/libplayit2.sh) .It Ev TMPDIR Emplacement par défaut où les fichiers temporaires sont traités. (défaut : /tmp) .It Ev XDG_CONFIG_HOME Détermine l’emplacement par défaut du fichier de configuration. .El .Sh FICHIERS .Bl -tag -width DS .It Ar $XDG_CONFIG_HOME/play.it/config Emplacement par défaut du fichier de configuration. Celui-ci contient une liste de paramètres qui seront passés à .Nm comme s’ils lui avaient été donnés sur la ligne de commande. .El .Sh COMPRESSION .Ss Arch Linux Lors de la construction de paquets pour Arch Linux (avec \-\-package arch): .Bl -tag -compact .It \-\-compression speed Utilisation de la méthode de compression zstd avec lʼoption \-\-fast=1. .It \-\-compression size Utilisation de la méthode de compression zstd avec lʼoption \-19. .It \-\-compression auto Cette option nʼest pas prise en charge. .El .Ss Debian Lors de la construction de paquets pour Debian (avec \-\-package deb): .Bl -tag -compact .It \-\-compression speed Utilisation de la méthode de compression gzip. .It \-\-compression size Utilisation de la méthode de compression xz. .It \-\-compression auto Utilisation du comportement par défaut de dpkg-deb. Ce comportement peut être contrôlé par les variables dʼenvironnement DPKG_DEB_THREADS_MAX, DPKG_DEB_COMPRESSOR_TYPE et DPKG_DEB_COMPRESSOR_LEVEL. Référez-vous à dpkg-deb(1) pour plus de détails sur la manière dont elles peuvent être utilisées. .El .Ss Gentoo Lors de la construction de paquets pour Gentoo (avec \-\-package gentoo ou \-\-package egentoo) .Bl -tag -compact .It \-\-compression speed Utilisation de la méthode de compression gzip. .It \-\-compression size Utilisation de la méthode de compression bz2. .It \-\-compression auto Utilisation du comportement par défaut de ebuild. Ce comportement peut être contrôlé par les variables dʼenvironnement BINPKG_COMPRESS et BINPKG_COMPRESS_FLAGS. Référez-vous à make.conf(5) pour plus de détails sur la manière dont elles peuvent être utilisées. .El src/00_header/00_license-and-version.sh0000644000000000000000000000367013120060140016563 0ustar rootroot### # Copyright © 2015, Antoine Le Gonidec # Copyright © 2016, Mopi # Copyright © 2017, Phil Morrell # Copyright © 2017, Jacek Szafarkiewicz # Copyright © 2018, VA # Copyright © 2018, Janeene "dawnmist" Beeforth # Copyright © 2018, BetaRays # Copyright © 2018, Andrey # Copyright © 2019, Emmanuel Gil Peyrot # Copyright © 2020, macaron # Copyright © 2020, Hoël Bézier # Copyright © 2020, quinao # Copyright © 2022, JashinYoda # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # This software is provided by the copyright holders and contributors "as is" # and any express or implied warranties, including, but not limited to, the # implied warranties of merchantability and fitness for a particular purpose # are disclaimed. In no event shall the copyright holder or contributors be # liable for any direct, indirect, incidental, special, exemplary, or # consequential damages (including, but not limited to, procurement of # substitute goods or services; loss of use, data, or profits; or business # interruption) however caused and on any theory of liability, whether in # contract, strict liability, or tort (including negligence or otherwise) # arising in any way out of the use of this software, even if advised of the # possibility of such damage. ### ### # common functions for ./play.it scripts # send your bug reports to contact@dotslashplay.it ### LIBRARY_VERSION=2.30.0 # shellcheck disable=SC2034 library_revision=20240725.1 src/10_common/00_common.sh0000644000000000000000000001027113120060140014242 0ustar rootroot# Apply minimal permissions on all files in the given paths: # - 755 for directories # - 644 for files # USAGE: set_standard_permissions $path[…] set_standard_permissions() { local path for path in "$@"; do # Error out if something does not look like a path to a directory if [ ! -d "$path" ]; then return 1 fi find "$path" -type d -exec chmod 755 '{}' + find "$path" -type f -exec chmod 644 '{}' + done } # convert files name to lower case # USAGE: tolower $dir[…] # CALLS: tolower_convmv tolower_shell tolower() { local directory for directory in "$@"; do if [ ! -d "$directory" ]; then error_not_a_directory "$directory" return 1 fi if command -v convmv > /dev/null; then tolower_convmv "$directory" else tolower_shell "$directory" fi done } # convert files name to lower case using convmv # USAGE: tolower_convmv $directory # RETURN: nothing # SIDE EFFECT: convert all file names in a given path to lowercase tolower_convmv() { debug_entering_function 'tolower_convmv' local directory convmv_options find_options directory="$1" convmv_options='-f utf8 --notest --lower -r' find_options='-mindepth 1 -maxdepth 1' # Hide convmv output unless $PLAYIT_OPTION_DEBUG is set to ≥ 1 local option_debug option_debug=$(option_value 'debug') if [ "$option_debug" -ge 1 ]; then # shellcheck disable=SC2086 find "$directory" $find_options -exec \ convmv $convmv_options {} + else # shellcheck disable=SC2086 find "$directory" $find_options -exec \ convmv $convmv_options {} + >/dev/null 2>&1 fi debug_leaving_function 'tolower_convmv' } # convert files name to lower case using pure shell # USAGE: tolower_shell $dir # CALLED BY: tolower tolower_shell() { local dir="$1" find "$dir" -depth -mindepth 1 | while read -r file; do newfile=$(dirname "$file")/$(basename "$file" | tr '[:upper:]' '[:lower:]') [ -e "$newfile" ] || mv "$file" "$newfile" done } # convert files name to upper case # USAGE: toupper $dir[…] # CALLS: toupper_convmv toupper_shell toupper() { local directory for directory in "$@"; do if [ ! -d "$directory" ]; then error_not_a_directory "$directory" return 1 fi if command -v convmv > /dev/null; then toupper_convmv "$directory" else toupper_shell "$directory" fi done } # convert files name to upper case using convmv # USAGE: toupper_convmv $directory # RETURN: nothing # SIDE EFFECT: convert all file names in a given path to uppercase toupper_convmv() { debug_entering_function 'toupper_convmv' local convmv_options find_options directory directory="$1" convmv_options='-f utf8 --notest --upper -r' find_options='-mindepth 1 -maxdepth 1' # Hide convmv output unless $PLAYIT_OPTION_DEBUG is set to ≥ 1 local option_debug option_debug=$(option_value 'debug') if [ "$option_debug" -ge 1 ]; then # shellcheck disable=SC2086 find "$directory" $find_options -exec \ convmv $convmv_options {} + else # shellcheck disable=SC2086 find "$directory" $find_options -exec \ convmv $convmv_options {} + >/dev/null 2>&1 fi debug_leaving_function 'toupper_convmv' } # convert files name to upper case using pure shell # USAGE: toupper_shell $dir # CALLED BY: toupper toupper_shell() { local dir="$1" find "$dir" -depth -mindepth 1 | while read -r file; do newfile="$(dirname "$file")/$(basename "$file" | tr '[:lower:]' '[:upper:]')" [ -e "$newfile" ] || mv "$file" "$newfile" done } # try to guess the tar implementation used for `tar` on the current system # USAGE: guess_tar_implementation guess_tar_implementation() { case "$(tar --version | head --lines 1)" in (*'GNU tar'*) PLAYIT_TAR_IMPLEMENTATION='gnutar' ;; (*'libarchive'*) PLAYIT_TAR_IMPLEMENTATION='bsdtar' ;; (*) error_unknown_tar_implementation return 1 ;; esac } # Return the MIME type of a given file # USAGE: file_type $file # RETURNS: the MIME type, as a string file_type() { local file file="$1" local file_type file_type=$(file --brief --dereference --mime-type "$file") # Everything behind the first ";" is removed, # so "application/x-executable; charset=binary" # would be returned as "application/x-executable". file_type=$(printf '%s' "$file_type" | cut --delimiter=';' --fields=1) printf '%s' "$file_type" } src/10_common/10_checks.sh0000644000000000000000000000770213120060140014220 0ustar rootroot# check that the target directory is on a case-sensitive filesystem # USAGE: check_directory_is_case_sensitive $tested_directory # RETURNS: 0 if case-sensitive, 1 if case-insensitive check_directory_is_case_sensitive() { # the first argument should be a writable directory local tested_directory tested_directory="$1" if [ ! -d "$tested_directory" ]; then error_not_a_directory "$tested_directory" return 1 fi if [ ! -w "$tested_directory" ]; then error_not_writable "$tested_directory" return 1 fi # check if "a" and "A" are created as distinct files, or as a single one # tests are done in an inner temporary directory to avoid messing up with existing files local inner_temp_directory inner_temp_directory=$(mktemp --directory --tmpdir="$tested_directory") touch "${inner_temp_directory}/a" touch "${inner_temp_directory}/A" local files_count files_count=$(find "$inner_temp_directory" -mindepth 1 -maxdepth 1 -iname a | wc --lines) rm --recursive "$inner_temp_directory" # if "a" and "A" were created as distinct files, the file system is case-sensitive # if they were created as a single file, it is case-insensitive case "$files_count" in (1) return 1 ;; (2) return 0 ;; (*) # we did not get an expected numeric value (1 or 2), let us assume we do not want to work with this directory # it might be better to actually throw some kind of explicit error with a message here return 1 ;; esac } # check that the target directory is on a filesystem supporting UNIX permissions # USAGE: check_directory_supports_unix_permissions $tested_directory # RETURNS: 0 if has support for UNIX permissions, 1 if has no support for UNIX permissions check_directory_supports_unix_permissions() { local tested_directory tested_directory="$1" if [ ! -d "$tested_directory" ]; then error_not_a_directory "$tested_directory" return 1 fi if [ ! -w "$tested_directory" ]; then error_not_writable "$tested_directory" return 1 fi # Change permissions on a file, and check it has an actual effect # Tests are done in an inner temporary directory to avoid messing up with existing files local inner_temp_directory tested_temp_file file_permissions_expected file_permissions_real inner_temp_directory=$(mktemp --directory --tmpdir="$tested_directory") tested_temp_file="${inner_temp_directory}/a" touch "$tested_temp_file" for file_permissions_expected in '600' '700'; do chmod "$file_permissions_expected" "$tested_temp_file" 2>/dev/null || true file_permissions_real=$(stat --printf='%a' "$tested_temp_file") if [ "$file_permissions_real" != "$file_permissions_expected" ]; then return 1 fi done rm --recursive "$inner_temp_directory" } # check that the target directory is on a filesystem supporting executable files # USAGE: check_directory_supports_executable_files $tested_directory # RETURNS: 0 if has support for executable files, 1 otherwise check_directory_supports_executable_files() { local tested_directory tested_directory="$1" if [ ! -d "$tested_directory" ]; then error_not_a_directory "$tested_directory" return 1 fi findmnt --first-only --list --options +noexec --target "$tested_directory" >/dev/null case $? in (0) return 1 ;; (1) return 0 ;; (*) # Something unexpected happened, we do not want to deal with it return 1 ;; esac } # Check if the current process is running under the root account # USAGE: check_is_running_as_root # RETURN: 0 if running as root, 1 otherwise check_is_running_as_root() { local current_user_id current_user_id=$(id --user) test "$current_user_id" -eq 0 } # assert a variable is not empty and display an error message otherwise # USAGE: assert_not_empty $variable_name $calling_function assert_not_empty() { local variable_name calling_function variable_name="$1" calling_function="$2" local variable_value variable_value=$(get_value "$variable_name") if [ -z "$variable_value" ]; then error_empty_string "$calling_function" "$variable_name" return 1 fi } src/10_common/10_compatibility-level.sh0000644000000000000000000000505113120060140016731 0ustar rootroot# Get the compatibility level that has been requested # USAGE: compatibility_level # RETURN: the requested compatibility level, # defaulting to the current library version compatibility_level() { local compatibility_level if [ -n "${PLAYIT_COMPATIBILITY_LEVEL:-}" ]; then compatibility_level="$PLAYIT_COMPATIBILITY_LEVEL" elif [ -n "${target_version:-}" ]; then ## FIXME: Find a way to show a deprecation warning *once*, not repeating it each time this function is called. ## The warning should be shown when the compatibility level is set to a value ≥ 2.26. compatibility_level="$target_version" else ## The bugfix version number is trimmed, to get a value respecting the expected "major.minor" format. compatibility_level="${LIBRARY_VERSION%.*}" fi # Check the validity of the compatibility level format local regexp regexp='^[1-9][0-9]*\.[0-9]\+$' if ! printf '%s' "$compatibility_level" | grep --quiet --regexp="$regexp"; then error_invalid_compatibility_level "$compatibility_level" return 1 fi printf '%s' "$compatibility_level" } # Check the compatibility level against a given version # USAGE: compatibility_level_is_at_least $minimum_version # RETURN: 0 if the compatibility level is equal or superior to the given level, # 1 if the compatibility level is inferior to the given level compatibility_level_is_at_least() { local minimum_version compatibility_level minimum_version="$1" if ! compatibility_level=$(compatibility_level); then ## Stop the script execution if the compatibility level is not set to a valid value. ## This needs to be done explicitly because this functions is called from tests. exit 1 fi # Compare major version fields local minimum_version_major compatibility_level_major minimum_version_major=$(printf '%s' "$minimum_version" | cut --delimiter='.' --fields=1) compatibility_level_major=$(printf '%s' "$compatibility_level" | cut --delimiter='.' --fields=1) if [ "$compatibility_level_major" -lt "$minimum_version_major" ]; then return 1 elif [ "$compatibility_level_major" -gt "$minimum_version_major" ]; then return 0 else # Compare minor version fields local minimum_version_minor compatibility_level_minor minimum_version_minor=$(printf '%s' "$minimum_version" | cut --delimiter='.' --fields=2) compatibility_level_minor=$(printf '%s' "$compatibility_level" | cut --delimiter='.' --fields=2) if [ "$compatibility_level_minor" -lt "$minimum_version_minor" ]; then return 1 elif [ "$compatibility_level_minor" -ge "$minimum_version_minor" ]; then return 0 fi fi } src/10_common/10_filters.sh0000644000000000000000000000115413120060140014423 0ustar rootroot# Clean-up the output of a command returning a list # USAGE: … | list_clean # RETURN: a list including only unique lines list_clean() { ## - remove leading and trailing spaces ## - sort the list and merge duplicate entries ## - remove empty lines, ignore errors on empty output sed 's/^\s*//;s/\s*$//' | \ sort --unique | \ grep --invert-match --regexp='^$' || true } # Clean-up the output of a command printing a snippet # USAGE: … | snippet_clean # RETURN: a snippet indented with tabulations snippet_clean() { ## - convert the series of 4 spaces to tabulations sed --regexp-extended 's/( ){4}/\t/g' } src/10_common/10_variables-manipulation.sh0000644000000000000000000000110013120060140017410 0ustar rootroot# Expand the given variable name and print its value # USAGE: get_value $variable_name # RETURN: the variable value get_value() { local variable_name variable_name="$1" eval printf -- '%s' '"${'"${variable_name}"':-}"' } # Check if the given varible is unset or set to an empty value. # USAGE: variable_is_empty $variable_name # RETURN: 0 if the variable is unset or empty, 1 if it is set to a non empty value variable_is_empty() { local variable_name variable_name="$1" local variable_value variable_value=$(get_value "$variable_name") test -z "$variable_value" } src/10_common/20_temporary-directories.sh0000644000000000000000000001015013120060140017304 0ustar rootroot# Set the path to the temporary directory # This path is exported as $PLAYIT_WORKDIR. # USAGE: set_temp_directories set_temp_directories() { debug_entering_function 'set_temp_directories' # Get path to temporary directory local temporary_directory_path temporary_directory_path=$(temporary_directory_path) # Check that the given path is valid for temporary files storage temporary_directory_checks "$temporary_directory_path" # Generate a directory with a unique name for the current instance local game_id game_id=$(game_id) PLAYIT_WORKDIR=$(mktemp --directory --tmpdir="$temporary_directory_path" "${game_id}.XXXXX") ## Ensure that we are always using an absolute path for PLAYIT_WORKDIR, ## to avoid issues when a relative path has been used with the ./play.it --tmpdir option. PLAYIT_WORKDIR=$(realpath "$PLAYIT_WORKDIR") debug_creating_directory "$PLAYIT_WORKDIR" export PLAYIT_WORKDIR debug_leaving_function 'set_temp_directories' } # Print the path to the temporary directory to use # USAGE: temporary_directory_path temporary_directory_path() { option_value 'tmpdir' } # Run checks on the path used for temporary files # USAGE: temporary_directory_checks $temporary_directory_path temporary_directory_checks() { local temporary_directory_path temporary_directory_path="$1" # Skip all checks if no file operation is going to take place local noop_option noop_option_value for noop_option in \ 'help' \ 'list-available-scripts' \ 'list-packages' \ 'list-requirements' \ 'list-supported-games' \ 'show-game-script' \ 'version' do noop_option_value=$(option_value "$noop_option") ## Setting a default value to 0 allows calling this function even when some of these options are not set, ## making it easier to use in unit tests. if [ "${noop_option_value:-0}" -eq 1 ]; then return 0 fi done # Check that the given path is an existing directory if [ ! -d "$temporary_directory_path" ]; then error_temporary_path_not_a_directory "$temporary_directory_path" return 1 fi # Check that the given path is writable if [ ! -w "$temporary_directory_path" ]; then error_temporary_path_not_writable "$temporary_directory_path" return 1 fi # Check that the given path is on a case-sensitive filesystem if ! check_directory_is_case_sensitive "$temporary_directory_path"; then error_temporary_path_not_case_sensitive "$temporary_directory_path" return 1 fi # Check that the given path is on a filesystem with support for UNIX permissions if ! check_directory_supports_unix_permissions "$temporary_directory_path"; then error_temporary_path_no_unix_permissions "$temporary_directory_path" return 1 fi # Check that the given path has allow use of the execution bit if ! check_directory_supports_executable_files "$temporary_directory_path"; then error_temporary_path_noexec "$temporary_directory_path" return 1 fi # Check that there is enough free space under the given path local option_free_space_check option_free_space_check=$(option_value 'free-space-check') if [ "$option_free_space_check" -eq 1 ]; then temporary_directory_check_free_space "$temporary_directory_path" fi } # Check that there is enough free space under the given path # USAGE: temporary_directory_check_free_space $path # RETURN: 0 if there is enough space, # 1 otherwise temporary_directory_check_free_space() { local path path="$1" # Compute the total size of the archives contents local free_space_required archives_list archive archive_size free_space_required=0 archives_list=$(archives_used_list) for archive in $archives_list; do archive_size=$(archive_size "$archive") free_space_required=$((free_space_required + archive_size)) done # Compare the free space available with twice the archives contents size local df_options free_space_available free_space_required_double df_options='--block-size=1K --output=avail' free_space_available=$(env --ignore-environment df $df_options "$path" | tail --lines=1) free_space_required_double=$((free_space_required * 2)) if [ "$free_space_available" -lt "$free_space_required_double" ]; then error_temporary_path_not_enough_space "$path" return 1 fi } src/10_common/90_messages.sh0000644000000000000000000003747613120060140014612 0ustar rootroot# Information - All the packages are already built, there is no need to run a new build # USAGE: info_all_packages_already_built info_all_packages_already_built() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Tous les paquets sont déjà présents, il nʼy a pas besoin de les reconstruire.\n' ;; ('en'|*) message='All the packages are already built, there is no need to run a new build.\n' ;; esac print_message 'info' "$message" } # Error - The given path is not a file # USAGE: error_not_a_file $path error_not_a_file() { local path path="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='"%s" nʼest pas un fichier valide.\n' ;; ('en'|*) message='"%s" is not a valid file.\n' ;; esac print_message 'error' "$message" \ "$path" } # Error - The available tar implementation is not supported # USAGE: error_unknown_tar_implementation error_unknown_tar_implementation() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La version de tar présente sur ce système nʼest pas reconnue.\n' message="$message"'./play.it ne peut utiliser que GNU tar ou bsdtar.\n' message="$message"'Merci de signaler cette erreur sur notre outil de gestion de bugs : %s\n' ;; ('en'|*) message='The tar implementation on this system wasnʼt recognized.\n' message="$message"'./play.it can only use GNU tar or bsdtar.\n' message="$message"'Please report this issue in our bug tracker: %s\n' ;; esac print_message 'error' "$message" \ "$PLAYIT_BUG_TRACKER_URL" } # Error - The given path is not a directory # USAGE: error_not_a_directory $path error_not_a_directory() { local path path="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='"%s" nʼest pas un répertoire.\n' ;; ('en'|*) message='"%s" is not a directory.\n' ;; esac print_message 'error' "$message" \ "$path" } # Error - The given path is not writable # USAGE: error_not_writable $path error_not_writable() { local path path="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='"%s" nʼest pas accessible en écriture.\n' ;; ('en'|*) message='"%s" is not writable.\n' ;; esac print_message 'error' "$message" \ "$path" } # Error - The function has been given an unexpected empty string # USAGE: error_empty_string $calling_function $variable_name error_empty_string() { local calling_function variable_name calling_function="$1" variable_name="$2" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La variable "%s" de la fonction "%s" ne doit pas être vide.\n' message="$message"'Merci de signaler cette erreur sur notre outil de gestion de bugs : %s\n' ;; ('en'|*) message='Variable "%s" in function "%s" can not be empty.\n' message="$message"'Please report this issue in our bug tracker: %s\n' ;; esac print_message 'error' "$message" \ "$variable_name" \ "$calling_function" \ "$PLAYIT_BUG_TRACKER_URL" } # Error - A required command is missing # USAGE: error_unavailable_command $function $required_command error_unavailable_command() { local function required_command function="$1" required_command="$2" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La commande "%s" nʼest pas disponible, mais elle est requise par la fonction "%s".\n' message="$message"'Merci de signaler cette erreur sur notre outil de gestion de bugs : %s\n' ;; ('en'|*) message='"%s" command is not available, but it is required by function "%s".\n' message="$message"'Please report this issue in our bug tracker: %s\n' ;; esac print_message 'error' "$message" \ "$required_command" \ "$function" \ "$PLAYIT_BUG_TRACKER_URL" } # Error - The calling script is not compatible with the provided library version # USAGE: error_incompatible_versions error_incompatible_versions() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Ce script nʼest pas compatible avec la version fournie de la bibliothèque ./play.it.\n' message="$message"'La bibliothèque utilisée fournit actuellement la version %s, mais le script attend une version ≥ %s et < %s.\n' ;; ('en'|*) message='This script is not compatible with the provided ./play.it library version.\n' message="$message"'The library in use currently provides version %s, but the script expects a version ≥ %s and < %s.\n' ;; esac local compatibility_level version_major_minimum compatibility_level=$(compatibility_level) version_major_minimum=$(printf '%s' "$compatibility_level" | cut --delimiter='.' --fields=1) print_message 'error' "$message" \ "$LIBRARY_VERSION" \ "$compatibility_level" \ "$((version_major_minimum + 1)).0" } # Error - The wrapper has been called with no archive argument # USAGE: error_archive_missing_from_arguments error_archive_missing_from_arguments() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Aucune archive nʼa été fournie sur la ligne de commande.\n' ;; ('en'|*) message='No archive has been provided on the command line.\n' ;; esac print_message 'error' "$message" } # Error - No game script has been found for the given archive # USAGE: error_no_script_found_for_archive $archive error_no_script_found_for_archive() { local archive archive="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Impossible de trouver un script pour le fichier %s\n' ;; ('en'|*) message='Could not find script for file %s\n' ;; esac print_message 'error' "$message" \ "$archive" } # Error - A variable is spanning multiple lines # USAGE: error_variable_multiline $variable_name error_variable_multiline() { local variable_name variable_name="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La valeur %s sʼétend sur plusieurs lignes, ce qui nʼest pas autorisé.\n' ;; ('en') message='%s value is spanning multiple lines, but this is not allowed.\n' ;; esac print_message 'error' "$message" \ "$variable_name" } # Error - A type-restricted function has been called on the wrong application type # USAGE: error_application_wrong_type $function_name $application_type error_application_wrong_type() { local function_name application_type function_name="$1" application_type="$2" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='%s ne peut pas être appelée sur les applications utilisant le type "%s".\n' ;; ('en') message='%s can not be called on applications using "%s" type.\n' ;; esac print_message 'error' "$message" \ "$function_name" \ "$application_type" } # Error - The directory for temporary files storage does not exist # USAGE: error_temporary_path_not_a_directory $temporary_directory_path error_temporary_path_not_a_directory() { local temporary_directory_path temporary_directory_path="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le chemin demandé pour stocker les fichiers temporaires nʼexiste pas' message="$message"', ou nʼest pas un répertoire : %s\n' message="$message"'Un chemin alternatif peut être fourni avec --tmpdir.\n' ;; ('en'|*) message='The path set for temporary files storage does not exist' message="$message"', or is not a directory: %s\n' message="$message"'An alternative path can be provided with --tmpdir.\n' ;; esac print_message 'error' "$message" \ "$temporary_directory_path" } # Error - The directory for temporary files storage has no write access # USAGE: error_temporary_path_not_writable $temporary_directory_path error_temporary_path_not_writable() { local temporary_directory_path temporary_directory_path="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le chemin demandé pour stocker les fichiers temporaires nʼest pas accessible en écriture : %s\n' message="$message"'Un chemin alternatif peut être fourni avec --tmpdir.\n' ;; ('en'|*) message='The path set for temporary files storage has no write access: %s\n' message="$message"'An alternative path can be provided with --tmpdir.\n' ;; esac print_message 'error' "$message" \ "$temporary_directory_path" } # Error - The directory for temporary files storage is not case-sensitive # USAGE: error_temporary_path_not_case_sensitive $temporary_directory_path error_temporary_path_not_case_sensitive() { local temporary_directory_path temporary_directory_path="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le chemin demandé pour stocker les fichiers temporaires est sur un système de fichiers qui nʼest pas sensible à la casse des noms de fichiers : %s\n' message="$message"'Un chemin alternatif peut être fourni avec --tmpdir.\n' ;; ('en'|*) message='The path set for temporary files storage is on a case-insensitive file system: %s\n' message="$message"'An alternative path can be provided with --tmpdir.\n' ;; esac print_message 'error' "$message" \ "$temporary_directory_path" } # Error - The directory for temporary files storage has no support for UNIX permissions # USAGE: error_temporary_path_no_unix_permissions $temporary_directory_path error_temporary_path_no_unix_permissions() { local temporary_directory_path temporary_directory_path="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le chemin demandé pour stocker les fichiers temporaires ne prend pas en charge les permissions UNIX : %s\n' message="$message"'Un chemin alternatif peut être fourni avec --tmpdir.\n' ;; ('en'|*) message='The path set for temporary files storage has no support for UNIX permissions: %s\n' message="$message"'An alternative path can be provided with --tmpdir.\n' ;; esac print_message 'error' "$message" \ "$temporary_directory_path" } # Error - The directory for temporary files storage is mounted with noexec # USAGE: error_temporary_path_noexec $temporary_directory_path error_temporary_path_noexec() { local temporary_directory_path temporary_directory_path="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le chemin demandé pour stocker les fichiers temporaires ne permet pas la création de fichiers exécutables : %s\n' message="$message"'Un chemin alternatif peut être fourni avec --tmpdir.\n' ;; ('en'|*) message='The path set for temporary files storage forbid the creation of executable files: %s\n' message="$message"'An alternative path can be provided with --tmpdir.\n' ;; esac print_message 'error' "$message" \ "$temporary_directory_path" } # Error - There is not enough free space in the directory for temporary files storage # USAGE: error_temporary_path_not_enough_space $temporary_directory_path error_temporary_path_not_enough_space() { local temporary_directory_path temporary_directory_path="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le chemin demandé pour stocker les fichiers temporaires ne dispose pas dʼassez dʼespace libre : %s\n' message="$message"'Un chemin alternatif peut être fourni avec --tmpdir.\n' message="$message"'Cette vérification de lʼespace libre peut aussi être contournée avec --no-free-space-check.\n' ;; ('en'|*) message='The path set for temporary files storage has not enough free space: %s\n' message="$message"'An alternative path can be provided with --tmpdir.\n' message="$message"'This free space check can also be disabled using --no-free-space-check.\n' ;; esac print_message 'error' "$message" \ "$temporary_directory_path" } # Error - A mandatory variable is not set # USAGE: error_missing_variable $variable_name error_missing_variable() { local variable_name variable_name="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La variable suivante est requise, mais elle nʼa pas été définie ou sa valeur est nulle : %s\n' ;; ('en'|*) message='The following variable is mandatory, but it is unset or its value is null: %s\n' ;; esac print_message 'error' "$message" \ "$variable_name" } # Error - ./play.it should not be run with the root account # USAGE: error_run_as_root error_run_as_root() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='./play.it ne doit pas être exécuté par le compte root.\n' ;; ('en'|*) message='./play.it should not be executed by the root account.\n' ;; esac print_message 'error' "$message" } # Error - The current archive identifier uses an invalid format. # USAGE: error_current_archive_format_invalid $archive error_current_archive_format_invalid() { local archive archive="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Lʼidentifiant dʼarchive "%s" ne respecte pas le format attendu: ARCHIVE_BASE_xxx\n' ;; ('en'|*) message='The archive identifier "%s" does not follow the expected format: ARCHIVE_BASE_xxx\n' ;; esac print_message 'error' "$message" \ "$archive" } # Error - The current package identifier uses an invalid format. # USAGE: error_current_package_format_invalid $package error_current_package_format_invalid() { local package package="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Lʼidentifiant de paquet "%s" ne respecte pas le format attendu: PKG_xxx\n' ;; ('en'|*) message='The package identifier "%s" does not follow the expected format: PKG_xxx\n' ;; esac print_message 'error' "$message" \ "$package" } # Error - The current package identifier is not included in the list of packages to build. # USAGE: error_current_package_not_in_list $package error_current_package_not_in_list() { local package package="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Lʼidentifiant de paquet "%s" ne fait pas partie de la liste de paquets à construire.\n' ;; ('en'|*) message='The package identifier "%s" is not included in the list of packages that should be built.\n' ;; esac print_message 'error' "$message" \ "$package" } # Error - The compatibility level of the current game script is set to an invalid value # USAGE: error_invalid_compatibility_level $compatibility_level error_invalid_compatibility_level() { local compatibility_level compatibility_level="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le niveau de compatibilité du script courant est défini à une valeur invalide : "%s"\n' message="$message"'Le format attendu est "version_majeure.version_mineure", par exemple "2.29".\n' ;; ('en'|*) message='The compatibility level of the current script is set to an invalid value: "%s"\n' message="$message"'The expected format is "major_version.minor_version", for example "2.29".\n' ;; esac print_message 'error' "$message" \ "$compatibility_level" } src/10_context/00_environment_archives.sh0000644000000000000000000000267713120060140017411 0ustar rootroot# Set the identifier of the current archive # USAGE: set_current_archive $archive set_current_archive() { local archive archive="$1" # Check that the identifier uses the expected ARCHIVE_BASE_xxx format. local regexp regexp='^ARCHIVE_BASE\(_[0-9A-Z]\+\)*_[0-9]\+$' if ! printf '%s' "$archive" | grep --quiet --regexp="$regexp"; then # Allow the old ARCHIVE_xxx format when targeting a compatibility level < 2.21. if ! compatibility_level_is_at_least '2.21'; then regexp='^ARCHIVE\(_[0-9A-Z]\+\)\+$' if ! printf '%s' "$archive" | grep --quiet --regexp="$regexp"; then error_current_archive_format_invalid "$archive" return 1 fi else error_current_archive_format_invalid "$archive" return 1 fi fi export PLAYIT_CONTEXT_ARCHIVE="$archive" } # Print the identifier of the current archive. # USAGE: current_archive # RETURNS: the current archive identifier, # or an empty string if no archive is set current_archive() { # To ensure backwards-compatibility, the legacy variable should have a higher priority than the modern one. # Otherwise the ability to set the context using $ARCHIVE from game scripts would be lost as soon as the library calls set_current_archive. local archive archive="${ARCHIVE:-}" if \ [ -n "$archive" ] && \ compatibility_level_is_at_least '2.27' then warning_context_legacy_archive fi if [ -z "$archive" ]; then archive="${PLAYIT_CONTEXT_ARCHIVE:-}" fi printf '%s' "$archive" } src/10_context/00_environment_packages.sh0000644000000000000000000000427513120060140017357 0ustar rootroot# Set the identifier of the current package # USAGE: set_current_package $package set_current_package() { local package package="$1" # Check that the identifier uses the expected PKG_xxx format. local regexp regexp='^PKG\(_[0-9A-Z]\+\)\+$' if ! printf '%s' "$package" | grep --quiet --regexp="$regexp"; then error_current_package_format_invalid "$package" return 1 fi # Check that the identifier is included in the list of packages to build. if ! package_is_included_in_packages_list "$package"; then error_current_package_not_in_list "$package" return 1 fi export PLAYIT_CONTEXT_PACKAGE="$package" } # Set the identifier of the default package # USAGE: set_default_package $package set_default_package() { local package package="$1" # Check that the identifier uses the expected PKG_xxx format. local regexp regexp='^PKG\(_[0-9A-Z]\+\)\+$' if ! printf '%s' "$package" | grep --quiet --regexp="$regexp"; then error_current_package_format_invalid "$package" return 1 fi # Check that the identifier is included in the list of packages to build. if ! package_is_included_in_packages_list "$package"; then error_current_package_not_in_list "$package" return 1 fi export PLAYIT_CONTEXT_PACKAGE_DEFAULT="$package" } # Print the identifier of the current package. # USAGE: current_package # RETURN: the current package identifier current_package() { # To ensure backwards-compatibility, the legacy variable should have a higher priority than the modern one. # Otherwise the ability to set the context using $PKG from game scripts would be lost as soon as the library calls set_current_package. local package package="${PKG:-}" if \ [ -n "$package" ] && \ compatibility_level_is_at_least '2.27' then warning_context_legacy_package fi if [ -z "$package" ]; then package="${PLAYIT_CONTEXT_PACKAGE:-}" fi # If no package context is explicitly set, set it to the first package in the list of packages to build. if [ -z "$package" ]; then package=$(default_package) fi printf '%s' "$package" } # Print the identifier of the default package. # USAGE: default_package # RETURN: the default package identifier default_package() { printf '%s' "$PLAYIT_CONTEXT_PACKAGE_DEFAULT" } src/10_context/10_names.sh0000644000000000000000000000731113120060140014253 0ustar rootroot# Print the suffix of the identifier of the current archive. # USAGE: current_archive_suffix # RETURN: the current archive identifier suffix including the leading underscore, # or an empty string if no archive is set current_archive_suffix() { local archive archive=$(current_archive) printf '%s' "${archive#ARCHIVE_BASE}" } # Print the suffix of the identifier of the current package. # USAGE: current_package_suffix # RETURN: the current package identifier suffix including the leading underscore current_package_suffix() { local package package=$(current_package) printf '%s' "${package#PKG}" } # Print the name of the variable containing the context-specific value of the given variable # Context priority order is the following one: # - archive-specific # - package-specific # - default # - empty # USAGE: context_name $variable_name # RETURN: the name of the variable containing the context-specific value, # or an empty string context_name() { local variable_name variable_name="$1" local current_archive_suffix current_package_suffix current_archive_suffix=$(current_archive_suffix) current_package_suffix=$(current_package_suffix) # Try to find an archive-specific value for the given variable. local context_name_archive if [ -n "$current_archive_suffix" ]; then context_name_archive=$(context_name_archive "$variable_name") if [ -n "$context_name_archive" ]; then printf '%s' "$context_name_archive" return 0 fi fi # Try to find a package-specific value for the given variable. local context_name_package if [ -n "$current_package_suffix" ] ; then context_name_package=$(context_name_package "$variable_name") if [ -n "$context_name_package" ]; then printf '%s' "$context_name_package" return 0 fi fi # Check if the base variable value is set. if ! variable_is_empty "$variable_name"; then printf '%s' "$variable_name" return 0 fi # If no value has been found for the given variable, an empty string is returned. } # Print the name of the variable containing the archive-specific value of the given variable # USAGE: context_name_archive $variable_name # RETURN: the name of the variable containing the archive-specific value, # or an empty string context_name_archive() { local variable_name variable_name="$1" local current_archive_suffix current_archive_suffix=$(current_archive_suffix) # Return early if no archive context is set if [ -z "$current_archive_suffix" ]; then return 0 fi local current_archive_name while [ -n "$current_archive_suffix" ]; do current_archive_name="${variable_name}${current_archive_suffix}" if ! variable_is_empty "$current_archive_name"; then printf '%s' "$current_archive_name" return 0 fi current_archive_suffix="${current_archive_suffix%_*}" done # If no value has been found for the given variable, an empty string is returned. } # Print the name of the variable containing the package-specific value of the given variable # USAGE: context_name_package $variable_name # RETURN: the name of the variable containing the package-specific value, # or an empty string context_name_package() { local variable_name variable_name="$1" local current_package_suffix current_package_suffix=$(current_package_suffix) # Return early if no package context is set if [ -z "$current_package_suffix" ]; then return 0 fi local current_package_name while [ -n "$current_package_suffix" ]; do current_package_name="${variable_name}${current_package_suffix}" if ! variable_is_empty "$current_package_name"; then printf '%s' "$current_package_name" return 0 fi current_package_suffix="${current_package_suffix%_*}" done # If no value has been found for the given variable, an empty string is returned. } src/10_context/20_values.sh0000644000000000000000000000105313120060140014445 0ustar rootroot# Print the context-sensitive value for the given variable # Context priority order is the following one: # - archive-specific # - package-specific # - default # - empty # USAGE: context_value $variable_name # RETURN: the context-sensitive value of the given variable, # or an empty string context_value() { local variable_name variable_name="$1" local context_name context_name=$(context_name "$variable_name") # Return early if this variable has no set value. if [ -z "$context_name" ]; then return 0 fi get_value "$context_name" } src/10_games/10_scripts.sh0000644000000000000000000000571613120060140014256 0ustar rootroot# Returns a list of directories to scan for game scripts # USAGE: games_list_sources # RETURNS: A list of directories, separated by line breaks games_list_sources() { # Include the current user game scripts collections local user_collections_basedir user_collections_basedir="${XDG_DATA_HOME:=$HOME/.local/share}/play.it/games" if [ -d "$user_collections_basedir" ]; then find "$user_collections_basedir" -mindepth 1 -maxdepth 1 -type d | sort fi # Include the system-provided game scripts collections local system_prefix system_collections_basedir for system_prefix in \ '/usr/local/share/games' \ '/usr/local/share' \ '/usr/share/games' \ '/usr/share' do system_collections_basedir="${system_prefix}/play.it/games" if [ -d "$system_collections_basedir" ]; then find "$system_collections_basedir" -mindepth 1 -maxdepth 1 -type d | sort fi done } # List all available game scripts # USAGE: games_list_scripts_all # RETURN: a list of available game scripts, # separated by line breaks games_list_scripts_all() { local games_sources games_sources=$(games_list_sources) # Return early if no game script could be found if [ -z "$games_sources" ]; then return 0 fi while read -r games_collection; do find "$games_collection" -name play-\*.sh | sort done <<- EOF $(printf '%s' "$games_sources") EOF } # List the game scripts providing support for the given archive name # USAGE: games_find_scripts_for_archive $archive_name # RETURN: a list of game scripts, # separated by line breaks games_find_scripts_for_archive() { local archive_name archive_name="$1" ## xargs return code is ignored, ## to prevent a failure state if no available script has support for the given archive. set +o errexit games_list_scripts_all | xargs grep \ --files-with-matches \ --regexp="^ARCHIVE_[0-9A-Z_]\\+=['\"]${archive_name}['\"]" set -o errexit } # Print the path to the first game script with support with the given archive name # USAGE: games_find_script_for_archive $archive_name # RETURN: the path to a single game script games_find_script_for_archive() { local archive_name archive_name="$1" local scripts_list scripts_list=$(games_find_scripts_for_archive "$archive_name") printf '%s' "$scripts_list" | head --lines=1 } # Print the version of the current game script # USAGE: script_version # RETURN: the script version string, # throw an error if it is not set, # throw an error if it does not follow the expected format script_version() { local version_string version_string="${script_version:-}" # Throw an error if the script version is not set if [ -z "$version_string" ]; then error_missing_variable 'script_version' return 1 fi # Throw an error if the version string does not use the expected format local regexp regexp='^[0-9]\{8\}\.[0-9]\+$' if ! printf '%s' "$version_string" | grep --quiet --regexp="$regexp"; then error_invalid_version_string "$version_string" return 1 fi printf '%s' "$version_string" } src/10_games/20_games.sh0000644000000000000000000000220613120060140013653 0ustar rootroot# List the games supported by the current script # USAGE: games_list_supported # RETURN: a list of games, # separated by line breaks, # using the following format for each line: # game-id | Game name games_list_supported() { local archives_list archives_list=$(archives_return_list) local archive game_id game_name for archive in $archives_list; do set_current_archive "$archive" game_id=$(game_id) game_name=$(game_name) printf '%s | %s\n' "$game_id" "$game_name" done | sort --unique } # List all games supported by the available scripts # USAGE: games_list_supported_all # RETURN: a list of games, # separated by line breaks, # using the following format for each line: # game-id | Game name games_list_supported_all() { local scripts_list scripts_list=$(games_list_scripts_all) local available_threads available_threads=$(nproc) ## Passing the --list-supported-games switch is not required, ## because $PLAYIT_OPTION_LIST_SUPPORTED_GAMES is already set. printf '%s' "$scripts_list" | \ xargs --delimiter='\n' --max-args=1 --max-procs="$available_threads" sh | \ sort --unique } src/10_games/90_messages.sh0000644000000000000000000000174213120060140014401 0ustar rootroot# Error - $script_version does not follow the expected format # USAGE: error_invalid_version_string $version_string error_invalid_version_string() { local version_string version_string="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La valeur suivante de $script_version ne respecte pas le format attendu : %s\n' message="$message"'Le format correct est "YYYYMMDD.N", avec "YYYYMMDD" la date de dernière édition du script, et "N" un nombre incrémenté si le script est édité plusieurs fois dans la même journée.\n' ;; ('en'|*) message='The following $script_version value does not follow the expected format: %s\n' message="$message"'The correct format is "YYYYMMDD.N", with "YYYYMMDD" the date of the last edition of the script, and "N" a number incremented if the script is edited multiple times at the same date.\n' ;; esac print_message 'error' "$message" \ "$version_string" } src/10_help/00_help.sh0000644000000000000000000002677613120060140013363 0ustar rootroot# display full usage instructions # USAGE: help help() { local script_name script_name=$(basename "$0") # print general usage instructions local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') if [ "$script_name" = 'play.it' ]; then message='\nUtilisation : %s ARCHIVE [OPTION]…\n\n' else message='\nUtilisation : %s [OPTION]… [ARCHIVE]\n\n' fi ;; ('en'|*) if [ "$script_name" = 'play.it' ]; then message='\nUsage: %s ARCHIVE [OPTION]…\n\n' else message='\nUsage: %s [OPTION]… [ARCHIVE]\n\n' fi ;; esac print_message 'info' "$message" \ "$script_name" # print details about options usage print_message 'info' '%s\n\n' \ 'OPTIONS' help_checksum help_compression help_prefix help_package help_icons help_overwrite help_output_dir help_debug help_no_mtree help_tmpdir help_skipfreespacecheck help_configfile help_listpackages help_listrequirements help_listavailablescripts help_listsupportedgames # do not print a list of supported archives if called throught the "play.it" wrapper script if [ "$script_name" = 'play.it' ]; then help_show_game_script return 0 fi # print list of supported archives print_message 'info' '%s\n\n' \ 'ARCHIVE' messages_language=$(messages_language) case "$messages_language" in ('fr') message='Ce script reconnaît les archives suivantes :\n' ;; ('en'|*) message='This script can work on the following archives:\n' ;; esac print_message 'info' "$message" # shellcheck disable=SC2046 information_archives_list $(archives_return_list) return 0 } # display --checksum option usage # USAGE: help_checksum help_checksum() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tChoix de la méthode de vérification dʼintégrité de lʼarchive\n\n' message="$message"'\t%s\tvérification via md5sum\n' # md5 message="$message"'\t%s\tpas de vérification\n\n' # none ;; ('en'|*) message='\tArchive integrity verification method selection\n\n' message="$message"'\t%s\tmd5sum verification\n' # md5 message="$message"'\t%s\tno verification\n\n' # none ;; esac print_message 'info' '--%s %s\n' \ 'checksum' \ 'md5|none' print_message 'info' "$message" \ 'md5' \ 'none' } # Display --compression option usage # USAGE: help_compression help_compression() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tChoix de la méthode de compression des paquets générés\n' message="$message"'\t%s\tpas de compression\n' message="$message"'\t%s\tméthode de compression mettant lʼaccent sur la rapidité\n' message="$message"'\t%s\tméthode de compression mettant lʼaccent sur la réduction de taille\n' message="$message"'\t%s\tméthode de compression par défaut du système actuel\n\n' ;; ('en'|*) message='\tGenerated packages compression method selection\n' message="$message"'\t%s\tno compression\n' message="$message"'\t%s\tcompression method focusing on compression speed\n' message="$message"'\t%s\tcompression method focusing on size reduction\n' message="$message"'\t%s\tdefault compression method on the current system\n\n' ;; esac print_message 'info' '--%s %s\n' \ 'compression' \ 'none|speed|size|auto' print_message 'info' "$message" \ 'none' \ 'speed' \ 'size' \ 'auto' } # display --prefix option usage # USAGE: help_prefix help_prefix() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tChoix du chemin dʼinstallation du jeu\n\n' message="$message"'\tCette option accepte uniquement un chemin absolu.\n\n' ;; ('en'|*) message='\tGame installation path setting\n\n' message="$message"'\tThis option accepts an absolute path only.\n\n' ;; esac print_message 'info' '--%s %s\n' \ 'prefix' \ 'path' print_message 'info' "$message" } # display --package option usage # USAGE: help_package help_package() { ## TODO: "egentoo" format should be included. local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tChoix du type de paquet à construire\n\n' message="$message"'\t%s\tpaquet .pkg.tar (Arch Linux)\n' message="$message"'\t%s\tpaquet .deb (Debian, Ubuntu)\n' message="$message"'\t%s\tpaquet .tbz2 (Gentoo)\n\n' ;; ('en'|*) message='\tGenerated package type selection\n\n' message="$message"'\t%s\t.pkg.tar package (Arch Linux)\n' message="$message"'\t%s\t.deb package (Debian, Ubuntu)\n' message="$message"'\t%s\t.tbz2 package (Gentoo)\n\n' ;; esac print_message 'info' '--%s %s\n' \ 'prefix' \ 'arch|deb|gentoo' print_message 'info' "$message" \ 'arch' \ 'deb' \ 'gentoo' } # display --no-icons option usage # USAGE: help_icons help_icons() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tNe pas inclure les icônes du jeu.\n\n' ;; ('en'|*) message='\tDo not include game icons.\n\n' ;; esac print_message 'info' '--%s\n' \ 'no-icons' print_message 'info' "$message" } # display --overwrite option usage # USAGE: help_overwrite help_overwrite() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tRemplace les paquets si ils existent déjà.\n\n' ;; ('en'|*) message='\tReplace packages if they already exist.\n\n' ;; esac print_message 'info' '--%s\n' \ 'overwrite' print_message 'info' "$message" } # display --output-dir option usage # USAGE: help_output_dir help_output_dir() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tDéfinit le répertoire de destination des paquets générés.\n\n' ;; ('en'|*) message='\tSet the output directory for generated packages.\n\n' ;; esac print_message 'info' '--%s\n' \ 'output-dir' print_message 'info' "$message" } # display --debug option usage # USAGE: help_debug help_debug() { local messages_language message messages_language=$(messages_language) # shellcheck disable=SC2050 if [ %%DEBUG_DISABLED%% -eq 1 ]; then case "$messages_language" in ('fr') message='\tLe debug a été désactivé lors de la compilation.\n' message="$message"'\tCette option est sans effet.\n\n' ;; ('en'|*) message='\tDebug was disabled at compile-time.\n' message="$message"'\tThis option has no effect.\n\n' ;; esac else case "$messages_language" in ('fr') message='\tDéfinit le niveau de debug. Il vaut 1 par défaut.\n\n' ;; ('en'|*) message='\tSet the debug level. Default is 1.\n\n' ;; esac fi print_message 'info' '--%s %s\n' \ 'debug' \ 'N' print_message 'info' "$message" } # display --show-game-script option usage # USAGE: help_show_game_script help_show_game_script() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tAffiche uniquement le chemin vers le script à utiliser, sans le lancer.\n\n' ;; ('en'|*) message='\tOnly displays the name of the script to use, without running it.\n\n' ;; esac print_message 'info' '--%s\n' \ 'show-game-script' print_message 'info' "$message" } # display --no-mtree option usage # USAGE: help_no_mtree help_no_mtree() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tNe crée pas de fichier .MTREE pour les paquets Arch Linux.\n\n' ;; ('en'|*) message='\tDo not make .MTREE file in Arch Linux packages\n\n' ;; esac print_message 'info' '--%s\n' \ 'no-mtree' print_message 'info' "$message" } # Display --tmpdir option usage # USAGE: help_tmpdir help_tmpdir() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tDéfinit le répertoire utilisé pour le stockage des fichiers temporaire.\n' message="$message"'\tLa valeur par défaut est : %s\n\n' ;; ('en'|*) message='\tSet the directory used for temporary files storage.\n' message="$message"'\tDefault value is: %s\n\n' ;; esac print_message 'info' '--%s\n' \ 'tmpdir' print_message 'info' "$message" \ "${TMPDIR:-/tmp}" } # Display --no-free-space-check option usage # USAGE: help_skipfreespacecheck help_skipfreespacecheck() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tNe pas tester lʼespace libre disponible.\n\n' ;; ('en'|*) message='\tDo not check for free space.\n\n' ;; esac print_message 'info' '--%s\n' \ 'no-free-space-check' print_message 'info' "$message" } # Display --config-file option usage # USAGE: help_configfile help_configfile() { local config_file_path config_file_path=$(configuration_file_default_path) local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tDéfinit le fichier de configuration à utiliser.\n' message="$message"'\tLe fichier par défaut est : %s\n\n' ;; ('en'|*) message='\tSet the configuration file to use.\n' message="$message"'\tDefault file is: %s\n\n' ;; esac print_message 'info' '--%s\n' \ 'config-file' print_message 'info' "$message" \ "$config_file_path" } # Display --list-packages option usage # USAGE: help_listpackages help_listpackages() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tAffiche la liste des paquets à construire.\n\n' ;; ('en'|*) message='\tPrint the list of packages to build.\n\n' ;; esac print_message 'info' '--%s\n' \ 'list-packages' print_message 'info' "$message" } # Display --list-requirements option usage # USAGE: help_listrequirements help_listrequirements() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tAffiche la liste des commandes nécessaire à la construction de paquets à partir de lʼarchive donnée.\n\n' ;; ('en'|*) message='\tPrint the list of commands required to build packages from the given archive.\n\n' ;; esac print_message 'info' '--%s\n' \ 'list-requirements' print_message 'info' "$message" } # Display --list-available-scripts option usage # USAGE: help_listavailablescripts help_listavailablescripts() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tAffiche la liste des scripts de prise en charge de jeux disponibles sur ce système.\n\n' ;; ('en'|*) message='\tPrint the list of game scripts available on this system.\n\n' ;; esac print_message 'info' '--%s\n' \ 'list-available-scripts' print_message 'info' "$message" } # Display --list-supported-games option usage # USAGE: help_listsupportedgames help_listsupportedgames() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tAffiche la liste des jeux pris en charge.\n' message="$message"'\tAttention : cette opération peut prendre plusieurs minutes.\n\n' ;; ('en'|*) message='\tPrint the list of supported games.\n' message="$message"'\tWarning: this operation can take several minutes.\n\n' ;; esac print_message 'info' '--%s\n' \ 'list-supported-games' print_message 'info' "$message" } src/10_messages/00_language.sh0000644000000000000000000000060613120060140015055 0ustar rootroot# Get the language set for messages in the current environment # USAGE: messages_language # RETURN: the language code, as a two-letters code, or the fallback value "C" messages_language() { # Relying on the "locale" command prevents the need to query the values of multiple variables, # and handle the priority between them. locale | sed --silent 's/LC_MESSAGES="\?\([^_"]*\).*/\1/p' } src/10_messages/10_wrapper.sh0000644000000000000000000001264313120060140014757 0ustar rootroot# Print a localized message # USAGE: print_message $level $message $extra_values[…] print_message() { local level # Valid levels are: # - error # - warning # - warning_once # - info # - info_once # Unknown levels will be handled similar to "info". level="$1" shift 1 case "$level" in ('error') print_message_error "$@" ;; ('warning') print_message_warning "$@" ;; ('warning_once') print_message_warning_once "$@" ;; ('info_once') print_message_info_once "$@" ;; ('info'|*) print_message_info "$@" ;; esac } # Print a localized error message # USAGE: print_message_error $message $extra_values[…] print_message_error() ( local message # The message is a format string, any included "%s" will be replaced by the values passed as extra arguments. # See printf(1) for details on the sequences that can be used in the format string. message="$1" shift 1 ## Since this is called from a subshell, this should not trigger unwanted output redirections. exec 1>&2 local messages_language error_string messages_language=$(messages_language) case "$messages_language" in ('fr') error_string='Erreur :' ;; ('en'|*) error_string='Error:' ;; esac printf '\n\033[1;31m%s\033[0m\n' "$error_string" ## Silence ShellCheck false-positive ## Don't use variables in the printf format string. Use printf "..%s.." "$foo". # shellcheck disable=SC2059 printf -- "$message" "$@" ) # Print a localized warning message # USAGE: print_message_warning $message $extra_values[…] print_message_warning() { local message # The message is a format string, any included "%s" will be replaced by the values passed as extra arguments. # See printf(1) for details on the sequences that can be used in the format string. message="$1" shift 1 local messages_language warning_string messages_language=$(messages_language) case "$messages_language" in ('fr') warning_string='Avertissement :' ;; ('en'|*) warning_string='Warning:' ;; esac printf '\n\033[1;33m%s\033[0m\n' "$warning_string" ## Silence ShellCheck false-positive ## Don't use variables in the printf format string. Use printf "..%s.." "$foo". # shellcheck disable=SC2059 printf -- "$message" "$@" } # Print a localized warning message, only if it has not already been shown # USAGE: print_message_warning_once $message $extra_values[…] print_message_warning_once() { # Return early if this message has already been shown. if message_has_been_shown_already "$@"; then return 0 fi print_message_warning "$@" # Prevent this message from being shown again. messages_already_shown_add "$@" } # Print a localized information message # USAGE: print_message_info $message $extra_values[…] print_message_info() { local message # The message is a format string, any included "%s" will be replaced by the values passed as extra arguments. # See printf(1) for details on the sequences that can be used in the format string. message="$1" shift 1 ## Silence ShellCheck false-positive ## Don't use variables in the printf format string. Use printf "..%s.." "$foo". # shellcheck disable=SC2059 printf -- "$message" "$@" } # Print a localized information message, only if it has not already been shown # USAGE: print_message_info_once $message $extra_values[…] print_message_info_once() { # Return early if this message has already been shown. if message_has_been_shown_already "$@"; then return 0 fi print_message_info "$@" # Prevent this message from being shown again. messages_already_shown_add "$@" } # Compute a unique message indentifier from a message function name and its arguments # USAGE: message_identifier $message $extra_values[…] message_identifier() { local message_function message_function="$1" shift 1 printf '%s' "$message_function" if [ $# -ge 1 ]; then printf ':%s' "$@" fi } # Print the path to a file listing messages already shown # USAGE: messages_already_shown_file # RETURN: the path to the file listing the messages, # or an empty string if such a path can not be computed yet messages_already_shown_file() { # The list of shown messages can only be used when PLAYIT_WORKDIR is already set. if [ -z "${PLAYIT_WORKDIR:-}" ]; then return 0 fi printf '%s/messages-shown' "$PLAYIT_WORKDIR" } # Add a message to the list of already shown ones. # USAGE: messages_already_shown_add $message $extra_values[…] messages_already_shown_add() { local messages_list messages_list=$(messages_already_shown_file) # The list of shown messages can only be used when PLAYIT_WORKDIR is already set. if [ -z "$messages_list" ]; then return 0 fi local message_identifier message_identifier=$(message_identifier "$@") printf '%s\n' "$message_identifier" >> "$messages_list" } # Check if a message has already been shown. # USAGE: messages_has_been_shown_already $message $extra_values[…] # RETURN: 0 if the given message has already been shown, # 1 if there is no messages list yet, # 1 otherwise message_has_been_shown_already() { local messages_list messages_list=$(messages_already_shown_file) # The list of shown messages can only be used when PLAYIT_WORKDIR is already set. if [ -z "$messages_list" ]; then return 1 fi # Return early if there is no list of shown messages yet. if [ ! -e "$messages_list" ]; then return 1 fi local message_identifier message_identifier=$(message_identifier "$@") grep --quiet --fixed-strings --line-regexp --regexp="$message_identifier" "$messages_list" } src/20_configuration/00_configuration.sh0000644000000000000000000000344513120060140017206 0ustar rootroot# Load options values from the configuration file. # USAGE: load_configuration_file $config_file_path load_configuration_file() { local config_file_path config_file_path="$1" # Default configuration file may not exist, ignoring this then. if [ ! -f "$config_file_path" ]; then return 0 fi # Parse the configuration file. local arguments arguments='' while read -r line; do case $line in ('#'*) # Ignore commented lines. ;; (*) arguments="$arguments $line" ;; esac done <<- EOF $(cat "$config_file_path") EOF parse_arguments_default $arguments } # Print the configuration file path. # USAGE: find_configuration_file $arguments[…] find_configuration_file() { local arguments_string arguments_string=$(getopt_arguments_cleanup "$@") eval set -- "$arguments_string" local config_file_path config_file_path='' # Override the default path if another one has been specified. while [ $# -gt 0 ]; do case "$1" in ('--config-file') config_file_path="$2" break ;; esac shift 1 done if \ [ -n "$config_file_path" ] \ && [ ! -f "$config_file_path" ] then error_config_file_not_found "$config_file_path" return 1 fi # Fall back on the default path if no custom one is set. if [ -z "$config_file_path" ]; then config_file_path=$(configuration_file_default_path) fi printf '%s' "$config_file_path" } # Print the default path to the configuration file # USAGE: configuration_file_default_path # RETURN: a string representing a path to a file, # no check of the file actual existence is done configuration_file_default_path() { local configuration_path if variable_is_empty 'XDG_CONFIG_HOME'; then configuration_path="${HOME}/.config" else configuration_path="$XDG_CONFIG_HOME" fi printf '%s/play.it/config' "$configuration_path" } src/20_configuration/10_arguments.sh0000644000000000000000000001025713120060140016344 0ustar rootroot# Clean up the command-line parameters using getopt # USAGE: getopt_arguments_cleanup $arguments[…] # RETURN: a standardized parameters string getopt_arguments_cleanup() { getopt \ --name 'play.it' \ --shell 'sh' \ --options '' \ --longoptions 'help' \ --longoptions 'list-available-scripts' \ --longoptions 'list-packages' \ --longoptions 'list-supported-games' \ --longoptions 'overwrite' \ --longoptions 'show-game-script' \ --longoptions 'version' \ --longoptions 'no-free-space-check' \ --longoptions 'no-icons' \ --longoptions 'no-mtree' \ --longoptions 'config-file:' \ --longoptions 'checksum:' \ --longoptions 'compression:' \ --longoptions 'output-dir:' \ --longoptions 'package:' \ --longoptions 'prefix:' \ --longoptions 'tmpdir:' \ --longoptions 'debug::' \ -- "$@" } # Parse the arguments given to the game script or wrapper # WARNING: Options that are already set from the user environment are not overriden. # USAGE: parse_arguments $arguments[…] parse_arguments() { local arguments_string arguments_string=$(getopt_arguments_cleanup "$@") eval set -- "$arguments_string" local option_name option_variable option_value while [ $# -gt 0 ]; do unset option_name option_variable option_value case "$1" in ( \ '--help' | \ '--list-available-scripts' | \ '--list-packages' | \ '--list-requirements' | \ '--list-supported-games' | \ '--overwrite' | \ '--show-game-script' | \ '--version' \ ) option_name=$(printf '%s' "$1" | sed 's/^--//') option_update "$option_name" 1 ;; ( \ '--no-free-space-check' | \ '--no-icons' | \ '--no-mtree' \ ) option_name=$(printf '%s' "$1" | sed 's/^--no-//') option_update "$option_name" 0 ;; ('--config-file') # Skip this argument, has it should have already been handled by find_configuration_file. shift 1 ;; ('--debug') option_name=$(printf '%s' "$1" | sed 's/^--//') option_variable=$(option_variable "$option_name") option_value="$2" shift 1 case "$option_value" in ([0-9]) option_update "$option_name" "$option_value" ;; (*) option_update "$option_name" 1 ;; esac ;; ( \ '--checksum' | \ '--compression' | \ '--output-dir' | \ '--package' | \ '--prefix' | \ '--tmpdir' \ ) option_name=$(printf '%s' "$1" | sed 's/^--//') option_variable=$(option_variable "$option_name") option_value="$2" shift 1 option_update "$option_name" "$option_value" ;; ('--') # Skip the "--" separator. ;; (*) if [ -f "$1" ]; then SOURCE_ARCHIVE_PATH="$1" SOURCE_ARCHIVE_NAME=$(basename "$SOURCE_ARCHIVE_PATH") PLAYIT_ARCHIVES_PATH_BASE=$(dirname "$SOURCE_ARCHIVE_PATH") export SOURCE_ARCHIVE_PATH SOURCE_ARCHIVE_NAME PLAYIT_ARCHIVES_PATH_BASE else error_not_a_file "$1" return 1 fi ;; esac shift 1 done } # Parse the arguments set through the configuration file # WARNING: Only a subset of the supported options are allowed here. # USAGE: parse_arguments $arguments[…] parse_arguments_default() { local arguments_string arguments_string=$(getopt_arguments_cleanup "$@") eval set -- "$arguments_string" local option_name option_value while [ $# -gt 0 ]; do unset option_name option_value case "$1" in ('--overwrite') option_name=$(printf '%s' "$1" | sed 's/^--//') option_update_default "$option_name" 1 ;; ( \ '--no-free-space-check' | \ '--no-icons' | \ '--no-mtree' \ ) option_name=$(printf '%s' "$1" | sed 's/^--//') option_update_default "$option_name" 0 ;; ('--debug') option_name=$(printf '%s' "$1" | sed 's/^--//') option_value="$2" shift 1 case "$option_value" in ([0-9]) option_update_default "$option_name" "$option_value" ;; (*) option_update_default "$option_name" 1 ;; esac ;; ( \ '--checksum' | \ '--compression' | \ '--output-dir' | \ '--package' | \ '--prefix' | \ '--tmpdir' \ ) option_name=$(printf '%s' "$1" | sed 's/^--//') option_value="$2" shift 1 option_update_default "$option_name" "$option_value" ;; esac shift 1 done } src/20_configuration/20_options.sh0000644000000000000000000002473413120060140016040 0ustar rootroot# Set default values for all options # USAGE: options_init_default options_init_default() { # Try to guess the desired package format based on the host system local default_package_format ## Get system codename. local host_system if [ -e '/etc/os-release' ]; then host_system=$(grep '^ID=' '/etc/os-release' | cut --delimiter='=' --fields=2) elif command -v lsb_release >/dev/null 2>&1; then host_system=$(lsb_release --id --short | tr '[:upper:]' '[:lower:]') fi ## Set the most appropriate package type for the current system. case "${host_system:-}" in ( \ 'debian' | \ 'ubuntu' | \ 'linuxmint' | \ 'handylinux' \ ) default_package_format='deb' ;; ( \ 'arch' | \ 'artix' | \ 'manjaro' | \ 'manjarolinux' | \ 'endeavouros' | \ 'steamos' \ ) default_package_format='arch' ;; ( \ 'gentoo' \ ) default_package_format='gentoo' ;; esac # Using a direct export call here instead of relying on option_update_default # massively improves performances when running a lot of ./play.it calls, # like what is done by the --list-supported-games option. export \ PLAYIT_DEFAULT_OPTION_CHECKSUM='md5' \ PLAYIT_DEFAULT_OPTION_COMPRESSION='none' \ PLAYIT_DEFAULT_OPTION_OUTPUT_DIR="$PWD" \ PLAYIT_DEFAULT_OPTION_PACKAGE="${default_package_format:-deb}" \ PLAYIT_DEFAULT_OPTION_PREFIX='/usr' \ PLAYIT_DEFAULT_OPTION_TMPDIR="${TPMDIR:-/tmp}" \ PLAYIT_DEFAULT_OPTION_FREE_SPACE_CHECK=1 \ PLAYIT_DEFAULT_OPTION_ICONS=1 \ PLAYIT_DEFAULT_OPTION_MTREE=1 \ PLAYIT_DEFAULT_OPTION_DEBUG=0 \ PLAYIT_DEFAULT_OPTION_HELP=0 \ PLAYIT_DEFAULT_OPTION_LIST_AVAILABLE_SCRIPTS=0 \ PLAYIT_DEFAULT_OPTION_LIST_PACKAGES=0 \ PLAYIT_DEFAULT_OPTION_LIST_REQUIREMENTS=0 \ PLAYIT_DEFAULT_OPTION_LIST_SUPPORTED_GAMES=0 \ PLAYIT_DEFAULT_OPTION_OVERWRITE=0 \ PLAYIT_DEFAULT_OPTION_SHOW_GAME_SCRIPT=0 \ PLAYIT_DEFAULT_OPTION_VERSION=0 } # Get the name of the variable used to store the value of the given option # USAGE: option_variable $option_name # RETURN: the variable name option_variable() { local option_name option_name="$1" # The environment variable used to store an option is derived from its name: # - replace "-" with "_" # - convert to uppercase # - prepend "PLAYIT_OPTION_" # As an exemple, the value of the option "output-dir" would be stored in the following variable: # PLAYIT_OPTION_OUTPUT_DIR printf 'PLAYIT_OPTION_%s' "$( printf '%s' "$option_name" | \ sed 's/-/_/g' | \ tr '[:lower:]' '[:upper:]' )" } # Get the name of the variable used to store the default value of the given option # USAGE: option_variable_default $option_name # RETURN: the variable name option_variable_default() { local option_name option_name="$1" # The environment variable used to store an option is derived from its name: # - replace "-" with "_" # - convert to uppercase # - prepend "PLAYIT_DEFAULT_OPTION_" # As an exemple, the default value of the option "output-dir" would be stored in the following variable: # PLAYIT_DEFAULT_OPTION_OUTPUT_DIR printf 'PLAYIT_DEFAULT_OPTION_%s' "$( printf '%s' "$option_name" | \ sed 's/-/_/g' | \ tr '[:lower:]' '[:upper:]' )" } # Update the value of the given option # USAGE: option_update $option_name $option_value option_update() { local option_name option_value option_name="$1" option_value="$2" local option_variable option_variable=$(option_variable "$option_name") export $option_variable="$option_value" } # Update the default value of the given option # USAGE: option_update_default $option_name $option_value option_update_default() { local option_name option_value option_name="$1" option_value="$2" local option_variable option_variable=$(option_variable_default "$option_name") export $option_variable="$option_value" } # Get the value of the given option # USAGE: option_value $option_name # RETURN: the option value option_value() { local option_name option_name="$1" local option_variable option_value option_variable=$(option_variable "$option_name") option_value=$(get_value "$option_variable") if [ -n "$option_value" ]; then printf '%s' "$option_value" return 0 fi # If no value is explicitly set, return the default one option_variable=$(option_variable_default "$option_name") get_value "$option_variable" } # Check the validity of all options # USAGE: options_validity_check # RETURN: nothing if all option values are valid, # throw an error otherwise options_validity_check() { local option_checksum option_checksum=$(option_value 'checksum') case "$option_checksum" in ('md5'|'none') ;; (*) error_option_invalid 'checksum' "$option_checksum" return 1 ;; esac local option_compression option_compression=$(option_value 'compression') case "$option_compression" in ('none'|'speed'|'size'|'auto') ;; (*) error_option_invalid 'compression' "$option_compression" return 1 ;; esac local option_debug option_debug=$(option_value 'debug') case "$option_debug" in ([0-9]) ;; (*) error_option_invalid 'debug' "$option_debug" return 1 ;; esac local option_free_space_check option_free_space_check=$(option_value 'free-space-check') case "$option_free_space_check" in (0|1) ;; (*) error_option_invalid 'free-space-check' "$option_free_space_check" return 1 ;; esac local option_help option_help=$(option_value 'help') case "$option_help" in (0|1) ;; (*) error_option_invalid 'help' "$option_help" return 1 ;; esac local option_icons option_icons=$(option_value 'icons') case "$option_icons" in (0|1) ;; (*) error_option_invalid 'icons' "$option_icons" return 1 ;; esac local option_list_available_scripts option_list_available_scripts=$(option_value 'list-available-scripts') case "$option_list_available_scripts" in (0|1) ;; (*) error_option_invalid 'list-available-scripts' "$option_list_available_scripts" return 1 ;; esac local option_list_packages option_list_packages=$(option_value 'list-packages') case "$option_list_packages" in (0|1) ;; (*) error_option_invalid 'list-packages' "$option_list_packages" return 1 ;; esac local option_list_requirements option_list_requirements=$(option_value 'list-requirements') case "$option_list_requirements" in (0|1) ;; (*) error_option_invalid 'list-requirements' "$option_list_requirements" return 1 ;; esac local option_list_supported_games option_list_supported_games=$(option_value 'list-supported-games') case "$option_list_supported_games" in (0|1) ;; (*) error_option_invalid 'list-supported-games' "$option_list_supported_games" return 1 ;; esac local option_mtree option_mtree=$(option_value 'mtree') case "$option_mtree" in (0|1) ;; (*) error_option_invalid 'mtree' "$option_mtree" return 1 ;; esac local option_output_dir option_output_dir=$(option_value 'output-dir') # Check that the value of "output-dir" is a path to a writable directory. ## This check is not useful if a no-op option has been set. local noop_option noop_option_value noop_option_set noop_option_set=0 for noop_option in \ 'help' \ 'list-available-scripts' \ 'list-packages' \ 'list-requirements' \ 'list-supported-games' \ 'show-game-script' \ 'version' do noop_option_value=$(option_value "$noop_option") if [ "$noop_option_value" -eq 1 ]; then noop_option_set=1 break fi done if [ "$noop_option_set" = 0 ]; then local output_dir_path output_dir_path=$(printf '%s' "$option_output_dir" | sed "s#^~/#${HOME}/#") if [ ! -d "$output_dir_path" ]; then error_not_a_directory "$output_dir_path" return 1 fi if [ ! -w "$output_dir_path" ]; then error_not_writable "$output_dir_path" return 1 fi fi local option_overwrite option_overwrite=$(option_value 'overwrite') case "$option_overwrite" in (0|1) ;; (*) error_option_invalid 'overwrite' "$option_overwrite" return 1 ;; esac local option_package option_package=$(option_value 'package') case "$option_package" in ('arch'|'deb'|'gentoo'|'egentoo') ;; (*) error_option_invalid 'package' "$option_package" return 1 ;; esac local option_prefix option_prefix=$(option_value 'prefix') # Check that the value of "prefix" is representing an absolute path. if printf '%s' "$option_prefix" | grep --quiet --invert-match --regexp '^/'; then return 0 fi local option_show_game_script option_show_game_script=$(option_value 'show-game-script') case "$option_show_game_script" in (0|1) ;; (*) error_option_invalid 'show-game-script' "$option_show_game_script" return 1 ;; esac local option_tmpdir option_tmpdir=$(option_value 'tmpdir') # Check that the value of "tmpdir" is a path to a writable directory. ## This check is not useful if a no-op option has been set. local noop_option noop_option_value noop_option_set noop_option_set=0 for noop_option in \ 'help' \ 'list-available-scripts' \ 'list-packages' \ 'list-requirements' \ 'list-supported-games' \ 'show-game-script' \ 'version' do noop_option_value=$(option_value "$noop_option") if [ "$noop_option_value" -eq 1 ]; then noop_option_set=1 break fi done if [ "$noop_option_set" = 0 ]; then local tmpdir_path tmpdir_path=$(printf '%s' "$option_tmpdir" | sed "s#^~/#${HOME}/#") if [ ! -d "$tmpdir_path" ]; then error_not_a_directory "$tmpdir_path" return 1 fi if [ ! -w "$tmpdir_path" ]; then error_not_writable "$tmpdir_path" return 1 fi fi local option_version option_version=$(option_value 'version') case "$option_version" in (0|1) ;; (*) error_option_invalid 'version' "$option_version" return 1 ;; esac } # Check the compatibility of all set options # USAGE: options_compatibility_check # RETURN: nothing if all the current options are valid used together, # throw an error otherwise options_compatibility_check() { # Check the compatibility of --compression auto with the target package format. local option_compression option_compression=$(option_value 'compression') case "$option_compression" in ('none') local option_package option_package=$(option_value 'package') case "$option_package" in ('gentoo') # --compression none has not been implemented for Gentoo packages yet. error_incompatible_options 'package' 'compression' return 1 ;; esac ;; ('auto') local option_package option_package=$(option_value 'package') case "$option_package" in ('arch') # --compression auto has not been implemented for Arch Linux packages yet. error_incompatible_options 'package' 'compression' return 1 ;; esac ;; esac } src/20_configuration/90_messages.sh0000644000000000000000000000365713120060140016164 0ustar rootroot# Error - An invalid value has been provided for the given option # USAGE: error_option_invalid $option_name $option_value error_option_invalid() { local option_name option_value option_name="$1" option_value="$2" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='"%s" nʼest pas une valeur valide pour --%s.\n' ;; ('en'|*) message='"%s" is not a valid value for --%s.\n' ;; esac print_message 'error' "$message" \ "$option_value" \ "$option_name" } # Error - The configuration file could not be found # USAGE: error_config_file_not_found $config_file_path error_config_file_not_found() { local config_file_path config_file_path="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le fichier de configuration %s nʼa pas pu être trouvé.\n' ;; ('en'|*) message='The configuration file %s has not been found.\n' ;; esac print_message 'error' "$message" \ "$config_file_path" } # Error - Some options are currently set to incompatible values # USAGE: error_incompatible_options $option_name_1 $option_name_2 error_incompatible_options() { local option_name_1 option_name_2 option_name_1="$1" option_name_2="$2" local option_value_1 option_value_2 option_value_1=$(option_value "$option_name_1") option_value_2=$(option_value "$option_name_2") local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Les options suivantes ne sont pas compatibles :\n' message="$message"'\t--%s %s\n' message="$message"'\t--%s %s\n\n' ;; ('en'|*) message='The following options are not compatible:\n' message="$message"'\t--%s %s\n' message="$message"'\t--%s %s\n\n' ;; esac print_message 'error' "$message" \ "$option_name_1" \ "$option_value_1" \ "$option_name_2" \ "$option_value_2" } src/20_game/00_common.sh0000644000000000000000000000401513120060140013663 0ustar rootroot# print the id of the current game # USAGE: game_id # RETURN: the game id, limited to the characters set [-0-9a-z], # the id can not start nor end with an hyphen (-) character game_id() { # The game id might might be archive-specific local game_id game_id=$(context_value 'GAME_ID') # Check that the id fits the format restrictions if ! game_id_validity_check "$game_id"; then error_game_id_invalid "$game_id" return 1 fi printf '%s' "$game_id" } # Print the id of the current expansion # USAGE: expansion_id # RETURN: the expansion id, limited to the characters set [-0-9a-z], # the id can not start nor end with an hyphen (-) character, # an empty value is returned if no expansion id is set expansion_id() { # The expansion id might might be archive-specific local expansion_id expansion_id=$(context_value 'EXPANSION_ID') # Return early if no expansion id is set. if [ -z "$expansion_id" ]; then return 0 fi # Check that the id fits the format restrictions if ! game_id_validity_check "$expansion_id"; then error_expansion_id_invalid "$expansion_id" return 1 fi printf '%s' "$expansion_id" } # Check the validity of the given game (or expansion) id # USAGE: game_id_validity_check $id_string # RETURN: 0 if the id is valid, 1 if it is not game_id_validity_check() { local game_id game_id="$1" # Check that the given id: # - is limited to the characters set [-0-9a-z] # - does not start with an hyphen (-) character # - does not end with an hyphen (-) character printf '%s' "$game_id" | \ grep --quiet --regexp='^[0-9a-z][-0-9a-z]\+[0-9a-z]$' } # Print the display name of the current game # If an expansion name is set, it is included # USAGE: game_name # RETURN: the game name, for use in package description and menu entries game_name() { local game_name expansion_name game_name=$(context_value 'GAME_NAME') expansion_name=$(context_value 'EXPANSION_NAME') if [ -n "$expansion_name" ]; then printf '%s - %s' "$game_name" "$expansion_name" else printf '%s' "$game_name" fi } src/20_game/10_engine.sh0000644000000000000000000000155713120060140013651 0ustar rootroot# Print the name of the engine used by the current game # USAGE: game_engine # RETURN: the game engine, # or an empty string if none is set game_engine() { local game_engine game_engine="${GAME_ENGINE:-}" # Try to identify games using Unity3D if [ -z "$game_engine" ]; then local unity3d_name unity3d_name=$(unity3d_name) if [ -n "$unity3d_name" ]; then game_engine='unity3d' fi fi # Try to identify games using Unreal Engine 4 if [ -z "$game_engine" ]; then local unrealengine4_name unrealengine4_name=$(unrealengine4_name) if [ -n "$unrealengine4_name" ]; then game_engine='unrealengine4' fi fi # Try to identify games using Visionaire if [ -z "$game_engine" ]; then local visionaire_name visionaire_name=$(visionaire_name) if [ -n "$visionaire_name" ]; then game_engine='visionaire' fi fi printf '%s' "$game_engine" } src/20_game/90_messages.sh0000644000000000000000000000320613120060140014214 0ustar rootroot# Error - An invalid format is used for game id # USAGE: error_game_id_invalid $game_id error_game_id_invalid() { local game_id game_id="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Lʼid de jeu fourni ne correspond pas au format attendu : "%s"\n' message="$message"'Cette valeur ne peut utiliser que des caractères du set [-a-z0-9],' message="$message"' et ne peut ni débuter ni sʼachever par un tiret.\n' ;; ('en'|*) message='The provided game id is not using the expected format: "%s"\n' message="$message"'The value should only include characters from the set [-a-z0-9],' message="$message"' and can not begin nor end with an hyphen.\n' ;; esac print_message 'error' "$message" \ "$game_id" } # Error - An invalid format is used for expansion id # USAGE: error_expansion_id_invalid $expansion_id error_expansion_id_invalid() { local expansion_id expansion_id="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Lʼid dʼextension fourni ne correspond pas au format attendu : "%s"\n' message="$message"'Cette valeur ne peut utiliser que des caractères du set [-a-z0-9],' message="$message"' et ne peut ni débuter ni sʼachever par un tiret.\n' ;; ('en'|*) message='The provided expansion id is not using the expected format: "%s"\n' message="$message"'The value should only include characters from the set [-a-z0-9],' message="$message"' and can not begin nor end with an hyphen.\n' ;; esac print_message 'error' "$message" \ "$expansion_id" } src/20_requirements/10_requirements.sh0000644000000000000000000001664013120060140016740 0ustar rootroot# List the requirements for the current game script # USAGE: requirements_list # RETURN: a list for required commands, one per line requirements_list() { local requirements_list requirements_list=$( # List explicit requirements if ! variable_is_empty 'SCRIPT_DEPS'; then printf '%s\n' $SCRIPT_DEPS fi # List requirements for the current archive integrity setting requirements_list_checksum # List requirements for the current output package format setting requirements_list_package # List requirements for the current icons setting requirements_list_icons # List requirements for the current archive requirements_list_archive ) printf '%s' "$requirements_list" | list_clean } # List requirements for the current archive integrity setting # USAGE: requirements_list_checksum # RETURN: a list for required commands, one per line requirements_list_checksum() { local option_checksum requirements option_checksum=$(option_value 'checksum') case "$option_checksum" in ('md5') requirements='md5sum' ;; esac if ! variable_is_empty 'requirements'; then printf '%s\n' $requirements fi } # List requirements for the current output package format setting # USAGE: requirements_list_package # RETURN: a list for required commands, one per line requirements_list_package() { local option_package requirements option_package=$(option_value 'package') case "$option_package" in ('arch') # bsdtar and gzip are required for .MTREE requirements='bsdtar gzip' ;; ('deb') requirements='dpkg-deb' ;; ('gentoo') # fakeroot-ng doesn't work anymore, fakeroot >=1.25.1 does requirements='fakeroot ebuild' ;; esac if ! variable_is_empty 'requirements'; then printf '%s\n' $requirements fi } # List requirements for the current icons setting # USAGE: requirements_list_icons # RETURN: a list for required commands, one per line requirements_list_icons() { # Return early if icons inclusion is disabled local option_icons option_icons=$(option_value 'icons') if [ "$option_icons" -eq 0 ]; then return 0 fi # Get list of icons local icons_list icons_list=$(icons_list_all) # Return early if there is no icon for the current game script if [ -z "$icons_list" ]; then return 0 fi # Print requirements for each icon. local icon icon_path for icon in $icons_list; do icon_path=$(icon_path "$icon" 2>/dev/null || true) case "$icon_path" in (*'.png') printf '%s\n' 'identify' ;; (*'.bmp'|*'.ico') printf '%s\n' 'identify' 'convert' ;; (*'.exe') printf '%s\n' 'identify' 'convert' 'wrestool' ;; esac done } # List requirements for the current archive # USAGE: requirements_list_archive # RETURN: a list for required commands, one per line requirements_list_archive() { local archive archive=$(current_archive) { requirements_list_archive_single "$archive" local archive_part part_index for part_index in $(seq 1 9); do archive_part="${archive}_PART${part_index}" # Stop looking at the first unset archive extra part. if variable_is_empty "$archive_part"; then break fi requirements_list_archive_single "$archive_part" done } | list_clean } # List requirements for the given archive # USAGE: requirements_list_archive_single $archive # RETURN: a list for required commands, one per line requirements_list_archive_single() { local archive archive="$1" local archive_extractor archive_extractor=$(archive_extractor "$archive") if [ -n "$archive_extractor" ]; then printf '%s\n' "$archive_extractor" return 0 fi local archive_type requirements archive_type=$(archive_type "$archive") case "$archive_type" in ('7z') requirements='7zr' ;; ('cabinet') requirements='cabextract' ;; ('debian') requirements='dpkg-deb' ;; ('innosetup') requirements='innoextract' ;; ('installshield') requirements='unshield' ;; ('iso') requirements='bsdtar' ;; ('lha') requirements='lha' ;; ('makeself') requirements=$(archive_requirements_makeself_list) ;; ('mojosetup') requirements=$(archive_requirements_mojosetup_list) ;; ('msi') requirements='msiextract' ;; ('nullsoft-installer') requirements='unar' ;; ('rar') requirements='unar' ;; ('tar') requirements='tar' ;; ('tar.bz2') requirements='tar bunzip2' ;; ('tar.gz') requirements='tar gunzip' ;; ('tar.xz') requirements='tar unxz' ;; ('zip') requirements='unzip' ;; esac if ! variable_is_empty 'requirements'; then printf '%s\n' $requirements fi } # Check the presence of the current game script requirements # The requirements specific to the current archive are omitted, # they are handled by another function: archive_dependencies_check. # USAGE: check_deps # RETURN: 0 if all required dependencies are available, # 1 if a dependency is missing check_deps() { local requirements_list_checksum requirements_list_package requirements_list_checksum=$(requirements_list_checksum) requirements_list_package=$(requirements_list_package) SCRIPT_DEPS="${SCRIPT_DEPS:-} $requirements_list_checksum $requirements_list_package" local requirement for requirement in $SCRIPT_DEPS; do if ! command -v "$requirement" >/dev/null 2>&1; then error_dependency_not_found "$requirement" return 1 fi done # Debian - Check the available version of dpkg-deb local option_package option_package=$(option_value 'package') case "$option_package" in ('deb') ## Explicitly return an error status if the version check failed. if ! requirements_check_dpkg_deb_version; then return 1 fi ;; esac # Check for the dependencies required to extract the icons requirements_check_icons } # Check the available version of dpkg-deb # USAGE: requirements_check_dpkg_deb_version # RETURN: 0 if dpkg-deb is recent enough, # 1 if dpkg-deb is too old. requirements_check_dpkg_deb_version() { local version_available version_required version_available=$( LANG=C dpkg-deb --version | \ sed --silent "s/Debian 'dpkg-deb' package archive backend version \\([\\.0-9]\\+\\) (amd64)\\./\\1/p" ) ## The required option --root-owner-group is only available with dpkg-deb ≥ 1.19.0. version_required='1.19.0' ## If dpkg-deb is available, we can assume dpkg is available too. if ! dpkg --compare-versions "$version_available" '>=' "$version_required"; then error_requirement_too_old 'dpkg-deb' "$version_available" "$version_required" return 1 fi } # Check the presence of the required commands for icons extraction # USAGE: requirements_check_icons requirements_check_icons() { # Return early if icons inclusion has been disabled. local option_icons option_icons=$(option_value 'icons') if [ "$option_icons" -eq 0 ]; then return 0 fi local icons_requirements requirement icons_requirements=$(requirements_list_icons) for requirement in $icons_requirements; do if ! command -v "$requirement" >/dev/null 2>&1; then error_dependency_not_found "$requirement" return 1 fi done } # output what a command is provided by # USAGE: dependency_provided_by $command # CALLED BY: error_dependency_not_found dependency_provided_by() { local command provider command="$1" case "$command" in ('7zr') provider='p7zip' ;; ('bsdtar') provider='libarchive' ;; ('convert'|'identify') provider='imagemagick' ;; ('lha') provider='lhasa' ;; ('icotool'|'wrestool') provider='icoutils' ;; ('dpkg-deb') provider='dpkg' ;; (*) provider="$command" ;; esac printf '%s' "$provider" return 0 } src/20_requirements/90_messages.sh0000644000000000000000000000250513120060140016027 0ustar rootroot# Error - A required dependency is missing # USAGE: error_dependency_not_found $command_name error_dependency_not_found() { local command_name provider_package command_name="$1" provider_package=$(dependency_provided_by "$command_name") local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='%s est introuvable. Installez %s avant de lancer ce script.\n' ;; ('en'|*) message='%s not found. Install %s before running this script.\n' ;; esac print_message 'error' "$message" \ "$command_name" \ "$provider_package" } # Error - A required dependency is available, but in a build that is too old # USAGE: error_requirement_too_old $command_name $version_available $version_required error_requirement_too_old() { local command_name version_available version_required command_name="$1" version_available="$2" version_required="$3" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='%s est disponible dans la version %s, mais la version %s ou plus récente est requise.\n' ;; ('en'|*) message='%s is available in version %s, but version %s or newer is required.\n' ;; esac print_message 'error' "$message" \ "$command_name" \ "$version_available" \ "$version_required" } src/30_applications/00_common.sh0000644000000000000000000002402713120060140015446 0ustar rootroot# List the application identifiers for the current game or expansion # USAGE: applications_list # RETURN: a list of application identifiers, one per line, # or an empty string if there is no application (common case for expansions) applications_list() { # Fetch the explicit list if it is set local applications_list applications_list=$(context_value 'APPLICATIONS_LIST') # Parse the environment to compute an applications list from it if [ -z "$applications_list" ]; then local sed_expression # The following expression matches: # - APP_xxx_EXE # - APP_xxx_SCUMMID # - APP_xxx_TYPE # and the suffixed variants of these variables. sed_expression='s/^\(APP_[0-9A-Z]\+\)_\(EXE\|SCUMMID\|TYPE\)\(_[0-9A-Z]\+\)*=.*/\1/p' applications_list=$(set | sed --silent --expression="$sed_expression") fi # Fall back on the default applications list for the current game engine if [ -z "$applications_list" ]; then local game_engine game_engine=$(game_engine) case "$game_engine" in ('unity3d') # Unity3D games are expected to provide a single application applications_list='APP_MAIN' ;; ('visionaire') applications_list=$(visionaire_applications_list) ;; esac fi printf '%s\n' $applications_list | list_clean } # Print the type of prefix to use for the given application. # If no type is explicitely set from the game script, it defaults to "symlinks". # The supported prefix types are: # - "symlinks", the default, generate our usual symbolic links farm # - "none", no prefix is generated, the game is run from the read-only system directory # USAGE: application_prefix_type $application # RETURN: the prefix type keyword, from the supported values application_prefix_type() { # Prefix types: # - "symlinks", the default, generate our usual symbolic links farm # - "none", no prefix is generated, the game is run from the read-only system directory local application application="$1" # Set the prefix type for the current application. local prefix_type prefix_type=$(context_value "${application}_PREFIX_TYPE") # Fall back on the default prefix type for the current game. if [ -z "$prefix_type" ]; then prefix_type=$(context_value 'APPLICATIONS_PREFIX_TYPE') fi # Fall back on the default prefix type for the current application type. if [ -z "$prefix_type" ]; then local application_type application_type=$(application_type "$application") case "$application_type" in ('renpy') prefix_type='none' ;; ('scummvm') prefix_type='none' ;; (*) prefix_type='symlinks' ;; esac fi # Check that a supported prefix type has been set. case "$prefix_type" in ('symlinks'|'none') ## This is a supported type, no error to throw. ;; (*) error_unknown_prefix_type "$prefix_type" return 1 ;; esac printf '%s' "$prefix_type" } # print the id of the given application # USAGE: application_id $application # RETURN: the application id, limited to the characters set [-_0-9a-z] # the id can not start nor end with a character from the set [-_] application_id() { local application application="$1" # Get the application type from its identifier # Fall back on the game id if no value is set local application_id application_id=$(context_value "${application}_ID") if [ -z "$application_id" ]; then application_id=$(game_id) fi # Check that the id fits the format restrictions if ! printf '%s' "$application_id" | \ grep --quiet --regexp='^[0-9a-z][-_0-9a-z]\+[0-9a-z]$' then error_application_id_invalid "$application" "$application_id" return 1 fi printf '%s' "$application_id" } # Print the name of the binary targeted by the given application # USAGE: application_exe $application # RETURN: the binary file name, # or an empty string is none is set application_exe() { local application application="$1" local application_exe application_exe=$(context_value "${application}_EXE") # If no binary is explicitly set, fall back on the default value for the current game engine. if [ -z "$application_exe" ]; then local game_engine game_engine=$(game_engine) case "$game_engine" in ('unity3d') application_exe=$(unity3d_application_exe_default "$application") ;; ('visionaire') application_exe=$(visionaire_application_exe) ;; esac fi printf '%s' "$application_exe" } # Print the path to the application binary, with single quotes escaped, # for inclusion in a single quote delimited variable declaration. # USAGE: application_exe_escaped $application application_exe_escaped() { local application application="$1" local application_exe application_exe=$(application_exe "$application") # If the file name includes single quotes, replace each one with: '\'' printf '%s' "$application_exe" | sed "s/'/'\\\''/g" } # Print the full path to the application binary. # USAGE: application_exe_path $application_exe # RETURN: the full path to the application binary, # or an empty string if it could not be found. application_exe_path() { local application_exe application_exe="$1" # Look for the application binary in the temporary path for archive content. ## Do not throw an error if CONTENT_PATH_DEFAULT is not set, ## but skip searching for the application binary in the archive content. local content_path content_path=$(content_path_default) 2>/dev/null || true if [ -n "$content_path" ]; then local application_exe_path application_exe_path="${PLAYIT_WORKDIR}/gamedata/${content_path}/${application_exe}" if [ -f "$application_exe_path" ]; then printf '%s' "$application_exe_path" return 0 fi fi # Look for the application binary in the current package. local package package_path path_game_data application_exe_path package=$(current_package) package_path=$(package_path "$package") path_game_data=$(path_game_data) application_exe_path="${package_path}${path_game_data}/${application_exe}" if [ -f "$application_exe_path" ]; then printf '%s' "$application_exe_path" return 0 fi # Look for the application binary in all packages. local packages_list packages_list=$(packages_list) for package in $packages_list; do package_path=$(package_path "$package") application_exe_path="${package_path}${path_game_data}/${application_exe}" if [ -f "$application_exe_path" ]; then printf '%s' "$application_exe_path" return 0 fi done } # print the name of the given application, for display in menus # USAGE: application_name $application # RETURN: the pretty version of the application name application_name() { local application application="$1" # Get the application name from its identifier # Fall back on the game name if no value is set local application_name application_name=$(context_value "${application}_NAME") if [ -z "$application_name" ]; then application_name=$(game_name) fi printf '%s' "$application_name" } # print the category of the given application, for sorting in menus with categories support # USAGE: application_category $application # RETURN: the application XDG menu category application_category() { local application application="$1" # Get the application category from its identifier local application_category application_category=$(get_value "${application}_CAT") ## If no category is explicitely set, fall back on "Game" if [ -z "$application_category" ]; then application_category='Game' fi # TODO - We could check that the category is part of the 1.0 XDG spec: # https://specifications.freedesktop.org/menu-spec/menu-spec-1.0.html#category-registry printf '%s' "$application_category" } # Print the pre-run actions for the given application. # USAGE: application_prerun $application # RETURN: the pre-run actions, can span over multiple lines, # or an empty string if there are none application_prerun() { local application application="$1" local application_prerun application_prerun=$(context_value "${application}_PRERUN") # Run engine specific actions local game_engine game_engine=$(game_engine) case "$game_engine" in ('unity3d') ## Use a dedicated log file for the current game session. application_prerun="$application_prerun mkdir --parents logs" ;; esac # If LD_PRELOAD hacks are provided in the current package, include them in the pre-run actions. local package hacks_list package=$(current_package) hacks_list=$(hacks_included_in_package "$package") if [ -n "$hacks_list" ]; then local hack hack_prerun for hack in $hacks_list; do hack_prerun=$(hack_application_prerun "$hack") application_prerun="$application_prerun $hack_prerun" done fi # Ensure the pre-run actions string always end with a line break. printf '%s\n' "$application_prerun" } # Print the post-run actions for the given application. # USAGE: application_postrun $application # RETURN: the post-run actions, can span over multiple lines, # or an empty string if there are none application_postrun() { local application application="$1" local application_postrun application_postrun=$(context_value "${application}_POSTRUN") # Ensure the post-run actions string always end with a line break. printf '%s\n' "$application_postrun" } # print the options string for the given application # USAGE: application_options $application # RETURN: the options string on a single line, # or an empty string if no options are set application_options() { # Get the application options string from its identifier local application application_options application="$1" application_options=$(context_value "${application}_OPTIONS") # Add engine specific options local game_engine game_engine=$(game_engine) case "$game_engine" in ('unity3d') ## Use a dedicated log file for the current game session. ## The quotes are not truly required, but they make ShellCheck happy. application_options="$application_options -logFile \"./logs/\$(date +%F-%R).log\"" ;; esac # Check that the options string does not span multiple lines local line_breaks_number line_breaks_number=$(printf '%s' "$application_options" | wc --lines) if [ "$line_breaks_number" -gt 0 ]; then error_variable_multiline "${application}_OPTIONS" return 1 fi printf '%s' "$application_options" } src/30_applications/10_type.sh0000644000000000000000000000744013120060140015140 0ustar rootroot# Print the type of the given application # USAGE: application_type $application # RETURN: the application type keyword, from the supported values: # - dosbox # - java # - mono # - native # - renpy # - scummvm # - wine # or an empty string if the type is not set and could not be guessed application_type() { local application application="$1" local application_type application_type=$(context_value "${application}_TYPE") # If no type has been explicitely set, try to guess one ## Try to detect ScummVM applications if [ -z "$application_type" ]; then local application_scummid application_scummid=$(application_scummvm_scummid "$application") if [ -n "$application_scummid" ]; then application_type='scummvm' fi fi ## Try to detect the application type based of the binary MIME type if [ -z "$application_type" ]; then if [ -n "${PLAYIT_WORKDIR:-}" ]; then application_type=$(application_type_guess_from_file "$application") fi fi # Return early if no type has been found if [ -z "$application_type" ]; then return 0 fi # Check that a supported type has been fetched case "$application_type" in ( \ 'custom' | \ 'dosbox' | \ 'java' | \ 'mono' | \ 'native' | \ 'renpy' | \ 'scummvm' | \ 'wine' \ ) ## This is a supported type, no error to throw. ;; (*) error_unknown_application_type "$application_type" return 1 ;; esac printf '%s' "$application_type" } # Try to find the application type from the MIME type of its binary file # USAGE: application_type_guess_from_file $application # RETURN: the guessed application type, # or an empty string if none could be guessed application_type_guess_from_file() { local application application="$1" # Get the path to the application binary. local application_exe application_exe_path application_exe=$(application_exe "$application") # If no binary is found for the current package, try to find one for any of the packages. if [ -z "$application_exe" ]; then local package packages_list packages_list=$(packages_list) for package in $packages_list; do application_exe=$( set_current_package "$package" application_exe "$application" ) if [ -n "$application_exe" ]; then break fi done fi # Return early if no binary seems to be set for the current application. if [ -z "$application_exe" ]; then return 0 fi # Compute the full path to the application binary. application_exe_path=$(application_exe_path "$application_exe") # Return early if no binary file can be found for the given application. if [ -z "$application_exe_path" ]; then return 0 fi local file_type application_type file_type=$(file_type "$application_exe_path") case "$file_type" in ( \ 'application/x-executable' | \ 'application/x-pie-executable' | \ 'application/x-sharedlib' \ ) application_type='native' ;; ( \ 'application/x-dosexec' | \ 'application/vnd.microsoft.portable-executable' \ ) local file_type_extended file_type_extended=$( \ env --ignore-environment file --brief --dereference "$application_exe_path" | \ cut --delimiter=',' --fields=1 \ ) case "$file_type_extended" in ( \ 'DOS executable (COM)' | \ 'MS-DOS executable' \ ) application_type='dosbox' ;; (*'Mono/.Net assembly') application_type='mono' ;; ( \ 'PE32 executable'* | \ 'PE32+ executable'* \ ) application_type='wine' ;; esac ;; ('application/octet-stream') local file_type_extended file_type_extended=$(env --ignore-environment file --brief --dereference "$application_exe_path") case "$file_type_extended" in ('MS-DOS executable') application_type='dosbox' ;; esac ;; esac printf '%s' "$application_type" } src/30_applications/90_messages.sh0000644000000000000000000001063513120060140015776 0ustar rootroot# Error - An unknown application type is used # USAGE: error_unknown_application_type $app_type error_unknown_application_type() { local application_type application_type="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le type dʼapplication "%s" est inconnu.\n' message="$message"'Merci de signaler cette erreur sur notre outil de gestion de bugs : %s\n' ;; ('en'|*) message='"%s" application type is unknown.\n' message="$message"'Please report this issue in our bug tracker: %s\n' ;; esac print_message 'error' "$message" \ "$application_type" \ "$PLAYIT_GAMES_BUG_TRACKER_URL" } # Error - No application type could be found # USAGE: error_no_application_type $application error_no_application_type() { local application application="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le type de lʼapplication "%s" nʼest pas défini, et nʼa pas pu être détecté automatiquement.\n' message="$message"'Merci de signaler cette erreur sur notre outil de suivi des problèmes : %s\n' ;; ('en'|*) message='The type of application "%s" is not set, and could not be guessed.\n' message="$message"'Please report this issue in our bug tracker: %s\n' ;; esac print_message 'error' "$message" \ "$application" \ "$PLAYIT_GAMES_BUG_TRACKER_URL" } # Error - An unknown prefix type is requested # USAGE: error_unknown_prefix_type $prefix_type error_unknown_prefix_type() { local prefix_type prefix_type="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le type de préfixe "%s" est inconnu.\n' message="$message"'Merci de signaler cette erreur sur notre outil de suivi des problèmes : %s\n' ;; ('en'|*) message='"%s" prefix type is unknown.\n' message="$message"'Please report this issue in our bug tracker: %s\n' ;; esac print_message 'error' "$message" \ "$prefix_type" \ "$PLAYIT_GAMES_BUG_TRACKER_URL" } # Error - An invalid format is used for the given application id # USAGE: error_application_id_invalid $application $application_id error_application_id_invalid() { local application application_id application="$1" application_id="$2" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Lʼid fourni pour lʼapplication %s ne correspond pas au format attendu : "%s"\n' message="$message"'Cette valeur ne peut utiliser que des caractères du set [-a-z0-9],' message="$message"' et ne peut ni débuter ni sʼachever par un tiret.\n' ;; ('en') message='The id provided for application %s is not using the expected format: "%s"\n' message="$message"'The value should only include characters from the set [-a-z0-9],' message="$message"' and can not begin nor end with an hyphen.\n' ;; esac print_message 'error' "$message" \ "$application" \ "$application_id" } # Error - APP_xxx_EXE is unset but has been required # USAGE: error_application_exe_empty $application $function_name error_application_exe_empty() { local application function_name application="$1" function_name="$2" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='%s nʼest pas défini, mais cette valeur est requise par la fonction "%s".\n' ;; ('en') message='%s is not set, but is required by the "%s" function.\n' ;; esac print_message 'error' "$message" \ "${application}_EXE" \ "$function_name" } # Error - The applications list for the current game script is empty # USAGE: error_applications_list_empty error_applications_list_empty() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La liste dʼapplications à prendre en charge pour ce jeu semble vide' message="$message"', mais un traitement de cette liste a été demandé.\n' message="$message"'Merci de signaler cette erreur sur notre outil de gestion de bugs : %s\n' ;; ('en'|*) message='The applications list for the current game seems to be empty' message="$message"', but some action on this list has been requested.\n' message="$message"'Please report this issue in our bug tracker: %s\n' ;; esac print_message 'error' "$message" \ "$PLAYIT_BUG_TRACKER_URL" } src/30_archives/00_selection.sh0000644000000000000000000002303213120060140015254 0ustar rootroot# Get the path to search for archives # USAGE: archives_path_base # RETURN: a string representing a path archives_path_base() { # Try to get a path from the $PLAYIT_ARCHIVES_PATH_BASE environment variable, # if it is not set $PWD is used as a fallback. printf '%s' "${PLAYIT_ARCHIVES_PATH_BASE:-$PWD}" } # Set up the base archive # USAGE: archive_initialize_base # RETURN: 0 if the archive is found, # 1 if it is missing archive_initialize_base() { local archive_identifier archive_identifier='SOURCE_ARCHIVE' # A file path might have already been set, if one has been given on the command line. # If this is the case, we only want to use an archive that uses the same name. ## The output redirection must be done inside the subshell, or bash --posix will ignore it. archive_name_expected=$(archive_name "$archive_identifier" 2>/dev/null) || true local archives_list archive_candidate archives_list=$(archives_return_list) for archive_candidate in $archives_list; do archive_path=$(archive_path "$archive_candidate") if [ -f "$archive_path" ]; then local archive_name archive_name=$(archive_name "$archive_candidate") # Skip candidate archives that do not have the expected name. if \ [ -n "$archive_name_expected" ] && \ [ "$archive_name" != "$archive_name_expected" ] then continue fi export "${archive_identifier}_NAME=${archive_name}" export "${archive_identifier}_PATH=${archive_path}" ## Cache the path to the candidate archive, to prevent the need to re-compute it later. export "${archive_candidate}_PATH=${archive_path}" ## Old game scripts relying on `archive_extraction 'SOURCE_ARCHIVE'` instead of `archive_extraction_default` expect either SOURCE_ARCHIVE_TYPE or SOURCE_ARCHIVE_EXTRACTOR to be set. local archive_extractor archive_extractor_options archive_type archive_extractor=$(archive_extractor "$archive_candidate") archive_extractor_options=$(archive_extractor_options "$archive_candidate") archive_type=$(archive_type "$archive_candidate") export "${archive_identifier}_EXTRACTOR=${archive_extractor}" export "${archive_identifier}_EXTRACTOR_OPTIONS=${archive_extractor_options}" export "${archive_identifier}_TYPE=${archive_type}" ## Export the context archive set_current_archive "$archive_candidate" ## Some old game scripts might expect the variable $SOURCE_ARCHIVE to be set export SOURCE_ARCHIVE="$archive_path" ## Look for extra parts archive_initialize_extra_parts "$archive_candidate" ## Update the list of archives that are going to be used archives_used_add "$archive_candidate" ## Archives integrity can not yet be checked, because $PLAYIT_WORKDIR is not set yet, ## and it is required to cache computed hashes. archives_integrity_check must be called later. return 0 fi done # Throw an error if no archive candidate has been found error_archive_not_found $archives_list return 1 } # Set up a required archive # USAGE: archive_initialize_required $archive_identifier $archive_candidate[…] # RETURN: 0 if the archive is found, # 1 if it is missing archive_initialize_required() { local archive_identifier archive_identifier="$1" shift 1 local archive_candidate archive_path for archive_candidate in "$@"; do archive_path=$(archive_path "$archive_candidate") if [ -f "$archive_path" ]; then local archive_name archive_name=$(archive_name "$archive_candidate") export "${archive_identifier}_NAME=${archive_name}" export "${archive_identifier}_PATH=${archive_path}" ## Cache the path to the candidate archive, to prevent the need to re-compute it later. export "${archive_candidate}_PATH=${archive_path}" ## Export the legacy variable, its value can be expected by game scripts. export "${archive_identifier}=${archive_path}" ## Set the extractor / type of the archive. local archive_extractor archive_extractor_options archive_type archive_extractor=$(archive_extractor "$archive_candidate") archive_extractor_options=$(archive_extractor_options "$archive_candidate") archive_type=$(archive_type "$archive_candidate") export "${archive_identifier}_EXTRACTOR=${archive_extractor}" export "${archive_identifier}_EXTRACTOR_OPTIONS=${archive_extractor_options}" export "${archive_identifier}_TYPE=${archive_type}" ## Look for extra parts archive_initialize_extra_parts "$archive_candidate" ## Update the list of archives that are going to be used archives_used_add "$archive_candidate" ## Check the archives integrity archives_integrity_check return 0 fi done # Throw an error if no archive candidate has been found error_archive_not_found "$@" return 1 } # Set up an optional archive # USAGE: archive_initialize_optional $archive_identifier $archive_candidate[…] archive_initialize_optional() { local archive_identifier archive_identifier="$1" shift 1 local archive_candidate archive_path for archive_candidate in "$@"; do archive_path=$(archive_path "$archive_candidate") if [ -f "$archive_path" ]; then local archive_name archive_name=$(archive_name "$archive_candidate") export "${archive_identifier}_NAME=${archive_name}" export "${archive_identifier}_PATH=${archive_path}" ## Cache the path to the candidate archive, to prevent the need to re-compute it later. export "${archive_candidate}_PATH=${archive_path}" ## Export the legacy variable, its value can be expected by game scripts. export "${archive_identifier}=${archive_path}" ## Set the extractor / type of the archive. local archive_extractor archive_extractor_options archive_type archive_extractor=$(archive_extractor "$archive_candidate") archive_extractor_options=$(archive_extractor_options "$archive_candidate") archive_type=$(archive_type "$archive_candidate") export "${archive_identifier}_EXTRACTOR=${archive_extractor}" export "${archive_identifier}_EXTRACTOR_OPTIONS=${archive_extractor_options}" export "${archive_identifier}_TYPE=${archive_type}" ## Look for extra parts archive_initialize_extra_parts "$archive_candidate" ## Update the list of archives that are going to be used archives_used_add "$archive_candidate" ## Check the archives integrity archives_integrity_check return 0 fi done # No archive has been found, but this does not warrant an error return 0 } # Set up a list of extra parts for a given archive # USAGE: archive_initialize_extra_parts $archive archive_initialize_extra_parts() { local archive archive="$1" local archive_part archive_part_name archive_part_path index for index in $(seq 1 99); do archive_part="${archive}_PART${index}" ## This would fail if no archive part is expected at this index. ## The output redirection must be done inside the subshell, or bash --posix will ignore it. archive_part_name=$(archive_name "$archive_part" 2>/dev/null) || true ## Exit at the first unset archive part. if [ -z "$archive_part_name" ]; then return 0 fi archive_part_path=$(archive_path "$archive_part") if [ -f "$archive_part_path" ]; then export "${archive_part}_PATH=${archive_part_path}" ## Update the list of archives that are going to be used archives_used_add "$archive_part" else error_archive_not_found "$archive_part_name" return 1 fi done } # Check the integrity of all archives # USAGE: archives_integrity_check archives_integrity_check() { local option_checksum option_checksum=$(option_value 'checksum') case "$option_checksum" in ('md5') archives_integrity_check_md5 ;; esac } # Check the integrity of all archives, using MD5 # USAGE: archives_integrity_check_md5 archives_integrity_check_md5() { local archives_list archive archive_hash_expected archive_hash_computed archives_list=$(archives_used_list) for archive in $archives_list; do archive_hash_expected=$(archive_hash_md5 "$archive") ## Skip archives that have no expected MD5 hash set. if [ -z "$archive_hash_expected" ]; then continue fi archive_hash_computed=$(archive_hash_md5_computed "$archive") if [ "$archive_hash_computed" != "$archive_hash_expected" ]; then local archive_path archive_path=$(archive_path "$archive") error_hashsum_mismatch "$archive_path" return 1 fi done } # List all the archives that are going to be used # USAGE: archives_used_list # RETURN: a list of archive identifiers, one per line, archives_used_list() { local archives_list archives_list="${PLAYIT_ARCHIVES_USED_LIST:-}" local archive for archive in $archives_list; do printf '%s\n' "$archive" done } # Add an archive to the list of archives that are going to be used # USAGE: archives_used_add $archive archives_used_add() { local archive archive="$1" local archives_list archives_list="$(archives_used_list)" export PLAYIT_ARCHIVES_USED_LIST="$archives_list $archive" } # Print the list of archives supported vby the current game script # USAGE: archives_return_list # RETURNS: the list of identifiers of the supported archives, # as a list of strings separated by spaces or line breaks archives_return_list() { # If a list is already explicitely set, return early if ! variable_is_empty 'ARCHIVES_LIST'; then # ShellCheck false-positive # Possible misspelling: ARCHIVES_LIST may not be assigned. Did you mean archives_list? # shellcheck disable=SC2153 printf '%s' "$ARCHIVES_LIST" return 0 fi # Try to find archives using the ARCHIVE_BASE_xxx_[0-9]+ naming scheme local archives_list archives_list=$(set | sed --silent 's/^\(ARCHIVE_BASE\(_[0-9A-Z]\+\)*_[0-9]\+\)\(_NAME\)\?=.*/\1/p') if [ -n "$archives_list" ]; then printf '%s' "$archives_list" return 0 fi error_no_archive_supported return 1 } src/30_archives/05_selection_extra-archives-optional.sh0000644000000000000000000000163413120060140022115 0ustar rootroot# Check for the presence of optional extra archives # USAGE: archives_optional_extra_presence_check archives_optional_extra_presence_check() { # Check for the presence of an optional icons pack local icons_pack_name icons_pack_name=$(context_value 'ARCHIVE_OPTIONAL_ICONS_NAME') if [ -n "$icons_pack_name" ]; then ## Set contextual values, for game scripts with support for multiple games. ARCHIVE_OPTIONAL_ICONS_NAME=$(context_value 'ARCHIVE_OPTIONAL_ICONS_NAME') ARCHIVE_OPTIONAL_ICONS_MD5=$(context_value 'ARCHIVE_OPTIONAL_ICONS_MD5') ARCHIVE_OPTIONAL_ICONS_URL=$(context_value 'ARCHIVE_OPTIONAL_ICONS_URL') export ARCHIVE_OPTIONAL_ICONS_NAME ARCHIVE_OPTIONAL_ICONS_MD5 ARCHIVE_OPTIONAL_ICONS_URL archive_initialize_optional \ 'ARCHIVE_ICONS' \ 'ARCHIVE_OPTIONAL_ICONS' if ! archive_is_available 'ARCHIVE_ICONS'; then warning_optional_archive_missing_icons 'ARCHIVE_OPTIONAL_ICONS' fi fi } src/30_archives/05_selection_extra-archives-required.sh0000644000000000000000000002175113120060140022112 0ustar rootroot# Check for the presence of required extra archives # USAGE: archives_required_extra_presence_check # RETURN: 0 if no extra archive is required, or all required archives are found, # 1 if a required archive is missing archives_required_extra_presence_check() { # Check the presence of archives providing required native libraries local libraries_required library_required libraries_required=$(dependencies_list_native_libraries_all) while read -r library_required; do ## When this function is called inside `{ … } || true`, errexit does not stop execution on errors. ## This is the expected errexit behaviour for commands called in the left hand operand of an || operator. case "$library_required" in ('libcurl.so.4+CURL_OPENSSL_3') archive_required_extra_presence_check_libcurl3 || return 1 ;; ('libFLAC.so.8') archive_required_extra_presence_check_libflac8 || return 1 ;; ('libgconf-2.so.4') archive_required_extra_presence_check_libgconf2 || return 1 ;; ('libidn.so.11') archive_required_extra_presence_check_libidn11 || return 1 ;; ('libpng12.so.0') archive_required_extra_presence_check_libpng12 || return 1 ;; ('libssl.so.1.0.0') archive_required_extra_presence_check_libssl100 || return 1 ;; ('libssl.so.1.1') archive_required_extra_presence_check_libssl11 || return 1 ;; esac done <<- EOL $(printf '%s' "$libraries_required") EOL # Check the presence of archives required to apply tweaks on the WINE prefix local wineprefix_tweaks wineprefix_tweak wineprefix_tweaks=$(wine_wineprefix_tweaks) while read -r wineprefix_tweak; do ## When this function is called inside `{ … } || true`, errexit does not stop execution on errors. ## This is the expected errexit behaviour for commands called in the left hand operand of an || operator. case "$wineprefix_tweak" in ('mono') archive_required_extra_presence_check_mono || return 1 ;; esac done <<- EOL $(printf '%s' "$wineprefix_tweaks") EOL } # Check for the presence of the extra archive providing libcurl.so.3 and libcurl.so.4 including the CURL_OPENSSL_3 symbol # USAGE: archive_required_extra_presence_check_libcurl3 # RETURN: 0 if the required archive is found, # 1 if it is missing archive_required_extra_presence_check_libcurl3() { ARCHIVE_REQUIRED_LIBCURL3_NAME='curl_7.52.1.tar.xz' ARCHIVE_REQUIRED_LIBCURL3_MD5='ae0368de97368164801618a08c70cb34' ARCHIVE_REQUIRED_LIBCURL3_URL='https://downloads.dotslashplay.it/resources/curl/' ## The archive properties will be reused later, for checking its integrity. export ARCHIVE_REQUIRED_LIBCURL3_NAME ARCHIVE_REQUIRED_LIBCURL3_MD5 ARCHIVE_REQUIRED_LIBCURL3_URL archive_initialize_required \ 'ARCHIVE_LIBCURL3' \ 'ARCHIVE_REQUIRED_LIBCURL3' } # Check for the presence of the extra archive providing libFLAC.so.8 # USAGE: archive_required_extra_presence_check_libflac8 # RETURN: 0 if the required archive is found, # 1 if it is missing archive_required_extra_presence_check_libflac8() { # On Arch Linux, this library is still provided in the packages repositories local option_package option_package=$(option_value 'package') case "$option_package" in ('arch') return 0 ;; esac ARCHIVE_REQUIRED_LIBFLAC8_NAME='libflac8.tar.xz' ARCHIVE_REQUIRED_LIBFLAC8_MD5='1f0d785f52474cb2232a2f8f8b561eda' ARCHIVE_REQUIRED_LIBFLAC8_URL='https://downloads.dotslashplay.it/resources/flac/' ## The archive properties will be reused later, for checking its integrity. export ARCHIVE_REQUIRED_LIBFLAC8_NAME ARCHIVE_REQUIRED_LIBFLAC8_MD5 ARCHIVE_REQUIRED_LIBFLAC8_URL archive_initialize_required \ 'ARCHIVE_LIBFLAC8' \ 'ARCHIVE_REQUIRED_LIBFLAC8' } # Check for the presence of the extra archive providing GConf 2 library # USAGE: archive_required_extra_presence_check_libgconf2 # RETURN: 0 if the required archive is found, # 1 if it is missing archive_required_extra_presence_check_libgconf2() { # On Arch Linux, this library is still provided in the packages repositories local option_package option_package=$(option_value 'package') case "$option_package" in ('arch') return 0 ;; esac ARCHIVE_REQUIRED_LIBGCONF2_NAME='libgconf-2-4.tar.xz' ARCHIVE_REQUIRED_LIBGCONF2_MD5='4ae540fd4114ee2ddd7bd841017aad3b' ARCHIVE_REQUIRED_LIBGCONF2_URL='https://downloads.dotslashplay.it/resources/gconf/' ## The archive properties will be reused later, for checking its integrity. export ARCHIVE_REQUIRED_LIBGCONF2_NAME ARCHIVE_REQUIRED_LIBGCONF2_MD5 ARCHIVE_REQUIRED_LIBGCONF2_URL archive_initialize_required \ 'ARCHIVE_LIBGCONF2' \ 'ARCHIVE_REQUIRED_LIBGCONF2' } # Check for the presence of the extra archive providing GNU Libidn 11 library # USAGE: archive_required_extra_presence_check_libidn11 # RETURN: 0 if the required archive is found, # 1 if it is missing archive_required_extra_presence_check_libidn11() { # On Arch Linux, this library is still provided in the packages repositories local option_package option_package=$(option_value 'package') case "$option_package" in ('arch') return 0 ;; esac ARCHIVE_REQUIRED_LIBIDN11_NAME='libidn11_1.33.tar.xz' ARCHIVE_REQUIRED_LIBIDN11_MD5='75d95702ec6a2b327c8902cc14998926' ARCHIVE_REQUIRED_LIBIDN11_URL='https://downloads.dotslashplay.it/resources/libidn/' ## The archive properties will be reused later, for checking its integrity. export ARCHIVE_REQUIRED_LIBIDN11_NAME ARCHIVE_REQUIRED_LIBIDN11_MD5 ARCHIVE_REQUIRED_LIBIDN11_URL archive_initialize_required \ 'ARCHIVE_LIBIDN11' \ 'ARCHIVE_REQUIRED_LIBIDN11' } # Check for the presence of the extra archive providing PNG 1.2 libraries # USAGE: archive_required_extra_presence_check_libpng12 # RETURN: 0 if the required archive is found, # 1 if it is missing archive_required_extra_presence_check_libpng12() { # On Arch Linux and Gentoo, this library is still provided in the packages repositories local option_package option_package=$(option_value 'package') case "$option_package" in ('arch'|'egentoo'|'gentoo') return 0 ;; esac ARCHIVE_REQUIRED_LIBPNG12_NAME='libpng_1.2.tar.xz' ARCHIVE_REQUIRED_LIBPNG12_MD5='121c92152cce69f589a6d66a1e613bdb' ARCHIVE_REQUIRED_LIBPNG12_URL='https://downloads.dotslashplay.it/resources/libpng/' ## The archive properties will be reused later, for checking its integrity. export ARCHIVE_REQUIRED_LIBPNG12_NAME ARCHIVE_REQUIRED_LIBPNG12_MD5 ARCHIVE_REQUIRED_LIBPNG12_URL archive_initialize_required \ 'ARCHIVE_LIBPNG12' \ 'ARCHIVE_REQUIRED_LIBPNG12' } # Check for the presence of the extra archive providing OpenSSL 1.0.0 libraries # USAGE: archive_required_extra_presence_check_libssl100 # RETURN: 0 if the required archive is found, # 1 if it is missing archive_required_extra_presence_check_libssl100() { # On Arch Linux and Gentoo, this library is still provided in the packages repositories local option_package option_package=$(option_value 'package') case "$option_package" in ('arch'|'egentoo'|'gentoo') return 0 ;; esac ARCHIVE_REQUIRED_OPENSSL100_NAME='openssl_1.0.0.tar.xz' ARCHIVE_REQUIRED_OPENSSL100_MD5='9822e4dd8cb467dad843044c3135b5c5' ARCHIVE_REQUIRED_OPENSSL100_URL='https://downloads.dotslashplay.it/resources/openssl/' ## The archive properties will be reused later, for checking its integrity. export ARCHIVE_REQUIRED_OPENSSL100_NAME ARCHIVE_REQUIRED_OPENSSL100_MD5 ARCHIVE_REQUIRED_OPENSSL100_URL archive_initialize_required \ 'ARCHIVE_OPENSSL100' \ 'ARCHIVE_REQUIRED_OPENSSL100' } # Check for the presence of the extra archive providing OpenSSL 1.1 libraries # USAGE: archive_required_extra_presence_check_libssl11 # RETURN: 0 if the required archive is found, # 1 if it is missing archive_required_extra_presence_check_libssl11() { # On Arch Linux and Gentoo, this library is still provided in the packages repositories local option_package option_package=$(option_value 'package') case "$option_package" in ('arch'|'egentoo'|'gentoo') return 0 ;; esac ARCHIVE_REQUIRED_OPENSSL11_NAME='openssl_1.1.1n.tar.xz' ARCHIVE_REQUIRED_OPENSSL11_MD5='dade7a54b213be8ac6e0bc2b571570cc' ARCHIVE_REQUIRED_OPENSSL11_URL='https://downloads.dotslashplay.it/resources/openssl/' ## The archive properties will be reused later, for checking its integrity. export ARCHIVE_REQUIRED_OPENSSL11_NAME ARCHIVE_REQUIRED_OPENSSL11_MD5 ARCHIVE_REQUIRED_OPENSSL11_URL archive_initialize_required \ 'ARCHIVE_OPENSSL11' \ 'ARCHIVE_REQUIRED_OPENSSL11' } # Check for the presence of the extra archive providing Mono, for inclusion in WINE prefixes # USAGE: archive_required_extra_presence_check_mono # RETURN: 0 if the required archive is found, # 1 if it is missing archive_required_extra_presence_check_mono() { ARCHIVE_REQUIRED_MONO_NAME='wine-mono-8.0.0-x86.msi' ARCHIVE_REQUIRED_MONO_MD5='4fe5c683fcd9634c7f6571f252b3603c' ARCHIVE_REQUIRED_MONO_URL='https://dl.winehq.org/wine/wine-mono/8.0.0/' ## The archive properties will be reused later, for checking its integrity. export ARCHIVE_REQUIRED_MONO_NAME ARCHIVE_REQUIRED_MONO_MD5 ARCHIVE_REQUIRED_MONO_URL archive_initialize_required \ 'ARCHIVE_MONO' \ 'ARCHIVE_REQUIRED_MONO' } src/30_archives/10_details.sh0000644000000000000000000002261013120060140014716 0ustar rootroot# Check if the given archive is available # USAGE: archive_is_available $archive # RETURN: 0 if the archive is available, # 1 if it is not archive_is_available() { local archive archive="$1" local archive_path archive_path=$(archive_path "$archive" 2>/dev/null || true) test -n "$archive_path" } # Get the file name of a given archive # USAGE: archive_name $archive # RETURN: the archive name, # throws an error if no name can be found archive_name() { local archive archive="$1" # The file name should be set using the ARCHIVE_xxx_NAME variable. local archive_name archive_name=$(get_value "${archive}_NAME") # Try to get a name from the archive path. # We can not use the archive_path function here, to prevent a loop between archive_path and archive_name. local archive_path archive_path=$(get_value "${archive}_PATH") if [ -n "$archive_path" ]; then archive_name=$(basename "$archive_path") fi # If no name is set using the dedicated varible, try to find one using the legacy ARCHIVE_xxx variable. if [ -z "$archive_name" ]; then local archive_value archive_value=$(get_value "$archive") ## The value of the legacy variable could be either a path or a file name. archive_name=$(basename "$archive_value") if \ compatibility_level_is_at_least '2.26' && \ [ -n "$archive_name" ] then warning_deprecated_variable "$archive" "${archive}_NAME" fi fi # Throw an error if no name is found for the given archive. if [ -z "$archive_name" ]; then error_missing_variable "${archive}_NAME" return 1 fi printf '%s' "$archive_name" } # Get the path to a given archive # WARNING: No check is done that this path actually exists. # USAGE: archive_path $archive # RETURN: the absolute path to the archive, # or an empty path if the given archive is not available archive_path() { local archive archive="$1" # If the path to the archive has already been computed, it has been cached in the ARCHIVE_xxx_PATH variable. local archive_path archive_path=$(get_value "${archive}_PATH") # If no path could be found, try to get one using the legacy ARCHIVE_xxx variable. if [ -z "$archive_path" ]; then local archive_value archive_value=$(get_value "$archive") ## If the value includes a "/", we assume it is a path. Otherwise, it is probably a file name. if printf '%s' "$archive_value" | grep --fixed-strings --quiet --regexp='/'; then archive_path="$archive_value" if \ compatibility_level_is_at_least '2.26' && \ [ -n "$archive_path" ] then warning_deprecated_variable "$archive" "${archive}_PATH" fi fi fi # Compute the path from the archive name if [ -z "$archive_path" ]; then local archives_path_base archive_name archives_path_base=$(archives_path_base) archive_name=$(archive_name "$archive" 2>/dev/null || true) ## Do not try to compute a path if the archive does not seem to be available. ## We can only reach this situation if errexit has been disabled due to calling `archive_path (…) || true`. if [ -z "$archive_name" ]; then return 0 fi archive_path="${archives_path_base}/${archive_name}" fi # An absolute path should always be used, to allow using the archive path even after a directory change. archive_path=$(realpath --canonicalize-missing --no-symlinks "$archive_path") printf '%s' "$archive_path" } # Get the size of the archive contents # This is not the size of the archive file itself, but the total size of the files it includes, without compression. # USAGE: archive_size $archive # RETURN: the archive contents size, as a number of KiB archive_size() { local archive archive="$1" local archive_size archive_size=$(get_value "${archive}_SIZE") # If no size is set, assuming a size of 0 if [ -z "$archive_size" ]; then archive_size=0 fi printf '%s' "$archive_size" } # Get the expected MD5 hash of a given archive # USAGE: archive_hash_md5 $archive # RETURN: the expected MD5 hash, # or an empty string if none is set archive_hash_md5() { local archive archive="$1" get_value "${archive}_MD5" } # Get the computed MD5 hash of a given archive # USAGE: archive_hash_md5_computed $archive # RETURN: the computed MD5 hash archive_hash_md5_computed() { local archive archive="$1" # If the hash has already been computed, use the cached value. ## We can not use a global variable here, because the current function can be called from a subshell. local cache_directory cache_file_hashes archive_hash_computed cache_directory="${PLAYIT_WORKDIR}/cache" cache_file_hashes="${cache_directory}/hashes" if [ -f "$cache_file_hashes" ]; then archive_hash_computed=$(sed --silent "s/^${archive} | \([0-9a-f]\{32\}\)$/\1/p" "$cache_file_hashes") fi # If no cached value has been found, compute the hash. if [ -z "${archive_hash_computed:-}" ]; then local archive_name archive_path archive_name=$(archive_name "$archive") archive_path=$(archive_path "$archive") ## Print the message to the error output, to prevent messing up with the current function output. info_archive_hash_computation "$archive_name" >/dev/stderr archive_hash_computed=$(md5sum "$archive_path" | awk '{print $1}') # Cache the hash to prevent computing it again. ## We can not use a global variable here, because the current function can be called from a subshell. mkdir --parents "$cache_directory" cat >> "$cache_file_hashes" <<- EOF $archive | $archive_hash_computed EOF fi printf '%s' "$archive_hash_computed" } # Get the type of a given archive # USAGE: archive_type $archive # RETURNS: an archive type, # or an empty string if no type is set and none can be guessed archive_type() { local archive archive="$1" local archive_type archive_type=$(get_value "${archive}_TYPE") # Guess the archive type from its file name if [ -z "$archive_type" ]; then local archive_name archive_name=$(archive_name "$archive") archive_type=$(archive_guess_type_from_name "$archive_name") fi # Guess the archive type from its headers if [ -z "$archive_type" ]; then local archive_path archive_path=$(archive_path "$archive") archive_type=$(archive_guess_type_from_headers "$archive_path") fi # Fall back on using the type of the parent archive, if there is one if \ [ -z "$archive_type" ] && \ printf '%s' "$archive" | \ grep --quiet --word-regexp '^ARCHIVE_.*_PART[0-9]\+$' then local parent_archive parent_archive=$(printf '%s' "$archive" | sed 's/^\(ARCHIVE_.*\)_PART[0-9]\+$/\1/') archive_type=$(archive_type "$parent_archive") fi printf '%s' "$archive_type" } # Guess the archive type from its file name # USAGE: archive_guess_type_from_name $archive_file # RETURNS: the archive type, # or an empty string is none could be guessed archive_guess_type_from_name() { local archive_file archive_file="$1" local archive_type case "$archive_file" in (*'.cab') archive_type='cabinet' ;; (*'.deb') archive_type='debian' ;; ('setup_'*'.exe'|'patch_'*'.exe') archive_type='innosetup' ;; (*'.iso') archive_type='iso' ;; (*'.msi') archive_type='msi' ;; (*'.rar') archive_type='rar' ;; (*'.tar') archive_type='tar' ;; (*'.tar.bz2'|*'.tbz2') archive_type='tar.bz2' ;; (*'.tar.gz'|*'.tgz') archive_type='tar.gz' ;; (*'.tar.xz'|*'.txz') archive_type='tar.xz' ;; (*'.zip') archive_type='zip' ;; (*'.7z') archive_type='7z' ;; (*) # No type could be guessed from the archive file name archive_type='' ;; esac printf '%s' "$archive_type" } # Guess the archive type from its headers # USAGE: archive_guess_type_from_headers $archive_path # RETURNS: the archive type, # or an empty string is none could be guessed archive_guess_type_from_headers() { local archive_path archive_path="$1" local archive_type if head --lines=20 "$archive_path" | grep --quiet 'Makeself'; then if head --lines=50 "$archive_path" | grep --quiet 'script="./startmojo.sh"'; then archive_type='mojosetup' else archive_type='makeself' fi fi printf '%s' "${archive_type:-}" } # get the extractor for the given archive # USAGE: archive_extractor $archive_identifier # RETURNS: the specific extractor to use for the given archive (as a single word string), # or an empty string if none has been explicitely set. archive_extractor() { local archive_identifier archive_identifier="$1" assert_not_empty 'archive_identifier' 'archive_extractor' # Return archive extractor early if it is already set local archive_extractor archive_extractor=$(get_value "${archive_identifier}_EXTRACTOR") if [ -n "$archive_extractor" ]; then printf '%s' "$archive_extractor" return 0 fi # Fall back on using the extractor of the parent archive, if there is one if \ printf '%s' "$archive_identifier" | \ grep --quiet --word-regexp '^ARCHIVE_.*_PART[0-9]\+$' then local parent_archive parent_archive=$(printf '%s' "$archive_identifier" | sed 's/^\(ARCHIVE_.*\)_PART[0-9]\+$/\1/') archive_extractor=$(archive_extractor "$parent_archive") if [ -n "$archive_extractor" ]; then printf '%s' "$archive_extractor" return 0 fi fi # No failure if no extractor could be found return 0 } # get the extractor options string for the given archive # USAGE: archive_extractor_options $archive # RETURNS: the options string to pass to the specific extractor to use for the given archive, # or an empty string if no options string has been explicitely set. archive_extractor_options() { local archive archive="$1" get_value "${archive}_EXTRACTOR_OPTIONS" } src/30_archives/20_extraction.sh0000644000000000000000000002256313120060140015461 0ustar rootroot# Check that the tools required to extract the content of a given archive are available, # including the archive extra parts. # USAGE: archive_dependencies_check $archive archive_dependencies_check() { local archive archive="$1" assert_not_empty 'archive' 'archive_dependencies_check' # Check extraction dependencies for main archive. ## When this function is called inside `{ … } || true`, errexit does not stop execution on errors. This is the expected errexit behaviour for commands called in the left hand operand of an || operator. archive_dependencies_check_single "$archive" || return 1 # Check extraction dependencies for archive extra parts. local archive_part for i in $(seq 1 9); do archive_part="${archive}_PART${i}" # Stop looking at the first unset archive extra part. if variable_is_empty "$archive_part"; then break fi archive_dependencies_check_single "$archive_part" done } # Check that the tools required to extract the content of a given single archive are available. # USAGE: archive_dependencies_check_single $archive archive_dependencies_check_single() { local archive archive="$1" assert_not_empty 'archive' 'archive_dependencies_check_single' local archive_extractor archive_extractor=$(archive_extractor "$archive") if [ -n "$archive_extractor" ]; then ## When this function is called inside `{ … } || true`, errexit does not stop execution on errors. This is the expected errexit behaviour for commands called in the left hand operand of an || operator. archive_dependencies_check_using_extractor "$archive" || return 1 else ## When this function is called inside `{ … } || true`, errexit does not stop execution on errors. This is the expected errexit behaviour for commands called in the left hand operand of an || operator. archive_dependencies_check_from_type "$archive" || return 1 fi } # Check that the tools required to extract the content of a given archive are available. # # Check the presence of the specific tools required to provide the given archive extractor. # # USAGE: archive_dependencies_check_using_extractor $archive archive_dependencies_check_using_extractor() { local archive archive="$1" assert_not_empty 'archive' 'archive_dependencies_check' local archive_extractor archive_extractor=$(archive_extractor "$archive") case "$archive_extractor" in ( \ '7za' | \ '7zr' | \ 'bsdtar' | \ 'cabextract' | \ 'dpkg-deb' | \ 'innoextract' | \ 'lha' | \ 'msiextract' | \ 'tar' | \ 'unar' | \ 'unshield' | \ 'unzip' \ ) # Supported extractor, no error to throw. ;; (*) error_archive_extractor_invalid "$archive_extractor" return 1 ;; esac if ! command -v "$archive_extractor" >/dev/null 2>&1; then error_dependency_not_found "$archive_extractor" return 1 fi } # Check that the tools required to extract the content of a given archive are available. # # Check the presence of any tool supporting the given archive type. # # USAGE: archive_dependencies_check_from_type $archive archive_dependencies_check_from_type() { local archive archive="$1" assert_not_empty 'archive' 'archive_dependencies_check' local archive_type archive_type=$(archive_type "$archive") case "$archive_type" in ('7z') archive_dependencies_check_type_7z ;; ('cabinet') archive_dependencies_check_type_cabinet ;; ('debian') archive_dependencies_check_type_debian ;; ('innosetup') archive_dependencies_check_type_innosetup ;; ('innosetup_nolowercase') archive_dependencies_check_type_innosetup ;; ('installshield') archive_dependencies_check_type_installshield ;; ('iso') archive_dependencies_check_type_iso ;; ('lha') archive_dependencies_check_type_lha ;; ('makeself') archive_requirements_makeself_check ;; ('mojosetup') archive_requirements_mojosetup_check ;; ('msi') archive_dependencies_check_type_msi ;; ('nullsoft-installer') archive_dependencies_check_type_nullsoft ;; ('rar') archive_dependencies_check_type_rar ;; ('tar') archive_dependencies_check_type_tar ;; ('tar.bz2') archive_dependencies_check_type_tarbz2 ;; ('tar.gz') archive_dependencies_check_type_targz ;; ('tar.xz') archive_dependencies_check_type_tarxz ;; ('zip') archive_dependencies_check_type_zip ;; esac } # Extract data from the current archive # USAGE: archive_extraction_default archive_extraction_default() { local archive archive=$(current_archive) archive_extraction "$archive" # Extract the contents from the extra archives providing required libraries archive_extraction_extra_libraries # Extract the contents from the extra archives providing icons if archive_is_available 'ARCHIVE_ICONS'; then archive_extraction_extra_icons fi } # Extract data from a given archive file # USAGE: archive_extraction $archive archive_extraction() { local archive archive="$1" local archive_name archive_name=$(archive_name "$archive") information_archive_data_extraction "$archive_name" local destination_directory destination_directory="${PLAYIT_WORKDIR}/gamedata" mkdir --parents "$destination_directory" # Get the path to the extraction log file local log_file log_directory log_file=$(archive_extraction_log_path) log_directory=$(dirname "$log_file") mkdir --parents "$log_directory" local archive_extractor archive_extractor=$(archive_extractor "$archive") if [ -n "$archive_extractor" ]; then archive_extraction_using_extractor "$archive" "$destination_directory" "$log_file" else archive_extraction_from_type "$archive" "$destination_directory" "$log_file" fi # Apply minimal permissions on extracted files set_standard_permissions "$destination_directory" } # extract data from the target archive, using the specified extractor # USAGE: archive_extraction_using_extractor $archive $destination_directory $log_file archive_extraction_using_extractor() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" local archive_extractor archive_extractor=$(archive_extractor "$archive") case "$archive_extractor" in ('7za') archive_extraction_using_7za "$archive" "$destination_directory" "$log_file" ;; ('7zr') archive_extraction_using_7zr "$archive" "$destination_directory" "$log_file" ;; ('bsdtar') archive_extraction_using_bsdtar "$archive" "$destination_directory" "$log_file" ;; ('cabextract') archive_extraction_using_cabextract "$archive" "$destination_directory" "$log_file" ;; ('dpkg-deb') archive_extraction_using_dpkgdeb "$archive" "$destination_directory" "$log_file" ;; ('innoextract') archive_extraction_using_innoextract "$archive" "$destination_directory" "$log_file" ;; ('lha') archive_extraction_using_lha "$archive" "$destination_directory" "$log_file" ;; ('msiextract') archive_extraction_using_msiextract "$archive" "$destination_directory" "$log_file" ;; ('tar') archive_extraction_using_tar "$archive" "$destination_directory" "$log_file" ;; ('unar') archive_extraction_using_unar "$archive" "$destination_directory" "$log_file" ;; ('unshield') archive_extraction_using_unshield "$archive" "$destination_directory" "$log_file" ;; ('unzip') archive_extraction_using_unzip "$archive" "$destination_directory" "$log_file" ;; (*) error_archive_extractor_invalid "$archive_extractor" return 1 ;; esac } # extract data from the target archive, guessing the extractor from the given type # USAGE: archive_extraction_from_type $archive $destination_directory $log_file archive_extraction_from_type() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" local archive_type archive_type=$(archive_type "$archive") if [ -z "$archive_type" ]; then error_archive_type_not_set "$archive" return 1 fi case "$archive_type" in ('7z') archive_extraction_7z "$archive" "$destination_directory" "$log_file" ;; ('cabinet') archive_extraction_cabinet "$archive" "$destination_directory" "$log_file" ;; ('debian') archive_extraction_debian "$archive" "$destination_directory" "$log_file" ;; ('innosetup') archive_extraction_innosetup "$archive" "$destination_directory" "$log_file" ;; ('innosetup_nolowercase') warning_archive_type_deprecated "$archive" export ${archive}_EXTRACTOR_OPTIONS='--progress=1 --silent' archive_extraction_innosetup "$archive" "$destination_directory" "$log_file" ;; ('installshield') archive_extraction_installshield "$archive" "$destination_directory" "$log_file" ;; ('iso') archive_extraction_iso "$archive" "$destination_directory" "$log_file" ;; ('lha') archive_extraction_lha "$archive" "$destination_directory" "$log_file" ;; ('makeself') archive_extraction_makeself "$archive" "$destination_directory" "$log_file" ;; ('mojosetup') archive_extraction_mojosetup "$archive" "$destination_directory" "$log_file" ;; ('msi') archive_extraction_msi "$archive" "$destination_directory" "$log_file" ;; ('nullsoft-installer') archive_extraction_nullsoft "$archive" "$destination_directory" "$log_file" ;; ('rar') archive_extraction_rar "$archive" "$destination_directory" "$log_file" ;; ('tar'|'tar.bz2'|'tar.gz'|'tar.xz') archive_extraction_tar "$archive" "$destination_directory" "$log_file" ;; ('zip') archive_extraction_zip "$archive" "$destination_directory" "$log_file" ;; (*) error_archive_type_invalid "$archive_type" return 1 ;; esac } src/30_archives/25_extraction_extra-archives.sh0000644000000000000000000000627513120060140020475 0ustar rootroot# Extract the contents from the extra archives providing required libraries # USAGE: archive_extraction_extra_libraries archive_extraction_extra_libraries() { local libraries_required library_required libraries_required=$(dependencies_list_native_libraries_all) while read -r library_required; do case "$library_required" in ('libcurl.so.4+CURL_OPENSSL_3') archive_extraction_extra_libcurl3 ;; ('libFLAC.so.8') archive_extraction_extra_libflac8 ;; ('libgconf-2.so.4') archive_extraction_extra_libgconf2 ;; ('libidn.so.11') archive_extraction_extra_libidn11 ;; ('libpng12.so.0') archive_extraction_extra_libpng12 ;; ('libssl.so.1.0.0') archive_extraction_extra_libssl100 ;; ('libssl.so.1.1') archive_extraction_extra_libssl11 ;; esac done <<- EOL $(printf '%s' "$libraries_required") EOL } # Extract libcurl.so.3 and libcurl.so.4 including the CURL_OPENSSL_3 symbol # USAGE: archive_extraction_extra_libcurl3 archive_extraction_extra_libcurl3() { archive_extraction 'ARCHIVE_LIBCURL3' } # Extract libFLAC.so.8 # USAGE: archive_extraction_extra_libflac8 archive_extraction_extra_libflac8() { # On Arch Linux, this library is still provided in the packages repositories local option_package option_package=$(option_value 'package') case "$option_package" in ('arch') return 0 ;; esac archive_extraction 'ARCHIVE_LIBFLAC8' } # Extract GConf 2 library # USAGE: archive_extraction_extra_libgconf2 archive_extraction_extra_libgconf2() { archive_extraction 'ARCHIVE_LIBGCONF2' } # Extract GNU Libidn 11 library # USAGE: archive_extraction_extra_libidn11 archive_extraction_extra_libidn11() { # On Arch Linux, this library is still provided in the packages repositories local option_package option_package=$(option_value 'package') case "$option_package" in ('arch') return 0 ;; esac archive_extraction 'ARCHIVE_LIBIDN11' } # Extract PNG 1.2 libraries # USAGE: archive_extraction_extra_libpng12 archive_extraction_extra_libpng12() { # On Arch Linux and Gentoo, this library is still provided in the packages repositories local option_package option_package=$(option_value 'package') case "$option_package" in ('arch'|'egentoo'|'gentoo') return 0 ;; esac archive_extraction 'ARCHIVE_LIBPNG12' } # Extract OpenSSL 1.0.0 libraries # USAGE: archive_extraction_extra_libssl100 archive_extraction_extra_libssl100() { # On Arch Linux and Gentoo, this library is still provided in the packages repositories local option_package option_package=$(option_value 'package') case "$option_package" in ('arch'|'egentoo'|'gentoo') return 0 ;; esac archive_extraction 'ARCHIVE_OPENSSL100' } # Extract OpenSSL 1.1 libraries # USAGE: archive_extraction_extra_libssl11 archive_extraction_extra_libssl11() { # On Arch Linux and Gentoo, this library is still provided in the packages repositories local option_package option_package=$(option_value 'package') case "$option_package" in ('arch'|'egentoo'|'gentoo') return 0 ;; esac archive_extraction 'ARCHIVE_OPENSSL11' } # Extract the content of an icons pack archive # USAGE: archive_extraction_extra_icons archive_extraction_extra_icons() { archive_extraction 'ARCHIVE_ICONS' } src/30_archives/30_type_7z.sh0000644000000000000000000000177513120060140014705 0ustar rootroot# check the presence of required tools to handle a 7z archive # USAGE: archive_dependencies_check_type_7z archive_dependencies_check_type_7z() { local required_command for required_command in '7zr' '7za' 'unar'; do if command -v "$required_command" >/dev/null 2>&1; then return 0 fi done error_dependency_not_found '7zr' return 1 } # extract the content of a 7z archive # USAGE: archive_extraction_7z $archive $destination_directory $log_file archive_extraction_7z() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" if command -v '7zr' >/dev/null 2>&1; then archive_extraction_using_7zr "$archive" "$destination_directory" "$log_file" elif command -v '7za' >/dev/null 2>&1; then archive_extraction_using_7za "$archive" "$destination_directory" "$log_file" elif command -v 'unar' >/dev/null 2>&1; then archive_extraction_using_unar "$archive" "$destination_directory" "$log_file" else error_archive_no_extractor_found '7z' return 1 fi } src/30_archives/30_type_cabinet.sh0000644000000000000000000000140513120060140015740 0ustar rootroot# check the presence of required tools to handle a Microsoft Cabinet (.cab) archive # USAGE: archive_dependencies_check_type_cabinet archive_dependencies_check_type_cabinet() { if command -v 'cabextract' >/dev/null 2>&1; then return 0 fi error_dependency_not_found 'cabextract' return 1 } # extract the content of a Microsoft Cabinet (.cab) archive # USAGE: archive_extraction_cabinet $archive $destination_directory $log_file archive_extraction_cabinet() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" if command -v 'cabextract' >/dev/null 2>&1; then archive_extraction_using_cabextract "$archive" "$destination_directory" "$log_file" else error_archive_no_extractor_found 'cabinet' return 1 fi } src/30_archives/30_type_debian.sh0000644000000000000000000000134113120060140015554 0ustar rootroot# check the presence of required tools to handle a Debian package (.deb) # USAGE: archive_dependencies_check_type_debian archive_dependencies_check_type_debian() { if command -v 'dpkg-deb' >/dev/null 2>&1; then return 0 fi error_dependency_not_found 'dpkg-deb' return 1 } # extract the content of a Debian package (.deb) # USAGE: archive_extraction_debian $archive $destination_directory $log_file archive_extraction_debian() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" if command -v 'dpkg-deb' >/dev/null 2>&1; then archive_extraction_using_dpkgdeb "$archive" "$destination_directory" "$log_file" else error_archive_no_extractor_found 'debian' return 1 fi } src/30_archives/30_type_innosetup.sh0000644000000000000000000000137113120060140016361 0ustar rootroot# check the presence of required tools to handle a InnoSetup installer # USAGE: archive_dependencies_check_type_innosetup archive_dependencies_check_type_innosetup() { if command -v 'innoextract' >/dev/null 2>&1; then return 0 fi error_dependency_not_found 'innoextract' return 1 } # extract the content of a InnoSetup installer # USAGE: archive_extraction_innosetup $archive $destination_directory $log_file archive_extraction_innosetup() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" if command -v 'innoextract' >/dev/null 2>&1; then archive_extraction_using_innoextract "$archive" "$destination_directory" "$log_file" else error_archive_no_extractor_found 'innosetup' return 1 fi } src/30_archives/30_type_installshield.sh0000644000000000000000000000141313120060140017171 0ustar rootroot# check the presence of required tools to handle an InstallShield installer # USAGE: archive_dependencies_check_type_installshield archive_dependencies_check_type_installshield() { if command -v 'unshield' >/dev/null 2>&1; then return 0 fi error_dependency_not_found 'unshield' return 1 } # extract the content of an InstallShield installer # USAGE: archive_extraction_installshield $archive $destination_directory $log_file archive_extraction_installshield() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" if command -v 'unshield' >/dev/null 2>&1; then archive_extraction_using_unshield "$archive" "$destination_directory" "$log_file" else error_archive_no_extractor_found 'installshield' return 1 fi } src/30_archives/30_type_iso.sh0000644000000000000000000000131313120060140015123 0ustar rootroot# check the presence of required tools to handle an ISO9660 CD-ROM image # USAGE: archive_dependencies_check_type_iso archive_dependencies_check_type_iso() { if command -v 'bsdtar' >/dev/null 2>&1; then return 0 fi error_dependency_not_found 'bsdtar' return 1 } # extract the content of an ISO9660 CD-ROM image # USAGE: archive_extraction_iso $archive $destination_directory $log_file archive_extraction_iso() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" if command -v 'bsdtar' >/dev/null 2>&1; then archive_extraction_using_bsdtar "$archive" "$destination_directory" "$log_file" else error_archive_no_extractor_found 'iso' return 1 fi } src/30_archives/30_type_lha.sh0000644000000000000000000000162613120060140015104 0ustar rootroot# check the presence of required tools to handle a LHA archive (.lzh) # USAGE: archive_dependencies_check_type_lha archive_dependencies_check_type_lha() { local required_command for required_command in 'lha' 'bsdtar'; do if command -v "$required_command" >/dev/null 2>&1; then return 0 fi done error_dependency_not_found 'lha' return 1 } # extract the content of a LHA archive (.lzh) # USAGE: archive_extraction_lha $archive $destination_directory $log_file archive_extraction_lha() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" if command -v 'lha' >/dev/null 2>&1; then archive_extraction_using_lha "$archive" "$destination_directory" "$log_file" elif command -v 'bsdtar' >/dev/null 2>&1; then archive_extraction_using_bsdtar "$archive" "$destination_directory" "$log_file" else error_archive_no_extractor_found 'lha' return 1 fi } src/30_archives/30_type_makeself.sh0000644000000000000000000000673713120060140016137 0ustar rootroot# List the requirements to extract the contents of a Makeself installer # USAGE: archive_requirements_makeself_list archive_requirements_makeself_list() { printf '%s\n' \ 'head' \ 'sed' \ 'wc' \ 'tr' \ 'gzip' \ 'tar' } # Check the presence of required tools to handle a Makeself installer # USAGE: archive_requirements_makeself_check archive_requirements_makeself_check() { local commands_list required_command commands_list=$(archive_requirements_makeself_list) for required_command in $commands_list; do if ! command -v "$required_command" >/dev/null 2>&1; then error_dependency_not_found "$required_command" return 1 fi done } # Extract the content of a Makeself installer # USAGE: archive_extraction_makeself $archive $destination_directory $log_file archive_extraction_makeself() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" local archive_path archive_path=$(archive_path "$archive") # Fetch the archive properties local archive_offset archive_filesize archive_block_size archive_blocks archive_bytes archive_offset=$(makeself_offset "$archive_path") archive_filesize=$(makeself_filesize "$archive_path") ## Arbitrary value, small values would increase the time spent on the dd calls. archive_block_size=4096 archive_blocks=$((archive_filesize / archive_block_size)) archive_bytes=$((archive_filesize % archive_block_size)) # Proceed with the contents extraction local archive_extraction_return_code { printf 'dd if="%s" ibs="%s" skip=1 obs=%s conv=sync 2>/dev/null | ' "$archive_path" "$archive_offset" "$archive_block_size" printf '{ test "%s" -gt 0 && dd ibs=%s obs=%s count="%s" ; ' "$archive_blocks" "$archive_block_size" "$archive_block_size" "$archive_blocks" printf 'test "%s" -gt 0 && dd ibs=1 obs=%s count="%s" ; } 2>/dev/null | ' "$archive_bytes" "$archive_block_size" "$archive_bytes" printf 'gzip --stdout --decompress | tar xvf - --directory="%s"\n' "$destination_directory" } >> "$log_file" { dd if="$archive_path" ibs="$archive_offset" skip=1 obs="$archive_block_size" conv=sync 2>/dev/null | \ { test "$archive_blocks" -gt 0 && dd ibs="$archive_block_size" obs="$archive_block_size" count="$archive_blocks" test "$archive_bytes" -gt 0 && dd ibs=1 obs="$archive_block_size" count="$archive_bytes" } 2>/dev/null | \ gzip --stdout --decompress | \ tar xvf - --directory="$destination_directory" >> "$log_file" 2>&1 archive_extraction_return_code=$? } || true if [ $archive_extraction_return_code -ne 0 ]; then error_archive_extraction_failure "$archive" return 1 fi } # Makeself - Get the offset of the given file # USAGE: makeself_offset $archive_path # RETURN: the archive offset makeself_offset() { local archive_path archive_path="$1" local archive_header_length archive_offset archive_header_length=$( \ head --lines=200 "$archive_path" | \ sed --silent 's/^\s*offset=`head -n \([0-9]\+\) "$1" | wc -c | tr -d " "`\s*/\1/p' \ ) archive_offset=$( \ head --lines="$archive_header_length" "$archive_path" | \ wc --bytes | \ tr --delete ' ' \ ) printf '%s' "$archive_offset" } # Makeself - Get the size of the archive included in the given file # USAGE: makeself_filesize $archive_path # RETURN: the archive file size makeself_filesize() { local archive_path archive_path="$1" local archive_filesize archive_filesize=$( \ head --lines=200 "$archive_path" | \ sed --silent 's/^\s*filesizes="\([0-9]\+\)"\s*/\1/p' \ ) printf '%s' "$archive_filesize" } src/30_archives/30_type_mojosetup.sh0000644000000000000000000000534013120060140016362 0ustar rootroot# List the requirements to extract the contents of a MojoSetup installer # USAGE: archive_requirements_mojosetup_list archive_requirements_mojosetup_list() { # ShellCheck false-positive # Quote this to prevent word splitting. # shellcheck disable=SC2046 printf '%s\n' \ $(archive_requirements_makeself_list) \ 'unzip' } # Check the presence of required tools to handle a MojoSetup installer # USAGE: archive_requirements_mojosetup_check archive_requirements_mojosetup_check() { local commands_list required_command commands_list=$(archive_requirements_mojosetup_list) for required_command in $commands_list; do if ! command -v "$required_command" >/dev/null 2>&1; then error_dependency_not_found "$required_command" return 1 fi done } # Extract the content of a MojoSetup installer # USAGE: archive_extraction_mojosetup $archive $destination_directory $log_file archive_extraction_mojosetup() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" local archive_path archive_path=$(archive_path "$archive") # Fetch the archive properties local archive_makeself_offset archive_mojosetup_filesize archive_offset archive_makeself_offset=$(makeself_offset "$archive_path") archive_mojosetup_filesize=$(makeself_filesize "$archive_path") archive_offset=$((archive_makeself_offset + archive_mojosetup_filesize)) ## Arbitrary value, small values would increase the time spent on the dd calls. archive_block_size=4096 # Extract the .zip archive containing the game data local archive_game_data archive_extraction_return_code archive_game_data="${destination_directory}/mojosetup-game-data.zip" ## Silence ShellCheck false-positive ## Consider using { cmd1; cmd2; } >> file instead of individual redirects. # shellcheck disable=SC2129 printf 'dd if="%s" ibs="%s" obs="%s" skip="%sB" > "%s"\n' "$archive_path" "$archive_block_size" "$archive_block_size" "$archive_offset" "$archive_game_data" >> "$log_file" { dd if="$archive_path" ibs="$archive_block_size" obs="$archive_block_size" skip="${archive_offset}B" > "$archive_game_data" 2>> "$log_file" archive_extraction_return_code=$? } || true if [ $archive_extraction_return_code -ne 0 ]; then error_archive_extraction_failure "$archive" return 1 fi # Extract the game data local archive_extraction_return_code printf 'unzip -o -d "%s" "%s"\n' "$destination_directory" "$archive_game_data" >> "$log_file" { ## unzip -o: overwrite existing files without prompting. unzip -o -d "$destination_directory" "$archive_game_data" >> "$log_file" 2>&1 archive_extraction_return_code=$? } || true if [ $archive_extraction_return_code -ne 0 ]; then error_archive_extraction_failure "$archive" return 1 fi rm "$archive_game_data" } src/30_archives/30_type_msi.sh0000644000000000000000000000134113120060140015122 0ustar rootroot# check the presence of required tools to handle a Windows Installer (.msi) # USAGE: archive_dependencies_check_type_msi archive_dependencies_check_type_msi() { if command -v 'msiextract' >/dev/null 2>&1; then return 0 fi error_dependency_not_found 'msiextract' return 1 } # extract the content of a Windows Installer (.msi) # USAGE: archive_extraction_msi $archive $destination_directory $log_file archive_extraction_msi() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" if command -v 'msiextract' >/dev/null 2>&1; then archive_extraction_using_msiextract "$archive" "$destination_directory" "$log_file" else error_archive_no_extractor_found 'msi' return 1 fi } src/30_archives/30_type_nullsoft.sh0000644000000000000000000000134013120060140016177 0ustar rootroot# check the presence of required tools to handle a NullSoft installer # USAGE: archive_dependencies_check_type_nullsoft archive_dependencies_check_type_nullsoft() { if command -v 'unar' >/dev/null 2>&1; then return 0 fi error_dependency_not_found 'unar' return 1 } # extract the content of a NullSoft installer # USAGE: archive_extraction_nullsoft $archive $destination_directory $log_file archive_extraction_nullsoft() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" if command -v 'unar' >/dev/null 2>&1; then archive_extraction_using_unar "$archive" "$destination_directory" "$log_file" else error_archive_no_extractor_found 'nullsoft-installer' return 1 fi } src/30_archives/30_type_rar.sh0000644000000000000000000000125713120060140015124 0ustar rootroot# check the presence of required tools to handle a RAR archive # USAGE: archive_dependencies_check_type_rar archive_dependencies_check_type_rar() { if command -v 'unar' >/dev/null 2>&1; then return 0 fi error_dependency_not_found 'unar' return 1 } # extract the content of a RAR archive # USAGE: archive_extraction_rar $archive $destination_directory $log_file archive_extraction_rar() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" if command -v 'unar' >/dev/null 2>&1; then archive_extraction_using_unar "$archive" "$destination_directory" "$log_file" else error_archive_no_extractor_found 'rar' return 1 fi } src/30_archives/30_type_tar.sh0000644000000000000000000000307213120060140015123 0ustar rootroot# check the presence of required tools to handle a tar archive # USAGE: archive_dependencies_check_type_tar archive_dependencies_check_type_tar() { if command -v 'tar' >/dev/null 2>&1; then return 0 fi error_dependency_not_found 'tar' return 1 } # check the presence of required tools to handle a tar.bz2 archive # USAGE: archive_dependencies_check_type_tarbz2 archive_dependencies_check_type_tarbz2() { archive_dependencies_check_type_tar if command -v 'bunzip2' >/dev/null 2>&1; then return 0 fi error_dependency_not_found 'bunzip2' return 1 } # check the presence of required tools to handle a tar.gz archive # USAGE: archive_dependencies_check_type_targz archive_dependencies_check_type_targz() { archive_dependencies_check_type_tar if command -v 'gunzip' >/dev/null 2>&1; then return 0 fi error_dependency_not_found 'gunzip' return 1 } # check the presence of required tools to handle a tar.xz archive # USAGE: archive_dependencies_check_type_tarxz archive_dependencies_check_type_tarxz() { archive_dependencies_check_type_tar if command -v 'unxz' >/dev/null 2>&1; then return 0 fi error_dependency_not_found 'unxz' return 1 } # extract the content of a .tar archive # USAGE: archive_extraction_tar $archive $destination_directory $log_file archive_extraction_tar() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" if command -v 'tar' >/dev/null 2>&1; then archive_extraction_using_tar "$archive" "$destination_directory" "$log_file" else error_archive_no_extractor_found 'tar' return 1 fi } src/30_archives/30_type_zip.sh0000644000000000000000000000126613120060140015142 0ustar rootroot# check the presence of required tools to handle a .zip archive # USAGE: archive_dependencies_check_type_zip archive_dependencies_check_type_zip() { if command -v 'unzip' >/dev/null 2>&1; then return 0 fi error_dependency_not_found 'unzip' return 1 } # extract the content of a .zip archive # USAGE: archive_extraction_zip $archive $destination_directory $log_file archive_extraction_zip() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" if command -v 'unzip' >/dev/null 2>&1; then archive_extraction_using_unzip "$archive" "$destination_directory" "$log_file" else error_archive_no_extractor_found 'zip' return 1 fi } src/30_archives/40_extractor_7za.sh0000644000000000000000000000155113120060140016071 0ustar rootroot# extract the content of an archive using 7za # USAGE: archive_extraction_using_7za $archive $destination_directory $log_file archive_extraction_using_7za() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" local archive_path archive_path=$(archive_path "$archive") local extractor_options archive_extraction_return_code extractor_options=$(archive_extractor_options "$archive") if [ -z "$extractor_options" ]; then extractor_options='-y' fi printf '7za x %s -o"%s" "%s"\n' "$extractor_options" "$destination_directory" "$archive_path" >> "$log_file" { 7za x $extractor_options -o"$destination_directory" "$archive_path" >> "$log_file" 2>&1 archive_extraction_return_code=$? } || true if [ $archive_extraction_return_code -ne 0 ]; then error_archive_extraction_failure "$archive" return 1 fi } src/30_archives/40_extractor_7zr.sh0000644000000000000000000000155113120060140016112 0ustar rootroot# extract the content of an archive using 7zr # USAGE: archive_extraction_using_7zr $archive $destination_directory $log_file archive_extraction_using_7zr() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" local archive_path archive_path=$(archive_path "$archive") local extractor_options archive_extraction_return_code extractor_options=$(archive_extractor_options "$archive") if [ -z "$extractor_options" ]; then extractor_options='-y' fi printf '7zr x %s -o"%s" "%s"\n' "$extractor_options" "$destination_directory" "$archive_path" >> "$log_file" { 7zr x $extractor_options -o"$destination_directory" "$archive_path" >> "$log_file" 2>&1 archive_extraction_return_code=$? } || true if [ $archive_extraction_return_code -ne 0 ]; then error_archive_extraction_failure "$archive" return 1 fi } src/30_archives/40_extractor_bsdtar.sh0000644000000000000000000000157313120060140016653 0ustar rootroot# extract the content of an archive using bsdtar # USAGE: archive_extraction_using_bsdtar $archive $destination_directory $log_file archive_extraction_using_bsdtar() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" local archive_path archive_path=$(archive_path "$archive") local extractor_options archive_extraction_return_code extractor_options=$(archive_extractor_options "$archive") printf 'bsdtar --verbose %s --directory "%s" --extract --file "%s"\n' "$extractor_options" "$destination_directory" "$archive_path" >> "$log_file" { bsdtar --verbose $extractor_options --directory "$destination_directory" --extract --file "$archive_path" >> "$log_file" 2>&1 archive_extraction_return_code=$? } || true if [ $archive_extraction_return_code -ne 0 ]; then error_archive_extraction_failure "$archive" return 1 fi } src/30_archives/40_extractor_cabextract.sh0000644000000000000000000000161213120060140017506 0ustar rootroot# extract the content of an archive using cabextract # USAGE: archive_extraction_using_cabextract $archive $destination_directory $log_file archive_extraction_using_cabextract() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" local archive_path archive_path=$(archive_path "$archive") local extractor_options archive_extraction_return_code extractor_options=$(archive_extractor_options "$archive") if [ -z "$extractor_options" ]; then extractor_options='-L' fi printf 'cabextract %s -d "%s" "%s"\n' "$extractor_options" "$destination_directory" "$archive_path" >> "$log_file" { cabextract $extractor_options -d "$destination_directory" "$archive_path" >> "$log_file" 2>&1 archive_extraction_return_code=$? } || true if [ $archive_extraction_return_code -ne 0 ]; then error_archive_extraction_failure "$archive" return 1 fi } src/30_archives/40_extractor_dpkgdeb.sh0000644000000000000000000000153513120060140016772 0ustar rootroot# extract the content of an archive using dpkg-deb # USAGE: archive_extraction_using_dpkgdeb $archive $destination_directory $log_file archive_extraction_using_dpkgdeb() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" local archive_path archive_path=$(archive_path "$archive") local extractor_options archive_extraction_return_code extractor_options=$(archive_extractor_options "$archive") printf 'dpkg-deb --verbose %s --extract "%s" "%s"\n' "$extractor_options" "$archive_path" "$destination_directory" >> "$log_file" { dpkg-deb --verbose $extractor_options --extract "$archive_path" "$destination_directory" >> "$log_file" 2>&1 archive_extraction_return_code=$? } || true if [ $archive_extraction_return_code -ne 0 ]; then error_archive_extraction_failure "$archive" return 1 fi } src/30_archives/40_extractor_innoextract.sh0000644000000000000000000000351413120060140017727 0ustar rootroot# extract the content of an archive using innoextract # USAGE: archive_extraction_using_innoextract $archive $destination_directory $log_file archive_extraction_using_innoextract() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" local archive_path archive_path=$(archive_path "$archive") if ! archive_extraction_using_innoextract_is_supported "$archive_path"; then error_innoextract_version_too_old "$archive_path" return 1 fi local extractor_options archive_extraction_return_code extractor_options=$(archive_extractor_options "$archive") if [ -z "$extractor_options" ]; then extractor_options='--lowercase' fi printf 'innoextract %s --extract --output-dir "%s" "%s"\n' "$extractor_options" "$destination_directory" "$archive_path" >> "$log_file" { innoextract $extractor_options --extract --output-dir "$destination_directory" "$archive_path" >> "$log_file" 2>&1 archive_extraction_return_code=$? } || true if [ $archive_extraction_return_code -ne 0 ]; then error_archive_extraction_failure "$archive" return 1 fi } # check that the InnoSetup archive can be processed by the available innoextract version # USAGE: archive_extraction_using_innoextract_is_supported $archive_path # RETURNS: 0 if supported, 1 if unsupported archive_extraction_using_innoextract_is_supported() { local archive_path archive_path="$1" # Use innoextract internal check if innoextract --list --silent "$archive_path" 2>&1 1>/dev/null | \ head --lines=1 | \ grep --ignore-case --quiet 'unexpected setup data version' then return 1 fi # Check for GOG archives based on Galaxy file fragments, unsupported by innoextract < 1.7 if innoextract --list "$archive_path" 2>/dev/null | \ grep --quiet ' - "tmp/[0-9a-f]\{2\}/[0-9a-f]\{2\}/[0-9a-f]\{32\}" (.*)' then return 1 fi return 0 } src/30_archives/40_extractor_lha.sh0000644000000000000000000000142313120060140016132 0ustar rootroot# extract the content of an archive using lha # USAGE: archive_extraction_using_lha $archive $destination_directory $log_file archive_extraction_using_lha() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" local archive_path archive_path=$(archive_path "$archive") local archive_extraction_return_code # Due to its unusual command syntax, lha extractor has no support for ARCHIVE_xxx_EXTRACTOR_OPTIONS printf 'lha -ew="%s" "%s"\n' "$destination_directory" "$archive_path" >> "$log_file" { lha -ew="$destination_directory" "$archive_path" >> "$log_file" 2>&1 archive_extraction_return_code=$? } || true if [ $archive_extraction_return_code -ne 0 ]; then error_archive_extraction_failure "$archive" return 1 fi } src/30_archives/40_extractor_msiextract.sh0000644000000000000000000000157313120060140017557 0ustar rootroot# extract the content of an archive using msiextract # USAGE: archive_extraction_using_msiextract $archive $destination_directory $log_file archive_extraction_using_msiextract() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" local archive_path archive_path=$(archive_path "$archive") local extractor_options archive_extraction_return_code extractor_options=$(archive_extractor_options "$archive") printf 'msiextract %s --directory "%s" "%s"\n' "$extractor_options" "$destination_directory" "$archive_path" >> "$log_file" { msiextract $extractor_options --directory "$destination_directory" "$archive_path" >> "$log_file" 2>&1 archive_extraction_return_code=$? } || true if [ $archive_extraction_return_code -ne 0 ]; then error_archive_extraction_failure "$archive" return 1 fi tolower "$destination_directory" } src/30_archives/40_extractor_tar.sh0000644000000000000000000000227613120060140016163 0ustar rootroot# extract the content of an archive using tar # USAGE: archive_extraction_using_tar $archive $destination_directory $log_file archive_extraction_using_tar() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" local archive_path archive_path=$(archive_path "$archive") local extractor_options archive_extraction_return_code extractor_options=$(archive_extractor_options "$archive") ## Set default options based on archive type. if [ -z "$extractor_options" ]; then local archive_type archive_type=$(archive_type "$archive") case "$archive_type" in ('tar.bz2') extractor_options='--bzip2' ;; ('tar.gz') extractor_options='--gzip' ;; ('tar.xz') extractor_options='--xz' ;; esac fi printf 'tar --verbose %s --extract --file "%s" --directory "%s"\n' "$extractor_options" "$archive_path" "$destination_directory" >> "$log_file" { tar --verbose $extractor_options --extract --file "$archive_path" --directory "$destination_directory" >> "$log_file" 2>&1 archive_extraction_return_code=$? } || true if [ $archive_extraction_return_code -ne 0 ]; then error_archive_extraction_failure "$archive" return 1 fi } src/30_archives/40_extractor_unar.sh0000644000000000000000000000164613120060140016342 0ustar rootroot# extract the content of an archive using unar # USAGE: archive_extraction_using_unar $archive $destination_directory $log_file archive_extraction_using_unar() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" local archive_path archive_path=$(archive_path "$archive") local extractor_options archive_extraction_return_code extractor_options=$(archive_extractor_options "$archive") if [ -z "$extractor_options" ]; then extractor_options='-force-overwrite -no-directory' fi printf 'unar %s -output-directory "%s" "%s"\n' "$extractor_options" "$destination_directory" "$archive_path" >> "$log_file" { unar $extractor_options -output-directory "$destination_directory" "$archive_path" >> "$log_file" 2>&1 archive_extraction_return_code=$? } || true if [ $archive_extraction_return_code -ne 0 ]; then error_archive_extraction_failure "$archive" return 1 fi } src/30_archives/40_extractor_unshield.sh0000644000000000000000000000160413120060140017202 0ustar rootroot# extract the content of an archive using unshield # USAGE: archive_extraction_using_unshield $archive $destination_directory $log_file archive_extraction_using_unshield() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" local archive_path archive_path=$(archive_path "$archive") local extractor_options archive_extraction_return_code extractor_options=$(archive_extractor_options "$archive") if [ -z "$extractor_options" ]; then extractor_options='-L' fi printf 'unshield %s -d "%s" x "%s"\n' "$extractor_options" "$destination_directory" "$archive_path" >> "$log_file" { unshield $extractor_options -d "$destination_directory" x "$archive_path" >> "$log_file" 2>&1 archive_extraction_return_code=$? } || true if [ $archive_extraction_return_code -ne 0 ]; then error_archive_extraction_failure "$archive" return 1 fi } src/30_archives/40_extractor_unzip.sh0000644000000000000000000000165413120060140016541 0ustar rootroot# extract the content of an archive using unzip # USAGE: archive_extraction_using_unzip $archive $destination_directory $log_file archive_extraction_using_unzip() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" local archive_path archive_path=$(archive_path "$archive") local extractor_options archive_extraction_return_code extractor_options=$(archive_extractor_options "$archive") if [ -z "$extractor_options" ]; then ## unzip -o: overwrite existing files without prompting. extractor_options='-o' fi printf 'unzip %s -d "%s" "%s"\n' "$extractor_options" "$destination_directory" "$archive_path" >> "$log_file" { unzip $extractor_options -d "$destination_directory" "$archive_path" >> "$log_file" 2>&1 archive_extraction_return_code=$? } || true if [ $archive_extraction_return_code -ne 0 ]; then error_archive_extraction_failure "$archive" return 1 fi } src/30_archives/50_logs.sh0000644000000000000000000000053413120060140014242 0ustar rootroot# Print the path to the log file for archive data extraction # USAGE: archive_extraction_log_path # RETURN: the absolute path to the log file, # the file might not exist yet, so the calling function should handle the directories and file creation archive_extraction_log_path() { printf '%s/logs/archive-extraction.log' "$PLAYIT_WORKDIR" } src/30_archives/90_messages.sh0000644000000000000000000002343713120060140015120 0ustar rootroot# display the name of a file currently processed # USAGE: information_file_in_use $file information_file_in_use() { local file file="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Utilisation de %s\n' ;; ('en'|*) message='Using %s\n' ;; esac print_message 'info' "$message" } # print data extraction message # USAGE: information_archive_data_extraction $file information_archive_data_extraction() { local file file="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Extraction des données de %s…\n' ;; ('en'|*) message='Extracting data from %s…\n' ;; esac print_message 'info' "$message" \ "$file" } # print hash computation message # USAGE: info_archive_hash_computation $file_path info_archive_hash_computation() { local file_path file_path="$1" local file_name file_name=$(basename "$file_path") local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Calcul de la somme de contrôle de %s…\n' ;; ('en'|*) message='Computing hashsum for %s…\n' ;; esac print_message 'info' "$message" \ "$file_name" } # display a list of archives, one per line, with their download URL if one is provided # USAGE: information_archives_list $archive[…] information_archives_list() { ## TODO: The archive URL should be fetched using a dedicated function. local archive archive_name archive_url for archive in "$@"; do archive_name=$(archive_name "$archive") archive_url=$(get_value "${archive}_URL") if [ -n "$archive_url" ]; then print_message 'info' '%s — %s\n' \ "$archive_name" \ "$archive_url" else print_message 'info' '%s\n' \ "$archive_name" fi done } # Warning - An optional icons archive is supported, but not available # USAGE: warning_optional_archive_missing_icons $archive warning_optional_archive_missing_icons() { local archive archive="$1" ## TODO: The archive URL should be fetched using a dedicated function. local archive_name archive_url archive_name=$(archive_name "$archive") archive_url=$(get_value "${archive}_URL") local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Une archive proposant des icônes pour ce jeu est disponible, mais n’a pas été trouvée : %s\n' message="$message"'Elle peut être téléchargée depuis l’URL suivante : %s\n\n' ;; ('en'|*) message='An archive providing icons for this game is available, but could not be found: %s\n' message="$message"'It can be downloaded from the following URL: %s\n\n' ;; esac print_message 'warning' "$message" \ "$archive_name" \ "$archive_url" } # Error - No archive supported for the current game script error_no_archive_supported() { local game_script game_script=$(realpath "$0") local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Ce script semble ne prendre en charge aucune archive : %s\n' message="$message"'Merci de signaler cette erreur sur notre outil de suivi : %s\n' ;; ('en'|*) message='This script seems to support no archive: %s\n' message="$message"'Please report this issue in our bug tracker: %s\n' ;; esac print_message 'error' "$message" \ "$game_script" \ "$PLAYIT_GAMES_BUG_TRACKER_URL" } # Error - No extractor is available to handle the given archive # USAGE: error_archive_no_extractor_found $archive_type error_archive_no_extractor_found() { local archive_type archive_type="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Ce script a essayé dʼextraire le contenu dʼune archive de type "%s", mais aucun outil approprié nʼa été trouvé.\n' message="$message"'Merci de signaler cette erreur sur notre outil de gestion de bugs : %s\n' ;; ('en'|*) message='This script tried to extract the contents of a "%s" archive, but not appropriate tool could be found.\n' message="$message"'Please report this issue in our bug tracker: %s\n' ;; esac print_message 'error' "$message" \ "$archive_type" \ "$PLAYIT_GAMES_BUG_TRACKER_URL" } # Error - A required archive is not found # List all the archives that could fulfill the requirement, with their download URL if one is provided # USAGE: error_archive_not_found $archive[…] error_archive_not_found() { # Get the path to the directory where the current archive is found local archives_path archives_path_full archives_path=$(archives_path_base) archives_path_full=$(realpath --no-symlinks "$archives_path") local messages_language message_1 message_2 messages_language=$(messages_language) case "$messages_language" in ('fr') if [ $# -eq 1 ]; then message_1='Le fichier suivant est introuvable dans %s :\n' else message_1='Aucun des fichiers suivants nʼest présent dans %s :\n' fi message_2='Vous devez télécharger le fichier requis avant de continuer.\n' ;; ('en'|*) if [ $# -eq 1 ]; then message_1='The following file could not be found in %s:\n' else message_1='None of the following files could be found in %s:\n' fi message_2='Please download the required file before proceeding.\n' ;; esac print_message 'error' "$message_1" \ "$archives_path_full" information_archives_list "$@" > /dev/stderr print_message 'info' "$message_2" > /dev/stderr } # Error - The type of the given archive could not be guessed # USAGE: error_archive_type_not_set $archive error_archive_type_not_set() { local archive archive="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='ARCHIVE_TYPE nʼest pas défini pour %s et nʼa pas pu être détecté automatiquement.\n' ;; ('en'|*) message='ARCHIVE_TYPE is not set for %s and could not be guessed.\n' ;; esac print_message 'error' "$message" \ "$archive" } # Error - An integrity check failed # USAGE: error_hashsum_mismatch $file_path error_hashsum_mismatch() { local file_path file_path="$1" local file_name file_name=$(basename "$file_path") local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Somme de contrôle incohérente. %s nʼest pas le fichier attendu.\n' message="$message"'Utilisez --checksum=none pour forcer son utilisation.\n' ;; ('en'|*) message='Hashsum mismatch. %s is not the expected file.\n' message="$message"'Use --checksum=none to force its use.\n' ;; esac print_message 'error' "$message" \ "$file_name" } # Error - The available version of innoextract is too old # USAGE: error_innoextract_version_too_old $archive error_innoextract_version_too_old() { local archive archive="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La version de innoextract disponible sur ce système est trop ancienne pour extraire les données de lʼarchive suivante : %s\n' message="$message"'Des instructions de mise-à-jour sont proposées :\n' message="$message"'- pour Debian : %s\n' message="$message"'- pour Ubuntu : %s\n' ;; ('en'|*) message='Available innoextract version is too old to extract data from the following archive: %s\n' message="$message"'Update instructions are proposed:\n' message="$message"'- for Debian: %s\n' message="$message"'- for Ubuntu: %s\n' ;; esac ## TODO: The innoextract upgrade instructions should be available from the Debian / Ubuntu documentations, ## not from the ./play.it one. print_message 'error' "$message" \ "$archive" \ 'https://forge.dotslashplay.it/play.it/doc/-/wikis/user/distributions/debian#available-innoextract-version-is-too-old' \ 'https://forge.dotslashplay.it/play.it/doc/-/wikis/user/distributions/ubuntu#innoextract-version-is-too-old' } # Error - Invalid value used for archive type # USAGE: error_archive_type_invalid $archive_type error_archive_type_invalid() { local archive_type archive_type="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La valeur suivante ne correspond pas à un type dʼarchive connu : "%s"\n' ;; ('en'|*) message='The following value is not a valid archive type: "%s"\n' ;; esac print_message 'error' "$message" \ "$archive_type" } # Error - Invalid value used for archive extractor # USAGE: error_archive_extractor_invalid $archive_extractor error_archive_extractor_invalid() { local archive_extractor archive_extractor="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La valeur suivante ne correspond pas à un extracteur dʼarchive connu : "%s"\n' ;; ('en'|*) message='The following value is not a valid archive extractor: "%s"\n' ;; esac print_message 'error' "$message" \ "$archive_extractor" } # Error - Archive data extraction failed # USAGE: error_archive_extraction_failure $archive error_archive_extraction_failure() { local archive archive="$1" local archive_name archive_name=$(archive_name "$archive") local log_file log_file=$(archive_extraction_log_path) local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Lʼextraction des données depuis lʼarchive suivante a échoué : %s\n' message="$message"'Vous pouvez obtenir plus de détails dans le fichier journal : %s\n' ;; ('en'|*) message='Data extraction from the following archive failed: %s\n' message="$message"'You can get more details from the following log file: %s\n' ;; esac print_message 'error' "$message" \ "$archive_name" \ "$log_file" } src/30_content/00_common.sh0000644000000000000000000000465213120060140014434 0ustar rootroot# Print the default path to the game data in the archive # USAGE: content_path_default # RETURN: a path relative to the archive root content_path_default() { local content_path content_path=$(context_value 'CONTENT_PATH_DEFAULT') if [ -z "$content_path" ]; then error_missing_variable 'CONTENT_PATH_DEFAULT' return 1 fi printf '%s' "$content_path" } # Print the path to the game data in the archive for a given identifier # USAGE: content_path $content_id # RETURN: a path relative to the archive root # or an empty path if none is set content_path() { local content_id content_id="$1" # Use the context-specific content path if available local content_path content_path=$(context_value "CONTENT_${content_id}_PATH") ## Try to parse legacy variables for old game scripts. if [ -z "$content_path" ]; then content_path=$(content_path_legacy "$content_id") fi # Fall back on the default path for the current game engine if [ -z "$content_path" ]; then local game_engine game_engine=$(game_engine) case "$game_engine" in ('visionaire') content_path=$(visionaire_content_path "$content_id") ;; esac fi # Fall back to default content path if unset if [ -z "$content_path" ]; then # Do not fail if no default path is set content_path=$(content_path_default) 2>/dev/null || true fi printf '%s' "$content_path" } # Print the list of files to include from the archive for a given identifier # USAGE: content_files $content_id # RETURN: a list of paths relative to the path for the given identifier, # line breaks are used as separator between each item, # this list can include globbing patterns, # this list can be empty content_files() { local content_id content_id="$1" local content_files content_files=$(context_value "CONTENT_${content_id}_FILES") ## Try to parse legacy variables for old game scripts. if [ -z "$content_files" ]; then content_files=$(content_files_legacy "$content_id") fi # Fall back on the default files list for the current game engine if [ -z "$content_files" ]; then local game_engine game_engine=$(game_engine) case "$game_engine" in ('unity3d') content_files=$(unity3d_content_files_default "$content_id") ;; ('unrealengine4') content_files=$(unrealengine4_content_files_default "$content_id") ;; ('visionaire') content_files=$(visionaire_content_files "$content_id") ;; esac fi printf '%s' "$content_files" } src/30_content/10_files-inclusion.sh0000644000000000000000000003054513120060140016250 0ustar rootroot# Fetch icon files, convert them to the expected format, include them in the given package. # # This function is the one that should be called from game scripts, # it can take several applications as its arguments, # and default to handle all applications if none are explicitely given. # # USAGE: content_inclusion_icons [$package [$application…]] content_inclusion_icons() { # Do nothing if icons inclusion has been disabled. local option_icons option_icons=$(option_value 'icons') if [ "$option_icons" -eq 0 ]; then return 0 fi # Ensure that the commands required for icons extraction are available requirements_check_icons # Get the package that should include the icons. local package if [ $# -ge 1 ]; then package="$1" shift 1 else package=$(current_package) fi # If an optional icons archive has been provided, use that instead of the icons shipped with the game if archive_is_available 'ARCHIVE_ICONS'; then information_icons_inclusion content_inclusion_optional_icons_archive "$package" ## Return early if an optional icons archive has been used. return 0 fi # If no applications are explicitely listed, # try to fetch the icons for all applications. if [ "$#" -eq 0 ]; then applications_list=$(applications_list) ## If content_inclusion_icons has been called with no argument, the applications list should not be empty. if [ -z "$applications_list" ]; then error_applications_list_empty return 1 fi content_inclusion_icons "$package" $applications_list return 0 fi information_icons_inclusion local application for application in "$@"; do icons_inclusion_single_application "$package" "$application" done } # Fetch files from the archive, and include them into the package skeleton. # A list of default content identifiers is used. # USAGE: content_inclusion_default content_inclusion_default() { information_content_inclusion local packages_list packages_list=$(packages_list) local package unity3d_plugins for package in $packages_list; do unity3d_plugins=$(unity3d_plugins) if [ -n "$unity3d_plugins" ]; then content_inclusion_unity3d_plugins "$package" fi content_inclusion_default_libraries "$package" content_inclusion_default_fonts "$package" content_inclusion_default_game_data "$package" content_inclusion_default_documentation "$package" done # Including files used to apply tweaks to the WINE prefix should only be done in the packages including the game binaries. local package_architecture for package in $packages_list; do package_architecture=$(package_architecture "$package") case "$package_architecture" in ('64'|'32') content_inclusion_wineprefix_tweaks "$package" ;; esac done # content_inclusion_extra_libraries must be called in its own loop, at the end of the content inclusion process. # Since it can trigger an archive_extraction call leading to a set_standard_permissions call, # calling it earlier could lead to unwanted permissions reset. for package in $packages_list; do content_inclusion_extra_libraries "$package" done # Delete the remaining files extracted from archives but not included in any package ## Skip the automatic clean-up for game scripts targeting ./play.it ≤ 2.25. if ! compatibility_level_is_at_least '2.26'; then return 0 fi rm --force --recursive "${PLAYIT_WORKDIR}/gamedata" } # Fetch files from the archive, and include them into the package skeleton. # A list of default content identifiers is used, limited to native libraries for a single package. # USAGE: content_inclusion_default_libraries $package content_inclusion_default_libraries() { local package package="$1" local package_suffix files_list package_suffix="${package#PKG_}" files_list=$(context_name "CONTENT_LIBS_${package_suffix}_FILES") if [ -z "$files_list" ]; then ## Check if a default files list is set for the current engine, ## return early otherwise. local content_files content_files=$(content_files "LIBS_${package_suffix}") if [ -z "$content_files" ]; then return 0 fi fi local target_directory ## On Arch Linux and Gentoo the libraries path can change based on the package architecture. target_directory=$( set_current_package "$package" path_libraries ) content_inclusion "LIBS_${package_suffix}" "$package" "$target_directory" local index for index in $(seq 0 9); do files_list=$(context_name "CONTENT_LIBS${index}_${package_suffix}_FILES") if [ -z "$files_list" ]; then ## Stop looping at the first unset files list. return 0 fi content_inclusion "LIBS${index}_${package_suffix}" "$package" "$target_directory" done } # Fetch files from the archive, and include them into the package skeleton. # A list of default content identifiers is used, limited to TTF fonts for a single package. # USAGE: content_inclusion_default_fonts $package content_inclusion_default_fonts() { local package package="$1" local package_suffix files_list package_suffix="${package#PKG_}" files_list=$(context_name "CONTENT_FONTS_${package_suffix}_FILES") if [ -z "$files_list" ]; then ## Check if a default files list is set for the current engine, ## return early otherwise. local content_files content_files=$(content_files "FONTS_${package_suffix}") if [ -z "$content_files" ]; then return 0 fi fi local target_directory target_directory=$(path_fonts_ttf) content_inclusion "FONTS_${package_suffix}" "$package" "$target_directory" local index for index in $(seq 0 9); do files_list=$(context_name "CONTENT_FONTS${index}_${package_suffix}_FILES") if [ -z "$files_list" ]; then ## Stop looping at the first unset files list. return 0 fi content_inclusion "FONTS${index}_${package_suffix}" "$package" "$target_directory" done } # Fetch files from the archive, and include them into the package skeleton. # A list of default content identifiers is used, limited to game data files for a single package. # USAGE: content_inclusion_default_game_data $package content_inclusion_default_game_data() { local package package="$1" local package_suffix files_list package_suffix="${package#PKG_}" files_list=$(context_name "CONTENT_GAME_${package_suffix}_FILES") if [ -z "$files_list" ]; then ## Try to parse legacy variables for old game scripts. content_inclusion_default_game_data_legacy "$package" ## Check if a default files list is set for the current engine, ## return early otherwise. local content_files content_files=$(content_files "GAME_${package_suffix}") if [ -z "$content_files" ]; then return 0 fi fi local target_directory target_directory=$(path_game_data) content_inclusion "GAME_${package_suffix}" "$package" "$target_directory" local index for index in $(seq 0 9); do files_list=$(context_name "CONTENT_GAME${index}_${package_suffix}_FILES") if [ -z "$files_list" ]; then ## Stop looping at the first unset files list. return 0 fi content_inclusion "GAME${index}_${package_suffix}" "$package" "$target_directory" done } # Fetch files from the archive, and include them into the package skeleton. # A list of default content identifiers is used, limited to documentation files for a single package. # USAGE: content_inclusion_default_documentation $package content_inclusion_default_documentation() { local package package="$1" local package_suffix files_list package_suffix="${package#PKG_}" files_list=$(context_name "CONTENT_DOC_${package_suffix}_FILES") if [ -z "$files_list" ]; then ## Try to parse legacy variables for old game scripts. content_inclusion_default_game_documentation_legacy "$package" ## Check if a default files list is set for the current engine, ## return early otherwise. local content_files content_files=$(content_files "DOC_${package_suffix}") if [ -z "$content_files" ]; then return 0 fi fi local target_directory target_directory=$(path_documentation) content_inclusion "DOC_${package_suffix}" "$package" "$target_directory" local index for index in $(seq 0 9); do files_list=$(context_name "CONTENT_DOC${index}_${package_suffix}_FILES") if [ -z "$files_list" ]; then ## Stop looping at the first unset files list. return 0 fi content_inclusion "DOC${index}_${package_suffix}" "$package" "$target_directory" done } # Fetch files from the archive, and include them into the package skeleton. # USAGE: content_inclusion $content_id $package $target_path content_inclusion() { local content_id package target_path content_id="$1" package="$2" target_path="$3" information_content_inclusion # Check that the given package is valid if ! package_is_included_in_packages_list "$package"; then error_current_package_not_in_list "$package" return 1 fi # Return early if the content source path is not set. local content_path content_path=$(content_path "$content_id") if [ -z "$content_path" ]; then return 0 fi # Return early if the content source path does not exist. content_path_full="${PLAYIT_WORKDIR}/gamedata/${content_path}" if [ ! -e "$content_path_full" ]; then return 0 fi # Debian - Handle huge files by splitting them in 9GB chunks, # and include the chunks in dedicated packages. if [ "$content_id" = "GAME_${package#PKG_}" ]; then local option_package option_package=$(option_value 'package') if [ "$option_package" = 'deb' ]; then local huge_files huge_files=$(huge_files_list "$package") if [ -n "$huge_files" ]; then content_inclusion_chunks "$package" fi fi fi # Set path to destination, # ensuring it is an absolute path. local package_path destination_path package_path=$(package_path "$package") destination_path=$(realpath --canonicalize-missing "${package_path}${target_path}") # Proceed with the actual files inclusion content_inclusion_include_paths "$content_id" "$destination_path" } # Convert a list of patterns to include into a series of find options # USAGE: content_inclusion_find_options $content_identifier # RETURN: a full find options string, using null-byte as separator content_inclusion_find_options() { local content_identifier content_identifier="$1" local content_patterns_list content_patterns_list=$(content_files "$content_identifier") local content_pattern first_path_shown printf_format first_path_shown=0 printf '.\0(\0' while read -r content_pattern; do ## Skip empty lines. if [ -z "$content_pattern" ]; then continue fi if [ $first_path_shown -eq 0 ]; then printf_format='-path\0./%s' first_path_shown=1 else printf_format='\0-o\0-path\0./%s' fi ## Silence ShellCheck false-positive ## Don't use variables in the printf format string. Use printf "..%s.." "$foo". # shellcheck disable=SC2059 printf -- "$printf_format" "${content_pattern#./}" done <<- EOL $(printf '%s' "$content_patterns_list") EOL printf '\0)\0-print0' } # Display the full list of paths to include, using a null-byte as the separator # USAGE: content_inclusion_list_paths $content_identifier # RETURN: a sorted list of paths, separated by null-bytes content_inclusion_list_paths() { local content_identifier content_identifier="$1" local content_path content_path_full content_path=$(content_path "$content_identifier") content_path_full="${PLAYIT_WORKDIR}/gamedata/${content_path}" ( cd "$content_path_full" content_inclusion_find_options "$content_identifier" | xargs --null find ) | env --ignore-environment sort --zero-terminated } # Fetch a list of paths from the archives contents # USAGE: content_inclusion_include_paths $content_identifier $destination_path content_inclusion_include_paths() { local content_identifier destination_path content_identifier="$1" destination_path="$2" local content_path content_path_full paths_list_base64 content_path=$(content_path "$content_identifier") content_path_full="${PLAYIT_WORKDIR}/gamedata/${content_path}" mkdir --parents "$destination_path" ( cd "$content_path_full" ## The list of paths is encoded in base64, because it uses null bytes as a separator and null bytes can not be stored in a variable. paths_list_base64=$(content_inclusion_list_paths "$content_identifier" | base64 --wrap=0) printf '%s' "$paths_list_base64" | base64 --decode | xargs --null --no-run-if-empty cp \ --force \ --link \ --recursive \ --no-dereference \ --parents \ --preserve=links \ --target-directory "$destination_path" ## Delete paths that have already been copied, so they will not end up duplicated in the packages. printf '%s' "$paths_list_base64" | base64 --decode | xargs --null --no-run-if-empty rm \ --force \ --recursive ) } src/30_content/15_files-inclusion_extra-libraries.sh0000644000000000000000000003107113120060140021425 0ustar rootroot# Include required native libraries provided by extra archives # USAGE: content_inclusion_extra_libraries $package content_inclusion_extra_libraries() { local package package="$1" local libraries_required library_required libraries_required=$(dependencies_list_native_libraries "$package") while read -r library_required; do case "$library_required" in ('libcurl.so.4+CURL_OPENSSL_3') content_inclusion_extra_libraries_libcurl3 "$package" ;; ('libFLAC.so.8') content_inclusion_extra_libraries_libflac8 "$package" ;; ('libgconf-2.so.4') content_inclusion_extra_libraries_libgconf2 "$package" ;; ('libidn.so.11') content_inclusion_extra_libraries_libidn11 "$package" ;; ('libpng12.so.0') content_inclusion_extra_libraries_libpng12 "$package" ;; ('libssl.so.1.0.0') content_inclusion_extra_libraries_libssl100 "$package" ;; ('libssl.so.1.1') content_inclusion_extra_libraries_libssl11 "$package" ;; esac done <<- EOL $(printf '%s' "$libraries_required") EOL } # Include libcurl.so.3 and libcurl.so.4 including the CURL_OPENSSL_3 symbol # USAGE: content_inclusion_extra_libraries_libcurl3 $package content_inclusion_extra_libraries_libcurl3() { local package package="$1" # Set the list of files to include from the provided archive local package_architecture CONTENT_LIBCURL3_PATH CONTENT_LIBCURL3_FILES package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') ## Silence ShellCheck false-positive ## CONTENT_LIBCURL3_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBCURL3_PATH='x86_32' ;; ('64') ## Silence ShellCheck false-positive ## CONTENT_LIBCURL3_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBCURL3_PATH='x86_64' ;; esac ## Silence ShellCheck false-positive ## CONTENT_LIBCURL3_FILES appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBCURL3_FILES=' libcrypto.so.1.0.2 libssl.so.1.0.2 libcurl.so.3 libcurl.so.4 libcurl.so.4.4.0' # Proceed with the actual files inclusion local path_libraries path_libraries=$( set_current_package "$package" path_libraries ) content_inclusion 'LIBCURL3' "$package" "$path_libraries" # Update the list of dependencies on native libraries local extra_native_libraries_required extra_native_libraries_required=' libcom_err.so.2 libc.so.6 libdl.so.2 libgssapi_krb5.so.2 libidn2.so.0 libk5crypto.so.3 libkrb5.so.3 libnghttp2.so.14 libpsl.so.5 libpthread.so.0 librtmp.so.1 libssh2.so.1 libz.so.1' dependencies_add_native_libraries "$package" "$extra_native_libraries_required" } # Include libFLAC.so.8 # USAGE: content_inclusion_extra_libraries_libflac8 $package content_inclusion_extra_libraries_libflac8() { # On Arch Linux, this library is still provided in the packages repositories local option_package option_package=$(option_value 'package') case "$option_package" in ('arch') return 0 ;; esac local package package="$1" # Set the list of files to include from the provided archive local package_architecture CONTENT_LIBFLAC8_PATH CONTENT_LIBFLAC8_FILES package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') ## Silence ShellCheck false-positive ## CONTENT_LIBFLAC8_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBFLAC8_PATH='i386-linux-gnu' ;; ('64') ## Silence ShellCheck false-positive ## CONTENT_LIBFLAC8_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBFLAC8_PATH='x86_64-linux-gnu' ;; esac ## Silence ShellCheck false-positive ## CONTENT_LIBFLAC8_FILES appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBFLAC8_FILES=' libFLAC.so.8 libFLAC.so.8.3.0' # Proceed with the actual files inclusion local path_libraries path_libraries=$( set_current_package "$package" path_libraries ) content_inclusion 'LIBFLAC8' "$package" "$path_libraries" # Update the list of dependencies on native libraries local extra_native_libraries_required extra_native_libraries_required=' libc.so.6 libm.so.6 libogg.so.0' dependencies_add_native_libraries "$package" "$extra_native_libraries_required" } # Include GConf 2 library # USAGE: content_inclusion_extra_libraries_libgconf2 $package content_inclusion_extra_libraries_libgconf2() { local package package="$1" # Set the list of files to include from the provided archive local package_architecture CONTENT_LIBGCONF2_PATH CONTENT_LIBGCONF2_FILES package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') ## Silence ShellCheck false-positive ## CONTENT_LIBGCONF2_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBGCONF2_PATH='i386-linux-gnu' ;; ('64') ## Silence ShellCheck false-positive ## CONTENT_LIBGCONF2_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBGCONF2_PATH='x86_64-linux-gnu' ;; esac ## Silence ShellCheck false-positive ## CONTENT_LIBGCONF2_FILES appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBGCONF2_FILES=' libgconf-2.so.4 libgconf-2.so.4.1.5' # Proceed with the actual files inclusion local path_libraries path_libraries=$( set_current_package "$package" path_libraries ) content_inclusion 'LIBGCONF2' "$package" "$path_libraries" # Update the list of dependencies on native libraries local extra_native_libraries_required extra_native_libraries_required=' libc.so.6 libdbus-1.so.3 libdbus-glib-1.so.2 libglib-2.0.so.0 libgmodule-2.0.so.0 libgobject-2.0.so.0' dependencies_add_native_libraries "$package" "$extra_native_libraries_required" } # Include GNU Libidn library # USAGE: content_inclusion_extra_libraries_libidn11 $package content_inclusion_extra_libraries_libidn11() { # On Arch Linux, this library is still provided in the packages repositories local option_package option_package=$(option_value 'package') case "$option_package" in ('arch') return 0 ;; esac local package package="$1" # Set the list of files to include from the provided archive local package_architecture CONTENT_LIBIDN11_PATH CONTENT_LIBIDN11_FILES package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') ## Silence ShellCheck false-positive ## CONTENT_LIBIDN11_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBIDN11_PATH='i386-linux-gnu' ;; ('64') ## Silence ShellCheck false-positive ## CONTENT_LIBIDN11_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBIDN11_PATH='x86_64-linux-gnu' ;; esac ## Silence ShellCheck false-positive ## CONTENT_LIBIDN11_FILES appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBIDN11_FILES=' libidn.so.11 libidn.so.11.6.16' # Proceed with the actual files inclusion local path_libraries path_libraries=$( set_current_package "$package" path_libraries ) content_inclusion 'LIBIDN11' "$package" "$path_libraries" # Update the list of dependencies on native libraries local extra_native_libraries_required extra_native_libraries_required=' libc.so.6' dependencies_add_native_libraries "$package" "$extra_native_libraries_required" } # Include PNG 1.2 libraries # USAGE: content_inclusion_extra_libraries_libpng12 $package content_inclusion_extra_libraries_libpng12() { # On Arch Linux and Gentoo, this library is still provided in the packages repositories local option_package option_package=$(option_value 'package') case "$option_package" in ('arch'|'egentoo'|'gentoo') return 0 ;; esac local package package="$1" # Set the list of files to include from the provided archive local package_architecture CONTENT_LIBPNG12_PATH CONTENT_LIBPNG12_FILES package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') ## Silence ShellCheck false-positive ## CONTENT_LIBPNG12_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBPNG12_PATH='x86_32' ;; ('64') ## Silence ShellCheck false-positive ## CONTENT_LIBPNG12_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBPNG12_PATH='x86_64' ;; esac ## Silence ShellCheck false-positive ## CONTENT_LIBPNG12_FILES appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBPNG12_FILES=' libpng12.so.0 libpng12.so.0.50.0' # Proceed with the actual files inclusion local path_libraries path_libraries=$( set_current_package "$package" path_libraries ) content_inclusion 'LIBPNG12' "$package" "$path_libraries" # Update the list of dependencies on native libraries local extra_native_libraries_required extra_native_libraries_required=' libc.so.6 libm.so.6 libz.so.1' dependencies_add_native_libraries "$package" "$extra_native_libraries_required" } # Include OpenSSL 1.0.0 libraries # USAGE: content_inclusion_extra_libraries_libssl100 $package content_inclusion_extra_libraries_libssl100() { # On Arch Linux and Gentoo, this library is still provided in the packages repositories local option_package option_package=$(option_value 'package') case "$option_package" in ('arch'|'egentoo'|'gentoo') return 0 ;; esac local package package="$1" # Set the list of files to include from the provided archive local package_architecture CONTENT_OPENSSL100_PATH CONTENT_OPENSSL100_FILES package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') ## Silence ShellCheck false-positive ## CONTENT_OPENSSL100_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_OPENSSL100_PATH='x86_32' ;; ('64') ## Silence ShellCheck false-positive ## CONTENT_OPENSSL100_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_OPENSSL100_PATH='x86_64' ;; esac ## Silence ShellCheck false-positive ## CONTENT_OPENSSL100_FILES appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_OPENSSL100_FILES=' libcrypto.so.1.0.0 libssl.so.1.0.0' # Proceed with the actual files inclusion local path_libraries path_libraries=$( set_current_package "$package" path_libraries ) content_inclusion 'OPENSSL100' "$package" "$path_libraries" # Update the list of dependencies on native libraries local extra_native_libraries_required extra_native_libraries_required=' libc.so.6 libdl.so.2' dependencies_add_native_libraries "$package" "$extra_native_libraries_required" } # Include OpenSSL 1.1 libraries # USAGE: content_inclusion_extra_libraries_libssl11 $package content_inclusion_extra_libraries_libssl11() { # On Arch Linux and Gentoo, this library is still provided in the packages repositories local option_package option_package=$(option_value 'package') case "$option_package" in ('arch'|'egentoo'|'gentoo') return 0 ;; esac local package package="$1" # Set the list of files to include from the provided archive local package_architecture CONTENT_OPENSSL11_PATH CONTENT_OPENSSL11_FILES package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') ## Silence ShellCheck false-positive ## CONTENT_OPENSSL11_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_OPENSSL11_PATH='i386-linux-gnu' ;; ('64') ## Silence ShellCheck false-positive ## CONTENT_OPENSSL11_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_OPENSSL11_PATH='x86_64-linux-gnu' ;; esac ## Silence ShellCheck false-positive ## CONTENT_OPENSSL11_FILES appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_OPENSSL11_FILES=' libcrypto.so.1.1 libssl.so.1.1' # Proceed with the actual files inclusion local path_libraries path_libraries=$( set_current_package "$package" path_libraries ) content_inclusion 'OPENSSL11' "$package" "$path_libraries" # Update the list of dependencies on native libraries local extra_native_libraries_required extra_native_libraries_required=' libc.so.6 libdl.so.2 libpthread.so.0' dependencies_add_native_libraries "$package" "$extra_native_libraries_required" } src/30_content/15_files-inclusion_icon-packs.sh0000644000000000000000000000050513120060140020355 0ustar rootroot# Include the icons provided by an extra archive # USAGE: content_inclusion_optional_icons_archive $package content_inclusion_optional_icons_archive() { local package package="$1" # Proceed with the actual files inclusion local path_icons path_icons=$(path_icons) content_inclusion 'ICONS' "$package" "$path_icons" } src/30_content/15_files-inclusion_wineprefix-tweaks.sh0000644000000000000000000000213313120060140022001 0ustar rootroot# Include files required to apply tweaks to the WINE prefix # USAGE: content_inclusion_wineprefix_tweaks $package content_inclusion_wineprefix_tweaks() { local package package="$1" local wineprefix_tweaks wineprefix_tweak wineprefix_tweaks=$(wine_wineprefix_tweaks) while read -r wineprefix_tweak; do case "$wineprefix_tweak" in ('mono') content_inclusion_wineprefix_tweaks_mono "$package" ;; esac done <<- EOL $(printf '%s' "$wineprefix_tweaks") EOL } # Include the Mono .msi installer # USAGE: content_inclusion_wineprefix_tweaks_mono $package content_inclusion_wineprefix_tweaks_mono() { local package package="$1" local mono_installer_source package_path path_game_data mono_installer_name mono_installer_destination mono_installer_source=$(archive_path 'ARCHIVE_MONO') package_path=$(package_path "$package") path_game_data=$(path_game_data) mono_installer_name=$(archive_name 'ARCHIVE_MONO') mono_installer_destination="${package_path}${path_game_data}/wineprefix-tweaks/${mono_installer_name}" install -D --mode=644 "$mono_installer_source" "$mono_installer_destination" } src/30_content/20_huge-files.sh0000644000000000000000000001260313120060140015171 0ustar rootroot# List huge files for the given package # The paths should be relative to CONTENT_PATH_DEFAULT. # USAGE: huge_files_list $package # RETURN: a list of files, # one per line huge_files_list() { local package package="$1" local huge_files huge_files=$(context_value "HUGE_FILES_${package#PKG_}") # Return early if no list is set for the given package if [ -z "$huge_files" ]; then return 0 fi printf '%s' "$huge_files" | list_clean } # Split the given file into 9GB chunks # USAGE: huge_file_split $file_path huge_file_split() { local file_path file_path="$1" information_huge_file_split "$file_path" local content_path content_path=$(content_path_default) ( cd "${PLAYIT_WORKDIR}/gamedata/${content_path}" split --bytes=9G --numeric-suffixes=1 --suffix-length=1 \ "$file_path" \ "${file_path}." rm --force "$file_path" ) } # List the chunks generated from a given file # USAGE: huge_file_chunks_list $file_path # RETURN: a list of files, # one per line huge_file_chunks_list() { local file_path file_path="$1" local content_path content_path=$(content_path_default) ( cd "${PLAYIT_WORKDIR}/gamedata/${content_path}" find . -path "./${file_path}.?" | \ sort | \ sed 's#^\./##' ) } # Print the commands concatenating chunks into a single file # USAGE: huge_file_concatenate $file_path huge_file_concatenate() { local file_path file_path="$1" local path_game path_game=$(path_game_data) cat <<- EOF # Rebuild a huge file from its chunks huge_file='${path_game}/${file_path}' EOF cat <<- 'EOF' for huge_file_chunk in "${huge_file}."*; do if [ -e "$huge_file_chunk" ]; then case "${LANG%_*}" in ('fr') message='Reconstruction de %s à partir de ses parties…\n' ;; ('en'|*) message='Rebuilding %s from its chunks…\n' ;; esac printf "$message" "$huge_file" cat "${huge_file}."* > "$huge_file" rm "${huge_file}."* break fi done EOF } # Print the commands deleting a single file that has been built from its chunks # USAGE: huge_file_delete $file_path huge_file_delete() { local file_path file_path="$1" local path_game path_game=$(path_game_data) cat <<- EOF # Delete a huge file that has been built from its chunks huge_file='${path_game}/${file_path}' EOF cat <<- 'EOF' rm --force "$huge_file" EOF } # Split huge files in chunks, and include them in dedicated packages # USAGE: content_inclusion_chunks $package content_inclusion_chunks() { local package package="$1" local huge_files huge_files=$(huge_files_list "$package") # Return early if no list is set for the given package if [ -z "$huge_files" ]; then return 0 fi local huge_file chunks_list chunk_path chunks_counter chunks_counter=1 while read -r huge_file; do # Split the current huge files into 9GB chunks huge_file_split "$huge_file" # Include each chunk into a dedicated new package chunks_list=$(huge_file_chunks_list "$huge_file") while read -r chunk_path; do content_inclusion_chunk_single "$package" "$chunk_path" "$chunks_counter" chunks_counter=$((chunks_counter + 1)) done <<- EOL1 $(printf '%s' "$chunks_list") EOL1 # Set the postinst commands used to rebuild the file from its chunks ## The file deletion is done when removing the chunks packages (see content_inclusion_chunk_single), ## to avoid deleting it when reinstalling the main package without rebuilding it afterwards ## because the chunks have been deleted already. local postinst_commands extra_postinst_commands postinst_commands=$(package_postinst_actions "$package") extra_postinst_commands=$(huge_file_concatenate "$huge_file") export "${package}_POSTINST_RUN"="$postinst_commands $extra_postinst_commands" done <<- EOL2 $(printf '%s' "$huge_files") EOL2 } # Include a single chunk into a new dedicated package # USAGE: content_inclusion_chunk_single $package $chunk_path $chunks_counter content_inclusion_chunk_single() { local package chunk_path chunks_counter package="$1" chunk_path="$2" chunks_counter="$3" # Compute the new package identifier and its content identifier local package_identifier package_suffix content_id package_identifier="${package}_CHUNK${chunks_counter}" package_suffix="${package_identifier#PKG_}" content_id="GAME_${package_suffix}" # Add the new package to the list of packages to build local packages_list packages_list_variable packages_list=$(packages_list) packages_list_variable=$(context_name 'PACKAGES_LIST') export "${packages_list_variable:-PACKAGES_LIST}= $package_identifier $packages_list" # Set the new package properties local package_id package_description prerm_commands package_id=$(package_id "$package") package_description=$(package_description "$package") prerm_commands=$(huge_file_delete "$huge_file") export "${package_identifier}_ID"="${package_id}-chunk${chunks_counter}" export "${package_identifier}_DESCRIPTION"="${package_description} - chunk ${chunks_counter}" export "${package_identifier}_PRERM_RUN"="$prerm_commands" # Add the new package to the list of dependencies of its parent dependencies_add_generic "$package" "${package_id}-chunk${chunks_counter}" # Include the chunk into the new package local path_game path_game=$(path_game_data) export "CONTENT_${content_id}_FILES"="$chunk_path" content_inclusion "$content_id" "$package_identifier" "$path_game" } src/30_content/90_messages.sh0000644000000000000000000000236413120060140014762 0ustar rootroot# Information: Icons from the archives are included into packages paths # USAGE: information_icons_inclusion information_icons_inclusion() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Inclusion des icônes…\n' ;; ('en'|*) message='Including icons…\n' ;; esac print_message 'info' "$message" } # Information: Files from the archives are included into packages paths # USAGE: information_content_inclusion information_content_inclusion() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Inclusion des fichiers du jeu…\n' ;; ('en'|*) message='Including game files…\n' ;; esac print_message 'info_once' "$message" } # Information: A huge file is split into 9GB chunks # USAGE: information_huge_file_split $path_file information_huge_file_split() { local path_file path_file="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Découpage de %s en plusieurs fichiers…\n' ;; ('en'|*) message='Splitting %s into smaller chunks…\n' ;; esac print_message 'info' "$message" \ "$path_file" } src/30_icons/00_common.sh0000644000000000000000000001153313120060140014071 0ustar rootroot# Print the list of all icon identifiers. # USAGE: icons_list_all # RETURN: a list of icons identifiers, one per line, # or an empty string if no icon seems to be set icons_list_all() { local applications_list applications_list=$(applications_list) # Return early if there is no application set for the current game if [ -z "$applications_list" ]; then return 0 fi local icons_list application application_icons_list icons_list='' for application in $applications_list; do application_icons_list=$(application_icons_list "$application") icons_list="$icons_list $application_icons_list" done if [ -n "$icons_list" ]; then printf '%s\n' $icons_list fi } # Print the list of icon identifiers for the given application. # USAGE: application_icons_list $application # RETURN: a space-separated list of icons identifiers, # or an empty string if no icon seems to be set application_icons_list() { local application application="$1" # Use the value of APP_xxx_ICONS_LIST if it is set. local icons_list icons_list=$(context_value "${application}_ICONS_LIST") # Fall back on the default value of a single APP_xxx_ICON icon if [ -z "$icons_list" ]; then local default_icon default_icon=$(context_name "${application}_ICON") ## If a value is explicitly set for APP_xxx_ICON, ## it is considered to be the only icon for the current application. if [ -n "$default_icon" ]; then icons_list="$default_icon" fi fi ## If no value is set for APP_xxx_ICON, try to guess one from the game engine. if [ -z "$icons_list" ]; then local game_engine game_engine=$(game_engine) case "$game_engine" in ('unity3d') ## It is expected that Unity3D games always come with a single icon. icons_list="${application}_ICON" ;; esac fi ## If no value is set for APP_xxx_ICON, try to guess one from the application type. if [ -z "$icons_list" ]; then local application_type application_type=$(application_type "$application") case "$application_type" in ('wine') ## If no value is explicitly set for the icon of a WINE application, ## the game binary is used as a fallback icons source. icons_list="${application}_ICON" ;; esac fi printf '%s' "$icons_list" } # Print the application identifier for the given icon # USAGE: icon_application $icon # RETURN: the application identifier icon_application() { local icon icon="$1" # Look for an application identifier that share the same prefix than the given icon identifier. local application application_identifier applications_list applications_list=$(applications_list) ## The applications list should not be empty. if [ -z "$applications_list" ]; then error_applications_list_empty return 1 fi for application in $applications_list; do case "$icon" in ("${application}_"*) application_identifier="$application" break ;; esac done # Throw an error if no valid application identifier could be found. if [ -z "${application_identifier:-}" ]; then error_icon_application_not_found "$icon" return 1 fi printf '%s' "$application_identifier" } # Print the path to the source file for the given icon # USAGE: icon_path $icon # RETURN: the path to the file used to extract icons from, # it is relative to CONTENT_PATH_DEFAULT icon_path() { local icon icon="$1" local icon_path icon_path=$(context_value "$icon") ## If no value is set for the icon path, try to guess one from the game engine. if [ -z "$icon_path" ]; then local game_engine game_engine=$(game_engine) case "$game_engine" in ('unity3d') icon_path=$(unity3d_icon_path "$icon") ;; esac fi ## If no value is set for the icon path, try to guess one from the application type. if [ -z "$icon_path" ]; then local application application_type application=$(icon_application "$icon") application_type=$(application_type "$application") case "$application_type" in ('wine') icon_path=$(icon_wine_path "$icon") ;; esac fi # Check that the path to the icon is not empty if [ -z "$icon_path" ]; then error_icon_path_empty "$icon" return 1 fi printf '%s' "$icon_path" } # Print the wrestool options string for the given .exe icon # USAGE: icon_wrestool_options $icon # RETURN: the options string to pass to wrestool icon_wrestool_options() { local icon icon="$1" # Check that the given icon is a .exe file if ! icon_path "$icon" | grep --quiet '\.exe$'; then error_icon_unexpected_type "$icon" '*.exe' return 1 fi local wrestool_options wrestool_options=$(get_value "${icon}_WRESTOOL_OPTIONS") # Fall back on a default value based on the game engine if [ -z "$wrestool_options" ]; then local game_engine game_engine=$(game_engine) case "$game_engine" in ('unrealengine4') wrestool_options=$(unrealengine4_icon_wrestool_options_default) ;; (*) wrestool_options='--type=14' ;; esac fi printf '%s' "$wrestool_options" } src/30_icons/10_conversion.sh0000644000000000000000000001730713120060140014774 0ustar rootroot# Fetch icons from the archive contents, # convert them to PNG if they are not already in a supported format, # include them in the given package. # # This function handles all icons for a given application. # # USAGE: icons_inclusion_single_application $package $application icons_inclusion_single_application() { local package application package="$1" application="$2" local application_icons_list application_icons_list=$(application_icons_list "$application") # Throw an error if no icon was found for the given application. if [ -z "$application_icons_list" ]; then ## Skip the error if an icons archives is supported, as a non-blocking warning has already been shown. if [ -n "${ARCHIVE_OPTIONAL_ICONS_NAME:-}" ]; then return 0 fi error_no_icon_found "$application" return 1 fi local icon for icon in $application_icons_list; do icons_inclusion_single_icon "$package" "$application" "$icon" done } # Compute the full path to the icon source file # USAGE: icon_full_path $icon # RETURN: the full path to the file that provides icons, # as an absolute path icon_full_path() { local icon icon="$1" local content_path icon_path content_path=$(content_path_default) icon_path=$(icon_path "$icon") printf '%s/gamedata/%s/%s' "$PLAYIT_WORKDIR" "$content_path" "$icon_path" } # Fetch icon from the archive contents, # convert it to PNG if it is not already in a supported format, # include it in the given package. # # This function handles a single icon. # # USAGE: icons_inclusion_single_icon $package $application $icon icons_inclusion_single_icon() { local package application icon package="$1" application="$2" icon="$3" # Check for the existence of the icons source file local icon_path icon_path=$(icon_full_path "$icon") if [ ! -f "$icon_path" ]; then error_icon_file_not_found "$icon_path" return 1 fi icons_temporary_directory="${PLAYIT_WORKDIR}/icons" mkdir --parents "$icons_temporary_directory" icon_extract_png_from_file "$icon" "$icons_temporary_directory" icons_include_from_directory "$package" "$application" "$icons_temporary_directory" rmdir "$icons_temporary_directory" } # Convert the given file into .png icons # USAGE: icon_extract_png_from_file $icon $destination icon_extract_png_from_file() { local icon destination icon="$1" destination="$2" local icon_file icon_type icon_file=$(icon_full_path "$icon") icon_type=$(file_type "$icon_file") case "$icon_type" in ( \ 'application/vnd.microsoft.portable-executable' | \ 'application/x-dosexec' \ ) icon_extract_png_from_exe "$icon" "$destination" ;; ('image/png') icon_copy_png "$icon" "$destination" ;; ('image/vnd.microsoft.icon') icon_extract_png_from_ico "$icon" "$destination" ;; ( \ 'image/bmp' | \ 'image/x-ms-bmp' \ ) icon_convert_bmp_to_png "$icon" "$destination" ;; ( \ 'image/x-xpixmap' | \ 'image/x-xpmi' \ ) icon_copy_xpm "$icon" "$destination" ;; (*) error_icon_unsupported_type "$icon_file" "$icon_type" return 1 ;; esac } # Extract .png file(s) from the given .exe file # USAGE: icon_extract_png_from_exe $icon $destination icon_extract_png_from_exe() { local icon destination icon="$1" destination="$2" # Extract the .ico file(s) for the given .exe file icon_extract_ico_from_exe "$icon" "$destination" # Extract the .png file(s) from each previously extracted .ico file local inner_icon_file content_path content_path=$(content_path_default) for inner_icon_file in "$destination"/*.ico; do ( inner_icon_file_name=$(basename "$inner_icon_file") inner_icon_file_temporary_path="${PLAYIT_WORKDIR}/gamedata/${content_path}/${inner_icon_file_name}" mv "$inner_icon_file" "$inner_icon_file_temporary_path" export TMP_INNER_ICON="$inner_icon_file_name" icon_extract_png_from_ico 'TMP_INNER_ICON' "$destination" rm "$inner_icon_file_temporary_path" ) done } # Extract .ico file(s) from the given .exe file # USAGE: icon_extract_ico_from_exe $icon $destination icon_extract_ico_from_exe() { local icon destination icon="$1" destination="$2" local icon_file wrestool_options icon_file=$(icon_full_path "$icon") wrestool_options=$(icon_wrestool_options "$icon") ## Silence ShellCheck false-positive ## Double quote to prevent globbing and word splitting. # shellcheck disable=SC2086 wrestool $wrestool_options --extract --output="$destination" "$icon_file" 2>/dev/null # Check that at least one .ico file has been extracted, throw an error otherwise local ico_files_number ico_files_number=$(find "$destination" -name '*.ico' | wc --lines) if [ "$ico_files_number" -lt 1 ]; then error_no_ico_file_extracted "$icon" return 1 fi } # Convert the given .bmp file to .png # USAGE: icon_convert_bmp_to_png $icon $destination icon_convert_bmp_to_png() { local icon destination icon="$1" destination="$2" icon_convert_to_png "$icon" "$destination" } # Extract .png file(s) from the given .ico file # USAGE: icon_extract_png_from_ico $icon $destination icon_extract_png_from_ico() { local icon destination icon="$1" destination="$2" icon_convert_to_png "$icon" "$destination" } # Convert multiple icon formats supported by ImageMagick to .png # USAGE: icon_convert_to_png $icon $destination icon_convert_to_png() { local icon destination icon="$1" destination="$2" local icon_file file_name icon_file=$(icon_full_path "$icon") file_name=$(basename "$icon_file") convert "$icon_file" "${destination}/${file_name%.*}.png" } # Copy the given .png file to the given directory # USAGE: icon_copy_png $icon $destination icon_copy_png() { local icon destination icon="$1" destination="$2" local icon_file icon_file=$(icon_full_path "$icon") cp "$icon_file" "$destination" } # Copy the given .xpm file to the given directory # USAGE: icon_copy_xpm $icon $destination icon_copy_xpm() { local icon destination icon="$1" destination="$2" local icon_file icon_file=$(icon_full_path "$icon") cp "$icon_file" "$destination" } # Get icon files from the given directory and put them in the given package # USAGE: icons_include_from_directory $package $application $directory icons_include_from_directory() { local package application source_directory package="$1" application="$2" source_directory="$3" local package_path path_icons package_path=$(package_path "$package") path_icons=$(path_icons) local application_id application_id=$( set_current_package "$package" application_id "$application" ) # Get the icons from the given source directory, then move them to the given package local source_file icon_resolution destination_directory destination_path for source_file in \ "$source_directory"/*.png \ "$source_directory"/*.xpm do ## Skip the current pattern if it matched no file. if [ ! -e "$source_file" ]; then continue fi icon_resolution=$(icon_get_resolution "$source_file") destination_directory="${package_path}${path_icons}/${icon_resolution}/apps" destination_path="${destination_directory}/${application_id}.${source_file##*.}" mkdir --parents "$destination_directory" mv "$source_file" "$destination_path" done } # Return the resolution of the given image file # USAGE: icon_get_resolution $file # RETURNS: image resolution, using the format ${width}x${height} icon_get_resolution() { local image_file image_file="$1" # `identify` should be available when this function is called. # Exits with an explicit error if it is missing if ! command -v 'identify' >/dev/null 2>&1; then error_unavailable_command 'icon_get_resolution' 'identify' return 1 fi local image_resolution_string image_resolution image_resolution_string=$(identify "$image_file" | sed "s;^${image_file} ;;" | cut --delimiter=' ' --fields=2) image_resolution="${image_resolution_string%+0+0}" printf '%s' "$image_resolution" return 0 } src/30_icons/90_messages.sh0000644000000000000000000000740013120060140014417 0ustar rootroot# Error - An icon file could not be found # USAGE: error_icon_file_not_found $file error_icon_file_not_found() { local file file="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le fichier dʼicône suivant est introuvable : %s\n' message="$message"'Merci de signaler cette erreur sur notre outil de gestion de bugs : %s\n' ;; ('en'|*) message='The following icon file could not be found: %s\n' message="$message"'Please report this issue in our bug tracker: %s\n' ;; esac print_message 'error' "$message" \ "$file" \ "$PLAYIT_GAMES_BUG_TRACKER_URL" } # Error - The path to the given icon is not set # USAGE: error_icon_path_empty $icon error_icon_path_empty() { local icon icon="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='%s nʼest pas défini, mais il y a eu une tentative de récupérer le chemin de cette icône.\n' ;; ('en'|*) message='%s is not set, but there has been a request for this icon path.\n' ;; esac print_message 'error' "$message" \ "$icon" } # Error - An icon file with an unsupported MIME type has been provided # USAGE: error_icon_unsupported_type $icon_file $icon_type error_icon_unsupported_type() { local icon_file icon_type icon_file="$1" icon_type="$2" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le fichier dʼicône suivant est du type MIME "%s", qui nʼest pas pris en charge : %s\n' ;; ('en'|*) message='The following icon file is of the "%s" MIME type, that is not supported: %s\n' ;; esac print_message 'error' "$message" \ "$icon_type" \ "$icon_file" } # Error - No application identifier could be found related to the given icon identifier # USAGE: error_icon_application_not_found $icon_identifier error_icon_application_not_found() { local icon_identifier icon_identifier="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='L’identifiant d’icône fourni ne semble pas correspondre à une des applications prises en charge : %s\n' ;; ('en'|*) message='The given icon identifier does not seem to related to any of the supported applications: %s\n' ;; esac print_message 'error' "$message" \ "$icon_identifier" } # Error - No .ico file has been extracted from the given .exe file # USAGE: error_no_ico_file_extracted $icon_identifier error_no_ico_file_extracted() { local icon_identifier icon_identifier="$1" local icon_file wrestool_options icon_file=$(icon_full_path "$icon_identifier") wrestool_options=$(icon_wrestool_options "$icon_identifier") local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Aucun fichier d’icône n’a été extrait du fichier suivant : "%s"\n' message="$message"'Les options suivantes ont été passées à wrestool : %s\n' ;; ('en'|*) message='No icon file has been extracted from the following file: "%s"\n' message="$message"'The following options have been passed to wrestool: %s\n' ;; esac print_message 'error' "$message" \ "$icon_file" \ "$wrestool_options" } # Error - No icon is set for the given application # USAGE: error_no_icon_found $application error_no_icon_found() { local application application="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Aucune icône n’est définie pour l’application suivante : %s\n' ;; ('en'|*) message='No icon is set for the following application: %s\n' ;; esac print_message 'error' "$message" \ "$application" } src/30_instructions/00_common.sh0000644000000000000000000001002413120060140015514 0ustar rootroot# print installation instructions # USAGE: print_instructions $pkg[…] print_instructions() { # If no explicit list of packages has been passed, fall back on handling all packages if [ $# -eq 0 ]; then local packages_list packages_list=$(packages_list) print_instructions $packages_list return 0 fi # Print the list of runtime commands that have been skipped local unknown_commands unknown_commands=$(dependencies_unknown_commands_list) if [ -n "$unknown_commands" ]; then warning_dependencies_unknown_commands # Clear list of skipped libraries dependencies, # so it will not be shown again. dependencies_unknown_commands_clear fi # Print the list of library dependencies that have been skipped local unknown_libraries unknown_libraries=$(dependencies_unknown_libraries_list) if [ -n "$unknown_libraries" ]; then warning_dependencies_unknown_libraries # Clear list of skipped libraries dependencies, # so it will not be shown again. dependencies_unknown_libraries_clear fi # Print the list of Mono library dependencies that have been skipped local unknown_mono_libraries unknown_mono_libraries=$(dependencies_unknown_mono_libraries_list) if [ -n "$unknown_mono_libraries" ]; then warning_dependencies_unknown_mono_libraries # Clear list of skipped Mono libraries dependencies, # so it will not be shown again. dependencies_unknown_mono_libraries_clear fi # Print the list of GStreamer media format dependencies that have been skipped local unknown_gstreamer_media_formats unknown_gstreamer_media_formats=$(dependencies_unknown_gstreamer_media_formats_list) if [ -n "$unknown_gstreamer_media_formats" ]; then warning_dependencies_unknown_gstreamer_media_formats # Clear list of skipped media format dependencies, # so it will not be shown again. dependencies_unknown_gstreamer_media_formats_clear fi # Sort packages by architecture local package package_architecture packages_list_32 packages_list_64 packages_list_all packages_list_32='' packages_list_64='' packages_list_all='' for package in "$@"; do package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') packages_list_32="$packages_list_32 $package" ;; ('64') packages_list_64="$packages_list_64 $package" ;; (*) packages_list_all="$packages_list_all $package" ;; esac done if [ -s "$(dependency_gentoo_overlays_file)" ]; then information_required_gentoo_overlays fi local option_package option_package=$(option_value 'package') if [ "$option_package" = 'egentoo' ]; then info_local_overlay_gentoo fi local game_name game_name=$(game_name) information_installation_instructions_common "$game_name" # If both 32-bit and 64-bit binaries packages are available, # display instructions on how to install one build or the other. # If only a single architecture is available, display standard instructions. if [ -n "$packages_list_32" ] && [ -n "$packages_list_64" ]; then print_instructions_architecture_specific '32' $packages_list_all $packages_list_32 print_instructions_architecture_specific '64' $packages_list_all $packages_list_64 else local option_package option_package=$(option_value 'package') case $option_package in ('arch') print_instructions_arch "$@" ;; ('deb') debian_install_instructions "$@" ;; ('gentoo') print_instructions_gentoo "$@" ;; ('egentoo') print_instructions_egentoo "$@" ;; esac fi printf '\n' } # print installation instructions, for a given architecture # USAGE: print_instructions_architecture_specific $pkg[…] print_instructions_architecture_specific() { local architecture_variant architecture_variant="${1}-bit" information_installation_instructions_variant "$architecture_variant" shift 1 local option_package option_package=$(option_value 'package') case $option_package in ('arch') print_instructions_arch "$@" ;; ('deb') debian_install_instructions "$@" ;; ('gentoo') print_instructions_gentoo "$@" ;; ('egentoo') print_instructions_egentoo "$@" ;; esac } src/30_instructions/90_messages.sh0000644000000000000000000001236313120060140016054 0ustar rootroot# print common part of packages installation instructions # USAGE: information_installation_instructions_common $game_name information_installation_instructions_common() { local game_name game_name="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\nInstallez "%s" en lançant la série de commandes suivantes en root :\n' ;; ('en'|*) message='\nInstall "%s" by running the following commands as root:\n' ;; esac print_message 'info' "$message" \ "$game_name" } # print variant precision for packages installation instructions # USAGE: information_installation_instructions_variant $variant information_installation_instructions_variant() { local variant variant="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\nversion %s :\n' ;; ('en'|*) message='\n%s version:\n' ;; esac print_message 'info' "$message" \ "$variant" } # Display a list of unknown runtime commands from packages dependencies # USAGE: warning_dependencies_unknown_commands warning_dependencies_unknown_commands() { local messages_language message1 message2 messages_language=$(messages_language) case "$messages_language" in ('fr') message1='Certaines dépendances de ce jeu ne sont pas encore prises en charge par ./play.it' message1="$message1"', voici la liste de celles qui ont été ignorées :\n' message2='Merci de signaler cette liste sur notre système de suivi :\n%s\n' ;; ('en'|*) message1='Some dependencies of this game are not supported by ./play.it yet' message1="$message1"', here are the ones that have been skipped:\n' message2='Please report this list on our issues tracker:\n%s\n' ;; esac print_message 'warning' "$message1" local unknown_command while read -r unknown_command; do print_message 'info' '- %s\n' \ "$unknown_command" done <<- EOL $(dependencies_unknown_commands_list) EOL print_message 'info' "$message2" \ "$PLAYIT_BUG_TRACKER_URL" } # Display a list of unknown libraries from packages dependencies # USAGE: warning_dependencies_unknown_libraries warning_dependencies_unknown_libraries() { local messages_language message1 message2 messages_language=$(messages_language) case "$messages_language" in ('fr') message1='Certaines dépendances de ce jeu ne sont pas encore prises en charge par ./play.it' message1="$message1"', voici la liste de celles qui ont été ignorées :\n' message2='Merci de signaler cette liste sur notre système de suivi :\n%s\n' ;; ('en'|*) message1='Some dependencies of this game are not supported by ./play.it yet' message1="$message1"', here are the ones that have been skipped:\n' message2='Please report this list on our issues tracker:\n%s\n' ;; esac print_message 'warning' "$message1" local unkown_library while read -r unkown_library; do print_message 'info' '- %s\n' \ "$unkown_library" done <<- EOL $(dependencies_unknown_libraries_list) EOL print_message 'info' "$message2" \ "$PLAYIT_BUG_TRACKER_URL" } # Display a list of unknown Mono libraries from packages dependencies # USAGE: warning_dependencies_unknown_mono_libraries warning_dependencies_unknown_mono_libraries() { local messages_language message1 message2 messages_language=$(messages_language) case "$messages_language" in ('fr') message1='Certaines dépendances Mono de ce jeu ne sont pas encore prises en charge par ./play.it' message1="$message1"', voici la liste de celles qui ont été ignorées :\n' message2='Merci de signaler cette liste sur notre système de suivi :\n%s\n' ;; ('en'|*) message1='Some Mono dependencies of this game are not supported by ./play.it yet' message1="$message1"', here are the ones that have been skipped:\n' message2='Please report this list on our issues tracker:\n%s\n' ;; esac print_message 'warning' "$message1" local unkown_mono_library while read -r unkown_mono_library; do print_message 'info' '- %s\n' \ "$unkown_mono_library" done <<- EOL $(dependencies_unknown_mono_libraries_list) EOL print_message 'info' "$message2" \ "$PLAYIT_BUG_TRACKER_URL" } # Display a list of unknown GStreamer media formats from packages dependencies # USAGE: warning_dependencies_unknown_gstreamer_media_formats warning_dependencies_unknown_gstreamer_media_formats() { local messages_language message1 message2 messages_language=$(messages_language) case "$messages_language" in ('fr') message1='Certains formats multimédia requis par ce jeu ne sont pas encore pris en charge par ./play.it' message1="$message1"', voici la liste de ceux qui ont été ignorés :\n' message2='Merci de signaler cette liste sur notre système de suivi :\n%s\n' ;; ('en'|*) message1='Some media formats required by this game are not supported by ./play.it yet' message1="$message1"', here are the ones that have been skipped:\n' message2='Please report this list on our issues tracker:\n%s\n' ;; esac print_message 'warning' "$message1" local media_format while read -r media_format; do print_message 'info' '- %s\n' \ "$media_format" done <<- EOL $(dependencies_unknown_gstreamer_media_formats_list) EOL print_message 'info' "$message2" \ "$PLAYIT_BUG_TRACKER_URL" } src/30_launchers/00_common.sh0000644000000000000000000002174213120060140014745 0ustar rootroot# Write launcher scripts and menu entries. # # This function can take several applications as its arguments, # and default to handle all applications if none are explicitely given. # # USAGE: launchers_generation [$package [$application…]] launchers_generation() { local package if [ $# -ge 1 ]; then package="$1" shift 1 else package=$(current_package) fi # If no applications are explicitely listed, # write launchers and menu entries for all applications. if [ "$#" -eq 0 ]; then applications_list=$(applications_list) ## If launchers_generation has been called with no argument, the applications list should not be empty. if [ -z "$applications_list" ]; then error_applications_list_empty return 1 fi launchers_generation "$package" $applications_list return 0 fi information_launchers_generation local application for application in "$@"; do launcher_generation_checks "$package" "$application" launcher_write_script "$package" "$application" launcher_write_desktop "$package" "$application" done } # Run checks to ensure all required information for the launcher generation is set # USAGE: launcher_generation_checks $package $application launcher_generation_checks() { local package application package="$1" application="$2" # Ensure that the application type is set, or can be guessed local application_type application_type=$( set_current_package "$package" application_type "$application" ) if [ -z "$application_type" ]; then error_no_application_type "$application" return 1 fi # If the current application type relies on a game binary, ensure its path is set case "$application_type" in ('custom') ## Custom launchers might not rely on a provided binary. ;; ('renpy') ## Ren'Py games do not rely on a provided binary. ;; ('scummvm') ## ScummVM games do not rely on a provided binary, ## but they expect a ScummVM game ID to be set. local application_scummid application_scummid=$(application_scummvm_scummid "$application") if [ -z "$application_scummid" ]; then error_application_scummid_invalid "$application" "$application_scummid" return 1 fi ;; (*) local application_exe application_exe=$( set_current_package "$package" application_exe "$application" ) if [ -z "$application_exe" ]; then error_application_exe_empty "$application" 'launcher_generation_checks' return 1 fi ;; esac # If the current application type relies on a game binary, ensure that it can be found if [ -n "${application_exe:-}" ]; then ## A dedicated function is used here to make it easier to override from game scripts. if ! launcher_target_presence_check "$package" "$application"; then error_launcher_missing_binary "$application_exe" return 1 fi fi } # Check that the launcher target exists # USAGE: launcher_target_presence_check $package $application # RETURN: 0 if the game binary has been found, # 1 if the game binary has not been found launcher_target_presence_check() { local package application package="$1" application="$2" local application_exe application_exe_path application_exe=$( set_current_package "$package" application_exe "$application" ) application_exe_path=$( set_current_package "$package" application_exe_path "$application_exe" ) test -f "$application_exe_path" } # Write the launcher script for the given application. # USAGE: launcher_write_script $package $application launcher_write_script() { local package application package="$1" application="$2" local launcher_path launcher_directory launcher_path=$(launcher_path "$package" "$application") launcher_directory=$(dirname "$launcher_path") mkdir --parents "$launcher_directory" touch "$launcher_path" chmod 755 "$launcher_path" ## The *_launcher functions are called on their own first, to avoid being spawned in a subshell. This prevents their return code from being lost. ## The package context is always set to ensure context-sensitive values for runtime options are used. local application_type launcher_content application_type=$( set_current_package "$package" application_type "$application" ) case "$application_type" in ('custom') launcher_content=$( set_current_package "$package" custom_launcher "$application" ) printf '%s' "$launcher_content" | snippet_clean > "$launcher_path" ;; ('dosbox') launcher_content=$( set_current_package "$package" dosbox_launcher "$application" ) printf '%s' "$launcher_content" | snippet_clean > "$launcher_path" dependencies_add_command "$package" 'dosbox' ;; ('java') launcher_content=$( set_current_package "$package" java_launcher "$application" ) printf '%s' "$launcher_content" | snippet_clean > "$launcher_path" dependencies_add_command "$package" 'java' ;; ('mono') launcher_content=$( set_current_package "$package" mono_launcher "$application" ) printf '%s' "$launcher_content" | snippet_clean > "$launcher_path" dependencies_add_command "$package" 'mono' ;; ('native') launcher_content=$( set_current_package "$package" native_launcher "$application" ) printf '%s' "$launcher_content" | snippet_clean > "$launcher_path" ## Add execution permissions to the game binary file. local application_exe application_exe_path application_exe=$( set_current_package "$package" application_exe "$application" ) application_exe_path=$( set_current_package "$package" application_exe_path "$application_exe" ) chmod +x "$application_exe_path" ;; ('renpy') launcher_content=$( set_current_package "$package" renpy_launcher "$application" ) printf '%s' "$launcher_content" | snippet_clean > "$launcher_path" dependencies_add_command "$package" 'renpy' ;; ('scummvm') launcher_content=$( set_current_package "$package" scummvm_launcher "$application" ) printf '%s' "$launcher_content" | snippet_clean > "$launcher_path" dependencies_add_command "$package" 'scummvm' ;; ('wine') launcher_content=$( set_current_package "$package" wine_launcher "$application" ) printf '%s' "$launcher_content" | snippet_clean > "$launcher_path" dependencies_add_command "$package" 'wine' ## Add a package dependency on winetricks if the current game relies on some winetricks verbs. local winetricks_verbs winetricks_verbs=$(wine_winetricks_verbs) if [ -n "$winetricks_verbs" ]; then dependencies_add_command "$package" 'winetricks' fi ## Add package dependencies on winetricks and rendering libraries if a non-default Direct3D renderer is required. local direct3d_renderer direct3d_renderer=$(wine_renderer_name) case "$direct3d_renderer" in ('wined3d/'*) dependencies_add_command "$package" 'winetricks' local wined3d_backend wined3d_backend=$(printf '%s' "$direct3d_renderer" | cut --delimiter='/' --fields=2) case "$wined3d_backend" in ('gl') dependencies_add_native_libraries "$package" 'libGL.so.1' ;; ('vulkan') dependencies_add_native_libraries "$package" 'libvulkan.so.1' ;; esac ;; ('dxvk') dependencies_add_command "$package" 'winetricks' dependencies_add_native_libraries "$package" 'libvulkan.so.1' ;; ('vkd3d') dependencies_add_command "$package" 'winetricks' dependencies_add_native_libraries "$package" 'libvulkan.so.1' ;; esac ;; esac } # Print the path to the launcher script for the given application. # USAGE: launcher_path $package $application # RETURN: The absolute path to the launcher launcher_path() { local package application package="$1" application="$2" local package_path path_binaries application_id package_path=$(package_path "$package") path_binaries=$(path_binaries) application_id=$( set_current_package "$package" application_id "$application" ) printf '%s%s/%s' "$package_path" "$path_binaries" "$application_id" } # Print the headers common to all launcher scripts # USAGE: launcher_headers launcher_headers() { cat <<- EOF #!/bin/sh # script generated by ./play.it $LIBRARY_VERSION - https://www.dotslashplay.it/ set -o errexit EOF } # Print the exit actions common to all launcher scripts # USAGE: launcher_exit launcher_exit() { cat <<- 'EOF' # Return the game exit code if [ -n "$game_exit_status" ]; then exit "$game_exit_status" else exit 0 fi EOF } # Print the line starting the game # USAGE: game_exec_line $application # RETURN: the command to execute, including its command line options game_exec_line() { local application application="$1" local application_type application_type=$(application_type "$application") case "$application_type" in ('dosbox') dosbox_exec_line "$application" ;; ('java') java_exec_line "$application" ;; ('mono') mono_exec_line "$application" ;; ('native') native_exec_line "$application" ;; ('renpy') renpy_exec_line "$application" ;; ('scummvm') scummvm_exec_line "$application" ;; ('wine') wine_exec_line "$application" ;; esac } src/30_launchers/10_desktop.sh0000644000000000000000000000566313120060140015133 0ustar rootroot# Write the XDG desktop file for the given application # USAGE: launcher_write_desktop $package $application launcher_write_desktop() { local package application package="$1" application="$2" local desktop_file desktop_directory desktop_file=$(launcher_desktop_filepath "$package" "$application") desktop_directory=$(dirname "$desktop_file") mkdir --parents "$desktop_directory" ( set_current_package "$package" launcher_desktop "$application" > "$desktop_file" ) } # Print the content of the XDG desktop file for the given application # USAGE: launcher_desktop $application # RETURN: the full content of the XDG desktop file launcher_desktop() { local application application="$1" local application_name application_category desktop_field_exec desktop_field_icon application_name=$(application_name "$application") application_category=$(application_category "$application") desktop_field_exec=$(desktop_field_exec "$application") desktop_field_icon=$(desktop_field_icon "$application") cat <<- EOF [Desktop Entry] Version=1.0 Type=Application Name=$application_name Icon=$desktop_field_icon Exec=$desktop_field_exec Categories=$application_category EOF } # Print the full path to the XDG desktop file for the given application # USAGE: launcher_desktop_filepath $package $application # RETURN: an absolute file path launcher_desktop_filepath() { local package application package="$1" application="$2" local application_id package_path path_xdg_desktop application_id=$( set_current_package "$package" application_id "$application" ) package_path=$(package_path "$package") path_xdg_desktop=$(path_xdg_desktop) printf '%s/%s.desktop' \ "${package_path}${path_xdg_desktop}" \ "$application_id" } # Print the XDG desktop "Exec" field for the given application # USAGE: desktop_field_exec $application # RETURN: The "Exec" field content, including escaping if required desktop_field_exec() { local application application="$1" local option_prefix application_id exec_field option_prefix=$(option_value 'prefix') application_id=$(application_id "$application") case "$option_prefix" in ## Standard path, only the command name is required. ('/usr'|'/usr/local') exec_field="$application_id" ;; ## Non-standard path including spaces, the full path enclosed in quotes is required. (*' '*) local path_binaries path_binaries=$(path_binaries) exec_field="'${path_binaries}/${application_id}'" ;; ## Non-standard path not including spaces, the full path is required. (*) local path_binaries path_binaries=$(path_binaries) exec_field="${path_binaries}/${application_id}" ;; esac printf '%s' "$exec_field" } # Print the XDG desktop "Icon" field for the given application # USAGE: desktop_field_icon $application # RETURN: The "Icon" field content desktop_field_icon() { local application application="$1" local icon_field icon_field=$(application_id "$application") printf '%s' "$icon_field" } src/30_launchers/10_fake-home.sh0000644000000000000000000000475113120060140015313 0ustar rootroot# Enable the fake $HOME # USAGE: fake_home_enable fake_home_enable() { cat <<- 'EOF' # Enable the fake $HOME HOME_PATH_REAL="$HOME" HOME_PATH_FAKE=$(fake_home_path) export HOME="$HOME_PATH_FAKE" EOF } # Disable the fake $HOME # USAGE: fake_home_disable fake_home_disable() { cat <<- 'EOF' # Disable the fake $HOME export HOME="$HOME_PATH_REAL" EOF } # Print the paths relative to the fake $HOME that should be diverted to persistent storage # USAGE: fake_home_persistent_directories # RETURN: A list of path to directories, # separated by line breaks. fake_home_persistent_directories() { local persistent_directories persistent_directories=$(context_value 'FAKE_HOME_PERSISTENT_DIRECTORIES') printf '%s' "$persistent_directories" } # Handle paths diversion from the fake $HOME to persistent storage # USAGE: fake_home_persistent fake_home_persistent() { local persistent_directories persistent_directories=$(fake_home_persistent_directories) cat <<- 'EOF' # Divert paths from the fake $HOME to persistent storage fake_home_path() { # Fake $HOME path can be explicitely set using an environment variable if [ -n "$PLAYIT_FAKE_HOME_PATH" ]; then printf '%s' "$PLAYIT_FAKE_HOME_PATH" return 0 fi # Compute the default fake $HOME path if none has been explicitely set printf '%s/play.it/home/%s' \ "${XDG_CACHE_HOME:="$HOME/.cache"}" \ "$GAME_ID" } FAKE_HOME_PATH=$(fake_home_path) ## Divert paths set by the XDG Base Directory Specification ## cf. https://specifications.freedesktop.org/basedir-spec/basedir-spec-0.8.html while read -r xdg_path_absolute; do if printf '%s' "$xdg_path_absolute" | grep --quiet --regexp="^${HOME}/"; then xdg_path_relative=$(printf '%s' "$xdg_path_absolute" | sed "s#^${HOME}/##") persistent_path_diversion "$FAKE_HOME_PATH" "$HOME" "$xdg_path_relative" fi done << EOL ${XDG_CACHE_HOME:-${HOME}/.cache} ${XDG_CONFIG_HOME:-${HOME}/.config} ${XDG_DATA_HOME:-${HOME}/.local/share} ${XDG_STATE_HOME:-${HOME}/.local/state} EOL unset xdg_path_absolute EOF cat <<- EOF ## Divert paths specific to the current game FAKE_HOME_PERSISTENT_DIRECTORIES="$persistent_directories" EOF cat <<- 'EOF' while read -r directory; do if [ -z "$directory" ]; then continue fi persistent_path_diversion "$FAKE_HOME_PATH" "${USER_PERSISTENT_PATH}/fake-home" "$directory" done << EOL $(printf '%s' "$FAKE_HOME_PERSISTENT_DIRECTORIES") EOL unset directory EOF } src/30_launchers/10_prefixes.sh0000644000000000000000000000445513120060140015305 0ustar rootroot# Print function computing the path to the game prefix # USAGE: prefix_function_prefix_path prefix_function_prefix_path() { cat <<- 'EOF' # Compute the path to game prefix for the current session prefix_path() { # Prefix path can be explicitely set using an environment variable if [ -n "$PLAYIT_PREFIX_PATH" ]; then printf '%s' "$PLAYIT_PREFIX_PATH" return 0 fi # Compute the default prefix path if none has been explicitely set printf '%s/play.it/prefixes/%s' \ "${XDG_CACHE_HOME:="$HOME/.cache"}" \ "$GAME_ID" } EOF } # Populate prefix with symbolic links to all game files # USAGE: prefix_generate_links_farm prefix_generate_links_farm() { cat <<- 'EOF' # Populate prefix with symbolic links to all game files prefix_generate_links_farm() { ## Remove links to game directories ( cd "$PATH_GAME" find . -type d | while read -r directory; do if [ -h "${PATH_PREFIX}/${directory}" ]; then rm "${PATH_PREFIX}/${directory}" fi done unset directory ) ## Populate prefix with links to all game files. cp \ --dereference --no-target-directory --recursive --remove-destination --symbolic-link \ "$PATH_GAME" "$PATH_PREFIX" ## Remove dangling links and non-game empty directories. ( cd "$PATH_PREFIX" find . -type l | while read -r link; do if [ ! -e "$link" ]; then rm "$link" fi done find . -depth -type d | while read -r directory; do if [ ! -e "${PATH_GAME}/${directory}" ]; then rmdir --ignore-fail-on-non-empty "$directory" fi done unset link directory ) } EOF } # Print the functions used to generate a symlinks prefix # USAGE: launcher_prefix_symlinks_functions launcher_prefix_symlinks_functions() { cat <<- 'EOF' # Set userdir- and prefix-related functions EOF prefix_function_prefix_path prefix_generate_links_farm } # Print the actions used to build a symlinks prefix # USAGE: launcher_prefix_symlinks_build launcher_prefix_symlinks_build() { cat <<- 'EOF' # Build user prefix PATH_PREFIX=$(prefix_path) mkdir --parents "$PATH_PREFIX" prefix_generate_links_farm EOF } src/30_launchers/20_persistent-user-data.sh0000644000000000000000000002305613120060140017542 0ustar rootroot# List the directories from the game prefix that should be diverted to a persistent path # USAGE: persistent_list_directories # RETURNS: a list of paths to directories, separated by line breaks # glob patterns can be included persistent_list_directories() { local persistent_directories persistent_directories=$(context_value 'USER_PERSISTENT_DIRECTORIES') # Fall back on the legacy CONFIG_DIRS / DATA_DIRS variables. if [ -z "$persistent_directories" ]; then persistent_directories=$(persistent_list_directories_legacy) fi printf '%s' "$persistent_directories" | list_clean } # List the files from the game prefix that should be diverted to a persistent path # USAGE: persistent_list_files # RETURNS: a list of paths to files, separated by line breaks # glob patterns can be included persistent_list_files() { local persistent_files persistent_files=$(context_value 'USER_PERSISTENT_FILES') # Fall back on the legacy CONFIG_FILES / DATA_FILES variables. if [ -z "$persistent_files" ]; then persistent_files=$(persistent_list_files_legacy) fi printf '%s' "$persistent_files" | list_clean } # Set path for persistent storage of user data, and populate the game prefix from the persistent storage # USAGE: persistent_storage_initialization persistent_storage_initialization() { cat <<- 'EOF' # Set path for persistent storage of user data persistent_user_path() { ## The path can be explicitely set using an environment variable if [ -n "$PLAYIT_PERSISTENT_USER_PATH" ]; then printf '%s' "$PLAYIT_PERSISTENT_USER_PATH" return 0 fi ## Compute the default path if none has been explicitly set printf '%s/games/%s' \ "${XDG_DATA_HOME:=$HOME/.local/share}" \ "$GAME_ID" } USER_PERSISTENT_PATH=$(persistent_user_path) mkdir --parents "$USER_PERSISTENT_PATH" # Populate the prefix from persistent files ( cd "$USER_PERSISTENT_PATH" find -L . -type f ! -path './wine/*' | while read -r file; do persistent_file="${USER_PERSISTENT_PATH}/${file}" prefix_file="${PATH_PREFIX}/${file}" if \ [ ! -e "$prefix_file" ] || \ [ "$(realpath "$prefix_file")" != "$(realpath "$persistent_file")" ] then mkdir --parents "$(dirname "$prefix_file")" ln --symbolic --force --no-target-directory \ "$persistent_file" \ "$prefix_file" fi done unset file persistent_file prefix_file ) EOF } # Set the common actions required for directories and files diversion to persistent storage # This is required for the diversions using one of the following variables: # - USER_PERSISTENT_DIRECTORIES # - USER_PERSISTENT_FILES # USAGE: persistent_storage_common persistent_storage_common() { local persistent_list_directories persistent_list_files persistent_list_directories=$(persistent_list_directories) persistent_list_files=$(persistent_list_files) # Return early if the current game script does not use paths diversion if \ [ -z "$persistent_list_directories" ] && \ [ -z "$persistent_list_files" ] then return 0 fi cat <<- 'EOF' # Expand a path pattern into a list of existing paths. # If the pattern can not be expanded, it is printed as-is instead. expand_path_pattern() { pattern="$1" ## Silently skip empty patterns if [ -z "$pattern" ]; then return 0 fi expanded_paths=$(find . -path "./${pattern#./}") if [ -n "$expanded_paths" ]; then printf '%s\n' "$expanded_paths" else printf '%s\n' "$pattern" fi unset pattern expanded_paths } EOF } # Set the action used to divert a given path to persistent storage # This is required for the diversions using one of the following variables: # - FAKE_HOME_PERSISTENT_DIRECTORIES # - USER_PERSISTENT_DIRECTORIES # - WINE_PERSISTENT_DIRECTORIES # USAGE: persistent_path_diversion persistent_path_diversion() { local fake_home_persistent_directories user_persistent_directories wine_persistent_directories fake_home_persistent_directories=$(fake_home_persistent_directories) user_persistent_directories=$(persistent_list_directories) wine_persistent_directories=$(wine_persistent_directories) # Return early if the current game script does not use paths diversion if \ [ -z "$fake_home_persistent_directories" ] && \ [ -z "$user_persistent_directories" ] && \ [ -z "$wine_persistent_directories" ] then return 0 fi cat <<- 'EOF' # Replace a given directory in a prefix by a link to another directory in persistent storage # USAGE: persistent_path_diversion $path_source $path_destination $directory persistent_path_diversion() { path_source="$1" path_destination="$2" directory="$3" ## If the target directory does not already exist in persistent storage, ## copy it from the prefix (if existing) or create a new empty one. if [ ! -e "${path_destination}/${directory}" ]; then if [ -e "${path_source}/${directory}" ]; then ( cd "$path_source" cp --dereference --parents --recursive \ "$directory" \ "$path_destination" ) else mkdir --parents "${path_destination}/${directory}" fi fi ## Replace the directory in the prefix by a link to the one in persistent storage. if [ ! -h "${path_source}/${directory}" ]; then directory_parent=$(dirname "${path_source}/${directory}") rm --recursive --force "${path_source:?}/${directory}" mkdir --parents "$directory_parent" ln --symbolic "${path_destination}/${directory}" "${path_source}/${directory}" fi unset directory_parent unset path_source path_destination directory } EOF } # Update directories diversions to persistent storage # USAGE: persistent_storage_update_directories persistent_storage_update_directories() { local persistent_list_directories persistent_list_directories=$(persistent_list_directories) # Return early if the current game script does not use directories diversion if [ -z "$persistent_list_directories" ]; then return 0 fi cat <<- EOF # Update directories diversions to persistent storage USER_PERSISTENT_DIRECTORIES='${persistent_list_directories}' EOF cat <<- 'EOF' ( cd "$PATH_PREFIX" while read -r directory_pattern; do ## Skip empty patterns if [ -z "$directory_pattern" ]; then continue fi while read -r directory; do persistent_path_diversion "$PATH_PREFIX" "$USER_PERSISTENT_PATH" "$directory" done <<- EOL $(expand_path_pattern "$directory_pattern") EOL done <<- EOL $(printf '%s' "$USER_PERSISTENT_DIRECTORIES") EOL unset directory_pattern ) EOF } # Update files diversions to persistent storage # USAGE: persistent_storage_update_files persistent_storage_update_files() { local persistent_list_files persistent_list_files=$(persistent_list_files) # Return early if the current game script does not use files diversion if [ -z "$persistent_list_files" ]; then return 0 fi cat <<- EOF # Update files diversions to persistent storage USER_PERSISTENT_FILES='${persistent_list_files}' EOF cat <<- 'EOF' ( cd "$PATH_PREFIX" while read -r file_pattern; do ## Skip empty patterns if [ -z "$file_pattern" ]; then continue fi while read -r file; do ## If the target file does not already exist in persistent storage, ## copy it from the prefix (if existing). if [ ! -e "${USER_PERSISTENT_PATH}/${file}" ]; then if [ -e "$file" ]; then cp --dereference --parents \ "$file" \ "${USER_PERSISTENT_PATH}" fi fi ## Replace the file in the prefix by a link to the one in persistent storage, ## if such a file already exists in persistent storage. if [ -e "${USER_PERSISTENT_PATH}/${file}" ]; then file_parent=$(dirname "$file") rm --force "$file" mkdir --parents "$file_parent" ln --symbolic "${USER_PERSISTENT_PATH}/${file}" "$file" fi done <<- EOL $(expand_path_pattern "$file_pattern") EOL unset file done <<- EOL $(printf '%s' "$USER_PERSISTENT_FILES") EOL unset file_pattern ) EOF } # Update persistent storage with files from the current prefix # USAGE: persistent_storage_update_files_from_prefix persistent_storage_update_files_from_prefix() { local persistent_list_files persistent_list_files=$(persistent_list_files) # Return early if the current game script does not use files diversion if [ -z "$persistent_list_files" ]; then return 0 fi cat <<- 'EOF' # Update persistent storage with files from the current prefix ( cd "$PATH_PREFIX" while read -r path_pattern; do ## Skip empty patterns if [ -z "$path_pattern" ]; then continue fi while read -r path; do if [ -f "$path" ] && [ ! -h "$path" ]; then cp --parents --remove-destination "$path" "$USER_PERSISTENT_PATH" rm --force "$path" ln --symbolic "${USER_PERSISTENT_PATH}/${path}" "$path" fi done <<- EOL $(expand_path_pattern "$path_pattern") EOL unset path done <<- EOL $(printf '%s' "$USER_PERSISTENT_FILES") EOL unset path_pattern ) EOF } src/30_launchers/90_messages.sh0000644000000000000000000000435113120060140015272 0ustar rootroot# Information: Launcher scripts and desktop entries are being written # USAGE: information_launchers_generation information_launchers_generation() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Génération des lanceurs…\n' ;; ('en'|*) message='Launchers generation…\n' ;; esac print_message 'info_once' "$message" } # Error - A binary file is missing # USAGE: error_launcher_missing_binary $binary # CALLS: print_error error_launcher_missing_binary() { local binary binary="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le fichier suivant est introuvable, mais la création dʼun lanceur pour celui-ci a été demandée : %s\n' message="$message"'Merci de signaler cette erreur sur notre outil de gestion de bugs : %s\n' ;; ('en'|*) message='The following file can not be found, but a launcher targeting it should have been created: %s\n' message="$message"'Please report this issue on our bug tracker: %s\n' ;; esac print_message 'error' "$message" \ "$binary" \ "$PLAYIT_GAMES_BUG_TRACKER_URL" } # Error - The requested prefix type is not compatible with the given application type # USAGE: error_launchers_prefix_type_unsupported $application error_launchers_prefix_type_unsupported() { local application application="$1" local application_type prefix_type application_type=$(application_type "$application") if [ -z "$application_type" ]; then error_no_application_type "$application" return 1 fi prefix_type=$(application_prefix_type "$application") local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le type de préfixe "%s" ne peut pas être utilisé pour une application du type "%s".\n' message="$message"'Merci de signaler cette erreur sur notre outil de suivi des problèmes : %s\n' ;; ('en'|*) message='Prefix type "%s" can not be used with application type "%s".\n' message="$message"'Please report this issue in our bug tracker: %s\n' ;; esac print_message 'error' "$message" \ "$prefix_type" \ "$application_type" \ "$PLAYIT_GAMES_BUG_TRACKER_URL" } src/30_packages/00_common.sh0000644000000000000000000003043513120060140014536 0ustar rootroot# Generate packages from the given list # USAGE: packages_generation $package[…] packages_generation() { # If not explicit packages list is given, generate all packages if [ $# -eq 0 ]; then local packages_list packages_list=$(packages_list) packages_generation $packages_list return 0 fi information_packages_generation local option_package option_package=$(option_value 'package') case "$option_package" in ('arch') archlinux_packages_metadata "$@" archlinux_packages_build "$@" ;; ('deb') debian_packages_metadata "$@" debian_packages_build "$@" ;; ('gentoo') gentoo_packages_metadata "$@" gentoo_packages_build "$@" ;; ('egentoo') egentoo_packages_metadata "$@" egentoo_packages_build "$@" ;; esac } # Print the full list of packages that should be built from the current archive # If no value is set to PACKAGES_LIST or some archive-specific variant of PACKAGES_LIST, # the following default value is returned: "PKG_MAIN". # USAGE: packages_list # RETURN: a list of package identifiers, # separated by line breaks packages_list() { # WARNING - most context-related functions can not be used here, # because current_package relies on the current function. local packages_list packages_list_variable packages_list_variable=$(context_name_archive 'PACKAGES_LIST') if [ -n "${packages_list_variable:-}" ]; then packages_list=$(get_value "$packages_list_variable") else packages_list="${PACKAGES_LIST:-}" fi # Fall back on the default packages list for the current game engine if [ -z "${packages_list:-}" ]; then ## WARNING: game_engine can not be called here, bacause it relies on context_value. local game_engine game_engine_variable game_engine_variable=$(context_name_archive 'GAME_ENGINE') if [ -n "${game_engine_variable:-}" ]; then game_engine=$(get_value "$game_engine_variable") else game_engine="${GAME_ENGINE:-}" fi if [ -z "$game_engine" ]; then local visionaire_name visionaire_name_variable visionaire_name_variable=$(context_name_archive 'VISIONAIRE_NAME') if [ -n "$visionaire_name_variable" ]; then visionaire_name=$(get_value "$visionaire_name_variable") else visionaire_name="${VISIONAIRE_NAME:-}" fi if [ -n "$visionaire_name" ]; then game_engine='visionaire' fi fi case "$game_engine" in ('visionaire') packages_list=$(visionaire_packages_list) ;; esac fi # Fall back of the default packages list (a single package identified by "PKG_MAIN") if [ -z "${packages_list:-}" ]; then packages_list='PKG_MAIN' fi local package for package in $packages_list; do printf '%s\n' "$package" done } # Check if the given package is included in the list of packages that should be built # USAGE: package_is_included_in_packages_list $package # RETURN: 0 if the package is included, 1 if it is not package_is_included_in_packages_list() { local package package="$1" local packages_list packages_list=$(packages_list) printf '%s' "$packages_list" | \ grep --quiet --fixed-strings --word-regexp "$package" } # Print the list of the packages that would be generated from the given archive. # USAGE: packages_print_list $archive # RETURN: a list of package file names, one per line packages_print_list() { local archive archive="$1" local option_package PLAYIT_CONTEXT_ARCHIVE option_package=$(option_value 'package') set_current_archive "$archive" case "$option_package" in ('egentoo') local package_name package_name=$(egentoo_package_name) printf '%s\n' "$package_name" ;; (*) local packages_list package package_name packages_list=$(packages_list) for package in $packages_list; do package_name=$(package_name "$package") printf '%s\n' "$package_name" done ;; esac } # Print the id of the given package # USAGE: package_id $package # RETURNS: the package id, as a non-empty string package_id() { local package package="$1" local package_id package_id=$(context_value "${package}_ID") # Fall back on the default package id for the current game engine if [ -z "$package_id" ]; then local game_engine game_engine=$(game_engine) case "$game_engine" in ('visionaire') package_id=$(visionaire_package_id "$package") ;; esac fi # Fall back on the game id if no package id is explicitly set if [ -z "$package_id" ]; then ## We need to explicitly set the context here, ## because the value of GAME_ID might be specific to the current package. package_id=$( set_current_package "$package" game_id ) ## Include the expansion id if one is available. local expansion_id expansion_id=$(expansion_id) if [ -n "$expansion_id" ]; then package_id="${package_id}-${expansion_id}" fi fi # Check that the id fits the format restrictions. if ! printf '%s' "$package_id" | \ grep --quiet --regexp='^[0-9a-z][-0-9a-z]\+[0-9a-z]$' then error_package_id_invalid "$package_id" return 1 fi # Apply tweaks specific to the target package format. local option_package option_package=$(option_value 'package') case "$option_package" in ('arch') package_id=$(archlinux_package_id "$package_id") ;; ('gentoo'|'egentoo') package_id=$(gentoo_package_id "$package_id") ;; esac printf '%s' "$package_id" } # Print the architecture of the given package # USAGE: package_architecture $package # RETURNS: the package architecture, as one of the following values: # - 32 # - 64 # - all package_architecture() { local package package="$1" local package_architecture package_architecture=$(context_value "${package}_ARCH") # If no architecture is explictly set for the given package, fall back to "all". if [ -z "$package_architecture" ]; then package_architecture='all' fi printf '%s' "$package_architecture" } # Print the desciption of the given package # USAGE: package_description $package # RETURNS: the package description, a non-empty string that should not include line breaks package_description() { local package package="$1" local package_description package_description=$(context_value "${package}_DESCRIPTION") # Fall back on the default package description for the current game engine if [ -z "$package_description" ]; then local game_engine game_engine=$(game_engine) case "$game_engine" in ('visionaire') package_description=$(visionaire_package_description "$package") ;; esac fi # Check that the package description does not span multiple lines if [ "$(printf '%s' "$package_description" | wc --lines)" -gt 0 ]; then error_variable_multiline "${package}_DESCRIPTION" return 1 fi printf '%s' "$package_description" } # Print the file name of the given package # USAGE: package_name $package # RETURNS: the file name, as a string package_name() { local package package="$1" local option_package package_name option_package=$(option_value 'package') case "$option_package" in ('arch') package_name=$(package_name_archlinux "$package") ;; ('deb') package_name=$(package_name_debian "$package") ;; ('gentoo') package_name=$(package_name_gentoo "$package") ;; ('egentoo') package_name=$(egentoo_package_name) ;; esac printf '%s' "$package_name" } # Get the path to the directory where the given package is prepared. # USAGE: package_path $package # RETURNS: path to a directory, it is not checked that it exists or is writable package_path() { local package package="$1" assert_not_empty 'PLAYIT_WORKDIR' 'package_path' local option_package package_name package_path option_package=$(option_value 'package') case "$option_package" in ('arch') package_path=$(package_path_archlinux "$package") ;; ('deb') package_path=$(package_path_debian "$package") ;; ('gentoo') package_path=$(package_path_gentoo "$package") ;; ('egentoo') package_path=$(package_path_egentoo "$package") ;; esac printf '%s/packages/%s' "$PLAYIT_WORKDIR" "$package_path" } # Print the maintainer string # USAGE: package_maintainer # RETURNS: the package maintainer, as a non-empty string package_maintainer() { local maintainer maintainer='' # Try to get a maintainer string from environment variables used by Debian tools. if ! variable_is_empty 'DEBEMAIL'; then if ! variable_is_empty 'DEBFULLNAME'; then maintainer="$DEBFULLNAME <${DEBEMAIL}>" else maintainer="$DEBEMAIL" fi fi if [ -n "$maintainer" ]; then printf '%s' "$maintainer" return 0 fi # Try to get a maintainer string from /etc/makepkg.conf. if \ [ -r '/etc/makepkg.conf' ] \ && grep --quiet '^PACKAGER=' '/etc/makepkg.conf' then if grep --quiet '^PACKAGER=".*"' '/etc/makepkg.conf'; then maintainer=$(sed --silent 's/^PACKAGER="\(.*\)"/\1/p' '/etc/makepkg.conf') elif grep --quiet "^PACKAGER='.*'" '/etc/makepkg.conf'; then maintainer=$(sed --silent "s/^PACKAGER='\\(.*\\)'/\\1/p" '/etc/makepkg.conf') else maintainer=$(sed --silent 's/^PACKAGER=\(.*\)/\1/p' '/etc/makepkg.conf') fi fi if [ -n "$maintainer" ]; then printf '%s' "$maintainer" return 0 fi # Compute a maintainer e-mail from the current hostname and user name, # falling back to "user@localhost". local hostname if command -v 'hostname' >/dev/null 2>&1; then hostname=$(hostname) elif [ -r '/etc/hostname' ]; then hostname=$(cat '/etc/hostname') else hostname='localhost' fi local username if ! variable_is_empty 'USER'; then username="$USER" elif command -v 'whoami' >/dev/null 2>&1; then username=$(whoami) elif command -v 'id' >/dev/null 2>&1; then username=$(id --name --user) else username='user' fi printf '%s@%s' "$username" "$hostname" } # Print the package version string # USAGE: package_version # RETURNS: the package version, as a non-empty string package_version() { # Get the version string for the current archive. local script_version_string archive package_version script_version_string=$(script_version) archive=$(current_archive) package_version=$(get_value "${archive}_VERSION") ## Fall back on "1.0-1" if no version string is explicitly set. if [ -z "$package_version" ]; then package_version='1.0-1' fi package_version="${package_version}+${script_version_string}" # Portage does not like some of our version names # cf. https://devmanual.gentoo.org/ebuild-writing/file-format/index.html local option_package option_package=$(option_value 'package') case "$option_package" in ('gentoo'|'egentoo') package_version=$(gentoo_package_version "$package_version") ;; esac printf '%s' "$package_version" } # Print the list of package names provided by the given package # This list is used to ensure conflicting packages can not be installed at the same time. # USAGE: package_provides $package # RETURN: a list of provided package names, # one per line, # or an empty string package_provides() { local package package="$1" local package_provides package_provides=$(context_value "${package}_PROVIDES") # Return early if there is no package name to print if [ -z "$package_provides" ]; then return 0 fi printf '%s' "$package_provides" | list_clean } # Print the actions that should be run post-installation for the given package # USAGE: package_postinst_actions $package # RETURN: a list of actions, that can span over several lines, # the list can be empty package_postinst_actions() { local package package="$1" local postinst_actions postinst_actions=$(get_value "${package}_POSTINST_RUN") # Return early if no action is set. if [ -z "$postinst_actions" ]; then return 0 fi # Ensure the list of actions always end with a line break. printf '%s\n' "$postinst_actions" } # Print the actions that should be run pre-removal for the given package # USAGE: package_prerm_actions $package # RETURN: a list of actions, that can span over several lines, # the list can be empty package_prerm_actions() { local package package="$1" local prerm_actions prerm_actions=$(get_value "${package}_PRERM_RUN") # Return early if no action is set. if [ -z "$prerm_actions" ]; then return 0 fi # Ensure the list of actions always end with a line break. printf '%s\n' "$prerm_actions" } # Print the warning messages that should be displayed at the end of the given package installation # USAGE: package_postinst_warnings $package # RETURN: one or several messages, separated by line breaks, # the message can be empty package_postinst_warnings() { local package package="$1" get_value "${package}_POSTINST_WARNINGS" } src/30_packages/20_dependencies.sh0000644000000000000000000000260713120060140015676 0ustar rootroot# Print the list of generic dependencies required by a given package # USAGE: dependencies_list_generic $package # RETURN: a list of generic dependcy keywords, # separated by line breaks dependencies_list_generic() { local package package="$1" local dependencies_generic dependencies_generic=$(context_value "${package}_DEPS") # Generic dependencies are deprecated for compatibility levels ≥ 2.30 if \ [ -n "$dependencies_generic" ] && \ compatibility_level_is_at_least '2.30' then warning_deprecated_variable "${package}_DEPS" "${package}_DEPENDENCIES_xxx" fi # Return early if the current package does not use legacy generic dependencies if [ -z "$dependencies_generic" ]; then return 0 fi printf '%s' "$dependencies_generic" | sed 's/ /\n/g' | list_clean } # Add a dependency to the list of the given package. # This function is used to update the generic dependencies list. # USAGE: dependencies_add_generic $package $dependency dependencies_add_generic() { local package dependency package="$1" dependency="$2" local current_dependencies current_dependencies=$(dependencies_list_generic "$package") local dependencies_variable_name dependencies_variable_name=$(context_name "${package}_DEPS") if [ -z "$dependencies_variable_name" ]; then dependencies_variable_name="${package}_DEPS" fi export $dependencies_variable_name="$current_dependencies $dependency" } src/30_packages/25_dependencies_commands.sh0000644000000000000000000000475213120060140017567 0ustar rootroot# Print the list of commands required by a given package # USAGE: dependencies_list_commands $package # RETURNS: a list of commands, # one per line dependencies_list_commands() { local package package="$1" local dependencies_commands dependencies_commands=$(context_value "${package}_DEPENDENCIES_COMMANDS") # Return early if the current package does not require any command if [ -z "$dependencies_commands" ]; then return 0 fi printf '%s' "$dependencies_commands" | list_clean } # Add a command to the list of the given package. # This function is used to update the commands dependencies list. # USAGE: dependencies_add_command $package $dependency dependencies_add_command() { local package dependency package="$1" dependency="$2" local current_dependencies current_dependencies=$(dependencies_list_commands "$package") local dependencies_variable_name dependencies_variable_name=$(context_name "${package}_DEPENDENCIES_COMMANDS") if [ -z "$dependencies_variable_name" ]; then dependencies_variable_name="${package}_DEPENDENCIES_COMMANDS" fi export $dependencies_variable_name="$current_dependencies $dependency" } # Print the path to a temporary files used for unknown commands listing # USAGE: dependencies_unknown_commands_file dependencies_unknown_commands_file() { printf '%s/unknown_commands_list' "$PLAYIT_WORKDIR" } # Print a list of unknown commands # USAGE: dependencies_unknown_commands_list dependencies_unknown_commands_list() { local unknown_commands_list unknown_commands_list=$(dependencies_unknown_commands_file) # Return early if there is no unknown command if [ ! -e "$unknown_commands_list" ]; then return 0 fi list_clean < "$unknown_commands_list" } # Clear the list of unknown commands # USAGE: dependencies_unknown_commands_clear dependencies_unknown_commands_clear() { local unknown_commands_list unknown_commands_list=$(dependencies_unknown_commands_file) rm --force "$unknown_commands_list" } # Add a command to the list of unknown ones # USAGE: dependencies_unknown_command_add $unknown_command dependencies_unknown_command_add() { local unknown_command unknown_command="$1" local unknown_commands_list unknown_commands_list=$(dependencies_unknown_commands_file) # Do nothing if this command is already included in the list if \ [ -e "$unknown_commands_list" ] \ && grep --quiet --fixed-strings --word-regexp "$unknown_command" "$unknown_commands_list" then return 0 fi printf '%s\n' "$unknown_command" >> "$unknown_commands_list" } src/30_packages/25_dependencies_gstreamer-plugins.sh0000644000000000000000000000505113120060140021427 0ustar rootroot# Print the list of GStreamer decoders required by a given package # USAGE: dependencies_list_gstreamer_decoders $package # RETURNS: a list of GStreamer decoders, # one per line dependencies_list_gstreamer_decoders() { local package package="$1" local gstreamer_decoders gstreamer_decoders=$(context_value "${package}_DEPENDENCIES_GSTREAMER_PLUGINS") # Fall back on the default list of decoders for the current game engine if [ -z "$gstreamer_decoders" ]; then local game_engine game_engine=$(game_engine) case "$game_engine" in ('unrealengine4') gstreamer_decoders=$(unrealengine4_dependencies_list_gstreamer_decoders_default "$package") ;; esac fi # Return early if the current package does not require any GStreamer decoder if [ -z "$gstreamer_decoders" ]; then return 0 fi # Always return a list with no duplicate entry, # excluding empty lines. # Ignore grep error return if there is nothing to print. printf '%s' "$gstreamer_decoders" | list_clean } # Print the path to a temporary files used for unknown GStreamer media formats listing # USAGE: dependencies_unknown_gstreamer_media_formats_file dependencies_unknown_gstreamer_media_formats_file() { printf '%s/unknown_gstreamer_media_formats_list' "$PLAYIT_WORKDIR" } # Print a list of unknown GStreamer media formats # USAGE: dependencies_unknown_gstreamer_media_formats_list dependencies_unknown_gstreamer_media_formats_list() { local unknown_formats_list unknown_formats_list=$(dependencies_unknown_gstreamer_media_formats_file) # Return early if there is no unknown library if [ ! -e "$unknown_formats_list" ]; then return 0 fi list_clean < "$unknown_formats_list" } # Clear the list of unknown GStreamer media formats # USAGE: dependencies_unknown_gstreamer_media_formats_clear dependencies_unknown_gstreamer_media_formats_clear() { local unknown_formats_list unknown_formats_list=$(dependencies_unknown_gstreamer_media_formats_file) rm --force "$unknown_formats_list" } # Add a GStreamer media format to the list of unknown ones # USAGE: dependencies_unknown_gstreamer_media_formats_add $unknown_format dependencies_unknown_gstreamer_media_formats_add() { local unknown_format unknown_formats_list unknown_format="$1" unknown_formats_list=$(dependencies_unknown_gstreamer_media_formats_file) # Do nothing if this format is already included in the list if \ [ -e "$unknown_formats_list" ] \ && grep --quiet --fixed-strings --word-regexp "$unknown_format" "$unknown_formats_list" then return 0 fi printf '%s\n' "$unknown_format" >> "$unknown_formats_list" } src/30_packages/25_dependencies_mono-libraries.sh0000644000000000000000000000613313120060140020703 0ustar rootroot# Print the list of Mono libraries required by a given package # USAGE: dependencies_list_mono_libraries $package # RETURNS: a list of Mono library names, # one per line dependencies_list_mono_libraries() { local package package="$1" # Distinct dependencies lists might be used based on source archive local dependencies_mono_libraries dependencies_mono_libraries=$(context_value "${package}_DEPENDENCIES_MONO_LIBRARIES") printf '%s' "$dependencies_mono_libraries" | list_clean } # Print the list of native packages providing the Mono libraries required by a given package # USAGE: dependencies_list_mono_libraries_packages $package # RETURNS: a list of native package names, # one per line dependencies_list_mono_libraries_packages() { local package package="$1" # Return early if the current package requires no Mono library local required_mono_libraries library required_mono_libraries=$(dependencies_list_mono_libraries "$package") if [ -z "$required_mono_libraries" ]; then return 0 fi # Return early when building packages for a system that does not provide Mono libraries in dedicated packages. local option_package option_package=$(option_value 'package') case "$option_package" in ('arch') # Arch Linux provides all Mono libraries in a single "mono" package. printf '%s\n' 'mono' return 0 ;; ('gentoo'|'egentoo') # Gentoo provides all Mono libraries in a single "dev-lang/mono" package. printf '%s\n' 'dev-lang/mono' return 0 ;; esac case "$option_package" in ('deb') debian_dependencies_providing_mono_libraries $required_mono_libraries return 0 ;; esac } # Print the path to a temporary files used for unknown Mono libraries listing # USAGE: dependencies_unknown_mono_libraries_file dependencies_unknown_mono_libraries_file() { printf '%s/unknown_mono_libraries_list' "$PLAYIT_WORKDIR" } # Print a list of unknown Mono libraries # USAGE: dependencies_unknown_mono_libraries_list dependencies_unknown_mono_libraries_list() { local unknown_library unknown_libraries_list unknown_libraries_list=$(dependencies_unknown_mono_libraries_file) # Return early if there is no unknown library if [ ! -e "$unknown_libraries_list" ]; then return 0 fi list_clean < "$unknown_libraries_list" } # Clear the list of unknown Mono libraries # USAGE: dependencies_unknown_mono_libraries_clear dependencies_unknown_mono_libraries_clear() { local unknown_library unknown_libraries_list unknown_libraries_list=$(dependencies_unknown_mono_libraries_file) rm --force "$unknown_libraries_list" } # Add a Mono library to the list of unknown ones # USAGE: dependencies_unknown_mono_libraries_add $unknown_library dependencies_unknown_mono_libraries_add() { local unknown_library unknown_libraries_list unknown_library="$1" unknown_libraries_list=$(dependencies_unknown_mono_libraries_file) # Do nothing if this library is already included in the list if \ [ -e "$unknown_libraries_list" ] \ && grep --quiet --fixed-strings --word-regexp "$unknown_library" "$unknown_libraries_list" then return 0 fi printf '%s\n' "$unknown_library" >> "$unknown_libraries_list" } src/30_packages/25_dependencies_native-libraries.sh0000644000000000000000000001122713120060140021221 0ustar rootroot# Print the list of native libraries required by a given package # USAGE: dependencies_list_native_libraries $package # RETURNS: a list of native library names, # one per line dependencies_list_native_libraries() { local package package="$1" local dependencies_libraries dependencies_libraries=$(context_value "${package}_DEPENDENCIES_LIBRARIES") # Fall back on the default list of native libraries for the current game engine if [ -z "$dependencies_libraries" ]; then local game_engine game_engine=$(game_engine) case "$game_engine" in ('visionaire') dependencies_libraries=$(visionaire_package_dependencies_native_libraries "$package") ;; esac fi printf '%s' "$dependencies_libraries" | list_clean } # Print the list of native libraries required by all packages # USAGE: dependencies_list_native_libraries_all # RETURNS: a list of native library names, # one per line dependencies_list_native_libraries_all() { local packages_list package dependencies_libraries dependencies_libraries_all packages_list=$(packages_list) for package in $packages_list; do dependencies_libraries=$(dependencies_list_native_libraries "$package") dependencies_libraries_all="${dependencies_libraries_all:-} $dependencies_libraries" done printf '%s' "$dependencies_libraries_all" | list_clean } # Print the list of native packages providing the native libraries required by a given package # USAGE: dependencies_list_native_libraries_packages $package # RETURNS: a list of native package names, # one per line dependencies_list_native_libraries_packages() { local package package="$1" local required_native_libraries option_package required_native_libraries=$(dependencies_list_native_libraries "$package") option_package=$(option_value 'package') case "$option_package" in ('arch') local package_architecture package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') archlinux_dependencies_providing_native_libraries_32bit $required_native_libraries ;; (*) archlinux_dependencies_providing_native_libraries $required_native_libraries ;; esac ;; ('deb') debian_dependencies_providing_native_libraries $required_native_libraries ;; ('gentoo'|'egentoo') local package_architecture package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') gentoo_dependencies_providing_native_libraries_32bit "$package" $required_native_libraries ;; (*) gentoo_dependencies_providing_native_libraries "$package" $required_native_libraries ;; esac ;; esac } # Print the path to a temporary files used for unknown libraries listing # USAGE: dependencies_unknown_libraries_file dependencies_unknown_libraries_file() { printf '%s/unknown_libraries_list' "$PLAYIT_WORKDIR" } # Print a list of unknown libraries # USAGE: dependencies_unknown_libraries_list dependencies_unknown_libraries_list() { local unknown_libraries_list unknown_libraries_list=$(dependencies_unknown_libraries_file) # Return early if there is no unknown library if [ ! -e "$unknown_libraries_list" ]; then return 0 fi list_clean < "$unknown_libraries_list" } # Clear the list of unknown libraries # USAGE: dependencies_unknown_libraries_clear dependencies_unknown_libraries_clear() { local unknown_libraries_list unknown_libraries_list=$(dependencies_unknown_libraries_file) rm --force "$unknown_libraries_list" } # Add a library to the list of unknown ones # USAGE: dependencies_unknown_libraries_add $unknown_library dependencies_unknown_libraries_add() { local unknown_library unknown_libraries_list unknown_library="$1" unknown_libraries_list=$(dependencies_unknown_libraries_file) # Do nothing if this library is already included in the list if \ [ -e "$unknown_libraries_list" ] \ && grep --quiet --fixed-strings --word-regexp "$unknown_library" "$unknown_libraries_list" then return 0 fi printf '%s\n' "$unknown_library" >> "$unknown_libraries_list" } # Add a depdendency to the list of the given package. # This function is used to update the native libraries dependencies list. # USAGE: dependencies_add_native_libraries $package $dependency dependencies_add_native_libraries() { local package dependency package="$1" dependency="$2" local current_dependencies current_dependencies=$(dependencies_list_native_libraries "$package") local dependencies_variable_name dependencies_variable_name=$(context_name "${package}_DEPENDENCIES_LIBRARIES") if [ -z "$dependencies_variable_name" ]; then dependencies_variable_name="${package}_DEPENDENCIES_LIBRARIES" fi export $dependencies_variable_name="$current_dependencies $dependency" } src/30_packages/25_dependencies_siblings.sh0000644000000000000000000000151113120060140017566 0ustar rootroot# Print the list of sibling packages required by a given package # USAGE: dependencies_list_siblings $package # RETURNS: a list of package identifiers, # one per line dependencies_list_siblings() { local package package="$1" local dependencies_siblings dependencies_siblings=$(context_value "${package}_DEPENDENCIES_SIBLINGS") # Fall back on the default list of dependencies for the current game engine if [ -z "$dependencies_siblings" ]; then local game_engine game_engine=$(game_engine) case "$game_engine" in ('visionaire') dependencies_siblings=$(visionaire_package_dependencies_siblings "$package") ;; esac fi # Return early if the current package does not require any sibling package if [ -z "$dependencies_siblings" ]; then return 0 fi printf '%s' "$dependencies_siblings" | list_clean } src/30_packages/90_messages.sh0000644000000000000000000000641213120060140015064 0ustar rootroot# Information: All packages are being built # USAGE: information_packages_generation information_packages_generation() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Géneration des paquets…\n' ;; ('en'|*) message='Packages generation…\n' ;; esac print_message 'info' "$message" } # display a notification when trying to build a package that already exists # USAGE: information_package_already_exists $file information_package_already_exists() { local file file="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='%s existe déjà.\n' ;; ('en'|*) message='%s already exists.\n' ;; esac print_message 'info' "$message" \ "$file" } # print package building message # USAGE: information_package_building $file information_package_building() { local file file="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Construction de %s…\n' ;; ('en'|*) message='Building %s…\n' ;; esac print_message 'info' "$message" \ "$file" } # Error - The provided package id uses an invalid format # USAGE: error_package_id_invalid $package_id error_package_id_invalid() { local package_id package_id="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Lʼid de paquet fourni ne correspond pas au format attendu : "%s"\n' message="$message"'Cette valeur ne peut utiliser que des caractères du set [-a-z0-9],' message="$message"' et ne peut ni débuter ni sʼachever par un tiret.\n' ;; ('en'|*) message='The provided package id is not using the expected format: "%s"\n' message="$message"'The value should only include characters from the set [-a-z0-9],' message="$message"' and can not begin nor end with an hyphen.\n' ;; esac print_message 'error' "$message" \ "$package_id" } # Error - The generation of the given package failed. # USAGE: error_package_generation_failed $package_name error_package_generation_failed() { local package_name package_name="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La génération du paquet suivant a échoué : %s\n' message="$message"'Merci de signaler cet échec sur notre système de suivi : %s\n\n' ;; ('en'|*) message='The generation of the following package failed: %s\n' message="$message"'Please report this error on our bugs tracker: %s\n\n' ;; esac print_message 'error' "$message" \ "$package_name" \ "$PLAYIT_BUG_TRACKER_URL" } # Error - The generation of the metadata for the given package failed. # USAGE: error_package_metadata_generation_failed $package_name error_package_metadata_generation_failed() { local package_name package_name="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La génération des méta-données du paquet suivant a échoué : %s\n' ;; ('en'|*) message='The generation of the following package metadata failed: %s\n' ;; esac print_message 'error' "$message" \ "$package_name" } src/30_paths/00_common.sh0000644000000000000000000001154413120060140014077 0ustar rootroot# Print install path for binaries. # USAGE: path_binaries path_binaries() { local install_prefix target_system path_structure install_prefix=$(option_value 'prefix') target_system=$(option_value 'package') case "$target_system" in ('deb') # Debian uses /usr/games as the default path for game-related binaries. path_structure='%s/games' ;; (*) # Non-Debian systems use /usr/bin as the default path for all binaries, # including game-related ones. path_structure='%s/bin' ;; esac ## Silence ShellCheck false-positive ## Don't use variables in the printf format string. Use printf "..%s.." "$foo". # shellcheck disable=SC2059 printf "$path_structure" "$install_prefix" } # Print install path for XDG .desktop menu entries. # USAGE: path_xdg_desktop path_xdg_desktop() { # For convenience, XDG .desktop menu entries are always installed under the default install prefix. # If they could be installed under a custom path like /opt/${game_id}, # they would not be picked up by applications menus without a manual intervention from the system administrator. printf '/usr/share/applications' } # Print install path for documentation files. # USAGE: path_documentation path_documentation() { local install_prefix install_prefix=$(option_value 'prefix') local option_package last_path_component option_package=$(option_value 'package') case "$option_package" in ('deb'|'arch') last_path_component=$(game_id) ;; ('gentoo'|'egentoo') local game_id package_version game_id=$(game_id) package_version=$(package_version) last_path_component="${game_id}-${package_version}" ;; esac printf '%s/share/doc/%s' "$install_prefix" "$last_path_component" } # Print install path for game files. # USAGE: path_game_data path_game_data() { local install_prefix game_id target_system path_structure install_prefix=$(option_value 'prefix') game_id=$(game_id) target_system=$(option_value 'package') case "$target_system" in ('deb') # Debian uses /usr/share/games as the default path for game-related data files. path_structure='%s/share/games/%s' ;; (*) # Non-Debian systems use /usr/share as the default path for all data files, # including game-related ones. path_structure='%s/share/%s' ;; esac ## Silence ShellCheck false-positive ## Don't use variables in the printf format string. Use printf "..%s.." "$foo". # shellcheck disable=SC2059 printf "$path_structure" "$install_prefix" "$game_id" } # Print install path for icons. # USAGE: path_icons path_icons() { # Icons are always installed under the default install prefix. # launcher_desktop (src/30_launchers/00_common.sh) expects the icon to be available under either /usr or /usr/local. printf '/usr/share/icons/hicolor' } # Print install path for native libraries. # USAGE: path_libraries path_libraries() { local install_prefix game_id install_prefix=$(option_value 'prefix') game_id=$(game_id) local path_structure target_system ## Set default value path_structure='%s/lib/games/%s' ## Override default value if required target_system=$(option_value 'package') case "$target_system" in ('arch') local package package_architecture package=$(current_package) package_architecture=$(package_architecture "$package") if [ "$package_architecture" = '32' ]; then path_structure='%s/lib32/games/%s' fi ;; ('gentoo'|'egentoo') local package package_architecture package=$(current_package) package_architecture=$(package_architecture "$package") if [ "$package_architecture" = '64' ]; then path_structure='%s/lib64/games/%s' fi ;; esac ## Silence ShellCheck false-positive ## Don't use variables in the printf format string. Use printf "..%s.." "$foo". # shellcheck disable=SC2059 printf "$path_structure" "$install_prefix" "$game_id" } # Print the path to system libraries # USAGE: path_libraries_system path_libraries_system() { local path_libraries current_package package_architecture option_package current_package=$(current_package) package_architecture=$(package_architecture "$current_package") option_package=$(option_value 'package') case "$option_package" in ('arch') case "$package_architecture" in ('32') path_libraries='/usr/lib32' ;; ('64') path_libraries='/usr/lib' ;; esac ;; ('deb') case "$package_architecture" in ('32') path_libraries='/usr/lib/i386-linux-gnu' ;; ('64') path_libraries='/usr/lib/x86_64-linux-gnu' ;; esac ;; ('gentoo'|'egentoo') case "$package_architecture" in ('32') path_libraries='/usr/lib' ;; ('64') path_libraries='/usr/lib64' ;; esac ;; esac printf '%s' "$path_libraries" } # Print install path for TTF fonts. # USAGE: path_fonts_ttf path_fonts_ttf() { local game_id game_id=$(game_id) # Fonts are always installed under the default install prefix. printf '/usr/share/fonts/truetype/%s' "$game_id" } src/40_hacks/00_list.sh0000644000000000000000000000064013120060140013530 0ustar rootroot# List the LD_PRELOAD hacks that should be included for the current game # USAGE: hacks_list # RETURN: a list of hack identifiers, one per line, # or an empty string if the current game requires no hack hacks_list() { local hacks_list hacks_list=$(context_value 'PRELOAD_HACKS_LIST') if [ -z "$hacks_list" ]; then return 0 fi local hack for hack in $hacks_list; do printf '%s\n' "$hack" done } src/40_hacks/10_details.sh0000644000000000000000000000414213120060140014204 0ustar rootroot# Print the name of the given hack # This is used to compute the file names of the source and the generated library. # USAGE: hack_name $hack # RETURN: the hack name, on a single line, # throw an error if it spans multiple lines, # throw an error if no name is set hack_name() { local hack hack="$1" local hack_name hack_name=$(context_value "${hack}_NAME") # Throw an error if the name is empty if [ -z "$hack_name" ]; then error_missing_variable "${hack}_NAME" return 1 fi # Throw an error if the name includes line breaks local line_breaks_number line_breaks_number=$(printf '%s' "$hack_name" | wc --lines) if [ "$line_breaks_number" -gt 0 ]; then error_variable_multiline "${hack}_NAME" return 1 fi printf '%s' "$hack_name" } # Print the description of the given hack # USAGE: hack_description $hack # RETURN: the hack description, it can span over multiple lines, # throw an error if no description is set hack_description() { local hack hack="$1" local hack_description hack_description=$(context_value "${hack}_DESCRIPTION") # Throw an error if the description is empty if [ -z "$hack_description" ]; then error_missing_variable "${hack}_DESCRIPTION" return 1 fi printf '%s' "$hack_description" } # Print the identifier of the package that should inclide the given hack # USAGE: hack_package $hack # RETURN: a package identifier, falling back on the current package hack_package() { local hack hack="$1" local hack_package hack_package=$(context_value "${hack}_PACKAGE") # Fall back on the current package identifier if [ -z "$hack_package" ]; then hack_package=$(current_package) fi printf '%s' "$hack_package" } # Print the content of the hack source # USAGE: hack_source $hack # RETURN: the hack source, usually spanning over multiple lines, # throw an error if no source is set hack_source() { local hack hack="$1" local hack_source hack_source=$(context_value "${hack}_SOURCE") # Throw an error if the source is empty if [ -z "$hack_source" ]; then error_missing_variable "${hack}_SOURCE" return 1 fi printf '%s' "$hack_source" } src/40_hacks/20_build.sh0000644000000000000000000000313513120060140013660 0ustar rootroot# Build the given LD_PRELOAD hack from its source # USAGE: hack_build $hack # RETURN: 0 if everything went well, # 1 if the build failed hack_build() { local hack hack="$1" # Write the source file local hack_path_source hack_path_source=$(hack_path_source "$hack") mkdir --parents "${PLAYIT_WORKDIR}/hacks" hack_source "$hack" > "$hack_path_source" # Prepare the compiler options string local gcc_options gcc_options='-shared -Wall -fPIC -ldl' local hack_package hack_package_architecture hack_package=$(hack_package "$hack") hack_package_architecture=$(package_architecture "$hack_package") if [ "$hack_package_architecture" = '32' ]; then gcc_options="$gcc_options -m32" fi # Build the .so library local hack_path_library hack_build_status hack_path_library=$(hack_path_library "$hack") { gcc $gcc_options "$hack_path_source" -o "$hack_path_library" hack_build_status=$? } || true if [ "$hack_build_status" -ne 0 ]; then error_hack_build_failure return 1 fi rm "$hack_path_source" } # Print the path of the source file used for building the given hack # USAGE: hack_path_source $hack # RETURN: the absolute path to the hack source file hack_path_source() { local hack hack="$1" local hack_name hack_name=$(hack_name "$hack") printf '%s/hacks/%s.c' "$PLAYIT_WORKDIR" "$hack_name" } # Print the path of the library built for the given hack # USAGE: hack_path_library $hack # RETURN: the absolute path to the hack library hack_path_library() { local hack hack="$1" local hack_name hack_name=$(hack_name "$hack") printf '%s/hacks/%s.so' "$PLAYIT_WORKDIR" "$hack_name" } src/40_hacks/30_inclusion.sh0000644000000000000000000000456513120060140014575 0ustar rootroot# Build and include all PRELOAD hacks # USAGE: hacks_inclusion_default # RETURN: 0 if all hacks could be built and included in the packages, # 0 if no hacks are to be included, # 1 otherwise hacks_inclusion_default() { local packages_list package hacks_list hack packages_list=$(packages_list) for package in $packages_list; do hacks_list=$(hacks_included_in_package "$package") if [ -z "$hacks_list" ]; then continue fi for hack in $hacks_list; do hack_build "$hack" hack_inclusion "$hack" done done } # Include the hack library for the given hack into its target package # USAGE: hack_inclusion $hack # RETURN: 0 if the inclusion succeeded, # 1 if the library could not be found hack_inclusion() { local hack hack="$1" local hack_path_library hack_path_library=$(hack_path_library "$hack") if [ ! -f "$hack_path_library" ]; then # TODO: Display an explicit error message return 1 fi local hack_package hack_package_path path_libraries hack_package=$(hack_package "$hack") hack_package_path=$(package_path "$hack_package") path_libraries=$(path_libraries) install -D --mode=644 --target-directory="${hack_package_path}${path_libraries}/preload-hacks" "$hack_path_library" rm "$hack_path_library" } # Print the pre-run actions required to use the given hack # USAGE: hack_application_prerun $hack # RETURN: the pre-run actions, as a string spanning multiple lines hack_application_prerun() { local hack hack="$1" local hack_description hack_description_line hack_description=$(hack_description "$hack") while read -r hack_description_line; do cat <<- EOF # $hack_description_line EOF done <<- EOL $(printf '%s' "$hack_description") EOL local hack_name path_libraries hack_name=$(hack_name "$hack") path_libraries=$(path_libraries) cat <<- EOF export LD_PRELOAD="\${LD_PRELOAD}:${path_libraries}/preload-hacks/${hack_name}.so" EOF } # Print the list of hacks included in the given package # USAGE: hacks_included_in_package $package # RETURN: a list of hack identifiers, one per line # or an empty string if no hack is included in the current package hacks_included_in_package() { local package package="$1" local hacks_list hack hack_package hacks_list=$(hacks_list) for hack in $hacks_list; do hack_package=$(hack_package "$hack") if [ "$hack_package" = "$package" ]; then printf '%s\n' "$hack" fi done } src/40_hacks/90_messages.sh0000644000000000000000000000124413120060140014376 0ustar rootroot# Error - The build of a preload shim failed # USAGE: error_hack_build_failure error_hack_build_failure() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La construction d’une bibliothèque depuis ses sources a échoué.\n' message="$message"'Ce message devrait être précédé d’une erreur indiquant des en-têtes de compilation manquantes.\n' ;; ('en'|*) message='The building of a library from its sources failed.\n' message="$message"'This message should be preceded with an error indicating the missing compilation headers.\n' ;; esac print_message 'error' "$message" } src/50_engine_custom/10_launchers.sh0000644000000000000000000000034413120060140016312 0ustar rootroot# Custom launcher - Throw an error if not overriden from the game script # USAGE: custom_launcher $application custom_launcher() { local application application="$1" error_custom_launcher_not_set "$application" return 1 } src/50_engine_custom/90_messages.sh0000644000000000000000000000116213120060140016144 0ustar rootroot# Error - The "custom_launcher" function has not been overriden by the game script # USAGE: error_custom_launcher_not_set $application error_custom_launcher_not_set() { local application application="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='L’application suivante nécessite que la fonction "custom_launcher" soit définie dans le script : %s\n' ;; ('en'|*) message='The following application expects the "custom_launcher" to be set by the game script: %s\n' ;; esac print_message 'error' "$message" \ "$application" } src/50_engine_dosbox/10_applications.sh0000644000000000000000000000131313120060140016775 0ustar rootroot# DOSBox - Print the DOSBox pre-run actions for the given application. # These actions are run inside DOSBox. # USAGE: dosbox_prerun $application # RETURN: the pre-run actions, can span over multiple lines, # or an empty string if there are none dosbox_prerun() { local application application="$1" get_value "${application}_DOSBOX_PRERUN" } # DOSBox - Print the DOSBox post-run actions for the given application. # These actions are run inside DOSBox. # USAGE: dosbox_postrun $application # RETURN: the post-run actions, can span over multiple lines, # or an empty string if there are none dosbox_postrun() { local application application="$1" get_value "${application}_DOSBOX_POSTRUN" } src/50_engine_dosbox/10_launchers.sh0000644000000000000000000001030313120060140016272 0ustar rootroot# DOSBox launcher - Print the script content # USAGE: dosbox_launcher $application dosbox_launcher() { local application application="$1" local prefix_type prefix_type=$(application_prefix_type "$application") case "$prefix_type" in ('symlinks') launcher_headers dosbox_launcher_environment "$application" # Generate the game prefix launcher_prefix_symlinks_functions launcher_prefix_symlinks_build # Set up the paths diversion to persistent storage persistent_storage_initialization persistent_storage_common persistent_path_diversion persistent_storage_update_directories persistent_storage_update_files dosbox_launcher_run "$application" # Update persistent storage with files from the current prefix persistent_storage_update_files_from_prefix launcher_exit ;; (*) error_launchers_prefix_type_unsupported "$application" return 1 ;; esac } # DOSBox launcher - Set the environment # USAGE: dosbox_launcher_environment $application dosbox_launcher_environment() { local application application="$1" local game_id path_game application_exe game_id=$(game_id) path_game=$(path_game_data) application_exe=$(application_exe_escaped "$application") cat <<- EOF # Set the environment GAME_ID='$game_id' PATH_GAME='$path_game' APP_EXE='$application_exe' EOF } # DOSBox launcher - Run DOSBox # USAGE: dosbox_launcher_run $application dosbox_launcher_run() { local application application="$1" local application_prerun application_postrun dosbox_instructions application_prerun=$(application_prerun "$application") application_postrun=$(application_postrun "$application") dosbox_instructions=$(dosbox_launcher_instructions "$application") cat <<- EOF # Run the game cd "\$PATH_PREFIX" $application_prerun ## Do not exit on application failure, ## to ensure post-run commands are run. set +o errexit ## Silence ShellCheck false-positive ## Argument mixes string and array. Use * or separate argument. # shellcheck disable=SC2145 "\${PLAYIT_DOSBOX_BINARY:-dosbox}" -c "$dosbox_instructions" game_exit_status=\$? set -o errexit $application_postrun EOF } # DOSBox launcher - Run commands inside DOSBox # USAGE: dosbox_launcher_instructions $application dosbox_launcher_instructions() { local application application="$1" # Compute the command used to mount the disk image if [ -n "${GAME_IMAGE:-}" ]; then # Find the disk image path local packages_list path_game_data packages_list=$(packages_list) path_game_data=$(path_game_data) ## Loop over the list of packages, one should include the disk image. local package package_path image_path disk_image for package in $packages_list; do package_path=$(package_path "$package") image_path="${package_path}${path_game_data}/${GAME_IMAGE}" if [ -e "$image_path" ]; then disk_image="$image_path" break fi done ## Exit with a failure state if the disk image has not been found. if [ -z "${disk_image:-}" ]; then error_dosbox_disk_image_no_found "$GAME_IMAGE" return 1 fi # Set the command used to mount the disk image, based on its type local mount_disk_image case "${GAME_IMAGE_TYPE:-iso}" in ('cdrom') if [ -d "$disk_image" ]; then mount_disk_image="mount d $GAME_IMAGE -t cdrom" else mount_disk_image="imgmount d $GAME_IMAGE -t cdrom" fi ;; ('iso') mount_disk_image="imgmount d $GAME_IMAGE -t iso -fs iso" ;; esac fi local dosbox_prerun dosbox_postrun dosbox_prerun=$(dosbox_prerun "$application") dosbox_postrun=$(dosbox_postrun "$application") cat <<- EOF mount c . c: EOF if [ -n "${mount_disk_image:-}" ]; then cat <<- EOF $mount_disk_image EOF fi if [ -n "$dosbox_prerun" ]; then cat <<- EOF $dosbox_prerun EOF fi game_exec_line "$application" if [ -n "$dosbox_postrun" ]; then cat <<- EOF $dosbox_postrun EOF fi cat <<- EOF exit EOF } # DOSBOX - Print the line starting the game # USAGE: dosbox_exec_line $application # RETURN: the command to execute, including its command line options dosbox_exec_line() { local application application="$1" local application_options application_options=$(application_options "$application") cat <<- EOF \$APP_EXE $application_options \$@ EOF } src/50_engine_dosbox/90_messages.sh0000644000000000000000000000076713120060140016142 0ustar rootroot# Error - The DOSBox image disk was not found # USAGE: error_dosbox_disk_image_no_found $disk_image error_dosbox_disk_image_no_found() { local disk_image disk_image="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Lʼimage de disque suivante nʼa pas été trouvée : %s\n' ;; ('en'|*) message='The following disk image could not be found: %s\n' ;; esac print_message 'error' "$message" \ "$disk_image" } src/50_engine_java/10_applications.sh0000644000000000000000000000217513120060140016427 0ustar rootroot# print the Java options string for the given application # USAGE: application_java_options $application # RETURN: the options string on a single line, # or an empty string if no options are set application_java_options() { # Check that the application uses the java type local application application_type application="$1" application_type=$(application_type "$application") if [ -z "$application_type" ]; then error_no_application_type "$application" return 1 fi if [ "$application_type" != 'java' ]; then error_application_wrong_type 'application_java_options' "$application_type" return 1 fi # Get the application Java options string from its identifier local application_java_options application_java_options=$(get_value "${application}_JAVA_OPTIONS") ## Return early if no options string is set. if [ -z "$application_java_options" ]; then return 0 fi # Check that the options string does not span multiple lines if [ "$(printf '%s' "$application_java_options" | wc --lines)" -gt 1 ]; then error_variable_multiline "${application}_JAVA_OPTIONS" return 1 fi printf '%s' "$application_java_options" } src/50_engine_java/10_launchers.sh0000644000000000000000000000444513120060140015727 0ustar rootroot# Java launcher - Print the script content # USAGE: java_launcher $application java_launcher() { local application application="$1" local prefix_type prefix_type=$(application_prefix_type "$application") case "$prefix_type" in ('symlinks') launcher_headers java_launcher_environment "$application" # Generate the game prefix launcher_prefix_symlinks_functions launcher_prefix_symlinks_build # Set up the paths diversion to persistent storage persistent_storage_initialization persistent_storage_common persistent_path_diversion persistent_storage_update_directories persistent_storage_update_files java_launcher_run "$application" # Update persistent storage with files from the current prefix persistent_storage_update_files_from_prefix launcher_exit ;; (*) error_launchers_prefix_type_unsupported "$application" return 1 ;; esac } # Java launcher - Set the environment # USAGE: java_launcher_environment $application java_launcher_environment() { local application application="$1" local game_id path_game application_exe game_id=$(game_id) path_game=$(path_game_data) application_exe=$(application_exe_escaped "$application") cat <<- EOF # Set the environment GAME_ID='$game_id' PATH_GAME='$path_game' APP_EXE='$application_exe' EOF } # Java launcher - Run Java # USAGE: java_launcher_run $application java_launcher_run() { local application application="$1" cat <<- 'EOF' # Run the game cd "$PATH_PREFIX" EOF # Set loading paths for libraries native_launcher_libraries application_prerun "$application" cat <<- 'EOF' ## Do not exit on application failure, ## to ensure post-run commands are run. set +o errexit EOF game_exec_line "$application" cat <<- 'EOF' game_exit_status=$? set -o errexit EOF application_postrun "$application" } # Java - Print the line starting the game # USAGE: java_exec_line $application # RETURN: the command to execute, including its command line options java_exec_line() { local application application="$1" local application_java_options application_options application_java_options=$(application_java_options "$application") application_options=$(application_options "$application") cat <<- EOF java $application_java_options -jar "\$APP_EXE" $application_options "\$@" EOF } src/50_engine_mono/10_launchers.sh0000644000000000000000000000560513120060140015755 0ustar rootroot# Mono launcher - Print the script content # USAGE: mono_launcher $application mono_launcher() { local application application="$1" local prefix_type prefix_type=$(application_prefix_type "$application") case "$prefix_type" in ('symlinks') launcher_headers mono_launcher_environment "$application" # Generate the game prefix launcher_prefix_symlinks_functions launcher_prefix_symlinks_build # Set up the paths diversion to persistent storage persistent_storage_initialization persistent_storage_common persistent_path_diversion persistent_storage_update_directories persistent_storage_update_files mono_launcher_run "$application" # Update persistent storage with files from the current prefix persistent_storage_update_files_from_prefix launcher_exit ;; ('none') launcher_headers mono_launcher_environment "$application" mono_launcher_run "$application" launcher_exit ;; (*) error_launchers_prefix_type_unsupported "$application" return 1 ;; esac } # Mono launcher - Set the environment # USAGE: mono_launcher_environment $application mono_launcher_environment() { local application application="$1" local game_id path_game application_exe game_id=$(game_id) path_game=$(path_game_data) application_exe=$(application_exe_escaped "$application") cat <<- EOF # Set the environment GAME_ID='$game_id' PATH_GAME='$path_game' APP_EXE='$application_exe' EOF } # Mono launcher - Run Mono # USAGE: mono_launcher_run $application mono_launcher_run() { local application application="$1" local prefix_type execution_path prefix_type=$(application_prefix_type "$application") case "$prefix_type" in ('symlinks') execution_path='$PATH_PREFIX' ;; ('none') execution_path='$PATH_GAME' ;; esac cat <<- EOF # Run the game cd "$execution_path" EOF # Set loading paths for libraries native_launcher_libraries application_prerun "$application" # Apply common workarounds for Mono games mono_launcher_tweaks cat <<- 'EOF' ## Do not exit on application failure, ## to ensure post-run commands are run. set +o errexit EOF game_exec_line "$application" cat <<- 'EOF' game_exit_status=$? set -o errexit EOF application_postrun "$application" } # Mono launcher - Common workarounds # USAGE: mono_launcher_tweaks mono_launcher_tweaks() { cat <<- 'EOF' ## Work around terminfo Mono bug, ## cf. https://github.com/mono/mono/issues/6752 export TERM="${TERM%-256color}" ## Work around Mono unpredictable behaviour with non-US locales export LANG=C EOF } # Mono - Print the line starting the game # USAGE: mono_exec_line $application # RETURN: the command to execute, including its command line options mono_exec_line() { local application application="$1" local application_options application_options=$(application_options "$application") cat <<- EOF mono "\$APP_EXE" $application_options "\$@" EOF } src/50_engine_native/10_launchers.sh0000644000000000000000000001403413120060140016267 0ustar rootroot# Linux native launcher - Print the script content # USAGE: native_launcher $application native_launcher() { local application application="$1" local prefix_type prefix_type=$(application_prefix_type "$application") case "$prefix_type" in ('symlinks') launcher_headers native_launcher_environment "$application" # Generate the game prefix launcher_prefix_symlinks_functions launcher_prefix_symlinks_build # Set up the paths diversion to persistent storage local fake_home_persistent_directories fake_home_persistent_directories=$(fake_home_persistent_directories) persistent_storage_initialization persistent_storage_common persistent_path_diversion persistent_storage_update_directories persistent_storage_update_files if [ -n "$fake_home_persistent_directories" ]; then fake_home_persistent fi native_launcher_run "$application" # Update persistent storage with files from the current prefix persistent_storage_update_files_from_prefix launcher_exit ;; ('none') launcher_headers native_launcher_environment "$application" native_launcher_run "$application" launcher_exit ;; (*) error_launchers_prefix_type_unsupported "$application" return 1 ;; esac } # Linux native launcher - Set the environment # USAGE: native_launcher_environment $application native_launcher_environment() { local application application="$1" local game_id path_game application_exe game_id=$(game_id) path_game=$(path_game_data) application_exe=$(application_exe_escaped "$application") cat <<- EOF # Set the environment GAME_ID='$game_id' PATH_GAME='$path_game' APP_EXE='$application_exe' EOF } # Linux native launcher - Run the game binary # USAGE: native_launcher_run $application native_launcher_run() { local application application="$1" local prefix_type execution_path prefix_type=$(application_prefix_type "$application") case "$prefix_type" in ('symlinks') execution_path='$PATH_PREFIX' ;; ('none') execution_path='$PATH_GAME' ;; esac cat <<- EOF # Run the game cd "$execution_path" EOF # Set loading paths for libraries ## This must be called before the engine specific tweaks, so they have access to the following variables: ## - PLAYIT_LIBS_PATH_SYSTEM ## - PLAYIT_LIBS_PATH_USER native_launcher_libraries local game_engine game_engine=$(game_engine) case "$game_engine" in ('unity3d') # Start pulseaudio if it is available launcher_unity3d_pulseaudio_start # Work around crash on launch related to libpulse # Some Unity3D games crash on launch if libpulse-simple.so.0 is available but pulseaudio is not running launcher_unity3d_pulseaudio_hide_libpulse # Make a hard copy of the game binary in the current prefix, # otherwise the engine might follow the link and run the game from the system path. native_launcher_binary_copy # Work around Unity3D poor support for non-US locales launcher_unity3d_force_locale # Force the use of the system SDL library unity3d_tweak_sdl_native # Unity3D 4.x and 5.x - Disable the MAP_32BIT flag to prevent a crash one some Linux versions when running a 64-bit build local unity3d_version unity3d_version=$(unity3d_version) case "$unity3d_version" in ('4.'*|'5.'*) local package package_architecture package=$(current_package) package_architecture=$(package_architecture "$package") if [ "$package_architecture" = '64' ]; then unity3d_disable_map32bit fi ;; esac ;; ('visionaire') # Force the use of the system SDL library visionaire_tweak_sdl_native ;; (*) # Make a hard copy of the game binary in the current prefix, # otherwise the engine might follow the link and run the game from the system path. local prefix_type prefix_type=$(application_prefix_type "$application") case "$prefix_type" in ('symlinks') native_launcher_binary_copy ;; esac ;; esac # Enable a fake $HOME path local fake_home_persistent_directories fake_home_persistent_directories=$(fake_home_persistent_directories) if [ -n "$fake_home_persistent_directories" ]; then fake_home_enable fi application_prerun "$application" cat <<- 'EOF' ## Do not exit on application failure, ## to ensure post-run commands are run. set +o errexit EOF game_exec_line "$application" cat <<- 'EOF' game_exit_status=$? set -o errexit EOF application_postrun "$application" # Disable the fake $HOME path if [ -n "$fake_home_persistent_directories" ]; then fake_home_disable fi case "$game_engine" in ('unity3d') # Stop pulseaudio if it has been started for this game session launcher_unity3d_pulseaudio_stop ;; esac } # Linux native launcher - Copy the game binary into the game prefix # USAGE: native_launcher_binary_copy native_launcher_binary_copy() { cat <<- 'EOF' # Copy the game binary into the user prefix exe_destination="${PATH_PREFIX}/${APP_EXE}" if [ -h "$exe_destination" ]; then exe_source=$(realpath "$exe_destination") cp --remove-destination "$exe_source" "$exe_destination" fi unset exe_destination exe_source EOF } # Linux native launcher - Load shipped libraries # USAGE: native_launcher_libraries native_launcher_libraries() { local path_system path_user path_system=$(path_libraries) path_user='${HOME}/.local/lib/games/${GAME_ID}' cat <<- EOF # Set loading paths for libraries PLAYIT_LIBS_PATH_SYSTEM='$path_system' PLAYIT_LIBS_PATH_USER="$path_user" EOF cat <<- 'EOF' if [ -e "$PLAYIT_LIBS_PATH_SYSTEM" ]; then LD_LIBRARY_PATH="${PLAYIT_LIBS_PATH_SYSTEM}:${LD_LIBRARY_PATH}" fi if [ -e "$PLAYIT_LIBS_PATH_USER" ]; then LD_LIBRARY_PATH="${PLAYIT_LIBS_PATH_USER}:${LD_LIBRARY_PATH}" fi export LD_LIBRARY_PATH EOF } # Linux native - Print the line starting the game # USAGE: native_exec_line $application # RETURN: the command to execute, including its command line options native_exec_line() { local application application="$1" local application_options application_options=$(application_options "$application") cat <<- EOF "./\$APP_EXE" $application_options "\$@" EOF } src/50_engine_renpy/10_launchers.sh0000644000000000000000000000177513120060140016146 0ustar rootroot# Ren'Py - Print the launcher script content # USAGE: renpy_launcher $application renpy_launcher() { local application application="$1" local prefix_type prefix_type=$(application_prefix_type "$application") case "$prefix_type" in ('none') launcher_headers renpy_launcher_run launcher_exit ;; (*) error_launchers_prefix_type_unsupported "$application" return 1 ;; esac } # Ren'Py - Run Ren'Py # USAGE: renpy_launcher_run renpy_launcher_run() { cat <<- 'EOF' # Run the game EOF application_prerun "$application" game_exec_line "$application" application_postrun "$application" } # Ren'Py - Print the line starting the game # USAGE: renpy_exec_line $application # RETURN: the command to execute renpy_exec_line() { ## The application identifier is not actually used for Ren'Py games, ## it is only passed for consistency with other *_exec_line functions. local application application="$1" local path_game path_game=$(path_game_data) cat <<- EOF renpy "$path_game" EOF } src/50_engine_scummvm/10_applications.sh0000644000000000000000000000157413120060140017177 0ustar rootroot# Print the ScummVM id for the given application # USAGE: application_scummvm_scummid $application # RETURN: the ScummVM id, or an empty string if none is set application_scummvm_scummid() { local application application="$1" # Get the application ScummVM id from its identifier local application_scummid application_scummid=$(context_value "${application}_SCUMMID") # Return early if no ScummVM id is set if [ -z "$application_scummid" ]; then return 0 fi # Check that the id fits the ScummVM id format # Allowed formats are: # - "engine:game" # - "game" # The game field is allowed to include hyphen-minus, like in: "ags:gobliiins5-1" if ! printf '%s' "$application_scummid" | \ grep --quiet --regexp='^\([0-9a-z]\+:\)\?[-0-9a-z]\+$' then error_application_scummid_invalid "$application" "$application_scummid" return 1 fi printf '%s' "$application_scummid" } src/50_engine_scummvm/10_launchers.sh0000644000000000000000000000271213120060140016470 0ustar rootroot# ScummVM launcher - Print the script content # USAGE: scummvm_launcher $application scummvm_launcher() { local application application="$1" local prefix_type prefix_type=$(application_prefix_type "$application") case "$prefix_type" in ('none') launcher_headers scummvm_launcher_environment "$application" scummvm_launcher_run launcher_exit ;; (*) error_launchers_prefix_type_unsupported "$application" return 1 ;; esac } # ScummVM launcher - Set the environment # USAGE: scummvm_launcher_environment $application scummvm_launcher_environment() { local application application="$1" local path_game application_scummid path_game=$(path_game_data) application_scummid=$(application_scummvm_scummid "$application") cat <<- EOF # Set the environment PATH_GAME='$path_game' SCUMMVM_ID='$application_scummid' EOF } # ScummVM launcher - Run ScummVM # USAGE: scummvm_launcher_run scummvm_launcher_run() { cat <<- 'EOF' # Run the game EOF application_prerun "$application" game_exec_line "$application" application_postrun "$application" } # ScummVM - Print the line starting the game # USAGE: scummvm_exec_line $application # RETURN: the command to execute, including its command line options scummvm_exec_line() { local application application="$1" local application_options application_options=$(application_options "$application") cat <<- EOF scummvm --path="\$PATH_GAME" $application_options "\$@" "\$SCUMMVM_ID" EOF } src/50_engine_scummvm/90_messages.sh0000644000000000000000000000157013120060140016324 0ustar rootroot# Error - The provided ScummVM id uses an invalid format # USAGE: error_application_scummid_invalid $application $application_scummid error_application_scummid_invalid() { local application application_scummid application="$1" application_scummid="$2" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Lʼid ScummVM fourni pour lʼapplication %s ne semble pas correct : "%s"\n' message="$message"'Une liste de valeurs acceptées peut se trouver sur le site Web de ScummVM : \n%s\n' ;; ('en') message='The ScummVM id provided for application %s does not seem correct: "%s"\n' message="$message"'A list of valid values can be found on ScummVM website: \n%s\n' ;; esac print_message 'error' "$message" \ "$application" \ "$application_scummid" \ 'https://www.scummvm.org/compatibility/' } src/50_engine_wine/00_common.sh0000644000000000000000000000405313120060140015246 0ustar rootroot# WINE - Print the paths relative to the WINE prefix that should be diverted to persistent storage # USAGE: wine_persistent_directories # RETURN: A list of path to directories, # separated by line breaks. wine_persistent_directories() { local persistent_directories persistent_directories=$(context_value 'WINE_PERSISTENT_DIRECTORIES') # Fall back on the default list of directories for the current game engine if [ -z "$persistent_directories" ]; then local game_engine game_engine=$(game_engine) case "$game_engine" in ('unrealengine4') persistent_directories=$(unrealengine4_wine_persistent_directories_default) ;; esac fi printf '%s' "$persistent_directories" } # WINE - Print the list of winetricks verbs that should be applied during the WINE prefix initialization. # USAGE: wine_winetricks_verbs # RETURN: A list of winetricks verbs, # the list can be empty. wine_winetricks_verbs() { local winetricks_verbs winetricks_verbs=$(context_value 'WINE_WINETRICKS_VERBS') # Fall back on the legacy variable, for game scripts targeting ./play.it ≤ 2.25 if \ [ -z "$winetricks_verbs" ] && \ ! compatibility_level_is_at_least '2.26' then winetricks_verbs=$(context_value 'APP_WINETRICKS') if \ [ -n "$winetricks_verbs" ] && \ compatibility_level_is_at_least '2.25' then warning_deprecated_variable 'APP_WINETRICKS' 'WINE_WINETRICKS_VERBS' fi fi # Fall back on the default list of winetricks verbs for the current game engine if [ -z "$winetricks_verbs" ]; then local game_engine game_engine=$(game_engine) case "$game_engine" in ('unrealengine4') winetricks_verbs=$(unrealengine4_wine_winetricks_verbs_default) ;; esac fi # Append the verb used to set a virtual desktop, if required local virtual_desktop virtual_desktop=$(wine_virtual_desktop) case "$virtual_desktop" in ('none') ;; ('auto') winetricks_verbs="${winetricks_verbs:-} vd=\"\$(screen_resolution)\"" ;; (*) winetricks_verbs="${winetricks_verbs:-} vd=$virtual_desktop" ;; esac printf '%s' "$winetricks_verbs" } src/50_engine_wine/10_icons.sh0000644000000000000000000000166713120060140015102 0ustar rootroot# Compute the icon path from the APP_xxx_EXE value # USAGE: icon_wine_path $icon # RETURN: the icon path, it can include spaces, # or an empty string icon_wine_path() { # Get the application identifier from the icon identifier. local icon application icon="$1" application=$(icon_application "$icon") # Check that the application uses the "wine" type. local application_type application_type=$(application_type "$application") if [ -z "$application_type" ]; then error_no_application_type "$application" return 1 fi if [ "$application_type" != 'wine' ]; then error_application_wrong_type 'icon_wine_path' "$application_type" return 1 fi # Print the path to the game binary. application_exe=$(application_exe "$application") ## Check that application binary has been found if [ -z "$application_exe" ]; then error_application_exe_empty "$application" 'icon_wine_path' return 1 fi printf '%s' "$application_exe" } src/50_engine_wine/10_launchers.sh0000644000000000000000000001136113120060140015743 0ustar rootroot# WINE launcher - Print the script content # USAGE: wine_launcher $application wine_launcher() { local application application="$1" local prefix_type prefix_type=$(application_prefix_type "$application") case "$prefix_type" in ('symlinks') launcher_headers wine_launcher_environment "$application" # Generate the game prefix launcher_prefix_symlinks_functions launcher_prefix_symlinks_build # Set up the paths diversion to persistent storage persistent_storage_initialization persistent_storage_common persistent_path_diversion persistent_storage_update_directories persistent_storage_update_files # Generate the WINE prefix wine_launcher_wineprefix_environment wine_launcher_wineprefix_generate wine_launcher_wineprefix_persistent # Handle persistent storage of registry keys wine_launcher_regedit_environment wine_launcher_regedit_load wine_launcher_run "$application" # Handle persistent storage of registry keys wine_launcher_regedit_store # Update persistent storage with files from the current prefix persistent_storage_update_files_from_prefix launcher_exit ;; (*) error_launchers_prefix_type_unsupported "$application" return 1 ;; esac } # WINE launcher - Set the environment # USAGE: wine_launcher_environment $application wine_launcher_environment() { local application application="$1" local game_id path_game application_exe game_id=$(game_id) path_game=$(path_game_data) application_exe=$(application_exe_escaped "$application") cat <<- EOF # Set the environment GAME_ID='$game_id' PATH_GAME='$path_game' APP_EXE='$application_exe' EOF cat <<- 'EOF' ## Print the path to the `wine` command wine_command() { if [ -z "$PLAYIT_WINE_CMD" ]; then command -v wine return 0 fi printf '%s' "$PLAYIT_WINE_CMD" } wineboot_command() { wine_command | sed 's#/wine$#/wineboot#' } EOF ## The `wineserver` command is only used by winetricks local winetricks_verbs winetricks_verbs=$(wine_winetricks_verbs) if \ [ -n "$winetricks_verbs" ] || \ [ -n "${WINE_DIRECT3D_RENDERER:-}" ] then cat <<- 'EOF' wineserver_command() { wine_command | sed 's#/wine$#/wineserver#' } EOF fi ## Include the path to the `regedit` command only if it is going to be used if \ [ -n "${APP_REGEDIT:-}" ] || \ [ -n "${WINE_REGEDIT_PERSISTENT_KEYS:-}" ] then cat <<- 'EOF' regedit_command() { wine_command | sed 's#/wine$#/regedit#' } EOF fi # Include the winetricks wrapper function only if it is going to be used local winetricks_verbs winetricks_verbs=$(wine_winetricks_verbs) if \ [ -n "$winetricks_verbs" ] || \ [ -n "${WINE_DIRECT3D_RENDERER:-}" ] then cat <<- 'EOF' ## Apply winetricks verbs, spawning a terminal if required winetricks_wrapper() { ## Export custom paths to WINE commands ## so winetricks use them instead of the default paths WINE=$(wine_command) WINESERVER=$(wineserver_command) WINEBOOT=$(wineboot_command) export WINE WINESERVER WINEBOOT ## Run winetricks, spawning a terminal if required ## to ensure it is not silently running in the background if [ -t 0 ] || command -v zenity kdialog >/dev/null; then winetricks "$@" elif command -v xterm >/dev/null; then xterm -e winetricks "$@" else winetricks "$@" fi ## Wait a bit for lingering WINE processes to terminate sleep 1s } EOF fi # Include the screen resolution detection function if it is required to set a virtual desktop. local virtual_desktop virtual_desktop=$(wine_virtual_desktop) if [ "$virtual_desktop" = 'auto' ]; then wine_snippet_screen_resolution fi } # WINE - Print the snippet handling the actual run of the game # USAGE: wine_launcher_run $application wine_launcher_run() { local application application="$1" cat <<- 'EOF' # Run the game cd "${WINEPREFIX}/drive_c/${GAME_ID}" EOF local game_engine game_engine=$(game_engine) case "$game_engine" in ('visionaire') # Prevent the use of wayland SDL video driver visionaire_tweak_sdl_wine ;; esac application_prerun "$application" cat <<- 'EOF' ## Do not exit on application failure, ## to ensure post-run commands are run. set +o errexit EOF game_exec_line "$application" cat <<- 'EOF' game_exit_status=$? set -o errexit EOF application_postrun "$application" } # WINE - Print the line starting the game # USAGE: wine_exec_line $application # RETURN: the command to execute, including its command line options wine_exec_line() { local application application="$1" local application_options application_options=$(application_options "$application") cat <<- EOF \$(wine_command) "\$APP_EXE" $application_options "\$@" EOF } src/50_engine_wine/20_regedit.sh0000644000000000000000000001011513120060140015377 0ustar rootroot# WINE launcher - Set environment for registry keys persistent storage # USAGE: wine_launcher_regedit_environment wine_launcher_regedit_environment() { # Return early if no persistent registry keys are listed if [ -z "${WINE_REGEDIT_PERSISTENT_KEYS:-}" ]; then return 0 fi cat <<- 'EOF' # Set environment for registry keys persistent storage USER_PERSISTENT_PATH_REGEDIT="${USER_PERSISTENT_PATH}/wine/regedit" REGEDIT_DUMPS_WINEPREFIX_PATH="${WINEPREFIX}/drive_c/${GAME_ID}/wine/regedit" EOF cat <<- EOF REGEDIT_PERSISTENT_KEYS='$WINE_REGEDIT_PERSISTENT_KEYS' EOF cat <<- 'EOF' ## Convert registry key name to file path regedit_convert_key_to_path() { printf '%s.reg' "$1" | \ sed 's#\\#/#g' | \ tr '[:upper:]' '[:lower:]' } EOF } # WINE launcher - Load registry keys during prefix initialization # USAGE: regedit_initial regedit_initial() { # Return early if there is no key to load during prefix initilization if [ -z "${APP_REGEDIT:-}" ]; then return 0 fi cat <<- EOF ## Load registry scripts registry_scripts='$APP_REGEDIT' EOF cat <<- 'EOF' ( cd "${WINEPREFIX}/drive_c/${GAME_ID}" for registry_script in $registry_scripts; do printf 'Loading registry script: %s\n' "$registry_script" if [ ! -e "$registry_script" ]; then printf '\n\033[1;31mError:\033[0m\n' printf 'Failed to load required registry script: %s\n' "$registry_script" exit 1 fi set +o errexit $(regedit_command) "$registry_script" regedit_return_code=$? set -o errexit if [ $regedit_return_code -ne 0 ]; then printf '\n\033[1;31mError:\033[0m\n' printf 'Failed to load required registry script: %s\n' "$registry_script" exit 1 fi unset regedit_return_code done unset registry_script ) unset registry_scripts EOF } # WINE launcher - Store registry keys in a persistent path # USAGE: wine_launcher_regedit_store wine_launcher_regedit_store() { # Return early if no persistent registry keys are listed if [ -z "${WINE_REGEDIT_PERSISTENT_KEYS:-}" ]; then return 0 fi cat <<- 'EOF' # Store registry keys in a persistent path while read -r registry_key; do if [ -z "$registry_key" ]; then continue fi registry_dump="${REGEDIT_DUMPS_WINEPREFIX_PATH}/$(regedit_convert_key_to_path "$registry_key")" registry_dump_directory=$(dirname "$registry_dump") mkdir --parents "$registry_dump_directory" printf 'Dumping registry key in "%s".\n' "$registry_dump" $(regedit_command) -E "$registry_dump" "$registry_key" done << EOL $(printf '%s' "$REGEDIT_PERSISTENT_KEYS") EOL unset registry_key registry_dump registry_dump_directory mkdir --parents "$USER_PERSISTENT_PATH_REGEDIT" ( cd "$REGEDIT_DUMPS_WINEPREFIX_PATH" find . -type f \ -exec cp --force --parents --target-directory="$USER_PERSISTENT_PATH_REGEDIT" {} + ) EOF } # WINE launcher - Load registry keys from persistent dumps # USAGE: wine_launcher_regedit_load wine_launcher_regedit_load() { # Return early if no persistent registry keys are listed if [ -z "${WINE_REGEDIT_PERSISTENT_KEYS:-}" ]; then return 0 fi cat <<- 'EOF' # Load registry keys from persistent dumps if [ -e "$USER_PERSISTENT_PATH_REGEDIT" ]; then mkdir --parents "$REGEDIT_DUMPS_WINEPREFIX_PATH" ( cd "$USER_PERSISTENT_PATH_REGEDIT" find . -type f \ -exec cp --force --parents --target-directory="$REGEDIT_DUMPS_WINEPREFIX_PATH" {} + ) fi while read -r registry_key; do if [ -z "$registry_key" ]; then continue fi registry_dump="${REGEDIT_DUMPS_WINEPREFIX_PATH}/$(regedit_convert_key_to_path "$registry_key")" if [ -e "$registry_dump" ]; then printf 'Loading registry key from "%s".\n' "$registry_dump" $(regedit_command) "$registry_dump" fi done << EOL $(printf '%s' "$REGEDIT_PERSISTENT_KEYS") EOL unset registry_key registry_dump EOF } src/50_engine_wine/20_renderer.sh0000644000000000000000000001030013120060140015556 0ustar rootroot# Print the name of the renderer to use for Direct3D # USAGE: wine_renderer_name wine_renderer_name() { # Fetch the preferred renderer from the game script, # if it is explicitely set. local direct3d_renderer direct3d_renderer="${WINE_DIRECT3D_RENDERER:-}" # Fall back on the default renderer for the current game engine if [ -z "$direct3d_renderer" ]; then local game_engine game_engine=$(game_engine) case "$game_engine" in ('unrealengine4') direct3d_renderer=$(unrealengine4_wine_renderer_name_default) ;; esac fi # Fall back to using the default renderer if [ -z "$direct3d_renderer" ]; then direct3d_renderer='default' fi # Convert "wined3d" alias to "wined3d/gl" if [ "$direct3d_renderer" = 'wined3d' ]; then direct3d_renderer='wined3d/gl' fi # Check that an allowed value has been set case "$direct3d_renderer" in ( \ 'default' | \ 'wined3d/gl' | \ 'wined3d/gdi' | \ 'wined3d/vulkan' | \ 'dxvk' | \ 'vkd3d' \ ) printf '%s' "$direct3d_renderer" return 0 ;; (*) error_unknown_wine_renderer "$direct3d_renderer" return 1 ;; esac } # WINE launcher - Set the correct Direct3D renderer # USAGE: wine_launcher_renderer wine_launcher_renderer() { local direct3d_renderer direct3d_renderer=$(wine_renderer_name) case "$direct3d_renderer" in ('default') # Nothing to do here. ;; ( \ 'wined3d/gl' | \ 'wined3d/gdi' | \ 'wined3d/vulkan' \ ) local wined3d_backend wined3d_backend=$(printf '%s' "$direct3d_renderer" | cut --delimiter='/' --fields=2) wine_launcher_renderer_wined3d "$wined3d_backend" ;; ('dxvk') wine_launcher_renderer_dxvk ;; ('vkd3d') wine_launcher_renderer_vkd3d ;; esac } # WINE launcher - Use WineD3D with a specific backend for Direct3D rendering # USAGE: wine_launcher_renderer_wined3d $wined3d_backend wine_launcher_renderer_wined3d() { local wined3d_backend wined3d_backend="$1" cat <<- EOF ## Use WineD3D for Direct3D rendering, with the "$wined3d_backend" backend wined3d_backend="$wined3d_backend" EOF cat <<- 'EOF' if command -v winetricks >/dev/null 2>&1; then winetricks_wrapper renderer=$wined3d_backend else message="\\033[1;33mWarning:\\033[0m\\n" message="${message}WineD3D backend could not be set to ${wined3d_backend}.\\n" message="${message}The game might run with display or performance issues.\\n" printf "\\n${message}\\n" fi unset wined3d_backend ## Wait a bit to ensure there is no lingering wine process sleep 1s EOF } # WINE launcher - Use DXVK for Direct3D rendering # USAGE: wine_launcher_renderer_dxvk wine_launcher_renderer_dxvk() { cat <<- 'EOF' ## Use DXVK for Direct3D 9/10/11 rendering if \ command -v dxvk-setup >/dev/null 2>&1 && \ command -v wine-development >/dev/null 2>&1 then ## Run dxvk-setup, spawning a terminal if required ## to ensure it is not silently running in the background. if [ ! -t 0 ] && command -v xterm >/dev/null; then xterm -e dxvk-setup install --development else dxvk-setup install --development fi elif command -v winetricks >/dev/null 2>&1; then winetricks_wrapper dxvk else message="\\033[1;33mWarning:\\033[0m\\n" message="${message}DXVK patches could not be installed in the WINE prefix.\\n" message="${message}The game might run with display or performance issues.\\n" printf "\\n${message}\\n" fi ## Wait a bit to ensure there is no lingering wine process sleep 1s EOF } # WINE launcher - Use vkd3d for Direct3D rendering # USAGE: wine_launcher_renderer_vkd3d wine_launcher_renderer_vkd3d() { cat <<- 'EOF' ## Install vkd3d on first launch if command -v winetricks >/dev/null 2>&1; then winetricks_wrapper vkd3d else message="\\033[1;33mWarning:\\033[0m\\n" message="${message}vkd3d patches could not be installed in the WINE prefix.\\n" message="${message}The game might run with display or performance issues.\\n" printf "\\n${message}\\n" fi ## Wait a bit to ensure there is no lingering wine process sleep 1s EOF } src/50_engine_wine/20_virtual-desktop.sh0000644000000000000000000000511513120060140017115 0ustar rootroot# WINE - Get the WINE virtual desktop setting expected by the current game. # # The value should be one of: # - none (default if no value is set) # - auto (use the current screen resolution when the game is launched for the first time) # - some specific resolution (example: "1280x1024") # # USAGE: wine_virtual_desktop wine_virtual_desktop() { local virtual_desktop virtual_desktop=${WINE_VIRTUAL_DESKTOP:-none} case "$virtual_desktop" in ('none'|'auto') ;; (*[0-9]x[0-9]*) if ! printf '%s' "$virtual_desktop" | grep --silent '^[0-9]\+x[0-9]\+$'; then error_wine_virtual_desktop_invalid "$virtual_desktop" fi ;; (*) error_wine_virtual_desktop_invalid "$virtual_desktop" ;; esac printf '%s' "$virtual_desktop" } # WINE - Get the display resolution of the current screen. # USAGE: wine_snippet_screen_resolution wine_snippet_screen_resolution() { cat <<- 'EOF' # Return the type of the current display server. # Supported return values: # - wayland/sway # - xorg # If no supported server is running, nothing is returned. display_server() { if [ -n "${WAYLAND_DISPLAY:-}" ]; then if [ -n "${SWAYSOCK:-}" ]; then display_server='wayland/sway' fi elif [ -n "${DISPLAY:-}" ]; then display_server='xorg' fi printf '%s' "${display_server:-}" unset display_server } # Return the resolution of the current screen. # If no supported server is running, a fallback value is returned. screen_resolution() { display_server=$(display_server) case "$display_server" in ('wayland/sway') current_screen=$( LANG=C swaymsg --pretty --type get_workspaces | \ grep --after 1 '(focused)' | \ sed --silent --expression='s/\s*Output: \(.*\)$/\1/p' ) screen_resolution=$( LANG=C swaymsg --pretty --type get_outputs | \ grep --after 1 "$current_screen" | \ sed --silent --expression='s/.*Current mode: \([0-9]\+x[0-9]\+\) @.*$/\1/p' ) unset current_screen ;; ('xorg') screen_resolution=$( LANG=C xrandr | \ sed --regexp-extended --silent --expression='s/.*primary.* ([0-9]+x[0-9]+).*/\1/p' ) ;; (*) ## Fall back on a default resolution if an unsupported display server is in use. screen_resolution='1024x768' ;; esac printf '%s' "${screen_resolution:-}" unset display_server screen_resolution } EOF } src/50_engine_wine/20_wineprefix.sh0000644000000000000000000001462213120060140016143 0ustar rootroot# WINE - Set default value for WINEDLLOVERRIDES # USAGE: wine_dlloverrides_default # RETURN: the default value to use for WINEDLLOVERRIDES, # if it is not set in the user environment. wine_dlloverrides_default() { # A default value can be set from the game script, using the variable WINE_DLLOVERRIDES_DEFAULT local wine_dlloverrides wine_dlloverrides=$(context_value 'WINE_DLLOVERRIDES_DEFAULT') # Fall back on a default value if [ -z "$wine_dlloverrides" ]; then local wineprefix_tweaks wineprefix_tweaks=$(wine_wineprefix_tweaks) if printf '%s' "$wineprefix_tweaks" | grep --quiet --fixed-strings --line-regexp --regexp='mono'; then ## If Mono is going to be used in the WINE prefix, "mscoree" should not be disabled. wine_dlloverrides='winemenubuilder.exe,mshtml=' else wine_dlloverrides='winemenubuilder.exe,mscoree,mshtml=' fi fi printf '%s' "$wine_dlloverrides" } # WINE launcher - Set the WINE prefix environment # USAGE: wine_launcher_wineprefix_environment wine_launcher_wineprefix_environment() { # Compute path to WINE prefix cat <<- 'EOF' # Compute the path to WINE prefix for the current session wineprefix_path() { # Prefix path can be explicitely set using an environment variable if [ -n "$WINEPREFIX" ]; then printf '%s' "$WINEPREFIX" return 0 fi # Compute the default prefix path if none has been explicitely set printf '%s/play.it/wine/%s' \ "${XDG_CACHE_HOME:="$HOME/.cache"}" \ "$GAME_ID" } EOF # Set compatibility links to legacy paths cat <<- 'EOF' # Set compatibility link to a legacy user path wineprefix_legacy_link() { path_current="$1" path_legacy="$2" user_directory="${WINEPREFIX}/drive_c/users/${USER}" ( cd "$user_directory" if [ ! -e "$path_current" ]; then path_current_parent=$(dirname "$path_current") mkdir --parents "$path_current_parent" mv "$path_legacy" "$path_current" fi path_legacy_parent=$(dirname "$path_legacy") mkdir --parents "$path_legacy_parent" ## Warning: the use of a link to an absolute path means that the WINE prefix can not be moved around without breaking the link. ## Using a link to a relative path would be better, but harder to implement when $path_legacy includes several path components (like "Local Settings/Application Data"). ln --symbolic "${user_directory}/${path_current}" "$path_legacy" ) unset user_directory path_current_parent unset path_current path_legacy } EOF # Compute WINE prefix architecture local package package_architecture wine_architecture package=$(current_package) package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') wine_architecture='win32' ;; ('64') wine_architecture='win64' ;; esac # Set variables used for WINE prefix local wine_dlloverrides_default wine_dlloverrides_default=$(wine_dlloverrides_default) cat <<- EOF # Set variables used for WINE prefix WINEARCH='$wine_architecture' WINEDEBUG="\${WINEDEBUG:=-all}" WINEPREFIX=\$(wineprefix_path) ## Disable some WINE anti-features ## - creation of desktop entries ## - installation of Mono ## - installation of Gecko WINEDLLOVERRIDES="\${WINEDLLOVERRIDES:=${wine_dlloverrides_default}}" ## Work around WINE bug 41639 - Wine with freetype 2.7 causes font rendering issues ## cf. https://bugs.winehq.org/show_bug.cgi?id=41639 FREETYPE_PROPERTIES='truetype:interpreter-version=35' export WINEARCH WINEDEBUG WINEDLLOVERRIDES WINEPREFIX FREETYPE_PROPERTIES EOF } # WINE launcher - Generate the WINE prefix # USAGE: wine_launcher_wineprefix_generate wine_launcher_wineprefix_generate() { cat <<- 'EOF' # Generate the WINE prefix if ! [ -e "$WINEPREFIX" ]; then mkdir --parents "$(dirname "$WINEPREFIX")" ## Use LANG=C to avoid localized directory names LANG=C $(wineboot_command) --init 2>/dev/null ## Wait until the WINE prefix creation is complete printf "Waiting for the WINE prefix initialization to complete, it might take a couple seconds…\\n" while [ ! -f "${WINEPREFIX}/system.reg" ]; do sleep 1s done ## Link game prefix into WINE prefix ln --symbolic \ "$PATH_PREFIX" \ "${WINEPREFIX}/drive_c/${GAME_ID}" ## Remove most links pointing outside of the WINE prefix rm "$WINEPREFIX/dosdevices/z:" find "$WINEPREFIX/drive_c/users/$(whoami)" -type l | while read -r directory; do rm "$directory" mkdir "$directory" done unset directory ## Set links to legacy paths wineprefix_legacy_link 'AppData/Roaming' 'Application Data' wineprefix_legacy_link 'AppData/Local' 'Local Settings/Application Data' wineprefix_legacy_link 'Documents' 'My Documents' EOF # If required, install Mono in the prefix local wineprefix_tweaks wineprefix_tweaks=$(wine_wineprefix_tweaks) if printf '%s' "$wineprefix_tweaks" | grep --quiet --fixed-strings --line-regexp --regexp='mono'; then wine_wineprefix_tweak_mono_install fi # Run initial winetricks call local winetricks_verbs winetricks_verbs=$(wine_winetricks_verbs) if [ -n "$winetricks_verbs" ]; then cat <<- EOF ## Run initial winetricks call winetricks_wrapper ${winetricks_verbs} EOF fi # Load registry scripts regedit_initial # Set Direct3D renderer wine_launcher_renderer cat <<- 'EOF' fi EOF } # WINE launcher - Handle paths diversion from WINE prefix to persistent storage # USAGE: wine_launcher_wineprefix_persistent wine_launcher_wineprefix_persistent() { local persistent_directories persistent_directories=$(wine_persistent_directories) if [ -n "$persistent_directories" ]; then cat <<- EOF # Divert paths from the WINE prefix to persistent storage WINE_PERSISTENT_DIRECTORIES="$persistent_directories" EOF cat <<- 'EOF' while read -r directory; do if [ -z "$directory" ]; then continue fi persistent_path_diversion "${WINEPREFIX}/drive_c" "${USER_PERSISTENT_PATH}/wineprefix" "$directory" done <<- EOL $(printf '%s' "$WINE_PERSISTENT_DIRECTORIES") EOL unset directory EOF return 0 fi # Handle diversions using the deprecated APP_WINE_LINK_DIRS variable, # for game scripts targeting ./play.it < 2.24. if compatibility_level_is_at_least '2.24'; then return 0 fi if [ -n "${APP_WINE_LINK_DIRS:-}" ]; then wine_launcher_wineprefix_persistent_legacy fi } src/50_engine_wine/25_wineprefix-tweaks.sh0000644000000000000000000000134513120060140017442 0ustar rootroot# WINE - List the tweaks that should be applied to the WINE prefix # USAGE: wine_wineprefix_tweaks # RETURN: the list of tweaks, one per line wine_wineprefix_tweaks() { local tweaks_list tweaks_list=${WINE_WINEPREFIX_TWEAKS:-} printf '%s' "$tweaks_list" | list_clean } # WINE - Print the snippet installing Mono in the WINE prefix # USAGE: wine_wineprefix_tweak_mono_install # RETURN: the code snippet, for inclusion in the game launcher script wine_wineprefix_tweak_mono_install() { local mono_installer_name mono_installer_name=$(archive_name 'ARCHIVE_MONO') { cat <<- EOF ## Install Mono in the WINE prefix. \$(wine_command) "\${WINEPREFIX}/drive_c/\${GAME_ID}/wineprefix-tweaks/${mono_installer_name}" EOF } } src/50_engine_wine/90_messages.sh0000644000000000000000000000216313120060140015576 0ustar rootroot# Error - The provided value for the WINE Direct3D renderer is invalid # USAGE: error_unknown_wine_renderer $unknown_value error_unknown_wine_renderer() { local unknown_value unknown_value="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La valeur suivante est invalide pour WINE_DIRECT3D_RENDERER : %s\n' ;; ('en'|*) message='The following value is not supported for WINE_DIRECT3D_RENDERER: %s\n' ;; esac print_message 'error' "$message" \ "$unknown_value" } # Error - The provided setting for the WINE virtual desktop is invalid. # USAGE: error_wine_virtual_desktop_invalid $invalid_setting error_wine_virtual_desktop_invalid() { local invalid_setting invalid_setting="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La valeur suivante est invalide pour WINE_VIRTUAL_DESKTOP : "%s"\n' ;; ('en'|*) message='The following value is not supported for WINE_VIRTUAL_DESKTOP: "%s"\n' ;; esac print_message 'error' "$message" \ "$invalid_setting" } src/55_engine-variant_unity3d/00_common.sh0000644000000000000000000000426413120060140017356 0ustar rootroot# Print the path to the engine file including the Unity3D build version # USAGE: unity3d_version_file $file_name # RETURN: the path to the engine file including the build version string unity3d_version_file() { local file_name file_name="$1" local unity3d_name engine_info_file unity3d_name=$(unity3d_name) engine_info_file="${unity3d_name}_Data/${file_name}" # Look for the engine file in the temporary path for archive content. local content_path engine_info_file_path content_path=$(content_path_default) engine_info_file_path="${PLAYIT_WORKDIR}/gamedata/${content_path}/${engine_info_file}" if [ -f "$engine_info_file_path" ]; then printf '%s' "$engine_info_file_path" return 0 fi # Look for the engine file in the current package. local package package_path path_game_data package=$(current_package) package_path=$(package_path "$package") path_game_data=$(path_game_data) engine_info_file_path="${package_path}${path_game_data}/${engine_info_file}" if [ -f "$engine_info_file_path" ]; then printf '%s' "$engine_info_file_path" return 0 fi # Look for the engine file in all packages. local packages_list packages_list=$(packages_list) for package in $packages_list; do package_path=$(package_path "$package") engine_info_file_path="${package_path}${path_game_data}/${engine_info_file}" if [ -f "$engine_info_file_path" ]; then printf '%s' "$engine_info_file_path" return 0 fi done } # Print the Unity3D build version for the current game # USAGE: unity3d_version # RETURN: the build version string unity3d_version() { # Find the engine file including the build version string. local engine_info_file_path ## Recent Unity3D releases stores the build version string in a file named "globalgamemanagers". engine_info_file_path=$(unity3d_version_file 'globalgamemanagers') if [ -z "$engine_info_file_path" ]; then ## Old Unity3D releases stores the build version string in a file named "mainData". engine_info_file_path=$(unity3d_version_file 'mainData') fi # Return early if the engine data file including the build version string could not be found. if [ -z "$engine_info_file_path" ]; then return 0 fi strings "$engine_info_file_path" | head --lines=1 } src/55_engine-variant_unity3d/30_applications.sh0000644000000000000000000000317413120060140020556 0ustar rootroot# Compute the file name of the game binary from the UNITY3D_NAME value # USAGE: unity3d_application_exe_default application # RETURN: the application file name, # or an empty string unity3d_application_exe_default() { local application application="$1" # We can not rely on the application type here, # to avoid a loop between application_exe and application_type. local unity3d_name unity3d_name=$(unity3d_name) local package package_architecture package=$(current_package) package_architecture=$(package_architecture "$package") local filename_candidates_list filename_candidate filename_path filename_found case "$package_architecture" in ('32') filename_candidates_list=" ${unity3d_name}.x86 ${unity3d_name}.exe ${unity3d_name}" ;; ('64') filename_candidates_list=" ${unity3d_name}.x86_64 ${unity3d_name}.exe ${unity3d_name}" ;; (*) filename_candidates_list=" ${unity3d_name}.x86 ${unity3d_name}.x86_64 ${unity3d_name}.exe ${unity3d_name}" ;; esac ## Use a while loop to avoid breaking on spaces in file name. while read -r filename_candidate; do ## Skip empty lines. if [ -z "$filename_candidate" ]; then continue fi filename_path=$(application_exe_path "$filename_candidate") if [ -n "$filename_path" ]; then filename_found="$filename_candidate" break fi done <<- EOL $(printf '%s' "$filename_candidates_list") EOL ## Throw an error if no binary has been found, ## as this would cause other problems later in the script execution. if [ -z "${filename_found:-}" ]; then error_unity3d_binary_not_found return 1 fi printf '%s' "$filename_found" } src/55_engine-variant_unity3d/30_content.sh0000644000000000000000000001301113120060140017531 0ustar rootroot# Print the Unity3D name for the current game # This function should not fail if no Unity3D name is set for the current game, # so it can be used to automatically detect games using the "unity3d" type variant. # USAGE: unity3d_name # RETURN: the Unity3D name, a string that can include spaces, # or an empty string if none is set unity3d_name() { context_value 'UNITY3D_NAME' } # Print the list of Unity3D plugins to include for the current game # USAGE: unity3d_plugins # RETURN: the list of plugins to include, one per line unity3d_plugins() { context_value 'UNITY3D_PLUGINS' } # Include the shipped Unity3D plugins into the current package # USAGE: content_inclusion_unity3d_plugins $package content_inclusion_unity3d_plugins() { local package package="$1" # Set the plugins source path local unity3d_name content_path plugins_directory plugins_path unity3d_name=$(unity3d_name) content_path=$(content_path_default) plugins_directory="${content_path}/${unity3d_name}_Data/Plugins" plugins_path="${PLAYIT_WORKDIR}/gamedata/${plugins_directory}" local CONTENT_UNITY3D_PLUGINS_FILES target_directory ## Silence ShellCheck false-positive ## CONTENT_UNITY3D_PLUGINS_FILES appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_UNITY3D_PLUGINS_FILES=$(unity3d_plugins) target_directory=$( set_current_package "$package" path_libraries ) # Proceed with the actual files inclusion. local package_architecture architecture_string package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') architecture_string='x86' ;; ('64') architecture_string='x86_64' ;; ('all') # Return early if the current package should not include binaries. return 0 ;; esac if [ -d "${plugins_path}/${architecture_string}" ]; then local CONTENT_UNITY3D_PLUGINS_PATH ## Silence ShellCheck false-positive ## CONTENT_UNITY3D_PLUGINS_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_UNITY3D_PLUGINS_PATH="${plugins_directory}/${architecture_string}" content_inclusion 'UNITY3D_PLUGINS' "$package" "$target_directory" # Delete the remaining plugins for the current architecture, # to prevent them from being included later. rm --force "${plugins_path}/${architecture_string}"/* rmdir --ignore-fail-on-non-empty --parents "${plugins_path}/${architecture_string}" fi # Some games include plugins in the "Plugins" directory, # without using an architecture sub-directory. if [ -d "$plugins_path" ]; then local CONTENT_UNITY3D_PLUGINS_PATH ## Silence ShellCheck false-positive ## CONTENT_UNITY3D_PLUGINS_FILES appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_UNITY3D_PLUGINS_PATH="$plugins_directory" content_inclusion 'UNITY3D_PLUGINS' "$package" "$target_directory" rm --force "$plugins_path"/*.* rmdir --ignore-fail-on-non-empty --parents "$plugins_path" fi } # Unity3D - Print the list of files to include from the archive for a given identifier # USAGE: unity3d_content_files_default $content_id # RETURN: a list of paths relative to the path for the given identifier, # line breaks are used as separator between each item, # this list can include globbing patterns, # this list can be empty unity3d_content_files_default() { local content_id content_id="$1" local unity3d_name unity3d_name=$(unity3d_name) local applications_list application application_type applications_list=$(applications_list) if [ -z "$applications_list" ]; then error_applications_list_empty fi application=$(printf '%s' "$applications_list" | head --lines=1) application_type=$(application_type "$application") local content_files case "$content_id" in ('GAME_BIN') case "$application_type" in ('native') content_files=" MonoBleedingEdge ${unity3d_name}_Data/Mono ${unity3d_name}_Data/MonoBleedingEdge ${unity3d_name}.x86_64 ${unity3d_name}.x86 ${unity3d_name} *.so" ;; ('wine') content_files=" Mono MonoBleedingEdge ${unity3d_name}_Data/Mono ${unity3d_name}_Data/MonoBleedingEdge ${unity3d_name}_Data/Plugins ${unity3d_name}.exe baselib.dll GameAssembly.dll UnityPlayer.dll" ## Include the lowercase variants of all paths. content_files="$content_files mono monobleedingedge ${unity3d_name}_data/mono ${unity3d_name}_data/monobleedingedge ${unity3d_name}_data/plugins gameassembly.dll unityplayer.dll" ## Game scripts targeting ./play.it < 2.28 might rely on more .dll files being included. if ! compatibility_level_is_at_least '2.28'; then content_files="$content_files *.dll" fi ;; esac ;; ('GAME_BIN32') case "$application_type" in ('native') content_files=" MonoBleedingEdge/x86 ${unity3d_name}_Data/Mono/x86 ${unity3d_name}_Data/MonoBleedingEdge/x86 ${unity3d_name}.x86 ${unity3d_name}" ;; esac ;; ('GAME_BIN64') case "$application_type" in ('native') content_files=" MonoBleedingEdge/x86_64 ${unity3d_name}_Data/Mono/x86_64 ${unity3d_name}_Data/MonoBleedingEdge/x86_64 ${unity3d_name}.x86_64 ${unity3d_name}" ;; esac ;; ('GAME_DATA') case "$application_type" in ('native') content_files=" ${unity3d_name}_Data" ;; ('wine') content_files=" ${unity3d_name}_Data ${unity3d_name}_data" ;; esac ;; esac printf '%s' "${content_files:-}" } src/55_engine-variant_unity3d/30_icons.sh0000644000000000000000000000136713120060140017205 0ustar rootroot# Unity3D - Compute the icon path from the UNITY3D_NAME value # USAGE: unity3d_icon_path # RETURN: the path to the icon file unity3d_icon_path() { local icon_path unity3d_name application application_type unity3d_name=$(unity3d_name) application=$(icon_application "$icon") application_type=$(application_type "$application") ## Throw an error if no application type is found, ## this is unexpected when relying on the default icon path for Unity3D games. if [ -z "$application_type" ]; then error_no_application_type "$application" return 1 fi case "$application_type" in ('native') icon_path="${unity3d_name}_Data/Resources/UnityPlayer.png" ;; ('wine') icon_path="${unity3d_name}.exe" ;; esac printf '%s' "${icon_path:-}" } src/55_engine-variant_unity3d/30_launchers.sh0000644000000000000000000000653013120060140020053 0ustar rootroot# Print the snippet starting pulseaudio if it is available # USAGE: launcher_unity3d_pulseaudio_start launcher_unity3d_pulseaudio_start() { cat <<- 'EOF' # Start pulseaudio if it is available pulseaudio_is_available() { command -v pulseaudio >/dev/null 2>&1 } if pulseaudio_is_available; then if ! pulseaudio --check; then touch .stop_pulseaudio_on_exit fi pulseaudio --start fi EOF } # Print the snippet stopping pulseaudio if it has been started for this game session # USAGE: launcher_unity3d_pulseaudio_stop launcher_unity3d_pulseaudio_stop() { cat <<- 'EOF' # Stop pulseaudio if it has been started for this game session if [ -e .stop_pulseaudio_on_exit ]; then pulseaudio --kill rm .stop_pulseaudio_on_exit fi EOF } # Print the snippet hiding libpulse-simple.so.0 if pulseaudio is not available # USAGE: launcher_unity3d_pulseaudio_hide_libpulse launcher_unity3d_pulseaudio_hide_libpulse() { cat <<- 'EOF' # Work around crash on launch related to libpulse # Some Unity3D games crash on launch if libpulse-simple.so.0 is available but pulseaudio is not running libpulse_null_link="${PLAYIT_LIBS_PATH_USER}/libpulse-simple.so.0" if pulseaudio_is_available; then rm --force "$libpulse_null_link" else mkdir --parents "$PLAYIT_LIBS_PATH_USER" ln --force --symbolic /dev/null "$libpulse_null_link" fi unset libpulse_null_link EOF } # Print the snippet setting forcing the use of a US-like locale # USAGE: launcher_unity3d_force_locale # RETURN: the code snippet, a multi-lines string launcher_unity3d_force_locale() { cat <<- 'EOF' # Work around Unity3D poor support for non-US locales export LANG=C EOF } # Unity3D - Force the use of the system SDL library, for use with native games # USAGE: unity3d_tweak_sdl_native unity3d_tweak_sdl_native() { local path_libraries path_libraries=$(path_libraries_system) cat <<- EOF # Force the use of the system SDL library export SDL_DYNAMIC_API="${path_libraries}/libSDL2-2.0.so.0" EOF } # Disable the MAP_32BIT flag to prevent a crash one some Linux versions when running a 64-bit build of Unity3D # USAGE: unity3d_disable_map32bit # RETURN: the code snippet, a multi-lines string, indented with four spaces unity3d_disable_map32bit() { local hacks_list hacks_list=$(hacks_list) export PRELOAD_HACKS_LIST="$hacks_list HACK_UNITY3D_DISABLE_MAP32BIT" local package package=$(current_package) export HACK_UNITY3D_DISABLE_MAP32BIT_NAME='disable-map32bit' export HACK_UNITY3D_DISABLE_MAP32BIT_PACKAGE="$package" export HACK_UNITY3D_DISABLE_MAP32BIT_DESCRIPTION='LD_PRELOAD shim disabling the MAP_32BIT flag This prevents crashes on some Linux versions when running a 64-bit build of Unity3D.' export HACK_UNITY3D_DISABLE_MAP32BIT_SOURCE=' #define _GNU_SOURCE #include #include #include typedef void *(*orig_mmap_type)(void *addr, size_t length, int prot, int flags, int fd, off_t offset); void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) { static orig_mmap_type orig_mmap = NULL; if (orig_mmap == NULL) orig_mmap = (orig_mmap_type)dlsym(RTLD_NEXT, "mmap"); flags &= ~MAP_32BIT; return orig_mmap(addr, length, prot, flags, fd, offset); } ' hack_build 'HACK_UNITY3D_DISABLE_MAP32BIT' hack_inclusion 'HACK_UNITY3D_DISABLE_MAP32BIT' } src/55_engine-variant_unity3d/90_messages.sh0000644000000000000000000000123113120060140017675 0ustar rootroot# Error - Unity3D - No binary has been found # USAGE: error_unity3d_binary_not_found error_unity3d_binary_not_found() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Aucun exécutable nʼa été trouvé pour ce jeu Unity3D.\n' message="$message"'Cette erreur est probablement due à une valeur incorrecte assignée à CONTENT_PATH_DEFAULT.\n' ;; ('en'|*) message='No binary has been found for the current Unity3D game.\n' message="$message"'This is probably due to an incorrect value for the CONTENT_PATH_DEFAULT variable.\n' ;; esac print_message 'error' "$message" } src/55_engine-variant_unrealengine4/30_content.sh0000644000000000000000000000356113120060140020703 0ustar rootroot# Unreal Engine 4 - Print the Unreal Engine 4 name for the current game # This function should not fail if no Unreal Engine 4 name is set for the current game, # so it can be used to automatically detect games using the "unrealengine4" type variant. # USAGE: unrealengine4_name # RETURN: the Unreal Engine 4 name, a string that can include spaces, # or an empty string if none is set unrealengine4_name() { context_value 'UNREALENGINE4_NAME' } # Unreal Engine 4 - Print the list of files to include from the archive for a given identifier # USAGE: unrealengine4_content_files_default $content_id # RETURN: a list of paths relative to the path for the given identifier, # line breaks are used as separator between each item, # this list can include globbing patterns, # this list can be empty unrealengine4_content_files_default() { local content_id content_id="$1" local unrealengine4_name unrealengine4_name=$(unrealengine4_name) local applications_list application application_type applications_list=$(applications_list) if [ -z "$applications_list" ]; then error_applications_list_empty fi application=$(printf '%s' "$applications_list" | head --lines=1) application_type=$(application_type "$application") local content_files case "$content_id" in ('GAME_BIN') case "$application_type" in ('native') content_files=" Engine ${unrealengine4_name}/Binaries ${unrealengine4_name}/Plugins" ;; ('wine') content_files=" engine ${unrealengine4_name}/binaries ${unrealengine4_name}/plugins ${unrealengine4_name}.exe" ;; esac ;; ('GAME_DATA') case "$application_type" in ('native') content_files=" ${unrealengine4_name}/Content" ;; ('wine') content_files=" ${unrealengine4_name}/content" ;; esac ;; esac printf '%s' "${content_files:-}" } src/55_engine-variant_unrealengine4/30_icons.sh0000644000000000000000000000037313120060140020342 0ustar rootroot# Unreal Engine 4 - Print the default wrestool options string # USAGE: unrealengine4_icon_wrestool_options_default # RETURN: the options string to pass to wrestool unrealengine4_icon_wrestool_options_default() { printf '%s' '--type=14 --name=101' } src/55_engine-variant_unrealengine4/30_packages.sh0000644000000000000000000000200513120060140020777 0ustar rootroot# Unreal Engine 4 - Print a default list of required GStreamer decoders # USAGE: unrealengine4_dependencies_list_gstreamer_decoders_default $package # RETURNS: a list of GStreamer decoders, one per line, # the list can be empty. unrealengine4_dependencies_list_gstreamer_decoders_default() { local package package="$1" # Return early if the current package should not depend on GStreamer plugins local package_architecture package_architecture=$(package_architecture "$package") if [ "$package_architecture" = 'all' ]; then return 0 fi local applications_list application application_type applications_list=$(applications_list) if [ -z "$applications_list" ]; then error_applications_list_empty fi application=$(printf '%s' "$applications_list" | head --lines=1) application_type=$(application_type "$application") local gstreamer_decoders case "$application_type" in ('wine') gstreamer_decoders=' video/quicktime, variant=(string)iso' ;; esac printf '%s' "${gstreamer_decoders:-}" } src/55_engine-variant_unrealengine4/50_wine.sh0000644000000000000000000000162513120060140020174 0ustar rootroot# Unreal Engine 4 - Print the name of the renderer to use for Direct3D # USAGE: unrealengine4_wine_renderer_name_default unrealengine4_wine_renderer_name_default() { printf '%s' 'dxvk' } # Unreal Engine 4 - Print the paths relative to the WINE prefix that should be diverted to persistent storage # USAGE: unrealengine4_wine_persistent_directories_default # RETURN: A list of path to directories, # separated by line breaks. unrealengine4_wine_persistent_directories_default() { local unrealengine4_name unrealengine4_name=$(unrealengine4_name) printf 'users/${USER}/AppData/Local/%s/Saved' "$unrealengine4_name" } # Unreal Engine 4 - Print a default list of winetricks verb that should be applied during the WINE prefix initialization # USAGE: unrealengine4_wine_winetricks_verbs_default # RETURN: A list of winetricks verbs unrealengine4_wine_winetricks_verbs_default() { printf '%s' 'vcrun2019' } src/55_engine-variant_visionaire/30_applications.sh0000644000000000000000000000214113120060140021320 0ustar rootroot# Visionaire - Get the list of applications # USAGE: visionaire_applications_list # RETURN: a list of application identifiers, # separated by line breaks visionaire_applications_list() { printf '%s\n' \ 'APP_MAIN' } # Visionaire - Get the name of the game binary # USAGE: visionaire_application_exe # RETURN: the game binary file name, # or an empty string visionaire_application_exe() { ## WARNING: We can not rely on the application type here, ## to avoid a loop between application_exe and application_type. local application_exe visionaire_name filename_candidates_list filename_candidate filename_path visionaire_name=$(visionaire_name) filename_candidates_list=" ${visionaire_name} ${visionaire_name}.exe" while read -r filename_candidate; do ## Skip empty lines. if [ -z "$filename_candidate" ]; then continue fi filename_path=$(application_exe_path "$filename_candidate") if [ -n "$filename_path" ]; then application_exe="$filename_candidate" break fi done <<- EOL $(printf '%s' "$filename_candidates_list") EOL printf '%s' "${application_exe:-}" } src/55_engine-variant_visionaire/30_content.sh0000644000000000000000000000705313120060140020313 0ustar rootroot# Visionaire - Print the Visionaire name for the current game # This function should not fail if no Visionaire name is set for the current game, # so it can be used to automatically detect games using the "visionaire" type variant. # USAGE: visionaire_name # RETURN: the Visionaire name, a string that can include spaces, # or an empty string if none is set visionaire_name() { context_value 'VISIONAIRE_NAME' } # Visionaire - Print the default path to include files from # USAGE: visionaire_content_path $content_id # RETURN: a list of paths relative to the path for the given identifier, # line breaks are used as separator between each item, # this list can include globbing patterns, # this list can be empty visionaire_content_path() { local content_id content_id="$1" local applications_list application application_type applications_list=$(applications_list) if [ -z "$applications_list" ]; then error_applications_list_empty fi application=$(printf '%s' "$applications_list" | head --lines=1) application_type=$(application_type "$application") local content_path content_path_default content_path_default=$(content_path_default) case "$content_id" in ('LIBS_BIN') case "$application_type" in ('native') content_path="${content_path_default}/libs64" ;; esac ;; ('DOC_DATA') case "$application_type" in ('native') content_path="${content_path_default}/documents" ;; ('wine') content_path="${content_path_default}/documents" ;; esac ;; esac printf '%s' "${content_path:-}" } # Visionaire - Print the list of files to include from the archive for a given identifier # USAGE: visionaire_content_files $content_id # RETURN: a list of paths relative to the path for the given identifier, # line breaks are used as separator between each item, # this list can include globbing patterns, # this list can be empty visionaire_content_files() { local content_id content_id="$1" local visionaire_name visionaire_name=$(visionaire_name) local applications_list application application_type applications_list=$(applications_list) if [ -z "$applications_list" ]; then error_applications_list_empty fi application=$(printf '%s' "$applications_list" | head --lines=1) application_type=$(application_type "$application") local content_files case "$content_id" in ('LIBS_BIN') case "$application_type" in ('native') content_files=' libavcodec.so.56 libavdevice.so.56 libavfilter.so.5 libavformat.so.56 libavutil.so.54 libswresample.so.1 libswscale.so.3' ;; esac ;; ('GAME_BIN') case "$application_type" in ('native') content_files=" config.ini $visionaire_name" ;; ('wine') content_files=" config.ini avcodec-*dll avformat-*.dll avutil-*.dll libsndfile-*.dll openal.dll openal32.dll sdl.dll sdl2.dll swresample-*.dll swscale-*.dll zlib1.dll ${visionaire_name}.exe" ;; esac ;; ('GAME_DATA') case "$application_type" in ('native') content_files=' characters lua scenes videos data.vis' ;; ('wine') content_files=' characters lua scenes videos data.vis banner.jpg folder.jpg languages.xml' ;; esac ;; ('DOC_DATA') case "$application_type" in ('native') content_files=' licenses' ;; ('wine') content_files=' licenses' ;; esac ;; esac printf '%s' "${content_files:-}" } src/55_engine-variant_visionaire/30_launchers.sh0000644000000000000000000000120013120060140020611 0ustar rootroot# Visionaire - Force the use of the system SDL library, for use with native games # USAGE: visionaire_tweak_sdl_native visionaire_tweak_sdl_native() { local path_libraries path_libraries=$(path_libraries_system) cat <<- EOF # Force the use of the system SDL library export SDL_DYNAMIC_API="${path_libraries}/libSDL2-2.0.so.0" EOF } # Visionaire - Prevent the use of wayland SDL video driver, for use with WINE games # USAGE: visionaire_tweak_sdl_wine visionaire_tweak_sdl_wine() { cat <<- 'EOF' # Prevent the use of wayland SDL video driver if [ "${SDL_VIDEODRIVER:-}" = 'wayland' ]; then unset SDL_VIDEODRIVER fi EOF } src/55_engine-variant_visionaire/30_packages.sh0000644000000000000000000000420213120060140020410 0ustar rootroot# Visionaire - Get the default list of packages to build # USAGE: visionaire_packages_list # RETURN: a list of packages identifiers, # separated by line breaks visionaire_packages_list() { printf '%s\n' \ 'PKG_BIN' \ 'PKG_DATA' } # Visionaire - Get the default package id for the given package # USAGE: visionaire_package_id $package # RETURN: a package id, # or an empty string if there is not default value for the given package visionaire_package_id() { local package package="$1" local package_id game_id game_id=$(game_id) case "$package" in ('PKG_DATA') package_id="${game_id}-data" ;; esac printf '%s' "${package_id:-}" } # Visionaire - Get the default package description for the given package # USAGE: visionaire_package_description $package # RETURN: a package description, # or an empty string if there is not default value for the given package visionaire_package_description() { local package package="$1" local package_description case "$package" in ('PKG_DATA') package_description='data' ;; esac printf '%s' "${package_description:-}" } # Visionaire - Get the list of dependencies on siblings for the given package # USAGE: visionaire_package_dependencies_siblings $package # RETURN: a list of package identifiers, # separated by line breaks visionaire_package_dependencies_siblings() { local package package="$1" local package_dependencies case "$package" in ('PKG_BIN') package_dependencies=' PKG_DATA' ;; esac printf '%s' "${package_dependencies:-}" | list_clean } # Visionaire - Get the list of native libraries dependencies for the given package # USAGE: visionaire_package_dependencies_native_libraries $package # RETURN: a list of native libraries, # separated by line breaks visionaire_package_dependencies_native_libraries() { local package package="$1" local package_dependencies case "$package" in ('PKG_BIN') package_dependencies=' libc.so.6 libdl.so.2 libgcc_s.so.1 libGL.so.1 libm.so.6 libopenal.so.1 libpthread.so.0 librt.so.1 libstdc++.so.6' ;; esac printf '%s' "${package_dependencies:-}" | list_clean } src/60_system_archlinux/10_instructions.sh0000644000000000000000000000137113120060140017636 0ustar rootroot# Arch Linux - Print installation instructions # USAGE: print_instructions_arch $package[…] print_instructions_arch() { local option_output_dir string_format option_output_dir=$(option_value 'output-dir') if printf '%s' "$option_output_dir" | grep --quiet --fixed-strings ' '; then string_format=' "%s"' else string_format=' %s' fi printf 'pacman -U' local package package_name package_output for package in "$@"; do package_name=$(package_name "$package") package_output=$(realpath "${option_output_dir}/${package_name}") ## Silence ShellCheck false-positive ## Don't use variables in the printf format string. Use printf "..%s.." "$foo". # shellcheck disable=SC2059 printf "$string_format" "$package_output" done printf '\n' } src/60_system_archlinux/10_packages.sh0000644000000000000000000001734013120060140016653 0ustar rootroot# Arch Linux - Write the metadata for the listed packages # USAGE: archlinux_packages_metadata $package[…] archlinux_packages_metadata() { local package for package in "$@"; do archlinux_package_metadata_single "$package" done } # Arch Linux - Write the metadata for the given package # USAGE: archlinux_package_metadata_single $package archlinux_package_metadata_single() { local package package="$1" local package_path target package_path=$(package_path "$package") target="${package_path}/.PKGINFO" mkdir --parents "$(dirname "$target")" local \ archlinux_field_pkgname \ archlinux_field_pkgver \ archlinux_field_packager \ archlinux_field_builddate \ archlinux_field_size \ archlinux_field_arch \ archlinux_field_pkgdesc \ archlinux_field_depend \ archlinux_field_provides archlinux_field_pkgname=$(archlinux_field_pkgname "$package") archlinux_field_pkgver=$(archlinux_field_pkgver "$package") archlinux_field_packager=$(archlinux_field_packager "$package") archlinux_field_builddate=$(archlinux_field_builddate "$package") archlinux_field_size=$(archlinux_field_size "$package") archlinux_field_arch=$(archlinux_field_arch "$package") archlinux_field_pkgdesc=$(archlinux_field_pkgdesc "$package") archlinux_field_depend=$(archlinux_field_depend "$package") archlinux_field_provides=$(archlinux_field_provides "$package") cat > "$target" <<- EOF # Generated by ./play.it $LIBRARY_VERSION pkgname = $archlinux_field_pkgname pkgver = $archlinux_field_pkgver packager = $archlinux_field_packager builddate = $archlinux_field_builddate size = $archlinux_field_size arch = $archlinux_field_arch pkgdesc = $archlinux_field_pkgdesc EOF if [ -n "$archlinux_field_depend" ]; then local field_depend_single while read -r field_depend_single; do cat >> "$target" <<- EOF depend = $field_depend_single EOF done <<- EOL $(printf '%s' "$archlinux_field_depend") EOL fi if [ -n "$archlinux_field_provides" ]; then local field_provides_single while read -r field_provides_single; do cat >> "$target" <<- EOF conflict = $field_provides_single provides = $field_provides_single EOF done <<- EOL $(printf '%s' "$archlinux_field_provides") EOL fi # Write the .INSTALL metadata file local install_contents install_contents=$(archlinux_script_install "$package") if [ -n "$install_contents" ]; then printf '%s' "$install_contents" > "${package_path}/.INSTALL" fi # Creates .MTREE local option_mtree option_mtree=$(option_value 'mtree') if [ "$option_mtree" -eq 1 ]; then package_archlinux_create_mtree "$package" fi } # Arch Linux - Build a list of packages # USAGE: archlinux_packages_build $package[…] archlinux_packages_build() { local package for package in "$@"; do archlinux_package_build_single "$package" done } # Arch Linux - Build a single package # USAGE: archlinux_package_build_single $package archlinux_package_build_single() { local package package="$1" local package_path package_path=$(package_path "$package") local option_output_dir package_name generated_package_path option_output_dir=$(option_value 'output-dir') package_name=$(package_name "$package") ## The path to the generated package must be an absolute path, ## because we do not run the tar call from the current directory. generated_package_path=$(realpath "${option_output_dir}/${package_name}") # Skip packages already existing, # unless called with --overwrite. local option_overwrite option_overwrite=$(option_value 'overwrite') if \ [ "$option_overwrite" -eq 0 ] \ && [ -e "$generated_package_path" ] then information_package_already_exists "$package_name" return 0 fi # Set basic tar options local tar_options tar_options='--create' if variable_is_empty 'PLAYIT_TAR_IMPLEMENTATION'; then guess_tar_implementation fi case "$PLAYIT_TAR_IMPLEMENTATION" in ('gnutar') tar_options="$tar_options --group=root --owner=root" ;; ('bsdtar') tar_options="$tar_options --gname=root --uname=root" ;; (*) error_unknown_tar_implementation return 1 ;; esac # Set compression setting local option_compression tar_compress_program option_compression=$(option_value 'compression') case "$option_compression" in ('none') tar_compress_program='' ;; ('speed') tar_compress_program='zstd --fast=1' ;; ('size') tar_compress_program='zstd -19' ;; esac # Run the actual package generation, using tar local package_generation_return_code information_package_building "$package_name" package_generation_return_code=$( cd "$package_path" local package_contents package_contents='.PKGINFO *' if [ -e '.INSTALL' ]; then package_contents=".INSTALL $package_contents" fi if [ -e '.MTREE' ]; then package_contents=".MTREE $package_contents" fi if [ -n "$tar_compress_program" ]; then debug_external_command "tar $tar_options --use-compress-program=\"$tar_compress_program\" --file \"$generated_package_path\" $package_contents" { tar $tar_options --use-compress-program="$tar_compress_program" --file "$generated_package_path" $package_contents package_generation_return_code=$? } || true else debug_external_command "tar $tar_options --file \"$generated_package_path\" $package_contents" { tar $tar_options --file "$generated_package_path" $package_contents package_generation_return_code=$? } || true fi printf '%s' "$package_generation_return_code" ) if [ $package_generation_return_code -ne 0 ]; then error_package_generation_failed "$package_name" return 1 fi } # creates .MTREE in package # USAGE: package_archlinux_create_mtree $pkg_path # RETURNS: nothing package_archlinux_create_mtree() { local package package="$1" local package_path package_path=$(package_path "$package") info_package_mtree_computation "$package" ( cd "$package_path" # shellcheck disable=SC2094 env --ignore-environment find . -print0 | \ env --ignore-environment bsdtar \ --create \ --file - \ --files-from - \ --format=mtree \ --no-recursion \ --null \ --options='!all,use-set,type,uid,gid,mode,time,size,md5,sha256,link' \ --exclude .MTREE \ | \ env --ignore-environment gzip \ --force \ --no-name \ --to-stdout \ > .MTREE ) } # Print the file name of the given package # USAGE: package_name_archlinux $package # RETURNS: the file name, as a string package_name_archlinux() { local package package="$1" local package_id package_version package_architecture package_name package_id=$(package_id "$package") package_version=$(package_version) package_architecture=$(archlinux_field_arch "$package") package_name="${package_id}_${package_version}_${package_architecture}.pkg.tar" local option_compression option_compression=$(option_value 'compression') case $option_compression in ('speed') package_name="${package_name}.zst" ;; ('size') package_name="${package_name}.zst" ;; esac printf '%s' "$package_name" } # Get the path to the directory where the given package is prepared, # relative to the directory where all packages are stored # USAGE: package_path_archlinux $package # RETURNS: relative path to a directory, as a string package_path_archlinux() { local package package="$1" local package_name package_path package_name=$(package_name "$package") package_path="${package_name%.pkg.tar*}" printf '%s' "$package_path" } # Tweak the given package id to follow Arch Linux standards # USAGE: archlinux_package_id $package_id # RETURNS: the package id, as a non-empty string archlinux_package_id() { local package_id package_id="$1" # Prepend "lib32-" to the ID of 32-bit packages. local package_architecture package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') package_id="lib32-${package_id}" ;; esac printf '%s' "$package_id" } src/60_system_archlinux/15_metadata-fields.sh0000644000000000000000000000720313120060140020123 0ustar rootroot# Arch Linux - Print the value of the "pkgname" field # USAGE: archlinux_field_pkgname $package # RETURN: the field value archlinux_field_pkgname() { local package package="$1" package_id "$package" } # Arch Linux - Print the value of the "pkgver" field # USAGE: archlinux_field_pkgver $package # RETURN: the field value archlinux_field_pkgver() { ## The package value is not actually used by this function, ## but it is still passed for consistency with other archlinux_field_* functions. local package package="$1" package_version } # Arch Linux - Print the value of the "packager" field # USAGE: archlinux_field_packager $package # RETURN: the field value archlinux_field_packager() { ## The package value is not actually used by this function, ## but it is still passed for consistency with other archlinux_field_* functions. local package package="$1" package_maintainer } # Arch Linux - Print the value of the "builddate" field # USAGE: archlinux_field_builddate $package # RETURN: the field value archlinux_field_builddate() { ## The package value is not actually used by this function, ## but it is still passed for consistency with other archlinux_field_* functions. local package package="$1" date '+%s' } # Arch Linux - Print the value of the "size" field # USAGE: archlinux_field_size $package # RETURN: the field value archlinux_field_size() { local package package="$1" local package_path package_path=$(package_path "$package") du --total --block-size=1 --summarize "$package_path" | \ tail --lines=1 | \ cut --fields=1 } # Arch Linux - Print the value of the "arch" field # USAGE: archlinux_field_arch $package # RETURN: the field value archlinux_field_arch() { local package package="$1" local package_architecture package_architecture_string package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32'|'64') package_architecture_string='x86_64' ;; ('all') package_architecture_string='any' ;; esac printf '%s' "$package_architecture_string" } # Arch Linux - Print the value of the "pkgdesc" field # USAGE: archlinux_field_pkgdesc $package # RETURN: the field value archlinux_field_pkgdesc() { local package package="$1" local game_name package_description script_version_string game_name=$(game_name) package_description=$(package_description "$package") script_version_string=$(script_version) printf '%s' "$game_name" if [ -n "$package_description" ]; then printf -- ' - %s' "$package_description" fi printf -- ' - ./play.it script version %s' "$script_version_string" } # Arch Linux - Print the value of the "depend" fields # Each package name is displayed on its own line. # USAGE: archlinux_field_depend $package # RETURN: the field value archlinux_field_depend() { local package package="$1" dependencies_archlinux_full_list "$package" } # Arch Linux - Print the value of the "provides" fields # Each package name is displayed on its own line. # These values are used for the "conflict" fields too. # USAGE: archlinux_field_provides $package # RETURN: the field value archlinux_field_provides() { local package package="$1" local package_provides package_provides=$(package_provides "$package") local package_architecture package_architecture=$(package_architecture "$package") if [ "$package_architecture" = '32' ]; then local package_id package_name_32 package_id=$(package_id "$package") package_name_32=$(printf '%s' "$package_id" | sed 's/^lib32-//') package_provides="$package_provides $package_name_32" fi # Return early if there is no package name provided if [ -z "$package_provides" ]; then return 0 fi printf '%s\n' $package_provides } src/60_system_archlinux/15_package-scripts.sh0000644000000000000000000000241113120060140020153 0ustar rootroot# Arch Linux - Print the contents of the .INSTALL script # USAGE: archlinux_script_install $package # RETURN: the contents of the .INSTALL file, # spanning over several lines archlinux_script_install() { local package package="$1" # Print the definitions of post_install and post_upgrade. local postinst_actions postinst_warnings postinst_actions=$(package_postinst_actions "$package") postinst_warnings=$(package_postinst_warnings "$package") if [ -n "$postinst_actions" ] || [ -n "$postinst_warnings" ]; then cat <<- EOF post_install() { EOF ## Include actions that should be run. if [ -n "$postinst_actions" ]; then printf '%s\n' "$postinst_actions" fi ## Include warnings that should be displayed. if [ -n "$postinst_warnings" ]; then local warning_line while read -r warning_line; do printf 'printf "Warning: %%s\\n" "%s"\n' "$warning_line" done <<- EOL $(printf '%s' "$postinst_warnings") EOL fi cat <<- EOF } post_upgrade() { post_install } EOF fi # Print the definitions of pre_remove and pre_upgrade. local prerm_actions prerm_actions=$(package_prerm_actions "$package") if [ -n "$prerm_actions" ]; then cat <<- EOF pre_remove() { $prerm_actions } pre_upgrade() { pre_remove } EOF fi } src/60_system_archlinux/20_dependencies.sh0000644000000000000000000001355213120060140017525 0ustar rootroot# Arch Linux - Set list of generic dependencies (32-bit) # USAGE: pkg_set_deps_arch32 $dep[…] pkg_set_deps_arch32() { for dep in "$@"; do case $dep in ('alsa') pkg_dep=' lib32-alsa-lib lib32-alsa-plugins' ;; ('freetype') pkg_dep='lib32-freetype2' ;; ('gcc32') pkg_dep=' gcc-multilib lib32-gcc-libs' ;; ('glibc') pkg_dep='lib32-glibc' ;; ('glu') pkg_dep='lib32-glu' ;; ('glx') pkg_dep='lib32-libgl' ;; ('gtk2') pkg_dep='lib32-gtk2' ;; ('json') pkg_dep='lib32-json-c' ;; ('libstdc++') pkg_dep='lib32-gcc-libs' ;; ('libudev1') pkg_dep='lib32-systemd' ;; ('libxrandr') pkg_dep='lib32-libxrandr' ;; ('nss') pkg_dep='lib32-nss' ;; ('openal') pkg_dep='lib32-openal' ;; ('sdl2') pkg_dep='lib32-sdl2' ;; ('xcursor') pkg_dep='lib32-libxcursor' ;; ( \ 'dosbox' | \ 'java' | \ 'mono' | \ 'pulseaudio' | \ 'scummvm' | \ 'wine' | \ 'winetricks' | \ 'xgamma' | \ 'xrandr' \ ) pkg_dep=$(archlinux_dependencies_single_command "$dep") ;; ( \ 'libgdk_pixbuf-2.0.so.0' | \ 'libc.so.6' | \ 'libglib-2.0.so.0' | \ 'libgobject-2.0.so.0' | \ 'libGLU.so.1' | \ 'libGL.so.1' | \ 'libgdk-x11-2.0.so.0' | \ 'libgtk-x11-2.0.so.0' | \ 'libasound.so.2' | \ 'libasound_module_'*'.so' | \ 'libmbedtls.so.12' | \ 'libpng16.so.16' | \ 'libpulse.so.0' | \ 'libpulse-simple.so.0' | \ 'libstdc++.so.6' | \ 'libudev.so.1' | \ 'libX11.so.6' | \ 'libopenal.so.1' | \ 'libSDL-1.2.so.0' | \ 'libSDL2-2.0.so.0' | \ 'libturbojpeg.so.0' | \ 'libuv.so.1' | \ 'libvorbisfile.so.3' | \ 'libz.so.1' \ ) pkg_dep=$(dependency_package_providing_library_arch32 "$dep") ;; (*) pkg_dep="$dep" ;; esac if variable_is_empty 'pkg_deps'; then pkg_deps="$pkg_dep" else pkg_deps="$pkg_deps $pkg_dep" fi done } # Arch Linux - Set list of generic dependencies (64-bit) # set list or Arch Linux 64-bit dependencies from generic names # USAGE: pkg_set_deps_arch64 $dep[…] pkg_set_deps_arch64() { for dep in "$@"; do case $dep in ('alsa') pkg_dep=' alsa-lib alsa-plugins' ;; ('freetype') pkg_dep='freetype2' ;; ('gcc32') pkg_dep=' gcc-multilib lib32-gcc-libs' ;; ('glibc') pkg_dep='glibc' ;; ('glu') pkg_dep='glu' ;; ('glx') pkg_dep='libgl' ;; ('gtk2') pkg_dep='gtk2' ;; ('json') pkg_dep='json-c' ;; ('libstdc++') pkg_dep='gcc-libs' ;; ('libudev1') pkg_dep='libudev.so=1-64' ;; ('libxrandr') pkg_dep='libxrandr' ;; ('nss') pkg_dep='nss' ;; ('openal') pkg_dep='openal' ;; ('sdl2') pkg_dep='sdl2' ;; ('xcursor') pkg_dep='libxcursor' ;; ( \ 'dosbox' | \ 'java' | \ 'mono' | \ 'pulseaudio' | \ 'scummvm' | \ 'wine' | \ 'winetricks' | \ 'xgamma' | \ 'xrandr' \ ) pkg_dep=$(archlinux_dependencies_single_command "$dep") ;; ( \ 'libgdk_pixbuf-2.0.so.0' | \ 'libc.so.6' | \ 'libglib-2.0.so.0' | \ 'libgobject-2.0.so.0' | \ 'libGLU.so.1' | \ 'libGL.so.1' | \ 'libgdk-x11-2.0.so.0' | \ 'libgtk-x11-2.0.so.0' | \ 'libasound.so.2' | \ 'libasound_module_'*'.so' | \ 'libmbedtls.so.12' | \ 'libpng16.so.16' | \ 'libpulse.so.0' | \ 'libpulse-simple.so.0' | \ 'libstdc++.so.6' | \ 'libudev.so.1' | \ 'libX11.so.6' | \ 'libopenal.so.1' | \ 'libSDL-1.2.so.0' | \ 'libSDL2-2.0.so.0' | \ 'libturbojpeg.so.0' | \ 'libuv.so.1' | \ 'libvorbisfile.so.3' | \ 'libz.so.1' \ ) pkg_dep=$(dependency_package_providing_library_arch "$dep") ;; (*) pkg_dep="$dep" ;; esac if variable_is_empty 'pkg_deps'; then pkg_deps="$pkg_dep" else pkg_deps="$pkg_deps $pkg_dep" fi done } # Arch Linux - List all dependencies for the given package # USAGE: dependencies_archlinux_full_list $package # RETURN: print a list of dependency strings, # one per line dependencies_archlinux_full_list() { local package package="$1" local packages_list packages_list_full packages_list_full='' # Include generic dependencies local package_architecture generic_dependencies_command package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') generic_dependencies_command='pkg_set_deps_arch32' ;; (*) generic_dependencies_command='pkg_set_deps_arch64' ;; esac local dependencies_generic dependency_generic pkg_deps dependencies_generic=$(dependencies_list_generic "$package") while read -r dependency_generic; do # pkg_set_deps_arch sets a variable $pkg_deps instead of printing a value, # we prevent it from leaking by setting it to an empty value. pkg_deps='' "$generic_dependencies_command" $dependency_generic packages_list_full="$packages_list_full $pkg_deps" done <<- EOL $(printf '%s' "$dependencies_generic") EOL # Include dependencies on sibling package packages_list=$(archlinux_dependencies_siblings "$package") packages_list_full="$packages_list_full $packages_list" # Include dependencies on commands packages_list=$(archlinux_dependencies_all_commands "$package") packages_list_full="$packages_list_full $packages_list" # Include dependencies on native libraries packages_list=$(dependencies_list_native_libraries_packages "$package") packages_list_full="$packages_list_full $packages_list" # Include dependencies on Mono libraries packages_list=$(dependencies_list_mono_libraries_packages "$package") packages_list_full="$packages_list_full $packages_list" # Include dependencies on GStreamer plugins packages_list=$(archlinux_dependencies_gstreamer_all_formats "$package") packages_list_full="$packages_list_full $packages_list" printf '%s' "$packages_list_full" | list_clean } src/60_system_archlinux/25_dependencies_commands.sh0000644000000000000000000000423513120060140021411 0ustar rootroot# Arch Linux - Print the package names providing the commands required by the given package # USAGE: archlinux_dependencies_all_commands $package # RETURN: a list of Arch Linux package names, # one per line archlinux_dependencies_all_commands() { local package package="$1" local required_commands required_commands=$(dependencies_list_commands "$package") # Return early if the current package does not require any command if [ -z "$required_commands" ]; then return 0 fi local command packages_list required_packages packages_list='' while read -r command; do required_packages=$(archlinux_dependencies_single_command "$command") packages_list="$packages_list $required_packages" done <<- EOL $(printf '%s' "$required_commands") EOL printf '%s' "$packages_list" | list_clean } # Arch Linux - Print the package names providing the required command # USAGE: archlinux_dependencies_single_command $required_command # RETURN: a list of Arch Linux package names, # one per line archlinux_dependencies_single_command() { local required_command required_command="$1" local package_names case "$required_command" in ('corsix-th') package_names=' corsix-th' ;; ('dos2unix') package_names=' dos2unix' ;; ('dosbox') package_names=' dosbox' ;; ('java') package_names=' jre8-openjdk' ;; ('mono') package_names=' mono' ;; ('mpv') package_names=' mpv' ;; ('openmw-iniimporter') package_names=' openmw' ;; ('openmw-launcher') package_names=' openmw' ;; ('pulseaudio') package_names=' pulseaudio' ;; ('renpy') package_names=' renpy' ;; ('scummvm') package_names=' scummvm' ;; ('sed') package_names=' sed' ;; ('setxkbmap') package_names=' xorg-setxkbmap' ;; ('vcmilauncher') package_names=' vcmi' ;; ('wine') package_names=' wine' ;; ('winetricks') package_names=' winetricks xterm' ;; ('xgamma') package_names=' xorg-xgamma' ;; ('xrandr') package_names=' xorg-xrandr' ;; (*) dependencies_unknown_command_add "$required_command" return 0 ;; esac printf '%s' "$package_names" } src/60_system_archlinux/25_dependencies_gstreamer-plugins.sh0000644000000000000000000001035513120060140023260 0ustar rootroot# Arch Linux - Print the package names providing the GStreamer plugins to decode the formats required by the given package # USAGE: archlinux_dependencies_gstreamer_all_formats $package # RETURN: a list of Arch Linux package names, # one per line archlinux_dependencies_gstreamer_all_formats() { local package package="$1" local gstreamer_decoders gstreamer_decoders=$(dependencies_list_gstreamer_decoders "$package") # Return early if the current package does not require any GStreamer decoders if [ -z "$gstreamer_decoders" ]; then return 0 fi local package_architecture command_dependencies_for_single_format package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') command_dependencies_for_single_format='archlinux_dependencies_gstreamer_single_format_32bit' ;; (*) command_dependencies_for_single_format='archlinux_dependencies_gstreamer_single_format' ;; esac local media_format packages_list required_packages packages_list='' while read -r media_format; do required_packages=$("$command_dependencies_for_single_format" "$media_format") packages_list="$packages_list $required_packages" done <<- EOL $(printf '%s' "$gstreamer_decoders") EOL printf '%s' "$packages_list" | list_clean } # Arch Linux - Print the package names providing the required GStreamer plugins to decode the given format # USAGE: archlinux_dependency_providing_gstreamer_plugin $media_format # RETURN: a list of Arch Linux package names, # one per line archlinux_dependencies_gstreamer_single_format() { local media_format media_format="$1" local package_names case "$media_format" in ('audioconvert') package_names=' gst-plugins-base' ;; ('avidemux') package_names=' gst-plugins-good' ;; ('decodebin') package_names=' gst-plugins-base' ;; ('deinterlace') package_names=' gst-plugins-good' ;; ('application/x-id3') package_names=' gst-plugins-good' ;; ('audio/mpeg, mpegversion=(int)1, layer=(int)3') package_names=' gst-plugins-good' ;; ('audio/x-wma, wmaversion=(int)1') package_names=' gst-libav' ;; ('video/mpeg, systemstream=(boolean)true, mpegversion=(int)1') package_names=' gst-plugins-ugly gst-plugins-bad' ;; ('video/quicktime, variant=(string)iso') package_names=' gst-plugins-good gst-libav' ;; ('video/x-ms-asf') package_names=' gst-plugins-ugly x-ms-asf' ;; ('video/x-msvideo') package_names=' gst-plugins-good gst-libav' ;; ('video/x-wmv, wmvversion=(int)1') package_names=' gst-libav' ;; (*) dependencies_unknown_gstreamer_media_formats_add "$media_format" return 0 ;; esac printf '%s' "$package_names" } # Arch Linux - Print the package names providing the required GStreamer plugins to decode the given format (32-bit) # USAGE: archlinux_dependency_providing_gstreamer_plugin_32bit $media_format # RETURN: a list of Arch Linux package names, # one per line archlinux_dependencies_gstreamer_single_format_32bit() { local media_format media_format="$1" local package_names case "$media_format" in ('audioconvert') package_names=' lib32-gst-plugins-base' ;; ('avidemux') package_names=' lib32-gst-plugins-good' ;; ('decodebin') package_names=' lib32-gst-plugins-base' ;; ('deinterlace') package_names=' lib32-gst-plugins-good' ;; ('application/x-id3') package_names=' lib32-gst-plugins-good' ;; ('audio/mpeg, mpegversion=(int)1, layer=(int)3') package_names=' lib32-gst-plugins-good' ;; ('audio/x-wma, wmaversion=(int)1') package_names=' lib32-gst-libav' ;; ('video/mpeg, systemstream=(boolean)true, mpegversion=(int)1') package_names=' lib32-gst-plugins-ugly lib32-gst-plugins-bad' ;; ('video/quicktime, variant=(string)iso') package_names=' lib32-gst-plugins-good lib32-gst-libav' ;; ('video/x-ms-asf') package_names=' lib32-gst-plugins-ugly lib32-gst-libav' ;; ('video/x-msvideo') package_names=' lib32-gst-plugins-good lib32-gst-libav' ;; ('video/x-wmv, wmvversion=(int)1') package_names=' lib32-gst-libav' ;; (*) dependencies_unknown_gstreamer_media_formats_add "$media_format" return 0 ;; esac printf '%s' "$package_names" } src/60_system_archlinux/25_dependencies_native-libraries.sh0000644000000000000000000005331413120060140023052 0ustar rootroot# Arch Linux - Print the package names providing the given native libraries # USAGE: archlinux_dependencies_providing_native_libraries $library[…] # RETURN: a list of Arch Linux package names, # one per line archlinux_dependencies_providing_native_libraries() { local library packages_list package packages_list='' for library in "$@"; do package=$(dependency_package_providing_library_arch "$library") packages_list="$packages_list $package" done printf '%s' "$packages_list" | list_clean } # Arch Linux - Print the package names providing the given native libraries in a 32-bit build # USAGE: archlinux_dependencies_providing_native_libraries_32bit $library[…] # RETURN: a list of Arch Linux package names, # one per line archlinux_dependencies_providing_native_libraries_32bit() { local library packages_list package packages_list='' for library in "$@"; do package=$(dependency_package_providing_library_arch32 "$library") packages_list="$packages_list $package" done printf '%s' "$packages_list" | list_clean } # Arch Linux - Print the package name providing the given native library # USAGE: dependency_package_providing_library_arch $library dependency_package_providing_library_arch() { local library package_name library="$1" case "$library" in ('ld-linux.so.2') package_name='glibc' ;; ('ld-linux-x86-64.so.2') package_name='glibc' ;; ('liballeg.so.4.4') package_name='allegro4' ;; ('liballegro.so.5.2') package_name='allegro' ;; ('liballegro_acodec.so.5.2') package_name='allegro' ;; ('liballegro_audio.so.5.2') package_name='allegro' ;; ('liballegro_font.so.5.2') package_name='allegro' ;; ('liballegro_image.so.5.2') package_name='allegro' ;; ('liballegro_primitives.so.5.2') package_name='allegro' ;; ('liballegro_ttf.so.5.2') package_name='allegro' ;; ('libalut.so.0') package_name='freealut' ;; ('libasound.so.2') package_name='alsa-lib' ;; ('libasound_module_'*'.so') package_name='alsa-plugins' ;; ('libatspi.so.0') package_name='at-spi2-core' ;; ('libatk-1.0.so.0') package_name='atk' ;; ('libaudio.so.2') package_name='nas' ;; ('libboost_locale.so.1.74.0') # This library is not provided for Arch Linux unset package_name ;; ('libbz2.so.1.0'|'libbz2.so.1') package_name='bzip2' ;; ('libc.so.6') package_name='glibc' ;; ('libc++.so.1') package_name='libc++' ;; ('libc++abi.so.1') package_name='libc++abi' ;; ('libcairo.so.2') package_name='cairo' ;; ('libCg.so') package_name='nvidia-cg-toolkit' ;; ('libCgGL.so') package_name='nvidia-cg-toolkit' ;; ('libcom_err.so.2') package_name='e2fsprogs' ;; ('libcrypt.so.1') package_name='libxcrypt' ;; ('libcups.so.2') package_name='libcups' ;; ('libcurl.so.4') package_name='curl' ;; ('libcurl.so.4+CURL_OPENSSL_3') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/curl/ return 0 ;; ('libcurl-gnutls.so.4') package_name='libcurl-gnutls' ;; ('libdbus-1.so.3') package_name='dbus' ;; ('libdbus-glib-1.so.2') package_name='dbus-glib' ;; ('libdl.so.2') package_name='glibc' ;; ('libEGL.so.1') package_name='libglvnd' ;; ('libexpat.so.1') package_name='expat' ;; ('libFAudio.so.0') package_name='faudio' ;; ('libFLAC.so.8') package_name='flac1.3' ;; ('libfontconfig.so.1') package_name='fontconfig' ;; ('libfreeimage.so.3') package_name='freeimage' ;; ('libfreetype.so.6') package_name='freetype2' ;; ('libfribidi.so.0') package_name='fribidi' ;; ('libgcc_s.so.1') package_name='gcc-libs' ;; ('libgconf-2.so.4') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/gconf/ return 0 ;; ('libgcrypt.so.11') package_name='libgcrypt15' ;; ('libgdiplus.so') package_name='libgdiplus' ;; ('libgdk-3.so.0') package_name='gtk3' ;; ('libgdk_pixbuf-2.0.so.0') package_name='gdk-pixbuf2' ;; ('libgdk-x11-2.0.so.0') package_name='gtk2' ;; ('libgio-2.0.so.0') package_name='glib2' ;; ('libGL.so.1') package_name='libgl' ;; ('libGLEW.so.2.2') package_name='glew' ;; ('libglfw.so.3') package_name='glfw' ;; ('libglib-2.0.so.0') package_name='glib2' ;; ('libGLU.so.1') package_name='glu' ;; ('libGLX.so.0') package_name='libglvnd' ;; ('libgmodule-2.0.so.0') package_name='glib2' ;; ('libgobject-2.0.so.0') package_name='glib2' ;; ('libgomp.so.1') package_name='gcc-libs' ;; ('libgpg-error.so.0') package_name='libgpg-error' ;; ('libgssapi_krb5.so.2') package_name='krb5' ;; ('libgthread-2.0.so.0') package_name='glib2' ;; ('libgtk-x11-2.0.so.0') package_name='gtk2' ;; ('libgtk-3.so.0') package_name='gtk3' ;; ('libICE.so.6') package_name='libice' ;; ('libidn.so.11') package_name='libidn11' ;; ('libidn2.so.0') package_name='libidn2' ;; ('libIL.so.1') package_name='devil' ;; ('libjpeg.so.62') package_name='libjpeg6-turbo' ;; ('libk5crypto.so.3') package_name='krb5' ;; ('libkrb5.so.3') package_name='krb5' ;; ('liblcms2.so.2') package_name='lcms2' ;; ('liblua5.3.so.0') package_name='lua' ;; ('libluajit-5.1.so.2') package_name='luajit' ;; ('liblz4.so.1') package_name='lz4' ;; ('libm.so.6') package_name='glibc' ;; ('libmbedtls.so.12') package_name='mbedtls' ;; ('libminiupnpc.so.17') package_name='miniupnpc' ;; ('libminizip.so.1') package_name='minizip' ;; ('libmodplug.so.1') package_name='libmodplug' ;; ('libmpg123.so.0') package_name='mpg123' ;; ('libnghttp2.so.14') package_name='libnghttp2' ;; ('libnotify.so.4') package_name='libnotify' ;; ('libnspr4.so') package_name='nspr' ;; ('libnss3.so') package_name='nss' ;; ('libnssutil3.so') package_name='nss' ;; ('libogg.so.0') package_name='libogg' ;; ('libopenal.so.1') package_name='openal' ;; ('libOpenGL.so.0') package_name='libglvnd' ;; ('libopenmpt.so.0') package_name='libopenmpt' ;; ('libpango-1.0.so.0') package_name='pango' ;; ('libpangocairo-1.0.so.0') package_name='pango' ;; ('libpangoft2-1.0.so.0') package_name='pango' ;; ('libpcre.so.3') # This library is not provided for Arch Linux unset package_name ;; ('libphysfs.so.1') package_name='physfs' ;; ('libpixman-1.so.0') package_name='pixman' ;; ('libplc4.so') package_name='nspr' ;; ('libplds4.so') package_name='nspr' ;; ('libpng12.so.0') package_name='libpng12' ;; ('libpng16.so.16') package_name='libpng' ;; ('libpsl.so.5') package_name='libpsl' ;; ('libpthread.so.0') package_name='glibc' ;; ('libpulse.so.0') package_name='libpulse' ;; ('libpulse-simple.so.0') package_name='libpulse' ;; ('libresolv.so.2') package_name='glibc' ;; ('librt.so.1') package_name='glibc' ;; ('librtmp.so.1') package_name='rtmpdump' ;; ('libSDL-1.2.so.0') package_name='sdl' ;; ('libSDL_image-1.2.so.0') package_name='sdl_image' ;; ('libSDL_kitchensink.so.1') # This library is not provided for Arch Linux unset package_name ;; ('libSDL_mixer-1.2.so.0') package_name='sdl_mixer' ;; ('libSDL_sound-1.0.so.1') package_name='sdl_sound' ;; ('libSDL_ttf-2.0.so.0') package_name='sdl_ttf' ;; ('libSDL2-2.0.so.0') package_name='sdl2' ;; ('libSDL2_image-2.0.so.0') package_name='sdl2_image' ;; ('libSDL2_mixer-2.0.so.0') package_name='sdl2_mixer' ;; ('libSDL2_ttf-2.0.so.0') package_name='sdl2_ttf' ;; ('libsecret-1.so.0') package_name='libsecret' ;; ('libsigc-2.0.so.0') package_name='libsigc++' ;; ('libSM.so.6') package_name='libsm' ;; ('libsmime3.so') package_name='nss' ;; ('libsmpeg-0.4.so.0') package_name='smpeg' ;; ('libsodium.so.23') package_name='libsodium' ;; ('libssh2.so.1') package_name='libssh2' ;; ('libssl.so.1.0.0') package_name='openssl-1.0' ;; ('libssl.so.1.1') package_name='openssl-1.1' ;; ('libssl3.so') package_name='nss' ;; ('libstdc++.so.5') package_name='libstdc++5' ;; ('libstdc++.so.6') package_name='gcc-libs' ;; ('libtcmalloc_minimal.so.4') package_name='gperftools' ;; ('libtheora.so.0') package_name='libtheora' ;; ('libtheoradec.so.1') package_name='libtheora' ;; ('libtheoraenc.so.1') package_name='libtheora' ;; ('libthread_db.so.1') package_name='glibc' ;; ('libtiff.so.6') package_name='libtiff' ;; ('libturbojpeg.so.0') package_name='libjpeg-turbo' ;; ('libudev.so.0') package_name='libudev0-shim' ;; ('libudev.so.1') package_name='libudev.so=1-64' ;; ('libutil.so.1') package_name='glibc' ;; ('libuuid.so.1') package_name='util-linux-libs' ;; ('libuv.so.1') package_name='libuv' ;; ('libvorbis.so.0') package_name='libvorbis' ;; ('libvorbisenc.so.2') package_name='libvorbis' ;; ('libvorbisfile.so.3') package_name='libvorbis' ;; ('libvulkan.so.1') package_name=' vulkan-icd-loader vulkan-driver' ;; ('libwayland-client.so.0') package_name='wayland' ;; ('libX11.so.6') package_name='libx11' ;; ('libX11-xcb.so.1') package_name='libx11' ;; ('libxcb.so.1') package_name='libxcb' ;; ('libxcb-randr.so.0') package_name='libxcb' ;; ('libXcomposite.so.1') package_name='libxcomposite' ;; ('libXcursor.so.1') package_name='libxcursor' ;; ('libXdamage.so.1') package_name='libxdamage' ;; ('libXext.so.6') package_name='libxext' ;; ('libXfixes.so.3') package_name='libxfixes' ;; ('libXft.so.2') package_name='libxft' ;; ('libXi.so.6') package_name='libxi' ;; ('libXinerama.so.1') package_name='libxinerama' ;; ('libxml2.so.2') package_name='libxml2' ;; ('libxmp.so.4') package_name='libxmp' ;; ('libXmu.so.6') package_name='libxmu' ;; ('libXrandr.so.2') package_name='libxrandr' ;; ('libXrender.so.1') package_name='libxrender' ;; ('libxslt.so.1') package_name='libxslt' ;; ('libXss.so.1') package_name='libxss' ;; ('libXt.so.6') package_name='libxt' ;; ('libXtst.so.6') package_name='libxtst' ;; ('libXxf86vm.so.1') package_name='libxxf86vm' ;; ('libyaml-0.so.2') package_name='libyaml' ;; ('libz.so.1') package_name='zlib' ;; esac if [ -n "${package_name:-}" ]; then printf '%s' "$package_name" return 0 fi dependencies_unknown_libraries_add "$library" } # Arch Linux - Print the package name providing the given native library in a 32-bit build # USAGE: dependency_package_providing_library_arch32 $library dependency_package_providing_library_arch32() { local library package_name library="$1" case "$library" in ('ld-linux.so.2') package_name='lib32-glibc' ;; ('ld-linux-x86-64.so.2') package_name='lib32-glibc' ;; ('liballeg.so.4.4') package_name='lib32-allegro4' ;; ('liballegro.so.5.2') package_name='lib32-allegro' ;; ('liballegro_acodec.so.5.2') package_name='lib32-allegro' ;; ('liballegro_audio.so.5.2') package_name='lib32-allegro' ;; ('liballegro_font.so.5.2') package_name='lib32-allegro' ;; ('liballegro_image.so.5.2') package_name='lib32-allegro' ;; ('liballegro_primitives.so.5.2') package_name='lib32-allegro' ;; ('liballegro_ttf.so.5.2') package_name='lib32-allegro' ;; ('libalut.so.0') package_name='lib32-freealut' ;; ('libasound.so.2') package_name='lib32-alsa-lib' ;; ('libasound_module_'*'.so') package_name='lib32-alsa-plugins' ;; ('libatspi.so.0') package_name='lib32-at-spi2-core' ;; ('libatk-1.0.so.0') package_name='lib32-atk' ;; ('libaudio.so.2') # This library is not provided in a 32-bit build for Arch Linux unset package_name ;; ('libboost_locale.so.1.74.0') # This library is not provided for Arch Linux unset package_name ;; ('libbz2.so.1.0'|'libbz2.so.1') package_name='lib32-bzip2' ;; ('libc.so.6') package_name='lib32-glibc' ;; ('libc++.so.1') # This library is not provided in a 32-bit build for Arch Linux unset package_name ;; ('libc++abi.so.1') # This library is not provided in a 32-bit build for Arch Linux unset package_name ;; ('libcairo.so.2') package_name='lib32-cairo' ;; ('libCg.so') package_name='lib32-nvidia-cg-toolkit' ;; ('libCgGL.so') package_name='lib32-nvidia-cg-toolkit' ;; ('libcom_err.so.2') package_name='lib32-e2fsprogs' ;; ('libcrypt.so.1') package_name='lib32-libxcrypt' ;; ('libcups.so.2') package_name='lib32-libcups' ;; ('libcurl.so.4') package_name='lib32-curl' ;; ('libcurl.so.4+CURL_OPENSSL_3') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/curl/ return 0 ;; ('libcurl-gnutls.so.4') package_name='lib32-libcurl-gnutls' ;; ('libdbus-1.so.3') package_name='lib32-dbus' ;; ('libdbus-glib-1.so.2') package_name='lib32-dbus-glib' ;; ('libdl.so.2') package_name='lib32-glibc' ;; ('libEGL.so.1') package_name='lib32-libglvnd' ;; ('libexpat.so.1') package_name='lib32-expat' ;; ('libFAudio.so.0') package_name='lib32-faudio' ;; ('libFLAC.so.8') package_name='lib32-flac1.3' ;; ('libfontconfig.so.1') package_name='lib32-fontconfig' ;; ('libfreeimage.so.3') package_name='lib32-freeimage' ;; ('libfreetype.so.6') package_name='lib32-freetype2' ;; ('libfribidi.so.0') package_name='lib32-fribidi' ;; ('libgcc_s.so.1') package_name='lib32-gcc-libs' ;; ('libgconf-2.so.4') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/gconf/ return 0 ;; ('libgcrypt.so.11') package_name='lib32-libgcrypt15' ;; ('libgdiplus.so') # This library is not provided in a 32-bit build for Arch Linux unset package_name ;; ('libgdk-3.so.0') package_name='lib32-gtk3' ;; ('libgdk_pixbuf-2.0.so.0') package_name='lib32-gdk-pixbuf2' ;; ('libgdk-x11-2.0.so.0') package_name='lib32-gtk2' ;; ('libgio-2.0.so.0') package_name='lib32-glib2' ;; ('libGL.so.1') package_name='lib32-libgl' ;; ('libGLEW.so.2.2') package_name='lib32-glew' ;; ('libglfw.so.3') package_name='lib32-glfw' ;; ('libglib-2.0.so.0') package_name='lib32-glib2' ;; ('libGLU.so.1') package_name='lib32-glu' ;; ('libGLX.so.0') package_name='lib32-libglvnd' ;; ('libgmodule-2.0.so.0') package_name='lib32-glib2' ;; ('libgobject-2.0.so.0') package_name='lib32-glib2' ;; ('libgomp.so.1') package_name='lib32-gcc-libs' ;; ('libgpg-error.so.0') package_name='lib32-libgpg-error' ;; ('libgssapi_krb5.so.2') package_name='lib32-krb5' ;; ('libgthread-2.0.so.0') package_name='lib32-glib2' ;; ('libgtk-x11-2.0.so.0') package_name='lib32-gtk2' ;; ('libgtk-3.so.0') package_name='lib32-gtk3' ;; ('libICE.so.6') package_name='lib32-libice' ;; ('libidn.so.11') package_name='lib32-libidn11' ;; ('libidn2.so.0') package_name='lib32-libidn2' ;; ('libIL.so.1') # This library is not provided in a 32-bit build for Arch Linux unset package_name ;; ('libjpeg.so.62') package_name='lib32-libjpeg6-turbo' ;; ('libk5crypto.so.3') package_name='lib32-krb5' ;; ('libkrb5.so.3') package_name='lib32-krb5' ;; ('liblcms2.so.2') package_name='lib32-lcms2' ;; ('liblua5.3.so.0') package_name='lib32-lua' ;; ('libluajit-5.1.so.2') package_name='lib32-luajit' ;; ('liblz4.so.1') package_name='lib32-lz4' ;; ('libm.so.6') package_name='lib32-glibc' ;; ('libmbedtls.so.12') # This library is not provided in a 32-bit build for Arch Linux unset package_name ;; ('libminiupnpc.so.17') # This library is not provided in a 32-bit build for Arch Linux unset package_name ;; ('libminizip.so.1') package_name='lib32-minizip' ;; ('libmodplug.so.1') package_name='lib32-libmodplug' ;; ('libmpg123.so.0') package_name='lib32-mpg123' ;; ('libnghttp2.so.14') package_name='lib32-libnghttp2' ;; ('libnotify.so.4') # This library is not provided in a 32-bit build for Arch Linux unset package_name ;; ('libnspr4.so') package_name='lib32-nspr' ;; ('libnss3.so') package_name='lib32-nss' ;; ('libnssutil3.so') package_name='lib32-nss' ;; ('libogg.so.0') package_name='lib32-libogg' ;; ('libopenal.so.1') package_name='lib32-openal' ;; ('libOpenGL.so.0') package_name='lib32-libglvnd' ;; ('libopenmpt.so.0') # This library is not provided in a 32-bit build for Arch Linux unset package_name ;; ('libpango-1.0.so.0') package_name='lib32-pango' ;; ('libpangocairo-1.0.so.0') package_name='lib32-pango' ;; ('libpangoft2-1.0.so.0') package_name='lib32-pango' ;; ('libpcre.so.3') # This library is not provided for Arch Linux unset package_name ;; ('libphysfs.so.1') package_name='lib32-physfs' ;; ('libpixman-1.so.0') package_name='lib32-pixman' ;; ('libplc4.so') package_name='lib32-nspr' ;; ('libplds4.so') package_name='lib32-nspr' ;; ('libpng12.so.0') package_name='lib32-libpng12' ;; ('libpng16.so.16') package_name='lib32-libpng' ;; ('libpsl.so.5') package_name='lib32-libpsl' ;; ('libpthread.so.0') package_name='lib32-glibc' ;; ('libpulse.so.0') package_name='lib32-libpulse' ;; ('libpulse-simple.so.0') package_name='lib32-libpulse' ;; ('libresolv.so.2') package_name='lib32-glibc' ;; ('librt.so.1') package_name='lib32-glibc' ;; ('librtmp.so.1') package_name='lib32-rtmpdump' ;; ('libSDL-1.2.so.0') package_name='lib32-sdl' ;; ('libSDL_image-1.2.so.0') package_name='lib32-sdl_image' ;; ('libSDL_kitchensink.so.1') # This library is not provided for Arch Linux unset package_name ;; ('libSDL_mixer-1.2.so.0') package_name='lib32-sdl_mixer' ;; ('libSDL_sound-1.0.so.1') package_name='lib32-sdl_sound' ;; ('libSDL_ttf-2.0.so.0') package_name='lib32-sdl_ttf' ;; ('libSDL2-2.0.so.0') package_name='lib32-sdl2' ;; ('libSDL2_image-2.0.so.0') package_name='lib32-sdl2_image' ;; ('libSDL2_mixer-2.0.so.0') package_name='lib32-sdl2_mixer' ;; ('libSDL2_ttf-2.0.so.0') package_name='lib32-sdl2_ttf' ;; ('libsecret-1.so.0') # This library is not provided in a 32-bit build for Arch Linux unset package_name ;; ('libsigc-2.0.so.0') package_name='lib32-libsigc++' ;; ('libSM.so.6') package_name='lib32-libsm' ;; ('libsmime3.so') package_name='lib32-nss' ;; ('libsmpeg-0.4.so.0') package_name='lib32-smpeg' ;; ('libsodium.so.23') package_name='lib32-libsodium' ;; ('libssh2.so.1') package_name='lib32-libssh2' ;; ('libssl.so.1.0.0') package_name='lib32-openssl-1.0' ;; ('libssl.so.1.1') package_name='lib32-openssl-1.1' ;; ('libssl3.so') package_name='lib32-nss' ;; ('libstdc++.so.5') package_name='lib32-libstdc++5' ;; ('libstdc++.so.6') package_name='lib32-gcc-libs' ;; ('libtcmalloc_minimal.so.4') package_name='lib32-gperftools' ;; ('libtheora.so.0') package_name='lib32-libtheora' ;; ('libtheoradec.so.1') package_name='lib32-libtheora' ;; ('libtheoraenc.so.1') package_name='lib32-libtheora' ;; ('libthread_db.so.1') package_name='lib32-glibc' ;; ('libtiff.so.6') package_name='lib32-libtiff' ;; ('libturbojpeg.so.0') package_name='lib32-libjpeg-turbo' ;; ('libudev.so.0') package_name='lib32-libudev0-shim' ;; ('libudev.so.1') package_name='lib32-systemd' ;; ('libutil.so.1') package_name='lib32-glibc' ;; ('libuuid.so.1') package_name='lib32-util-linux' ;; ('libuv.so.1') # This library is not provided in a 32-bit build for Arch Linux unset package_name ;; ('libvorbis.so.0') package_name='lib32-libvorbis' ;; ('libvorbisenc.so.2') package_name='lib32-libvorbis' ;; ('libvorbisfile.so.3') package_name='lib32-libvorbis' ;; ('libvulkan.so.1') package_name=' lib32-vulkan-icd-loader lib32-vulkan-driver' ;; ('libwayland-client.so.0') package_name='lib32-wayland' ;; ('libX11.so.6') package_name='lib32-libx11' ;; ('libX11-xcb.so.1') package_name='lib32-libx11' ;; ('libxcb.so.1') package_name='lib32-libxcb' ;; ('libxcb-randr.so.0') package_name='lib32-libxcb' ;; ('libXcomposite.so.1') package_name='lib32-libxcomposite' ;; ('libXcursor.so.1') package_name='lib32-libxcursor' ;; ('libXdamage.so.1') package_name='lib32-libxdamage' ;; ('libXext.so.6') package_name='lib32-libxext' ;; ('libXfixes.so.3') package_name='lib32-libxfixes' ;; ('libXft.so.2') package_name='lib32-libxft' ;; ('libXi.so.6') package_name='lib32-libxi' ;; ('libXinerama.so.1') package_name='lib32-libxinerama' ;; ('libxml2.so.2') package_name='lib32-libxml2' ;; ('libxmp.so.4') package_name='lib32-libxmp-git' ;; ('libXmu.so.6') package_name='lib32-libxmu' ;; ('libXrandr.so.2') package_name='lib32-libxrandr' ;; ('libXrender.so.1') package_name='lib32-libxrender' ;; ('libxslt.so.1') package_name='lib32-libxslt' ;; ('libXss.so.1') package_name='lib32-libxss' ;; ('libXt.so.6') package_name='lib32-libxt' ;; ('libXtst.so.6') package_name='lib32-libxtst' ;; ('libXxf86vm.so.1') package_name='lib32-libxxf86vm' ;; ('libyaml-0.so.2') # This library is not provided in a 32-bit build for Arch Linux unset package_name ;; ('libz.so.1') package_name='lib32-zlib' ;; esac if [ -n "${package_name:-}" ]; then printf '%s' "$package_name" return 0 fi dependencies_unknown_libraries_add "$library" } src/60_system_archlinux/25_dependencies_siblings.sh0000644000000000000000000000142013120060140021413 0ustar rootroot# Arch Linux - Print the sinling package names required by the given package # USAGE: archlinux_dependencies_siblings $package # RETURN: a list of package names, # one per line archlinux_dependencies_siblings() { local package package="$1" local required_siblings required_siblings=$(dependencies_list_siblings "$package") # Return early if the current package does not require any sibling if [ -z "$required_siblings" ]; then return 0 fi local sibling dependencies_siblings_list required_packages while read -r sibling; do required_package=$(package_id "$sibling") dependencies_siblings_list="$dependencies_siblings_list $required_package" done <<- EOL $(printf '%s' "$required_siblings") EOL printf '%s' "$dependencies_siblings_list" | list_clean } src/60_system_archlinux/90_messages.sh0000644000000000000000000000077013120060140016713 0ustar rootroot# print mtree computation message # USAGE: info_package_mtree_computation $package info_package_mtree_computation() { local package package="$1" local package_path package_path=$(package_path "$package") local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Création du fichier .MTREE pour %s…\n' ;; ('en'|*) message='Creating .MTREE file for %s…\n' ;; esac print_message 'info' "$message" \ "$package_path" } src/60_system_debian/10_instructions.sh0000644000000000000000000000465313120060140017071 0ustar rootroot# Debian - Print installation instructions # USAGE: debian_install_instructions $package[…] debian_install_instructions() { if [ "${PLAYIT_DEBIAN_OLD_DEB_FORMAT:-0}" -eq 1 ]; then debian_install_instructions_dpkg "$@" else debian_install_instructions_apt "$@" fi } # Debian - Print installation instructions, using apt # USAGE: debian_install_instructions_apt $package[…] debian_install_instructions_apt() { local option_output_dir string_format option_output_dir=$(option_value 'output-dir') if printf '%s' "$option_output_dir" | grep --quiet --fixed-strings ' '; then string_format=' "%s"' else string_format=' %s' fi printf 'apt install' local package package_name package_output string_format for package in "$@"; do package_name=$(package_name "$package") package_output=$(realpath "${option_output_dir}/${package_name}") ## Silence ShellCheck false-positive ## Don't use variables in the printf format string. Use printf "..%s.." "$foo". # shellcheck disable=SC2059 printf "$string_format" "$package_output" done printf '\n' } # Debian - Print installation instructions, using dpkg # USAGE: debian_install_instructions_dpkg $package[…] debian_install_instructions_dpkg() { local option_output_dir string_format option_output_dir=$(option_value 'output-dir') if printf '%s' "$option_output_dir" | grep --quiet --fixed-strings ' '; then string_format=' "%s"' else string_format=' %s' fi printf 'dpkg --install' local package package_name package_output string_format for package in "$@"; do package_name=$(package_name "$package") package_output=$(realpath "${option_output_dir}/${package_name}") ## Silence ShellCheck false-positive ## Don't use variables in the printf format string. Use printf "..%s.." "$foo". # shellcheck disable=SC2059 printf "$string_format" "$package_output" done printf '\n' printf 'apt-get install --fix-broken\n\n' local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Les éventuelles erreurs de dépendances suite à la commande dpkg peuvent être ignorées,' message="$message"' elles seront corrigées par la commande apt-get à entrer ensuite.\n' ;; ('en'|*) message='Potential errors related to dependencies after the dpkg command can be ignored,' message="$message"' they will be fixed by the command apt-get to run afterwards.\n' ;; esac print_message 'info' "$message" } src/60_system_debian/10_packages.sh0000644000000000000000000001510313120060140016073 0ustar rootroot# Debian - Write the metadata for the listed packages # USAGE: debian_packages_metadata $package[…] debian_packages_metadata() { local package for package in "$@"; do debian_package_metadata_single "$package" done } # Debian - Write the metadata for the given package # USAGE: debian_package_metadata_single $package debian_package_metadata_single() { local package package="$1" # Create metadata directory. local package_path control_directory package_path=$(package_path "$package") control_directory="${package_path}/DEBIAN" mkdir --parents "$control_directory" # Write main metadata file (DEBIAN/control). local control_file control_file="${control_directory}/control" debian_package_metadata_control "$package" > "$control_file" # Write postinst/prerm scripts, enforce correct permissions. local postinst_actions postinst_warnings postinst_actions=$(package_postinst_actions "$package") postinst_warnings=$(package_postinst_warnings "$package") if [ -n "$postinst_actions" ] || [ -n "$postinst_warnings" ]; then debian_script_postinst "$package" > "${control_directory}/postinst" chmod 755 "${control_directory}/postinst" fi local prerm_actions prerm_actions=$(package_prerm_actions "$package") if [ -n "$prerm_actions" ]; then debian_script_prerm "$package" > "${control_directory}/prerm" chmod 755 "${control_directory}/prerm" fi } # Print the content of the DEBIAN/control metadata file for the given package # USAGE: debian_package_metadata_control $package # RETURN: the contents of the DEBIAN/control file, # spanning over multiple lines debian_package_metadata_control() { local package package="$1" local \ debian_field_package \ debian_field_source \ debian_field_version \ debian_field_architecture \ debian_field_maintainer \ debian_field_installedsize \ debian_field_provides \ debian_field_depends \ debian_field_description debian_field_package=$(debian_field_package "$package") debian_field_source=$(debian_field_source "$package") debian_field_version=$(debian_field_version "$package") debian_field_architecture=$(debian_field_architecture "$package") debian_field_maintainer=$(debian_field_maintainer "$package") debian_field_installedsize=$(debian_field_installedsize "$package") debian_field_provides=$(debian_field_provides "$package") debian_field_depends=$(debian_field_depends "$package") debian_field_description=$(debian_field_description "$package") cat <<- EOF Package: $debian_field_package Source: $debian_field_source Version: $debian_field_version Architecture: $debian_field_architecture Multi-Arch: foreign Maintainer: $debian_field_maintainer Installed-Size: $debian_field_installedsize Section: non-free/games EOF if [ -n "$debian_field_provides" ]; then cat <<- EOF Conflicts: $debian_field_provides Provides: $debian_field_provides Replaces: $debian_field_provides EOF fi if [ -n "$debian_field_depends" ]; then cat <<- EOF Depends: $debian_field_depends EOF fi cat <<- EOF Description: $debian_field_description EOF } # Debian - Build a list of packages # USAGE: debian_packages_build $package[…] debian_packages_build() { local package for package in "$@"; do debian_package_build_single "$package" done } # Debian - Build a single package # USAGE: debian_package_build_single $package debian_package_build_single() { local package package="$1" local package_path package_path=$(package_path "$package") local option_output_dir package_name generated_package_path option_output_dir=$(option_value 'output-dir') package_name=$(package_name "$package") generated_package_path="${option_output_dir}/${package_name}" # Skip packages already existing, # unless called with --overwrite. local option_overwrite option_overwrite=$(option_value 'overwrite') if \ [ "$option_overwrite" -eq 0 ] \ && [ -e "$generated_package_path" ] then information_package_already_exists "$package_name" return 0 fi # Set the common dpkg-deb options local dpkg_options ## Create all files and directories with owner:group = root:root. dpkg_options='--root-owner-group' # Use old .deb format if the package is going over the size limit for the modern format local package_size package_size=$(debian_field_installedsize "$package") if [ "$package_size" -gt 9700000 ]; then warning_debian_size_limit "$package" export PLAYIT_DEBIAN_OLD_DEB_FORMAT=1 dpkg_options="${dpkg_options:-} --deb-format=0.939000" fi # Set compression setting local option_compression option_compression=$(option_value 'compression') case "$option_compression" in ('none') dpkg_options="${dpkg_options:-} -Znone" ;; ('speed') dpkg_options="${dpkg_options:-} -Zgzip" ;; ('size') if [ "${PLAYIT_DEBIAN_OLD_DEB_FORMAT:-0}" -eq 1 ]; then ## Old .deb format 0.939000 is not compatible with xz compression. dpkg_options="${dpkg_options:-} -Zgzip" else dpkg_options="${dpkg_options:-} -Zxz" fi ;; ('auto') if [ "${PLAYIT_DEBIAN_OLD_DEB_FORMAT:-0}" -eq 1 ]; then ## Old .deb format 0.939000 is not compatible with xz compression. dpkg_options="${dpkg_options:-} -Zgzip" fi ;; esac # Run the actual package generation, using dpkg-deb local TMPDIR package_generation_return_code information_package_building "$package_name" ## We need to explicitly export TMPDIR, or dpkg-deb will not pick it up. export TMPDIR="$PLAYIT_WORKDIR" { debug_external_command "dpkg-deb $dpkg_options --build \"$package_path\" \"$generated_package_path\" 1>/dev/null" dpkg-deb ${dpkg_options:-} --build "$package_path" "$generated_package_path" 1>/dev/null package_generation_return_code=$? } || true if [ $package_generation_return_code -ne 0 ]; then error_package_generation_failed "$package_name" return 1 fi } # Print the file name of the given package # USAGE: package_name_debian $package # RETURNS: the file name, as a string package_name_debian() { local package package="$1" local package_id package_version package_architecture package_name package_id=$(package_id "$package") package_version=$(package_version) package_architecture=$(debian_field_architecture "$package") package_name="${package_id}_${package_version}_${package_architecture}.deb" printf '%s' "$package_name" } # Get the path to the directory where the given package is prepared, # relative to the directory where all packages are stored # USAGE: package_path_debian $package # RETURNS: relative path to a directory, as a string package_path_debian() { local package package="$1" local package_name package_path package_name=$(package_name "$package") package_path="${package_name%.deb}" printf '%s' "$package_path" } src/60_system_debian/15_metadata-fields.sh0000644000000000000000000001022213120060140017343 0ustar rootroot# Debian - Print the content of the "Package" field # USAGE: debian_field_package $package # RETURN: the field content debian_field_package() { local package package="$1" package_id "$package" } # Debian - Print the content of the "Source" field # USAGE: debian_field_source $package # RETURN: the field content debian_field_source() { ## The package argument is not actually used, ## but is passed for consistency with other debian_field_* functions. local package package="$1" ## Including the version should make upgrades easier when using `apt install --with-source (…)`. local game_id package_version game_id=$(game_id) package_version=$(package_version) printf '%s (%s)' "$game_id" "$package_version" } # Debian - Print the content of the "Version" field # USAGE: debian_field_version $package # RETURN: the field content debian_field_version() { ## The package argument is not actually used, ## but is passed for consistency with other debian_field_* functions. local package package="$1" package_version } # Debian - Print the content of the "Architecture" field # USAGE: debian_field_architecture $package # RETURN: the field content debian_field_architecture() { local package package="$1" local package_architecture package_architecture_string package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') package_architecture_string='i386' ;; ('64') package_architecture_string='amd64' ;; ('all') package_architecture_string='all' ;; esac printf '%s' "$package_architecture_string" } # Debian - Print the content of the "Maintainer" field # USAGE: debian_field_maintainer $package # RETURN: the field content debian_field_maintainer() { ## The package argument is not actually used, ## but is passed for consistency with other debian_field_* functions. local package package="$1" package_maintainer } # Debian - Print the content of the "Installed-Size" field # USAGE: debian_field_installedsize $package # RETURN: the field content debian_field_installedsize() { local package package="$1" # Compute the package size, in kilobytes local package_path package_path=$(package_path "$package") du --total --block-size=1K --summarize "$package_path" | \ tail --lines=1 | \ cut --fields=1 } # Debian - Print the content of the "Provides" field # This value is used for the fields "Conflicts" and "Replaces" too. # USAGE: debian_field_provides $package # RETURN: the field content debian_field_provides() { local package package="$1" local package_provides package_provides=$(package_provides "$package") # Return early if there is no package name provided if [ -z "$package_provides" ]; then return 0 fi local provides_list package_name while read -r package_name; do if [ -z "${provides_list:-}" ]; then provides_list="$package_name" else provides_list="$provides_list, $package_name" fi done <<- EOL $(printf '%s' "$package_provides") EOL printf '%s' "$provides_list" } # Debian - Print the content of the "Depends" field # USAGE: debian_field_depends $package # RETURN: the field content debian_field_depends() { local package package="$1" local dependencies_list first_item_displayed dependency_string dependencies_list=$(dependencies_debian_full_list "$package") first_item_displayed=0 while read -r dependency_string; do if [ -z "$dependency_string" ]; then continue fi if [ "$first_item_displayed" -eq 0 ]; then printf '%s' "$dependency_string" first_item_displayed=1 else printf ', %s' "$dependency_string" fi done <<- EOF $(printf '%s' "$dependencies_list") EOF } # Debian - Print the content of the "Description" field # USAGE: debian_field_description $package # RETURN: the field content, # spanning over multiple lines debian_field_description() { local package package="$1" local game_name package_description script_version_string game_name=$(game_name) package_description=$(package_description "$package") script_version_string=$(script_version) printf '%s' "$game_name" if [ -n "$package_description" ]; then printf -- ' - %s' "$package_description" fi printf '\n ./play.it script version %s' "$script_version_string" } src/60_system_debian/15_package-scripts.sh0000644000000000000000000000222513120060140017403 0ustar rootroot# Debian - Print the content of the DEBIAN/postinst script for the given package # USAGE: debian_script_postinst $package # RETURN: the contents of the DEBIAN/postinst file, # spanning over multiple lines debian_script_postinst() { local package package="$1" cat <<- EOF #!/bin/sh set -o errexit EOF ## Include actions that should be run. package_postinst_actions "$package" ## Include warnings that should be displayed. local warning_messages warning_messages=$(package_postinst_warnings "$package") if [ -n "$warning_messages" ]; then local warning_line while read -r warning_line; do printf 'printf "Warning: %%s\\n" "%s"\n' "$warning_line" done <<- EOL $(printf '%s' "$warning_messages") EOL fi cat <<- EOF exit 0 EOF } # Debian - Print the content of the DEBIAN/prerm script for the given package # USAGE: debian_script_prerm $package # RETURN: the contents of the DEBIAN/prerm file, # spanning over multiple lines debian_script_prerm() { local package package="$1" cat <<- EOF #!/bin/sh set -o errexit EOF ## Include actions that should be run. package_prerm_actions "$package" cat <<- EOF exit 0 EOF } src/60_system_debian/20_dependencies.sh0000644000000000000000000000720313120060140016746 0ustar rootroot# Print the list of packages required to provide the given generic dependency keyword on Debian # USAGE: pkg_set_deps_deb $dependency_keyword [$package] pkg_set_deps_deb() { # The optional second argument is only used with the "wine" dependency keyword. local dependency_keyword package dependency_keyword="$1" package="$2" case "$dependency_keyword" in ('alsa') printf 'libasound2-plugins' ;; ('freetype') printf 'libfreetype6' ;; ('gcc32') printf 'gcc-multilib:amd64 | gcc' ;; ('glibc') printf 'libc6' ;; ('glu') printf 'libglu1-mesa | libglu1' ;; ('glx') printf 'libgl1 | libgl1-mesa-glx, libglx-mesa0 | libglx-vendor | libgl1-mesa-glx' ;; ('gtk2') printf 'libgtk2.0-0' ;; ('json') printf 'libjson-c3 | libjson-c2 | libjson0' ;; ('libstdc++') printf 'libstdc++6' ;; ('libudev1') printf 'libudev1' ;; ('libxrandr') printf 'libxrandr2' ;; ('nss') printf 'libnss3' ;; ('openal') printf 'libopenal1' ;; ('sdl2') printf 'libsdl2-2.0-0' ;; ('xcursor') printf 'libxcursor1' ;; ( \ 'dosbox' | \ 'java' | \ 'mono' | \ 'pulseaudio' | \ 'scummvm' | \ 'wine' | \ 'winetricks' | \ 'xgamma' | \ 'xrandr' \ ) debian_dependencies_single_command "$package" "$dependency_keyword" ;; ( \ 'libgdk_pixbuf-2.0.so.0' | \ 'libc.so.6' | \ 'libglib-2.0.so.0' | \ 'libgobject-2.0.so.0' | \ 'libGLU.so.1' | \ 'libGL.so.1' | \ 'libgdk-x11-2.0.so.0' | \ 'libgtk-x11-2.0.so.0' | \ 'libasound.so.2' | \ 'libasound_module_'*'.so' | \ 'libmbedtls.so.12' | \ 'libpng16.so.16' | \ 'libpulse.so.0' | \ 'libpulse-simple.so.0' | \ 'libstdc++.so.6' | \ 'libudev.so.1' | \ 'libX11.so.6' | \ 'libopenal.so.1' | \ 'libSDL-1.2.so.0' | \ 'libSDL2-2.0.so.0' | \ 'libturbojpeg.so.0' | \ 'libuv.so.1' | \ 'libvorbisfile.so.3' | \ 'libz.so.1' \ ) dependency_package_providing_library_deb "$dependency_keyword" ;; (*) # Unknown dependency keywords are assumed to be litteral package names. printf '%s' "$dependency_keyword" ;; esac } # Debian - List all dependencies for the given package # USAGE: dependencies_debian_full_list $package # RETURN: print a list of dependency strings, # one per line dependencies_debian_full_list() { local package package="$1" local packages_list packages_list_full packages_list_full='' # Include generic dependencies local dependencies_generic dependency_generic dependency_package dependencies_generic=$(dependencies_list_generic "$package") while read -r dependency_generic; do dependency_package=$(pkg_set_deps_deb "$dependency_generic" "$package") packages_list_full="$packages_list_full $dependency_package" done <<- EOL $(printf '%s' "$dependencies_generic") EOL # Include dependencies on sibling package packages_list=$(debian_dependencies_siblings "$package") packages_list_full="$packages_list_full $packages_list" # Include dependencies on commands packages_list=$(debian_dependencies_all_commands "$package") packages_list_full="$packages_list_full $packages_list" # Include dependencies on native libraries packages_list=$(dependencies_list_native_libraries_packages "$package") packages_list_full="$packages_list_full $packages_list" # Include dependencies on Mono libraries packages_list=$(dependencies_list_mono_libraries_packages "$package") packages_list_full="$packages_list_full $packages_list" # Include dependencies on GStreamer plugins packages_list=$(debian_dependencies_gstreamer_all_formats "$package") packages_list_full="$packages_list_full $packages_list" printf '%s' "$packages_list_full" | list_clean } src/60_system_debian/25_dependencies_commands.sh0000644000000000000000000000553313120060140020640 0ustar rootroot# Debian - Print the package names providing the commands required by the given package # USAGE: debian_dependencies_all_commands $package # RETURN: a list of Debian package names, # one per line debian_dependencies_all_commands() { local package package="$1" local required_commands required_commands=$(dependencies_list_commands "$package") # Return early if the current package does not require any command if [ -z "$required_commands" ]; then return 0 fi local command packages_list required_packages packages_list='' while read -r command; do required_packages=$(debian_dependencies_single_command "$package" "$command") packages_list="$packages_list $required_packages" done <<- EOL $(printf '%s' "$required_commands") EOL printf '%s' "$packages_list" | list_clean } # Debian - Print the package names providing the required command # USAGE: debian_dependencies_single_command $package $required_command # RETURN: a list of Debian package names, # one per line debian_dependencies_single_command() { local package required_command package="$1" required_command="$2" local package_names case "$required_command" in ('corsix-th') package_names=' corsix-th' ;; ('dos2unix') package_names=' dos2unix' ;; ('dosbox') package_names=' dosbox' ;; ('java') package_names=' default-jre | java-runtime' ;; ('mono') package_names=' mono-runtime' ;; ('mpv') package_names=' mpv:amd64 | mpv' ;; ('openmw-iniimporter') package_names=' openmw-launcher' ;; ('openmw-launcher') package_names=' openmw-launcher' ;; ('pulseaudio') package_names=' pulseaudio:amd64 | pulseaudio' ;; ('renpy') package_names=' renpy' ;; ('scummvm') package_names=' scummvm' ;; ('sed') # The Debian policy advises against adding dependencies on packages that are part of the required set. package_names='' ;; ('setxkbmap') package_names=' x11-xkb-utils' ;; ('vcmilauncher') package_names=' vcmi' ;; ('wine') local package_architecture package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') package_names=' wine32 | wine32-development | wine-stable-i386 | wine-devel-i386 | wine-staging-i386 wine:amd64 | wine' ;; ('64') package_names='wine64 | wine64-development | wine-stable-amd64 | wine-devel-amd64 | wine-staging-amd64 wine' ;; esac ;; ('winetricks') package_names=' winetricks xterm:amd64 | xterm | zenity:amd64 | zenity | kdialog:amd64 | kdialog' ;; ('xgamma') package_names=' x11-xserver-utils:amd64 | x11-xserver-utils' ;; ('xrandr') package_names=' x11-xserver-utils:amd64 | x11-xserver-utils' ;; (*) dependencies_unknown_command_add "$required_command" return 0 ;; esac printf '%s' "$package_names" } src/60_system_debian/25_dependencies_gstreamer-plugins.sh0000644000000000000000000000467113120060140022511 0ustar rootroot# Debian - Print the package names providing the GStreamer plugins to decode the formats required by the given package # USAGE: debian_dependencies_gstreamer_all_formats $package # RETURN: a list of Debian package names, # one per line debian_dependencies_gstreamer_all_formats() { local package package="$1" local gstreamer_decoders gstreamer_decoders=$(dependencies_list_gstreamer_decoders "$package") # Return early if the current package does not require any GStreamer plugin if [ -z "$gstreamer_decoders" ]; then return 0 fi local media_format packages_list required_packages packages_list='' while read -r media_format; do required_packages=$(debian_dependencies_gstreamer_single_format "$media_format") packages_list="$packages_list $required_packages" done <<- EOL $(printf '%s' "$gstreamer_decoders") EOL printf '%s' "$packages_list" | list_clean } # Debian - Print the package names providing the required GStreamer plugins to decode the given format # USAGE: debian_dependency_providing_gstreamer_plugin $media_format # RETURN: a list of Debian package names, # one per line debian_dependencies_gstreamer_single_format() { local media_format media_format="$1" local package_names case "$media_format" in ('audioconvert') package_names=' gstreamer1.0-plugins-base' ;; ('avidemux') package_names=' gstreamer1.0-plugins-good' ;; ('decodebin') package_names=' gstreamer1.0-plugins-base' ;; ('deinterlace') package_names=' gstreamer1.0-plugins-good' ;; ('application/x-id3') package_names=' gstreamer1.0-plugins-good' ;; ('audio/mpeg, mpegversion=(int)1, layer=(int)3') package_names=' gstreamer1.0-plugins-good' ;; ('audio/x-wma, wmaversion=(int)1') package_names=' gstreamer1.0-libav' ;; ('video/mpeg, systemstream=(boolean)true, mpegversion=(int)1') package_names=' gstreamer1.0-plugins-ugly gstreamer1.0-plugins-bad' ;; ('video/quicktime, variant=(string)iso') package_names=' gstreamer1.0-plugins-good gstreamer1.0-libav' ;; ('video/x-ms-asf') package_names=' gstreamer1.0-plugins-ugly gstreamer1.0-libav' ;; ('video/x-msvideo') package_names=' gstreamer1.0-plugins-good gstreamer1.0-libav' ;; ('video/x-wmv, wmvversion=(int)1') package_names=' gstreamer1.0-libav' ;; (*) dependencies_unknown_gstreamer_media_formats_add "$media_format" return 0 ;; esac printf '%s' "$package_names" } src/60_system_debian/25_dependencies_mono-libraries.sh0000644000000000000000000000742013120060140021756 0ustar rootroot# Debian - Print the package names providing the given Mono libraries # WARNING - Unknown Mono libraries are silently omitted. # USAGE: debian_dependencies_providing_mono_libraries $library[…] # RETURN: a list of Debian package names, # one per line debian_dependencies_providing_mono_libraries() { local library packages_list package packages_list='' for library in "$@"; do package=$(debian_dependency_providing_mono_library "$library") packages_list="$packages_list $package" done printf '%s' "$packages_list" | list_clean } # Debian - Print the package name providing the given Mono library # WARNING - Unknown Mono libraries are silently omitted. # USAGE: debian_dependency_providing_mono_library $library # RETURN: a single Debian package name, # followed by a line break debian_dependency_providing_mono_library() { local library library="$1" local package_name case "$library" in ('mscorlib.dll') package_name='libmono-corlib4.5-cil' ;; ('I18N.dll') package_name='libmono-i18n4.0-cil' ;; ('I18N.West.dll') package_name='libmono-i18n-west4.0-cil' ;; ('Microsoft.CSharp.dll') package_name='libmono-microsoft-csharp4.0-cil' ;; ('Mono.CSharp.dll') package_name='libmono-csharp4.0c-cil' ;; ('Mono.Posix.dll') package_name='libmono-posix4.0-cil' ;; ('Mono.Security.dll') package_name='libmono-security4.0-cil' ;; ('OpenTK.dll') package_name='libopentk1.1-cil' ;; ('OpenTK.Compatibility.dll') package_name='libopentk1.1-cil' ;; ('OpenTK.GLControl.dll') package_name='libopentk1.1-cil' ;; ('System.dll') package_name='libmono-system4.0-cil' ;; ('System.ComponentModel.DataAnnotations.dll') package_name='libmono-system-componentmodel-dataannotations4.0-cil' ;; ('System.Configuration.dll') package_name='libmono-system-configuration4.0-cil' ;; ('System.Configuration.Install.dll') package_name='libmono-system-configuration-install4.0-cil' ;; ('System.Core.dll') package_name='libmono-system-core4.0-cil' ;; ('System.Data.dll') package_name='libmono-system-data4.0-cil' ;; ('System.Design.dll') package_name='libmono-system-design4.0-cil' ;; ('System.Drawing.dll') package_name='libmono-system-drawing4.0-cil' ;; ('System.IO.Compression.dll') package_name='libmono-system-io-compression4.0-cil' ;; ('System.IO.Compression.FileSystem.dll') package_name='libmono-system-io-compression-filesystem4.0-cil' ;; ('System.Management.dll') package_name='libmono-system-management4.0-cil' ;; ('System.Net.dll') package_name='libmono-system-net4.0-cil' ;; ('System.Net.Http.dll') package_name='libmono-system-net-http4.0-cil' ;; ('System.Numerics.dll') package_name='libmono-system-numerics4.0-cil' ;; ('System.Runtime.Serialization.dll') package_name='libmono-system-runtime-serialization4.0-cil' ;; ('System.Security.dll') package_name='libmono-system-security4.0-cil' ;; ('System.Transactions.dll') package_name='libmono-system-transactions4.0-cil' ;; ('System.Web.dll') package_name='libmono-system-web4.0-cil' ;; ('System.Web.Extensions.dll') package_name='libmono-system-web-extensions4.0-cil' ;; ('System.Web.Http.dll') package_name='libmono-system-web-http4.0-cil' ;; ('System.Web.Services.dll') package_name='libmono-system-web-services4.0-cil' ;; ('System.Windows.Forms.dll') package_name='libmono-system-windows-forms4.0-cil' ;; ('System.Xml.dll') package_name='libmono-system-xml4.0-cil' ;; ('System.Xml.Linq.dll') package_name='libmono-system-xml-linq4.0-cil' ;; ('WindowsBase.dll') package_name='libmono-windowsbase4.0-cil' ;; esac if [ -n "${package_name:-}" ]; then printf '%s' "$package_name" return 0 fi dependencies_unknown_mono_libraries_add "$library" } src/60_system_debian/25_dependencies_native-libraries.sh0000644000000000000000000002614213120060140022276 0ustar rootroot# Debian - Print the package names providing the given native libraries # USAGE: debian_dependencies_providing_native_libraries $library[…] # RETURN: a list of Debian package names, # one per line debian_dependencies_providing_native_libraries() { local library packages_list package packages_list='' for library in "$@"; do package=$(dependency_package_providing_library_deb "$library") packages_list="$packages_list $package" done printf '%s' "$packages_list" | list_clean } # Debian - Print the package name providing the given native library # USAGE: dependency_package_providing_library_deb $library dependency_package_providing_library_deb() { local library package_name library="$1" case "$library" in ('ld-linux.so.2') package_name='libc6' ;; ('ld-linux-x86-64.so.2') package_name='libc6' ;; ('liballeg.so.4.4') package_name='liballegro4.4' ;; ('liballegro.so.5.2') package_name='liballegro5.2' ;; ('liballegro_acodec.so.5.2') package_name='liballegro-acodec5.2' ;; ('liballegro_audio.so.5.2') package_name='liballegro-audio5.2' ;; ('liballegro_font.so.5.2') package_name='liballegro5.2' ;; ('liballegro_image.so.5.2') package_name='liballegro-image5.2' ;; ('liballegro_primitives.so.5.2') package_name='liballegro5.2' ;; ('liballegro_ttf.so.5.2') package_name='liballegro-ttf5.2' ;; ('libalut.so.0') package_name='libalut0' ;; ('libasound.so.2') package_name='libasound2' ;; ('libasound_module_'*'.so') package_name='libasound2-plugins' ;; ('libatspi.so.0') package_name='libatspi2.0-0' ;; ('libatk-1.0.so.0') package_name='libatk1.0-0' ;; ('libaudio.so.2') package_name='libaudio2' ;; ('libboost_locale.so.1.74.0') package_name='libboost-locale1.74.0' ;; ('libbz2.so.1.0'|'libbz2.so.1') package_name='libbz2-1.0' ;; ('libc.so.6') package_name='libc6' ;; ('libc++.so.1') package_name='libc++1' ;; ('libc++abi.so.1') package_name='libc++abi1' ;; ('libcairo.so.2') package_name='libcairo2' ;; ('libCg.so') package_name='libcg' ;; ('libCgGL.so') package_name='libcggl' ;; ('libcom_err.so.2') package_name='libcom-err2' ;; ('libcrypt.so.1') package_name='libcrypt1' ;; ('libcups.so.2') package_name='libcups2' ;; ('libcurl.so.4') package_name='libcurl4' ;; ('libcurl.so.4+CURL_OPENSSL_3') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/curl/ return 0 ;; ('libcurl-gnutls.so.4') package_name='libcurl3-gnutls' ;; ('libdbus-1.so.3') package_name='libdbus-1-3' ;; ('libdbus-glib-1.so.2') package_name='libdbus-glib-1-2' ;; ('libdl.so.2') package_name='libc6' ;; ('libEGL.so.1') package_name='libegl1' ;; ('libexpat.so.1') package_name='libexpat1' ;; ('libFAudio.so.0') package_name='libfaudio0' ;; ('libFLAC.so.8') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/flac/ return 0 ;; ('libfontconfig.so.1') package_name='libfontconfig1' ;; ('libfreeimage.so.3') package_name='libfreeimage3' ;; ('libfreetype.so.6') package_name='libfreetype6' ;; ('libfribidi.so.0') package_name='libfribidi0' ;; ('libgcc_s.so.1') package_name='libgcc-s1' ;; ('libgconf-2.so.4') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/gconf/ return 0 ;; ('libgcrypt.so.11') # This old library is no longer available from Debian. unset package_name ;; ('libgdiplus.so') package_name='libgdiplus' ;; ('libgdk-3.so.0') package_name='libgtk-3-0' ;; ('libgdk_pixbuf-2.0.so.0') package_name='libgdk-pixbuf-2.0-0 | libgdk-pixbuf2.0-0' ;; ('libgdk-x11-2.0.so.0') package_name='libgtk2.0-0' ;; ('libgio-2.0.so.0') package_name='libglib2.0-0' ;; ('libGL.so.1') package_name=' libgl1 | libgl1-mesa-glx libglx-mesa0 | libglx-vendor | libgl1-mesa-glx' ;; ('libGLEW.so.2.2') package_name='libglew2.2' ;; ('libglfw.so.3') package_name='libglfw3 | libglfw3-wayland' ;; ('libglib-2.0.so.0') package_name='libglib2.0-0' ;; ('libGLU.so.1') package_name='libglu1-mesa | libglu1' ;; ('libGLX.so.0') package_name='libglx0' ;; ('libgmodule-2.0.so.0') package_name='libglib2.0-0' ;; ('libgobject-2.0.so.0') package_name='libglib2.0-0' ;; ('libgomp.so.1') package_name='libgomp1' ;; ('libgpg-error.so.0') package_name='libgpg-error0' ;; ('libgssapi_krb5.so.2') package_name='libgssapi-krb5-2' ;; ('libgthread-2.0.so.0') package_name='libglib2.0-0' ;; ('libgtk-x11-2.0.so.0') package_name='libgtk2.0-0' ;; ('libgtk-3.so.0') package_name='libgtk-3-0' ;; ('libICE.so.6') package_name='libice6' ;; ('libidn.so.11') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/libidn/ return 0 ;; ('libidn2.so.0') package_name='libidn2-0' ;; ('libIL.so.1') package_name='libdevil1c2' ;; ('libjpeg.so.62') package_name='libjpeg62-turbo | libjpeg62' ;; ('libk5crypto.so.3') package_name='libk5crypto3' ;; ('libkrb5.so.3') package_name='libkrb5-3' ;; ('liblcms2.so.2') package_name='liblcms2-2' ;; ('liblua5.3.so.0') package_name='liblua5.3-0' ;; ('libluajit-5.1.so.2') package_name='libluajit-5.1-2' ;; ('liblz4.so.1') package_name='liblz4-1' ;; ('libm.so.6') package_name='libc6' ;; ('libmbedtls.so.12') package_name='libmbedtls12' ;; ('libminiupnpc.so.17') package_name='libminiupnpc17' ;; ('libminizip.so.1') package_name='libminizip1' ;; ('libmodplug.so.1') package_name='libmodplug1' ;; ('libmpg123.so.0') package_name='libmpg123-0' ;; ('libnghttp2.so.14') package_name='libnghttp2-14' ;; ('libnotify.so.4') package_name='libnotify4' ;; ('libnspr4.so') package_name='libnspr4' ;; ('libnss3.so') package_name='libnss3' ;; ('libnssutil3.so') package_name='libnss3' ;; ('libogg.so.0') package_name='libogg0' ;; ('libopenal.so.1') package_name='libopenal1' ;; ('libOpenGL.so.0') package_name='libopengl0' ;; ('libopenmpt.so.0') package_name='libopenmpt0' ;; ('libpango-1.0.so.0') package_name='libpango-1.0-0' ;; ('libpangocairo-1.0.so.0') package_name='libpangocairo-1.0-0' ;; ('libpangoft2-1.0.so.0') package_name='libpangoft2-1.0-0' ;; ('libpcre.so.3') package_name='libpcre3' ;; ('libphysfs.so.1') package_name='libphysfs1' ;; ('libpixman-1.so.0') package_name='libpixman-1-0' ;; ('libplc4.so') package_name='libnspr4' ;; ('libplds4.so') package_name='libnspr4' ;; ('libpng12.so.0') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/libpng/ return 0 ;; ('libpng16.so.16') package_name='libpng16-16' ;; ('libpsl.so.5') package_name='libpsl5' ;; ('libpthread.so.0') package_name='libc6' ;; ('libpulse.so.0') package_name='libpulse0' ;; ('libpulse-simple.so.0') package_name='libpulse0' ;; ('libresolv.so.2') package_name='libc6' ;; ('librt.so.1') package_name='libc6' ;; ('librtmp.so.1') package_name='librtmp1' ;; ('libSDL-1.2.so.0') package_name='libsdl1.2debian' ;; ('libSDL_image-1.2.so.0') package_name='libsdl-image1.2' ;; ('libSDL_kitchensink.so.1') package_name='libsdl-kitchensink1' ;; ('libSDL_mixer-1.2.so.0') package_name='libsdl-mixer1.2' ;; ('libSDL_sound-1.0.so.1') package_name='libsdl-sound1.2' ;; ('libSDL_ttf-2.0.so.0') package_name='libsdl-ttf2.0-0' ;; ('libSDL2-2.0.so.0') package_name='libsdl2-2.0-0' ;; ('libSDL2_image-2.0.so.0') package_name='libsdl2-image-2.0-0' ;; ('libSDL2_mixer-2.0.so.0') package_name='libsdl2-mixer-2.0-0' ;; ('libSDL2_ttf-2.0.so.0') package_name='libsdl2-ttf-2.0-0' ;; ('libsecret-1.so.0') package_name='libsecret-1-0' ;; ('libsigc-2.0.so.0') package_name='libsigc++-2.0-0v5' ;; ('libSM.so.6') package_name='libsm6' ;; ('libsmime3.so') package_name='libnss3' ;; ('libsmpeg-0.4.so.0') package_name='libsmpeg0' ;; ('libsodium.so.23') package_name='libsodium23' ;; ('libssh2.so.1') package_name='libssh2-1' ;; ('libssl.so.1.0.0') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/openssl/ return 0 ;; ('libssl.so.1.1') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/openssl/ return 0 ;; ('libssl3.so') package_name='libnss3' ;; ('libstdc++.so.5') package_name='libstdc++5' ;; ('libstdc++.so.6') package_name='libstdc++6' ;; ('libtcmalloc_minimal.so.4') package_name='libtcmalloc-minimal4' ;; ('libtheora.so.0') package_name='libtheora0' ;; ('libtheoradec.so.1') package_name='libtheora0' ;; ('libtheoraenc.so.1') package_name='libtheora0' ;; ('libthread_db.so.1') package_name='libc6' ;; ('libtiff.so.6') package_name='libtiff6' ;; ('libturbojpeg.so.0') package_name='libturbojpeg0' ;; ('libudev.so.0') package_name='libudev0' ;; ('libudev.so.1') package_name='libudev1' ;; ('libutil.so.1') package_name='libc6' ;; ('libuuid.so.1') package_name='libuuid1' ;; ('libuv.so.1') package_name='libuv1' ;; ('libvorbis.so.0') package_name='libvorbis0a' ;; ('libvorbisenc.so.2') package_name='libvorbisenc2' ;; ('libvorbisfile.so.3') package_name='libvorbisfile3' ;; ('libvulkan.so.1') package_name=' libvulkan1 mesa-vulkan-drivers | vulkan-icd' ;; ('libwayland-client.so.0') package_name='libwayland-client0' ;; ('libX11.so.6') package_name='libx11-6' ;; ('libX11-xcb.so.1') package_name='libx11-xcb1' ;; ('libxcb.so.1') package_name='libxcb1' ;; ('libxcb-randr.so.0') package_name='libxcb-randr0' ;; ('libXcomposite.so.1') package_name='libxcomposite1' ;; ('libXcursor.so.1') package_name='libxcursor1' ;; ('libXdamage.so.1') package_name='libxdamage1' ;; ('libXext.so.6') package_name='libxext6' ;; ('libXfixes.so.3') package_name='libxfixes3' ;; ('libXft.so.2') package_name='libxft2' ;; ('libXi.so.6') package_name='libxi6' ;; ('libXinerama.so.1') package_name='libxinerama1' ;; ('libxml2.so.2') package_name='libxml2' ;; ('libxmp.so.4') package_name='libxmp4' ;; ('libXmu.so.6') package_name='libxmu6' ;; ('libXrandr.so.2') package_name='libxrandr2' ;; ('libXrender.so.1') package_name='libxrender1' ;; ('libxslt.so.1') package_name='libxslt1.1' ;; ('libXss.so.1') package_name='libxss1' ;; ('libXt.so.6') package_name='libxt6' ;; ('libXtst.so.6') package_name='libxtst6' ;; ('libXxf86vm.so.1') package_name='libxxf86vm1' ;; ('libyaml-0.so.2') package_name='libyaml-0-2' ;; ('libz.so.1') package_name='zlib1g' ;; esac if [ -n "${package_name:-}" ]; then printf '%s' "$package_name" return 0 fi dependencies_unknown_libraries_add "$library" } src/60_system_debian/25_dependencies_siblings.sh0000644000000000000000000000142513120060140020645 0ustar rootroot# Debian - Print the sinling package names required by the given package # USAGE: debian_dependencies_siblings $package # RETURN: a list of package names, # one per line debian_dependencies_siblings() { local package package="$1" local required_siblings required_siblings=$(dependencies_list_siblings "$package") # Return early if the current package does not require any sibling package if [ -z "$required_siblings" ]; then return 0 fi local sibling dependencies_siblings_list required_package while read -r sibling; do required_package=$(package_id "$sibling") dependencies_siblings_list="${dependencies_siblings_list:-} $required_package" done <<- EOL $(printf '%s' "$required_siblings") EOL printf '%s' "${dependencies_siblings_list:-}" | list_clean } src/60_system_debian/90_messages.sh0000644000000000000000000000132413120060140016134 0ustar rootroot# Warning: A .deb package is going over the 9GB size limit # USAGE: warning_debian_size_limit $package warning_debian_size_limit() { local package package="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le paquet suivant est trop gros pour le format .deb moderne : %s\n' message="$message"'Merci de signaler cet avertissement sur notre système de suivi : %s\n\n' ;; ('en'|*) message='The following package is too big for .deb modern format: %s\n' message="$message"'Please report this warning on our issues tracker: %s\n\n' ;; esac print_message 'warning' "$message" \ "$package" \ "$PLAYIT_GAMES_BUG_TRACKER_URL" } src/60_system_gentoo/10_instructions_egentoo-variant.sh0000644000000000000000000000126513120060140022320 0ustar rootroot# print installation instructions for Gentoo Linux with ebuilds # USAGE: print_instructions_egentoo $pkg[…] print_instructions_egentoo() { info_package_to_distfiles local pkg pkg_path ebuild_path package_id for pkg in "$@"; do pkg_path=$(realpath "$(get_value "${pkg}_PKG")") ebuild_path="$(basename "${pkg_path%%.*}").ebuild" package_id=$(package_id "$pkg") printf 'mkdir -p ${OVERLAY_PATH}/games-playit/%s\n' \ "$package_id" printf 'mv %s ${OVERLAY_PATH}/games-playit/%s/\n' \ "$ebuild_path" "$package_id" printf 'ebuild ${OVERLAY_PATH}/games-playit/%s/%s manifest\n' \ "$package_id" "$ebuild_path" printf 'emerge games-playit/%s\n' \ "$package_id" done } src/60_system_gentoo/10_instructions_gentoo-variant.sh0000644000000000000000000000156513120060140022156 0ustar rootroot# Gentoo - Print installation instructions # USAGE: print_instructions_gentoo $package[…] print_instructions_gentoo() { local option_output_dir string_format option_output_dir=$(option_value 'output-dir') if printf '%s' "$option_output_dir" | grep --quiet --fixed-strings ' '; then string_format=' "%s"' else string_format=' %s' fi printf 'quickunpkg --' local package package_name package_output for package in "$@"; do package_name=$(package_name "$package") package_output=$(realpath "${option_output_dir}/${package_name}") ## Silence ShellCheck false-positive ## Don't use variables in the printf format string. Use printf "..%s.." "$foo". # shellcheck disable=SC2059 printf "$string_format" "$package_output" done printf ' # https://downloads.dotslashplay.it/resources/gentoo/ ' information_installation_instructions_gentoo_comment printf '\n' } src/60_system_gentoo/10_packages_egentoo-variant.sh0000644000000000000000000002015413120060140021330 0ustar rootroot# Gentoo ("egentoo" variant) - Write the metadata for the listed packages # USAGE: egentoo_packages_metadata $package[…] egentoo_packages_metadata() { local ebuild_path local inherits inherits="xdg" local option_output_dir package_id package_name option_output_dir=$(option_value 'output-dir') package_id="$(egentoo_package_id)" package_name="$(egentoo_package_name)" mkdir --parents "${option_output_dir}/overlay/games-playit/${package_id}" ebuild_path=$(realpath "${option_output_dir}/overlay/games-playit/${package_id}/${package_name}.ebuild") local \ egentoo_field_keywords \ egentoo_field_srcuri \ egentoo_field_rdepend \ egentoo_field_bdepend egentoo_field_keywords=$(egentoo_field_keywords "$@") egentoo_field_srcuri=$(egentoo_field_srcuri "$@") egentoo_field_rdepend=$(egentoo_field_rdepend "$@") egentoo_field_bdepend=$(egentoo_field_bdepend "$@") local install_cp_options install_cp_options='--link --no-dereference --recursive --verbose' cat > "$ebuild_path" << EOF # Copyright 1999-2021 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=7 RESTRICT="fetch strip binchecks" inherit $inherits KEYWORDS="$egentoo_field_keywords" DESCRIPTION="Ebuild automatically generated with ./play.it" HOMEPAGE="https://forge.dotslashplay.it/play.it" SRC_URI="$egentoo_field_srcuri" SLOT="0" RDEPEND="$egentoo_field_rdepend" BDEPEND="$egentoo_field_bdepend" S=\${WORKDIR} pkg_nofetch() { elog "Please move \$SRC_URI" elog "to your distfiles folder." } src_install() { if test -d \$S/data; then cp $install_cp_options \$S/data/* \$D || die fi if use x86 && test -d \$S/x86; then cp $install_cp_options \$S/x86/* \$D || die elif use amd64; then if test -d \$S/amd64; then cp $install_cp_options \$S/amd64/* \$D || die elif test -d \$S/x86; then cp $install_cp_options \$S/x86/* \$D || die fi fi } EOF } # Append the required extension to the given package name, # relying on BINPKG_COMPRESS environment variable. # USAGE: egentoo_package_filename_auto package_filename # RETURN: the given package name, with the correct filename extension added, # following the current value of BINPKG_COMPRESS egentoo_package_filename_auto() { local package_filename package_filename="$1" # "zstd" is the default value for BINKPG_COMPRESS, # according to make.conf(5) manpage. # cf. https://dev.gentoo.org/~zmedico/portage/doc/man/make.conf.5.html local package_filename case "${BINPKG_COMPRESS:-zstd}" in ('bzip2') package_filename="${package_filename}.bz2" ;; ('gzip') package_filename="${package_filename}.gz" ;; ('lz4') package_filename="${package_filename}.lz4" ;; ('lzip') package_filename="${package_filename}.lz" ;; ('lzop') package_filename="${package_filename}.lzop" ;; ('xz') package_filename="${package_filename}.xz" ;; ('zstd') package_filename="${package_filename}.zst" ;; esac printf '%s' "$package_filename" } # Print the command that should be used for the generated .tar archive compression, # relying on BINPKG_COMPRESS environment variable. # USAGE: egentoo_package_compression_command_auto # RETURN: the command to use for the generated archive compression, # following the current value of BINPKG_COMPRESS egentoo_package_compression_command_auto() { # "zstd" is the default value for BINKPG_COMPRESS, # according to make.conf(5) manpage. # cf. https://dev.gentoo.org/~zmedico/portage/doc/man/make.conf.5.html local compression_command case "${BINPKG_COMPRESS:-zstd}" in ('bzip2') compression_command='bzip2' ;; ('gzip') compression_command='gzip' ;; ('lz4') compression_command='lz4' ;; ('lzip') compression_command='lzip' ;; ('lzop') compression_command='lzop' ;; ('xz') compression_command='xz' ;; ('zstd') compression_command='zstd' ;; esac printf '%s' "$compression_command" } # Gentoo ("egentoo" variant) - Build dummy package # USAGE: egentoo_packages_build $package[…] egentoo_packages_build() { local option_output_dir package_name package_filename option_output_dir=$(option_value 'output-dir') mkdir --parents "${option_output_dir}/packages" package_name="$(egentoo_package_name)" package_filename=$(realpath "${option_output_dir}/packages/${package_name}.tar") # Set base tar archiving command and options local tar_command tar_options tar_command='tar' tar_options='--create -P' if variable_is_empty 'PLAYIT_TAR_IMPLEMENTATION'; then guess_tar_implementation fi case "$PLAYIT_TAR_IMPLEMENTATION" in ('gnutar') tar_options="$tar_options --group=root --owner=root" ;; ('bsdtar') tar_options="$tar_options --gname=root --uname=root" ;; (*) error_unknown_tar_implementation return 1 ;; esac # Set compression command and options local option_compression compression_command compression_options option_compression=$(option_value 'compression') case "$option_compression" in ('speed') package_filename="${package_filename}.gz" compression_command='gzip' compression_options='' ;; ('size') package_filename="${package_filename}.bz2" compression_command='bzip2' compression_options='' ;; ('auto') package_filename=$(egentoo_package_filename_auto "$package_filename") compression_command=$(egentoo_package_compression_command_auto) compression_options="${BINPKG_COMPRESS_FLAGS:-}" ;; esac compression_options="$compression_options --stdout --quiet" local option_overwrite package option_overwrite=$(option_value 'overwrite') if [ -e "$package_filename" ] && [ "$option_overwrite" -eq 0 ]; then information_package_already_exists "$(basename "$package_filename")" for package in "$@"; do eval "${package}"_PKG=\""$package_filename"\" export "${package}"_PKG done return 0 else rm -f "$package_filename" fi local packages_paths package_path package_architecture packages_paths='' for package in "$@"; do package_path=$(package_path "$package") packages_paths="$packages_paths $package_path" package_architecture=$(package_architecture "$package") case "$package_architecture" in ('64') tar_options="$tar_options --xform=s:^${package_path}:./amd64:x" ;; ('32') tar_options="$tar_options --xform=s:^${package_path}:./x86:x" ;; (*) tar_options="$tar_options --xform=s:^${package_path}:./data:x" ;; esac export "${package}_PKG=$package_filename" done # Run the actual package generation, using tar local package_generation_return_code information_package_building "$(basename "$package_filename")" if [ -z "$compression_command" ]; then debug_external_command "\"$tar_command\" $tar_options --file \"$package_filename\" $packages_paths" { # shellcheck disable=SC2046 "$tar_command" $tar_options --file "$package_filename" $packages_paths package_generation_return_code=$? } || true else debug_external_command "\"$tar_command\" $tar_options $packages_paths | \"$compression_command\" $compression_options > \"$package_filename\"" { "$tar_command" $tar_options $packages_paths | "$compression_command" $compression_options > "$package_filename" package_generation_return_code=$? } || true fi if [ $package_generation_return_code -ne 0 ]; then error_package_generation_failed "$package_name" return 1 fi } # Get the path to the directory where the given package is prepared, # relative to the directory where all packages are stored # USAGE: package_path_egentoo $package # RETURNS: relative path to a directory, as a string package_path_egentoo() { local package package="$1" local package_id package_version package_architecture package_path package_id=$(package_id "$package") package_version=$(package_version) package_architecture=$(gentoo_package_architecture_string "$package") package_path="${package_id}_${package_version}_${package_architecture}" printf '%s' "$package_path" } # id of the single package built on egentoo # USAGE: egentoo_package_id egentoo_package_id() { game_id } # Prints the name of the single package built using the egentoo variant # USAGE: egentoo_package_name egentoo_package_name() { local package_id package_version package_id=$(egentoo_package_id) package_version=$(package_version) printf '%s-%s' "$package_id" "$package_version" } src/60_system_gentoo/10_packages_gentoo-variant.sh0000644000000000000000000002077513120060140021174 0ustar rootroot# Gentoo ("gentoo" variant) - Write the metadata for the listed packages # USAGE: gentoo_packages_metadata $package[…] gentoo_packages_metadata() { local package for package in "$@"; do gentoo_package_metadata_single "$package" done } # Gentoo ("gentoo" variant) - Write the metadata for the given package # USAGE: gentoo_package_metadata_single $package gentoo_package_metadata_single() { local package package="$1" local package_id overlay_path overlay_path="${PLAYIT_WORKDIR}/${package}/gentoo-overlay" mkdir --parents "${overlay_path}/metadata" cat > "${overlay_path}/metadata/layout.conf" <<- EOF masters = gentoo EOF mkdir --parents "${overlay_path}/profiles" cat > "${overlay_path}/profiles/categories" <<- EOF games-playit EOF local package_id package_path package_id=$(package_id "$package") package_path=$(package_path "$package") mkdir --parents "${overlay_path}/games-playit/${package_id}/files" ln --symbolic --force --no-target-directory \ "$package_path" \ "${overlay_path}/games-playit/${package_id}/files/install" # Write the ebuild file local ebuild_path ebuild_path=$(gentoo_ebuild_path "$package") gentoo_ebuild_content "$package" > "$ebuild_path" } # Gentoo - Print path to ebuild file for the given package # USAGE: gentoo_ebuild_path $package # RETURN: the path to the .ebuild file gentoo_ebuild_path() { local package package="$1" local package_id package_name package_id=$(package_id "$package") package_name=$(package_name "$package") printf '%s/%s/gentoo-overlay/games-playit/%s/%s.ebuild' "$PLAYIT_WORKDIR" "$package" "$package_id" "${package_name%.tbz2}" } # Gentoo ("gentoo" variant) - Print the contents of the ebuild file # USAGE: gentoo_ebuild_content $package # RETURN: the file contents, # spanning over multiple lines gentoo_ebuild_content() { local package package="$1" local \ gentoo_field_keywords \ gentoo_field_description \ gentoo_field_rdepend gentoo_field_keywords=$(gentoo_field_keywords "$package") gentoo_field_description=$(gentoo_field_description "$package") gentoo_field_rdepend=$(gentoo_field_rdepend "$package") cat <<- EOF EAPI=7 RESTRICT="fetch strip binchecks" KEYWORDS="$gentoo_field_keywords" DESCRIPTION="$gentoo_field_description" SLOT="0" RDEPEND="$gentoo_field_rdepend" EOF cat <<- 'EOF' src_unpack() { mkdir --parents "$S" } src_install() { cp --recursive --link $FILESDIR/install/* $ED/ } EOF # Print the pkg_postinst function, if some postinst actions are set gentoo_script_postinst "$package" # Print the pkg_prerm function, if some prerm actions are set gentoo_script_prerm "$package" } # Gentoo ("gentoo" variant) - Build a list of packages # USAGE: gentoo_packages_build $package[…] gentoo_packages_build() { local package for package in "$@"; do gentoo_package_build_single "$package" done } # Gentoo ("gentoo" variant) - Build a single package # USAGE: gentoo_package_build_single $package gentoo_package_build_single() { local package package="$1" # Set the path where the package should be generated. local option_output_dir package_name generated_package_path generated_package_directory option_output_dir=$(option_value 'output-dir') package_name=$(package_name "$package") generated_package_path="${option_output_dir}/${package_name}" generated_package_directory=$(dirname "$generated_package_path") # Skip packages already existing, # unless called with --overwrite. local option_overwrite option_overwrite=$(option_value 'overwrite') if \ [ "$option_overwrite" -eq 0 ] \ && [ -e "$generated_package_path" ] then information_package_already_exists "$package_name" return 0 fi # Set compression setting local option_compression binpkg_compress option_compression=$(option_value 'compression') case "$option_compression" in ('speed') binpkg_compress='gzip' ;; ('size') binpkg_compress='bzip2' ;; ('auto') binpkg_compress='' ;; esac # Run the actual package generation, using ebuild local ebuild_path metadata_generation_return_code package_generation_return_code information_package_building "$package_name" mkdir --parents "${PLAYIT_WORKDIR}/portage-tmpdir" ebuild_path=$(gentoo_ebuild_path "$package") { ebuild "$ebuild_path" manifest 1>/dev/null metadata_generation_return_code=$? } || true if [ $metadata_generation_return_code -ne 0 ]; then error_package_metadata_generation_failed "$package_name" return 1 fi ## The following variables must be exported, otherwise ebuild would not pick them up. PORTAGE_TMPDIR="${PLAYIT_WORKDIR}/portage-tmpdir" PKGDIR="${PLAYIT_WORKDIR}/gentoo-pkgdir" export PORTAGE_TMPDIR PKGDIR if [ -n "$binpkg_compress" ]; then { ## The following variable must be exported, otherwise ebuild would not pick it up. BINPKG_COMPRESS="$binpkg_compress" export BINPKG_COMPRESS debug_external_command "fakeroot -- ebuild \"$ebuild_path\" package 1>/dev/null" fakeroot -- ebuild "$ebuild_path" package 1>/dev/null package_generation_return_code=$? } || true else { debug_external_command "fakeroot -- ebuild \"$ebuild_path\" package 1>/dev/null" fakeroot -- ebuild "$ebuild_path" package 1>/dev/null package_generation_return_code=$? } || true fi if [ $package_generation_return_code -ne 0 ]; then error_package_generation_failed "$package_name" return 1 fi mkdir --parents "$generated_package_directory" mv "${PLAYIT_WORKDIR}/gentoo-pkgdir/games-playit/${package_name}" "$generated_package_path" rm --recursive "${PLAYIT_WORKDIR}/portage-tmpdir" } # Print the file name of the given package # USAGE: package_name_gentoo $package # RETURNS: the file name, as a string package_name_gentoo() { local package package="$1" local package_id package_version package_name package_id=$(package_id "$package") package_version=$(package_version) package_name="${package_id}-${package_version}.tbz2" # Avoid paths collisions when building multiple architecture variants for a same package local packages_list current_package current_package_id packages_list=$(packages_list) for current_package in $packages_list; do current_package_id=$(package_id "$current_package") if \ [ "$current_package" != "$package" ] \ && [ "$current_package_id" = "$package_id" ] then local package_architecture package_architecture=$(gentoo_package_architecture_string "$package") package_name="${package_architecture}/${package_name}" break fi done printf '%s' "$package_name" } # Get the path to the directory where the given package is prepared, # relative to the directory where all packages are stored # USAGE: package_path_gentoo $package # RETURNS: relative path to a directory, as a string package_path_gentoo() { local package package="$1" local package_name package_path package_name=$(package_name "$package") package_path="${package_name%.tbz2}" printf '%s' "$package_path" } # Tweak the given package version string to ensure it is compatible with portage # USAGE: gentoo_package_version $package_version # RETURNS: the package version, as a non-empty string gentoo_package_version() { local package_version package_version="$1" set +o errexit package_version=$( printf '%s' "$package_version" | \ grep --extended-regexp --only-matching '^([0-9]{1,18})(\.[0-9]{1,18})*[a-z]?' ) set -o errexit if [ -z "$package_version" ]; then package_version='1.0' fi local script_version_string script_version_string=$(script_version) printf '%s_p%s' "$package_version" "$(printf '%s' "$script_version_string" | sed 's/\.//g')" } # Print the architecture string of the given package, in the format expected by portage # USAGE: gentoo_package_architecture_string $package # RETURNS: the package architecture, as one of the following values: # - x86 # - amd64 # - data (dummy value) gentoo_package_architecture_string() { local package package="$1" local package_architecture package_architecture_string package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') package_architecture_string='x86' ;; ('64') package_architecture_string='amd64' ;; ('all') # We could put anything here, it should not be used for package metadata. package_architecture_string='data' ;; esac printf '%s' "$package_architecture_string" } # Tweak the given package id to ensure compatibility with portage # USAGE: gentoo_package_id $package_id # RETURNS: the package id, as a non-empty string gentoo_package_id() { local package_id package_id="$1" # Avoid mixups between numbers in package id and version number. printf '%s' "$package_id" | sed 's/-/_/g' } src/60_system_gentoo/15_metadata-fields_egentoo-variant.sh0000644000000000000000000000430613120060140022604 0ustar rootroot# Gentoo ("egentoo" variant) - Print the content of the "KEYWORDS" field # USAGE: egentoo_field_keywords $package… # RETURN: the field value egentoo_field_keywords() { local keywords package package_architecture keywords='x86 amd64' for package in "$@"; do package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') printf '%s' '-* x86 amd64' return 0 ;; ('64') keywords='-* amd64' ;; esac done printf '%s' "$keywords" } # Gentoo ("egentoo" variant) - Print the content of the "SRC_URI" field # USAGE: egentoo_field_srcuri $package… # RETURN: the field value egentoo_field_srcuri() { ## The list of packages is ignored by this function, ## it is only passed for consistency with other egentoo_field_* functions. package_filename="$(egentoo_package_name).tar" local option_compression option_compression=$(option_value 'compression') case $option_compression in ('speed') package_filename="${package_filename}.gz" ;; ('size') package_filename="${package_filename}.bz2" ;; esac printf '%s' "$package_filename" } # Gentoo ("egentoo" variant) - Print the content of the "RDEPEND" field # USAGE: egentoo_field_rdepend $package… # RETURN: the field value egentoo_field_rdepend() { local package package_architecture package_64bits package_32bits package_data package_64bits='' package_32bits='' package_data='' for package in "$@"; do package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') package_32bits="$package" ;; ('64') package_64bits="$package" ;; (*) package_data="$package" ;; esac done if [ -n "$package_64bits" ]; then gentoo_field_rdepend "$package_64bits" elif [ -n "$package_32bits" ]; then gentoo_field_rdepend "$package_32bits" elif [ -n "$package_data" ]; then gentoo_field_rdepend "$package_data" fi } # Gentoo ("egentoo" variant) - Print the content of the "BDEPEND" field # USAGE: egentoo_field_bdepend $package… # RETURN: the field value egentoo_field_bdepend() { local field_bdepend ## FIXME: $build_deps is not set anywhere, so this field will always be empty. field_bdepend=${build_deps:-} printf '%s' "$field_bdepend" } src/60_system_gentoo/15_metadata-fields_gentoo-variant.sh0000644000000000000000000000701413120060140022436 0ustar rootroot# Gentoo - Print the content of the "KEYWORDS" field # USAGE: gentoo_field_keywords $package # RETURN: the field value gentoo_field_keywords() { local package package="$1" local package_architecture ebuild_keywords package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') ebuild_keywords='-* x86 amd64' ;; ('64') ebuild_keywords='-* amd64' ;; (*) ebuild_keywords='x86 amd64' # data packages ;; esac printf '%s' "$ebuild_keywords" } # Gentoo - Print the content of the "DESCRIPTION" field # USAGE: gentoo_field_description $package # RETURN: the field value gentoo_field_description() { local package package="$1" local game_name package_description script_version_string game_name=$(game_name) package_description=$(package_description "$package") script_version_string=$(script_version) printf '%s' "$game_name" if [ -n "$package_description" ]; then printf -- ' - %s' "$package_description" fi printf -- ' - ./play.it script version %s' "$script_version_string" } # Gentoo - Print the content of the "RDEPEND" field # USAGE: gentoo_field_rdepend $package # RETURN: the field value gentoo_field_rdepend() { local package package="$1" local dependencies_list first_item_displayed dependency_string dependencies_list=$(dependencies_gentoo_full_list "$package") first_item_displayed=0 while IFS= read -r dependency_string; do if [ -z "$dependency_string" ]; then continue fi # Gentoo policy is that dependencies should be displayed one per line, # and indentation is to be done using tabulations. if [ "$first_item_displayed" -eq 0 ]; then printf '%s' "$dependency_string" first_item_displayed=1 else printf '\n\t%s' "$dependency_string" fi done <<- EOL $(printf '%s' "$dependencies_list") EOL # Return early when building package in the "egentoo" format, # as we have no good way to set conflicts with this format. local option_package option_package=$(option_value 'package') if [ "$option_package" = 'egentoo' ]; then return 0 fi local package_conflicts package_conflict package_conflicts=$(package_provides "$package") # Return early if the current package has no "provides" field. if [ -z "$package_conflicts" ]; then return 0 fi # Gentoo has no notion of "provided" package, # so we need to loop over all supported archives # to get the name of all packages providing a given package id. local archives_list packages_list archives_list=$(archives_return_list) packages_list=$( for archive in $archives_list; do set_current_archive "$archive" packages_list done | list_clean ) # For each conflict of the current package, # find all potential packages that would provide this package id. local package_current package_current_id package_current_provides package_current_provide for package_conflict in $package_conflicts; do for package_current in $packages_list; do # Skip the package we are writing metadata for, # so it does not end up conflicting with itself. if [ "$package_current" = "$package" ]; then continue fi package_current_provides=$(package_provides "$package_current") for package_current_provide in $package_current_provides; do if [ "$package_current_provide" = "$package_conflict" ]; then package_current_id=$(package_id "$package_current") if [ "$first_item_displayed" -eq 0 ]; then printf '!games-play.it/%s' "$package_current_id" first_item_displayed=1 else printf '\n\t!games-play.it/%s' "$package_current_id" fi fi done done done } src/60_system_gentoo/15_package-scripts.sh0000644000000000000000000000241613120060140017456 0ustar rootroot# Gentoo - Print the pkg_postinst function # USAGE: gentoo_script_postinst $package # RETURN: the defintion of the pkg_postinst function, # spanning over several lines gentoo_script_postinst() { local package package="$1" local postinst_actions postinst_warnings postinst_actions=$(package_postinst_actions "$package") postinst_warnings=$(package_postinst_warnings "$package") if [ -n "$postinst_actions" ] || [ -n "$postinst_warnings" ]; then cat <<- EOF pkg_postinst() { EOF # Include actions that should be run. if [ -n "$postinst_actions" ]; then printf '%s\n' "$postinst_actions" fi # Include warnings that should be displayed. if [ -n "$postinst_warnings" ]; then local warning_line while read -r warning_line; do printf 'ewarn "%s"\n' "$warning_line" done <<- EOL $(printf '%s' "$postinst_warnings") EOL fi cat <<- EOF } EOF fi } # Gentoo - Print the pkg_prerm function # USAGE: gentoo_script_prerm $package # RETURN: the defintion of the pkg_prerm function, # spanning over several lines gentoo_script_prerm() { local package package="$1" local prerm_actions prerm_actions=$(package_prerm_actions "$package") if [ -n "$prerm_actions" ]; then cat <<- EOF pkg_prerm() { $prerm_actions } EOF fi } src/60_system_gentoo/20_dependencies.sh0000644000000000000000000001517513120060140017026 0ustar rootroot# Gentoo - Set list of generic dependencies # USAGE: pkg_set_deps_gentoo $package $dep[…] pkg_set_deps_gentoo() { local package package="$1" shift local package_architecture architecture_suffix package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') architecture_suffix='[abi_x86_32]' ;; ('64') architecture_suffix='' ;; esac local pkg_dep for dep in "$@"; do pkg_dep='' case $dep in ('alsa') pkg_dep="media-libs/alsa-lib$architecture_suffix media-plugins/alsa-plugins$architecture_suffix" ;; ('freetype') pkg_dep="media-libs/freetype$architecture_suffix" ;; ('gcc32') pkg_dep='' #gcc (in @system) should be multilib unless it is a no-multilib profile, in which case the 32 bits libraries wouldn't work ;; ('glibc') pkg_dep="sys-libs/glibc" if [ "$package_architecture" = '32' ]; then pkg_dep="$pkg_dep amd64? ( sys-libs/glibc[multilib] )" fi ;; ('glu') pkg_dep="virtual/glu$architecture_suffix" ;; ('glx') pkg_dep="virtual/opengl$architecture_suffix" ;; ('gtk2') pkg_dep="x11-libs/gtk+:2$architecture_suffix" ;; ('json') pkg_dep="dev-libs/json-c$architecture_suffix" ;; ('libstdc++') pkg_dep='' #maybe this should be virtual/libstdc++, otherwise, it is included in gcc, which should be in @system ;; ('libudev1') pkg_dep="virtual/libudev$architecture_suffix" ;; ('libxrandr') pkg_dep="x11-libs/libXrandr$architecture_suffix" ;; ('nss') pkg_dep="dev-libs/nss$architecture_suffix" ;; ('openal') pkg_dep="media-libs/openal$architecture_suffix" ;; ('sdl2') pkg_dep="media-libs/libsdl2$architecture_suffix" ;; ('xcursor') pkg_dep="x11-libs/libXcursor$architecture_suffix" ;; ( \ 'dosbox' | \ 'java' | \ 'mono' | \ 'pulseaudio' | \ 'scummvm' | \ 'wine' | \ 'winetricks' | \ 'xgamma' | \ 'xrandr' \ ) gentoo_dependencies_single_command "$package" "$dependency_keyword" ;; ( \ 'libasound.so.2' | \ 'libasound_module_'*'.so' | \ 'libc.so.6' | \ 'libgdk_pixbuf-2.0.so.0' | \ 'libgdk-x11-2.0.so.0' | \ 'libGL.so.1' | \ 'libglib-2.0.so.0' | \ 'libGLU.so.1' | \ 'libgobject-2.0.so.0' | \ 'libgtk-x11-2.0.so.0' | \ 'libmbedtls.so.12' | \ 'libpng16.so.16' | \ 'libpulse.so.0' | \ 'libpulse-simple.so.0' | \ 'libopenal.so.1' | \ 'libSDL-1.2.so.0' | \ 'libSDL2-2.0.so.0' | \ 'libstdc++.so.6' | \ 'libturbojpeg.so.0' | \ 'libuv.so.1' | \ 'libudev.so.1' | \ 'libvorbisfile.so.3' | \ 'libX11.so.6' | \ 'libz.so.1' \ ) case "$package_architecture" in ('32') pkg_dep=$(dependency_package_providing_library_gentoo32 "$dep" "$package") ;; (*) pkg_dep=$(dependency_package_providing_library_gentoo "$dep" "$package") ;; esac ;; esac if [ -n "$pkg_dep" ]; then if variable_is_empty 'pkg_deps'; then pkg_deps="$pkg_dep" else pkg_deps="$pkg_deps $pkg_dep" fi fi done } # Gentoo - List all dependencies for the given package # USAGE: dependencies_gentoo_full_list $package dependencies_gentoo_full_list() { local package package="$1" # Include generic dependencies local dependencies_generic dependency_generic dependencies_generic=$(dependencies_list_generic "$package") while read -r dependency_generic; do # pkg_set_deps_gentoo sets a variable $pkg_deps instead of printing a value, # we prevent it from leaking by setting it to an empty value. pkg_deps='' pkg_set_deps_gentoo $package $dependency_generic printf '%s\n' "$pkg_deps" done <<- EOL $(printf '%s' "$dependencies_generic") EOL local packages_list packages_list_full packages_list_full='' # Include dependencies on sibling package (not when building "egentoo" merged packages) local option_package option_package=$(option_value 'package') case "$option_package" in ('gentoo') packages_list=$(gentoo_dependencies_siblings "$package") packages_list_full="$packages_list_full $packages_list" ;; esac # Include dependencies on commands packages_list=$(gentoo_dependencies_all_commands "$package") packages_list_full="$packages_list_full $packages_list" # Include dependencies on native libraries packages_list=$(dependencies_list_native_libraries_packages "$package") packages_list_full="$packages_list_full $packages_list" # Include dependencies on Mono libraries packages_list=$(dependencies_list_mono_libraries_packages "$package") packages_list_full="$packages_list_full $packages_list" # Include dependencies on GStreamer plugins packages_list=$(gentoo_dependencies_gstreamer_all_formats "$package") packages_list_full="$packages_list_full $packages_list" printf '%s' "$packages_list_full" | list_clean } # Gentoo - Print the path to a temporary file used for additional overlays listing # USAGE: dependency_gentoo_overlays_file dependency_gentoo_overlays_file() { printf '%s/overlays' "$PLAYIT_WORKDIR" } # Gentoo - Add an overlay to the list of additional overlays # USAGE: dependency_gentoo_overlays_add $overlay dependency_gentoo_overlays_add() { local overlay overlays_file overlay="$1" overlays_file="$(dependency_gentoo_overlays_file)" # Do nothing if this overlay is already included in the list if test -e "$overlays_file" \ && grep --quiet --fixed-strings --word-regexp "$overlay" < "$overlays_file" then return 0 fi printf '%s\n' "$overlay" >> "$overlays_file" } # Gentoo - Print gentoo libdir name # USAGE: dependency_gentoo_libdir $arch_string # Note: This prints the name (ie. “lib”) not the path (ie. “/usr/lib”) dependency_gentoo_libdir() { local arch_string="$1" if command -v portageq >/dev/null 2>&1; then print '%s' "$(portageq envvar "LIBDIR_${arch_string}")" else case "$arch_string" in ('amd64') printf '%s' 'lib64' ;; ('x86') printf '%s' 'lib' ;; ('x32') printf '%s' 'libx32' ;; (*) error_unknown_gentoo_architecture_string "$arch_string" 'dependency_gentoo_libdir' return 1 ;; esac fi return 0 } # Gentoo - Link library installed in non-standard libdir to the game’s libdir # USAGE: dependencies_gentoo_link $libname $libdir $package # Note: $libdir is the library’s directory, not the game’s one! dependencies_gentoo_link() { local libname libdir package game_libdir libname="$1" libdir="$2" package="$3" game_libdir=$( set_current_package "$package" path_libraries ) local package_path library_destination package_path=$(package_path "$package") library_destination="${package_path}${game_libdir}" mkdir --parents "$library_destination" ln -sft "$library_destination" "${libdir}/${libname}" } src/60_system_gentoo/25_dependencies_commands.sh0000644000000000000000000000556413120060140020715 0ustar rootroot# Gentoo - Print the package names providing the commands required by the given package # USAGE: gentoo_dependencies_all_commands $package # RETURN: a list of Gentoo package names, # one per line gentoo_dependencies_all_commands() { local package package="$1" local required_commands required_commands=$(dependencies_list_commands "$package") # Return early if the current package does not require any command if [ -z "$required_commands" ]; then return 0 fi local command packages_list required_packages packages_list='' while read -r command; do required_packages=$(gentoo_dependencies_single_command "$package" "$command") packages_list="$packages_list $required_packages" done <<- EOL $(printf '%s' "$required_commands") EOL printf '%s' "$packages_list" | list_clean } # Gentoo - Print the package names providing the required command # USAGE: gentoo_dependencies_single_command $package $required_command # RETURN: a list of Gentoo package names, # one per line gentoo_dependencies_single_command() { local package required_command package="$1" required_command="$2" local package_names case "$required_command" in ('corsix-th') package_names=' games-simulation/corsix-th' ;; ('dos2unix') package_names=' app-text/dos2unix' ;; ('dosbox') package_names=' games-emulation/dosbox' ;; ('java') package_names=' virtual/jre' ;; ('mono') package_names=' dev-lang/mono' ;; ('mpv') package_names=' media-video/mpv' ;; ('openmw-iniimporter') package_names=' games-engines/openmw' ;; ('openmw-launcher') package_names=' games-engines/openmw' ;; ('pulseaudio') package_names=' media-sound/pulseaudio' ;; ('renpy') package_names=' games-engines/renpy' ;; ('scummvm') package_names=' games-engines/scummvm[mp3,truetype,opengl,vorbis,theora]' ;; ('sed') package_names=' sys-apps/sed' ;; ('setxkbmap') package_names=' x11-apps/setxkbmap' ;; ('vcmilauncher') dependency_gentoo_overlays_add 'https://cgit.gentoo.org/proj/gamerlay.git' package_names=' games-strategy/vcmi' ;; ('wine') local package_architecture package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') package_names=' virtual/wine[abi_x86_32]' ;; ('64') package_names=' virtual/wine[abi_x86_64]' ;; esac ;; ('winetricks') ## TODO - Add an OR dependency on one of these packages: ## - x11-terms/xterm ## - gnome-extra/zenity ## - kde-apps/kdialog ## This dependency must be set on a single line. package_names=' app-emulation/winetricks' ;; ('xgamma') package_names=' x11-apps/xgamma' ;; ('xrandr') package_names=' x11-apps/xrandr' ;; (*) dependencies_unknown_command_add "$required_command" return 0 ;; esac printf '%s' "$package_names" } src/60_system_gentoo/25_dependencies_gstreamer-plugins.sh0000644000000000000000000001135213120060140022554 0ustar rootroot# Gentoo - Print the package names providing the GStreamer plugins to decode the formats required by the given package # USAGE: gentoo_dependencies_gstreamer_all_formats $package # RETURN: a list of Gentoo package names, # one per line gentoo_dependencies_gstreamer_all_formats() { local package package="$1" local gstreamer_decoders gstreamer_decoders=$(dependencies_list_gstreamer_decoders "$package") # Return early if the current package does not require any GStreamer plugin if [ -z "$gstreamer_decoders" ]; then return 0 fi local package_architecture command_dependencies_for_single_format package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') command_dependencies_for_single_format='gentoo_dependencies_gstreamer_single_format_32bit' ;; (*) command_dependencies_for_single_format='gentoo_dependencies_gstreamer_single_format' ;; esac local media_format packages_list required_packages packages_list='' while read -r media_format; do required_packages=$("$command_dependencies_for_single_format" "$media_format") packages_list="$packages_list $required_packages" done <<- EOL $(printf '%s' "$gstreamer_decoders") EOL printf '%s' "$packages_list" | list_clean } # Gentoo - Print the package names providing the required GStreamer plugins to decode the given format # USAGE: gentoo_dependency_providing_gstreamer_plugin $media_format # RETURN: a list of Gentoo package names, # one per line gentoo_dependencies_gstreamer_single_format() { local media_format media_format="$1" local package_names case "$media_format" in ('audioconvert') package_names=' media-libs/gst-plugins-base' ;; ('avidemux') package_names=' media-libs/gst-plugins-good' ;; ('decodebin') package_names=' media-libs/gst-plugins-base' ;; ('deinterlace') package_names=' media-libs/gst-plugins-good' ;; ('application/x-id3') package_names=' media-libs/gst-plugins-good' ;; ('audio/mpeg, mpegversion=(int)1, layer=(int)3') package_names=' media-libs/gst-plugins-good' ;; ('audio/x-wma, wmaversion=(int)1') package_names=' media-plugins/gst-plugins-libav' ;; ('video/mpeg, systemstream=(boolean)true, mpegversion=(int)1') package_names=' media-libs/gst-plugins-ugly media-libs/gst-plugins-bad' ;; ('video/quicktime, variant=(string)iso') package_names=' media-libs/gst-plugins-good media-plugins/gst-plugins-libav' ;; ('video/x-ms-asf') package_names=' media-libs/gst-plugins-ugly media-plugins/gst-plugins-libav' ;; ('video/x-msvideo') package_names=' media-libs/gst-plugins-good media-plugins/gst-plugins-libav' ;; ('video/x-wmv, wmvversion=(int)1') package_names=' media-plugins/gst-plugins-libav' ;; (*) dependencies_unknown_gstreamer_media_formats_add "$media_format" return 0 ;; esac printf '%s' "$package_names" } # Gentoo - Print the package names providing the required GStreamer plugins to decode the given format (32-bit) # USAGE: gentoo_dependency_providing_gstreamer_plugin_32bit $media_format # RETURN: a list of Gentoo package names, # one per line gentoo_dependencies_gstreamer_single_format_32bit() { local media_format media_format="$1" local package_names case "$media_format" in ('audioconvert') package_names=' media-libs/gst-plugins-base[abi_x86_32]' ;; ('avidemux') package_names=' media-libs/gst-plugins-good[abi_x86_32]' ;; ('decodebin') package_names=' media-libs/gst-plugins-base[abi_x86_32]' ;; ('deinterlace') package_names=' media-libs/gst-plugins-good[abi_x86_32]' ;; ('application/x-id3') package_names=' media-libs/gst-plugins-good[abi_x86_32]' ;; ('audio/mpeg, mpegversion=(int)1, layer=(int)3') package_names=' media-libs/gst-plugins-good[abi_x86_32]' ;; ('audio/x-wma, wmaversion=(int)1') package_names=' media-plugins/gst-plugins-libav[abi_x86_32]' ;; ('video/mpeg, systemstream=(boolean)true, mpegversion=(int)1') package_names=' media-libs/gst-plugins-ugly[abi_x86_32] media-libs/gst-plugins-bad[abi_x86_32]' ;; ('video/quicktime, variant=(string)iso') package_names=' media-libs/gst-plugins-good[abi_x86_32] media-plugins/gst-plugins-libav[abi_x86_32]' ;; ('video/x-ms-asf') package_names=' media-libs/gst-plugins-ugly[abi_x86_32] media-plugins/gst-plugins-libav[abi_x86_32]' ;; ('video/x-msvideo') package_names=' media-libs/gst-plugins-good[abi_x86_32] media-plugins/gst-plugins-libav[abi_x86_32]' ;; ('video/x-wmv, wmvversion=(int)1') package_names=' media-plugins/gst-plugins-libav[abi_x86_32]' ;; (*) dependencies_unknown_gstreamer_media_formats_add "$media_format" return 0 ;; esac printf '%s' "$package_names" } src/60_system_gentoo/25_dependencies_native-libraries.sh0000644000000000000000000006640113120060140022351 0ustar rootroot# Gentoo - Print the package names providing the given native libraries # USAGE: gentoo_dependencies_providing_native_libraries $package $library[…] # RETURN: a list of Gentoo package names, # one per line gentoo_dependencies_providing_native_libraries() { local package package="$1" shift 1 local library native_packages_list native_package native_packages_list='' for library in "$@"; do native_package=$(dependency_package_providing_library_gentoo "$library" "$package") native_packages_list="$native_packages_list $native_package" done printf '%s' "$native_packages_list" | list_clean } # Gentoo - Print the package names providing the given native libraries in a 32-bit build # USAGE: gentoo_dependencies_providing_native_libraries_32bit $package $library[…] # RETURN: a list of Gentoo package names, # one per line gentoo_dependencies_providing_native_libraries_32bit() { local package package="$1" shift 1 local library native_packages_list native_package native_packages_list='' for library in "$@"; do native_package=$(dependency_package_providing_library_gentoo32 "$library" "$package") native_packages_list="$native_packages_list $native_package" done printf '%s' "$native_packages_list" | list_clean } # Gentoo - Print the package name providing the given native library # USAGE: dependency_package_providing_library_gentoo $library $package dependency_package_providing_library_gentoo() { local library package package_name pkg_overlay library="$1" package="$2" case "$library" in ('ld-linux.so.2') package_name='sys-libs/glibc' ;; ('ld-linux-x86-64.so.2') package_name='sys-libs/glibc' ;; ('liballeg.so.4.4') package_name='media-libs/allegro' ;; ('liballegro.so.5.2') package_name='media-libs/allegro' ;; ('liballegro_acodec.so.5.2') package_name='media-libs/allegro' ;; ('liballegro_audio.so.5.2') package_name='media-libs/allegro' ;; ('liballegro_font.so.5.2') package_name='media-libs/allegro' ;; ('liballegro_image.so.5.2') package_name='media-libs/allegro' ;; ('liballegro_primitives.so.5.2') package_name='media-libs/allegro' ;; ('liballegro_ttf.so.5.2') package_name='media-libs/allegro' ;; ('libalut.so.0') package_name='media-libs/freealut' ;; ('libasound.so.2') package_name='media-libs/alsa-lib' ;; ('libasound_module_'*'.so') package_name='media-plugins/alsa-plugins' ;; ('libatspi.so.0') package_name='app-accessibility/at-spi2-core' ;; ('libatk-1.0.so.0') package_name='dev-libs/atk' ;; ('libaudio.so.2') package_name='media-libs/nas' ;; ('libboost_locale.so.1.74.0') # This library is not provided for Gentoo unset package_name ;; ('libbz2.so.1.0'|'libbz2.so.1') package_name='app-arch/bzip2' ;; ('libc.so.6') package_name='sys-libs/glibc' ;; ('libc++.so.1') package_name='sys-libs/libcxx' ;; ('libc++abi.so.1') package_name='sys-libs/libcxxabi' ;; ('libcairo.so.2') package_name='x11-libs/cairo' ;; ('libCg.so') # This library is not provided for Gentoo unset package_name ;; ('libCgGL.so') # This library is not provided for Gentoo unset package_name ;; ('libcom_err.so.2') package_name='sys-libs/e2fsprogs-libs' ;; ('libcrypt.so.1') package_name='sys-libs/libxcrypt' ;; ('libcups.so.2') package_name='net-print/cups' ;; ('libcurl.so.4') package_name='net-misc/curl' ;; ('libcurl.so.4+CURL_OPENSSL_3') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/curl/ return 0 ;; ('libcurl-gnutls.so.4') package_name='net-libs/libcurl-debian' pkg_overlay='steam-overlay' dependencies_gentoo_link 'libcurl-gnutls.so.4' "/usr/$(dependency_gentoo_libdir 'amd64')/debiancompat" "$package" ;; ('libdbus-1.so.3') package_name='sys-apps/dbus' ;; ('libdbus-glib-1.so.2') package_name='dev-libs/dbus-glib' ;; ('libdl.so.2') package_name='sys-libs/glibc' ;; ('libEGL.so.1') package_name='media-libs/libglvnd' ;; ('libexpat.so.1') package_name='dev-libs/expat' ;; ('libFAudio.so.0') package_name='app-emulation/faudio' ;; ('libFLAC.so.8') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/flac/ return 0 ;; ('libfontconfig.so.1') package_name='media-libs/fontconfig' ;; ('libfreeimage.so.3') package_name='media-libs/freeimage' ;; ('libfreetype.so.6') package_name='media-libs/freetype' ;; ('libfribidi.so.0') package_name='dev-libs/fribidi' ;; ('libgcc_s.so.1') package_name='sys-devel/gcc' ;; ('libgconf-2.so.4') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/gconf/ return 0 ;; ('libgcrypt.so.11') package_name='dev-libs/libgcrypt-compat' ;; ('libgdiplus.so') package_name='dev-dotnet/libgdiplus' ;; ('libgdk-3.so.0') package_name='x11-libs/gtk+:3' ;; ('libgdk_pixbuf-2.0.so.0') package_name='x11-libs/gdk-pixbuf:2' ;; ('libgdk-x11-2.0.so.0') package_name='x11-libs/gtk+:2' ;; ('libgio-2.0.so.0') package_name='dev-libs/glib:2' ;; ('libGL.so.1') package_name='virtual/opengl' ;; ('libGLEW.so.2.2') package_name='media-libs/glew' ;; ('libglfw.so.3') package_name='media-libs/glfw' ;; ('libglib-2.0.so.0') package_name='dev-libs/glib:2' ;; ('libGLU.so.1') package_name='virtual/glu' ;; ('libGLX.so.0') package_name='media-libs/libglvnd' ;; ('libgmodule-2.0.so.0') package_name='dev-libs/glib:2' ;; ('libgobject-2.0.so.0') package_name='dev-libs/glib:2' ;; ('libgomp.so.1') package_name='sys-devel/gcc' ;; ('libgpg-error.so.0') package_name='dev-libs/libgpg-error' ;; ('libgssapi_krb5.so.2') package_name='app-crypt/mit-krb5' ;; ('libgthread-2.0.so.0') package_name='dev-libs/glib:2' ;; ('libgtk-x11-2.0.so.0') package_name='x11-libs/gtk+:2' ;; ('libgtk-3.so.0') package_name='x11-libs/gtk+:3' ;; ('libICE.so.6') package_name='x11-libs/libICE' ;; ('libidn.so.11') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/libidn/ return 0 ;; ('libidn2.so.0') package_name='net-dns/libidn2' ;; ('libIL.so.1') package_name='media-libs/devil' ;; ('libjpeg.so.62') package_name='media-libs/libjpeg-turbo' ;; ('libk5crypto.so.3') package_name='app-crypt/mit-krb5' ;; ('libkrb5.so.3') package_name='app-crypt/mit-krb5' ;; ('liblcms2.so.2') package_name='media-libs/lcms' ;; ('liblua5.3.so.0') package_name='dev-lang/lua' ;; ('libluajit-5.1.so.2') package_name='dev-lang/luajit' ;; ('liblz4.so.1') package_name='app-arch/lz4' ;; ('libm.so.6') package_name='sys-libs/glibc' ;; ('libmbedtls.so.12') package_name='net-libs/mbedtls:0/12' ;; ('libminiupnpc.so.17') package_name='net-libs/miniupnpc' ;; ('libminizip.so.1') package_name='sys-libs/zlib' ;; ('libmodplug.so.1') package_name='media-libs/libmodplug' ;; ('libmpg123.so.0') package_name='media-sound/mpg123' ;; ('libnghttp2.so.14') package_name='net-libs/nghttp2' ;; ('libnotify.so.4') package_name='x11-libs/libnotify' ;; ('libnspr4.so') package_name='dev-libs/nspr' ;; ('libnss3.so') package_name='dev-libs/nss' ;; ('libnssutil3.so') package_name='dev-libs/nss' ;; ('libogg.so.0') package_name='media-libs/libogg' ;; ('libopenal.so.1') package_name='media-libs/openal' ;; ('libOpenGL.so.0') package_name='media-libs/libglvnd' ;; ('libopenmpt.so.0') package_name='media-libs/libopenmpt' ;; ('libpango-1.0.so.0') package_name='x11-libs/pango' ;; ('libpangocairo-1.0.so.0') package_name='x11-libs/pango' ;; ('libpangoft2-1.0.so.0') package_name='x11-libs/pango' ;; ('libpcre.so.3') package_name='dev-libs/libpcre-debian' ;; ('libphysfs.so.1') package_name='dev-games/physfs' ;; ('libpixman-1.so.0') package_name='x11-libs/pixman' ;; ('libplc4.so') package_name='dev-libs/nspr' ;; ('libplds4.so') package_name='dev-libs/nspr' ;; ('libpng12.so.0') package_name='media-libs/libpng-compat:1.2' ;; ('libpng16.so.16') package_name='media-libs/libpng:0/16' ;; ('libpsl.so.5') package_name='net-libs/libpsl' ;; ('libpthread.so.0') package_name='sys-libs/glibc' ;; ('libpulse.so.0') package_name='media-sound/pulseaudio' ;; ('libpulse-simple.so.0') package_name='media-sound/pulseaudio' ;; ('libresolv.so.2') package_name='sys-libs/glibc' ;; ('librt.so.1') package_name='sys-libs/glibc' ;; ('librtmp.so.1') package_name='media-video/rtmpdump' ;; ('libSDL-1.2.so.0') package_name='media-libs/libsdl[opengl]' ;; ('libSDL_image-1.2.so.0') package_name='media-libs/sdl-image' ;; ('libSDL_kitchensink.so.1') # This library is not provided for Gentoo unset package_name ;; ('libSDL_mixer-1.2.so.0') package_name='media-libs/sdl-mixer' ;; ('libSDL_sound-1.0.so.1') package_name='media-libs/sdl-sound' ;; ('libSDL_ttf-2.0.so.0') package_name='media-libs/sdl-ttf' ;; ('libSDL2-2.0.so.0') package_name='media-libs/libsdl2[opengl]' ;; ('libSDL2_image-2.0.so.0') # Most games will require at least jpeg and png # Maybe we should add gif and tiff to that list? package_name='media-libs/sdl2-image[jpeg,png]' ;; ('libSDL2_mixer-2.0.so.0') # Most games will require at least one of flac, mp3, vorbis or wav USE flags, # it should better to require them all instead of not requiring any # and having non-fonctionnal sound in some games. package_name='media-libs/sdl2-mixer[flac,mp3,vorbis,wav]' ;; ('libSDL2_ttf-2.0.so.0') package_name='media-libs/sdl2-ttf' ;; ('libsecret-1.so.0') package_name='app-crypt/libsecret' ;; ('libsigc-2.0.so.0') package_name='dev-libs/libsigc++' ;; ('libSM.so.6') package_name='x11-libs/libSM' ;; ('libsmime3.so') package_name='dev-libs/nss' ;; ('libsmpeg-0.4.so.0') package_name='media-libs/smpeg' ;; ('libsodium.so.23') package_name='dev-libs/libsodium' ;; ('libssh2.so.1') package_name='net-libs/libssh2' ;; ('libssl.so.1.0.0') package_name='dev-libs/openssl-compat:1.0.0' ;; ('libssl.so.1.1') package_name='dev-libs/openssl-compat:1.1.1' ;; ('libssl3.so') package_name='dev-libs/nss' ;; ('libstdc++.so.5') package_name='sys-libs/libstdc++-v3' ;; ('libstdc++.so.6') package_name='sys-devel/gcc' ;; ('libtcmalloc_minimal.so.4') package_name='dev-util/google-perftools' ;; ('libtheora.so.0') package_name='media-libs/libtheora' ;; ('libtheoradec.so.1') package_name='media-libs/libtheora' ;; ('libtheoraenc.so.1') package_name='media-libs/libtheora' ;; ('libthread_db.so.1') package_name='sys-libs/glibc' ;; ('libtiff.so.6') package_name='media-libs/tiff' ;; ('libturbojpeg.so.0') package_name='media-libs/libjpeg-turbo' ;; ('libudev.so.0') package_name='sys-libs/libudev-compat' ;; ('libudev.so.1') package_name='virtual/libudev' ;; ('libutil.so.1') package_name='sys-libs/glibc' ;; ('libuuid.so.1') package_name='sys-apps/util-linux' ;; ('libuv.so.1') package_name='dev-libs/libuv:0/1' ;; ('libvorbis.so.0') package_name='media-libs/libvorbis' ;; ('libvorbisenc.so.2') package_name='media-libs/libvorbis' ;; ('libvorbisfile.so.3') package_name='media-libs/libvorbis' ;; ('libvulkan.so.1') package_name='media-libs/vulkan-loader' ;; ('libwayland-client.so.0') package_name='dev-libs/wayland' ;; ('libX11.so.6') package_name='x11-libs/libX11' ;; ('libX11-xcb.so.1') package_name='x11-libs/libX11' ;; ('libxcb.so.1') package_name='x11-libs/libxcb' ;; ('libxcb-randr.so.0') package_name='x11-libs/libxcb' ;; ('libXcomposite.so.1') package_name='x11-libs/libXcomposite' ;; ('libXcursor.so.1') package_name='x11-libs/libXcursor' ;; ('libXdamage.so.1') package_name='x11-libs/libXdamage' ;; ('libXext.so.6') package_name='x11-libs/libXext' ;; ('libXfixes.so.3') package_name='x11-libs/libXfixes' ;; ('libXft.so.2') package_name='x11-libs/libXft' ;; ('libXi.so.6') package_name='x11-libs/libXi' ;; ('libXinerama.so.1') package_name='x11-libs/libXinerama' ;; ('libxml2.so.2') package_name='dev-libs/libxml2' ;; ('libxmp.so.4') package_name='media-libs/libxmp' ;; ('libXmu.so.6') package_name='x11-libs/libXmu' ;; ('libXrandr.so.2') package_name='x11-libs/libXrandr' ;; ('libXrender.so.1') package_name='x11-libs/libXrender' ;; ('libxslt.so.1') package_name='dev-libs/libxslt' ;; ('libXss.so.1') package_name='x11-libs/libXScrnSaver' ;; ('libXt.so.6') package_name='x11-libs/libXt' ;; ('libXtst.so.6') package_name='x11-libs/libXtst' ;; ('libXxf86vm.so.1') package_name='x11-libs/libXxf86vm' ;; ('libyaml-0.so.2') package_name='dev-libs/libyaml' ;; ('libz.so.1') package_name='sys-libs/zlib:0/1' ;; esac if [ -n "${package_name:-}" ]; then printf '%s' "$package_name" if [ -n "${pkg_overlay:-}" ]; then dependency_gentoo_overlays_add "$pkg_overlay" fi return 0 fi dependencies_unknown_libraries_add "$library" } # Gentoo - Print the package name providing the given native library in a 32-bit build # USAGE: dependency_package_providing_library_gentoo32 $library $package dependency_package_providing_library_gentoo32() { local library package package_name pkg_overlay library="$1" package="$2" case "$library" in ('ld-linux.so.2') package_name='sys-libs/glibc amd64? ( sys-libs/glibc[multilib] )' ;; ('ld-linux-x86-64.so.2') package_name='sys-libs/glibc amd64? ( sys-libs/glibc[multilib] )' ;; ('liballeg.so.4.4') package_name='media-libs/allegro[abi_x86_32]' ;; ('liballegro.so.5.2') package_name='media-libs/allegro[abi_x86_32]' ;; ('liballegro_acodec.so.5.2') package_name='media-libs/allegro[abi_x86_32]' ;; ('liballegro_audio.so.5.2') package_name='media-libs/allegro[abi_x86_32]' ;; ('liballegro_font.so.5.2') package_name='media-libs/allegro[abi_x86_32]' ;; ('liballegro_image.so.5.2') package_name='media-libs/allegro[abi_x86_32]' ;; ('liballegro_primitives.so.5.2') package_name='media-libs/allegro[abi_x86_32]' ;; ('liballegro_ttf.so.5.2') package_name='media-libs/allegro[abi_x86_32]' ;; ('libalut.so.0') package_name='media-libs/freealut[abi_x86_32]' ;; ('libasound.so.2') package_name='media-libs/alsa-lib[abi_x86_32]' ;; ('libasound_module_'*'.so') package_name='media-plugins/alsa-plugins[abi_x86_32]' ;; ('libatspi.so.0') package_name='app-accessibility/at-spi2-core[abi_x86_32]' ;; ('libatk-1.0.so.0') package_name='dev-libs/atk[abi_x86_32]' ;; ('libaudio.so.2') package_name='media-libs/nas[abi_x86_32]' ;; ('libboost_locale.so.1.74.0') # This library is not provided for Gentoo unset package_name ;; ('libbz2.so.1.0'|'libbz2.so.1') package_name='app-arch/bzip2[abi_x86_32]' ;; ('libc.so.6') package_name='sys-libs/glibc amd64? ( sys-libs/glibc[multilib] )' ;; ('libc++.so.1') package_name='sys-libs/libcxx[abi_x86_32]' ;; ('libc++abi.so.1') package_name='sys-libs/libcxxabi[abi_x86_32]' ;; ('libcairo.so.2') package_name='x11-libs/cairo[abi_x86_32]' ;; ('libCg.so') # This library is not provided for Gentoo unset package_name ;; ('libCgGL.so') # This library is not provided for Gentoo unset package_name ;; ('libcom_err.so.2') package_name='sys-libs/e2fsprogs-libs[abi_x86_32]' ;; ('libcrypt.so.1') package_name='sys-libs/libxcrypt[abi_x86_32]' ;; ('libcups.so.2') package_name='net-print/cups[abi_x86_32]' ;; ('libcurl.so.4') package_name='net-misc/curl[abi_x86_32]' ;; ('libcurl.so.4+CURL_OPENSSL_3') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/curl/ return 0 ;; ('libcurl-gnutls.so.4') package_name='net-libs/libcurl-debian[abi_x86_32]' pkg_overlay='steam-overlay' dependencies_gentoo_link 'libcurl-gnutls.so.4' "/usr/$(dependency_gentoo_libdir 'x86')/debiancompat" "$package" ;; ('libdbus-1.so.3') package_name='sys-apps/dbus[abi_x86_32]' ;; ('libdbus-glib-1.so.2') package_name='dev-libs/dbus-glib[abi_x86_32]' ;; ('libdl.so.2') package_name='sys-libs/glibc amd64? ( sys-libs/glibc[multilib] )' ;; ('libEGL.so.1') package_name='media-libs/libglvnd[abi_x86_32]' ;; ('libexpat.so.1') package_name='dev-libs/expat[abi_x86_32]' ;; ('libFAudio.so.0') package_name='app-emulation/faudio[abi_x86_32]' ;; ('libFLAC.so.8') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/flac/ return 0 ;; ('libfontconfig.so.1') package_name='media-libs/fontconfig[abi_x86_32]' ;; ('libfreeimage.so.3') package_name='media-libs/freeimage[abi_x86_32]' ;; ('libfreetype.so.6') package_name='media-libs/freetype[abi_x86_32]' ;; ('libfribidi.so.0') package_name='dev-libs/fribidi[abi_x86_32]' ;; ('libgcc_s.so.1') package_name='sys-devel/gcc[abi_x86_32]' ;; ('libgconf-2.so.4') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/gconf/ return 0 ;; ('libgcrypt.so.11') package_name='dev-libs/libgcrypt-compat[abi_x86_32]' ;; ('libgdiplus.so') package_name='dev-dotnet/libgdiplus[abi_x86_32]' ;; ('libgdk-3.so.0') package_name='x11-libs/gtk+:3[abi_x86_32]' ;; ('libgdk_pixbuf-2.0.so.0') package_name='x11-libs/gdk-pixbuf:2[abi_x86_32]' ;; ('libgdk-x11-2.0.so.0') package_name='x11-libs/gtk+:2[abi_x86_32]' ;; ('libgio-2.0.so.0') package_name='dev-libs/glib:2[abi_x86_32]' ;; ('libGL.so.1') package_name='virtual/opengl[abi_x86_32]' ;; ('libGLEW.so.2.2') package_name='media-libs/glew[abi_x86_32]' ;; ('libglfw.so.3') package_name='media-libs/glfw[abi_x86_32]' ;; ('libglib-2.0.so.0') package_name='dev-libs/glib:2[abi_x86_32]' ;; ('libGLU.so.1') package_name='virtual/glu[abi_x86_32]' ;; ('libGLX.so.0') package_name='media-libs/libglvnd[abi_x86_32]' ;; ('libgmodule-2.0.so.0') package_name='dev-libs/glib:2[abi_x86_32]' ;; ('libgobject-2.0.so.0') package_name='dev-libs/glib:2[abi_x86_32]' ;; ('libgomp.so.1') package_name='sys-devel/gcc[abi_x86_32]' ;; ('libgpg-error.so.0') package_name='dev-libs/libgpg-error[abi_x86_32]' ;; ('libgssapi_krb5.so.2') package_name='app-crypt/mit-krb5[abi_x86_32]' ;; ('libgthread-2.0.so.0') package_name='dev-libs/glib:2[abi_x86_32]' ;; ('libgtk-x11-2.0.so.0') package_name='x11-libs/gtk+:2[abi_x86_32]' ;; ('libgtk-3.so.0') package_name='x11-libs/gtk+:3[abi_x86_32]' ;; ('libICE.so.6') package_name='x11-libs/libICE[abi_x86_32]' ;; ('libidn.so.11') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/libidn/ return 0 ;; ('libidn2.so.0') package_name='net-dns/libidn2[abi_x86_32]' ;; ('libIL.so.1') package_name='media-libs/devil[abi_x86_32]' ;; ('libjpeg.so.62') package_name='media-libs/libjpeg-turbo[abi_x86_32]' ;; ('libk5crypto.so.3') package_name='app-crypt/mit-krb5[abi_x86_32]' ;; ('libkrb5.so.3') package_name='app-crypt/mit-krb5[abi_x86_32]' ;; ('liblcms2.so.2') package_name='media-libs/lcms[abi_x86_32]' ;; ('liblua5.3.so.0') package_name='dev-lang/lua[abi_x86_32]' ;; ('libluajit-5.1.so.2') package_name='dev-lang/luajit[abi_x86_32]' ;; ('liblz4.so.1') package_name='app-arch/lz4[abi_x86_32]' ;; ('libm.so.6') package_name='sys-libs/glibc amd64? ( sys-libs/glibc[multilib] )' ;; ('libmbedtls.so.12') package_name='net-libs/mbedtls:0/12[abi_x86_32]' ;; ('libminiupnpc.so.17') package_name='net-libs/miniupnpc[abi_x86_32]' ;; ('libminizip.so.1') package_name='sys-libs/zlib[abi_x86_32]' ;; ('libmodplug.so.1') package_name='media-libs/libmodplug[abi_x86_32]' ;; ('libmpg123.so.0') package_name='media-sound/mpg123[abi_x86_32]' ;; ('libnghttp2.so.14') package_name='net-libs/nghttp2[abi_x86_32]' ;; ('libnotify.so.4') package_name='x11-libs/libnotify[abi_x86_32]' ;; ('libnspr4.so') package_name='dev-libs/nspr[abi_x86_32]' ;; ('libnss3.so') package_name='dev-libs/nss[abi_x86_32]' ;; ('libnssutil3.so') package_name='dev-libs/nss[abi_x86_32]' ;; ('libogg.so.0') package_name='media-libs/libogg[abi_x86_32]' ;; ('libopenal.so.1') package_name='media-libs/openal[abi_x86_32]' ;; ('libOpenGL.so.0') package_name='media-libs/libglvnd[abi_x86_32]' ;; ('libopenmpt.so.0') package_name='media-libs/libopenmpt[abi_x86_32]' ;; ('libpango-1.0.so.0') package_name='x11-libs/pango[abi_x86_32]' ;; ('libpangocairo-1.0.so.0') package_name='x11-libs/pango[abi_x86_32]' ;; ('libpangoft2-1.0.so.0') package_name='x11-libs/pango[abi_x86_32]' ;; ('libpcre.so.3') package_name='dev-libs/libpcre-debian[abi_x86_32]' ;; ('libphysfs.so.1') package_name='dev-games/physfs[abi_x86_32]' ;; ('libpixman-1.so.0') package_name='x11-libs/pixman[abi_x86_32]' ;; ('libplc4.so') package_name='dev-libs/nspr[abi_x86_32]' ;; ('libplds4.so') package_name='dev-libs/nspr[abi_x86_32]' ;; ('libpng12.so.0') package_name='media-libs/libpng-compat:1.2[abi_x86_32]' ;; ('libpng16.so.16') package_name='media-libs/libpng:0/16[abi_x86_32]' ;; ('libpsl.so.5') package_name='net-libs/libpsl[abi_x86_32]' ;; ('libpthread.so.0') package_name='sys-libs/glibc amd64? ( sys-libs/glibc[multilib] )' ;; ('libpulse.so.0') package_name='media-sound/pulseaudio[abi_x86_32]' ;; ('libpulse-simple.so.0') package_name='media-sound/pulseaudio[abi_x86_32]' ;; ('libresolv.so.2') package_name='sys-libs/glibc amd64? ( sys-libs/glibc[multilib] )' ;; ('librt.so.1') package_name='sys-libs/glibc amd64? ( sys-libs/glibc[multilib] )' ;; ('librtmp.so.1') package_name='media-video/rtmpdump[abi_x86_32]' ;; ('libSDL-1.2.so.0') package_name='media-libs/libsdl[abi_x86_32,opengl]' ;; ('libSDL_image-1.2.so.0') package_name='media-libs/sdl-image[abi_x86_32]' ;; ('libSDL_kitchensink.so.1') # This library is not provided for Gentoo unset package_name ;; ('libSDL_mixer-1.2.so.0') package_name='media-libs/sdl-mixer[abi_x86_32]' ;; ('libSDL_sound-1.0.so.1') package_name='media-libs/sdl-sound[abi_x86_32]' ;; ('libSDL_ttf-2.0.so.0') package_name='media-libs/sdl-ttf[abi_x86_32]' ;; ('libSDL2-2.0.so.0') package_name='media-libs/libsdl2[abi_x86_32,opengl]' ;; ('libSDL2_image-2.0.so.0') # Most games will require at least jpeg and png # Maybe we should add gif and tiff to that list? package_name='media-libs/sdl2-image[jpeg,png,abi_x86_32]' ;; ('libSDL2_mixer-2.0.so.0') # Most games will require at least one of flac, mp3, vorbis or wav USE flags, # it should better to require them all instead of not requiring any # and having non-fonctionnal sound in some games. package_name='media-libs/sdl2-mixer[flac,mp3,vorbis,wav,abi_x86_32]' ;; ('libSDL2_ttf-2.0.so.0') package_name='media-libs/sdl2-ttf[abi_x86_32]' ;; ('libsecret-1.so.0') package_name='app-crypt/libsecret[abi_x86_32]' ;; ('libsigc-2.0.so.0') package_name='dev-libs/libsigc++[abi_x86_32]' ;; ('libSM.so.6') package_name='x11-libs/libSM[abi_x86_32]' ;; ('libsmime3.so') package_name='dev-libs/nss[abi_x86_32]' ;; ('libsmpeg-0.4.so.0') package_name='media-libs/smpeg[abi_x86_32]' ;; ('libsodium.so.23') package_name='dev-libs/libsodium[abi_x86_32]' ;; ('libssh2.so.1') package_name='net-libs/libssh2[abi_x86_32]' ;; ('libssl.so.1.0.0') package_name='dev-libs/openssl-compat:1.0.0[abi_x86_32]' ;; ('libssl.so.1.1') package_name='dev-libs/openssl-compat:1.1.1[abi_x86_32]' ;; ('libssl3.so') package_name='dev-libs/nss[abi_x86_32]' ;; ('libstdc++.so.5') package_name='sys-libs/libstdc++-v3[abi_x86_32]' ;; ('libstdc++.so.6') package_name='sys-devel/gcc amd64? ( sys-devel/gcc[multilib] )' ;; ('libtcmalloc_minimal.so.4') package_name='dev-util/google-perftools[abi_x86_32]' ;; ('libtheora.so.0') package_name='media-libs/libtheora[abi_x86_32]' ;; ('libtheoradec.so.1') package_name='media-libs/libtheora[abi_x86_32]' ;; ('libtheoraenc.so.1') package_name='media-libs/libtheora[abi_x86_32]' ;; ('libthread_db.so.1') package_name='sys-libs/glibc amd64? ( sys-libs/glibc[multilib] )' ;; ('libtiff.so.6') package_name='media-libs/tiff[abi_x86_32]' ;; ('libturbojpeg.so.0') package_name='media-libs/libjpeg-turbo[abi_x86_32]' ;; ('libudev.so.0') package_name='sys-libs/libudev-compat[abi_x86_32]' ;; ('libudev.so.1') package_name='virtual/libudev[abi_x86_32]' ;; ('libutil.so.1') package_name='sys-libs/glibc amd64? ( sys-libs/glibc[multilib] )' ;; ('libuuid.so.1') package_name='sys-apps/util-linux[abi_x86_32]' ;; ('libuv.so.1') package_name='dev-libs/libuv:0/1[abi_x86_32]' ;; ('libvorbis.so.0') package_name='media-libs/libvorbis[abi_x86_32]' ;; ('libvorbisenc.so.2') package_name='media-libs/libvorbis[abi_x86_32]' ;; ('libvorbisfile.so.3') package_name='media-libs/libvorbis[abi_x86_32]' ;; ('libvulkan.so.1') package_name='media-libs/vulkan-loader[abi_x86_32]' ;; ('libwayland-client.so.0') package_name='dev-libs/wayland[abi_x86_32]' ;; ('libX11.so.6') package_name='x11-libs/libX11[abi_x86_32]' ;; ('libX11-xcb.so.1') package_name='x11-libs/libX11[abi_x86_32]' ;; ('libxcb.so.1') package_name='x11-libs/libxcb[abi_x86_32]' ;; ('libxcb-randr.so.0') package_name='x11-libs/libxcb[abi_x86_32]' ;; ('libXcomposite.so.1') package_name='x11-libs/libXcomposite[abi_x86_32]' ;; ('libXcursor.so.1') package_name='x11-libs/libXcursor[abi_x86_32]' ;; ('libXdamage.so.1') package_name='x11-libs/libXdamage[abi_x86_32]' ;; ('libXext.so.6') package_name='x11-libs/libXext[abi_x86_32]' ;; ('libXfixes.so.3') package_name='x11-libs/libXfixes[abi_x86_32]' ;; ('libXft.so.2') package_name='x11-libs/libXft[abi_x86_32]' ;; ('libXi.so.6') package_name='x11-libs/libXi[abi_x86_32]' ;; ('libXinerama.so.1') package_name='x11-libs/libXinerama[abi_x86_32]' ;; ('libxml2.so.2') package_name='dev-libs/libxml2[abi_x86_32]' ;; ('libxmp.so.4') package_name='media-libs/libxmp[abi_x86_32]' ;; ('libXmu.so.6') package_name='x11-libs/libXmu[abi_x86_32]' ;; ('libXrandr.so.2') package_name='x11-libs/libXrandr[abi_x86_32]' ;; ('libXrender.so.1') package_name='x11-libs/libXrender[abi_x86_32]' ;; ('libxslt.so.1') package_name='dev-libs/libxslt[abi_x86_32]' ;; ('libXss.so.1') package_name='x11-libs/libXScrnSaver[abi_x86_32]' ;; ('libXt.so.6') package_name='x11-libs/libXt[abi_x86_32]' ;; ('libXtst.so.6') package_name='x11-libs/libXtst[abi_x86_32]' ;; ('libXxf86vm.so.1') package_name='x11-libs/libXxf86vm[abi_x86_32]' ;; ('libyaml-0.so.2') package_name='dev-libs/libyaml[abi_x86_32]' ;; ('libz.so.1') package_name='sys-libs/zlib:0/1[abi_x86_32]' ;; esac if [ -n "${package_name:-}" ]; then printf '%s' "$package_name" if [ -n "${pkg_overlay:-}" ]; then dependency_gentoo_overlays_add "$pkg_overlay" fi return 0 fi dependencies_unknown_libraries_add "$library" } src/60_system_gentoo/25_dependencies_siblings.sh0000644000000000000000000000142513120060140020716 0ustar rootroot# Gentoo - Print the sinling package names required by the given package # USAGE: gentoo_dependencies_siblings $package # RETURN: a list of package names, # one per line gentoo_dependencies_siblings() { local package package="$1" local required_siblings required_siblings=$(dependencies_list_siblings "$package") # Return early if the current package does not require any sibling package if [ -z "$required_siblings" ]; then return 0 fi local sibling dependencies_siblings_list required_package while read -r sibling; do required_package=$(package_id "$sibling") dependencies_siblings_list="${dependencies_siblings_list:-} $required_package" done <<- EOL $(printf '%s' "$required_siblings") EOL printf '%s' "${dependencies_siblings_list:-}" | list_clean } src/60_system_gentoo/90_messages.sh0000644000000000000000000000574413120060140016217 0ustar rootroot# print notification about required overlays when building Gentoo packages # USAGE: information_required_gentoo_overlays $overlays information_required_gentoo_overlays() { local overlays_file overlays_file=$(dependency_gentoo_overlays_file) local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Vous pourriez avoir besoin des overlays suivants pour installer ces paquets :' ;; ('en'|*) message='You may need the following overlays to install these packages:' ;; esac print_message 'info' '\n%s\n' \ "$message" print_message 'info' '\t%s\n' \ "$(cat "$overlays_file")" } # add comment to packages installation instructions on Gentoo # USAGE: information_installation_instructions_gentoo_comment information_installation_instructions_gentoo_comment() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='ou mettez les paquets dans un PKGDIR (dans un dossier nommé games-playit) et emergez-les' ;; ('en'|*) message='or put the packages in a PKGDIR (in a folder named games-playit) and emerge them' ;; esac print_message 'info' "$message" } # inform the need of a local overlay on gentoo for ebuilds # USAGE: info_local_overlay_gentoo info_local_overlay_gentoo() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\nUn overlay local est nécessaire pour utiliser les ebuilds générés par ./play.it\n' message="$message"'Dans la suite OVERLAY_PATH correspond au chemin de votre overlay local\n' ;; ('en'|*) message='\nA local overlay is needed to use the ebuilds generated by ./play.it\n' message="$message"'In what comes next, OVERLAY_PATH is the path to your local overlay\n' ;; esac print_message 'info' "$message" } # inform the need to move the packages to a distfile on egentoo # USAGE: info_package_to_distfiles info_package_to_distfiles() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Déplacez les paquets créés dans votre disfile\n' message="$message"'puis exécutez les instructions suivantes :\n' ;; ('en'|*) message='Move the generated packages into your distfile\n' message="$message"'then run the following commands:\n' ;; esac print_message 'info' "$message" } # Error - An unknown architecture string is used # USAGE: error_unknown_gentoo_architecture_string $arch_string $caller error_unknown_gentoo_architecture_string() { local arch_string caller arch_string="$1" caller="$2" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Lʼarchitecture « %s », utilisée dans %s, est inconnue sous Gentoo.\n' ;; ('en'|*) message='“%s” architecture, used in %s, is unknown on Gentoo.\n' ;; esac print_message 'error' "$message" \ "$arch_string" \ "$caller" } src/70_deprecation/90_messages.sh0000644000000000000000000001175213120060140015612 0ustar rootroot# Warning: A deprecated function has been called. # USAGE: warning_deprecated_function $old_function $new_function warning_deprecated_function() { local old_function new_function old_function="$1" new_function="$2" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La fonction suivante est dépréciée : %s\n' message="$message"'Cette nouvelle fonction devrait être utilisée à sa place : %s\n\n' ;; ('en'|*) message='The following function is deprecated: %s\n' message="$message"'This new function should be used instead: %s\n\n' ;; esac # Print the message on the standard error output, # to avoid messing up the regular output of the function that triggered this warning. print_message 'warning_once' "$message" \ "$old_function" \ "$new_function" \ > /dev/stderr } # Warning: A deprecated variable is set. # USAGE: warning_deprecated_variable $old_variable $new_variable warning_deprecated_variable() { local old_variable new_variable old_variable="$1" new_variable="$2" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La variable suivante est dépréciée : %s\n' message="$message"'Cette nouvelle variable devrait être utilisée à sa place : %s\n\n' ;; ('en'|*) message='The following variable is deprecated: %s\n' message="$message"'This new variable should be used instead: %s\n\n' ;; esac # Print the message on the standard error output, # to avoid messing up the regular output of the variable that triggered this warning. print_message 'warning_once' "$message" \ "$old_variable" \ "$new_variable" \ > /dev/stderr } # Warning: An option is set to a deprecated value. # USAGE: warning_option_value_deprecated $option_name $option_value warning_option_value_deprecated() { local option_name option_value option_name="$1" option_value="$2" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La valeur suivante est dépréciée pour lʼoption "%s", et ne sera plus acceptée dans une future version : "%s"\n\n' ;; ('en'|*) message='The following value is deprecated for option "%s", and will no longer be supported with some future update: "%s"\n\n' ;; esac # Print the message on the standard error output, # to avoid messing up the regular output of the variable that triggered this warning. print_message 'warning_once' "$message" \ "$option_name" \ "$option_value" \ > /dev/stderr } # Warning: An archive has a deprecated type set. # USAGE: warning_archive_type_deprecated $archive warning_archive_type_deprecated() { local archive archive="$1" local archive_type game_name archive_type=$(archive_type "$archive") game_name=$(game_name) local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La prise en charge de "%s" utilise un type dʼarchive déprécié : %s\n\n' ;; ('en'|*) message='Support for "%s" is using an obsolete archive type: %s\n\n' ;; esac # Print the message on the standard error output, # to avoid messing up the regular output of the variable that triggered this warning. print_message 'warning_once' "$message" \ "$game_name" \ "$archive_type" \ > /dev/stderr } # Warning - The legacy variable has been use to set the current archive. # USAGE: warning_context_legacy_archive warning_context_legacy_archive() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Lʼarchive actuelle a été définie en utilisant la variable dépréciée $ARCHIVE.\n' message="$message"'La fonction set_current_archive devrait être utilisée à sa place.\n' ;; ('en'|*) message='The current archive has been set using the deprecated variable $ARCHIVE.\n' message="$message"'The function set_current_archive should be used instead.\n' ;; esac # Print the message on the standard error output, # to avoid messing up the regular output of the function that triggered this warning. print_message 'warning_once' "$message" > /dev/stderr } # Warning - The legacy variable has been use to set the current package. # USAGE: warning_context_legacy_package warning_context_legacy_package() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le paquet actuel a été défini en utilisant la variable dépréciée $PKG.\n' message="$message"'La fonction set_current_package devrait être utilisée à sa place.\n' ;; ('en'|*) message='The current package has been set using the deprecated variable $PKG.\n' message="$message"'The function set_current_package should be used instead.\n' ;; esac # Print the message on the standard error output, # to avoid messing up the regular output of the function that triggered this warning. print_message 'warning_once' "$message" > /dev/stderr } src/80_debug/00_debug.sh0000644000000000000000000000424313120060140013647 0ustar rootroot# print DEBUG # USAGE: print_debug print_debug() { printf '\033[1;32mDEBUG:\033[0m ' >> /dev/stderr return 0 } # print the name of the entered function # USAGE: debug_entering_function $function_name [$debug_level] debug_entering_function() { local debug_level if [ "$#" -eq 2 ]; then debug_level="$2" case "$debug_level" in ([0-9]) ;; (*) # TODO - Display an explicit error message return 1 ;; esac else debug_level=1 fi local option_debug option_debug=$(option_value 'debug') if [ "$option_debug" -lt "$debug_level" ]; then return 0 fi local function_name function_name="$1" assert_not_empty 'function_name' 'debug_entering_function' print_debug printf 'Entering %s\n' "$function_name" >> /dev/stderr return 0 } # print the name of the leaved function # USAGE: debug_leaving_function $function_name [$debug_level] debug_leaving_function() { local debug_level if [ "$#" -eq 2 ]; then debug_level="$2" case "$debug_level" in ([0-9]) ;; (*) # TODO - Display an explicit error message return 1 ;; esac else debug_level=1 fi local option_debug option_debug=$(option_value 'debug') if [ "$option_debug" -lt "$debug_level" ]; then return 0 fi local function_name function_name="$1" assert_not_empty 'function_name' 'debug_leaving_function' print_debug printf 'Leaving %s\n' "$function_name" >> /dev/stderr return 0 } # print the name of the created directory # USAGE: debug_creating_directory $dir_path debug_creating_directory() { local option_debug option_debug=$(option_value 'debug') if [ "$option_debug" -eq 0 ]; then return 0 fi local dir_path dir_path="$1" assert_not_empty 'dir_path' 'debug_creating_directory' print_debug printf 'Creating directory %s\n' "$dir_path" >> /dev/stderr return 0 } # print an external command # USAGE: debug_external_command $command debug_external_command() { local option_debug option_debug=$(option_value 'debug') if [ "$option_debug" -eq 0 ]; then return 0 fi local external_command external_command="$1" assert_not_empty 'external_command' 'debug_external_command' print_debug printf 'Running: %s\n' "$external_command" >> /dev/stderr return 0 } src/90_compatibility/82_2.27-and-older.sh0000644000000000000000000000077213120060140016615 0ustar rootroot# Keep compatibility with 2.27 and older icons_inclusion() { if compatibility_level_is_at_least '2.28'; then warning_deprecated_function 'icons_inclusion' 'content_inclusion_icons' fi local package package=$(current_package) content_inclusion_icons "$package" "$@" } launchers_write() { if compatibility_level_is_at_least '2.28'; then warning_deprecated_function 'launchers_write' 'launchers_generation' fi local package package=$(current_package) launchers_generation "$package" "$@" } src/90_compatibility/85_2.23-and-older.sh0000644000000000000000000000527513120060140016617 0ustar rootroot# Keep compatibility with 2.23 and older wine_launcher_wineprefix_persistent_legacy() { if compatibility_level_is_at_least '2.23'; then warning_deprecated_variable 'APP_WINE_LINK_DIRS' 'WINE_PERSISTENT_DIRECTORIES' fi cat <<- EOF # Move files that should be diverted to persistent paths to the game directory APP_WINE_LINK_DIRS="$APP_WINE_LINK_DIRS" EOF cat <<- 'EOF' printf '%s' "$APP_WINE_LINK_DIRS" | grep ':' | while read -r line; do prefix_dir="$PATH_PREFIX/${line%%:*}" wine_dir="$WINEPREFIX/drive_c/${line#*:}" mkdir --parents "$prefix_dir" if [ ! -h "$wine_dir" ]; then if [ -d "$wine_dir" ]; then # A basic recursive cp will not work due to the presence of symbolic links to directories in the destination. ( cd "$prefix_dir" find . -type l ) | while read -r link; do if [ -e "${wine_dir}/${link}" ]; then cp --no-clobber --recursive "${wine_dir}/${link}"/* "${prefix_dir}/${link}"/ rm --force --recursive "${wine_dir:?}/${link}" fi done cp --no-clobber --no-target-directory --recursive "$wine_dir" "$prefix_dir" rm --force --recursive "$wine_dir" fi if [ ! -d "$prefix_dir" ]; then mkdir --parents "$prefix_dir" fi mkdir --parents "$(dirname "$wine_dir")" ln --symbolic "$prefix_dir" "$wine_dir" fi done EOF } write_metadata() { if compatibility_level_is_at_least '2.24'; then warning_deprecated_function 'write_metadata' 'packages_generation' fi # If not explicit packages list is given, write metadata for all packages if [ $# -eq 0 ]; then local packages_list packages_list=$(packages_list) write_metadata $packages_list fi local option_package option_package=$(option_value 'package') case "$option_package" in ('arch') archlinux_packages_metadata "$@" ;; ('deb') debian_packages_metadata "$@" ;; ('gentoo') gentoo_packages_metadata "$@" ;; ('egentoo') egentoo_packages_metadata "$@" ;; esac } build_pkg() { if compatibility_level_is_at_least '2.24'; then warning_deprecated_function 'build_pkg' 'packages_generation' fi # If not explicit packages list is given, build all packages if [ $# -eq 0 ]; then local packages_list packages_list=$(packages_list) build_pkg $packages_list fi local option_package option_package=$(option_value 'package') case "$option_package" in ('arch') archlinux_packages_build "$@" ;; ('deb') debian_packages_build "$@" ;; ('gentoo') gentoo_packages_build "$@" ;; ('egentoo') egentoo_packages_build "$@" ;; esac } src/90_compatibility/90_2.18-and-older.sh0000644000000000000000000000503213120060140016606 0ustar rootroot# Keep compatibility with 2.18 and older content_path_legacy() { local content_id content_id="$1" local content_path_legacy content_path_legacy=$(context_value "ARCHIVE_${content_id}_PATH") if [ -z "$content_path_legacy" ]; then ## Return early if no path is set. return 0 fi if compatibility_level_is_at_least '2.18'; then warning_deprecated_variable \ "ARCHIVE_${content_id}_PATH" \ "CONTENT_${content_id}_PATH" fi printf '%s' "$content_path_legacy" } content_files_legacy() { local content_id content_id="$1" local content_files_legacy content_files_legacy=$(context_value "ARCHIVE_${content_id}_FILES") if [ -z "$content_files_legacy" ]; then ## Return early if no files list is set. return 0 fi if compatibility_level_is_at_least '2.18'; then warning_deprecated_variable \ "ARCHIVE_${content_id}_FILES" \ "CONTENT_${content_id}_FILES" fi # Legacy variable could use spaces as a delimiter, # line breaks are expected instead. local file_pattern for file_pattern in $content_files_legacy; do printf '%s\n' "$file_pattern" done } content_inclusion_default_game_data_legacy() { local package package="$1" local package_suffix files_list package_suffix="${package#PKG_}" files_list=$(context_name "ARCHIVE_GAME_${package_suffix}_FILES") if [ -z "$files_list" ]; then ## Return early if no files list is set. return 0 fi local target_directory target_directory=$(path_game_data) content_inclusion "GAME_${package_suffix}" "$package" "$target_directory" local index for index in $(seq 0 9); do files_list=$(context_name "ARCHIVE_GAME${index}_${package_suffix}_FILES") if [ -z "$files_list" ]; then ## Stop looping at the first unset files list. return 0 fi content_inclusion "GAME${index}_${package_suffix}" "$package" "$target_directory" done } content_inclusion_default_game_documentation_legacy() { local package package="$1" local package_suffix files_list package_suffix="${package#PKG_}" files_list=$(context_name "ARCHIVE_DOC_${package_suffix}_FILES") if [ -z "$files_list" ]; then ## Return early if no files list is set. return 0 fi local target_directory target_directory=$(path_game_data) content_inclusion "DOC_${package_suffix}" "$package" "$target_directory" local index for index in $(seq 0 9); do files_list=$(context_name "ARCHIVE_DOC${index}_${package_suffix}_FILES") if [ -z "$files_list" ]; then ## Stop looping at the first unset files list. return 0 fi content_inclusion "DOC${index}_${package_suffix}" "$package" "$target_directory" done } src/90_compatibility/91_2.17-and-older.sh0000644000000000000000000000035413120060140016610 0ustar rootroot# Keep compatibility with 2.17 and older prepare_package_layout() { if compatibility_level_is_at_least '2.18'; then warning_deprecated_function 'prepare_package_layout' 'content_inclusion_default' fi content_inclusion_default } src/90_compatibility/92_2.16-and-older.sh0000644000000000000000000000701113120060140016605 0ustar rootroot# Keep compatibility with 2.16 and older icons_get_from_package() { if compatibility_level_is_at_least '2.28'; then warning_deprecated_function 'icons_get_from_package' 'content_inclusion_icons' elif compatibility_level_is_at_least '2.17'; then warning_deprecated_function 'icons_get_from_package' 'icons_inclusion' fi local package package_path path_game_data icon_source_directory_legacy package=$(current_package) package_path=$(package_path "$package") path_game_data=$(path_game_data) icon_source_directory_legacy="${package_path}${path_game_data}" # Get the archive inner path, falling back on a default value. local content_path icon_source_directory_new if [ -z "${CONTENT_PATH_DEFAULT:-}" ]; then local CONTENT_PATH_DEFAULT CONTENT_PATH_DEFAULT='.' fi content_path=$(content_path_default) icon_source_directory_new="${PLAYIT_WORKDIR}/gamedata/${content_path}" local application for application in "$@"; do local application_icons_list application_icons_list=$(application_icons_list "$application") if [ -z "$application_icons_list" ]; then continue fi local icon for icon in $application_icons_list; do local icon_path icon_full_path_legacy icon_full_path_new icon_path=$(icon_path "$icon") ## Compute icon legacy path. icon_full_path_legacy="${icon_source_directory_legacy}/${icon_path}" ## Compute icon expected path for new function. icon_full_path_new="${icon_source_directory_new}/${icon_path}" ## Set compatibility link. icon_full_path_legacy_canonical=$(realpath --canonicalize-missing "$icon_full_path_legacy") icon_full_path_new_canonical=$(realpath --canonicalize-missing "$icon_full_path_new") if [ "$icon_full_path_legacy_canonical" != "$icon_full_path_new_canonical" ]; then mkdir --parents "$(dirname "$icon_full_path_new")" ln --symbolic "$icon_full_path_legacy" "$icon_full_path_new" fi ## Call new function. icons_inclusion_single_icon "$package" "$application" "$icon" ## Remove compatibility link. if [ "$icon_full_path_legacy_canonical" != "$icon_full_path_new_canonical" ]; then rm "$icon_full_path_new" ## Do not try to delete "…/gamedata/.", rmdir would fail with "Invalid argument". if [ "$content_path" != '.' ]; then rmdir --parents --ignore-fail-on-non-empty "$(dirname "$icon_full_path_new")" fi fi done done } persistent_list_directories_legacy() { set +o noglob if [ -n "${CONFIG_DIRS:-}" ]; then if compatibility_level_is_at_least '2.17'; then warning_deprecated_variable 'CONFIG_DIRS' 'USER_PERSISTENT_DIRECTORIES' fi local legacy_directory for legacy_directory in $CONFIG_DIRS; do printf '%s\n' "$legacy_directory" done fi if [ -n "${DATA_DIRS:-}" ]; then if compatibility_level_is_at_least '2.17'; then warning_deprecated_variable 'DATA_DIRS' 'USER_PERSISTENT_DIRECTORIES' fi local legacy_directory for legacy_directory in $DATA_DIRS; do printf '%s\n' "$legacy_directory" done fi set -o noglob } persistent_list_files_legacy() { set +o noglob if [ -n "${CONFIG_FILES:-}" ]; then if compatibility_level_is_at_least '2.17'; then warning_deprecated_variable 'CONFIG_FILES' 'USER_PERSISTENT_FILES' fi local legacy_file for legacy_file in $CONFIG_FILES; do printf '%s\n' "$legacy_file" done fi if [ -n "${DATA_FILES:-}" ]; then if compatibility_level_is_at_least '2.17'; then warning_deprecated_variable 'DATA_FILES' 'USER_PERSISTENT_FILES' fi local legacy_file for legacy_file in $DATA_FILES; do printf '%s\n' "$legacy_file" done fi set -o noglob } src/90_compatibility/93_2.15-and-older.sh0000644000000000000000000000312113120060140016603 0ustar rootroot# Keep compatibility with 2.15 and older extract_data_from() { if compatibility_level_is_at_least '2.16'; then warning_deprecated_function 'extract_data_from' 'archive_extraction' fi local archive archive_path_from_environment archive_path_from_environment_real archive=$(current_archive) archive_path_from_environment=$(archive_path "$archive") archive_path_from_environment_real=$(realpath --canonicalize-existing "$archive_path_from_environment") local archive_path_from_parameters archive_path_from_parameters_real archive_path_from_parameters="$1" archive_path_from_parameters_real=$(realpath --canonicalize-existing "$archive_path_from_parameters") if [ "${archive_path_from_environment_real:-}" != "${archive_path_from_parameters_real:-}" ]; then local messages_language message game_name game_name=$(game_name) messages_language=$(messages_language) case "$messages_language" in ('fr') message='La prise en charge de "%s" utilise du code obsolète qui nʼest plus fonctionnel.\n' message="$message"'La fonction ayant déclenché cette erreur est : %s\n' message="$message"'Merci de signaler cette erreur sur notre système de suivi : %s\n' ;; ('en'|*) message='Support for "%s" is relying on obsolete code that no longer works.\n' message="$message"'The function that triggered this error is: %s\n' message="$message"'Please report this error on our issues tracker: %s\n' ;; esac print_message 'error' "$message" \ "$game_name" \ 'extract_data_from' \ "$PLAYIT_GAMES_BUG_TRACKER_URL" return 1 fi archive_extraction "$archive" } src/99_init/00_init.sh0000644000000000000000000001677113120060140013424 0ustar rootrootif [ "$(basename "$0")" != 'libplayit2.sh' ] && [ -z "$LIB_ONLY" ]; then # Exit immediately on error set -o errexit # Exit if the current process has been spawned by the root user ## This check can be skipped by setting the following environment variable: ## PLAYIT_OPTION_RUN_AS_ROOT=1 if \ [ "${PLAYIT_OPTION_RUN_AS_ROOT:-0}" -eq 0 ] && \ check_is_running_as_root then error_run_as_root exit 1 fi # Error out (and exit) when trying to expand an unset variable ## Only for game scripts targeting ./play.it ≥ 2.23 if compatibility_level_is_at_least '2.23'; then set -o nounset fi # Set input field separator to default value (space, tab, newline) unset IFS # Force umask to ensure all paths are created with correct permissions umask 0022 # Check early if ./play.it has been called in games listing mode, # since most of the initialization steps are not required in this mode. option_list_supported_games=$(option_value 'list-supported-games') if [ "${option_list_supported_games:-0}" -eq 1 ]; then ## Set the default package, used by the context system when no explicit package context is set. ## It is required by the context system, but is usually set later, when the current archive has been set already. packages_list=$(packages_list) default_package=$(printf '%s' "$packages_list" | head --lines=1) set_default_package "$default_package" unset packages_list default_package games_list_supported exit 0 fi unset option_list_supported_games # Unset variables that we do not want to import from the user environment unset SOURCE_ARCHIVE_PATH unset PLAYIT_WORKDIR # Set URLs for error messages PLAYIT_GAMES_BUG_TRACKER_URL='https://forge.dotslashplay.it/play.it/games/issues' PLAYIT_BUG_TRACKER_URL='https://forge.dotslashplay.it/play.it/scripts/issues' # Check the current library version against the expected compatibility level COMPATIBILITY_LEVEL=$(compatibility_level) COMPATIBILITY_LEVEL_MAJOR=$(printf '%s' "$COMPATIBILITY_LEVEL" | cut --delimiter='.' --fields=1) LIBRARY_VERSION_MAJOR=$(printf '%s' "$LIBRARY_VERSION" | cut --delimiter='.' --fields=1) if [ "$LIBRARY_VERSION_MAJOR" -lt "$COMPATIBILITY_LEVEL_MAJOR" ]; then error_incompatible_versions exit 1 elif [ "$LIBRARY_VERSION_MAJOR" -eq "$COMPATIBILITY_LEVEL_MAJOR" ]; then COMPATIBILITY_LEVEL_MINOR=$(printf '%s' "$COMPATIBILITY_LEVEL" | cut --delimiter='.' --fields=2) LIBRARY_VERSION_MINOR=$(printf '%s' "$LIBRARY_VERSION" | cut --delimiter='.' --fields=2) if [ "$LIBRARY_VERSION_MINOR" -lt "$COMPATIBILITY_LEVEL_MINOR" ]; then error_incompatible_versions exit 1 fi fi unset \ COMPATIBILITY_LEVEL \ COMPATIBILITY_LEVEL_MAJOR COMPATIBILITY_LEVEL_MINOR \ LIBRARY_VERSION_MAJOR LIBRARY_VERSION_MINOR # Set options ## Set hardcoded defaults options_init_default ## Load defaults from the configuration file config_file_path=$(find_configuration_file "$@") load_configuration_file "$config_file_path" ## Set options from the command-line parse_arguments "$@" # Display the help message, # if called with --help. option_help=$(option_value 'help') if [ "$option_help" -eq 1 ]; then help exit 0 fi unset option_help # Display the version string, # if called with --version. option_version=$(option_value 'version') if [ "$option_version" -eq 1 ]; then printf '%s\n' "$LIBRARY_VERSION" exit 0 fi unset option_version # If called with --list-supported-games, # print the list of games supported by the current game script, # then exit early. option_list_supported_games=$(option_value 'list-supported-games') if [ "$option_list_supported_games" -eq 1 ]; then games_list_supported exit 0 fi unset option_list_supported_games # Check the validity of all options options_validity_check options_compatibility_check # Set the base archive that is going to be used. archive_initialize_base # Set the default package, used by the context system when no explicit package context is set packages_list=$(packages_list) default_package=$(printf '%s' "$packages_list" | head --lines=1) set_default_package "$default_package" unset packages_list default_package # Set the path to the temporary directory used by ./play.it. # This can not be set earlier, because GAME_ID (required to compute this path) might be unset before the call to archive_initialize_base. set_temp_directories # Display the path to the game script, # if called with --show-game-script. option_show_game_script=$(option_value 'show-game-script') if [ "$option_show_game_script" -eq 1 ]; then printf '%s\n' "$(realpath "$0")" ## Delete temporary files rm --force --recursive "${PLAYIT_WORKDIR:-}" exit 0 fi unset option_show_game_script # If called with --list-packages, # print the list of packages that would be generated from the given archive # then exit early. option_list_packages=$(option_value 'list-packages') if [ "$option_list_packages" -eq 1 ]; then archive=$(current_archive) packages_print_list "$archive" ## Delete temporary files rm --force --recursive "${PLAYIT_WORKDIR:-}" exit 0 fi unset option_list_packages # If called with --list-requirements, # print the list of commands required to run the current game script # then exit early. option_list_requirements=$(option_value 'list-requirements') if [ "$option_list_requirements" -eq 1 ]; then requirements_list ## Delete temporary files rm --force --recursive "${PLAYIT_WORKDIR:-}" exit 0 fi unset option_list_requirements # Check the list of packages that would be built, # exit early if all are already there. option_overwrite=$(option_value 'overwrite') if [ "$option_overwrite" -eq 0 ]; then option_output_dir=$(option_value 'output-dir') archive=$(current_archive) generated_packages_list=$(packages_print_list "$archive") missing_package=0 while read -r generated_package; do if [ ! -e "${option_output_dir}/${generated_package}" ]; then missing_package=1 break fi done <<- EOL $(printf '%s' "$generated_packages_list") EOL if [ "$missing_package" -eq 0 ]; then info_all_packages_already_built ## Delete temporary files rm --force --recursive "${PLAYIT_WORKDIR:-}" exit 0 fi unset option_output_dir generated_packages_list generated_package missing_package fi unset option_overwrite # Check the presence of required tools { check_deps check_deps_status=$? } || true if [ $check_deps_status -ne 0 ]; then ## Delete temporary files rm --force --recursive "${PLAYIT_WORKDIR:-}" exit 1 fi unset check_deps_status archive=$(current_archive) { archive_dependencies_check "$archive" archive_dependencies_check_status=$? } || true if [ $archive_dependencies_check_status -ne 0 ]; then ## Delete temporary files rm --force --recursive "${PLAYIT_WORKDIR:-}" exit 1 fi unset archive archive_dependencies_check_status # Check for the presence of extra required archives { archives_required_extra_presence_check archives_required_extra_presence_check_status=$? } || true if [ $archives_required_extra_presence_check_status -ne 0 ]; then ## Delete temporary files rm --force --recursive "${PLAYIT_WORKDIR:-}" exit 1 fi unset archives_required_extra_presence_check_status # Check for the presence of extra optional archives archives_optional_extra_presence_check # Check the archives integrity { archives_integrity_check archives_integrity_check_status=$? } || true if [ $archives_integrity_check_status -ne 0 ]; then ## Delete temporary files rm --force --recursive "${PLAYIT_WORKDIR:-}" exit 1 fi unset archives_integrity_check_status fi tests/shunit2/coverage.sh0000755000000000000000000000154113120060140014430 0ustar rootroot#!/bin/sh source_functions_counter=0 tested_functions_counter=0 while read source_file; do printf 'Testing shUnit2 coverage for %s…\n' "$source_file" while read source_function; do test -n "$source_function" || continue source_functions_counter=$((source_functions_counter + 1)) test_function="test_${source_function}" if grep --quiet "^${test_function}() {" tests/shunit2/*/*.sh; then tested_functions_counter=$((tested_functions_counter + 1)) printf '\033[1;32m[Found]\033[0m %s\n' "$source_function" else printf '\033[1;31m[Missing]\033[0m %s\n' "$source_function" fi done <<- EOL $(sed --silent 's/^\([^\s].*\)() {/\1/p' "$source_file") EOL done << EOL $(find src/ -name \*.sh ! -name 90_messages.sh | LANG=C sort) EOL printf '\n%s/%s functions are covered by unit tests.\n' "$tested_functions_counter" "$source_functions_counter" tests/shunit2/10_common/00_common.sh0000644000000000000000000000652713120060140016222 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh # Set some default option values export PLAYIT_OPTION_DEBUG=0 } setUp() { # Set a temporary directory to mess with real files TEST_TEMP_DIR=$(mktemp --directory) export TEST_TEMP_DIR } tearDown() { rm --force --recursive "$TEST_TEMP_DIR" } test_set_standard_permissions() { # Create a file and a directory with inappropriate permissions local test_directory test_file test_directory="${TEST_TEMP_DIR}/test_directory" test_file="${TEST_TEMP_DIR}/test_file" mkdir --parents "$test_directory" touch "$test_file" chmod 777 "$test_directory" "$test_file" # Enforce standard permissions set_standard_permissions "$TEST_TEMP_DIR" # Check the permissions local test_directory_permissions test_file_permissions test_directory_permissions=$(stat --printf='%a' "$test_directory") test_file_permissions=$(stat --printf='%a' "$test_file") assertEquals 755 "$test_directory_permissions" assertEquals 644 "$test_file_permissions" } _test_tolower_generic() { local tolower_command tolower_command="$1" # Create a file and a directory using mixed case local test_directory test_file test_directory="${TEST_TEMP_DIR}/Test_Directory" test_file="${test_directory}/Test_File" mkdir --parents "$test_directory" touch "$test_file" # Convert all paths to lowercase $tolower_command "$TEST_TEMP_DIR" # Check that the paths have been converted assertTrue "test -f '${TEST_TEMP_DIR}/test_directory/test_file'" assertFalse "test -f '${TEST_TEMP_DIR}/Test_Directory/Test_File'" } test_tolower() { _test_tolower_generic 'tolower' } test_tolower_convmv() { _test_tolower_generic 'tolower_convmv' } test_tolower_shell() { _test_tolower_generic 'tolower_shell' } _test_toupper_generic() { local toupper_command toupper_command="$1" # Create a file and a directory using mixed case local test_directory test_file test_directory="${TEST_TEMP_DIR}/Test_Directory" test_file="${test_directory}/Test_File" mkdir --parents "$test_directory" touch "$test_file" # Convert all paths to uppercase $toupper_command "$TEST_TEMP_DIR" # Check that the paths have been converted assertTrue "test -f '${TEST_TEMP_DIR}/TEST_DIRECTORY/TEST_FILE'" assertFalse "test -f '${TEST_TEMP_DIR}/Test_Directory/Test_File'" } test_toupper() { _test_toupper_generic 'toupper' } test_toupper_convmv() { _test_toupper_generic 'toupper_convmv' } test_toupper_shell() { _test_toupper_generic 'toupper_shell' } test_guess_tar_implementation() { local PLAYIT_TAR_IMPLEMENTATION # Mock GNU tar output tar() { printf 'tar (GNU tar) 1.34 Copyright © 2021 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later . This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Written by John Gilmore and Jay Fenlason. ' } guess_tar_implementation assertEquals 'gnutar' "$PLAYIT_TAR_IMPLEMENTATION" unset PLAYIT_TAR_IMPLEMENTATION # Mock BSD tar output tar() { printf 'bsdtar 3.6.2 - libarchive 3.6.2 zlib/1.2.13 liblzma/5.4.0 bz2lib/1.0.8 liblz4/1.9.4 libzstd/1.5.2\n' } guess_tar_implementation assertEquals 'bsdtar' "$PLAYIT_TAR_IMPLEMENTATION" unset -f tar } test_file_type() { local file_type file_type=$(file_type 'play.it') assertEquals 'text/x-shellscript' "$file_type" } tests/shunit2/10_common/10_checks.sh0000644000000000000000000000046613120060140016167 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } test_assert_not_empty() { local empty not_empty empty='' not_empty='something' assertTrue "assert_not_empty 'not_empty'" assertFalse "assert_not_empty 'empty'" assertFalse "assert_not_empty 'not_set'" } tests/shunit2/10_common/10_compatibility-level.sh0000644000000000000000000000251613120060140020703 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } test_compatibility_level() { local compatibility_level PLAYIT_COMPATIBILITY_LEVEL target_version LIBRARY_VERSION PLAYIT_COMPATIBILITY_LEVEL='2.22' compatibility_level=$(compatibility_level) assertEquals '2.22' "$compatibility_level" unset PLAYIT_COMPATIBILITY_LEVEL target_version='2.21' compatibility_level=$(compatibility_level) assertEquals '2.21' "$compatibility_level" unset target_version ## The LIBRARY_VERSION value is used as a fallback if no compatibility level is set from the game script. LIBRARY_VERSION='1.42.0' compatibility_level=$(compatibility_level) assertEquals '1.42' "$compatibility_level" unset LIBRARY_VERSION ## Check that errors are thrown on invalid values. PLAYIT_COMPATIBILITY_LEVEL='2' assertFalse 'compatibility_level' PLAYIT_COMPATIBILITY_LEVEL='2.29.0' assertFalse 'compatibility_level' unset PLAYIT_COMPATIBILITY_LEVEL } test_compatibility_level_is_at_least() { local PLAYIT_COMPATIBILITY_LEVEL PLAYIT_COMPATIBILITY_LEVEL='2.21' assertTrue 'compatibility_level_is_at_least "1.0"' assertTrue 'compatibility_level_is_at_least "2.0"' assertTrue 'compatibility_level_is_at_least "2.21"' assertFalse 'compatibility_level_is_at_least "2.42"' assertFalse 'compatibility_level_is_at_least "3.0"' } tests/shunit2/10_common/10_context.sh0000644000000000000000000001651713120060140016417 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } test_set_current_archive() { local PLAYIT_CONTEXT_ARCHIVE set_current_archive 'ARCHIVE_BASE_0' assertEquals 'ARCHIVE_BASE_0' "$PLAYIT_CONTEXT_ARCHIVE" # Check that the format ARCHIVE_BASE_xxx is enforced assertFalse 'set_current_archive "ARCHIVE_GOG_OLD0"' } test_set_current_package() { local PLAYIT_CONTEXT_PACKAGE PACKAGES_LIST set_current_package 'PKG_MAIN' assertEquals 'PKG_MAIN' "$PLAYIT_CONTEXT_PACKAGE" # Check that the format PKG_xxx is enforced assertFalse 'set_current_package "PACKAGE_MAIN"' # An error should be thrown when trying to set a package that is not included in the list of packages to build. PACKAGES_LIST=' PKG_BIN PKG_DATA' assertFalse 'set_current_package "PKG_BIN32"' } test_set_default_package() { local PLAYIT_CONTEXT_PACKAGE_DEFAULT PACKAGES_LIST set_default_package 'PKG_MAIN' assertEquals 'PKG_MAIN' "$PLAYIT_CONTEXT_PACKAGE_DEFAULT" # Check that the format PKG_xxx is enforced assertFalse 'set_default_package "PACKAGE_MAIN"' # An error should be thrown when trying to set a package that is not included in the list of packages to build. PACKAGES_LIST=' PKG_BIN PKG_DATA' assertFalse 'set_default_package "PKG_BIN32"' } test_current_archive() { local current_archive PLAYIT_COMPATIBILITY_LEVEL PLAYIT_CONTEXT_ARCHIVE ARCHIVE current_archive=$(current_archive) assertNull "$current_archive" set_current_archive 'ARCHIVE_BASE_0' current_archive=$(current_archive) assertEquals 'ARCHIVE_BASE_0' "$current_archive" unset PLAYIT_CONTEXT_ARCHIVE # Set the current archive using the legacy global variable PLAYIT_COMPATIBILITY_LEVEL='2.25' ARCHIVE='ARCHIVE_BASE_1' current_archive=$(current_archive) assertEquals 'ARCHIVE_BASE_1' "$current_archive" unset PLAYIT_COMPATIBILITY_LEVEL PLAYIT_CONTEXT_ARCHIVE ARCHIVE # Check the priority order between the current context system and the legacy one. PLAYIT_COMPATIBILITY_LEVEL='2.25' set_current_archive 'ARCHIVE_BASE_2' ARCHIVE='ARCHIVE_BASE_3' current_archive=$(current_archive) assertEquals \ 'current_archive ignored the value of $ARCHIVE because set_current_archive has been used. This will break compatibility with game scripts relying on the ability to set the context using $ARCHIVE.' \ 'ARCHIVE_BASE_3' "$current_archive" unset PLAYIT_COMPATIBILITY_LEVEL PLAYIT_CONTEXT_ARCHIVE ARCHIVE } test_current_package() { local PACKAGES_LIST PLAYIT_CONTEXT_PACKAGE_DEFAULT PLAYIT_CONTEXT_PACKAGE PLAYIT_COMPATIBILITY_LEVEL PKG assertNull "$(current_package)" PACKAGES_LIST=' PKG_BIN32 PKG_BIN64 PKG_L10N PKG_DATA' set_default_package 'PKG_BIN32' assertEquals 'PKG_BIN32' "$(current_package)" set_current_package 'PKG_BIN64' assertEquals 'PKG_BIN64' "$(current_package)" # Backwards compatibility with compatibility level ≤ 2.25 PLAYIT_COMPATIBILITY_LEVEL='2.25' ## Set the current package using the legacy global variable PKG='PKG_L10N' assertEquals 'PKG_L10N' "$(current_package)" ## Check the priority order between the current context system and the legacy one PKG='PKG_DATA' current_package=$(current_package) assertEquals \ 'current_package ignored the value of $PKG because set_current_package has been used. This will break compatibility with game scripts relying on the ability to set the context using $PKG.' \ 'PKG_DATA' "$(current_package)" } test_default_package() { local PLAYIT_CONTEXT_PACKAGE_DEFAULT assertNull "$(default_package)" set_default_package 'PKG_MAIN' assertEquals 'PKG_MAIN' "$(default_package)" } test_current_archive_suffix() { local PLAYIT_CONTEXT_ARCHIVE set_current_archive 'ARCHIVE_BASE_MULTIARCH_0' assertEquals '_MULTIARCH_0' "$(current_archive_suffix)" } test_current_package_suffix() { local current_package_suffix PLAYIT_CONTEXT_PACKAGE PACKAGES_LIST PACKAGES_LIST=' PKG_BIN32 PKG_BIN64 PKG_DATA' set_current_package 'PKG_BIN32' current_package_suffix=$(current_package_suffix) assertEquals '_BIN32' "$current_package_suffix" } test_context_name() { local \ context_name \ PLAYIT_CONTEXT_ARCHIVE PLAYIT_CONTEXT_PACKAGE PACKAGES_LIST \ SOME_VARIABLE SOME_VARIABLE_BIN32 SOME_VARIABLE_MULTIARCH SOME_VARIABLE_MULTIARCH_0 set_current_archive 'ARCHIVE_BASE_MULTIARCH_0' PACKAGES_LIST=' PKG_BIN32 PKG_BIN64 PKG_DATA' set_current_package 'PKG_BIN32' context_name=$(context_name 'SOME_VARIABLE') assertNull "$context_name" SOME_VARIABLE='some value' context_name=$(context_name 'SOME_VARIABLE') assertEquals 'SOME_VARIABLE' "$context_name" SOME_VARIABLE_BIN32='some value' context_name=$(context_name 'SOME_VARIABLE') assertEquals 'SOME_VARIABLE_BIN32' "$context_name" SOME_VARIABLE_MULTIARCH='some value' context_name=$(context_name 'SOME_VARIABLE') assertEquals 'SOME_VARIABLE_MULTIARCH' "$context_name" SOME_VARIABLE_MULTIARCH_0='some value' context_name=$(context_name 'SOME_VARIABLE') assertEquals 'SOME_VARIABLE_MULTIARCH_0' "$context_name" } test_context_name_archive() { local \ context_name_archive \ PLAYIT_CONTEXT_ARCHIVE \ SOME_VARIABLE SOME_VARIABLE_MULTIARCH SOME_VARIABLE_MULTIARCH_0 set_current_archive 'ARCHIVE_BASE_MULTIARCH_0' context_name_archive=$(context_name_archive 'SOME_VARIABLE') assertNull "$context_name_archive" SOME_VARIABLE='some value' context_name_archive=$(context_name_archive 'SOME_VARIABLE') assertNull "$context_name_archive" SOME_VARIABLE_MULTIARCH='some value' context_name_archive=$(context_name_archive 'SOME_VARIABLE') assertEquals 'SOME_VARIABLE_MULTIARCH' "$context_name_archive" SOME_VARIABLE_MULTIARCH_0='some value' context_name_archive=$(context_name_archive 'SOME_VARIABLE') assertEquals 'SOME_VARIABLE_MULTIARCH_0' "$context_name_archive" } test_context_name_package() { local \ context_name_package \ PLAYIT_CONTEXT_PACKAGE PACKAGES_LIST \ SOME_VARIABLE SOME_VARIABLE_BIN32 PACKAGES_LIST=' PKG_BIN32 PKG_BIN64 PKG_DATA' set_current_package 'PKG_BIN32' context_name_package=$(context_name_package 'SOME_VARIABLE') assertNull "$context_name_package" SOME_VARIABLE='some value' context_name_package=$(context_name_package 'SOME_VARIABLE') assertNull "$context_name_package" SOME_VARIABLE_BIN32='some value' context_name_package=$(context_name_package 'SOME_VARIABLE') assertEquals 'SOME_VARIABLE_BIN32' "$context_name_package" } test_context_value() { local \ context_value \ PLAYIT_CONTEXT_ARCHIVE PLAYIT_CONTEXT_PACKAGE PACKAGES_LIST \ SOME_VARIABLE SOME_VARIABLE_BIN32 SOME_VARIABLE_MULTIARCH SOME_VARIABLE_MULTIARCH_0 set_current_archive 'ARCHIVE_BASE_MULTIARCH_0' PACKAGES_LIST=' PKG_BIN32 PKG_BIN64 PKG_DATA' set_current_package 'PKG_BIN32' context_value=$(context_value 'SOME_VARIABLE') assertNull "$context_value" SOME_VARIABLE='some value' context_value=$(context_value 'SOME_VARIABLE') assertEquals 'some value' "$context_value" SOME_VARIABLE_BIN32='some value specific to PKG_BIN32' context_value=$(context_value 'SOME_VARIABLE') assertEquals 'some value specific to PKG_BIN32' "$context_value" SOME_VARIABLE_MULTIARCH='some value specific to ARCHIVE_BASE_MULTIARCH_*' context_value=$(context_value 'SOME_VARIABLE') assertEquals 'some value specific to ARCHIVE_BASE_MULTIARCH_*' "$context_value" SOME_VARIABLE_MULTIARCH_0='some value specific to ARCHIVE_BASE_MULTIARCH_0' context_value=$(context_value 'SOME_VARIABLE') assertEquals 'some value specific to ARCHIVE_BASE_MULTIARCH_0' "$context_value" } tests/shunit2/10_common/10_lists.sh0000644000000000000000000000053013120060140016055 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } test_list_clean() { local list_unsorted list_sorted list_expected list_unsorted='aaa ccc bbb aaa ' list_expected='aaa bbb ccc' list_sorted=$(printf '%s' "$list_unsorted" | list_clean) assertEquals "$list_expected" "$list_sorted" } tests/shunit2/10_common/10_variables-manipulation.sh0000644000000000000000000000070013120060140021364 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } test_get_value() { local SOME_VARIABLE variable_value SOME_VARIABLE='some value' variable_value=$(get_value 'SOME_VARIABLE') assertEquals 'some value' "$variable_value" } test_variable_is_empty() { local SOME_VARIABLE assertTrue 'variable_is_empty SOME_VARIABLE' SOME_VARIABLE='some value' assertFalse 'variable_is_empty SOME_VARIABLE' } tests/shunit2/10_common/20_temporary-directories.sh0000644000000000000000000000143013120060140021254 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh # Set some default option values PLAYIT_OPTION_TMPDIR=$(mktemp --directory --dry-run) PLAYIT_OPTION_FREE_SPACE_CHECK=0 export PLAYIT_OPTION_TMPDIR PLAYIT_OPTION_FREE_SPACE_CHECK } setUp() { mkdir --parents "$PLAYIT_OPTION_TMPDIR" } tearDown() { rm --force --recursive "$PLAYIT_OPTION_TMPDIR" } test_set_temp_directories() { local GAME_ID PLAYIT_WORKDIR PKG_MAIN_PATH GAME_ID='some-game' set_temp_directories assertTrue "test -n '$PLAYIT_WORKDIR'" assertTrue "test -d '$PLAYIT_WORKDIR'" } test_temporary_directory_path() { local temporary_directory_path temporary_directory_path=$(temporary_directory_path) assertEquals "$PLAYIT_OPTION_TMPDIR" "$temporary_directory_path" } tests/shunit2/10_games/10_scripts.sh0000644000000000000000000000606713120060140016225 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh # Set up a couple fake game scripts TEST_TEMP_DIR=$(mktemp --directory) export TEST_TEMP_DIR mkdir \ "${TEST_TEMP_DIR}/00_scripts-collection" \ "${TEST_TEMP_DIR}/99_old-scripts-collection" touch \ "${TEST_TEMP_DIR}/00_scripts-collection/play-some-game.sh" \ "${TEST_TEMP_DIR}/00_scripts-collection/play-some-other-game.sh" \ "${TEST_TEMP_DIR}/99_old-scripts-collection/play-some-game.sh" cat > "${TEST_TEMP_DIR}/00_scripts-collection/play-some-game.sh" <<- EOF ARCHIVE_BASE_0='some_game_archive.tar.gz' EOF cat > "${TEST_TEMP_DIR}/00_scripts-collection/play-some-other-game.sh" <<- EOF ARCHIVE_BASE_0='another_game_archive.zip' EOF cat > "${TEST_TEMP_DIR}/99_old-scripts-collection/play-some-game.sh" <<- EOF ARCHIVE_MAIN='some_game_archive.tar.gz' EOF } oneTimeTearDown() { rm --force --recursive "$TEST_TEMP_DIR" } test_games_list_scripts_all() { local game_scripts_list game_scripts_list_expected # Use a fake list of sources, because we can not rely on real game scripts being available games_list_sources() { printf '%s\n' \ "${TEST_TEMP_DIR}/00_scripts-collection" \ "${TEST_TEMP_DIR}/99_old-scripts-collection" } game_scripts_list_expected="${TEST_TEMP_DIR}/00_scripts-collection/play-some-game.sh ${TEST_TEMP_DIR}/00_scripts-collection/play-some-other-game.sh ${TEST_TEMP_DIR}/99_old-scripts-collection/play-some-game.sh" game_scripts_list=$(games_list_scripts_all) assertEquals "$game_scripts_list_expected" "$game_scripts_list" } test_games_find_scripts_for_archive() { local game_scripts game_scripts_expected # Use a fake list of sources, because we can not rely on real game scripts being available games_list_sources() { printf '%s\n' \ "${TEST_TEMP_DIR}/00_scripts-collection" \ "${TEST_TEMP_DIR}/99_old-scripts-collection" } game_scripts_expected="${TEST_TEMP_DIR}/00_scripts-collection/play-some-game.sh ${TEST_TEMP_DIR}/99_old-scripts-collection/play-some-game.sh" game_scripts=$(games_find_scripts_for_archive 'some_game_archive.tar.gz') assertEquals "$game_scripts_expected" "$game_scripts" } test_games_find_script_for_archive() { local game_script game_script_expected # Use a fake list of sources, because we can not rely on real game scripts being available games_list_sources() { printf '%s\n' \ "${TEST_TEMP_DIR}/00_scripts-collection" \ "${TEST_TEMP_DIR}/99_old-scripts-collection" } game_script_expected="${TEST_TEMP_DIR}/00_scripts-collection/play-some-game.sh" game_script=$(games_find_script_for_archive 'some_game_archive.tar.gz') assertEquals "$game_script_expected" "$game_script" } test_script_version() { local version_string script_version script_version='19700101.1' version_string=$(script_version) assertEquals '19700101.1' "$version_string" # An error is thrown if the script version is not set unset script_version assertFalse 'script_version' # An error is thrown is the script version does not follow the expected "YYYYMMDD.N" format script_version='19700101' assertFalse 'script_version' } tests/shunit2/20_configuration/00_configuration.sh0000644000000000000000000000222513120060140021150 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh # Set required environment variables if [ -z "${XDG_CONFIG_PATH:-}" ]; then XDG_CONFIG_PATH="${HOME}/.config" fi # Set a fake configuration file TEST_TEMP_DIR=$(mktemp --directory) TEST_CONFIG_FILE="${TEST_TEMP_DIR}/config" cat > "$TEST_CONFIG_FILE" <<- EOF --tmpdir /var/tmp/play.it EOF } oneTimeTearDown() { rm --force --recursive "$TEST_TEMP_DIR" } test_load_configuration_file() { local option_tmpdir load_configuration_file "$TEST_CONFIG_FILE" option_tmpdir=$(option_value 'tmpdir') assertEquals '/var/tmp/play.it' "$option_tmpdir" } test_find_configuration_file() { local configuration_file configuration_file=$(find_configuration_file) assertEquals "${XDG_CONFIG_PATH}/play.it/config" "$configuration_file" configuration_file=$(find_configuration_file --config-file "$TEST_CONFIG_FILE") assertEquals "$TEST_CONFIG_FILE" "$configuration_file" } test_configuration_file_default_path() { local configuration_file configuration_file=$(configuration_file_default_path) assertEquals "${XDG_CONFIG_PATH}/play.it/config" "$configuration_file" } tests/shunit2/20_configuration/10_arguments.sh0000644000000000000000000000404313120060140020307 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } setUp() { # Set a temporary directory to mess with real files TEST_TEMP_DIR=$(mktemp --directory) export TEST_TEMP_DIR } tearDown() { rm --force --recursive "$TEST_TEMP_DIR" } test_getopt_arguments_cleanup() { local options_string options_string=$(getopt_arguments_cleanup --compression=auto --output-dir debian --tmpdir=/var/tmp/play.it /home/jeux/alpha-centauri/archives/gog.com/setup_sid_meiers_alpha_centauri_2.0.2.23.exe) # TODO: Find why there is a leading space in getopt output. assertEquals " --compression 'auto' --output-dir 'debian' --tmpdir '/var/tmp/play.it' -- '/home/jeux/alpha-centauri/archives/gog.com/setup_sid_meiers_alpha_centauri_2.0.2.23.exe'" "$options_string" # Check error on missing mandatory option value assertFalse 'getopt_arguments_cleanup --output-dir' # Check error on invalid option name assertFalse 'getopt_arguments_cleanup --invalid-option' } test_parse_arguments() { local SOURCE_ARCHIVE_PATH option_overwrite option_free_space_check option_prefix touch "${TEST_TEMP_DIR}/some_game_archive.tar.gz" parse_arguments --overwrite --no-free-space-check --prefix /usr/local "${TEST_TEMP_DIR}/some_game_archive.tar.gz" option_overwrite=$(option_value 'overwrite') assertEquals 1 "$option_overwrite" option_free_space_check=$(option_value 'free-space-check') assertEquals 0 "$option_free_space_check" option_prefix=$(option_value 'prefix') assertEquals '/usr/local' "$option_prefix" assertEquals "${TEST_TEMP_DIR}/some_game_archive.tar.gz" "$SOURCE_ARCHIVE_PATH" } test_parse_arguments_default() { local option_overwrite option_free_space_check option_tmpdir parse_arguments_default --overwrite --no-free-space-check --tmpdir /var/tmp/play.it option_overwrite=$(option_value 'overwrite') assertEquals 1 "$option_overwrite" option_free_space_check=$(option_value 'free-space-check') assertEquals 0 "$option_free_space_check" option_tmpdir=$(option_value 'tmpdir') assertEquals '/var/tmp/play.it' "$option_tmpdir" } tests/shunit2/20_configuration/20_options.sh0000644000000000000000000000371413120060140020002 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } setUp() { # Set a temporary directory to mess with real files TEST_TEMP_DIR=$(mktemp --directory) export TEST_TEMP_DIR } tearDown() { rm --force --recursive "$TEST_TEMP_DIR" } test_options_init_default() { local option_prefix options_init_default option_prefix=$(option_value 'prefix') assertEquals '/usr' "$option_prefix" } test_option_variable() { local option_variable option_variable=$(option_variable 'free-space-check') assertEquals 'PLAYIT_OPTION_FREE_SPACE_CHECK' "$option_variable" } test_option_variable_default() { local option_variable option_variable=$(option_variable_default 'free-space-check') assertEquals 'PLAYIT_DEFAULT_OPTION_FREE_SPACE_CHECK' "$option_variable" } test_option_update() { local PLAYIT_COMPATIBILITY_LEVEL option_value option_update 'prefix' '/usr/local' option_value=$(option_value 'prefix') assertEquals '/usr/local' "$option_value" } test_option_update_default() { local option_value option_update_default 'prefix' '/usr/local' option_value=$(option_value 'prefix') assertEquals '/usr/local' "$option_value" } test_option_value() { local option_value option_update 'prefix' '/usr/local' option_value=$(option_value 'prefix') assertEquals '/usr/local' "$option_value" } test_options_validity_check() { options_init_default # Create paths expected to be existing and writable option_update 'tmpdir' "${TEST_TEMP_DIR}/tmpdir" option_update 'output-dir' "${TEST_TEMP_DIR}output-dir" mkdir "${TEST_TEMP_DIR}/tmpdir" "${TEST_TEMP_DIR}output-dir" option_update 'checksum' 'none' assertTrue 'options_validity_check' option_update 'checksum' 'invalid' assertFalse 'options_validity_check' } test_options_compatibility_check() { option_update 'package' 'arch' option_update 'compression' 'size' assertTrue 'options_compatibility_check' option_update 'compression' 'auto' assertFalse 'options_compatibility_check' } tests/shunit2/20_game/00_common.sh0000644000000000000000000000142413120060140015633 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } test_game_id() { local GAME_ID game_id GAME_ID='some-game' game_id=$(game_id) assertEquals 'some-game' "$game_id" } test_expansion_id() { local EXPANSION_ID expansion_id EXPANSION_ID='some-expansion' expansion_id=$(expansion_id) assertEquals 'some-expansion' "$expansion_id" } test_game_id_validity_check() { assertTrue 'game_id_validity_check "valid-game-id"' assertFalse 'game_id_validity_check "invalid_game_id"' } test_game_name() { local GAME_NAME EXPANSION_NAME game_name GAME_NAME='Some Game' game_name=$(game_name) assertEquals 'Some Game' "$game_name" EXPANSION_NAME='Expansion 1' game_name=$(game_name) assertEquals 'Some Game - Expansion 1' "$game_name" } tests/shunit2/20_game/10_engine.sh0000644000000000000000000000113613120060140015611 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } test_game_engine() { local game_engine GAME_ENGINE UNITY3D_NAME UNREALENGINE4_NAME GAME_ENGINE='visionaire' game_engine=$(game_engine) assertEquals 'visionaire' "$game_engine" unset GAME_ENGINE UNITY3D_NAME='ChildrenOfMorta' game_engine=$(game_engine) assertEquals 'unity3d' "$game_engine" unset UNITY3D_NAME UNREALENGINE4_NAME='raji' game_engine=$(game_engine) assertEquals 'unrealengine4' "$game_engine" unset UNREALENGINE4_NAME game_engine=$(game_engine) assertNull "$game_engine" } tests/shunit2/20_requirements/10_requirements.sh0000644000000000000000000000507013120060140020702 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh # Set some required options PLAYIT_OPTION_ICONS=1 } test_requirements_list() { local \ requirements_list requirements_list_expected \ SCRIPT_DEPS \ PLAYIT_CONTEXT_ARCHIVE ARCHIVE_BASE_0_EXTRACTOR SCRIPT_DEPS='command1 command2 command3' ARCHIVE_BASE_0_EXTRACTOR='unzip' set_current_archive 'ARCHIVE_BASE_0' requirements_list=$(requirements_list) requirements_list_expected='command1 command2 command3 unzip' assertEquals "$requirements_list_expected" "$requirements_list" } test_requirements_list_checksum() { local requirements_list option_update 'checksum' 'md5' requirements_list=$(requirements_list_checksum) assertEquals 'md5sum' "$requirements_list" } test_requirements_list_package() { local requirements_list requirements_list_expected option_update 'package' 'deb' requirements_list=$(requirements_list_package) requirements_list_expected='dpkg-deb' assertEquals "$requirements_list_expected" "$requirements_list" } test_requirements_list_icons() { local APPLICATIONS_LIST APP_MAIN_ICONS_LIST APP_MAIN_ICON requirements_list requirements_list_expected APPLICATIONS_LIST='APP_MAIN' APP_MAIN_ICONS_LIST='APP_MAIN_ICON' APP_MAIN_ICON='icon.exe' requirements_list=$(requirements_list_icons) requirements_list_expected='identify convert wrestool' assertEquals "$requirements_list_expected" "$requirements_list" } test_requirements_list_archive() { local \ requirements_list requirements_list_expected \ PLAYIT_CONTEXT_ARCHIVE ARCHIVE_BASE_0_TYPE ARCHIVE_BASE_0_PART1 ARCHIVE_BASE_0_PART1_TYPE ARCHIVE_BASE_0_TYPE='innosetup' ARCHIVE_BASE_0_PART1='setup_something-1.bin' ARCHIVE_BASE_0_PART1_TYPE='rar' set_current_archive 'ARCHIVE_BASE_0' requirements_list_expected='innoextract unar' requirements_list=$(requirements_list_archive) assertEquals "$requirements_list_expected" "$requirements_list" } test_requirements_list_archive_single() { local ARCHIVE_BASE_0_TYPE ARCHIVE_BASE_0_EXTRACTOR requirements_list requirements_list_expected ARCHIVE_BASE_0_TYPE='mojosetup' requirements_list=$(requirements_list_archive_single 'ARCHIVE_BASE_0') requirements_list_expected='head sed wc tr gzip tar unzip' assertEquals "$requirements_list_expected" "$requirements_list" ARCHIVE_BASE_0_EXTRACTOR='innosetup' requirements_list=$(requirements_list_archive_single 'ARCHIVE_BASE_0') assertEquals 'innosetup' "$requirements_list" } test_dependency_provided_by() { local provider provider=$(dependency_provided_by 'bsdtar') assertEquals 'libarchive' "$provider" } tests/shunit2/30_applications/00_common.sh0000644000000000000000000002055213120060140017414 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh # Set required options PLAYIT_OPTION_FREE_SPACE_CHECK=0 PLAYIT_OPTION_PACKAGE='deb' PLAYIT_OPTION_PREFIX='/usr' script_version='19700101.1' } setUp() { # Set a temporary directory to mess with real files TEST_TEMP_DIR=$(mktemp --directory) export TEST_TEMP_DIR } tearDown() { rm --force --recursive "$TEST_TEMP_DIR" } test_applications_list() { local APPLICATIONS_LIST APP_MAIN_EXE APP_MAIN_EXE_SOME_CONTEXT APP_EXTRA_SCUMMID UNITY3D_NAME applications_list applications_list_expected applications_list=$(applications_list) assertNull "$applications_list" APPLICATIONS_LIST='APP_MAIN APP_CONFIG APP_EDITOR' applications_list=$(applications_list) applications_list_expected='APP_CONFIG APP_EDITOR APP_MAIN' assertEquals "$applications_list_expected" "$applications_list" unset APPLICATIONS_LIST APP_MAIN_EXE='something.x86_64' APP_EXTRA_SCUMMID='engine:game' applications_list=$(applications_list) applications_list_expected='APP_EXTRA APP_MAIN' assertEquals "$applications_list_expected" "$applications_list" unset APP_MAIN_EXE APP_EXTRA_SCUMMID APP_MAIN_EXE_SOME_CONTEXT='something.x86_64' applications_list=$(applications_list) assertEquals 'APP_MAIN' "$applications_list" unset APP_MAIN_EXE_SOME_CONTEXT UNITY3D_NAME='SomeName' applications_list=$(applications_list) assertEquals 'APP_MAIN' "$applications_list" } test_application_prefix_type() { local application_prefix_type APP_MAIN_PREFIX_TYPE APPLICATIONS_PREFIX_TYPE APP_MAIN_TYPE APP_MAIN_PREFIX_TYPE='none' application_prefix_type=$(application_prefix_type 'APP_MAIN') assertEquals 'none' "$application_prefix_type" unset APP_MAIN_PREFIX_TYPE APPLICATIONS_PREFIX_TYPE='symlinks' application_prefix_type=$(application_prefix_type 'APP_MAIN') assertEquals 'symlinks' "$application_prefix_type" unset APPLICATIONS_PREFIX_TYPE APP_MAIN_TYPE='scummvm' application_prefix_type=$(application_prefix_type 'APP_MAIN') assertEquals 'none' "$application_prefix_type" APP_MAIN_TYPE='native' application_prefix_type=$(application_prefix_type 'APP_MAIN') assertEquals 'symlinks' "$application_prefix_type" unset APP_MAIN_TYPE # Invalid values should throw an error. APP_MAIN_PREFIX_TYPE='invalid' assertFalse 'application_prefix_type "APP_MAIN"' } test_application_id() { local GAME_ID APP_MAIN_ID application_id GAME_ID='some-game' application_id=$(application_id 'APP_MAIN') assertEquals 'some-game' "$application_id" APP_MAIN_ID='some-application' application_id=$(application_id 'APP_MAIN') assertEquals 'some-application' "$application_id" # Check the format restriction APP_MAIN_ID='-some-invalid-application-id' assertFalse 'application_id "APP_MAIN"' } test_application_exe() { local APP_MAIN_EXE UNITY3D_NAME application_exe unity3d_application_exe_default() { printf 'SomeGame.x86_64' } UNITY3D_NAME='SomeGame' application_exe=$(application_exe 'APP_MAIN') assertEquals 'SomeGame.x86_64' "$application_exe" APP_MAIN_EXE='SomeGame.exe' application_exe=$(application_exe 'APP_MAIN') assertEquals 'SomeGame.exe' "$application_exe" } test_application_exe_escaped() { local APP_MAIN_EXE application_exe_escaped APP_MAIN_EXE="Some'Tricky'Name.x86" application_exe_escaped=$(application_exe_escaped 'APP_MAIN') assertEquals "Some'\''Tricky'\''Name.x86" "$application_exe_escaped" } test_application_exe_path() { local \ PLAYIT_OPTION_TMPDIR GAME_ID CONTENT_PATH_DEFAULT APP_MAIN_EXE \ PACKAGES_LIST PKG_BIN32_ID PKG_BIN64_ID PKG_DATA_ID \ PLAYIT_WORKDIR \ application_exe_path_gamedata application_exe_path_bin32 application_exe_path_bin64 application_exe_path_data \ PLAYIT_CONTEXT_PACKAGE_DEFAULT PLAYIT_CONTEXT_PACKAGE # Create a couple files in the temporary directory, and set required variables. PLAYIT_OPTION_TMPDIR="$TEST_TEMP_DIR" GAME_ID='some-game' CONTENT_PATH_DEFAULT='.' APP_MAIN_EXE='SomeGame.exe' PACKAGES_LIST=' PKG_BIN32 PKG_BIN64 PKG_DATA' PKG_BIN32_ID='some-game-bin32' PKG_BIN64_ID='some-game-bin64' PKG_DATA_ID='some-game-data' set_default_package 'PKG_BIN32' set_temp_directories application_exe_path_gamedata="${PLAYIT_WORKDIR}/gamedata/${CONTENT_PATH_DEFAULT}/${APP_MAIN_EXE}" application_exe_path_bin32="${PLAYIT_WORKDIR}/packages/${PKG_BIN32_ID}_1.0-1+19700101.1_all/usr/share/games/${GAME_ID}/${APP_MAIN_EXE}" application_exe_path_bin64="${PLAYIT_WORKDIR}/packages/${PKG_BIN64_ID}_1.0-1+19700101.1_all/usr/share/games/${GAME_ID}/${APP_MAIN_EXE}" application_exe_path_data="${PLAYIT_WORKDIR}/packages/${PKG_DATA_ID}_1.0-1+19700101.1_all/usr/share/games/${GAME_ID}/${APP_MAIN_EXE}" mkdir --parents \ "$(dirname "$application_exe_path_gamedata")" \ "$(dirname "$application_exe_path_bin32")" \ "$(dirname "$application_exe_path_bin64")" \ "$(dirname "$application_exe_path_data")" touch \ "$application_exe_path_gamedata" \ "$application_exe_path_bin32" \ "$application_exe_path_bin64" \ "$application_exe_path_data" # Look in archive contents assertEquals "$application_exe_path_gamedata" "$(application_exe_path "$APP_MAIN_EXE")" rm "$application_exe_path_gamedata" # Look in the current package set_current_package 'PKG_BIN64' assertEquals "$application_exe_path_bin64" "$(application_exe_path "$APP_MAIN_EXE")" rm "$application_exe_path_bin32" "$application_exe_path_bin64" # Look in all packages assertEquals "$application_exe_path_data" "$(application_exe_path "$APP_MAIN_EXE")" rm "$application_exe_path_data" } test_application_name() { local GAME_NAME APP_MAIN_NAME application_name GAME_NAME='Some Game' application_name=$(application_name 'APP_MAIN') assertEquals 'Some Game' "$application_name" APP_MAIN_NAME='Some Application' application_name=$(application_name 'APP_MAIN') assertEquals 'Some Application' "$application_name" } test_application_category() { local APP_MAIN_CAT application_category application_category=$(application_category 'APP_MAIN') assertEquals 'Game' "$application_category" APP_MAIN_CAT='Settings' application_category=$(application_category 'APP_MAIN') assertEquals 'Settings' "$application_category" } test_application_prerun() { local \ application_prerun application_prerun_expected \ APP_MAIN_PRERUN \ PLAYIT_COMPATIBILITY_LEVEL APP_MAIN_TYPE \ HACK_SDL1COMPAT_NAME HACK_SDL1COMPAT_DESCRIPTION HACK_NOATIME_NAME HACK_NOATIME_DESCRIPTION APP_MAIN_PRERUN='some commands' application_prerun=$(application_prerun 'APP_MAIN') assertEquals 'some commands' "$application_prerun" # Check that LD_PRELOAD hacks are automatically added hacks_included_in_package() { printf '%s\n' 'HACK_SDL1COMPAT' 'HACK_NOATIME' } path_libraries() { printf '/usr/lib/games/some-game' } HACK_SDL1COMPAT_NAME='smacshim' HACK_SDL1COMPAT_DESCRIPTION='LD_PRELOAD shim allowing the old engine to run on top of latest SDL1 library cf. https://github.com/ZeroPointEnergy/smacshim' HACK_NOATIME_NAME='vv_noatime' HACK_NOATIME_DESCRIPTION='LD_PRELOAD shim working around the engine expectation that files are owned by the current user' application_prerun=$(application_prerun 'APP_MAIN') application_prerun_expected='some commands # LD_PRELOAD shim allowing the old engine to run on top of latest SDL1 library # cf. https://github.com/ZeroPointEnergy/smacshim export LD_PRELOAD="${LD_PRELOAD}:/usr/lib/games/some-game/preload-hacks/smacshim.so" # LD_PRELOAD shim working around the engine expectation that files are owned by the current user export LD_PRELOAD="${LD_PRELOAD}:/usr/lib/games/some-game/preload-hacks/vv_noatime.so"' assertEquals "$application_prerun_expected" "$application_prerun" } test_application_postrun() { local APP_MAIN_POSTRUN APP_MAIN_TYPE PLAYIT_COMPATIBILITY_LEVEL application_postrun APP_MAIN_TYPE='native' APP_MAIN_POSTRUN='some commands' application_postrun=$(application_postrun 'APP_MAIN') assertEquals 'some commands' "$application_postrun" } test_application_options() { local APP_MAIN_OPTIONS application_options application_options=$(application_options 'APP_MAIN') assertNull "$application_options" APP_MAIN_OPTIONS='--some-option --some-other-option' application_options=$(application_options 'APP_MAIN') assertEquals '--some-option --some-other-option' "$application_options" # Ensure that line breaks are not allowed APP_MAIN_OPTIONS='--some-option --some-other-option' assertFalse 'application_options failed to correctly prevent a multi-lines options string.' 'application_options "APP_MAIN"' return 0 } tests/shunit2/30_applications/10_type.sh0000644000000000000000000000103013120060140017074 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } test_application_type() { local APP_MAIN_TYPE APP_MAIN_SCUMMID application_type APP_MAIN_SCUMMID='engine:game' application_type=$(application_type 'APP_MAIN') assertEquals 'scummvm' "$application_type" APP_MAIN_TYPE='native' application_type=$(application_type 'APP_MAIN') assertEquals 'native' "$application_type" # Test that invalid types are rejected APP_MAIN_TYPE='invalid' assertFalse "application_type 'APP_MAIN'" } tests/shunit2/30_archives/00_selection.sh0000644000000000000000000000625013120060140017226 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } setUp() { # Set a temporary directory to mess with real files TEST_TEMP_DIR=$(mktemp --directory) export TEST_TEMP_DIR } tearDown() { rm --force --recursive "$TEST_TEMP_DIR" } test_archives_path_base() { local PLAYIT_ARCHIVES_PATH_BASE PLAYIT_ARCHIVES_PATH_BASE='/home/jeux/alpha-centauri/archives/gog.com' archives_path_base=$(archives_path_base) assertEquals '/home/jeux/alpha-centauri/archives/gog.com' "$archives_path_base" unset PLAYIT_ARCHIVES_PATH_BASE # Falls back on $PWD if $PLAYIT_ARCHIVES_PATH_BASE is not set archives_path_base=$(archives_path_base) assertEquals "$PWD" "$archives_path_base" } test_archives_integrity_check_md5() { local PLAYIT_ARCHIVES_USED_LIST PLAYIT_WORKDIR ARCHIVE_BASE_0_MD5 PLAYIT_ARCHIVES_USED_LIST='ARCHIVE_BASE_0' PLAYIT_WORKDIR="$TEST_TEMP_DIR" mkdir --parents "${PLAYIT_WORKDIR}/cache" cat >> "${PLAYIT_WORKDIR}/cache/hashes" <<- EOF ARCHIVE_BASE_0 | e70187d92fa120771db99dfa81679cfc EOF ARCHIVE_BASE_0_MD5='e70187d92fa120771db99dfa81679cfc' assertTrue 'archives_integrity_check_md5' rm "${PLAYIT_WORKDIR}/cache/hashes" mkdir --parents "${PLAYIT_WORKDIR}/cache" cat >> "${PLAYIT_WORKDIR}/cache/hashes" <<- EOF ARCHIVE_BASE_0 | 62298f00f1f2268c8d5004f5b2e9fc93 EOF ARCHIVE_BASE_0_MD5='2ee16fab54493e1c2a69122fd2e56635' assertFalse 'archives_integrity_check_md5' mkdir --parents "${PLAYIT_WORKDIR}/cache" cat >> "${PLAYIT_WORKDIR}/cache/hashes" <<- EOF ARCHIVE_BASE_0 | eda90c314fd4d845ec9f3805b44de9f8 EOF unset ARCHIVE_BASE_0_MD5 assertTrue 'A hashsum mismatch has been triggered for an archive that has no expected MD5 hash.' 'archives_integrity_check_md5' } test_archives_used_list() { local archives_list archives_list_expected PLAYIT_ARCHIVES_USED_LIST PLAYIT_ARCHIVES_USED_LIST='ARCHIVE_BASE_0 ARCHIVE_REQUIRED_ENGINE ARCHIVE_OPTIONAL_ICONS' archives_list=$(archives_used_list) archives_list_expected='ARCHIVE_BASE_0 ARCHIVE_REQUIRED_ENGINE ARCHIVE_OPTIONAL_ICONS' assertEquals "$archives_list_expected" "$archives_list" unset PLAYIT_ARCHIVES_USED_LIST # The list can be empty archives_list=$(archives_used_list) assertNull "$archives_list" } test_archives_used_add() { local archives_list archives_list_expected PLAYIT_ARCHIVES_USED_LIST archives_used_add 'ARCHIVE_BASE_0' archives_list=$(archives_used_list) assertEquals 'ARCHIVE_BASE_0' "$archives_list" archives_used_add 'ARCHIVE_REQUIRED_ENGINE' archives_list=$(archives_used_list) archives_list_expected='ARCHIVE_BASE_0 ARCHIVE_REQUIRED_ENGINE' assertEquals "$archives_list_expected" "$archives_list" } test_archives_return_list() { local \ ARCHIVE_BASE_0 ARCHIVE_BASE_1 ARCHIVE_NOT_EXPECTED_FORMAT \ ARCHIVES_LIST \ archives_list_expected ARCHIVE_BASE_0='some_game_archive.tar.gz' ARCHIVE_BASE_1='some_other_game_archive.tar.gz' ARCHIVE_NOT_EXPECTED_FORMAT='some_game_archive_that_should_not_beincluded.tar.gz' archives_list_expected='ARCHIVE_BASE_0 ARCHIVE_BASE_1' assertEquals "$archives_list_expected" "$(archives_return_list)" ARCHIVES_LIST='ARCHIVE_BASE_0 ARCHIVE_BASE_1 ARCHIVE_BASE_2' assertEquals "$ARCHIVES_LIST" "$(archives_return_list)" } tests/shunit2/30_archives/10_details.sh0000644000000000000000000001306213120060140016666 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } setUp() { # Set a temporary directory to mess with real files TEST_TEMP_DIR=$(mktemp --directory) export TEST_TEMP_DIR } tearDown() { rm --force --recursive "$TEST_TEMP_DIR" } test_archive_is_available() { local ARCHIVE_BASE_0_PATH ARCHIVE_BASE_0_PATH='/some/path/to/archive.tar.gz' assertTrue 'archive_is_available "ARCHIVE_BASE_0"' unset ARCHIVE_BASE_0_PATH assertFalse 'archive_is_available "ARCHIVE_BASE_0"' } test_archive_name() { local archive_name ARCHIVE_BASE_0_NAME ARCHIVE_BASE_0 PLAYIT_COMPATIBILITY_LEVEL ARCHIVE_BASE_0_NAME='setup_sid_meiers_alpha_centauri_2.0.2.23.exe' archive_name=$(archive_name 'ARCHIVE_BASE_0') assertEquals 'setup_sid_meiers_alpha_centauri_2.0.2.23.exe' "$archive_name" unset ARCHIVE_BASE_0_NAME # If ARCHIVE_xxx_NAME is not set, the name can be fetched from the legacy variable ARCHIVE_xxx PLAYIT_COMPATIBILITY_LEVEL='2.25' ARCHIVE_BASE_0='stellaris_3_8_4_1_65337.sh' archive_name=$(archive_name 'ARCHIVE_BASE_0') assertEquals 'stellaris_3_8_4_1_65337.sh' "$archive_name" unset ARCHIVE_BASE_0 # An error is thrown if no archive name is set assertFalse 'archive_name "ARCHIVE_BASE_0"' } test_archive_path() { local \ archive_path \ ARCHIVE_BASE_0_PATH \ PLAYIT_ARCHIVES_PATH_BASE ARCHIVE_REQUIRED_OPENSSL100_NAME \ ARCHIVE_INNER PLAYIT_COMPATIBILITY_LEVEL ARCHIVE_BASE_0_PATH='/home/jeux/warcraft-3/archives/blizzard/War3-1.27-Installer-enUS-ROC/Installer Tome.mpq' archive_path=$(archive_path 'ARCHIVE_BASE_0') assertEquals '/home/jeux/warcraft-3/archives/blizzard/War3-1.27-Installer-enUS-ROC/Installer Tome.mpq' "$archive_path" # Compute the archive path from its name PLAYIT_ARCHIVES_PATH_BASE='/home/jeux/baldurs-gate-2-enhanced-edition/archives/gog.com' ARCHIVE_REQUIRED_OPENSSL100_NAME='openssl_1.0.0.tar.xz' archive_path=$(archive_path 'ARCHIVE_REQUIRED_OPENSSL100') assertEquals '/home/jeux/baldurs-gate-2-enhanced-edition/archives/gog.com/openssl_1.0.0.tar.xz' "$archive_path" unset PLAYIT_ARCHIVES_PATH_BASE # Get the archive path from the legacy variable ARCHIVE_xxx PLAYIT_COMPATIBILITY_LEVEL='2.25' ARCHIVE_INNER='/var/tmp/play.it/rayman-origins.A9xV8/gamedata/data1.hdr' archive_path=$(archive_path 'ARCHIVE_INNER') assertEquals '/var/tmp/play.it/rayman-origins.A9xV8/gamedata/data1.hdr' "$archive_path" } test_archive_size() { local archive_size ARCHIVE_BASE_0_SIZE ARCHIVE_BASE_0_SIZE='42000' archive_size=$(archive_size 'ARCHIVE_BASE_0') assertEquals '42000' "$archive_size" unset ARCHIVE_BASE_0_SIZE # Default to 0 if no size is set archive_size=$(archive_size 'ARCHIVE_BASE_0') assertEquals '0' "$archive_size" } test_archive_hash_md5() { local archive_hash ARCHIVE_BASE_0_MD5 ARCHIVE_BASE_0_MD5='6c9bd7e1cf88fdbfa0e75f694bf8b0e5' archive_hash=$(archive_hash_md5 'ARCHIVE_BASE_0') assertEquals '6c9bd7e1cf88fdbfa0e75f694bf8b0e5' "$archive_hash" } test_archive_hash_md5_computed() { local archive_hash PLAYIT_WORKDIR ARCHIVE_BASE_0_NAME ARCHIVE_BASE_0_PATH PLAYIT_WORKDIR="$TEST_TEMP_DIR" mkdir --parents "${PLAYIT_WORKDIR}/cache" cat >> "${PLAYIT_WORKDIR}/cache/hashes" <<- EOF ARCHIVE_BASE_0 | 6c9bd7e1cf88fdbfa0e75f694bf8b0e5 EOF archive_hash=$(archive_hash_md5_computed 'ARCHIVE_BASE_0') assertEquals '6c9bd7e1cf88fdbfa0e75f694bf8b0e5' "$archive_hash" rm "${PLAYIT_WORKDIR}/cache/hashes" ARCHIVE_BASE_0_NAME='null' ARCHIVE_BASE_0_PATH='/dev/null' archive_hash=$(archive_hash_md5_computed 'ARCHIVE_BASE_0') 2>/dev/null assertEquals 'd41d8cd98f00b204e9800998ecf8427e' "$archive_hash" } test_archive_type() { local ARCHIVE_BASE_0_NAME ARCHIVE_BASE_0_TYPE ARCHIVE_BASE_0_PART1_NAME archive_type # Bypass the search for the archive path archive_path() { return 0 } archive_guess_type_from_headers() { return 0 } ARCHIVE_BASE_0_NAME='some_game_archive.tar.gz' archive_type=$(archive_type 'ARCHIVE_BASE_0') assertEquals 'tar.gz' "$archive_type" ARCHIVE_BASE_0_TYPE='zip' archive_type=$(archive_type 'ARCHIVE_BASE_0') assertEquals 'zip' "$archive_type" ARCHIVE_BASE_0_PART1_NAME='some_strange_game_archive.xyz' archive_type=$(archive_type 'ARCHIVE_BASE_0_PART1') assertEquals 'zip' "$archive_type" ARCHIVE_BASE_0_NAME='some_strange_game_archive.xyz' unset ARCHIVE_BASE_0_TYPE archive_type=$(archive_type 'ARCHIVE_BASE_0') assertNull "$archive_type" } test_archive_guess_type_from_name() { local archive_type archive_type=$(archive_guess_type_from_name 'some_game_archive.tar.gz') assertEquals 'tar.gz' "$archive_type" archive_type=$(archive_guess_type_from_name 'some_strange_game_archive.xyz') assertNull 'archive_guess_type_from_name hallucinated a type for an archive using an unknown extension.' "$archive_type" return 0 } test_archive_extractor() { local ARCHIVE_BASE_0_EXTRACTOR archive_extractor ARCHIVE_BASE_0_EXTRACTOR='bsdtar' archive_extractor=$(archive_extractor 'ARCHIVE_BASE_0') assertEquals 'bsdtar' "$archive_extractor" archive_extractor=$(archive_extractor 'ARCHIVE_BASE_0_PART1') assertEquals 'bsdtar' "$archive_extractor" unset ARCHIVE_BASE_0_EXTRACTOR archive_extractor=$(archive_extractor 'ARCHIVE_BASE_0') assertNull "$archive_extractor" } test_archive_extractor_options() { local ARCHIVE_BASE_0_EXTRACTOR_OPTIONS archive_extractor_options ARCHIVE_BASE_0_EXTRACTOR_OPTIONS='--gog' archive_extractor_options=$(archive_extractor_options 'ARCHIVE_BASE_0') assertEquals '--gog' "$archive_extractor_options" unset ARCHIVE_BASE_0_EXTRACTOR_OPTIONS archive_extractor_options=$(archive_extractor_options 'ARCHIVE_BASE_0') assertNull "$archive_extractor_options" } tests/shunit2/30_archives/50_logs.sh0000644000000000000000000000047513120060140016215 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } test_archive_extraction_log_path() { local PLAYIT_WORKDIR log_path PLAYIT_WORKDIR='/some/temp/dir' log_path=$(archive_extraction_log_path) assertEquals '/some/temp/dir/logs/archive-extraction.log' "$log_path" } tests/shunit2/30_content/00_common.sh0000644000000000000000000000542013120060140016375 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } test_content_path_default() { local \ content_path_default \ CONTENT_PATH_DEFAULT \ PLAYIT_CONTEXT_ARCHIVE CONTENT_PATH_DEFAULT_0 CONTENT_PATH_DEFAULT='some/path' content_path_default=$(content_path_default) assertEquals \ 'content_path_default failed to get an explicitly set value from CONTENT_PATH_DEFAULT.' \ 'some/path' "$content_path_default" unset CONTENT_PATH_DEFAULT set_current_archive 'ARCHIVE_BASE_0' CONTENT_PATH_DEFAULT_0='some/other/path' content_path_default=$(content_path_default) assertEquals \ 'content_path_default failed to get a contextual value for CONTENT_PATH_DEFAULT.' \ 'some/other/path' "$content_path_default" unset ARCHIVE CONTENT_PATH_DEFAULT_0 assertFalse \ 'content_path_default did not fail, despite no value being set for CONTENT_PATH_DEFAULT.' \ 'content_path_default' } test_content_path() { local \ CONTENT_PATH_DEFAULT CONTENT_GAME_DATA_PATH CONTENT_GAME_DATA_PATH_0 \ PLAYIT_CONTEXT_ARCHIVE \ PLAYIT_COMPATIBILITY_LEVEL \ ARCHIVE_GAME_BIN_PATH ARCHIVE_GAME_BIN_PATH_0 CONTENT_PATH_DEFAULT='default/path' assertEquals "$CONTENT_PATH_DEFAULT" "$(content_path)" CONTENT_GAME_DATA_PATH='specific/path' assertEquals "$CONTENT_GAME_DATA_PATH" "$(content_path 'GAME_DATA')" set_current_archive 'ARCHIVE_BASE_0' CONTENT_GAME_DATA_PATH_0='more/specific/path' assertEquals "$CONTENT_GAME_DATA_PATH_0" "$(content_path 'GAME_DATA')" # Test the behaviour of game scripts targeting ./play.it < 2.18 PLAYIT_COMPATIBILITY_LEVEL='2.17' ARCHIVE_GAME_BIN_PATH='old/path' assertEquals "$ARCHIVE_GAME_BIN_PATH" "$(content_path 'GAME_BIN')" ARCHIVE_GAME_BIN_PATH_0='other/old/path' assertEquals "$ARCHIVE_GAME_BIN_PATH_0" "$(content_path 'GAME_BIN')" } test_content_files() { local \ CONTENT_GAME_DATA_FILES CONTENT_GAME_DATA_FILES_0 \ PLAYIT_CONTEXT_ARCHIVE \ PLAYIT_COMPATIBILITY_LEVEL \ ARCHIVE_GAME_BIN_FILES ARCHIVE_GAME_BIN_FILES_0 assertNull "$(content_files 'GAME_DATA')" CONTENT_GAME_DATA_FILES='some list of files' assertEquals "$CONTENT_GAME_DATA_FILES" "$(content_files 'GAME_DATA')" set_current_archive 'ARCHIVE_BASE_0' CONTENT_GAME_DATA_FILES_0='specific list of files' assertEquals "$CONTENT_GAME_DATA_FILES_0" "$(content_files 'GAME_DATA')" # Test the behaviour of game scripts targeting ./play.it < 2.18 PLAYIT_COMPATIBILITY_LEVEL='2.17' assertNull "$(content_files 'GAME_BIN')" ARCHIVE_GAME_BIN_FILES='old list of files' assertEquals "$ARCHIVE_GAME_BIN_FILES" "$(content_files 'GAME_BIN')" ARCHIVE_GAME_BIN_FILES_0='other old list of files' assertEquals "$ARCHIVE_GAME_BIN_FILES_0" "$(content_files 'GAME_BIN')" # Fallback lists of files for Unity3D and Unreal Engine 4 games are not tested here. } tests/shunit2/30_content/10_files-inclusion.sh0000644000000000000000000000312013120060140020204 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } setUp() { # Set a temporary directory to mess with real files TEST_TEMP_DIR=$(mktemp --directory) export TEST_TEMP_DIR } tearDown() { rm --force --recursive "$TEST_TEMP_DIR" } test_content_inclusion() { local PLAYIT_WORKDIR CONTENT_GAME_MAIN_PATH CONTENT_GAME_DATA_FILES # Create source files mkdir --parents "${TEST_TEMP_DIR}/gamedata" mkdir \ "${TEST_TEMP_DIR}/gamedata/directory to include" \ "${TEST_TEMP_DIR}/gamedata/directory to exclude" touch \ "${TEST_TEMP_DIR}/gamedata/file to include" \ "${TEST_TEMP_DIR}/gamedata/file to exclude" # Use a hardcoded destination package_path() { printf '%s/packages/destination-package' "$TEST_TEMP_DIR" } PLAYIT_WORKDIR="$TEST_TEMP_DIR" CONTENT_GAME_MAIN_PATH='.' CONTENT_GAME_MAIN_FILES='directory to include file to include missing file' content_inclusion 'GAME_MAIN' 'PKG_MAIN' '/' paths_included=$(find "${TEST_TEMP_DIR}/packages/destination-package" | env --ignore-environment sort) paths_included_expected="${TEST_TEMP_DIR}/packages/destination-package ${TEST_TEMP_DIR}/packages/destination-package/directory to include ${TEST_TEMP_DIR}/packages/destination-package/file to include" assertEquals "$paths_included_expected" "$paths_included" paths_excluded=$(find "${TEST_TEMP_DIR}/gamedata" | env --ignore-environment sort) paths_excluded_expected="${TEST_TEMP_DIR}/gamedata ${TEST_TEMP_DIR}/gamedata/directory to exclude ${TEST_TEMP_DIR}/gamedata/file to exclude" assertEquals "$paths_excluded_expected" "$paths_excluded" } tests/shunit2/30_content/20_huge-files.sh0000644000000000000000000000343613120060140017144 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } test_huge_files_list() { local HUGE_FILES_MAIN huge_files HUGE_FILES_MAIN=' some-big-file.pak' huge_files=$(huge_files_list 'PKG_MAIN') assertEquals 'some-big-file.pak' "$huge_files" # Check that duplicates are dropped HUGE_FILES_MAIN=' some-duplicated-file.pak some-duplicated-file.pak' huge_files=$(huge_files_list 'PKG_MAIN') assertEquals 'some-duplicated-file.pak' "$huge_files" } test_content_inclusion_chunk_single() { local \ GAME_ID PACKAGES_LIST \ PKG_DATA_ID PKG_DATA_DESCRIPTION \ PKG_DATA_CHUNK1_ID PKG_DATA_CHUNK1_DESCRIPTION PKG_DATA_CHUNK1_PRERM_RUN \ CONTENT_GAME_DATA_CHUNK1_FILES \ packages_list packages_list_expected package_id package_description package_prerm_actions content_files # Skip the actual file inclusion path_game_data() { printf '/some/arbitrary/path' } content_inclusion() { return 0 ; } # Use a simplified function for returning the package description package_description() { context_value "${1}_DESCRIPTION" } PACKAGES_LIST=' PKG_BIN PKG_DATA' PKG_DATA_ID='package-data' PKG_DATA_DESCRIPTION='data' content_inclusion_chunk_single 'PKG_DATA' 'some-file-chunk.pak.1' '1' packages_list_expected='PKG_DATA_CHUNK1 PKG_BIN PKG_DATA' packages_list=$(packages_list) assertEquals "$packages_list_expected" "$packages_list" package_id=$(package_id 'PKG_DATA_CHUNK1') assertEquals 'package-data-chunk1' "$package_id" package_description=$(package_description 'PKG_DATA_CHUNK1') assertEquals 'data - chunk 1' "$package_description" package_prerm_actions=$(package_prerm_actions 'PKG_DATA_CHUNK1') assertNotNull "$package_prerm_actions" content_files=$(content_files 'GAME_DATA_CHUNK1') assertEquals 'some-file-chunk.pak.1' "$content_files" } tests/shunit2/30_icons/00_common.sh0000644000000000000000000000720513120060140016041 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } test_icons_list_all() { local APPLICATIONS_LIST APP_MAIN_ICONS_LIST APP_EXTRA_ICONS_LIST icons_list icons_list_expected APPLICATIONS_LIST='APP_MAIN APP_EXTRA' APP_MAIN_ICONS_LIST='APP_MAIN_ICON' APP_EXTRA_ICONS_LIST='APP_EXTRA_ICON1 APP_EXTRA_ICON2' icons_list=$(icons_list_all) icons_list_expected='APP_EXTRA_ICON1 APP_EXTRA_ICON2 APP_MAIN_ICON' assertEquals "$icons_list_expected" "$icons_list" } test_application_icons_list() { local APP_MAIN_ICONS_LIST APP_MAIN_ICON APP_MAIN_TYPE GAME_ENGINE icons_list APP_MAIN_ICONS_LIST='APP_MAIN_ICON1 APP_MAIN_ICON_2' icons_list=$(application_icons_list 'APP_MAIN') assertEquals 'APP_MAIN_ICON1 APP_MAIN_ICON_2' "$icons_list" unset APP_MAIN_ICONS_LIST APP_MAIN_ICON='something.ico' icons_list=$(application_icons_list 'APP_MAIN') assertEquals 'APP_MAIN_ICON' "$icons_list" unset APP_MAIN_ICON APP_MAIN_TYPE='wine' icons_list=$(application_icons_list 'APP_MAIN') assertEquals 'APP_MAIN_ICON' "$icons_list" unset APP_MAIN_TYPE GAME_ENGINE='unity3d' icons_list=$(application_icons_list 'APP_MAIN') assertEquals 'APP_MAIN_ICON' "$icons_list" unset GAME_ENGINE icons_list=$(application_icons_list 'APP_MAIN') assertNull "$icons_list" } test_icon_application() { local APPLICATIONS_LIST application APPLICATIONS_LIST='APP_MAIN APP_EXTRA' application=$(icon_application 'APP_MAIN_ICON') assertEquals 'APP_MAIN' "$application" application=$(icon_application 'APP_EXTRA_ICON1') assertEquals 'APP_EXTRA' "$application" # Check that icon_application does not get confused between applications with similar names. APPLICATIONS_LIST='APP_HOF APP_HOFEDIT' APP_HOF_ID='heroes-of-might-and-magic-5-hammers-of-fate' APP_HOFEDIT_ID='heroes-of-might-and-magic-5-hammers-of-fate-map-editor' application=$(icon_application 'APP_HOFEDIT_ID') assertEquals 'icon_application linked an icon to the wrong application.' 'APP_HOFEDIT' "$application" } test_icon_path() { local \ APP_MAIN_ICON icon_path \ APPLICATIONS_LIST APP_MAIN_TYPE APP_MAIN_EXE \ GAME_ENGINE UNITY3D_NAME APP_MAIN_ICON='something.ico' icon_path=$(icon_path 'APP_MAIN_ICON') assertEquals 'something.ico' "$APP_MAIN_ICON" unset APP_MAIN_ICON APPLICATIONS_LIST='APP_MAIN' APP_MAIN_TYPE='wine' APP_MAIN_EXE='something.exe' icon_path=$(icon_path 'APP_MAIN_ICON') assertEquals 'something.exe' "$icon_path" unset APPLICATIONS_LIST APP_MAIN_TYPE APP_MAIN_EXE GAME_ENGINE='unity3d' UNITY3D_NAME='Something' APP_MAIN_TYPE='native' icon_path=$(icon_path 'APP_MAIN_ICON') assertEquals 'Something_Data/Resources/UnityPlayer.png' "$icon_path" APP_MAIN_TYPE='wine' icon_path=$(icon_path 'APP_MAIN_ICON') assertEquals 'Something.exe' "$icon_path" unset GAME_ENGINE UNITY3D_NAME APP_MAIN_TYPE } test_icon_wrestool_options() { local \ APP_MAIN_ICON APP_MAIN_ICON_WRESTOOL_OPTIONS wrestool_options \ APPLICATIONS_LIST GAME_ENGINE \ APP_MAIN_ICON_ID # Check that an error is triggered if the icon is not using the expected file extension APP_MAIN_ICON='something.ico' assertFalse "wrestool_options 'APP_MAIN_ICON'" APP_MAIN_ICON='something.exe' APP_MAIN_ICON_WRESTOOL_OPTIONS='--type=14 --name=221' wrestool_options=$(icon_wrestool_options 'APP_MAIN_ICON') assertEquals '--type=14 --name=221' "$wrestool_options" unset APP_MAIN_ICON_WRESTOOL_OPTIONS APPLICATIONS_LIST='APP_MAIN' GAME_ENGINE='unrealengine4' wrestool_options=$(icon_wrestool_options 'APP_MAIN_ICON') assertEquals '--type=14 --name=101' "$wrestool_options" unset GAME_ENGINE wrestool_options=$(icon_wrestool_options 'APP_MAIN_ICON') assertEquals '--type=14' "$wrestool_options" } tests/shunit2/30_icons/10_conversion.sh0000644000000000000000000000244213120060140016735 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } test_icons_inclusion_single_application() { ## Prevent actions requiring files manipulation. icons_inclusion_single_icon() { return 0; } # Ensure that an error is thrown when trying to extract icons from an application with no icon set assertFalse \ 'icons_inclusion_single_application failed to notice it has been asked to handle an application with no icon set.' \ 'icons_inclusion_single_application "PKG_MAIN" "APP_MAIN"' # Check that the previous error is not thrown if an optional icons archive is supported. local ARCHIVE_OPTIONAL_ICONS_NAME ARCHIVE_OPTIONAL_ICONS_NAME='alpha-centauri-icons.2022-10-04.tar.gz' assertTrue \ 'icons_inclusion_single_application failed to notice that an optional icons archive is supported.' \ 'icons_inclusion_single_application "PKG_MAIN" "APP_MAIN"' unset -f icons_inclusion_single_icon } test_icon_full_path() { local icon_full_path PLAYIT_WORKDIR CONTENT_PATH_DEFAULT APP_MAIN_ICON PLAYIT_WORKDIR='/var/tmp/play.it/jazz-jackrabbit-2.t3nm8' CONTENT_PATH_DEFAULT='app' APP_MAIN_ICON='jazz2.exe' icon_full_path=$(icon_full_path 'APP_MAIN_ICON') assertEquals '/var/tmp/play.it/jazz-jackrabbit-2.t3nm8/gamedata/app/jazz2.exe' "$icon_full_path" } tests/shunit2/30_launchers/00_common.sh0000644000000000000000000000173713120060140016716 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } test_launcher_generation_checks() { local APP_MAIN_TYPE APP_MAIN_EXE APP_MAIN_SCUMMID # Disable launcher_target_presence_check, # it should be tested in its own function. launcher_target_presence_check() { return 0; } # Ensure that an error is triggered on missing application type. unset APP_MAIN_TYPE assertFalse 'launcher_generation_checks "PKG_MAIN" "APP_MAIN"' # Ensure that an error is triggered when no binary path is set, # but one is expected by the current application type. unset APP_MAIN_EXE for APP_MAIN_TYPE in \ 'dosbox' \ 'java' \ 'mono' \ 'native' \ 'wine' do assertFalse 'launcher_generation_checks "PKG_MAIN" "APP_MAIN"' done # Ensure that an error is triggered when no ScummVM game ID is set, # but one is expected by the current application type. APP_MAIN_TYPE='scummvm' assertFalse 'launcher_generation_checks "PKG_MAIN" "APP_MAIN"' } tests/shunit2/30_launchers/10_fake-home.sh0000644000000000000000000000224513120060140017256 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } test_fake_home_persistent_directories() { local ARCHIVE FAKE_HOME_PERSISTENT_DIRECTORIES FAKE_HOME_PERSISTENT_DIRECTORIES_MAIN persistent_directories_expected persistent_directories local \ persistent_directories persistent_directories_expected \ FAKE_HOME_PERSISTENT_DIRECTORIES FAKE_HOME_PERSISTENT_DIRECTORIES_MAIN \ PLAYIT_CONTEXT_ARCHIVE FAKE_HOME_PERSISTENT_DIRECTORIES='path/to/dir/1 path/to/dir/2' persistent_directories=$(fake_home_persistent_directories) persistent_directories_expected='path/to/dir/1 path/to/dir/2' assertEquals "$persistent_directories_expected" "$persistent_directories" unset FAKE_HOME_PERSISTENT_DIRECTORIES set_current_archive 'ARCHIVE_BASE_MAIN_0' FAKE_HOME_PERSISTENT_DIRECTORIES_MAIN='path/to/archive/specific/dir/1 path/to/archive/specific/dir/2' persistent_directories=$(fake_home_persistent_directories) persistent_directories_expected='path/to/archive/specific/dir/1 path/to/archive/specific/dir/2' assertEquals "$persistent_directories_expected" "$persistent_directories" unset ARCHIVE FAKE_HOME_PERSISTENT_DIRECTORIES_MAIN } tests/shunit2/30_launchers/20_persistent-user-data.sh0000644000000000000000000000365013120060140021507 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } test_persistent_list_directories() { local \ persistent_directories persistent_directories_expected \ USER_PERSISTENT_DIRECTORIES \ CONFIG_DIRS DATA_DIRS PLAYIT_COMPATIBILITY_LEVEL USER_PERSISTENT_DIRECTORIES=' mods users' persistent_directories=$(persistent_list_directories) persistent_directories_expected='mods users' assertEquals "$persistent_directories_expected" "$persistent_directories" unset USER_PERSISTENT_DIRECTORIES # Check support for legacy variables PLAYIT_COMPATIBILITY_LEVEL='2.16' CONFIG_DIRS='./drivers' DATA_DIRS='./badges ./banners ./playback ./profiles ./screenshots ./stats' persistent_directories=$(persistent_list_directories) persistent_directories_expected='./badges ./banners ./drivers ./playback ./profiles ./screenshots ./stats' assertEquals "$persistent_directories_expected" "$persistent_directories" unset CONFIG_DIRS DATA_DIRS # Check that empty values do not trigger an error assertTrue 'persistent_list_directories' } test_persistent_list_files() { local \ persistent_files persistent_files_expected \ USER_PERSISTENT_FILES \ CONFIG_FILES DATA_FILES PLAYIT_COMPATIBILITY_LEVEL USER_PERSISTENT_FILES=' *.cfg *.dat player-data.json' persistent_files=$(persistent_list_files) persistent_files_expected='*.cfg *.dat player-data.json' assertEquals "$persistent_files_expected" "$persistent_files" unset USER_PERSISTENT_FILES # Check support for legacy variables PLAYIT_COMPATIBILITY_LEVEL='2.16' CONFIG_FILES='./userdata/*.ini' DATA_FILES='./userdata/*.sav ./userdata/*.png' persistent_files=$(persistent_list_files) persistent_files_expected='./userdata/*.ini ./userdata/*.png ./userdata/*.sav' assertEquals "$persistent_files_expected" "$persistent_files" unset CONFIG_FILES DATA_FILES # Check that empty values do not trigger an error assertTrue 'persistent_list_files' } tests/shunit2/30_packages/00_common.sh0000644000000000000000000000633113120060140016503 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } test_packages_generation() { # Check that packages_generation called without an explicit list of packages does no end up running twice local PLAYIT_OPTION_PACKAGE function_calls_counter PLAYIT_OPTION_PACKAGE='deb' function_calls_counter=0 debian_packages_metadata() { function_calls_counter=$((function_calls_counter + 1)) export function_calls_counter } debian_packages_build() { return 0; } ## Prevent the console message from being mixed with shunit2 output. packages_generation >/dev/null assertEquals \ 'packages_generation called without an argument ended up running multiple times instead of only once.' \ '1' "$function_calls_counter" unset -f debian_packages_metadata debian_packages_build } test_package_description() { local package_description PKG_MAIN_DESCRIPTION PKG_MAIN_DESCRIPTION='French localization' package_description=$(package_description 'PKG_MAIN') assertEquals 'French localization' "$package_description" # Line breaks should trigger an error PKG_MAIN_DESCRIPTION='French localization Includes text and voices' assertFalse 'package_description "PKG_MAIN"' } test_package_postinst_actions() { local postinst_actions postinst_actions_expected PKG_MAIN_POSTINST_RUN PKG_MAIN_POSTINST_RUN="# Link common files shared by the games series ln --symbolic '/usr/share/games/heroes-chronicles/data' '/usr/share/games/heroes-chronicles-3' ln --symbolic '/usr/share/games/heroes-chronicles/mp3' '/usr/share/games/heroes-chronicles-3'" postinst_actions=$(package_postinst_actions 'PKG_MAIN') postinst_actions_expected="# Link common files shared by the games series ln --symbolic '/usr/share/games/heroes-chronicles/data' '/usr/share/games/heroes-chronicles-3' ln --symbolic '/usr/share/games/heroes-chronicles/mp3' '/usr/share/games/heroes-chronicles-3'" assertEquals "$postinst_actions_expected" "$PKG_MAIN_POSTINST_RUN" } test_package_prerm_actions() { local prerm_actions prerm_actions_expected PKG_MAIN_PRERM_RUN PKG_MAIN_PRERM_RUN="# Delete links to common files shared by the games series rm '/usr/share/games/heroes-chronicles-3/mp3' rm '/usr/share/games/heroes-chronicles-3/data'" prerm_actions=$(package_prerm_actions 'PKG_MAIN') prerm_actions_expected="# Delete links to common files shared by the games series rm '/usr/share/games/heroes-chronicles-3/mp3' rm '/usr/share/games/heroes-chronicles-3/data'" assertEquals "$prerm_actions_expected" "$PKG_MAIN_PRERM_RUN" } test_package_postinst_warnings() { local postinst_warnings postinst_warnings_expected PKG_MAIN_POSTINST_WARNINGS PKG_MAIN_POSTINST_WARNINGS='You may need to generate the ja_JP.UTF-8 locale for the configuration program to run You need a MIDI synthetiser for music to play in the game (you can use timidity++ or fluidsynth if you don’t have a hardware synthetiser)' postinst_warnings=$(package_postinst_warnings 'PKG_MAIN') postinst_warnings_expected='You may need to generate the ja_JP.UTF-8 locale for the configuration program to run You need a MIDI synthetiser for music to play in the game (you can use timidity++ or fluidsynth if you don’t have a hardware synthetiser)' assertEquals "$postinst_warnings_expected" "$postinst_warnings" } tests/shunit2/40_hacks/00_list.sh0000644000000000000000000000126413120060140015502 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } test_hacks_list() { local \ hacks_list hacks_list_expected \ PRELOAD_HACKS_LIST PRELOAD_HACKS_LIST_MAIN \ PLAYIT_CONTEXT_ARCHIVE hacks_list=$(hacks_list) assertNull "$hacks_list" PRELOAD_HACKS_LIST=' HACK_DEFAULT1 HACK_DEFAULT2' hacks_list=$(hacks_list) hacks_list_expected='HACK_DEFAULT1 HACK_DEFAULT2' assertEquals "$hacks_list_expected" "$hacks_list" set_current_archive 'ARCHIVE_BASE_MAIN_0' PRELOAD_HACKS_LIST_MAIN=' HACK_OTHER1 HACK_OTHER2' hacks_list=$(hacks_list) hacks_list_expected='HACK_OTHER1 HACK_OTHER2' assertEquals "$hacks_list_expected" "$hacks_list" } tests/shunit2/40_hacks/10_details.sh0000644000000000000000000000407013120060140016153 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } test_hack_name() { local hack_name HACK_SDL1COMPAT_NAME HACK_SDL1COMPAT_NAME='smacshim' hack_name=$(hack_name 'HACK_SDL1COMPAT') assertEquals 'smacshim' "$hack_name" # Values spanning multiple lines are not allowed HACK_SDL1COMPAT_NAME='smac shim' assertFalse 'hack_name "HACK_SDL1COMPAT"' # Empty values are not allowed HACK_SDL1COMPAT_NAME='' assertFalse 'hack_name "HACK_SDL1COMPAT"' } test_hack_description() { local hack_description HACK_SDL1COMPAT_DESCRIPTION HACK_SDL1COMPAT_DESCRIPTION='Build and include the LD_PRELOAD shim allowing the old engine to run on top of latest SDL1 library' hack_description=$(hack_description 'HACK_SDL1COMPAT') assertEquals 'Build and include the LD_PRELOAD shim allowing the old engine to run on top of latest SDL1 library' "$hack_description" # Empty values are not allowed HACK_SDL1COMPAT_DESCRIPTION='' assertFalse 'hack_description "HACK_SDL1COMPAT"' } test_hack_package() { local hack_package HACK_SDL1COMPAT_PACKAGE PLAYIT_CONTEXT_PACKAGE HACK_SDL1COMPAT_PACKAGE='PKG_BIN' hack_package=$(hack_package 'HACK_SDL1COMPAT') assertEquals 'PKG_BIN' "$hack_package" # If no value is set, fall back on the current package unset HACK_SDL1COMPAT_PACKAGE set_current_package 'PKG_MAIN' hack_package=$(hack_package 'HACK_SDL1COMPAT') assertEquals 'PKG_MAIN' "$hack_package" } test_hack_source() { local hack_source hack_source_expected HACK_SDL1COMPAT_SOURCE HACK_SDL1COMPAT_SOURCE=' #define something #include void __do_something(some args) { # Nothing to see here } ' hack_source=$(hack_source 'HACK_SDL1COMPAT') ## Extra line breaks at the end of the source file content are dropped during the variable assignation. hack_source_expected=' #define something #include void __do_something(some args) { # Nothing to see here }' assertEquals "$hack_source_expected" "$hack_source" # Empty values are not allowed HACK_SDL1COMPAT_SOURCE='' assertFalse 'hack_source "HACK_SDL1COMPAT"' } tests/shunit2/40_hacks/20_build.sh0000644000000000000000000000123013120060140015621 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } test_hack_path_source() { local hack_path_source PLAYIT_WORKDIR HACK_SDL1COMPAT_NAME PLAYIT_WORKDIR='/some/path' HACK_SDL1COMPAT_NAME='smacshim' hack_path_source=$(hack_path_source 'HACK_SDL1COMPAT') assertEquals "${PLAYIT_WORKDIR}/hacks/smacshim.c" "$hack_path_source" } test_hack_path_library() { local hack_path_library PLAYIT_WORKDIR HACK_SDL1COMPAT_NAME PLAYIT_WORKDIR='/some/path' HACK_SDL1COMPAT_NAME='smacshim' hack_path_library=$(hack_path_library 'HACK_SDL1COMPAT') assertEquals "${PLAYIT_WORKDIR}/hacks/smacshim.so" "$hack_path_library" } tests/shunit2/40_hacks/30_inclusion.sh0000644000000000000000000000256013120060140016535 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } test_hack_application_prerun() { local hack_application_prerun hack_application_prerun_expected HACK_SDL1COMPAT_NAME HACK_SDL1COMPAT_DESCRIPTION path_libraries() { printf '/usr/lib/games/some-game' } HACK_SDL1COMPAT_NAME='smacshim' HACK_SDL1COMPAT_DESCRIPTION='LD_PRELOAD shim allowing the old engine to run on top of latest SDL1 library cf. https://github.com/ZeroPointEnergy/smacshim' hack_application_prerun=$(hack_application_prerun 'HACK_SDL1COMPAT') hack_application_prerun_expected='# LD_PRELOAD shim allowing the old engine to run on top of latest SDL1 library # cf. https://github.com/ZeroPointEnergy/smacshim export LD_PRELOAD="${LD_PRELOAD}:/usr/lib/games/some-game/preload-hacks/smacshim.so"' assertEquals "$hack_application_prerun_expected" "$hack_application_prerun" } test_hacks_included_in_package() { local \ hacks_list hacks_list_expected \ PRELOAD_HACKS_LIST HACK_INCLUDED1_PACKAGE HACK_INCLUDED2_PACKAGE HACK_EXCLUDED_PACKAGE PRELOAD_HACKS_LIST=' HACK_INCLUDED1 HACK_INCLUDED2 HACK_EXCLUDED' HACK_INCLUDED1_PACKAGE='PKG_BIN' HACK_INCLUDED2_PACKAGE='PKG_BIN' HACK_EXCLUDED_PACKAGE='PKG_MAIN' hacks_list=$(hacks_included_in_package 'PKG_BIN') hacks_list_expected='HACK_INCLUDED1 HACK_INCLUDED2' assertEquals "$hacks_list_expected" "$hacks_list" } tests/shunit2/55_engine-variant_unity3d/30_applications.sh0000644000000000000000000000166013120060140022523 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } setUp() { # Set a temporary directory to mess with real files TEST_TEMP_DIR=$(mktemp --directory) export TEST_TEMP_DIR } tearDown() { rm --force --recursive "$TEST_TEMP_DIR" } test_unity3d_application_exe_default() { # Check that the correct Unity3D game binary is picked up local CONTENT_PATH_DEFAULT PLAYIT_WORKDIR UNITY3D_NAME CONTENT_PATH_DEFAULT='.' PLAYIT_WORKDIR="$TEST_TEMP_DIR" UNITY3D_NAME='Dungeons2' mkdir --parents "${PLAYIT_WORKDIR}/gamedata/${CONTENT_PATH_DEFAULT}" touch \ "${PLAYIT_WORKDIR}/gamedata/${CONTENT_PATH_DEFAULT}/${UNITY3D_NAME}" \ "${PLAYIT_WORKDIR}/gamedata/${CONTENT_PATH_DEFAULT}/${UNITY3D_NAME}.x86" assertEquals \ 'unity3d_application_exe_default picked up the wrong file as the main game binary.' \ "${UNITY3D_NAME}.x86" \ "$(unity3d_application_exe_default 'APP_MAIN')" return 0 } tests/shunit2/60_system_archlinux/15_metadata-fields.sh0000644000000000000000000000117213120060140022071 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } test_archlinux_field_pkgdesc() { local field_pkgdesc script_version GAME_NAME PKG_MAIN_DESCRIPTION script_version='19700101.1' GAME_NAME='Alpha Centauri' PKG_MAIN_DESCRIPTION='French localization' field_pkgdesc=$(archlinux_field_pkgdesc 'PKG_MAIN') assertEquals 'Alpha Centauri - French localization - ./play.it script version 19700101.1' "$field_pkgdesc" unset PKG_MAIN_DESCRIPTION field_pkgdesc=$(archlinux_field_pkgdesc 'PKG_MAIN') assertEquals 'Alpha Centauri - ./play.it script version 19700101.1' "$field_pkgdesc" } tests/shunit2/60_system_archlinux/15_package-scripts.sh0000644000000000000000000000406213120060140022126 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } test_archlinux_script_install() { local \ script_install script_install_expected \ PKG_MAIN_POSTINST_RUN PKG_MAIN_PRERM_RUN \ PKG_MAIN_POSTINST_WARNINGS PKG_MAIN_POSTINST_RUN="# Link common files shared by the games series ln --symbolic '/usr/share/games/heroes-chronicles/data' '/usr/share/games/heroes-chronicles-3' ln --symbolic '/usr/share/games/heroes-chronicles/mp3' '/usr/share/games/heroes-chronicles-3'" PKG_MAIN_PRERM_RUN="# Delete links to common files shared by the games series rm '/usr/share/games/heroes-chronicles-3/mp3' rm '/usr/share/games/heroes-chronicles-3/data'" script_install=$(archlinux_script_install 'PKG_MAIN') script_install_expected="post_install() { # Link common files shared by the games series ln --symbolic '/usr/share/games/heroes-chronicles/data' '/usr/share/games/heroes-chronicles-3' ln --symbolic '/usr/share/games/heroes-chronicles/mp3' '/usr/share/games/heroes-chronicles-3' } post_upgrade() { post_install } pre_remove() { # Delete links to common files shared by the games series rm '/usr/share/games/heroes-chronicles-3/mp3' rm '/usr/share/games/heroes-chronicles-3/data' } pre_upgrade() { pre_remove }" assertEquals "$script_install_expected" "$script_install" unset PKG_MAIN_POSTINST_RUN PKG_MAIN_PRERM_RUN PKG_MAIN_POSTINST_WARNINGS='You may need to generate the ja_JP.UTF-8 locale for the configuration program to run You need a MIDI synthetiser for music to play in the game (you can use timidity++ or fluidsynth if you don’t have a hardware synthetiser)' script_install=$(archlinux_script_install 'PKG_MAIN') script_install_expected='post_install() { printf "Warning: %s\n" "You may need to generate the ja_JP.UTF-8 locale for the configuration program to run" printf "Warning: %s\n" "You need a MIDI synthetiser for music to play in the game (you can use timidity++ or fluidsynth if you don’t have a hardware synthetiser)" } post_upgrade() { post_install }' assertEquals "$script_install_expected" "$script_install" } tests/shunit2/60_system_debian/15_metadata-fields.sh0000644000000000000000000000143613120060140021321 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } test_debian_field_description() { local field_description field_description_expected script_version GAME_NAME PKG_MAIN_DESCRIPTION script_version='19700101.1' GAME_NAME='Alpha Centauri' PKG_MAIN_DESCRIPTION='French localization' field_description=$(debian_field_description 'PKG_MAIN') field_description_expected='Alpha Centauri - French localization ./play.it script version 19700101.1' assertEquals "$field_description_expected" "$field_description" unset PKG_MAIN_DESCRIPTION field_description=$(debian_field_description 'PKG_MAIN') field_description_expected='Alpha Centauri ./play.it script version 19700101.1' assertEquals "$field_description_expected" "$field_description" } tests/shunit2/60_system_debian/15_package-scripts.sh0000644000000000000000000000423013120060140021350 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } test_debian_script_postinst() { local script_postinst script_postinst_expected PKG_MAIN_POSTINST_RUN PKG_MAIN_POSTINST_WARNINGS PKG_MAIN_POSTINST_RUN='# Link common files shared by the games series ln --symbolic /usr/share/games/heroes-chronicles/data /usr/share/games/heroes-chronicles-3 ln --symbolic /usr/share/games/heroes-chronicles/mp3 /usr/share/games/heroes-chronicles-3' script_postinst=$(debian_script_postinst 'PKG_MAIN') script_postinst_expected='#!/bin/sh set -o errexit # Link common files shared by the games series ln --symbolic /usr/share/games/heroes-chronicles/data /usr/share/games/heroes-chronicles-3 ln --symbolic /usr/share/games/heroes-chronicles/mp3 /usr/share/games/heroes-chronicles-3 exit 0' assertEquals "$script_postinst_expected" "$script_postinst" unset PKG_MAIN_POSTINST_RUN PKG_MAIN_POSTINST_WARNINGS='You may need to generate the ja_JP.UTF-8 locale for the configuration program to run You need a MIDI synthetiser for music to play in the game (you can use timidity++ or fluidsynth if you don’t have a hardware synthetiser)' script_postinst=$(debian_script_postinst 'PKG_MAIN') script_postinst_expected='#!/bin/sh set -o errexit printf "Warning: %s\n" "You may need to generate the ja_JP.UTF-8 locale for the configuration program to run" printf "Warning: %s\n" "You need a MIDI synthetiser for music to play in the game (you can use timidity++ or fluidsynth if you don’t have a hardware synthetiser)" exit 0' assertEquals "$script_postinst_expected" "$script_postinst" } test_debian_script_prerm() { local script_prerm script_prerm_expected PKG_MAIN_PRERM_RUN PKG_MAIN_PRERM_RUN='# Delete links to common files shared by the games series rm /usr/share/games/heroes-chronicles-3/mp3 rm /usr/share/games/heroes-chronicles-3/data' script_prerm=$(debian_script_prerm 'PKG_MAIN') script_prerm_expected='#!/bin/sh set -o errexit # Delete links to common files shared by the games series rm /usr/share/games/heroes-chronicles-3/mp3 rm /usr/share/games/heroes-chronicles-3/data exit 0' assertEquals "$script_prerm_expected" "$script_prerm" } tests/shunit2/60_system_debian/25_dependencies_mono-libraries.sh0000644000000000000000000000111713120060140023722 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } test_debian_dependency_providing_mono_library() { ## Prevent actions requiring the creation/modification of files dependencies_unknown_mono_libraries_add() { return 0; } # Check that setting a dependency on an unkown Mono library is allowed set -o nounset assertTrue \ 'debian_dependency_providing_mono_library choked on an unkwown Mono library.' \ 'debian_dependency_providing_mono_library "Mono.unknown.dll"' set +o nounset unset -f dependencies_unknown_mono_libraries_add } tests/shunit2/60_system_gentoo/15_metadata-fields_gentoo-variant.sh0000644000000000000000000000122113120060140024377 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } test_gentoo_field_description() { local field_description script_version GAME_NAME PKG_MAIN_DESCRIPTION script_version='19700101.1' GAME_NAME='Alpha Centauri' PKG_MAIN_DESCRIPTION='French localization' field_description=$(gentoo_field_description 'PKG_MAIN') assertEquals 'Alpha Centauri - French localization - ./play.it script version 19700101.1' "$field_description" unset PKG_MAIN_DESCRIPTION field_description=$(gentoo_field_description 'PKG_MAIN') assertEquals 'Alpha Centauri - ./play.it script version 19700101.1' "$field_description" }