CHANGELOG0000644000000000000000000021665513120060140010770 0ustar rootroot2.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 LICENSE0000644000000000000000000000344613120060140010553 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 © 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.md0000644000000000000000000001034013120060140011014 0ustar rootroot# ./play.it: Installer for DRM-free commercial games The canonical repository is https://forge.dotslashplay.it/play.it/scripts, issues and merge requests raised at mirrors will probably be missed. ## 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 2.27.2 --depth 1 https://forge.dotslashplay.it/play.it/scripts.git 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://forge.dotslashplay.it/play.it/games) - [vv221ʼs games collection](https://forge.dotslashplay.it/vv221/games) - [Hoëlʼs games](https://forge.dotslashplay.it/hoel/les-jeux-de-hoel) - [ahubʼs My Game Collection](https://forge.dotslashplay.it/ahub/my-game-collection) - [Caliban’s games collection](https://forge.dotslashplay.it/caliban/calibans) ## 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 Makefile0000644000000000000000000000742413120060140011206 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 | 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 | 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/gitlab/gitlab-ci.yml 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/gitlab/gitlab-ci.yml 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 ## 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 | 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.27.2 # shellcheck disable=SC2034 library_revision=20240111.3 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.sh0000644000000000000000000000376513120060140016743 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 compatibility_level="$LIBRARY_VERSION" 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" compatibility_level=$(compatibility_level) # 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_context.sh0000644000000000000000000001643213120060140014444 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" } # 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" } # 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" } # 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 local packages_list packages_list=$(packages_list) package=$(printf '%s' "$packages_list" | head --lines=1) set_current_package "$package" fi printf '%s' "$package" } # 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() { if ! compatibility_level_is_at_least '2.21'; then current_archive_suffix_legacy return 0 fi 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. } # 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_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.sh0000644000000000000000000001070413120060140017311 0ustar rootroot# set temporary directories # 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 # Export the path to the packages to build as PKG_xxx_PATH # Some game scripts are expecting this variable to be set # These should be updated to call `package_path` instead local packages_list package package_path packages_list=$(packages_list) for package in $packages_list; do package_path=$(package_path "$package") eval "${package}_PATH='${package_path}'" export "${package?}_PATH" done 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.sh0000644000000000000000000003300213120060140014567 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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" } 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.sh0000644000000000000000000000164013120060140014376 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 message case "${LANG%_*}" 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.sh0000644000000000000000000002443313120060140013347 0ustar rootroot# display full usage instructions # USAGE: help help() { local message script_name script_name=$(basename "$0") # print general usage instructions case "${LANG%_*}" 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' case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message # shellcheck disable=SC2050 if [ %%DEBUG_DISABLED%% -eq 1 ]; then case "${LANG%_*}" 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 "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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_wrapper.sh0000644000000000000000000000315213120060140014751 0ustar rootroot# Print a localized message # USAGE: print_message $level $message $extra_values[…] print_message() { local level message # Valid levels are "error", "warning" and "info". Unknown levels will be handled similar to "info". level="$1" # 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="$2" shift 2 case "$level" in ('warning') local warning_string case "${LANG%_*}" 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" "$@" ;; ('error') ( ## Since this is called from a subshell, this should not trigger unwanted output redirections. exec 1>&2 local error_string case "${LANG%_*}" 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" "$@" ) ;; ('info'|*) ## Silence ShellCheck false-positive ## Don't use variables in the printf format string. Use printf "..%s.." "$foo". # shellcheck disable=SC2059 printf -- "$message" "$@" ;; esac } 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.sh0000644000000000000000000002507213120060140016034 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" if ! compatibility_level_is_at_least '2.23'; then option_export_legacy "$option_name" fi } # 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.sh0000644000000000000000000000335113120060140016153 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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.sh0000644000000000000000000000300213120060140014206 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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.sh0000644000000000000000000001574313120060140016743 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" | sort --unique } # 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='fakeroot 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 } | sort --unique } # 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' ;; ('mojosetup_unzip') # WARNING - This archive type is deprecated. requirements=$(archive_requirements_mojosetup_list) ;; ('zip_unclean') # WARNING - This archive type is deprecated. 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 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" for dep in $SCRIPT_DEPS; do case $dep in ('lzip') get_lzip_implementation >/dev/null ;; (*) if ! command -v "$dep" >/dev/null 2>&1; then error_dependency_not_found "$dep" return 1 fi ;; esac done # Check for the dependencies required to extract the icons local option_icons option_icons=$(option_value 'icons') if [ "$option_icons" -eq 1 ]; then requirements_check_icons 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 } # returns best available lzip implementation # fails if lzip is not available # USAGE: get_lzip_implementation get_lzip_implementation() { for command in 'tarlz' 'plzip' 'lzip'; do if command -v "$command" >/dev/null 2>&1; then printf '%s' "$command" return 0 fi done error_dependency_not_found 'lzip' return 1 } src/20_requirements/90_messages.sh0000644000000000000000000000103213120060140016021 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 message case "${LANG%_*}" 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" } src/30_applications/00_common.sh0000644000000000000000000002460713120060140015452 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 # and the suffixed variants of these variables. sed_expression='s/^\(APP_[0-9A-Z]\+\)_\(EXE\|SCUMMID\)\(_[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 # Always return a list with no duplicate entry, # excluding empty lines. # Ignore grep error return if there is nothing to print. printf '%s\n' $applications_list | \ sort --unique | \ grep --invert-match --regexp='^$' || true } # 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 ('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 file name of the application, with single quotes escaped, # for inclusion in a single quote delimited variable declaration. # USAGE: application_exe_escaped $application # RETURN: the application file name with single quotes escaped application_exe_escaped() { local application application="$1" local application_exe application_exe=$(application_exe "$application") ## Check that application binary has been found if [ -z "$application_exe" ]; then error_application_exe_empty "$application" 'application_exe_escaped' return 1 fi # 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" # Scripts for DOSBox games targeting a compatibility level < 2.20 have no support for regular pre-run actions. if ! compatibility_level_is_at_least '2.20'; then local application_type application_type=$(application_type "$application") if [ "$application_type" = 'dosbox' ]; then return 0 fi fi local application_prerun application_prerun=$(context_value "${application}_PRERUN") # 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" # Scripts for DOSBox games targeting a compatibility level < 2.20 have no support for regular post-run actions. if ! compatibility_level_is_at_least '2.20'; then local application_type application_type=$(application_type "$application") if [ "$application_type" = 'dosbox' ]; then return 0 fi fi 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") # 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.sh0000644000000000000000000000730013120060140015133 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 # - 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 ( \ 'dosbox' | \ 'java' | \ 'mono' | \ 'native' | \ '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 ('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.sh0000644000000000000000000001002113120060140015763 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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.sh0000644000000000000000000002343113120060140015257 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 # Fall back on trying to find archives using the old naming scheme if ! compatibility_level_is_at_least '2.21'; then archives_list=$(archives_return_list_legacy) if [ -n "$archives_list" ]; then printf '%s' "$archives_list" return 0 fi fi error_no_archive_supported return 1 } src/30_archives/05_selection_extra-archives.sh0000644000000000000000000001522613120060140020274 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() { 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 ;; ('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 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 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' } 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.sh0000644000000000000000000002327613120060140015463 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 ;; ('mojosetup_unzip') # WARNING - This archive type is deprecated. 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 ;; ('zip_unclean') 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 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" ;; ('mojosetup_unzip') warning_archive_type_deprecated "$archive" 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" ;; ('zip_unclean') warning_archive_type_deprecated "$archive" archive_extraction_zip_unclean "$archive" "$destination_directory" "$log_file" ;; (*) error_archive_type_invalid "$archive_type" return 1 ;; esac } src/30_archives/25_extraction_extra-archives.sh0000644000000000000000000000550013120060140020463 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 ;; ('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 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' } 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.sh0000644000000000000000000000227413120060140015142 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 } # extract the content of a .zip archive (ignore errors) # USAGE: archive_extraction_zip_unclean $archive $destination_directory $log_file archive_extraction_zip_unclean() { # WARNING - This function is deprecated. 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" || true 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.sh0000644000000000000000000000155413120060140016161 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") 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.sh0000644000000000000000000001775413120060140015125 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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() { 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 } # Error - No archive supported for the current game script error_no_archive_supported() { local game_script game_script=$(realpath "$0") local message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message_1 message_2 case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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.sh0000644000000000000000000000502013120060140014422 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" ] && \ ! compatibility_level_is_at_least '2.19' 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" ] && \ ! compatibility_level_is_at_least '2.19' 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.sh0000644000000000000000000001712113120060140016243 0ustar rootroot# 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() { 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_game_data "$package" content_inclusion_default_documentation "$package" 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_suffix package="$1" assert_not_empty 'package' 'content_inclusion_default_libraries' package_suffix="${package#PKG_}" 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 files_list for index in $(seq 0 9); do # Stop looping at the first unset files list files_list=$(context_name "CONTENT_LIBS${index}_${package_suffix}_FILES") if [ -z "$files_list" ]; then break 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 game data files for a single package. # USAGE: content_inclusion_default_game_data $package content_inclusion_default_game_data() { local package package_suffix package="$1" assert_not_empty 'package' 'content_inclusion_default_game_data' package_suffix="${package#PKG_}" local target_directory target_directory=$(path_game_data) content_inclusion "GAME_${package_suffix}" "$package" "$target_directory" local index files_list for index in $(seq 0 9); do # Stop looping at the first unset files list files_list=$(context_name "CONTENT_GAME${index}_${package_suffix}_FILES") if [ -z "$files_list" ]; then break fi content_inclusion "GAME${index}_${package_suffix}" "$package" "$target_directory" done # Try to parse legacy variables for old game scripts if ! compatibility_level_is_at_least '2.19'; then for index in $(seq 0 9); do # Stop looping at the first unset files list files_list=$(context_name "ARCHIVE_GAME${index}_${package_suffix}_FILES") if [ -z "$files_list" ]; then break fi content_inclusion "GAME${index}_${package_suffix}" "$package" "$target_directory" done fi } # 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_suffix package="$1" assert_not_empty 'package' 'content_inclusion_default_documentation' package_suffix="${package#PKG_}" local target_directory target_directory=$(path_documentation) content_inclusion "DOC_${package_suffix}" "$package" "$target_directory" local index files_list for index in $(seq 0 9); do # Stop looping at the first unset files list files_list=$(context_name "CONTENT_DOC${index}_${package_suffix}_FILES") if [ -z "$files_list" ]; then break fi content_inclusion "DOC${index}_${package_suffix}" "$package" "$target_directory" done # Try to parse legacy variables for old game scripts if ! compatibility_level_is_at_least '2.19'; then for index in $(seq 0 9); do # Stop looping at the first unset files list files_list=$(context_name "ARCHIVE_DOC${index}_${package_suffix}_FILES") if [ -z "$files_list" ]; then break fi content_inclusion "DOC${index}_${package_suffix}" "$package" "$target_directory" done fi } # 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" # 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 ( cd "$content_path_full" local file_pattern file_patterns file_patterns=$(content_files "$content_id") while read -r file_pattern; do if [ -z "$file_pattern" ]; then continue fi if [ -e "$file_pattern" ]; then content_inclusion_include_file "$file_pattern" "$destination_path" else content_inclusion_include_pattern "$file_pattern" "$destination_path" fi done <<- EOF $(printf '%s' "$file_patterns") EOF ) } # Fetch a given file or directory from the archive, # identified by an explicit path. # Non existing files are skipped silently. # USAGE: content_inclusion_include_file $file_path $destination_path content_inclusion_include_file() { local file_path destination_path file_path="$1" destination_path="$2" # Skip silently files that are not found if [ ! -e "$file_path" ]; then return 0 fi mkdir --parents "$destination_path" cp \ --force \ --link \ --recursive \ --no-dereference \ --parents \ --preserve=links \ "$file_path" "$destination_path" rm --force --recursive "$file_path" } # Fetch several files or directories from the archive, # identified by a pattern. # USAGE: content_inclusion_include_pattern $file_pattern $destination_path content_inclusion_include_pattern() { local file_pattern destination_path file_pattern="$1" destination_path="$2" local file_path while read -r file_path; do content_inclusion_include_file "$file_path" "$destination_path" done <<- EOF $(find . -path "./${file_pattern#./}") EOF } src/30_content/15_files-inclusion_extra-libraries.sh0000644000000000000000000002532713120060140021434 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" ;; ('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=$(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=$(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 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=$(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=$(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=$(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=$(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/20_huge-files.sh0000644000000000000000000001266213120060140015176 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" | \ grep --invert-match --regexp='^$' | \ sort --unique } # 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.sh0000644000000000000000000000062013120060140014753 0ustar rootroot# 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 message case "${LANG%_*}" 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.sh0000644000000000000000000001204713120060140014072 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 ### # TODO # This should be deprecated ### # Check for an explicit wrestool id if ! variable_is_empty "${icon}_ID"; then wrestool_options="$wrestool_options --name=$(get_value "${icon}_ID")" fi printf '%s' "$wrestool_options" } src/30_icons/10_conversion.sh0000644000000000000000000002175213120060140014773 0ustar rootroot# Fetch icon from the archive contents, # convert it to PNG if it is not already in a supported format, # include it in the current 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: icons_inclusion $application[…] icons_inclusion() { # 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 # A late requirements check is run here, # in case it was skipped earlier because of an empty icons list. # This can happen if both the application type and icon are implicit, # like with WINE games where the icons source is the game binary. # Failing here is not ideal because the archive contents extraction already happened, # but at least the failure is explicit instead of silent. requirements_check_icons # If no applications are explicitely listed, # try to fetch the icons for all applications. if [ "$#" -eq 0 ]; then applications_list=$(applications_list) # If icons_inclusion 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 icons_inclusion $applications_list return 0 fi local application for application in "$@"; do assert_not_empty 'application' 'icons_inclusion' icons_inclusion_single_application "$application" done } # Fetch icon from the archive contents, # convert it to PNG if it is not already in a supported format, # include it in the current package. # # This function handles all icons for a given application. # # USAGE: icons_inclusion_single_application $application icons_inclusion_single_application() { local application application="$1" assert_not_empty 'application' 'icons_inclusion_single_application' local application_icons_list application_icons_list=$(application_icons_list "$application") # Return early if the current application has no associated icon if [ -z "$application_icons_list" ]; then return 0 fi local icon for icon in $application_icons_list; do icons_inclusion_single_icon "$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 current package. # # This function handles a single icon. # # USAGE: icons_inclusion_single_icon $application $icon icons_inclusion_single_icon() { local application icon application="$1" icon="$2" # 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 "$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 } # 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 current package # USAGE: icons_include_from_directory $app $directory # RETURNS: nothing # SIDE EFFECT: take the icons from the given directory, move them to standard paths into the current package icons_include_from_directory() { # Get the application id local application application_id application="$1" application_id=$(application_id "$application") # Get the icons from the given source directory, # then move them to the current package local source_directory source_file destination_name destination_directory destination_file path_icons source_directory="$2" path_icons=$(path_icons) 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 # Compute icon file name destination_name="${application_id}.${source_file##*.}" # Compute icon path local package package_path icon_resolution package=$(current_package) package_path=$(package_path "$package") icon_resolution=$(icon_get_resolution "$source_file") destination_directory="${package_path}${path_icons}/${icon_resolution}/apps" # Move current icon file to its destination destination_file="${destination_directory}/${destination_name}" mkdir --parents "$destination_directory" mv "$source_file" "$destination_file" 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.sh0000644000000000000000000000421513120060140014420 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" in ('fr') message='Lʼidentifient 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" } 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.sh0000644000000000000000000001154713120060140016057 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message1 message2 case "${LANG%_*}" 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 message1 message2 case "${LANG%_*}" 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 message1 message2 case "${LANG%_*}" 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 message1 message2 case "${LANG%_*}" 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.sh0000644000000000000000000002064313120060140014744 0ustar rootroot# Print the path to the launcher script for the given application. # USAGE: launcher_path $application # RETURN: The absolute path to the launcher launcher_path() { local application application="$1" local package package_path path_binaries application_id target_file package=$(current_package) package_path=$(package_path "$package") path_binaries=$(path_binaries) application_id=$(application_id "$application") printf '%s%s/%s' "$package_path" "$path_binaries" "$application_id" } # Write launcher script for the given application # USAGE: launcher_write_script $application launcher_write_script() { local application application="$1" assert_not_empty 'application' 'launcher_write_script' # Check that the launcher target exists if ! launcher_target_presence_check "$application"; then local application_exe application_exe=$(application_exe "$application") ## Check that application binary has been found if [ -z "$application_exe" ]; then error_application_exe_empty "$application" 'launcher_write_script' return 1 fi error_launcher_missing_binary "$application_exe" return 1 fi # Get application type and prefix type 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") # write launcher script local target_file debug_write_launcher "$application_type" "$binary_file" target_file=$(launcher_path "$application") mkdir --parents "$(dirname "$target_file")" touch "$target_file" chmod 755 "$target_file" case "$application_type" in ('dosbox') dosbox_launcher "$application" > "$target_file" local package package=$(current_package) dependencies_add_command "$package" 'dosbox' ;; ('java') java_launcher "$application" > "$target_file" local package package=$(current_package) dependencies_add_command "$package" 'java' ;; ('mono') mono_launcher "$application" > "$target_file" local package package=$(current_package) dependencies_add_command "$package" 'mono' ;; ('native') native_launcher "$application" > "$target_file" ;; ('scummvm') scummvm_launcher "$application" > "$target_file" local package package=$(current_package) dependencies_add_command "$package" 'scummvm' ;; ('wine') wine_launcher "$application" > "$target_file" local package package=$(current_package) dependencies_add_command "$package" 'wine' ;; esac # For native applications, add execution permissions to the game binary file. case "$application_type" in ('native') local application_exe application_exe_path application_exe=$(application_exe "$application") ## Check that application binary has been found if [ -z "$application_exe" ]; then error_application_exe_empty "$application" 'launcher_write_script' return 1 fi application_exe_path=$(application_exe_path "$application_exe") chmod +x "$application_exe_path" ;; esac } # Check that the launcher target exists. # USAGE: launcher_target_presence_check $application # RETURN: 0 if the game binary has been found, # 0 if no game binary is expected, # 1 if the game binary has not been found launcher_target_presence_check() { local application application="$1" local application_type application_type_status application_type=$(application_type "$application") application_type_status=$? ## Exit early if fetching the application type failed. if [ $application_type_status -ne 0 ]; then ## Use exit instead of return to ensure the script execution ends here, ## return 1 would not stop it because this function is called through test. exit 1 fi case "$application_type" in ('scummvm') # ScummVM games do not rely on a provided binary. return 0 ;; esac local application_exe application_exe_path application_exe=$(application_exe "$application") ## Check that application binary has been found if [ -z "$application_exe" ]; then error_application_exe_empty "$application" 'launcher_target_presence_check' ## Use exit instead of return to ensure the script execution ends here, ## return 1 would not stop it because this function is called through test. exit 1 fi application_exe_path=$(application_exe_path "$application_exe") test -f "$application_exe_path" } # 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 } | sed --regexp-extended 's/( ){4}/\t/g' } # write the XDG desktop file for the given application # USAGE: launcher_write_desktop $application launcher_write_desktop() { local application application="$1" # write desktop file local desktop_file desktop_file=$(launcher_desktop_filepath "$application") mkdir --parents "$(dirname "$desktop_file")" 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_icon application_category launcher_desktop_exec application_name=$(application_name "$application") application_icon=$(application_id "$application") application_category=$(application_category "$application") launcher_desktop_exec=$(launcher_desktop_exec "$application") cat <<- EOF [Desktop Entry] Version=1.0 Type=Application Name=$application_name Icon=$application_icon $launcher_desktop_exec Categories=$application_category EOF } # print the full path to the XDG desktop file for the given application # USAGE: launcher_desktop_filepath $application # RETURN: an absolute file path launcher_desktop_filepath() { local application application_id package package_path path_xdg_desktop application="$1" application_id=$(application_id "$application") package=$(current_package) 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: launcher_desktop_exec $application # RETURN: the "Exec" field string, including escaping if required launcher_desktop_exec() { local application application="$1" # Enclose the path in single quotes if it includes spaces local option_prefix field_format option_prefix=$(option_value 'prefix') case "$option_prefix" in (*' '*) field_format="Exec='%s'" ;; (*) field_format='Exec=%s' ;; esac # Use the full path for non-standard prefixes local field_value application_id application_id=$(application_id "$application") case "$option_prefix" in ('/usr'|'/usr/local') field_value="$application_id" ;; (*) local path_binaries path_binaries=$(path_binaries) field_value="${path_binaries}/${application_id}" ;; esac # shellcheck disable=SC2059 printf "$field_format" "$field_value" } # Write both the launcher script and menu entry for the given application # USAGE: launcher_write $application launcher_write() { local application application="$1" assert_not_empty 'application' 'launcher_write' launcher_write_script "$application" launcher_write_desktop "$application" return 0 } # write both a launcher script and a menu entry for each application from a list # USAGE: launchers_write [$application…] # RETURN: nothing launchers_write() { debug_entering_function 'launchers_write' 2 # If called with no argument, default to handling the full list of applications if [ $# -eq 0 ]; then local applications_list applications_list=$(applications_list) # Calling launchers_write with no explicit arguments is not supported # if the applications list is empty if [ -z "$applications_list" ]; then error_applications_list_empty return 1 fi launchers_write $applications_list debug_leaving_function 'launchers_write' 2 return 0 fi # Write a launcher script and a menu entry for each application local application for application in "$@"; do launcher_write "$application" done debug_leaving_function 'launchers_write' 2 } src/30_launchers/10_fake-home.sh0000644000000000000000000000515513120060140015312 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 } | sed --regexp-extended 's/( ){4}/\t/g' 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 } | sed --regexp-extended 's/( ){4}/\t/g' } src/30_launchers/10_prefixes.sh0000644000000000000000000000467113120060140015305 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 } | sed --regexp-extended 's/( ){4}/\t/g' } # 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 } | sed --regexp-extended 's/( ){4}/\t/g' } # 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.sh0000644000000000000000000002422213120060140017536 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" | \ ## Ignore grep failure if its input is empty grep --invert-match --regexp='^$' || true } # 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" | \ ## Ignore grep failure if its input is empty grep --invert-match --regexp='^$' || true } # 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 } | sed --regexp-extended 's/( ){4}/\t/g' } # 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 } | sed --regexp-extended 's/( ){4}/\t/g' } # 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 } | sed --regexp-extended 's/( ){4}/\t/g' } # 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 } | sed --regexp-extended 's/( ){4}/\t/g' } # 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 } | sed --regexp-extended 's/( ){4}/\t/g' } # 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 } | sed --regexp-extended 's/( ){4}/\t/g' } src/30_launchers/90_messages.sh0000644000000000000000000000331113120060140015265 0ustar rootroot# Error - A binary file is missing # USAGE: error_launcher_missing_binary $binary # CALLS: print_error error_launcher_missing_binary() { local binary binary="$1" local message case "${LANG%_*}" 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 message case "${LANG%_*}" 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.sh0000644000000000000000000003400513120060140014533 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 fi 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 all the packages that could be built from the current game script, # not restricted to the current archive. # USAGE: packages_list_all_archives # RETURN: a list of package identifiers, # separated by line breaks, # without duplicates packages_list_all_archives() { # List the packages that could be generated for each supported archive. # This list will include duplicates. 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 ) ## TODO: This output sanitizing should be moved to a dedicated function. # Print the full list of packages, one per line, with no duplicates. printf '%s\n' $packages_list | \ grep --invert-match --regexp='^$' | \ sort --unique } # 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 package_id=$(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 architecture string of the given package, in the format expected by the packages manager # USAGE: package_architecture_string $package # RETURNS: the package architecture, in the format expected by the packages manager package_architecture_string() { local package package="$1" local option_package package_architecture_string option_package=$(option_value 'package') case "$option_package" in ('arch') package_architecture_string=$(archlinux_package_architecture_string "$package") ;; ('deb') package_architecture_string=$(debian_package_architecture_string "$package") ;; ('gentoo'|'egentoo') package_architecture_string=$(gentoo_package_architecture_string "$package") ;; esac printf '%s' "$package_architecture_string" } # 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") # Fall back to the legacy PKG_xxx_PROVIDE variable, # for game scripts targeting ./play.it ≤ 2.23 only. if \ [ -z "$package_provides" ] && \ ! compatibility_level_is_at_least '2.24' then package_provides=$(package_provide_legacy "$package") fi # Return early if there is no package name to print if [ -z "$package_provides" ]; then return 0 fi # Skip empty lines, # ignore grep error state if there is nothing to return. printf '%s' "$package_provides" | \ grep --invert-match --regexp='^$' || true } # 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.sh0000644000000000000000000000311113120060140015665 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") # Fall back on the default list of generic dependencies for the current game engine if [ -z "$dependencies_generic" ]; then local game_engine game_engine=$(game_engine) case "$game_engine" in ('visionaire') dependencies_generic=$(visionaire_package_dependencies_generic "$package") ;; esac fi ## TODO: This output sanitizing should be moved to a dedicated function. # Always return a list with no duplicate entry, excluding empty lines. # Ignore grep error return if there is nothing to print. printf '%s' "$dependencies_generic" | \ sed 's/ /\n/g' | \ sort --unique | \ grep --invert-match --regexp='^$' || true } # 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.sh0000644000000000000000000000544213120060140017564 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 # Always return a list with no duplicate entry, # excluding empty lines. # Ignore grep error return if there is nothing to print. printf '%s' "$dependencies_commands" | \ sort --unique | \ grep --invert-match --regexp='^$' || true } # 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 # Display the list of unknown commands, # skipping duplicates and empty entries. sort --unique "$unknown_commands_list" | \ grep --invert-match --regexp='^$' } # 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.sh0000644000000000000000000000533313120060140021432 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" | \ sort --unique | \ grep --invert-match --regexp='^$' || true } # 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 # Display the list of unknown formats, # skipping duplicates and empty entries. sort --unique "$unknown_formats_list" | \ grep --invert-match --regexp='^$' } # 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.sh0000644000000000000000000000662413120060140020710 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") # Always return a list with no duplicate entry, # excluding empty lines. # Ignore grep error return if there is nothing to print. printf '%s' "$dependencies_mono_libraries" | \ sort --unique | \ grep --invert-match --regexp='^$' || true } # 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 # Display the list of unknown libraries, # skipping duplicates and empty entries. sort --unique "$unknown_libraries_list" | \ grep --invert-match --regexp='^$' } # 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.sh0000644000000000000000000001236313120060140021223 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 ## TODO: This output sanitizing should be moved to a dedicated function. # Always return a list with no duplicate entry, excluding empty lines. # Ignore grep error return if there is nothing to print. printf '%s' "$dependencies_libraries" | \ sort --unique | \ grep --invert-match --regexp='^$' || true } # 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 # Always return a list with no duplicate entry, # excluding empty lines. # Ignore grep error return if there is nothing to print. printf '%s' "$dependencies_libraries_all" | \ sort --unique | \ grep --invert-match --regexp='^$' || true } # 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_library 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 # Display the list of unknown libraries, # skipping duplicates and empty entries. sort --unique "$unknown_libraries_list" | \ grep --invert-match --regexp='^$' } # Clear the list of unknown libraries # USAGE: dependencies_unknown_libraries_clear dependencies_unknown_libraries_clear() { local unknown_library 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/90_messages.sh0000644000000000000000000000511013120060140015056 0ustar rootroot# 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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.sh0000644000000000000000000001102713120060140014073 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') last_path_component=$(egentoo_package_name) ;; 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" } 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.sh0000644000000000000000000000315613120060140013663 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 # TODO: Display an explicit error message 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.sh0000644000000000000000000000453113120060140014566 0ustar rootroot# Build and include all PRELOAD hacks for the current package # USAGE: hacks_inclusion_default # RETURN: 0 if all hacks could be built and included in the package, # 0 if the current package should include no hack, # 1 otherwise hacks_inclusion_default() { local package hacks_list hack package=$(current_package) hacks_list=$(hacks_included_in_package "$package") if [ -z "$hacks_list" ]; then return 0 fi for hack in $hacks_list; do hack_build "$hack" hack_inclusion "$hack" 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/50_engine_dosbox/10_applications.sh0000644000000000000000000000213013120060140016773 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() { # Game scripts targeting ./play.it < 2.20 used to set these actions through APP_xxx_PRERUN. if ! compatibility_level_is_at_least '2.20'; then dosbox_prerun_legacy "$application" return 0 fi 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() { # Game scripts targeting ./play.it < 2.20 used to set these actions through APP_xxx_PRERUN. if ! compatibility_level_is_at_least '2.20'; then dosbox_postrun_legacy "$application" return 0 fi local application application="$1" get_value "${application}_DOSBOX_POSTRUN" } src/50_engine_dosbox/10_launchers.sh0000644000000000000000000000774313120060140016310 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 application_options game_id=$(game_id) path_game=$(path_game_data) application_exe=$(application_exe_escaped "$application") application_options=$(application_options "$application") cat <<- EOF # Set the environment GAME_ID='$game_id' PATH_GAME='$path_game' APP_EXE='$application_exe' APP_OPTIONS="$application_options" 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 cat <<- 'EOF' $APP_EXE $APP_OPTIONS $@ EOF if [ -n "$dosbox_postrun" ]; then cat <<- EOF $dosbox_postrun EOF fi cat <<- EOF exit EOF } src/50_engine_dosbox/90_messages.sh0000644000000000000000000000066513120060140016137 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 message case "${LANG%_*}" 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.sh0000644000000000000000000000463313120060140015726 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 application_options application_java_options game_id=$(game_id) path_game=$(path_game_data) application_exe=$(application_exe_escaped "$application") application_options=$(application_options "$application") application_java_options=$(application_java_options "$application") ## Legacy variable APP_xxx_LIBS should not be used with compatibility level ≥ 2.19, ## a warning will be trigerred is it is set. application_libs=$(application_libs_legacy "$application") cat <<- EOF # Set the environment GAME_ID='$game_id' PATH_GAME='$path_game' APP_EXE='$application_exe' APP_OPTIONS="$application_options" JAVA_OPTIONS='$application_java_options' APP_LIBS='$application_libs' 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 ## Silence ShellCheck false-positive ## Double quote to prevent globbing and word splitting. # shellcheck disable=SC2086 java $JAVA_OPTIONS -jar "$APP_EXE" $APP_OPTIONS "$@" game_exit_status=$? set -o errexit EOF application_postrun "$application" } src/50_engine_mono/10_launchers.sh0000644000000000000000000000573513120060140015761 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 application_options game_id=$(game_id) path_game=$(path_game_data) application_exe=$(application_exe_escaped "$application") application_options=$(application_options "$application") ## Legacy variable APP_xxx_LIBS should not be used with compatibility level ≥ 2.19, ## a warning will be trigerred is it is set. application_libs=$(application_libs_legacy "$application") cat <<- EOF # Set the environment GAME_ID='$game_id' PATH_GAME='$path_game' APP_EXE='$application_exe' APP_OPTIONS="$application_options" APP_LIBS='$application_libs' 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 ## Silence ShellCheck false-positive ## Double quote to prevent globbing and word splitting. # shellcheck disable=SC2086 mono "$APP_EXE" $APP_OPTIONS "$@" 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 } src/50_engine_native/10_launchers.sh0000644000000000000000000001452513120060140016274 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 application_options game_id=$(game_id) path_game=$(path_game_data) application_exe=$(application_exe_escaped "$application") application_options=$(application_options "$application") ## Legacy variable APP_xxx_LIBS should not be used with compatibility level ≥ 2.19, ## a warning will be trigerred is it is set. application_libs=$(application_libs_legacy "$application") cat <<- EOF # Set the environment GAME_ID='$game_id' PATH_GAME='$path_game' APP_EXE='$application_exe' APP_OPTIONS="$application_options" APP_LIBS='$application_libs' 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 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 # Use a dedicated log file for the current game session launcher_unity3d_dedicated_log # 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 # Set loading paths for libraries native_launcher_libraries # 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 ## Silence ShellCheck false-positive ## Double quote to prevent globbing and word splitting. # shellcheck disable=SC2086 "./$APP_EXE" $APP_OPTIONS "$@" 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 } | sed --regexp-extended 's/( ){4}/\t/g' } # Linux native launcher - Load shipped libraries # USAGE: native_launcher_libraries native_launcher_libraries() { local path_legacy path_system path_user path_legacy='$APP_LIBS' path_system=$(path_libraries) path_user='${HOME}/.local/lib/games/${GAME_ID}' cat <<- EOF # Set loading paths for libraries PLAYIT_LIBS_PATH_LEGACY="$path_legacy" PLAYIT_LIBS_PATH_SYSTEM='$path_system' PLAYIT_LIBS_PATH_USER="$path_user" EOF { cat <<- 'EOF' if [ -n "$PLAYIT_LIBS_PATH_LEGACY" ]; then LD_LIBRARY_PATH="${PLAYIT_LIBS_PATH_LEGACY}:${LD_LIBRARY_PATH}" fi 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 } | sed --regexp-extended 's/( ){4}/\t/g' } src/50_engine_scummvm/10_applications.sh0000644000000000000000000000145113120060140017171 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" 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.sh0000644000000000000000000000274513120060140016476 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 application_options path_game=$(path_game_data) application_scummid=$(application_scummvm_scummid "$application") if [ -z "$application_scummid" ]; then error_application_scummid_invalid "$application" "$application_scummid" return 1 fi application_options=$(application_options "$application") cat <<- EOF # Set the environment PATH_GAME='$path_game' SCUMMVM_ID='$application_scummid' APP_OPTIONS="$application_options" EOF } # ScummVM launcher - Run ScummVM # USAGE: scummvm_launcher_run scummvm_launcher_run() { cat <<- 'EOF' # Run the game EOF application_prerun "$application" cat <<- 'EOF' ## Silence ShellCheck false-positive ## Double quote to prevent globbing and word splitting. # shellcheck disable=SC2086 scummvm --path="$PATH_GAME" $APP_OPTIONS "$@" "$SCUMMVM_ID" EOF application_postrun "$application" } src/50_engine_scummvm/90_messages.sh0000644000000000000000000000146613120060140016330 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 message case "${LANG%_*}" 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.sh0000644000000000000000000000332613120060140015250 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 verb 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 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.sh0000644000000000000000000001174213120060140015746 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 # Automatically add required dependencies to the current package local winetricks_verbs winetricks_verbs=$(wine_winetricks_verbs) if [ -n "$winetricks_verbs" ]; then local package package=$(current_package) dependencies_add_command "$package" 'winetricks' fi } # WINE launcher - Set the environment # USAGE: wine_launcher_environment $application wine_launcher_environment() { local application application="$1" local game_id path_game application_exe application_options game_id=$(game_id) path_game=$(path_game_data) application_exe=$(application_exe_escaped "$application") application_options=$(application_options "$application") cat <<- EOF # Set the environment GAME_ID='$game_id' PATH_GAME='$path_game' APP_EXE='$application_exe' APP_OPTIONS="$application_options" 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 } | sed --regexp-extended 's/( ){4}/\t/g' ## 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 } | sed --regexp-extended 's/( ){4}/\t/g' 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 } | sed --regexp-extended 's/( ){4}/\t/g' 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 } | sed --regexp-extended 's/( ){4}/\t/g' 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 ('unity3d') # Use a dedicated log file for the current game session launcher_unity3d_dedicated_log ;; ('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 ## Silence ShellCheck false-positive ## Double quote to prevent globbing and word splitting. # shellcheck disable=SC2086 $(wine_command) "$APP_EXE" $APP_OPTIONS "$@" game_exit_status=$? set -o errexit EOF application_postrun "$application" } src/50_engine_wine/20_regedit.sh0000644000000000000000000001053013120060140015400 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 } | sed --regexp-extended 's/( ){4}/\t/g' } # 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 } | sed --regexp-extended 's/( ){4}/\t/g' } # 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 } | sed --regexp-extended 's/( ){4}/\t/g' } # 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 } | sed --regexp-extended 's/( ){4}/\t/g' } src/50_engine_wine/20_renderer.sh0000644000000000000000000001236113120060140015567 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 } | sed --regexp-extended 's/( ){4}/\t/g' # Automatically add required dependencies to the current package local package dependency_library package=$(current_package) case "$wined3d_backend" in ('gl') dependency_library='libGL.so.1' ;; ('vulkan') dependency_library='libvulkan.so.1' ;; esac dependencies_add_command "$package" 'winetricks' if [ -n "${dependency_library:-}" ]; then dependencies_add_native_libraries "$package" "$dependency_library" fi } # 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 } | sed --regexp-extended 's/( ){4}/\t/g' # Automatically add required dependencies to the current package local package package=$(current_package) dependencies_add_native_libraries "$package" 'libvulkan.so.1' dependencies_add_command "$package" 'winetricks' } # 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 } | sed --regexp-extended 's/( ){4}/\t/g' # Automatically add required dependencies to the current package local package package=$(current_package) dependencies_add_native_libraries "$package" 'libvulkan.so.1' dependencies_add_command "$package" 'winetricks' } src/50_engine_wine/20_wineprefix.sh0000644000000000000000000001420213120060140016135 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 wine_dlloverrides='winemenubuilder.exe,mscoree,mshtml=' 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 } | sed --regexp-extended 's/( ){4}/\t/g' # 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 } | sed --regexp-extended 's/( ){4}/\t/g' # 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 } | sed --regexp-extended 's/( ){4}/\t/g' # 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 } | sed --regexp-extended 's/( ){4}/\t/g' 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 } | sed --regexp-extended 's/( ){4}/\t/g' 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/90_messages.sh0000644000000000000000000000075413120060140015602 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 message case "${LANG%_*}" 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" } 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} ${unity3d_name}.x86 ${unity3d_name}.exe" ;; ('64') filename_candidates_list=" ${unity3d_name} ${unity3d_name}.x86_64 ${unity3d_name}.exe" ;; (*) filename_candidates_list=" ${unity3d_name} ${unity3d_name}.x86 ${unity3d_name}.x86_64 ${unity3d_name}.exe" ;; 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.sh0000644000000000000000000001214013120060140017533 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=$(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 mono MonoBleedingEdge monobleedingedge ${unity3d_name}_Data/Mono ${unity3d_name}_data/mono ${unity3d_name}_Data/MonoBleedingEdge ${unity3d_name}_data/monobleedingedge ${unity3d_name}_Data/Plugins ${unity3d_name}_data/plugins ${unity3d_name}.exe *.dll" ;; 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.sh0000644000000000000000000001004313120060140020045 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 } | sed --regexp-extended 's/( ){4}/\t/g' } # 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 } | sed --regexp-extended 's/( ){4}/\t/g' } # Print the snippet hiding libpulse-simple.so.0 if pulseaudio is not available # USAGE: launcher_unity3d_pulseaudio_hide_libpulse launcher_unity3d_pulseaudio_hide_libpulse() { ## FIXME: This snippet should be updated to no longer rely on APP_LIBS. { 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="${APP_LIBS:=libs}/libpulse-simple.so.0" if pulseaudio_is_available; then rm --force "$libpulse_null_link" else mkdir --parents "$(dirname "$libpulse_null_link")" ln --force --symbolic /dev/null "$libpulse_null_link" fi unset libpulse_null_link EOF } | sed --regexp-extended 's/( ){4}/\t/g' } # Print the snippet setting a dedicated log file for the current game session # USAGE: launcher_unity3d_dedicated_log # RETURN: the code snippet, a multi-lines string launcher_unity3d_dedicated_log() { { cat <<- 'EOF' # Use a dedicated log file for the current game session mkdir --parents logs APP_OPTIONS="${APP_OPTIONS} -logFile ./logs/$(date +%F-%R).log" EOF } | sed --regexp-extended 's/( ){4}/\t/g' } # 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 } | sed --regexp-extended 's/( ){4}/\t/g' } # 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.sh0000644000000000000000000000112713120060140017701 0ustar rootroot# Error - Unity3D - No binary has been found # USAGE: error_unity3d_binary_not_found error_unity3d_binary_not_found() { local message case "${LANG%_*}" 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.sh0000644000000000000000000000126413120060140020623 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 } | sed --regexp-extended 's/( ){4}/\t/g' } src/55_engine-variant_visionaire/30_packages.sh0000644000000000000000000000514113120060140020413 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 generic dependencies for the given package # USAGE: visionaire_package_dependencies_generic $package # RETURN: a list of package ids, # separated by line breaks visionaire_package_dependencies_generic() { local package package="$1" local package_dependencies case "$package" in ('PKG_BIN') local pkg_data_id pkg_data_id=$(package_id 'PKG_DATA') package_dependencies=" $pkg_data_id" ;; esac ## TODO: This output sanitizing should be moved to a dedicated function. # Always return a list excluding empty lines. # Ignore grep error return if there is nothing to print. printf '%s' "${package_dependencies:-}" | \ grep --invert-match --regexp='^$' || true } # 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 ## TODO: This output sanitizing should be moved to a dedicated function. # Always return a list excluding empty lines. # Ignore grep error return if there is nothing to print. printf '%s' "${package_dependencies:-}" | \ grep --invert-match --regexp='^$' || true } 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.sh0000644000000000000000000002607013120060140016653 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 package_id package_version package_maintainer package_builddate package_size package_architecture package_description package_provides package_fields_depend package_id=$(package_id "$package") package_version=$(package_version) package_maintainer=$(package_maintainer) package_builddate=$(date +%s) package_size=$(du --total --block-size=1 --summarize "$package_path" | tail --lines=1 | cut --fields=1) package_architecture=$(package_architecture_string "$package") package_pkgdesc=$(archlinux_field_pkgdesc "$package") package_provides=$(archlinux_field_provides "$package") package_fields_depend=$(package_archlinux_fields_depend "$package") cat > "$target" <<- EOF # Generated by ./play.it $LIBRARY_VERSION pkgname = $package_id pkgver = $package_version packager = $package_maintainer builddate = $package_builddate size = $package_size arch = $package_architecture pkgdesc = $package_pkgdesc $package_fields_depend EOF if [ -n "$package_provides" ]; then cat >> "$target" <<- EOF $package_provides EOF fi # Write the .INSTALL metadata file local install_contents install_contents=$(archlinux_metadata_file_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 - Print the full list of postinstall commands # USAGE: archlinux_postinst_commands $package # RETURN: the postinstallation shell commands, including warnings, # spanning over several lines archlinux_postinst_commands() { local package package="$1" local postinst_actions postinst_warnings postinst_actions=$(package_postinst_actions "$package") postinst_warnings=$(package_postinst_warnings "$package") # 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 } # Arch Linux - Print the contents of the .INSTALL metadata file # USAGE: archlinux_metadata_file_install $package # RETURN: the contents of the .INSTALL file, # spanning over several lines archlinux_metadata_file_install() { local package package="$1" # Print the definitions of post_install and post_upgrade. local postinst_commands postinst_commands=$(archlinux_postinst_commands "$package") if [ -n "$postinst_commands" ]; then cat <<- EOF post_install() { $postinst_commands } 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 } # 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 ) } # Arch Linux - Print the contents of the "pkgdesc" field # USAGE: archlinux_field_pkgdesc $package 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 contents of the "conflict" and "provides" fields # USAGE: archlinux_field_provides $package 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 'conflict = %s\n' $package_provides printf 'provides = %s\n' $package_provides } # Arch Linux - Print list of "depend" fields # USAGE: package_archlinux_fields_depend $package package_archlinux_fields_depend() { local package package="$1" local dependencies_list dependency_string dependencies_list=$(dependencies_archlinux_full_list "$package") while read -r dependency_string; do if [ -z "$dependency_string" ]; then continue fi printf 'depend = %s\n' "$dependency_string" done <<- EOL $(printf '%s' "$dependencies_list") EOL } # 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=$(package_architecture_string "$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" } # Print the architecture string of the given package, in the format expected by pacman # USAGE: archlinux_package_architecture_string $package # RETURNS: the package architecture, as one of the following values: # - x86_64 # - any archlinux_package_architecture_string() { 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" } # 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/20_dependencies.sh0000644000000000000000000001410413120060140017517 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 Arch-specific dependencies local dependencies_specific dependencies_specific=$(context_value "${package}_DEPS_ARCH") if [ -n "$dependencies_specific" ]; then packages_list=$(printf '%s\n' "$dependencies_specific" | sed 's/ /\n/g') packages_list_full="$packages_list_full $packages_list" fi # 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" | \ sed 's/^\s*//g' | \ grep --invert-match --regexp='^$' | \ sort --unique } src/60_system_archlinux/25_dependencies_commands.sh0000644000000000000000000000426413120060140021413 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" | \ sed 's/^\s*//g' | \ grep --invert-match --regexp='^$' | \ sort --unique } # 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' ;; ('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.sh0000644000000000000000000001025313120060140023255 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" | \ sed 's/^\s*//g' | \ grep --invert-match --regexp='^$' | \ sort --unique } # 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 ('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 ('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.sh0000644000000000000000000005256313120060140023057 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" | \ sed 's/^\s*//g' | \ grep --invert-match --regexp='^$' | \ sort --unique } # 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" | \ sed 's/^\s*//g' | \ grep --invert-match --regexp='^$' | \ sort --unique } # 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' ;; ('libavcodec.so.58') package_name='ffmpeg' ;; ('libavformat.so.58') package_name='ffmpeg' ;; ('libavutil.so.56') package_name='ffmpeg' ;; ('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' ;; ('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') package_name='gconf' ;; ('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' ;; ('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' ;; ('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 ;; ('libavcodec.so.58') package_name='lib32-libffmpeg' ;; ('libavformat.so.58') package_name='lib32-libffmpeg' ;; ('libavutil.so.56') package_name='lib32-libffmpeg' ;; ('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' ;; ('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') package_name='lib32-gconf' ;; ('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' ;; ('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' ;; ('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/90_messages.sh0000644000000000000000000000066613120060140016717 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 message case "${LANG%_*}" 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.sh0000644000000000000000000000455113120060140017066 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 message case "${LANG%_*}" 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.sh0000644000000000000000000002556113120060140016104 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, enforce correct permissions. local package_path control_directory package_path=$(package_path "$package") control_directory="${package_path}/DEBIAN" mkdir --parents "$control_directory" chmod 755 "$control_directory" # Write main metadata file (DEBIAN/control), enforce correct permissions. local control_file control_file="${control_directory}/control" debian_package_metadata_control "$package" > "$control_file" chmod 644 "$control_file" # Write postinst/prerm scripts, enforce correct permissions. local postinst_commands postinst_commands=$(debian_postinst_commands "$package") if [ -n "$postinst_commands" ]; 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 package_architecture package_depends package_description package_id package_maintainer package_provides package_size package_version package_architecture=$(package_architecture_string "$package") package_depends=$(package_debian_field_depends "$package") package_description=$(debian_field_description "$package") package_id=$(package_id "$package") package_maintainer=$(package_maintainer) package_provides=$(debian_field_provides "$package") package_size=$(debian_package_size "$package") package_version=$(package_version) cat <<- EOF Package: $package_id Version: $package_version Architecture: $package_architecture Multi-Arch: foreign Maintainer: $package_maintainer Installed-Size: $package_size Section: non-free/games EOF if [ -n "$package_provides" ]; then cat <<- EOF $package_provides EOF fi if [ -n "$package_depends" ]; then cat <<- EOF Depends: $package_depends EOF fi cat <<- EOF Description: $package_description EOF } # Debian - Print the necessary header for Debian control scripts # USAGE: debian_script_header # RETURN: the header for Debian control scripts, # spanning over multiple lines debian_script_header() { cat <<- EOF #!/bin/sh set -o errexit EOF } # Debian - Print the necessary footer for Debian control scripts # USAGE: debian_script_header # RETURN: the footer for Debian control scripts, # spanning over multiple lines debian_script_footer() { cat <<- EOF exit 0 EOF } # Debian - Print the commands that should be run after the package installation # USAGE: debian_postinst_commands $package # RETURN: a list of commands that can span over several lines, # or an empty string if there is no command to run debian_postinst_commands() { local package package="$1" # 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 } # 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" debian_script_header debian_postinst_commands "$package" debian_script_footer } # 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" debian_script_header package_prerm_actions "$package" debian_script_footer } # 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 # Use old .deb format if the package is going over the size limit for the modern format local dpkg_options package_size package_size=$(debian_package_size "$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 "fakeroot -- dpkg-deb $dpkg_options --build \"$package_path\" \"$generated_package_path\" 1>/dev/null" fakeroot -- 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 } # Debian - Compute the package installed size # USAGE: debian_package_size $package # RETURN: the package contents size, in kilobytes debian_package_size() { local package package="$1" # Compute the package size, in kilobytes. local package_path package_size package_path=$(package_path "$package") package_size=$( du --total --block-size=1K --summarize "$package_path" | \ tail --lines=1 | \ cut --fields=1 ) printf '%s' "$package_size" } # Debian - Print contents of "Depends" field # USAGE: package_debian_field_depends $package package_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 contents of "Description" field # USAGE: debian_field_description $package 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" } # Debian - Print the contents of the "Conflicts", "Provides" and "Replaces" fields # USAGE: debian_field_provides $package 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 packages_list package_name packages_list='' while read -r package_name; do if [ -z "$packages_list" ]; then packages_list="$package_name" else packages_list="$packages_list, $package_name" fi done <<- EOL $(printf '%s' "$package_provides") EOL printf 'Conflicts: %s\n' "$packages_list" printf 'Provides: %s\n' "$packages_list" printf 'Replaces: %s\n' "$packages_list" } # 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=$(package_architecture_string "$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" } # Print the architecture string of the given package, in the format expected by dpkg # USAGE: debian_package_architecture_string $package # RETURNS: the package architecture, as one of the following values: # - i386 # - amd64 # - all debian_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='i386' ;; ('64') package_architecture_string='amd64' ;; ('all') package_architecture_string='all' ;; esac printf '%s' "$package_architecture_string" } src/60_system_debian/20_dependencies.sh0000644000000000000000000000754313120060140016755 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 Debian-specific dependencies local dependencies_specific dependencies_specific=$(context_value "${package}_DEPS_DEB") if [ -n "$dependencies_specific" ]; then packages_list=$(printf '%s\n' "$dependencies_specific" | sed 's/, \?/\n/g') packages_list_full="$packages_list_full $packages_list" fi # 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" | \ sed 's/^\s*//g' | \ grep --invert-match --regexp='^$' | \ sort --unique } src/60_system_debian/25_dependencies_commands.sh0000644000000000000000000000556213120060140020642 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" | \ sed 's/^\s*//g' | \ grep --invert-match --regexp='^$' | \ sort --unique } # 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' ;; ('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.sh0000644000000000000000000000466413120060140022513 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" | \ sed 's/^\s*//g' | \ grep --invert-match --regexp='^$' | \ sort --unique } # 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 ('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.sh0000644000000000000000000000734613120060140021765 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" | \ sed 's/^\s*//g' | \ grep --invert-match --regexp='^$' | \ sort --unique } # 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.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.sh0000644000000000000000000002604313120060140022276 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" | \ sed 's/^\s*//g' | \ grep --invert-match --regexp='^$' | \ sort --unique } # 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' ;; ('libavcodec.so.58') package_name='libavcodec58 | libavcodec-extra58' ;; ('libavformat.so.58') package_name='libavformat58 | libavformat-extra58' ;; ('libavutil.so.56') package_name='libavutil56' ;; ('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' ;; ('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') package_name='libgconf-2-4' ;; ('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' ;; ('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' ;; ('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/90_messages.sh0000644000000000000000000000122213120060140016131 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 message case "${LANG%_*}" 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.sh0000644000000000000000000001757013120060140021340 0ustar rootroot# Gentoo ("egentoo" variant) - Write the metadata for the listed packages # USAGE: egentoo_packages_metadata $package[…] egentoo_packages_metadata() { local package_filename local ebuild_path local inherits inherits="xdg" 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 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 field_keywords field_rdepend field_bdepend field_keywords=$(egentoo_field_keywords "$@") field_rdepend=$(egentoo_field_rdepend "$@") ## FIXME: $build_deps is not set anywhere, so this field will always be empty. field_bdepend=${build_deps:-} 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="$field_keywords" DESCRIPTION="Ebuild automatically generated with ./play.it" HOMEPAGE="https://forge.dotslashplay.it/play.it" SRC_URI="$package_filename" SLOT="0" RDEPEND="$field_rdepend" BDEPEND="$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 --recursive --link --verbose \$S/data/* \$D || die fi if use x86 && test -d \$S/x86; then cp --recursive --link --verbose \$S/x86/* \$D || die elif use amd64; then if test -d \$S/amd64; then cp --recursive --link --verbose \$S/amd64/* \$D || die elif test -d \$S/x86; then cp --recursive --link --verbose \$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=$(package_architecture_string "$package") package_path="${package_id}_${package_version}_${package_architecture}" printf '%s' "$package_path" } src/60_system_gentoo/10_packages_gentoo-variant.sh0000644000000000000000000002346413120060140021172 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 ebuild_description ebuild_rdepend ebuild_description=$(gentoo_field_description "$package") ebuild_rdepend=$(package_gentoo_field_rdepend "$package") 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 cat <<- EOF EAPI=7 RESTRICT="fetch strip binchecks" KEYWORDS="$ebuild_keywords" DESCRIPTION="$ebuild_description" SLOT="0" RDEPEND="$ebuild_rdepend" EOF cat <<- 'EOF' src_unpack() { mkdir --parents "$S" } src_install() { cp --recursive --link $FILESDIR/install/* $ED/ } EOF 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 local prerm_actions prerm_actions=$(package_prerm_actions "$package") if [ -n "$prerm_actions" ]; then cat <<- EOF pkg_prerm() { $prerm_actions } EOF fi } # Gentoo ("gentoo" variant) - Print the contents of the "description" field # USAGE: gentoo_field_description $package 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 ("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=$(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/20_dependencies.sh0000644000000000000000000001506413120060140017023 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 # Include Gentoo-specific dependencies local dependencies_specific dependencies_specific=$(context_value "${package}_DEPS_GENTOO") if [ -n "$dependencies_specific" ]; then printf '%s\n' "$dependencies_specific" fi local packages_list packages_list_full packages_list_full='' # 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" | \ sed 's/^\s*//g' | \ grep --invert-match --regexp='^$' | \ sort --unique } # 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="$(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/20_ebuild_fields.sh0000644000000000000000000000701413120060140017163 0ustar rootroot# display keywords of an ebuild made of several packages # USAGE: egentoo_field_keywords $package… 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" } # display egentoo ebuild RDEPEND field # USAGE: egentoo_field_rdepend $package… 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 package_gentoo_field_rdepend "$package_64bits" elif [ -n "$package_32bits" ]; then package_gentoo_field_rdepend "$package_32bits" elif [ -n "$package_data" ]; then package_gentoo_field_rdepend "$package_data" fi } # Gentoo - Print "RDEPEND" field # USAGE: package_gentoo_field_rdepend $package package_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 packages_list package_current package_current_id package_current_provides package_current_provide packages_list=$(packages_list_all_archives) # For each conflict of the current package, # find all potential packages that would provide this package id. 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/20_package.sh0000644000000000000000000000073113120060140015763 0ustar rootroot# id of the single package built on egentoo # USAGE: egentoo_package_id egentoo_package_id() { game_id } # name of the single package built on egentoo # USAGE: egentoo_package_name egentoo_package_name() { # archive context is required to get the package version assert_not_empty 'ARCHIVE' '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/25_dependencies_commands.sh0000644000000000000000000000553213120060140020710 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" | \ sed 's/^\s*//g' | \ grep --invert-match --regexp='^$' | \ sort --unique } # 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' ;; ('scummvm') package_names=' games-engines/scummvm' ;; ('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.sh0000644000000000000000000001121413120060140022551 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" | \ sed 's/^\s*//g' | \ grep --invert-match --regexp='^$' | \ sort --unique } # 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 ('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 ('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.sh0000644000000000000000000006601013120060140022345 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" | \ sed 's/^\s*//g' | \ grep --invert-match --regexp='^$' | \ sort --unique } # 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" | \ sed 's/^\s*//g' | \ grep --invert-match --regexp='^$' | \ sort --unique } # 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' ;; ('libavcodec.so.58') package_name='media-video/ffmpeg' ;; ('libavformat.so.58') package_name='media-video/ffmpeg' ;; ('libavutil.so.56') package_name='media-video/ffmpeg' ;; ('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' ;; ('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') package_name='gnome-base/gconf' ;; ('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' ;; ('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' ;; ('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]' ;; ('libavcodec.so.58') package_name='media-video/ffmpeg[abi_x86_32]' ;; ('libavformat.so.58') package_name='media-video/ffmpeg[abi_x86_32]' ;; ('libavutil.so.56') package_name='media-video/ffmpeg[abi_x86_32]' ;; ('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]' ;; ('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') package_name='gnome-base/gconf[abi_x86_32]' ;; ('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]' ;; ('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]' ;; ('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/90_messages.sh0000644000000000000000000000523213120060140016207 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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 message case "${LANG%_*}" 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/00_warnings-list.sh0000644000000000000000000000311513120060140016565 0ustar rootroot# Print the path to a file listing the deprecation warnings already shown. # USAGE: deprecation_warnings_shown_file # RETURN: the path to the file listing the warnings, # or an empty string if such a path can not be computed yet deprecation_warnings_shown_file() { # The list of shown warnings can only be used when PLAYIT_WORKDIR is already set. if [ -z "${PLAYIT_WORKDIR:-}" ]; then return 0 fi printf '%s/warnings-shown/deprecation' "$PLAYIT_WORKDIR" } # Add a warning to the list of already shown ones. # USAGE: deprecation_warnings_shown_add $warning deprecation_warnings_shown_add() { local warnings_file warnings_file=$(deprecation_warnings_shown_file) # The list of shown warnings can only be used when PLAYIT_WORKDIR is already set. if [ -z "$warnings_file" ]; then return 0 fi local warnings_directory warnings_directory=$(dirname "$warnings_file") mkdir --parents "$warnings_directory" local warning warning="$1" printf '%s\n' "$warning" >> "$warnings_file" } # Check if a deprecation warning has already been shown. # USAGE: deprecation_warning_has_been_shown_already $warning # RETURN: 0 if the given warning has already been shown, # 1 if there is no warnings list yet, # 1 otherwise deprecation_warning_has_been_shown_already() { local warnings_file warnings_file=$(deprecation_warnings_shown_file) # The list of shown warnings can only be used when PLAYIT_WORKDIR is already set. if [ ! -e "$warnings_file" ]; then return 1 fi local warning warning="$1" grep --quiet --fixed-strings --line-regexp --regexp="$warning" "$warnings_file" } src/70_deprecation/90_messages.sh0000644000000000000000000001422213120060140015605 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" # Skip this warning if it has already been shown. if deprecation_warning_has_been_shown_already "warning_deprecated_function $*"; then return 0 fi local message case "${LANG%_*}" 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' "$message" \ "$old_function" \ "$new_function" \ > /dev/stderr # Prevent this warning from being shown again. deprecation_warnings_shown_add "warning_deprecated_function $*" } # 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" # Skip this warning if it has already been shown. if deprecation_warning_has_been_shown_already "warning_deprecated_variable $*"; then return 0 fi local message case "${LANG%_*}" 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' "$message" \ "$old_variable" \ "$new_variable" \ > /dev/stderr # Prevent this warning from being shown again. deprecation_warnings_shown_add "warning_deprecated_variable $*" } # 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" # Skip this warning if it has already been shown. if deprecation_warning_has_been_shown_already "warning_option_value_deprecated $*"; then return 0 fi local message case "${LANG%_*}" 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' "$message" \ "$option_name" \ "$option_value" \ > /dev/stderr # Prevent this warning from being shown again. deprecation_warnings_shown_add "warning_option_value_deprecated $*" } # Warning: An archive has a deprecated type set. # USAGE: warning_archive_type_deprecated $archive warning_archive_type_deprecated() { local archive archive="$1" # Skip this warning if it has already been shown. if deprecation_warning_has_been_shown_already "warning_archive_type_deprecated $*"; then return 0 fi local archive_type game_name archive_type=$(archive_type "$archive") game_name=$(game_name) local message case "${LANG%_*}" 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' "$message" \ "$game_name" \ "$archive_type" \ > /dev/stderr # Prevent this warning from being shown again. deprecation_warnings_shown_add "warning_archive_type_deprecated $*" } # Warning - The legacy variable has been use to set the current archive. # USAGE: warning_context_legacy_archive warning_context_legacy_archive() { # Skip this warning if it has already been shown. if deprecation_warning_has_been_shown_already 'warning_context_legacy_archive'; then return 0 fi local message case "${LANG%_*}" 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' "$message" > /dev/stderr # Prevent this warning from being shown again. deprecation_warnings_shown_add 'warning_context_legacy_archive' } # Warning - The legacy variable has been use to set the current package. # USAGE: warning_context_legacy_package warning_context_legacy_package() { # Skip this warning if it has already been shown. if deprecation_warning_has_been_shown_already 'warning_context_legacy_package'; then return 0 fi local message case "${LANG%_*}" 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' "$message" > /dev/stderr # Prevent this warning from being shown again. deprecation_warnings_shown_add 'warning_context_legacy_package' } src/80_debug/00_debug.sh0000644000000000000000000000615213120060140013650 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 the name of an used directory # USAGE: debug_using_directory $dir_path debug_using_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_using_directory' print_debug printf 'Using existing directory %s\n' "$dir_path" >> /dev/stderr return 0 } # print the type of the launcher being created # USAGE: debug_write_launcher $launcher_type debug_write_launcher() { local option_debug option_debug=$(option_value 'debug') if [ "$option_debug" -eq 0 ]; then return 0 fi local launcher_type binary_file launcher_type="$1" binary_file="$2" assert_not_empty 'launcher_type' 'debug_write_launcher' print_debug if [ -z "$binary_file" ]; then printf 'Writing launcher: %s\n' "$launcher_type" >> /dev/stderr else printf 'Writing %s launcher: %s\n' "$launcher_type" "$binary_file" >> /dev/stderr fi 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/83_2.26-and-older.sh0000644000000000000000000000055213120060140016611 0ustar rootroot# Keep compatibility with 2.26 and older archive_get_type() { if compatibility_level_is_at_least '2.27'; then warning_deprecated_function 'archive_get_type' 'archive_type' fi archive_type "$1" } packages_get_list() { if compatibility_level_is_at_least '2.27'; then warning_deprecated_function 'packages_get_list' 'package_list' fi packages_list } src/90_compatibility/84_2.25-and-older.sh0000644000000000000000000000031513120060140016606 0ustar rootroot# Keep compatibility with 2.25 and older archive_find_path() { if compatibility_level_is_at_least '2.26'; then warning_deprecated_function 'archive_find_path' 'archive_path' fi archive_path "$1" } src/90_compatibility/85_2.23-and-older.sh0000644000000000000000000000623613120060140016615 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 } | sed --regexp-extended 's/( ){4}/\t/g' } 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 } launcher_write_script_headers() { if compatibility_level_is_at_least '2.24'; then warning_deprecated_function 'launcher_write_script_headers' 'launcher_headers' fi launcher_headers > "$1" } launcher_native_libraries_paths() { if compatibility_level_is_at_least '2.24'; then warning_deprecated_function 'launcher_native_libraries_paths' 'native_launcher_libraries' fi native_launcher_libraries } src/90_compatibility/85_2.26-and-older.sh0000644000000000000000000000104613120060140016612 0ustar rootroot# Keep compatibility with 2.26 and older context_archive() { if compatibility_level_is_at_least '2.27'; then warning_deprecated_function 'context_archive' 'current_archive' fi current_archive } context_package() { if compatibility_level_is_at_least '2.27'; then warning_deprecated_function 'context_package' 'current_package' fi current_package } context_archive_suffix() { if compatibility_level_is_at_least '2.27'; then warning_deprecated_function 'context_archive_suffix' 'current_archive_suffix' fi current_archive_suffix } src/90_compatibility/86_2.22-and-older.sh0000644000000000000000000000372513120060140016615 0ustar rootroot# Keep compatibility with 2.22 and older package_provide_legacy() { local package package="$1" local package_provide package_provide=$(context_value "${package}_PROVIDE") # Return early if no alternative package name is provided by the current package. if [ -z "$package_provide" ]; then return 0 fi # Apply Gentoo-specific tweaks. local option_package option_package=$(option_value 'package') case "$option_package" in ('gentoo'|'egentoo') package_provide=$(gentoo_package_provide_legacy "$package_provide") ;; esac printf '%s' "$package_provide" } gentoo_package_provide_legacy() { local provided_package_id provided_package_id="$1" # Avoid mixups between numbers in package ID and version number. provided_package_id=$(gentoo_package_id "$provided_package_id") # Add the required "!games-playit/" prefix to the package ID. printf '!games-playit/%s' "$provided_package_id" } option_export_legacy() { local option_name option_name="$1" local option_value option_value=$(option_value "$option_name") case "$option_name" in ('checksum') export OPTION_CHECKSUM="$option_value" ;; ('compression') export OPTION_COMPRESSION="$option_value" ;; ('debug') export DEBUG="$option_value" ;; ('free-space-check') case "$option_value" in (0) export NO_FREE_SPACE_CHECK=1 ;; (1) export NO_FREE_SPACE_CHECK=0 ;; esac ;; ('icons') case "$option_value" in (0) export OPTION_ICONS='no' ;; (1) export OPTION_ICONS='yes' ;; esac ;; ('list-packages') export PRINT_LIST_OF_PACKAGES="$option_value" ;; ('list-requirements') export PRINT_REQUIREMENTS="$option_value" ;; ('mtree') export MTREE="$option_value" ;; ('output-dir') export OPTION_OUTPUT_DIR="$option_value" ;; ('overwrite') export OVERWRITE_PACKAGES="$option_value" ;; ('package') export OPTION_PACKAGE="$option_value" ;; ('prefix') export OPTION_PREFIX="$option_value" ;; esac } src/90_compatibility/87_2.21-and-older.sh0000644000000000000000000000041413120060140016605 0ustar rootroot# Keep compatibility with 2.21 and older packages_get_version() { if compatibility_level_is_at_least '2.22'; then warning_deprecated_function 'packages_get_version' 'package_version' fi local PLAYIT_CONTEXT_ARCHIVE set_current_archive "$1" package_version } src/90_compatibility/88_2.20-and-older.sh0000644000000000000000000000142013120060140016603 0ustar rootroot# Keep compatibility with 2.20 and older archives_return_list_legacy() { local variable_name_pattern variable_name_pattern='^ARCHIVE_[0-9A-Z]\+\(_OLD[0-9A-Z]*\)*=' set | \ grep --regexp="$variable_name_pattern" | \ cut --delimiter='=' --fields=1 } current_archive_suffix_legacy() { local archive archive=$(current_archive) if \ compatibility_level_is_at_least '2.13' && \ [ "${archive#ARCHIVE_BASE}" != "$archive" ] then printf '%s' "${archive#ARCHIVE_BASE}" else printf '%s' "${archive#ARCHIVE}" fi } get_context_specific_value() { # WARNING - Context limitation to either archive or package is ignored. if compatibility_level_is_at_least '2.21'; then warning_deprecated_function 'get_context_specific_value' 'context_value' fi context_value "$2" } src/90_compatibility/89_2.19-and-older.sh0000644000000000000000000000062613120060140016623 0ustar rootroot# Keep compatibility with 2.19 and older dosbox_prerun_legacy() { local application application="$1" get_value "${application}_PRERUN" } dosbox_postrun_legacy() { local application application="$1" get_value "${application}_POSTRUN" } package_get_path() { if compatibility_level_is_at_least '2.20'; then warning_deprecated_function 'package_get_path' 'package_path' fi package_path "$1" } src/90_compatibility/90_2.18-and-older.sh0000644000000000000000000000155713120060140016616 0ustar rootroot# Keep compatibility with 2.18 and older content_path_legacy() { context_value "ARCHIVE_${1}_PATH" } content_files_legacy() { local content_files_legacy content_files_legacy=$(context_value "ARCHIVE_${1}_FILES") # 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 } application_libs_legacy() { local application application="$1" local application_libs application_libs=$(context_value "${application}_LIBS") # The deprecation warning should only be shown if the legacy variable is actually used. if \ [ -n "$application_libs" ] && \ compatibility_level_is_at_least '2.19' then warning_deprecated_variable "${application}_LIBS" 'CONTENT_LIBS_xxx_PATH / CONTENT_LIBS_xxx_FILES' fi printf '%s' "$application_libs" } 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.sh0000644000000000000000000001175013120060140016612 0ustar rootroot# Keep compatibility with 2.16 and older icons_get_from_package() { if 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 package=$(current_package) package_path=$(package_path "$package") path_game_data=$(path_game_data) icon_source_directory="${package_path}${path_game_data}" icons_get_from_legacy_path "$icon_source_directory" "$@" } icons_get_from_workdir() { if compatibility_level_is_at_least '2.17'; then warning_deprecated_function 'icons_get_from_workdir' 'icons_inclusion' fi local icon_source_directory icon_source_directory="${PLAYIT_WORKDIR}/gamedata" icons_get_from_legacy_path "$icon_source_directory" "$@" } icons_get_from_legacy_path() { local icon_source_directory_legacy icon_source_directory_legacy="$1" shift 1 # Get the archive inner path, falling back on a default value. local content_path content_path=$(content_path_default) 2>/dev/null || true if [ -z "$content_path" ]; then content_path='.' fi if variable_is_empty 'CONTENT_PATH_DEFAULT'; then local CONTENT_PATH_DEFAULT CONTENT_PATH_DEFAULT='.' fi local application for application in "$@"; do assert_not_empty 'application' 'icons_get_from_path' 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 assert_not_empty 'icon' 'icons_get_from_path' local icon_path icon_full_path_legacy content_path icon_source_directory_new 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_source_directory_new="${PLAYIT_WORKDIR}/gamedata/${content_path}" 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 "$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_DEFAULT" != '.' ]; then rmdir --parents --ignore-fail-on-non-empty "$(dirname "$icon_full_path_new")" fi fi done done } icons_move_to() { if compatibility_level_is_at_least '2.17'; then warning_deprecated_function 'icons_move_to' 'icons_inclusion' fi local option_icons option_icons=$(option_value 'icons') if [ "$option_icons" -eq 0 ]; then return 0 fi local path_icons path_icons=$(path_icons) local source_package source_package_path source_directory source_package=$(current_package) source_package_path=$(package_path "$source_package") source_directory="${source_package_path}${path_icons}" local destination_package destination_package_path destination_directory destination_package="$1" destination_package_path=$(package_path "$destination_package") destination_directory="${destination_package_path}${path_icons}" # a basic `mv` call here would fail if the destination is not empty mkdir --parents "$destination_directory" cp --link --recursive "$source_directory"/* "$destination_directory" rm --recursive "${source_directory:?}"/* rmdir --ignore-fail-on-non-empty --parents "$source_directory" } 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.sh0000644000000000000000000000301613120060140016606 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 message game_name game_name=$(game_name) case "${LANG%_*}" 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.sh0000644000000000000000000001616713120060140013423 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 # 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 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 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 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 # Legacy - Export install paths as global variables. # Pre-2.19 game scripts might rely on the availability of these global variables. # New game scripts should not rely on these, as they are deprecated and will be dropped in some future release. PATH_BIN=$(path_binaries) PATH_DESK=$(path_xdg_desktop) PATH_DOC=$(path_documentation) PATH_GAME=$(path_game_data) PATH_ICON_BASE=$(path_icons) export PATH_BIN PATH_DESK PATH_DOC PATH_GAME PATH_ICON_BASE fi tests/gitlab/gitlab-ci.yml0000644000000000000000000000007013120060140014516 0ustar rootrootcheck: interruptible: true script: - make check 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.sh0000644000000000000000000000166013120060140020702 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 LIBRARY_VERSION='1.42' compatibility_level=$(compatibility_level) assertEquals '1.42' "$compatibility_level" target_version='2.21' compatibility_level=$(compatibility_level) assertEquals '2.21' "$compatibility_level" PLAYIT_COMPATIBILITY_LEVEL='2.22' compatibility_level=$(compatibility_level) assertEquals '2.22' "$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.sh0000644000000000000000000001664713120060140016423 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" } test_set_current_package() { local PLAYIT_CONTEXT_PACKAGE set_current_package 'PKG_MAIN' assertEquals 'PKG_MAIN' "$PLAYIT_CONTEXT_PACKAGE" } 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 # Check that the format ARCHIVE_BASE_xxx is enforced assertFalse 'set_current_archive "ARCHIVE_GOG_OLD0"' # Check that the old format ARCHIVE_xxx is allowed when targeting compatibility level < 2.21 PLAYIT_COMPATIBILITY_LEVEL='2.20' assertTrue 'set_current_archive "ARCHIVE_GOG_OLD0"' unset PLAYIT_CONTEXT_ARCHIVE } test_current_package() { local current_package PLAYIT_COMPATIBILITY_LEVEL PLAYIT_CONTEXT_PACKAGE PACKAGES_LIST PKG set_current_package 'PKG_MAIN' current_package=$(current_package) assertEquals 'PKG_MAIN' "$current_package" unset PLAYIT_CONTEXT_PACKAGE PACKAGES_LIST=' PKG_BIN PKG_DATA' current_package=$(current_package) assertEquals 'PKG_BIN' "$current_package" unset PLAYIT_CONTEXT_PACKAGE PACKAGES_LIST # Set the current package using the legacy global variable PLAYIT_COMPATIBILITY_LEVEL='2.25' PKG='PKG_BIN32' current_package=$(current_package) assertEquals 'PKG_BIN32' "$current_package" unset PLAYIT_COMPATIBILITY_LEVEL PLAYIT_CONTEXT_PACKAGE PKG # Check the priority order between the current context system and the legacy one. PLAYIT_COMPATIBILITY_LEVEL='2.25' PACKAGES_LIST=' PKG_BIN32 PKG_BIN64 PKG_DATA' set_current_package 'PKG_BIN32' PKG='PKG_BIN64' 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_BIN64' "$current_package" unset PLAYIT_COMPATIBILITY_LEVEL PLAYIT_CONTEXT_PACKAGE PACKAGES_LIST PKG # 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_current_archive_suffix() { local current_archive_suffix PLAYIT_CONTEXT_ARCHIVE PLAYIT_COMPATIBILITY_LEVEL ARCHIVE set_current_archive 'ARCHIVE_BASE_MULTIARCH_0' current_archive_suffix=$(current_archive_suffix) assertEquals '_MULTIARCH_0' "$current_archive_suffix" PLAYIT_COMPATIBILITY_LEVEL='2.20' set_current_archive 'ARCHIVE_OLD0' current_archive_suffix=$(current_archive_suffix) assertEquals '_OLD0' "$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_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.sh0000644000000000000000000000157013120060140021261 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'" # Check that the legacy PKG_xxx_PATH variables are set assertTrue "test -n '$PKG_MAIN_PATH'" } 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.sh0000644000000000000000000000421213120060140017774 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" # Check that legacy variables are set when targeting ./play.it ≤ 2.22 PLAYIT_COMPATIBILITY_LEVEL='2.22' option_update 'prefix' '/usr/local' assertEquals '/usr/local' "$OPTION_PREFIX" } 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.sh0000644000000000000000000000510113120060140020675 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='fakeroot 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.sh0000644000000000000000000002125413120060140017414 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 APP_MAIN_EXE \ CONTENT_PATH_DEFAULT fake_binary application_exe_path \ PLAYIT_CONTEXT_PACKAGE PKG_MAIN_ID \ PACKAGES_LIST PKG_BIN32_ID PKG_BIN64_ID PKG_DATA_ID # Create a couple files in the temporary directory, and set required variables. PLAYIT_OPTION_TMPDIR="$TEST_TEMP_DIR" GAME_ID='some-game' APP_MAIN_EXE='SomeGame.exe' set_temp_directories mkdir --parents \ "${PLAYIT_WORKDIR}/gamedata" \ "${PLAYIT_WORKDIR}/packages/some-game-main_1.0-1+19700101.1_all/usr/share/games/some-game" \ "${PLAYIT_WORKDIR}/packages/some-game-bin64_1.0-1+19700101.1_all/usr/share/games/some-game" touch \ "${PLAYIT_WORKDIR}/gamedata/SomeGame.exe" \ "${PLAYIT_WORKDIR}/packages/some-game-main_1.0-1+19700101.1_all/usr/share/games/some-game/SomeGame.exe" \ "${PLAYIT_WORKDIR}/packages/some-game-bin64_1.0-1+19700101.1_all/usr/share/games/some-game/SomeGame.exe" # Look in archive contents CONTENT_PATH_DEFAULT='.' application_exe_path=$(application_exe_path "$APP_MAIN_EXE") assertEquals "${PLAYIT_WORKDIR}/gamedata/./SomeGame.exe" "$application_exe_path" unset CONTENT_PATH_DEFAULT # Look in the current package set_current_package 'PKG_MAIN' PKG_MAIN_ID='some-game-main' application_exe_path=$(application_exe_path "$APP_MAIN_EXE") assertEquals "${PLAYIT_WORKDIR}/packages/some-game-main_1.0-1+19700101.1_all/usr/share/games/some-game/SomeGame.exe" "$application_exe_path" unset PKG PKG_MAIN_ID # Look in all packages 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' application_exe_path=$(application_exe_path "$APP_MAIN_EXE") assertEquals "${PLAYIT_WORKDIR}/packages/some-game-bin64_1.0-1+19700101.1_all/usr/share/games/some-game/SomeGame.exe" "$application_exe_path" unset PACKAGES_LIST PKG_BIN32_ID PKG_BIN64_ID PKG_DATA_ID } 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" # For DOSBox games targeting ./play.it ≤ 2.19, APP_xxx_PRERUN should be ignored here PLAYIT_COMPATIBILITY_LEVEL='2.19' APP_MAIN_TYPE='dosbox' application_prerun=$(application_prerun 'APP_MAIN') assertNull "$application_prerun" unset PLAYIT_COMPATIBILITY_LEVEL APP_MAIN_TYPE # 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" # For DOSBox games targeting ./play.it ≤ 2.19, APP_xxx_POSTRUN should be ignored here PLAYIT_COMPATIBILITY_LEVEL='2.19' APP_MAIN_TYPE='dosbox' application_postrun=$(application_postrun 'APP_MAIN') assertNull "$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.sh0000644000000000000000000000717613120060140017236 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 \ archives_list_expected archives_list \ ARCHIVES_LIST \ ARCHIVE_BASE_0 ARCHIVE_BASE_1 \ PLAYIT_COMPATIBILITY_LEVEL ARCHIVE_MAIN ARCHIVE_MAIN_OLD ARCHIVES_LIST='ARCHIVE_BASE_0 ARCHIVE_BASE_1' archives_list=$(archives_return_list) assertEquals 'ARCHIVE_BASE_0 ARCHIVE_BASE_1' "$archives_list" unset ARCHIVES_LIST ARCHIVE_BASE_0='some_game_archive.tar.gz' ARCHIVE_BASE_1='some_other_game_archive.tar.gz' archives_list_expected='ARCHIVE_BASE_0 ARCHIVE_BASE_1' archives_list=$(archives_return_list) assertEquals "$archives_list_expected" "$archives_list" unset ARCHIVE_BASE_0 ARCHIVE_BASE_1 # Check that archive names using the old format are detected for game scripts targeting ./play.it ≤ 2.20 PLAYIT_COMPATIBILITY_LEVEL='2.20' ARCHIVE_MAIN='some_game_archive.tar.gz' ARCHIVE_MAIN_OLD='some_other_game_archive.tar.gz' archives_list_expected='ARCHIVE_MAIN ARCHIVE_MAIN_OLD' archives_list=$(archives_return_list) assertEquals "$archives_list_expected" "$archives_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.sh0000644000000000000000000000747013120060140016404 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 \ CONTENT_PATH_DEFAULT \ CONTENT_GAME_DATA_PATH \ PLAYIT_CONTEXT_ARCHIVE CONTENT_GAME_DATA_PATH_0 \ PLAYIT_COMPATIBILITY_LEVEL \ PLAYIT_CONTEXT_ARCHIVE ARCHIVE_GAME_DATA_PATH_MAIN CONTENT_PATH_DEFAULT='default/path' content_path=$(content_path 'GAME_DATA') assertEquals 'default/path' "$content_path" CONTENT_GAME_DATA_PATH='specific/path' content_path=$(content_path 'GAME_DATA') assertEquals 'specific/path' "$content_path" set_current_archive 'ARCHIVE_BASE_0' CONTENT_GAME_DATA_PATH_0='more/specific/path' content_path=$(content_path 'GAME_DATA') assertEquals 'more/specific/path' "$content_path" unset CONTENT_PATH_DEFAULT CONTENT_GAME_DATA_PATH ARCHIVE CONTENT_GAME_DATA_PATH_0 # Test the behaviour of game scripts targeting ./play.it < 2.19 PLAYIT_COMPATIBILITY_LEVEL='2.18' CONTENT_PATH_DEFAULT='default/path' content_path=$(content_path 'GAME_DATA') assertEquals 'default/path' "$content_path" ARCHIVE_GAME_DATA_PATH='old/path' content_path=$(content_path 'GAME_DATA') assertEquals 'old/path' "$content_path" ## Old archive naming scheme must be used here, ## because we target ./play.it < 2.21 set_current_archive 'ARCHIVE_MAIN' ARCHIVE_GAME_DATA_PATH_MAIN='other/old/path' content_path=$(content_path 'GAME_DATA') assertEquals 'other/old/path' "$content_path" } test_content_files() { local \ content_files content_files_expected \ CONTENT_GAME_DATA_FILES CONTENT_GAME_DATA_FILES_0 \ PLAYIT_CONTEXT_ARCHIVE PLAYIT_COMPATIBILITY_LEVEL \ ARCHIVE_GAME_DATA_FILES ARCHIVE_GAME_DATA_FILES_MAIN content_files=$(content_files 'GAME_DATA') assertNull "$content_files" CONTENT_GAME_DATA_FILES='some list of files' content_files=$(content_files 'GAME_DATA') content_files_expected='some list of files' assertEquals "$content_files_expected" "$content_files" set_current_archive 'ARCHIVE_BASE_0' CONTENT_GAME_DATA_FILES_0='specific list of files' content_files=$(content_files 'GAME_DATA') content_files_expected='specific list of files' assertEquals "$content_files_expected" "$content_files" unset PLAYIT_CONTEXT_ARCHIVE # Test the behaviour of game scripts targeting ./play.it < 2.19 unset CONTENT_GAME_DATA_FILES PLAYIT_COMPATIBILITY_LEVEL='2.18' content_files=$(content_files 'GAME_DATA') assertNull "$content_files" ARCHIVE_GAME_DATA_FILES='old list of files' content_files=$(content_files 'GAME_DATA') content_files_expected='old list of files' assertEquals "$content_files_expected" "$content_files" ## Old archive naming scheme must be used here, ## because we target ./play.it < 2.21 set_current_archive 'ARCHIVE_MAIN' ARCHIVE_GAME_DATA_FILES_MAIN='other old list of files' content_files=$(content_files 'GAME_DATA') content_files_expected='other old list of files' assertEquals "$content_files_expected" "$content_files" # Fallback lists of files for Unity3D and Unreal Engine 4 games are not tested here. } tests/shunit2/30_content/10_files-inclusion.sh0000644000000000000000000001042613120060140020213 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" | LANG=C 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" | LANG=C 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" } test_content_inclusion_include_file() { local paths_included_expected paths_included paths_excluded_expected paths_excluded # 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" ( cd "${TEST_TEMP_DIR}/gamedata" content_inclusion_include_file 'directory-to-include' "${TEST_TEMP_DIR}/packages/destination-package" content_inclusion_include_file 'file-to-include' "${TEST_TEMP_DIR}/packages/destination-package" ) paths_included=$(find "${TEST_TEMP_DIR}/packages/destination-package" | LANG=C 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" | LANG=C 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" } test_content_inclusion_include_pattern() { local paths_included_expected paths_included paths_excluded_expected paths_excluded # 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" \ "${TEST_TEMP_DIR}/gamedata/directory-to-exclude/file-to-include" \ "${TEST_TEMP_DIR}/gamedata/directory-to-exclude/file-to-exclude" ( cd "${TEST_TEMP_DIR}/gamedata" content_inclusion_include_pattern '*-to-include' "${TEST_TEMP_DIR}/packages/destination-package" ) paths_included=$(find "${TEST_TEMP_DIR}/packages/destination-package" | LANG=C sort) paths_included_expected="${TEST_TEMP_DIR}/packages/destination-package ${TEST_TEMP_DIR}/packages/destination-package/directory-to-exclude ${TEST_TEMP_DIR}/packages/destination-package/directory-to-exclude/file-to-include ${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" | LANG=C sort) paths_excluded_expected="${TEST_TEMP_DIR}/gamedata ${TEST_TEMP_DIR}/gamedata/directory-to-exclude ${TEST_TEMP_DIR}/gamedata/directory-to-exclude/file-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.sh0000644000000000000000000000742013120060140016040 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" APP_MAIN_ICON_ID='42' wrestool_options=$(icon_wrestool_options 'APP_MAIN_ICON') assertEquals '--type=14 --name=42' "$wrestool_options" } tests/shunit2/30_icons/10_conversion.sh0000644000000000000000000000071713120060140016740 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } 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/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='./drivers ./badges ./banners ./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/*.sav ./userdata/*.png' 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.sh0000644000000000000000000000501013120060140016474 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } 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/60_system_archlinux/10_packages.sh0000644000000000000000000000524313120060140020621 0ustar rootroot#!/bin/sh oneTimeSetUp() { # Load the ./play.it library export LIB_ONLY=1 . lib/libplayit2.sh } test_archlinux_metadata_file_install() { local \ metadata_file_install metadata_file_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'" metadata_file_install=$(archlinux_metadata_file_install 'PKG_MAIN') metadata_file_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 "$metadata_file_install_expected" "$metadata_file_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)' metadata_file_install=$(archlinux_metadata_file_install 'PKG_MAIN') metadata_file_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 "$metadata_file_install_expected" "$metadata_file_install" } 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_debian/10_packages.sh0000644000000000000000000000552213120060140020046 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" } 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_gentoo/10_packages_gentoo-variant.sh0000644000000000000000000000122113120060140023124 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" } tests/shunit2/70_deprecation/00_warnings-list.sh0000644000000000000000000000276213120060140020543 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_deprecation_warnings_shown_file() { local warnings_file PLAYIT_WORKDIR warnings_file=$(deprecation_warnings_shown_file) assertNull "$warnings_file" PLAYIT_WORKDIR="$TEST_TEMP_DIR" warnings_file=$(deprecation_warnings_shown_file) assertEquals "${PLAYIT_WORKDIR}/warnings-shown/deprecation" "$warnings_file" } test_deprecation_warnings_shown_add() { local warnings_list warnings_list_expected warnings_file PLAYIT_WORKDIR PLAYIT_WORKDIR="$TEST_TEMP_DIR" warnings_file=$(deprecation_warnings_shown_file) deprecation_warnings_shown_add 'warning_test1 arg1 arg2' warnings_list=$(cat "$warnings_file") assertEquals 'warning_test1 arg1 arg2' "$warnings_list" deprecation_warnings_shown_add 'warning_test2 arg1 arg2' warnings_list=$(cat "$warnings_file") warnings_list_expected='warning_test1 arg1 arg2 warning_test2 arg1 arg2' assertEquals "$warnings_list_expected" "$warnings_list" } test_deprecation_warning_has_been_shown_already() { local PLAYIT_WORKDIR PLAYIT_WORKDIR="$TEST_TEMP_DIR" assertFalse 'deprecation_warning_has_been_shown_already "warning_test arg1 arg2"' deprecation_warnings_shown_add 'warning_test arg1 arg2' assertTrue 'deprecation_warning_has_been_shown_already "warning_test arg1 arg2"' }