CHANGELOG0000644000000000000000000032621713120060140010764 0ustar rootroot2.34.1 * Fix ./play.it uninstallation if shell completions were not installed. * Improve zsh completion. * DOSBox: Fix deprecation warnings related to disk images. * DOSBox: Fix fetching image type from legacy variable. * Debian: Fix dependencies on Mono libraries. * Web games: Fix initial paths setting. * Web games: Prevent Firefox extensions from loading in game windows. * Web games: Drop calls to retired functions: - application_exe_escaped - launcher_prefix_symlinks_functions 2.34.0 * Deprecation notices: * --package egentoo is no longer supported. See "New .gpkg.tar package format for Gentoo" for information about the supported --package gentoo option. * --package gentoo + --compression auto can no longer be used together. The new default for --package gentoo is --compression none. * The legacy dependency system relying on PKG_xxx_DEPS variables can now only be used to set dependencies on sibling packages. The following dependencies variables should be used instead of PKG_xxx_DEPS: - PKG_xxx_DEPENDENCIES_LIBRARIES, to list native libraries (./play.it ≥ 2.18) - PKG_xxx_DEPENDENCIES_MONO_LIBRARIES, to list Mono libraries (./play.it ≥ 2.21) - PKG_xxx_DEPENDENCIES_GSTREAMER_PLUGINS, to list GStreamer decoders (./play.it ≥ 2.23) - PKG_xxx_DEPENDENCIES_COMMANDS, to list commands (./play.it ≥ 2.25) - PKG_xxx_DEPENDENCIES_SIBLINGS, to list sibling packages (./play.it ≥ 2.30) * Support for legacy APP_REGEDIT variable is dropped. WINE_REGISTRY_INIT, introduced with ./play.it 2.33, should be used instead. * New .gpkg.tar package format for Gentoo: * Generation of packages using ebuild is replaced with .gpkg.tar packages, relying only on coreutils, tar and compressors. * Packages for Gentoo can now be generated from systems not providing ebuild. * Improved support for Web games: * Web games are no longer run using the shipped Google Chrome build, but with a local Web server (using the server built into Python) and Firefox to provide the graphical interface. * Improved support for Unreal Engine 3 games: * Inputs related problems are avoided: - ignored keyboard inputs - broken mouse camera control * Improved support for GameMaker games: * A crash on Mesa is avoided. * Broken support for non-US locales is worked around. * The following variables can be omitted: - APPLICATIONS_LIST - APP_xxx_EXE - APP_xxx_ICON - CONTENT_GAME_BIN_FILES - CONTENT_GAME_DATA_FILES - PACKAGES_LIST - PKG_BIN_DEPENDENCIES_SIBLINGS - PKG_BIN_DEPENDENCIES_LIBRARIES - PKG_DATA_ID - PKG_DATA_DESCRIPTION * Archives with multiple valid MD5 hashes: * ARCHIVE_xxx_MD5 is expanded to support multiple values. Multiple valid values should be separated by line breaks, empty lines are ignored. Here is an example usage: ARCHIVE_BASE_2_NAME='setup_jazz_jackrabbit_2_1.24hf_(16886).exe' ARCHIVE_BASE_2_MD5=' 25a730c0813eb006555e6bbaf9613487 45be80bad040ea821bc6096abe6f3196 48a48258ed60b24068cbbb2f110b049b' * Improvements to menu entries for desktop environments: * Support is added for SVG icons. * The value set through APP_xxx_CAT is checked against a set list of valid values, and a warning is triggered if an unknown value has been set. * Improvements to WINE registry support: * WINE_REGISTRY_INIT now has support for contextual values (package-specific or archive-specific). * Only the key name is used when generating a regedit dump. This should avoid problems with dump paths going over the length limit for regedit. Old dumps are not automatically migrated to the new path. This can be done manually by running the following commands: DUMPS_PATH=~/.local/share/games/${game_name}/wine/regedit/ find $DUMPS_PATH -name \*.reg -exec mv --target-directory=$DUMPS_PATH {} + find $DUMPS_PATH -type d -empty -delete * Rework of package dependencies listing: * This update reduces the difference between distinct dependencies listing systems (commands, GStreamer decoders, Mono libraries, native libraries, sibling packages) as well as between distinct target package formats (Arch Linux, Debian, Gentoo). 2.33.4 * Fix package context setting from several functions: - icons_inclusion_single_application - content_inclusion * Debian: Fix dependency on libQt5Core.so.5 / libQt5Gui.so.5. 2.33.3 * Drop obsolete function application_exe_escaped. * Reduce code duplication when fetching the type of the default application. * Remove shebang from fish completion file. * Fix config file path in manpage and zsh-completion. * Add --list-packages option to zsh completion. * Add support for multiple Qt 5 native libraries: - libQt5Core.so.5 - libQt5Gui.so.5 - libQt5Widgets.so.5 2.33.2 * Include shell completion files in the release tarball. * Improve current system automatic identification. * Only try to identify the current system if a default package format is not already set. * Visionaire games: Add explicit dependency on SDL2. * AppImage archives: Add missing early requirements check. * Arch Linux: Fix generation of WINE prefixes. * Debian: Prevent a leak of forced gzip compression when going over .deb size limit. * Debian: Display an explicit error when dpkg-deb version can not be found. 2.33.1 * Prevent default values for LD_PRELOAD_SOURCE from overriding maintainer-set ones. * Add support for completion with some interactive shells: - fish - zsh * DOSBox games: Fix detection of MS-DOS binaries through file/libmagic. * UNity3D games: Improve error message when no binary is found. * WINE games: Fix inclusion of the winetricks wrapper when using a non-default Direct3D renderer. * WINE games: Prevent the creation of an empty registry-scripts directory. 2.33.0 * Deprecation notices: * --package egentoo setting is deprecated, --package gentoo should be used instead. * Support for SCRIPT_DEPS and check_deps is dropped, REQUIREMENTS_LIST and requirements_check, introduced with ./play.it 2.31, must be used instead. * Support for ARCHIVE global variable is dropped, set_current_archive, introduced with ./play.it 2.27, must be used instead. * The following variables are no longer exposed from generated launchers: - APP_EXE - SCUMMVM_ID * The following native libraries are no longer provided by current distributions, and can no longer be set as package dependencies: - libboost_locale.so.1.74.0 - libminiupnpc.so.17 * The previous system for LD_PRELOAD shim support is deprecated, the following variables should no longer be used from game scripts: - PRELOAD_HACKS_LIST - HACK_xxx_NAME - HACK_xxx_DESCRIPTION - HACK_xxx_PACKAGE - HACK_xxx_SOURCE See "New system for LD_PRELOAD hacks" below for details. * The following variables used to set a disk image loaded by DOSBox are deprecated: - GAME_IMAGE - GAME_IMAGE_TYPE See "Reworked DOSBox disk images support" below for details. * The following functions are deprecated: - prefix_generate_links_farm - launcher_prefix_symlinks_build See "Launchers clean-up" below for details. * New system for LD_PRELOAD hacks: * A new single variable LD_PRELOAD_SOURCE is exposed to game scripts, to set a C snippet that should be built into a .so loaded through LD_PRELOAD. * Reworked DOSBox disk images support: * The path to a DOSBox disk image should be set with DOSBOX_DISK_IMAGE_PATH, a path relative to the game root. * The type of a DOSBox disk image should be set with DOSBOX_DISK_IMAGE_TYPE, supported types are "iso" and "cdrom". If omitted it defaults to "iso". * Launchers clean-up: * The launcher snippet creating a symlinks farm prefix is now printed using a single function: prefix_symlinks_generate This function replaces both previous functions prefix_generate_links_farm and launcher_prefix_symlinks_build. * Improved support for Adventure Game Studio games: * An Adventure Game Studio game is identified by the AGS_NAME variable being set. It should be set to the ScummVM identifier, excluding the engine prefix. * The following variables can be omitted from game scripts for Adventure Game Studio games: - APP_MAIN_SCUMMID - CONTENT_GAME_MAIN_FILES * Archive selection improvements: * Ensure that archive_initialize_optional checks all candidates if required, instead of stopping at the first one with the expected name. * Initialising an archive through archive_initialize_required / archive_initialize_optional sets the version string on the new archive. * Expanded context support: * Support for contextual values (archive-specific or package-specific) is added for the following variables: - APP_xxx_DOSBOX_PRERUN - APP_xxx_DOSBOX_POSTRUN - REQUIREMENTS_LIST - WINE_WINEPREFIX_TWEAKS * Package properties fallback values: * Fallback values are supported for some package properties: - PKG_xxx_ID - PKG_xxx_DESCRIPTION - PKG_xxx_PROVIDES - PKG_xxx_DEPENDENCIES_SIBLINGS - PKG_xxx_DEPENDENCIES_LIBRARIES * If a value is not set for one of the following common package identifiers, a fallback value can used instead if it is set: current package → fallback package PKG_BIN64 / PKG_BIN32 → PKG_BIN PKG_LIBS64 / PKG_LIBS32 → PKG_LIBS PKG_L10N_xxx → PKG_L10N * Improvements specific to WINE games: * Improve the ability to set custom WINEPREFIX actions from game scripts, by relying on a new function exposed to game scripts: wineprefix_init_custom By default this function does nothing, but if overridden from a game script its output is included in the list of actions to run on WINEPREFIX initialisation. * A variable WINE_REGISTRY_INIT is exposed to game scripts, allowing to list registry keys that should be set on WINE prefix initialisation. 2.32.8 * Fix the ability to require multiple ./play.it-provided old libraries. * Fix help message for --package option. * Add support for extra native libraries: - libSDL2_net-2.0.so.0 * Add support for extra GStreamer decoders: - audio/x-wma - video/x-wmv 2.32.7 * Restore late icons requirements check. 2.32.6 * Allow early icons requirement check to fail for Unity3D games. 2.32.5 * Fix support for package-specific APP_xxx_SCUMMID during launcher generation checks. * Fix early detection of icons for WINE and Mono games. * Drop late icons requirements check. * Prevent some ignored errors from being shown if /bin/sh = bash. * New package dependencies on native commands: - pidwait * New package dependencies on native libraries: - alleg-alsadigi.so - alleg-alsamidi.so - libaldmb.so.1 - liballeg.so.4.4 - libdumb.so.1 2.32.4 * Fix the free space check against the working directory. 2.32.3 * Fix warnings for features deprecated with ./play.it 2.31. * Drop a call to deprecated function check_deps. * Improve the clean up of Unity3D plugins directory. * Add the ability to set a dependency on julius, the free engine for Caesar 3. * Add the ability to set dependencies on extra native libraries: - libcrypto.so.3 - libssl.so.3 2.32.2 * Do not override SDL_DYNAMIC_API if already set by the environment. * Fix the error message on missing files from multi-parts installers. * Fix application type detection with file ≥ 5.46. * Fix calls to persistent_path_diversion from launchers when the destination does not exist yet. * Fix the ability to use the --list-requirements switch. * When listing supported games, prevent a leak of the compatibility level. 2.32.1 * Visionaire games - Add a missing dependency on SDL 2. * Linux native Unity3D games - Add an implicit dependency on SDL2. * Ren'Py games - Fix the generation of launchers. * Update huge files handling to no longer rely on inherited variables. * Gentoo (egentoo variant) - Hardcode some package metadata instead of relying on a broken function. 2.32.0 * Deprecation notices: * The debug system is fully dropped: the library can no longer be built in "debug mode" and the --debug option is no longer supported. * Support for the obsolete compatibility wrapper extract_data_from is dropped. In most cases game scripts should use archive_extraction_default, introduced with ./play.it 2.25. In some specific cases archive_extraction should be used instead, introduced with ./play.it 2.16. * Support for the obsolete compatibility wrapper icons_get_from_package is dropped. Game scripts should use content_inclusion_icons instead, introduced with ./play.it 2.28. * Support for the obsolete compatibility wrapper prepare_package_layout is dropped. Game scripts should use content_inclusion_default instead, introduced with ./play.it 2.18. * The variables ARCHIVE_xxx_PATH and ARCHIVE_xxx_FILES can no longer be used to list archive contents. Game scripts should use the CONTENT_xxx_PATH (or CONTENT_PATH_DEFAULT) and CONTENT_xxx_FILES variables introduced with ./play.it 2.18. * The variable APP_WINE_LINK_DIRS can no longer be used to divert paths from the WINE prefix to the game prefix. Game scripts should use the WINE_PERSISTENT_DIRECTORIES variable instead, introduced with ./play.it 2.23. * Support for the obsolete APP_WINETRICKS variable is dropped. The variable WINE_WINETRICKS_VERBS, introduced with ./play.it 2.25, should be used instead. * Improvements to MD5 hashes caching: * Prior to this update, the MD5 hash computation of an archive could happen multiple times, if this archive name was shared between multiple supported archives and a MD5 hash was required to identify the correct one. * As a side effect of PLAYIT_WORKDIR being set earlier in the process, it no longer always include the game identifier in its path. * Users can once again set a custom working directory path by setting PLAYIT_WORKDIR in their environment. Beware that running parallel ./play.it calls with a same value for PLAYIT_WORKDIR will inevitably lead to failures. * Predictable ordering for archives list: * The order of archive identifiers returned by archives_list is now predictable, with the most recent archives at the top. * New default paths for game scripts collections: * The new default paths are, in priority order: - ~/.local/share/play.it/collections - /usr/local/share/games/play.it/collections - /usr/local/share/play.it/collections - /usr/share/games/play.it/collections - /usr/share/play.it/collections * The legacy paths are still supported, with a lower priority: - ~/.local/share/play.it/games - /usr/local/share/games/play.it/games - /usr/local/share/play.it/games - /usr/share/games/play.it/games - /usr/share/play.it/games * Graphical terminal spawning from launchers: * The function "terminal_wrapper" is made available in a launcher by: 1. Including the output of launcher_wrapper_terminal in the launcher 2. Add a dependency on the command "terminal_wrapper" * On Debian it uses x-terminal-emulator by default, and falls back to xterm. * On Arch Linux and Gentoo it uses xterm. * Improved support for Debian-provided DXVK: * The code handling DXVK installation in WINE prefixes is updated to work with the recent dxvk 2.5.1-1 update that reached Debian unstable. * If the Debian-provided dxvk package is available, winetricks is no longer a requirement. If this package is not installed winetricks is still used as a fallback. * Improvements to winetricks handling: * On Debian, x-terminal-emulator is used instead of xterm when winetricks needs to spawn in a terminal. * Expanded context support for GAME_ID: * The package specific value of GAME_ID is used if provided when fetching it through one of the following functions: - path_documentation - path_game_data - path_fonts_ttf - path_libraries * Ren'Py write access to the game directory: * Ren'Py games can be run from a symlinks farm prefix instead of the read-only system path, allowing them to write files in the game directory. * Case-insensitive archives content: * The paths listed in CONTENT_xxx_FILES variables are now case-insensitive. * The paths listed in CONTENT_xxx_PATH are still case-sensitive. 2.31.1 * Work around a loss of input on loss of focus with Unity3D games run through WINE. * Display the message about icons inclusion only once. * Failure to guess an application type from its MIME information due to CONTENT_PATH_DEFAULT being unset should now trigger an error explicit about the missing variable. * Failure to guess an application type from its MIME information due to APP_xxx_EXE being set to a wrong value should now trigger an error explicit about the wrong path. * Drop the reliance on a deprecated dependencies system when handling huge files that need to be split. * Fix the error thrown when a required archive is missing. 2.31.0 * Deprecation notices: * The variable GAME_ID is no longer set in generated launcher scripts. * The ability to set a dependency against liballeg.so.4.4 is dropped. * For game scripts targeting a compatibility level ≥ 2.31, sourcing the library no longer automatically run some actions. See the "Improved library sourcing" section below for more details. * Deleting the temporary path $PLAYIT_WORKDIR should be done using the new dedicated function: working_directory_cleanup See the "Working directory clean up" section below for more details. * SCRIPT_DEPS should no longer be used to set the list of commands required by the game script. See the "Requirements check rework" section below for more details. * check_deps should no longer be used to check for the presence of required commands. See the "Requirements check rework" section below for more details. * The following runtime variables are no longer supported: - PLAYIT_PERSISTENT_USER_PATH - PLAYIT_PREFIX_PATH - PLAYIT_FAKE_HOME_PATH See the "Custom paths in launchers" section below for more details. * The following variables used to list persistent paths are no longer supported: - CONFIG_DIRS - CONFIG_FILES - DATA_DIRS - DATA_FILES The following variables (introduced with ./play.it 2.17) must be used instead: - USER_PERSISTENT_DIRECTORIES - USER_PERSISTENT_FILES * Improved library sourcing: * The initialization process must now be explicitly started by calling the new dedicated function: initialization_default No action is run implicitly on library sourcing, unless a game script is targeting a compatibility level ≤ 2.30. Disabling the actions when a compatibility level ≤ 2.30 is targeted can still be done by setting the environment variable $LIB_ONLY to a non-null value. * Most steps of the initialization process can be tweaked from game scripts, by overriding the following functions: - init_fail_as_root - init_shell_options - init_games_list - init_environment - init_compatibility_level_check - init_options - init_early_actions - init_options_validity_check - init_archive - init_package - init_working_directory - init_noop_actions - init_packages_already_built - init_requirements_check - init_extra_archives_required - init_extra_archives_optional - init_archives_integrity_check * Working directory clean up: * A new dedicated function is provided for deleting the temporary working directory: working_directory_cleanup It should be used instead of a direct call to rm. * Support for partial context: * context_value should not fail even if the current archive, the current package or the default package are not set. * Requirements check rework: * A new variable is available to list the commands required by the current game script: REQUIREMENTS_LIST. It should be used instead of SCRIPT_DEPS in game scripts targeting a compatibility level ≥ 2.31. Unlike SCRIPT_DEPS, it uses line breaks as a separator. This legacy requirements list declaration: SCRIPT_DEPS="${SCRIPT_DEPS:-} ffmpeg mplex" should be replaced with: REQUIREMENTS_LIST="${REQUIREMENTS_LIST:-} ffmpeg mplex" * A new command is provided to check for the presence of required commands: requirements_check A compatibility wrapper for game scripts relying on check_deps is provided, but it will trigger a deprecation warning when targeting a compatibility level ≥ 2.31. * Support for multiple archives with identical name: * The name of an archive is no longer assumed to be unique between all supported games collections. * If multiple archives share a same name and are supported by distinct scripts, an extra identification step relying on the archive MD5 hash is run. * Support for AppImage archives: * Extracting game data from AppImage is now supported, relying on binwalk and unsquashfs. * Custom paths in launchers: * The paths are set early in the generated launchers, using overrideable functions. * The following paths are included: - PATH_GAME_DATA (the path to the root of the read-only game data) - PATH_PERSISTENT (the path to the persistent user data) - PATH_PREFIX (the path to the volatile game prefix) - PATH_FAKE_HOME (the path to the volatile fake $HOME) - PATH_LIBRARIES_SYSTEM (the path to libraries provided by the package) - PATH_LIBRARIES_USER (the path to libraries provided by the user) - PATH_WINE_REGISTRY (the path to persistent WINE registry dumps) - PATH_WINEPREFIX (the path to the WINE prefix) * The following functions can be overriden from game scripts to use non-default paths: - launcher_path_persistent - launcher_path_prefix - launcher_path_fake_home - launcher_paths_libraries - java_init_paths - mono_init_paths - native_init_paths - wine_init_paths * Some paths can be overriden at runtime by exporting the following variables: - PLAYIT_PATH_PERSISTENT (the path to persistent user data) - PLAYIT_PATH_PREFIX (the path to the volatile game prefix) - PLAYIT_PATH_FAKE_HOME (the path to the fake $HOME) - PLAYIT_PATH_LIBRARIES_USER (the path to user-provided native libraries) - WINEPREFIX (the path to the WINE prefix) * Custom WINE prefix actions: * Game scripts can override the actions run during the WINE prefix initial generation by overriding the following function: wine_wineprefix_init_actions. * Linking the game prefix directory into the WINE prefix is included in the default actions, so that step can be skipped by overriding wine_wineprefix_init_actions. * Extra option to set archive inner paths: * Game script have the ability to set an archive inner path relative to the base path, using the new variable CONTENT_xxx_RELATIVE_PATH. * Collection path: * A new option is provided, limiting the search path for game scripts: --collection-path Usage: --collection-path path When a path is provided through this option, game scripts outside of this path are ignored. 2.30.1 * Fix the detection of the dpkg-deb version in environments where LC_ALL is set. * Fix the handling of dependencies on siblings packages for Arch Linux. * Display a warning when $target_version is used from a game script targeting ./play.it ≥ 2.26. * Improve the error shown during launchers generation if a binary is missing. * Throw an explicit error if the obsolete archives naming scheme is used. * Throw an explicit error when failing to fetch the header length from a Makeself installer. * Throw an explicit error when trying to query the system libraries path for an unsupported architecture. * Add the ability to set dependencies on more commands: - 7za - godot3-runner * Add the ability to set dependencies on more native libraries: - libatk-bridge-2.0.so.0 - libdrm.so.2 - libgbm.so.1 - libxkbcommon.so.0 2.30.0 * Deprecation notices: * Game scripts targeting a compatibility level ≥ 2.30 should not set dependencies using "PKG_xxx_DEPS". * Support for the legacy archive naming convention "ARCHIVE_xxx" is dropped. All game scripts must now use the naming convention introduced with ./play.it 2.13 for the main archives: "ARCHIVE_BASE_xxx". * Support for the following obsolete compatibility wrappers is dropped: - context_archive - icons_get_from_workdir - icons_move_to - launcher_desktop_exec - launcher_write * Support for the following obsolete variable is dropped: - PKG_xxx_PROVIDE * Support for setting distribution-specific dependencies using the following variables is dropped: - PKG_xxx_DEPS_ARCH - PKG_xxx_DEPS_DEB - PKG_xxx_DEPS_GENTOO The "PKG_DEPENDENCIES_xxx" variables should be used instead. * Increased verbosity: * Each of these functions now displays a message when it starts its actions: - content_inclusion_icons - content_inclusion_default / content_inclusion - launchers_generation - packages_generation * Performance improvements: * A default package is set early, to be used by the context system when no current package is explicitly set. * Improvements to files inclusion: * The functions called from "content_inclusion" have been reworked to reduce the number of calls to external commands (find, cp, rm). A slight improvement of the time spent on files inclusion can be noticed. * Support for the following legacy variables is restored: - ARCHIVE_GAME_xxx_FILES - ARCHIVE_DOC_xxx_FILES Setting any of these in a game script targeting a compatibility level ≥ 2.18 now triggers a deprecation warning, instead of being silently ignored (that could cause empty packages to be generated). * Improvements to launchers generation: * All checks that should be run prior to a launcher generation are now run early, before taking any real action, and only once. * New dependency system: sibling packages: * A "sibling" package is a package that is built from the current game script, in contrast to packages available from the distribution repositories. This old style dependencies list: PKG_BIN_DEPS="$PKG_L10N_ID $PKG_DATA_ID" should be converted to: PKG_BIN_DEPENDENCIES_SIBLINGS=' PKG_L10N PKG_DATA' 2.29.2 * Ensure that the commands required for icons extraction are available. 2.29.1 * Throw an error when trying to include an icon for an application with no icon set. * Prevent packages_generation from running twice, when called with no argument. * Unity3D games: Improve the automatic detection of the game binary. * Mono games: Prevent a failure on unknown Mono libraries set as dependencies. * Mono games: Add support for setting dependencies on extra Mono libraries: - System.Configuration.Install.dll 2.29.0 * Deprecation notices: * The obsolete function package_architecture_string has been dropped. * Support for the legacy PKG_xxx_PATH variables is dropped. Game scripts should rely on the package_path function instead. * The special behaviour of APP_xxx_PRERUN / APP_xxx_POSTRUN for DOSBox is dropped. The actions listed in these variables are now always executed before/after calling DOSBox. * The following variables are no longer exported: - PATH_BIN - PATH_DESK - PATH_DOC - PATH_GAME - PATH_ICON_BASE The following functions should be used instead: - path_binaries - path_xdg_desktop - path_documentation - path_game_data - path_icons * The following compatibility wrappers are no longer in use by game scripts, and have been removed: - archive_get_type - context_archive_suffix - context_package - packages_get_list * Codebase improvements: * APPLICATIONS_LIST can now be omitted if APP_xxx_TYPE is set. * hacks_inclusion_default no longer relies on the package context. It loops over the full list of packages, then build and include the hacks for all relevant packages. * launcher_write_desktop is updated to no longer rely on the package context. The launcher_write_desktop function now expects two arguments: USAGE: launcher_write_desktop $package $application * Some tar decompression options are explicitly set based on the archive type. This prevents tar from failing to extract the content of archives using a non-standard extension. The following archive types are included: - "tar.bz2" → add "--bunzip2" - "tar.gz" → add "--gzip" - "tar.xz" → add "--xz" * A new function is provided to clean-up snippets: snippet_clean. The following operation is done on the function input: - convert the series of 4 spaces to tabulations. * A new function is provided to clean-up lists: list_clean. The following operations are done on the function input: - remove leading and trailing spaces (including tabs); - sort the list and merge duplicate entries; - remove empty lines. * Optional icons archives: * An optional archive providing icons for the current game can be included in a game script by setting the following variables: - ARCHIVE_OPTIONAL_ICONS_NAME - ARCHIVE_OPTIONAL_ICONS_MD5 - ARCHIVE_OPTIONAL_ICONS_URL - CONTENT_ICONS_PATH - CONTENT_ICONS_FILES The inclusion is done from the archive, if provided, when the icons inclusion function is called: content_inclusion_icons. If the archive is not provided, the inclusion is done from the icon shipped in the game installer (if such an icon is available). * Better integration of shipped fonts: * A new function is provided to print the install path for TrueType fonts: path_fonts_ttf. * A list of TTF font files to include can be set using CONTENT_FONTS_xxx_PATH + CONTENT_FONTS_xxx_FILES. * Improved Ren'Py support: * Game scripts can rely on system-provided Ren'Py by setting: APP_xxx_TYPE='renpy' Such game scripts do not rely on APP_xxx_EXE. The game content should be available at the root of the game install path. Shipped binaries and libraries should all be excluded from the package. * Improved support for custom launchers: * Custom launchers can be generated by setting: APP_xxx_TYPE='custom' and a "custom_launcher" function. This function should output the full content of the launcher script that is to be included in the package. * More supported dependencies: * Support is added for "audioconvert" GStreamer decoder. * Changes specific to DOSBox games: * The detection of DOS binaries is improved to include .com files in addition to .exe ones. * Changes specific to WINE games: * Game scripts can require the installation of Mono in the WINE prefix by setting: WINE_WINEPREFIX_TWEAKS='mono' When Mono is to be installed, the "wine-mono-8.0.0-x86.msi" archive downloadable from the following URL is required: https://dl.winehq.org/wine/wine-mono/8.0.0/ * Changes specific to Debian: * The generation of the following metadata files has been reworked: - DEBIAN/control - DEBIAN/postinst - DEBIAN/prerm * The fields in DEBIAN/control are now all filled using dedicated functions. * Changes specific to Arch Linux: * The generation of the following metadata files has been reworked: - .PKGINFO - .INSTALL * The fields in .PKGINFO are now all filled using dedicated functions. * Changes specific to Gentoo: * The generation of the ebuild has been reworked, and most of it is now filled using dedicated functions. This change has been done for both the "gentoo" variant (binary packages) and the "egentoo" variant (source packages). 2.28.1 * Check the validity of game scripts compatibility level declaration. * Fix typos in several messages. * Display an explicit error when no .ico file is extracted from a given .exe. * Fix icons inclusion using legacy functions. The following functions should no longer trigger an error: - icons_get_from_package - icons_get_from_workdir 2.28.0 * Deprecation notices: * The function icons_inclusion is deprecated, content_inclusion_icons should be used instead. cf. "Icons system" below. * The functions launchers_write and launcher_write are deprecated, launchers_generation should be used instead. cf. "Launchers system" below. * The function launcher_desktop_exec is deprecated, desktop_field_exec should be used instead. cf. "Launchers system" below. * Support for the following deprecated archive types is dropped: - mojosetup_unzip - zip_unclean * Support for the following deprecated functions is dropped: - archive_find_path - get_context_specific_value - launcher_native_libraries_paths - launcher_write_script_headers - package_get_path - packages_get_version * Support for the following deprecated variables is dropped: - APP_xxx_ICON_ID - APP_xxx_LIBS - OPTION_xxx * Declaring dependencies on the following native libraries is no longer supported: - libavcodec.so.58 - libavformat.so.58 - libavutil.so.56 * Codebase improvements: * A package-specific contextual value can now be set for GAME_ID. * The environment language detection now honours LC_ALL and LC_MESSAGES, in addition to LANG. * Icons system: * A new function is provided to include game icons: content_inclusion_icons USAGE: content_inclusion_icons [$package [$application…]] * Launchers system: * The game execution command-line can now be overridden from game scripts by redefining the function game_exec_line. * New functions are provided to easily fetch or override the value of fields in XDG desktop files: - desktop_field_exec - desktop_field_icon * A new function is provided to generate launchers: launchers_generation USAGE: launchers_generation [$package [$application…]] * Dependencies system: * Support for the following native library is updated to rely on a downloadable archive: - libgconf-2.so.4 * Changes specific to game engines: * Unity3D: A more targeted list of libraries is included for Windows builds. * ScummVM: Hyphen-minus is now allowed in ScummVM ids, like in the following valid example: "ags:gobliiins5-1". * WINE: A virtual desktop can be set from game scripts, using the following variable: WINE_VIRTUAL_DESKTOP. WINE_VIRTUAL_DESKTOP can take the following values: - none (default if no value is set) - auto (use the current screen resolution when the game is launched for the first time) - some specific resolution (example: 1280x1024) * Changes specific to Debian: * dpkg-deb ≥ 1.19.0 is required. * fakeroot is no longer required. * Changes specific to Gentoo: * Several use flags are added to scummvm. 2.27.4 * An explicit error message is shown if the compilation of a preload shim failed. * Support for dependencies on several native libraries is added: - libboost_locale.so.1.74.0 - libdbus-glib-1.so.2 - libnotify.so.4 - libtheoraenc.so.1 2.27.3 * Prevent the current umask value to mess with permissions on packaged paths. * Ensure the package context is always set when fetching the path to install libraries into. * Debian - Drop the chmod calls made obsolete by the explicit umask setting. * Gentoo - Drop an obsolete check that would always fail. * Gentoo - Prevent a call to egentoo_package_name when using the gentoo variant. * Gentoo - Fix copying of symbolic links during installation. 2.27.2 * application_type - Ensure errors always stop the execution. * error_archive_not_found - Improve the message shown when a required archive is missing. * gentoo_package_build_single - Display an explicit error if the `ebuild (…) manifest` call failed. * package_archlinux_create_mtree - Fix the message severity level. * path_libraries - Ensure the value is always coherent with the package architecture. * temporary_directory_checks - Skip all checks if no file operation is going to take place. * Add support for dependencies on several commands: - corsix-th - sed - setxkbmap * Add support for dependencies on several native libraries: - libEGL.so.1 - libfribidi.so.0 - libminizip.so.1 - libtcmalloc_minimal.so.4 - libwayland-client.so.0 2.27.1 * Ensure that a missing required extra archive stops the script execution. * error_icon_path_empty - Fix showing the error message when LANG is not set to "fr_*" or "en_*". * launcher_target_presence_check - Ensure that the script execution stops if the binary path is not set. * unity3d_icon_path - Throw an error if no application type is found. * unity3d_application_exe_default - Throw an error if no binary could be found. 2.27.0 * Deprecation notices: * Support for the following deprecated functions is dropped: - context_specific_value - icons_linking_postinst - organize_data - use_archive_specific_value * Support for the following message functions is dropped: - print_warning - print_error See "New wrapper for messages display" for more details. * archive_get_type is deprecated, archive_type should be used instead. See "Changes related to archives" for more details. * The legacy global variables ARCHIVE and PKG should no longer be used. See "Improvements of the context system" for more details. * The functions context_archive and context_package are deprecated. See "Improvements of the context system" for more details. * The variable APP_xxx_TYPE_VARIANT is no longer supported, GAME_ENGINE should be used instead. It can usually be omitted, like with APP_xxx_TYPE_VARIANT. * The packages_get_list function is deprecated, packages_list should be used instead. * New wrapper for messages display: * A new function is provided to display all messages: print_message It expects a priority level as its first argument: - `print_message 'error' $message` replaces `print_error ; printf $message` - `print_message 'warning' $message` replaces `print_warning ; printf $message` - `print_message 'info' $message` replaces `printf $message` * Changes related to archives: * The presence of an optional archive can be checked using a dedicated function: archive_is_available * The type of an archive is retrieved using a new function: archive_type Unlike the previous function (archive_get_type), this will not trigger an error if no type is set for the given archive. * Improvements related to icons extraction: * A new function is provided to get the full path to an icon file: icon_full_path * Most functions related to icons extraction now take an icon identifier instead of the path to an icon file. * Reliance on the global variable WRESTOOL_OPTIONS is dropped. * Improvements of the context system: * A new function is provided to set the current archive: set_current_archive * A new function is provided to set the current package: set_current_package * The functions used to get the current context have been renamed: - context_archive → current_archive - context_package → current_package * Support for Visionaire engine: * A game script can rely on Visionaire engine support by setting GAME_ENGINE='visionaire' or by setting a value to VISIONAIRE_NAME. Since the engine value falls back to "visionaire" when VISIONAIRE_NAME is set, GAME_ENGINE can usually be omitted. * Default values are set for multiple variables: - APPLICATIONS_LIST - APP_xxx_EXE - CONTENT_LIBS_BIN_PATH - CONTENT_LIBS_BIN_FILES - CONTENT_GAME_BIN_FILES - CONTENT_GAME_DATA_FILES - CONTENT_DOC_DATA_PATH - CONTENT_DOC_DATA_FILES - PACKAGES_LIST - PKG_DATA_ID - PKG_DATA_DESCRIPTION - PKG_BIN_DEPS - PKG_BIN_DEPENDENCIES_LIBRARIES * For native Linux games, the used of system SDL is forced. * For WINE games, SDL_VIDEODRIVER is prevented from taking the value "wayland". * Improvements specific to WINE games: * A new compatibility link is added in the WINE user directory: "Local Settings/Application Data" → "AppData/Local". * Improvements specific to Unity3D games: * For native Linux games, the use of system SDL is forced. 2.26.3 * archive_path - Ensure that the output is always empty when the archive is not set. * icon_application - Print an explicit error if no application identifier could be found for the given icon. * launcher_target_presence_check - Do not display an error when no application type is set. * launcher_write_script - Check for the binary presence early. * Arch Linux: Fix .INSTALL metadata file generation when post-installation messages are set. 2.26.2 * archive_dependencies_check - Ensure that a failure to get the archive type triggers a fatal error. * archive_name - Add ability to compute the archive name from ARCHIVE_xxx_PATH. * archive_path - Ensure that the output is always empty when the archive is not set. * archives_integrity_check_md5 - Prevent an unexpected hashsum mismatch error for archives with no expected MD5 hash set. * icon_application - Prevent a mix up between applications sharing a similar prefix. * Arch Linux - Fix broken .PKGINFO "conflict" / "provides" fields. 2.26.1 * Fix archive integrity check when using extra archives providing native libraries. * Always use an absolute path for the archive path. * Store a cached value of the archive path the first time it is computed. * Ensure that archive content extraction using unzip overwrites files. * Debian - Ensure that dpkg-deb follows the custom TMPDIR value. 2.26.0 * Deprecation notices: * ./play.it can no longer be run by the root account, unless PLAYIT_OPTION_RUN_AS_ROOT=1 is set. * ${PLAYIT_WORKDIR}/gamedata is deleted automatically at the end of the content_inclusion_default execution for game scripts targeting a compatibility level ≥ 2.26, so a manual deletion attempt after that would fail. * target_version should no longer be set for game scripts targeting ./play.it ≥ 2.26. See "New way to set the compatibility level from a game script". * The legacy variable SOURCE_ARCHIVE should no longer be used from game scripts. See "Improvements to archives content extraction". * The legacy variable ARCHIVE_xxx should no longer be used to set neither the archive name nor the archive path. See "New archives properties". * New way to set the compatibility level from a game script: * Setting the compatibility level should be done using a new variable: PLAYIT_COMPATIBILITY_LEVEL. * Setting a compatibility level is no longer required. It is still very strongly recommended. Game scripts with no compatibility level set will break. * New functions are provided to work with the compatibility level: - compatibility_level: Get the compatibility level that has been requested. - compatibility_level_is_at_least: Check the compatibility level against a given version. * Improvements to archives content extraction: * A new function is provided to extract content from the current archive: archive_extraction_default. Unlike archive_extraction, it does not take any argument and will automatically pick the current archive (the one passed on the command line or found in the current directory). * New archives properties: * A new variable is exposed to set an archive name: ARCHIVE_xxx_NAME. * A new variable is exposed to set an archive path: ARCHIVE_xxx_PATH. * The presence of archive extra parts is checked automatically up to ARCHIVE_xxx_PART99. * Support for obsolete native libraries provided through downloadable archives: * Support for the following native libraries is added: - libcurl.so.4+CURL_OPENSSL_3 (libcurl.so.3 and libcurl.so.4 including the CURL_OPENSSL_3 symbol) - libFLAC.so.8 - libidn.so.11 - libpng12.so.0 - libssl.so.1.0.0 - libssl.so.1.1 * Adding a dependency on one of these libraries is done by including it in PKG_xxx_DEPENDENCIES_LIBRARIES, like it is done for system-provided packages. * The extra archive is required only when building packages for a system that does not provided the expected library from its repositories. * To be able to use these dependencies, the new function "archive_extraction_default" must be used. See "Improvements to archives content extraction". * Provide the ability to use a fake $HOME path, to prevent cluttering of the real $HOME: * The fake $HOME path is automatically enabled if FAKE_HOME_PERSISTENT_DIRECTORIES is set by the game script. Paths listed in this variable are diverted to persistent storage. The default fake $HOME path is ${XDG_CACHE_HOME}/play.it/home/${GAME_ID}, this path can be overridden at runtime by exporting the variable PLAYIT_FAKE_HOME_PATH. * XDG basedir paths from the fake $HOME are automatically diverted to the same paths in the real $HOME. * Support for LD_PRELOAD hacks: * A list of hacks can be provided by PRELOAD_HACKS_LIST, one per line. * Each hack must set the following properties: - HACK_xxx_NAME - HACK_xxx_DESCRIPTION - HACK_xxx_PACKAGE (can be omitted, probably not a good idea) - HACK_xxx_SOURCE * The following new function should be called from the game script to build and include the hacks: hacks_inclusion_default. * Improvements to the handling of packages metadata: * The "package_description" function now only returns the value of PKG_xxx_DESCRIPTION, without extra formatting. In addition, it triggers an error if this description includes line breaks. * Expansion of the context system: * The following variables got support for contextual values: - APPLICATIONS_PREFIX_TYPE - APP_xxx_PREFIX_TYPE - APP_xxx_PRERUN - APP_xxx_POSTRUN - USER_PERSISTENT_FILES - USER_PERSISTENT_DIRECTORIES * Rework packages post-installation and pre-removal actions: * A new variable can be set to a list of warnings that are displayed post-installation, one per line: PKG_xxx_POSTINST_WARNINGS. 2.25.6 * content_inclusion_chunk_single - Fix behaviour when PACKAGES_LIST has a contextual value set. * error_package_does_not_exist - Fix French translation. * Gentoo - Fix the definition of the postinst/prerm package scripts. 2.25.5 * Gentoo - Fix the function call used to fill the DESCRIPTION ebuild field. * Gentoo - Fix the function call used to fill the RDEPEND ebuild field. * Gentoo - Drop unwanted "$" at the beginning of the version string. * Gentoo - Fix a typo in the variable setting the compression command. * Gentoo - Fix ebuild settings through environment variables. * Gentoo - Drop broken support for --compression none. * Gentoo - Move the generated packages to the output directory, instead of deleting them. 2.25.4 * Add support for extra native libraries. * Trigger a warning if APP_xxx_LIBS is set from a game script targeting a compatibility level ≥ 2.19. * content_path - Prevent a failure if CONTENT_PATH_DEFAULT is empty. * games_find_script_for_archive - Prevent a broken pipe non-blocking error. 2.25.3 * application_type_guess_from_file - Prevent failure when APP_xxx_EXE is not set. * archive_extraction_makeself - Decrease the time spent on files extraction. * archive_extraction_mojosetup - Decrease the time spent on files extraction. * content_path_default - Throw an explicit error if no path is set. 2.25.2 * persistent_list_directories / persistent_list_files - Reduce reliance on variable_is_empty. 2.25.1 * archive_get_type - Fix archive type detection using file headers. * debian_package_build_single - Fix usage of $dpkg_options variable. 2.25.0 * Deprecation notices: * Support for the legacy compression options is dropped. See "Compression methods rework" in 2.22.0 release notes for more details. * The variable APP_WINETRICKS is deprecated, and will be ignored for game scripts targeting ./play.it ≥ 2.26. See "Improvements to support for WINE games" below for more details. * New command line options: * --list-available-scripts — Print the list of game scripts available on this system. * --list-supported-games — Print the list of supported games. The output of this option can take up to several minutes to be generated, depending on the number of available game scripts. * New make actions: * make shunit2 - Run a series of unit tests based on shUnit2. * make shunit2-coverage - Display a coverage report for shUnit2 tests. * make check - This command is extended to include the shUnit2 tests and their coverage report. * New dependencies system for commands required at runtime: * A new variable is exposed to game scripts: PKG_xxx_DEPENDENCIES_COMMANDS It can be set to a list of commands that are required by the game at runtime, one per line. * Improvements to support for WINE games: * Add ability to set the default value for WINEDLLOVERRIDES, using a new variables: WINE_DLLOVERRIDES_DEFAULT. If this variable is not set, the generic default value is used: WINEDLLOVERRIDES='winemenubuilder.exe,mscoree,mshtml=' * A new variable is exposed for listing required winetricks verbs: WINE_WINETRICKS_VERBS. The old variable name APP_WINETRICKS is still supported for game scripts targeting ./play.it < 2.26. * Improved support for Unity3D games, the following variables can be omitted and will fall back on default values: - APP_MAIN_EXE (including for Windows games) - CONTENT_GAME_BIN_FILES - CONTENT_GAME_BIN32_FILES - CONTENT_GAME_BIN64_FILES - CONTENT_GAME_DATA_FILES * Improved support for Unreal Engine 4, the following variables can be omitted and will fall back on default values: - APP_MAIN_ICON_WRESTOOL_OPTIONS - CONTENT_GAME_BIN_FILES - CONTENT_GAME_DATA_FILES - PKG_BIN_DEPENDENCIES_GSTREAMER_PLUGINS - WINE_DIRECT3D_RENDERER - WINE_PERSISTENT_DIRECTORIES - WINE_WINETRICKS_VERBS 2.24.1 * application_options - Fix test preventing line breaks in application options string. * archive_guess_type_from_name - Ensure that an empty string is returned if no type could be guessed. * content_inclusion - Check that the given package identifier is valid. * content_inclusion_chunks - Drop declaration of unused variable. * Prevent incompatibility between old .deb format 0.939000 and xz compression. * Do not tweak shell options from inside functions. * Add support for extra native libraries. 2.24.0 * Deprecation notices: * The following functions are deprecated: - launcher_native_libraries_paths - launcher_write_script_headers * The following functions are deprecated for game scripts targeting ./play.it ≥ 2.14: - write_metadata - build_pkg See the section "Changes related to packages generation" below for more details. * Support for the following application types is dropped: - renpy - residualvm * Ignoring errors during calls to archive_extraction can no longer be done using `set +o errexit`, because `set -o errexit` is forced after the call to external tools used to handle the archives. The recommended snippet to use instead is: archive_extraction 'SOURCE_ARCHIVE' 2>/dev/null || true * Support for the unused variable PREFIX_PREPARE is dropped. * DOSBox games: The "userdir_toupper_files" function is no longer included in launchers. * Mono games: Support for "APP_xxx_MONO_OPTIONS" is dropped. * Improved support for game expansions: * The expansion id is appended to the package id by default. Explicit PKG_xxx_ID declaration is no longer required for expansions. * Changes related to packages generation: * A new function is provided for packages generation: packages_generation It replaces the two following functions: - write_metadata - build_pkg * Changes related to files extraction from archives: * An extraction log is stored at: ${PLAYIT_WORKDIR}/logs/archive-extraction.log * Minimal permissions are always applied on extracted files. * MojoSetup archives: unzip is used instead of unar to handle the inner archive. * Changes specific to ScummVM games: * Allow omitting APPLICATION_xxx_TYPE for ScummVM applications. * Changes specific to WINE games: * Throw an explicit error if a required registry script could not be loaded. 2.23.6 * Fix error displayed when an unkown archive type is set. * WINE: Fix automatic dependencies addition based on WineD3D renderer. 2.23.5 * MojoSetup archives: Fix the extraction of the inner .zip archive. 2.23.4 * Prevent output redirection from leaking after error messages. * Add support for dependencies on extra native libraries. 2.23.3 * Add support for extra native libraries. * Unity3D: Prevent plugins inclusion failure if no architecture-specific directory is shipped. * Unity3D: Prevent a crash on Linux 6.1 by disabling the MAP_32BIT flag with some Unity3D builds. 2.23.2 * Fix warning displayed on unsupported GStreamer media format. * Fix content inclusion when using legacy ARCHIVE_xxx_FILES variables. * Do not generate an empty list of unsupported GStreamer decoders. * Add support for extra GStreamer decoders. * Add support for extra native libraries. * Avoid querying unset variables. 2.23.1 * Display an explicit error on package building failure. * Fix install path for native libraries. * Add support for more native libraries dependencies. * Arch Linux: Fix filling "conflict" and "provides" in package metadata. * Gentoo: Fix setting the list of dependencies on native libraries. 2.23.0 * Deprecation notices: * Game scripts targeting ./play.it ≥ 2.23 can no longer use the legacy compression values, see "Compression methods rework" in the 2.22.0 section for more details. * Trying to expand an unset variable triggers an error, for game scripts targeting ./play.it ≥ 2.23. * APP_WINE_LINK_DIRS is deprecated, setting it from a game script targeting ./play.it ≥ 2.23 triggers a warning. For games scripts targeting ./play.it ≥ 2.24, it will by ignored silently. See "Improved support for WINE games" below for more details. * PKG_xxx_PROVIDE is deprecated, setting it from a game script targeting ./play.it ≥ 2.23 triggers a warning. For games scripts targeting ./play.it ≥ 2.24, it will by ignored silently. See "Packages metadata improvements" below for more details. * Dependencies handling improvements: * The generation of a launcher for the following application types automatically adds the required dependency to the current package: - dosbox - java - mono - renpy - residualvm - scummvm - wine * Dependencies on media formats decoded by GStreamer can be listed using a new variable: PKG_xxx_DEPENDENCIES_GSTREAMER_PLUGINS. The following formats are supported: - avidemux - decodebin - deinterlace - audio/x-wma, wmaversion=(int)1 - video/quicktime, variant=(string)iso - video/x-ms-asf - video/x-msvideo - video/x-wmv, wmvversion=(int)1 * Application type improvements: * The application type can be guessed from the game binary even if it is not in the current package. * Packages generation improvements: * A package can now provide/conflict with multiple package names, using a new variable PKG_xxx_PROVIDES One provided name should be written on each line, using line breaks as the list separator. * If all packages are already built, the execution stops early to avoid unnecessary resources and time usage. * Debian: Automatically use old .deb format (0.939000) to avoid size limits. * Improved support for Unity3D games: * Shipped plugins for Unity3D games can now be listed using a new dedicated variable: UNITY3D_PLUGINS. If it is set to a non-empty value, it is used during the call to "content_inclusion_default" to include the listed plugins into the path dedicated to shipped libraries. Plugins that are not listed are removed to ensure they are not included later by some "CONTENT_xxx_FILES" greedy pattern. * Improved support for WINE games: * A new variable is used to list the paths that should be diverted from the WINE prefix: WINE_PERSISTENT_DIRECTORIES Its value is a list of paths relative to $WINEPREFIX/drive_c, one per line. * Improved handling of huge files (>9GB): * Files that are bigger than 9GB should be listed using a dedicated variable: HUGE_FILES_xxx "xxx" is the suffix of the package that includes these files by default, so a list for PKG_DATA would be named HUGE_FILES_DATA. * The files split is done only when building .deb packages, as Arch Linux and Gentoo packages have no size limit. * Improved support for Makeself and MojoSetup archives: * The type declaration can be omitted for MojoSetup archives. * Support is added for Makeself archives. 2.22.5 * Use a single function to handle the copy of the game binary into the prefix. * Fix permissions on the manual pages. * Add support for more native libraries dependencies: - libc++.so.1 - libc++abi.so.1 - libpcre.so.3 * Arch Linux: Improve listing of packages providing generic dependencies. * Arch Linux: Prevent querying of unset variables. 2.22.4 * Arch Linux: Fix fetching the maintainer name from /etc/makepkg.conf. * Arch Linux: Fix adding multiple dependencies for a single generic keyword. * Gentoo: Fix support for PKG_xxx_PROVIDE. * Gentoo: Prevent querying of unset variables. 2.22.3 * Ensure function return codes are never hidden behind cat calls. * Ensure package architecture is always fetched using the dedicated function. * Run a late requirements check for icons extraction. * Add support for missing native libraries dependencies: - libutil.so.1 - libXmu.so.6 * Arch Linux: Rework packages metadata generation to no longer rely on a variable leak. * Arch Linux: Fix libvulkan.so.1 dependency. 2.22.2 * Improve automatic detection of DOS executables. * Display a list of unknown Mono libraries if some have been required. * Prevent functions failures to be hidden by while loops. 2.22.1 * Fix inclusion of pre-run/post-run actions in launchers for WINE games. * Improve handling of pre-run/post-run actions in launchers for native games. * Add support for libbz2.so.1 native library dependency. 2.22.0 * Deprecation notices: * Multiple options are dropped, here are the replacements that should be used: - --icons yes (no replacement, this is the default behaviour) - --icons no → --no-icons - --skip-free-space-check → --no-free-space-check - -c → --config-file - -h → --help - -v → --version * Functions used to fetch packages metadata have been renamed: - packages_get_maintainer → package_maintainer - packages_get_version → package_version (a compatibility alias is provided) - package_get_architecture → package_architecture - package_get_architecture_string → package_architecture_string - package_get_description → package_description - package_get_id → package_id * "launcher_write_script_wine_run" is obsolete, "wine_launcher_run" should be used instead. A compatibility wrapper is provided for game scripts targeting ./play.it < 2.22. * Legacy environment variables (usually "OPTION_xxx") are still exported only for game scripts targeting ./play.it ≤ 2.22. See "Options system rework" below for more details. * The legacy values for --compression are still valid only for game scripts targeting ./play.it ≤ 2.22. See "Compression methods rework" below for more details. * "unity3d" is no longer a full blown application type, but a variant of the application type "native". See "New application type variant system" below for more details. * The internal test "variable_is_set" is dropped. "get_value" no longer throws an error when trying to query an unset variable, it returns an empty value instead. * Options system rework: * A new function is provided to fetch the current value of a given option: option_value $option_name As an example, instead of a direct use of the value of the deprecated variable $OPTION_COMPRESSION, the following function call should be used: option_value 'compression' * The game archive is no longer the first mandatory argument when calling the "play.it" command. It can now be provided anywhere on the command line. * All options can be set using environment variables, here is the full list of command line arguments and for each one the equivalent environment variable: - --checksum → PLAYIT_OPTION_CHECKSUM - --compression → PLAYIT_OPTION_COMPRESSION - --config-file → PLAYIT_OPTION_CONFIG_FILE - --debug → PLAYIT_OPTION_DEBUG - --help → PLAYIT_OPTION_HELP - --list-packages → PLAYIT_OPTION_LIST_PACKAGES - --list-requirements → PLAYIT_OPTION_LIST_REQUIREMENTS - --no-free-space-check → PLAYIT_OPTION_FREE_SPACE_CHECK - --no-icons → PLAYIT_OPTION_ICONS - --no-mtree → PLAYIT_OPTION_MTREE - --overwrite → PLAYIT_OPTION_OVERWRITE - --output-dir → PLAYIT_OPTION_OUTPUT_DIR - --package → PLAYIT_OPTION_PACKAGE - --prefix → PLAYIT_OPTION_PREFIX - --show-game-script → PLAYIT_OPTION_SHOW_GAME_SCRIPT - --tmpdir → PLAYIT_OPTION_TMPDIR - --version → PLAYIT_OPTION_VERSION If a same option is set using both an environment variable and a command line argument, the command line argument is ignored. * Compression methods rework: * Compression options are no longer specific to the target package format, the same four values are available: - "none": no compression (default value) - "speed": compression method focusing on compression speed - "size": compression method focusing on size reduction - "auto": use the current defaults for the packages generation tool (not supported when building packages for Arch Linux) The legacy values are still available for game scripts targeting ./play.it ≤ 2.22, but their use triggers a deprecation warning. For game scripts targeting ./play.it > 2.22, an error is thrown. * New application type variant system: * The application type variant can be set from a game script using the variable "APP_xxx_TYPE_VARIANT". There is only one accepted value for now: "unity3d". * Unity3D games for Windows are no longer wrongly identified as native Linux games if they set "UNITY3D_NAME" but not "APP_xxx_TYPE". * A dedicated per-session log file is used for Unity3D games running through WINE. * Changes related to packages: * Environment variables set for Debian tools are followed when trying to get the maintainer name and e-mail. * Changes specific to Debian: * The generation of the DEBIAN/control file is moved to a dedicated function. * The package metadata generation is updated to no longer rely on implicit variable inheritance. * Dead code removals: * Support is dropped for unused compatibility wrappers: - package_get_current * Support is dropped for unused messages: - error_archive_unset - error_context_invalid - error_no_valid_temp_dir_found - error_obsolete_function - print_ok - warning_missing_library * Support is dropped for unused generic dependency keywords: - bzip2 - gconf - libcurl - libcurl-gnutls - sdl1.2 - sdl2_image - sdl2_mixer - theora - vorbis - wine-staging / wine32-staging / wine64-staging - xft 2.21.2 * Display an explicit error when a game script seems to support no archive. * Use dpkg for version strings comparison. * Ensure the pre-run and post-run actions strings always end with a line break. * Add missing native library dependency: libIL.so.1 * Gentoo: Add the opengl USE flag to relevant SDL packages. 2.21.1 * Fix Make rule used to build the library. * Run syntax checks from the Makefile. * Add ability to build release tarballs from Make. * Add support for dependencies on more native libraries: - libaudio.so.2 - libcrypt.so.1 - libFAudio.so.0 - libgomp.so.1 - liblcms2.so.2 - libpixman-1.so.0 - libSDL_kitchensink.so.1 - libSDL_sound-1.0.so.1 - libsigc-2.0.so.0 - libvorbisenc.so.2 - libX11-xcb.so.1 - libxcb.so.1 - libxcb-randr.so.0 * Add support for dependencies on more Mono libraries: - OpenTK.dll - OpenTK.Compatibility.dll - OpenTK.GLControl.dll * Prevent non-fatal errors when listing package dependencies. * Fix the error displayed when no supported archive is found. * Add support for "image/x-xpixmap" MIME type for icons. 2.21.0 * Deprecation notices: * Support is dropped for --icons auto. The only valid values are --icon yes|no, defaulting to --icons yes. * Support for application type "native_no-prefix" is dropped. * For game scripts targeting ./play.it ≥ 2.21, the main archives identifiers must start with "ARCHIVE_BASE". Game scripts targeting an older version can still use the prefix "ARCHIVE" instead. * The following functions are deprecated, but still available through compatibility aliases: - context_specific_value - get_context_specific_value - package_get_current * The following old functions are dropped, with no compatibility alias: - get_context_suffix_archive - get_context_suffix_package - context_specific_name - test_var * Up to ./play.it 2.20, all of these content identifiers were tested when calling "content_inclusion_default" (or "prepare_package_layout" for older game scripts): - LIBS_xxx - LIBS0_xxx - (…) - LIBS9_xxx - GAME_xxx - GAME0_xxx - (…) - GAME9_xxx - DOC_xxx - DOC0_xxx - (…) - DOC9_xxx Starting with ./play.it 2.21, the search stops at the first unset numbered identifier. So if "GAME1_xxx" is not set, "GAME2_xxx" up to "GAME9_xxx" will be skipped. No legacy behaviour is provided for old game scripts. * For WINE games, winecfg launchers are no longer generated. To spawn winecfg using the prefix for a given name, you can run the following command: WINEPREFIX=~/.cache/play.it/wine/${game_id} winecfg * New options: * --list-packages: List the packages that would be built from the given archive. * --list-requirements: List the game script requirements for the given archive. * Improvements of file type detection based on MIME: * Exclude charset information from "file" output. * Add support for "application/x-sharedlib" MIME type. * Improve detection of PE32 executables. * Reworked context system * The following commonly used variables now have context support: - APP_xxx_ID - APP_xxx_NAME - APP_xxx_ICONS_LIST - APP_xxx_SCUMMID * New functions are provided: - context_value: Print the context-sensitive value for the given variable. - context_name: Print the name of the variable containing the context-specific value of the given variable. - context_archive: Print the identifier of the current archive. - context_package: Print the identifier of the current package. * Changes related to launchers: * Provide a new function printing the path to a launcher script: launcher_path. * Changes related to icons: * Rework "icons_list_all" to include implicit icons (like the ones from Unity3D or WINE games). * Changes specific to Mono games: * Dependencies on a list of Mono libraries can be declared with a new dedicated variable: PKG_xxx_DEPENDENCIES_MONO_LIBRARIES. * Changes specific to Gentoo: * Use PKG_xxx_DEPS_GENTOO with no transformation, allowing to pass more complex dependency constraints. * Other changes: * Provide new warnings and errors to be used when deprecated/obsolete functions are called: - warning_deprecated_function - error_obsolete_function * If EXPANSION_NAME is set, it is automatically included in the output of game_name. 2.20.3 * Fix usage of pkg_set_deps_gentoo. 2.20.2 * Display an error when a DOSBox image disk was not found. * Gentoo (egentoo variant): Install data files only if present. * Gentoo: Use correct documentation paths. * Add support for more native libraries. * New generic dependencies: - residualvm - scummvm 2.20.1 * Drop duplicate declaration of message function "error_variable_not_set". * Update documentation with the "--config-file" option. * application_icons_list - Fix support for archive context. * applications_list - When parsing the game script, avoid printing duplicates. * debian_dependencies_full_list - Fix listing when both PKG_xxx_DEPS_DEB and PKG_xxx_DEPENDENCIES_LIBRARIES are set. * Add support for MIME type "application/vnd.microsoft.portable-executable". 2.20.0 * Deprecation warnings: * APP_xxx_PRERUN/APP_xxx_POSTRUN behaviour is changed for DOSBox game scripts targeting ./play.it ≥ 2.20. See "Changes specific to DOSBox" below for more details. * "package_get_path" is now a compatibility wrapper around "package_path". See "Changes related to packages" below for more details * Codebase improvements: * Provide new variable manipulation helpers: - variable_is_set - variable_is_empty * Rework multiple functions to avoid querying unset variables. * Changes related to packages: * A new function "package_name" is provided, returning the file name of a given package. * Packages are prepared in "${PLAYIT_WORKDIR}/packages" instead of the root of "$PLAYIT_WORKDIR". * The function returning the path to the directory where a given package is prepared is renamed from "package_get_path" to "package_path". * Changes specific to DOSBox: * Drop special behaviour when using APP_xxx_PRERUN/APP_xxx_POSTRUN. Game scripts targeting ./play.it < 2.20 are unaffected, commands set using APP_xxx_PRERUN/APP_xxx_POSTRUN are still run from inside DOSBox. For games scripts targeting ./play.it ≥ 2.20, commands set using APP_xxx_PRERUN are run before spawning DOSBox, and commands set using APP_xxx_POSTRUN are run after exiting DOSBox. Two new variables are provided to set commands that should be run inside DOSBox: - APP_xxx_DOSBOX_PRERUN - APP_xxx_DOSBOX_POSTRUN * Changes specific to Mono: * Add ability to run games without a local user prefix. * Changes specific to Gentoo: * Change egentoo output paths. Instead of making packages in data, amd64 and x86 directories, ebuilds are now created in an overlay/games-playit directory and packages in a packages directory. * With egentoo variant, generate only one ebuild and one tar archive, with a bit more complexity in the ebuild, in order to decide which content to install. 2.19.1 * Ensure errors during calls to "game_id" are always blocking. * Gentoo: Fix dependencies handling. * Gentoo: Ensure libstdc++.so.6 is provided by the correct package. 2.19.0 * Deprecation warnings: * The legacy variables for content inclusion are no longer supported for game scripts targeting ./play.it ≥ 2.19: - ARCHIVE_xxx_PATH - ARCHIVE_xxx_FILES The new variables introduced with ./play.it 2.18 must be used instead: - CONTENT_xxx_PATH - CONTENT_xxx_FILES The legacy ones can still be used only from game scripts targeting ./play.it ≤ 2.18. * The following global variables are still available, but are deprecated: - PATH_BIN - PATH_DESK - PATH_DOC - PATH_GAME - PATH_ICON_BASE * "APP_xxx_LIBS" is deprecated, game scripts should no longer rely on it. Libraries installed under /usr/lib/games/${GAME_ID} are automatically added to LD_LIBRARY_PATH. * The following internal functions are dropped: - launcher_write_script_dosbox_application_variables - launcher_write_script_dosbox_run - launcher_write_script_java_application_variables - launcher_write_script_java_run - launcher_write_script_mono_application_variables - launcher_write_script_mono_run - launcher_write_script_native_application_variables - launcher_write_script_native_run - launcher_write_script_nativenoprefix_run - launcher_write_script_native_run_common - launcher_write_script_renpy_run - launcher_write_script_residualvm_application_variables - launcher_write_script_residualvm_run - launcher_write_script_scummvm_application_variables - launcher_write_script_scummvm_run * "application_type" no longer fails if no type could be found. Support for the special fallback value "unknown" is dropped, as its only purpose was to avoid the error on empty application type. * "package_get_current" no longer falls back on "PKG_MAIN", see "Changes related to packages" below for more details. * Improved integration of shipped libraries * In addition to standard system paths, libraries are loaded from the following paths, ordered from lower priority to higher priority: - APP_xxx_LIBS (deprecated) - /usr/lib/games/${GAME_ID} (/usr is replaced by the custom install prefix) - ${HOME}/.local/lib/games/${GAME_ID} * In addition to game data files and documentation files, "content_inclusion_default" now automatically fetches listed native libraries and put them into a specific install path. The native libraries can be listed using global variables similar to the ones already in use: - CONTENT_LIBS_xxx_PATH - CONTENT_LIBS_xxx_FILES As with game data files and documentation files, as single-digit number can be appended to "LIBS": - CONTENT_LIBS0_xxx_FILES - CONTENT_LIBS1_xxx_FILES - (…) - CONTENT_LIBS9_xxx_FILES * New dedicated functions printing install paths are available: - path_binaries - path_xdg_desktop - path_documentation - path_game_data - path_icons - path_libraries These should be used instead of the deprecated "PATH_xxx" variables. * Rework launchers generation * Game execution failure no longer prevents the execution of post-run actions. * The late extra eval step of JAVA_OPTIONS and MONO_OPTIONS is dropped, no current game script was relying on these. * APP_xxx_OPTIONS support is added to ResidualVM and ScummVM launchers. * Changes related to icons integration: * Rework icons requirement check to no longer rely on applications. * Add support for archive context to application_icons_list. * If no icon is set for a WINE application, try to extract one from the game binary. * Changes related to applications: * Update applications_list to follow the archive context. * Add automatic application type detection based on file type, APP_xxx_TYPE declaration is now optional in more cases. * Changes related to packages: * Extend the list of supported native libraries for packages dependencies declarations. * package_get_current returns the first package of the list of packages to build if no value is explicitly set, instead of the previously hardcoded value "PKG_MAIN". * Other changes: * Drop system-specific install prefix. The default install prefix is now the same for all supported systems: /usr * Do not run a full dependencies check when initializing an archive, only the presence of the dependencies required to handle the current archive is tested. * Set working directory path as soon as the archive is found. * Automatically add dependency on winetricks if APP_WINETRICKS is set. 2.18.3 * content_path_default - Do not throw an error if no default path is set. * Extend the list of supported native libraries for packages dependencies declarations. * Add a format check for package id. * WINE: Fix APP_WINE_LINK_DIRS failure on non-empty target. 2.18.2 * Add ability to get the name of a context-specific variable, using a new dedicated function: context_specific_name. This new function should not be used from game scripts before ./play.it 2.19 release. * Fix dependencies addition using dedicated functions. * Extend the list of supported native libraries for packages dependencies declarations. 2.18.1 * Identify SteamOS as an Arch Linux derivative. * Extend the list of supported native libraries for packages dependencies declarations. * archive_dependencies_check - Include archive extra parts during extraction dependencies check. * icons_inclusion - Fail explicitly if called with no argument and no applications list can be guessed. * launcher_write_script - Throw an explicit error if called with an empty argument. * print_instructions - Clear the list of unknown libraries after it has been displayed once. 2.18.0 * Changes related to archive contents inclusion: * CONTENT_xxx_PATH is a new variable replacing ARCHIVE_xxx_PATH. * CONTENT_xxx_FILES is a new variable replacing ARCHIVE_xxx_FILES. * content_inclusion is a new function replacing organize_data. * content_inclusion_default is a new function replacing prepare_package_layout. * Changes related to packages: * Provide a new dependency system for native libraries, using a new variable: PKG_xxx_DEPENDENCIES_LIBRARIES * Support is added for extra native libraries. * Changes specific to WINE: * WINEDLLOVERRIDES can be overridden at runtime. * Provide persistent storage for registry keys, using a new dedicated variable: WINE_REGEDIT_PERSISTENT_KEYS * Add ability to set the Direct3D renderer using WINE_DIRECT3D_RENDERER. * Other changes: * Drop messages on completion of long tasks. * Codebase improvements: * Rework prefix generation for WINE games. 2.17.2 * Prefixes - Fix handling of symbolic links in read-only game data * icons_move_to (deprecated) - Drop declaration of unused variable * Improve automatic package format setting * Identify Artix as an Arch Linux derivative 2.17.1 * icons_get_from_legacy_path - Fix clean-up step, to avoid an error from rmdir. 2.17.0 * Changes related to launchers: * Prefix type can be set at the application level (using APP_xxx_PREFIX_TYPE) or for all applications (using APPLICATIONS_PREFIX_TYPE), with the following valid values: - symlinks: generate our usual symbolic links farm, the default for most application types - none: the game is run from the read-only system directory, the default for ScummVM and ResidualVM * Prefix path can be overridden for the current game session by setting PLAYIT_PREFIX_PATH The default value is: ~/.cache/play.it/prefixes/${GAME_ID} * WINE prefix path follows WINEPREFIX if it is set. If the variable is unset, it defaults to: ~/.cache/play.it/wine/${GAME_ID} * A single directory is used for persistent storage of user data. It can be set using PLAYIT_PERSISTENT_USER_PATH, and defaults to: ~/.local/share/games/${GAME_ID} * The undocumented runtime variable PREFIX_ID is no longer supported. * Changes related to archives: * New archive type "tar.bz2". * For multi-part archives, type is optional for extra parts. * Allow use of unzip for data extraction from mojosetup archives, if bsdtar is not available. * Force the use of a specific tool for content extraction: - An archive extractor can be set with ARCHIVE_xxx_EXTRACTOR - An options strings can be set with ARCHIVE_xxx_EXTRACTOR_OPTIONS * Changes related to icons: * Icons inclusion is now done using a single function, icons_inclusion. * The following functions are deprecated: - icons_get_from_package - icons_get_from_workdir - icons_move_to * Other changes: * Drop option to do partial runs with no actual disk write. (--dry-run option) * New option provided to change ./play.it working path: --tmpdir The default value is $TMPDIR, falling back on /tmp * Codebase improvements: * New layout for source files. * Use a dedicated check to detect unexpected empty variables. 2.16.2 * applications_list - Throw an explicit error on unexpected empty list. * prepare_package_layout - Prevent PKG value changes to leak outside of the current function call. 2.16.1 * icons_list_dependencies - Rely on application_icons_list to get the list of icons. * launcher_write_desktop - Drop requirement on APP_xxx_TYPE. * get_context_specific_value: - Do not try to guess a package identifier if none is set. - Do not look for an archive-specific value before an archive is set. * Check for dependencies only after the main archive is set. 2.16.0 * Major changes: * Game scripts are no longer provided by this repository, they should instead be fetched from dedicated collections. The main official game scripts collection is available from https://forge.dotslashplay.it/play.it/games * Drop support for selective architecture building (--architecture option) * Disable debug support by default, its inclusion is now done through a build flag. * A new extended man page is provided, available in both English and French. * Codebase improvements * Rewrite the code used to generate and update the volatile game prefixes. * Rework the code handling data extraction from archives. * Launchers: Escape single quotes in binary file name. * Applications: Allow setting package-specific versions of several application properties. * Changes specific to WINE: * Add ability to set a custom path to `wine` command, using a new environment variable: PLAYIT_WINE_CMD * Changes specific to ScummVM: * Improve ScummVM ID validation. * Changes specific to Debian: * Drop support for installation instructions relying on dpkg (useful only for apt versions older than 1.1) * Changes specific to Arch Linux: * Restore the generation of the .MTREE package metadata file. Its generation can be skipped with --no-mtree. * Changes specific to Gentoo: * Ensure the directory used to prepare packages allows files execution. * Append script revision to package version number. 2.15.1 * WINE: Set compatibility links to legacy user paths * Drop direct calls to PACKAGES_LIST environment variable * Fix behaviour of archive_find_path when the archive variable contains an absolute path 2.15.0 * New features: * All command line options can now be set through a configuration file, its path can be set with --config-file some_path and defaults to: $XDG_CONFIG_HOME/play.it/config * --show-game-script - display the path to the game script that would be used for a given archive, but take no action * General improvements: * New functions used to fetch game-related values: * game_id - print the id of the current game * game_name - print the display name of the current game * Provide a new variable CONTENT_PATH_DEFAULT, used as a default value for ARCHIVE_xxx_PATH * Setting the current package to a value that is not included in the list of packages to build triggers an error * Rely on MIME type for icon type detection, instead of file extension * application_type can now be passed a fallback value * Changes specific to Debian: * Improve handling of dependency to WINE * Changes specific to Gentoo: * Use unpacker eclass when using a non-standard compression option * Drop support for lzop compression * Replace spaces by tabulations and linefeeds in pkg_deps * Refactor compression handling 2.14.4 * Fix ambiguous quoting when using prefix/suffix pattern removal 2.14.3 * Fix ability to call the wrapper with --help * WINE: Fix a failure to get the path to current package when generating the .desktop launcher for winecfg * Gentoo: Update icon cache after installation and removal * Gentoo: Fix "provides" fields for ebuild generation 2.14.2 * Ensure all errors related to fetching the path where a package is prepared are blocking * Fix fetching current package when getting the path where a .desktop file should be written * Fall back on guessed value for game binary only if no value has been explicitely set * Improve handling of failure state on errors 2.14.1 * Only use application type guessing if none is explicitely set * Drop ShellCheck "disable" directives for non-POSIX local usage * Ensure invalid application type always throws blocking errors * Fix fetching application type * Show an explicit error when tolower/toupper is given an invalid target 2.14.0 * New features: * Archives: Add support for tar.xz archives * Engines: Improve support for Unity3D games, with a new dedicated application type * Launchers: Provide new functions used to display localized messages * Launchers: Add a new hook for commands to run before the initialization of the game prefix * Launchers: Provide a new function used to convert filenames case * Icons: Add support for X PixMap icons (.xpm) * General improvements: * Improve the validity tests run when setting temporary directories * Packages-related improvements: * Always use the archive-specific list of packages when available * Add several new dependency keywords * Installation: * The default path for installing the "play.it" command as a non root user is now "$HOME/.local/bin", instead of "$HOME/bin" * The path for installing game scripts is now $(datadir)/play.it/games/50_core, where $(datadir) defaults to: - /usr/local/share/games for installation as root - $XDG_DATA_HOME for installation as a non-privileged user * Code maintenance: * A new source file is used for functions dedicated to variable manipulations * A new function is provided for getting context-specific variable values * Provide new helper functions easing the manipulation of applications and icons * Launchers: Rework the generation of the XDG desktop files * Deprecation notices: * get_archive_specific_value is no longer available * use_archive_specific_value and use_package_specific_value are now compatibility aliases around the new function get_context_specific_value 2.13.3 * Redirect all debug messages to /dev/stderr * Gentoo: Stop emerge on errors * Arch Linux: Drop generation of .MTREE metadata file 2.13.2 * Fix getting icon path for pre-2.8 game scripts with globs expanding to spaces * Improve the search for a proper working directory * Improve function descriptions in the icons source file * Gentoo: Use fakeroot (≥1.25.1) instead of fakeroot-ng 2.13.1 * Fix data migration from volatile prefix to persistent paths * Fix filenames case conversion failure on non-ascii characters * Fix wrapper installation path * Gentoo: Ensure sdl2-image is built with png and jpeg support 2.13.0 * New features: * New option --version (or -v), displaying the library version * Debug notices, disabled by default, enabled with --debug * Archives: Add support for InstallShield installers * Engines: Add ability to use system-provided Ren'Py * Packages: Add support for extra compression methods: lz4, lzip, lzop and zstd * Packages: Add an alternative output format targeting Gentoo * The installation process GNU Make has been improved to allow targeted actions: install-library, install-games, install-wrapper and install-manpage * Archive-related fixes and improvements: * Use a new archives naming convention, more robust, see our documentation for details: https://forge.dotslashplay.it/play.it/doc/-/wikis/dev/scripts/variables#base-archives * Rewrite functions related to archives initialization * .deb archives: Extract data without relying on dpkg-deb only * Fix the error shown when an archive type is not set and could not be guessed * Fix the error shown when an unknown archive type is used * WINE-specific improvements: * Do not override WINEDEBUG if it is already set by the user * Provide a standard method to divert arbitrary files from the WINE prefix to persistent user-writable paths * Packages-related changes: * Provide wrapper functions to get package-related informations, including packages metadata * New generic dependency keywords * Improve handling of supported compression methods based on the target package format * Debian: Update dependency on libgdk_pixbuf-2.0.so.0 * Gentoo: Change default installation prefix to /usr from /usr/local * Environment checks: * Run early filesystem checks, allowing to detect failure that would happen only at the packages building step * New function version_is_at_least comparing two version strings using the "x.y.z" format * Deprecation notices: * icons_linking_postinst is now a compatibility wrapper around standard icons extraction functions 2.12.2 * Archives-related improvements: * Improve compatibility check for InnoSetup archives * Icons-related fixes: * Avoid a potential infinite loop in the icons resolution guessing function * Fix compatibility with GraphicksMagic compatibility wrapper for ImageMagick * Package-related fixes and improvements: * Arch Linux: Add support for EndeavourOS as an Arch Linux derivative * Fix getting hostname on systems without /etc/hostname * Errors handling: * Fix error message displayed when passing an invalid value to a supported option * Improve handling of error cases related to empty variables * Fix error message shown when there was not enough disk space in tested temporary directory candidates * Codebase improvements: * Use grep --quiet instead of output redirection 2.12.1 * Wrapper fixes: * Fix script detection from archive name * Archive-related fixes: * .zip archives: Drop selected files extraction using unzip * Package-related improvements: * Arch Linux: Use multithreaded xz compression by default * Arch Linux: Fix packages building failure when using --output-dir with a relative path * Arch Linux: Generate a .MTREE metadata file * Debian: Improve APT version detection * Dependencies-related improvements: * Show a more explicit message if icotool or wrestool is required but missing * Launchers-related fixes * Avoid broken .desktop Exec field when using spaces in the install prefix * Icons-related fixes: * Fix icons_move_to failure when targeting a non-empty directory * Codebase improvements: * Drop the dependency on hostname * Unset variables that we do not want to import from the user environment 2.12.0 * New options: * --output-dir: Set the output directory for generated packages * --overwrite: Replace packages if they already exist * --icons: Allow including icons only if dependencies are present * Wrapper changes: * Drop $XDG_RUNTIME_DIR from the candidates for temporary directories * Prevent scan of unneeded directories * Drop script identification by MD5 hash * Archive-related changes: * Only extract needed files when using unzip * Allow to use renamed installers * Add support for LHA archives extraction * Engines-related changes: * New engine: ResidualVM * New engine: System-provided Mono runtime * DOSBox: Use PLAYIT_DOSBOX_BINARY in launchers if defined * Packages-related changes: * Add ability to set variables for package-specific postinst and prerm scripts * Arch Linux: Improve consistence of 32-bit packages naming * New helper functions: * version_target_is_older_than: Check if the game script target version is older than a given one * toupper: Convert files name to upper case * New generic dependency keywords: * libgdk_pixbuf-2.0.so.0 * libglib-2.0.so.0 / libgobject-2.0.so.0 * libmbedtls.so.12 * libpng16.so.16 * libopenal.so.1 (alias for "openal") * libSDL2-2.0.so.0 (alias for "sdl2") * libturbojpeg.so.0 * libuv.so.1 * libvorbisfile.so.3 (alias for "vorbis") * libz.so.1 * Codebase clean-up and improvements: * Massive rework of all message-related functions * Drop hardcoded paths for icons and .desktop launchers * Use system-specific default installation prefix for generated packages * Forcefully set errexit setting on library initialization * Use dirname/basename instead of built-in shell patterns 2.11.4 * Throw an explicit error when trying to write a launcher for a missing binary * Use safer `find | while read` constructs in prefix functions * Drop unrequired spawning of subshell by organize_data * Drop unrequired spawning of subshell by move_icons_to * Ensure $PLAYIT_WORKDIR is always an absolute path * Arch Linux: Fix bugs in dependencies handling * Debian: Fix APT version detection with APT ≥ 2.0.0 * Debian: Enforce correct permissions for packages metadata * Gentoo: Update download link for quickunpkg 2.11.3 * Fix cdrom type (file or directory) detection for DOSBox games * Use -eq instead of = for arithmetic comparisons * Update link to issues tracker * Improve handling of 7z archives extraction * Improve error messages shown when a required script dependency is missing * Spawn a terminal when calling winetricks, so its actions are no longer hidden from users not running their games from a terminal * Use convmv when available for converting file names to lower case, as it has better performances than our custom-made function * Fix argument usage for check_option_validity, and move it outside of play.it-2/src/99_init.sh * Arch Linux: Advertise ./play.it in the generated packages metadata * Arch Linux: Fix builddate entry in the generated packages metadata * Arch Linux: Improve support for libarchive implementation of tar ("bsdtar") when building packages * Debian: Improve handling of WINE dependency, thanks to Jens Reyer for the help on debian-wine mailing list * Gentoo: Fix default compression method for generated packages * Gentoo: Drop abi_x86_32 abi_x86_32 USE flag for app-emulation/winetricks dependency 2.11.2 * Do not throw an error when a script has been called with --dry-run and an icon file is not found * Move all compatibility aliases to a dedicated source file * glx meta-dependency do not install a transitional package anymore on recent Debian versions * Allow to skip control of InnoSetup version used in archive passed to innoextract * New test: Run a single game script in dry mode once for each supported archive * Check for file existence when an archive is given as argument to a game-specific script * Display an error when an unknown application type is used * When extracting multiple icons at once, ensure that they won’t override the ones extracted before them * Fix get_archive_specific_value / get_package_specific_value on corner cases, by dropping avoidable calls to eval * Use cabextract’s -L option to extract files as lowercase * Gentoo packages: bump EAPI version to 7 2.11.1 * Fix apt version detection on old versions of apt 2.11.0 * Add ability to generate packages for Gentoo, using --package=gentoo * Rewrite of launchers-related functions. Compatibility wrappers still exist for old functions * archive_guess_type: Automatically set ARCHIVE_TYPE for 7-zip archives * Nixstaller archives: Guess header length instead of using hardcoded value * Improve support for Java games * pkg_write_*: Reliably use archive-specific dependencies list when available * New option --skip-free-space-check: Bypass free space check, create temporary directories under $TMPDIR, defaults to /tmp * Fix issue with pre-2.8 compatibility code in icons_linking_postinst * move_icons_to: Fix crash when --prefix is set to a custom value 2.10.5 * select_package_architecture: Fix bug when using --architecture=64 (or --architecture=auto on 64-bit systems) on an archive providing 32-bit binaries only 2.10.4 * Improve ShellCheck tests coverage * Do not let empty dirs lingering after uninstallation using make * main script: Fix wrong usage of 'return' instead of 'exit' * main script: Update search paths for library and game scripts * help: Fix usage instructions when called from main script * packages_guess_format: Fix falling back on default package format value when host OS couldn’t be guessed * icons_get_from_path: Fail with an explicit error message if called with an empty icon path * print_instructions: Improve message readability * write_bin - init_prefix_dirs: Fix use of globbing when setting the paths to user directories 2.10.3 * write_bin - init_prefix_files: Do not ignore symbolic links in $CONFIG_PATH / $DATA_PATH when generating or updating prefix * pkg_write_deb: Always set "Multi-Arch: foreign" in packages meta-data, fixes issues when trying to install DLC for 32-bit games on 64-bit systems 2.10.2 * write_bin_run_native: Restore pre-2.10.1 version, as the 2.10.1 fix has unexpected side effects 2.10.1 * Drop pre-built library from repository * make install: Use different default paths if not run as root * Arch Linux: Add missing package to generic dependency 'gcc32' * archive_extraction_innosetup: Hide innoextract warnings during extraction * write_bin_build_wine: Always sleep 1 second after using winetricks, as some tweaks won’t apply directly if the game is called to quickly * Set input field separator to default value (space, tab, newline) * write_bin_run_native: Remove unneeded duplicated code 2.10.0 * get_tmp_dir: New function allowing to set a temporary working directory by setting $TMPDIR * New archive type 'iso', using bsdtar for data extraction * get_value: New helper function allowing to get the value of a variable using a dynamically generated name * New meta-dependencies 'libudev1' and 'theora' respectively providing libudev.so.1 and libtheora.so.0 * New archive type 'innosetup1.7', allowing to ensure that InnoExtract >= 1.7 is available * icon_file_not_found_error: New function displaying an error if some function should work on an icon file that could not be found * write_bin_run_dosbox: Improve handling of CD-ROM images for DOSBox games * Add support for bzip2 compression * Set all application-specific variables even for ScummVM games 2.9.2 * set_temp_directories: Improve the generation of the temporary work directory, dropping "mktemp --dry-run" usage 2.9.1 * extract_data_from: Do not print "OK" after innosetup variants extraction (progress already has a visual feedback) * Better handling of game updates and DLC (un)installation by automatically updating the user prefix on each game launch * meta-script: if called without argument, show usage instructions * icon_get_resolution_from_file: Fix a bug with pre 2.8 scripts where the resolution value of the first analyzed file would be kept for all files 2.9.0 * Update license file to keep track of all authors * New generic dependency: libcurl * Remove shebang from library, as it is always meant to be sourced, never directly executed * Makefile: Add ability to choose the installation path, now defaulting to /usr/local/share/games/play.it for library and scripts, and /usr/local/games for meta-script * Makefile: Do not install play.it 1.x scripts and library anymore * meta-script: Add manpage 2.8.3 * Fix typo in help(), that led to suboptimal wording when called on a script with a single supported archive (previous 2.8.2 fix was incomplete) * Add automated shellcheck tests based on GitLab CI * Improve syntax based on shellcheck 0.5.0 report 2.8.2 * icon_extract_ico_from_exe: Suppress wrestool error output * icon_get_resolution_from_file: Fix compatibility with scripts targeting library version 2.7 or older in a more robust way than what has been done in 2.8.1 * Fix typo in help(), that led to suboptimal wording when called on a script with a single supported archive 2.8.1 * Fix icon_get_resolution_from_file and icons_linking_postinst compatibility with scripts targeting library version 2.7 or older * WINE: Do not remove links to $HOME for scripts targeting library version 2.7 or older 2.8.0 * Greatly reduce time taken by play.it meta-script to identify the correct script for an archive, by using file name before falling back on MD5 hash sum * WINE: Remove most links pointing outside of the WINE prefix, to reduce $HOME clutter * Rework most icon-related code for easier understanding and maintenance * Use ImageMagick to extract .png files from .ico containers * Update dependencies automatic detection based on new icon extraction methods 2.7.5 * Fix sort_icons behaviour when icon extraction produced a single file * meta-script: Fix broken support for ./play.it 1 scripts 2.7.4 * Fix write_bin_winecfg breaking init_prefix_dirs in winecfg launcher 2.7.3 * archive_extraction_innosetup: Fix InnoSetup version test * Fix archives_get_list not detecting archives named ARCHIVE_(…)_OLD * Fix error message displayed by write_metadata when called on an unknown package 2.7.2 * Fix an inverted test in archive_get_infos that broke the MD5 integrity check again, the 2.6.2 fix having been accidentally reverted on 2.7.0 release 2.7.1 * Fix variable leak during multi-parts archive automatic handling 2.7.0 * InnoSetup archives: Check ability of available innoextract version to extract the target archive before trying to proceed with extraction * Try to guess the value of ARCHIVES_LIST if it is not set by the script * Use 'APP_ICON' as a fallback value for APP_ICONS_LIST if it is not set * Add automatic detection of multi-parts archives * New function get_package_version allowing a reliable way to use different version values for multiple packages generated from a single archive 2.6.2 * Fix an inverted test in archive_get_infos that broke the MD5 integrity check 2.6.1 * Fix postinst_icons_linking not working on more than one single app * Rework most archive-related code for easier understanding and maintenance 2.6.0 * Add ability to build only packages for a given architecture * Add automatic architecture handling to print_instructions * New function prepare_package_layout providing a wrapper for organize_data * Improve sort_icons so it can be used on single .png file produced by convert * Add --dry-run switch, running tests but not extracting data nor building packages * Use a dedicated function to guess package format to build from host OS 2.5.3 * When no supported archive is provided, add download URL to archives list * Fix variable leaking function scope when calling print_instructions * Follow symblic links when copying native game binary in user prefix 2.5.2 * Fix init_prefix_files crashing when some files exist in PATH_DATA or PATH_CONFIG with no equivalent in PATH_PREFIX 2.5.1 * Add a patch allowing to chose wether or not the desktop files should include the full path to the launcher script (default includes it and does not use $PATH, so custom values for installation prefix can be used without further tweaking) * Improve patches syntax following ShellCheck feedback * Display an error when using an invalid value for PKG * Fix error displayed if calling extract_icon_from on an unsupported file type * Improve user prefix generation * Improve library syntax following ShellCheck feedback 2.5.0 * Add support for games requiring wine-staging patches * Add support for 64-bit WINE games * Add support for Windows MSI installers * Add support for Microsoft cabinet archives * New archive type 'nullsoft-installer' * New archive type value 'innosetup_nolowercase' allowing to skip files names conversion to lower case * New archive type 'zip_unclean' * New function use_package_specific_value allowing to get package-specific value for a given variable name (if such value exists) * New function use_archive_specific_value allowing to get archive-specific value for a given variable name (if such value exists) * Add 'xrandr' and 'xgamma' to the generic dependencies * Add automatic dependencies detection for archive types 'mojosetup_unzip' and 'tar' 2.4.2 * Fix handling by init_prefix_files of files created after the first game launch 2.4.1 * Fix messed up icon path when using get_icon_from_temp_dir 2.4.0 * Add option to load regedit files on WINE prefix initialization * New function get_icon_from_temp_dir, to get an icon in .png format directly from the source archive 2.3.2 * Fix launchers broken in 2.3.1 2.3.1 * Use full path to game binary in launchers, to avoid issues when using non-default install path * Do not store temporary files outside of prefix * Drop post-run user directories clean-up, redundant with another function 2.3.0 * Improve handling of MojoSetup extraction with unzip ending with an error code * Improve handling of user directories * Add new packages to the list of generic dependencies * Add a patch allowing to change the default package compression method * Remove the need to manually clean-up package scripts * Preserve symbolic links when copying files 2.2.0 * Automatically detect archive type for *.rar files * Allow to use multiple fallback values for $ARCHIVE_PATH and $ARCHIVE_FILES * Make /tmp/play.it world-writable to allow using ./play.it more easily on shared systems * Allow to set package-specific values for $APP_OPTIONS, $APP_PRERUN and $APP_POSTRUN * Add $APP_POSTRUN support to WINE launchers * Skip building already existing packages * New distro-agnostic method to declare dependencies 2.1.1 * Fails gracefully if organize_data() is called before setting $PKG * Fix a bug where postinst_icons_linking() would erase the prerm/postinst prior content instead of appending to it * Fix a bug with wrestool, where calling it once with the --name option would have this option been carried out to all subsequent invocations 2.1.0 * First fully installable version of ./play.it * New script 'play.it' automatically loading the adequate game-specific script for the archive given in argument * Display an error when calling a script with unsupported arguments * New function easing the management of icons provided by post-installation links * New application type allowing to run native games without using a ./play.it prefix * The library can now be loaded without implying that it has been called by a ./play.it script, making it easier to use by third-party projects 2.0.3 * Fix error displayed when building .deb packages on systems without apt * Better handling of spaces in directories names when displaying installation instructions * Work around WINE bug 41639 2.0.2 * Fix a bug with the copy of the game binary in the user prefix for some native games * Work around WINE bug 29661 for WINE versions prior to 1.9.20 2.0.1 * Test the validity of options values early in the script execution to throw an error before any potentially long task * Print 'OK' on potentially long tasks completion without a visual progression indicator * If host OS auto-detection failed, display a warning before falling back on deb format * On all distributions providing apt >= 1.1, installation instructions show apt usage instead of dpkg + apt-get LICENSE0000644000000000000000000000375313120060140010554 0ustar rootrootCopyright © 2015 Antoine Le Gonidec Copyright © 2016 Mopi Copyright © 2017 Jacek Szafarkiewicz Copyright © 2017 HS-157 Copyright © 2017 Phil Morrell Copyright © 2017 Quentin Barbe Copyright © 2018 Janeene "dawnmist" Beeforth Copyright © 2018 VA Copyright © 2018 BetaRays Copyright © 2018 Julien Noblet Copyright © 2018 Andrey Butirsky Copyright © 2019 Emmanuel Gil Peyrot Copyright © 2019 Luc Didry Copyright © 2020 macaron Copyright © 2020 Hoël Bézier Copyright © 2020 quinao Copyright © 2021 Gael Le Mignot Copyright © 2022 JashinYoda Copyright © 2025 Christopher Bock Copyright © 2025 Fabrice Mouhartem Copyright © 2025 Bernd Schumacher 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.md0000644000000000000000000001600213120060140011015 0ustar rootroot# ./play.it: Installer for DRM-free commercial games The codebase is maintained at [https://git.dotslashplay.it/play.it/] Bug reports should be sent to [bugs@dotslashplay.it], and feature requests to [requests@dotslashplay.it]. [https://git.dotslashplay.it/play.it/]: https://git.dotslashplay.it/play.it/ [bugs@dotslashplay.it]: mailto:bugs@dotslashplay.it [requests@dotslashplay.it]: mailto:requests@dotslashplay.it ## Description ./play.it is a free software building native packages from installers for Windows or Linux, mainly those sold by stores focusing on DRM-free games distribution. The goal is that a game installed via ./play.it is indistinguishable from a game installed via the official repositories of your favourite distribution. The games are installed globally on multi-user systems, avoiding unnecessary duplication. The locations of save games, settings, mods, temporary files and backups are standardized with XDG Base Directory support. Packaging the games simplifies future updates, uninstalls and handling of any necessary dependencies, including integrated obsolete dependencies if specific versions are needed. ## Installation ### Distributions providing ./play.it The following distributions provide installation instructions in their official documentation: - [Debian] - [Gentoo] - [Ubuntu] (French article) [Debian]: https://wiki.debian.org/Games/PlayIt#Installation [Gentoo]: https://wiki.gentoo.org/wiki/Play.it#Installation [Ubuntu]: https://doc.ubuntu-fr.org/play.it#installation In most cases, these instructions should work in the same way for derivatives of these distributions. ### Installation from git If your distribution does not already have a package for ./play.it, you can install it from this git repository: ``` git clone --branch main --depth 1 https://git.dotslashplay.it/play.it play.it.git cd play.it.git make make install ``` ## Supported games ./play.it by itself does not provide support for any game. You need to install games collections in addition to it, each one adding support for multiple games usually sharing a same genre or thematic. The following ones are available: - [Action games collection](https://git.dotslashplay.it/games-action/about/) - [Adventure games collection](https://git.dotslashplay.it/games-adventure/about/) - [Platform games collection](https://git.dotslashplay.it/games-platform/about/) - [Puzzle games collection](https://git.dotslashplay.it/games-puzzle/about/) - [Role-playing games collection](https://git.dotslashplay.it/games-rpg/about/) - [Strategy games collection](https://git.dotslashplay.it/games-strategy/about/) - [ahubʼs collection](https://git.dotslashplay.it/berru/games-berru/about/) - [ArRay’s collection](https://git.dotslashplay.it/array/game-collection/) - [Caliban’s collection](https://git.dotslashplay.it/caliban/games-caliban/) - [vv221ʼs collection](https://git.vv221.fr/play.it-vv221/about/) - [A messy collection of games](https://git.dotslashplay.it/games-community/about/) You can check which games are supported by the collections you already have installed with the following command: ``` play.it --list-supported-games ``` ## 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 ### First contribution There is no real rule for your first contribution. You can host your updated code anywhere you like and contact us via any way described in the [Contact information] section below. It is OK to expose your updated code through GitHub or GitLab.com if you have nothing better at hand, but we would be thankful if you would instead use some hosting outside of these big silos. [Contact information]: #contact-information We do not enforce any workflow like merge/pull requests or any such thing. We are interested in the result of your work, not in how you got to it. So shatter your shackles, and for once take pleasure in working *however you like*! You are not even required to use git if you do not know or do not like this tool. Please try to follow these simple guidelines, and your contribution is probably going to be accepted quickly: - Run `make check` before submitting your code, to ensure you did not break anything by accident; - Use tabs for indentation. No real developer would ever indent anything with spaces. ### Regular contributions If you keep contributing on a more regular basis (sending 3 patches in the same year would already make you a regular) we can grant you direct write access to the repositories hosted at git.dotslashplay.it. This is not mandatory, if for some reason you can not or do not want to work with git you can simply keep following the "First contribution" guidelines, and forget about this whole "Regular contributions" section. To grant you such an access we need nothing but a public SSH key, that you can send us through any way described in the [Contact information] section below. [Contact information]: #contact-information Once you have been granted access, you should add the following to your local SSH configuration: ``` Host git.dotslashplay.it Port 1962 User gitolite3 ``` You should then update the remote of your local repository, with the following command (assuming a remote named "upstream"): ``` git remote set-url --push upstream ssh://git.dotslashplay.it/play.it ``` Since these repositories are worked on by several people, there are a couple extra guidelines that you should follow: - Your work should always be pushed to a dedicated branch, never on the main branch; - Bugfixes and messages improvements (including adding new error cases and error messages) should be pushed to branches named with a "fix/" prefix; - New features and major rewrites should be pushed to branches named with a "feature/" prefix; - You are allowed to push code to branches opened by other contributors, but please communicate with them if you plan to do so; - Force push and branches deletion are not allowed, if you want a branch to be deleted please ask us to do it for you. ## Contact information ### 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. [#play.it]: https://webchat.oftc.net/?channels=#play.it ### 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. Makefile0000644000000000000000000001124113120060140011176 0ustar rootroot# Build the library .PHONY: all clean all: lib/libplayit2.sh lib/libplayit2.sh: src/*/*.sh mkdir --parents lib find src -type f -name '*.sh' -print0 | LC_ALL=C sort -z | xargs -0 cat > lib/libplayit2.sh 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 zshdir = $(prefix)/share/zsh fishdir = $(prefix)/share/fish 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 zshdir = $(prefix)/zsh fishdir = $(prefix)/fish 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: uninstall-completion 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 # Install shell completion .PHONY: install-completion-all install-completion-zsh install-completion-fish uninstall-completion install-completion-all: install-completion-zsh install-completion-fish install-completion-zsh: install -D --mode=644 shell-completions/zsh/_play.it $(DESTDIR)$(zshdir)/vendor-completions/_play.it install-completion-fish: install -D --mode=644 shell-completions/fish/play.it.fish $(DESTDIR)$(fishdir)/vendor_completions.d/play.it.fish uninstall-completion: rm --force $(DESTDIR)$(zshdir)/vendor-completions/_play.it rm --force $(DESTDIR)$(fishdir)/vendor_completions.d/play.it.fish test -d $(DESTDIR)$(zshdir)/vendor-completions && \ rmdir --parents --ignore-fail-on-non-empty $(DESTDIR)$(zshdir)/vendor-completions || true test -d $(DESTDIR)$(fishdir)/vendor_completions.d && \ rmdir --parents --ignore-fail-on-non-empty $(DESTDIR)$(fishdir)/vendor_completions.d || true # 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/fr/man6/play.it.6 shell-completions/zsh/_play.it shell-completions/fish/play.it.fish src/*/*.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/fr/man6/play.it.6 \ shell-completions/zsh/_play.it \ shell-completions/fish/play.it.fish \ src/*/*.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 man-syntax check: shellcheck-library shellcheck-wrapper shunit2 man-syntax ## This is a unicode quote. Delete and retype it (or ignore/doublequote for literal). shellcheck-library: SHELLCHECK_EXCLUDE += --exclude=SC1112 ## Expressions don't expand in single quotes, use double quotes for that. shellcheck-library: SHELLCHECK_EXCLUDE += --exclude=SC2016 ## Double quote to prevent globbing and word splitting. shellcheck-library: SHELLCHECK_EXCLUDE += --exclude=SC2086 ## In POSIX sh, local is undefined. shellcheck-library: SHELLCHECK_EXCLUDE += --exclude=SC3043 shellcheck-library: shellcheck --shell=sh $(SHELLCHECK_EXCLUDE) lib/libplayit2.sh shellcheck-wrapper: shellcheck --external-sources --shell=sh play.it SHUNIT2_SCRIPTS := $(wildcard tests/shunit2/*/*.sh) SHUNIT2_TESTS := $(addprefix shunit2_, $(SHUNIT2_SCRIPTS)) .PHONY: $(SHUNIT2_TESTS) shunit2: $(SHUNIT2_TESTS) $(SHUNIT2_TESTS): shunit2_%: % shunit2 $< man-syntax: 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.it0000755000000000000000000000301613120060140011045 0ustar rootroot#!/bin/sh set -o errexit 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 init_fail_as_root # Set the default and current options init_options "$@" # Display the help message, if called with no argument if [ $# -eq 0 ]; then help exit 0 fi # If some early action is set, run it then exit init_early_actions # Exit with an error if no archive is provided if [ -z "${SOURCE_ARCHIVE_PATH:-}" ]; then error_archive_missing_from_arguments exit 1 fi # Set the path to the working directory, # so it can be used to cash the result of MD5 hashsums computations init_working_directory # Locate a game script by file name (and MD5 hash in case of names collision) FILE_NAME=$(archive_name 'SOURCE_ARCHIVE') GAME_SCRIPT=$(games_find_script_for_archive "$FILE_NAME") # Load the game script ## PLAYIT_WORKDIR is passed to the new process to keep the cached computed MD5 hashes. exec \ env PLAYIT_WORKDIR="$PLAYIT_WORKDIR" \ "$GAME_SCRIPT" "$@" man/man6/play.it.60000644000000000000000000001241213120060140012622 0ustar rootroot.Dd $Mdocdate$ .Dt ./play.it 6 .Os .Sh NAME .Nm ./play.it .Nd Installer for drm-free commercial games .Sh SYNOPSIS .Nm .Op Fl \-checksum Cm md5 | Cm none .Op Fl \-collection\-path Ar path .Op Fl \-compression Cm none | Cm speed | Cm size | Cm auto .Op Fl \-config\-file Ar path .Op Fl \-no\-free\-space\-check .Op Fl \-no\-icons .Op Fl \-no\-mtree .Op Fl \-output\-dir Ar path .Op Fl \-overwrite .Op Fl \-package Cm arch | Cm deb | Cm gentoo .Op Fl \-prefix Ar path .Op Fl \-tmpdir Ar path .Ar archive .Nm .Op Fl \-collection\-path Ar path .Op Fl \-list\-packages .Op Fl \-list\-requirements .Op Fl \-show\-game\-script .Ar archive .Nm .Op Fl \-collection\-path Ar path .Op Fl \-list\-available\-scripts .Op Fl \-list\-supported\-games .Op Fl \-version .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 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 .gpkg.tar package (Gentoo) .El .It Fl \-no\-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 \-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 \-collection\-path Ar path Limit the search for game scripts to a given path. .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) .Bl -tag -compact .It \-\-compression speed Use gzip compression. .It \-\-compression size Use xz compression. .El man/fr/man6/play.it.60000644000000000000000000001475713120060140013247 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 .Op Fl \-checksum Cm md5 | Cm none .Op Fl \-collection\-path Ar chemin .Op Fl \-compression Cm none | Cm speed | Cm size | Cm auto .Op Fl \-config\-file Ar chemin .Op Fl \-no\-free\-space\-check .Op Fl \-no\-icons .Op Fl \-no\-mtree .Op Fl \-output\-dir Ar chemin .Op Fl \-overwrite .Op Fl \-package Cm arch | Cm deb | Cm gentoo .Op Fl \-prefix Ar chemin .Op Fl \-tmpdir Ar chemin .Ar archive .Nm .Op Fl \-collection\-path Ar chemin .Op Fl \-list\-packages .Op Fl \-list\-requirements .Op Fl \-show\-game\-script .Ar archive .Nm .Op Fl \-collection\-path Ar chemin .Op Fl \-list\-available\-scripts .Op Fl \-list\-supported\-games .Op Fl \-version .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 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 .gpkg.tar (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 \-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 \-collection\-path Ar chemin Limiter la recherche de scripts de prise en charge de jeux au chemin donné. .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) .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. .El shell-completions/zsh/_play.it0000644000000000000000000000265413120060140015455 0ustar rootroot#compdef play.it # Copyright © 2025 Christopher Bock # SPDX-License-Identifier: BSD 2-Clause "Simplified" License local -a arguments arguments=( '--collection-path[Limit the search for game scripts to path]: :_files -/' - set1 '--checksum[archive integrity verification method]: :_values "checksum" md5 none' '--compression[packages compression method]: :_values "compression" auto size speed none' '--config-file[set configuration]: :_files' '--no-free-space-check[do not check for free space]' '--no-icons[do not include game icons]' '--no-mtree[do not create .mtree in arch packages]' '--output-dir[directory for generated packages]: :_files -/' '--overwrite[replace package if present]' '--package[package type]: :_values "package type" arch deb gentoo' '--prefix[game installation path]: :_files -/' '--tmpdir[temporary directory]: :_path_files -/' - set2 '(: -)--show-game-script[show script name for given archive]: :_files' '(: -)--list-packages[print list of required command to build package for archive]: :_files' '(: -)--list-requirements[print the list of commands required to build packages from the given archive]: :_files' '(: -)--list-available-scripts[print the list of game scripts available on this system]' '(: -)--list-supported-games[print the list of supported games]' '(: -)--help[show help]' '(: -)--version[show version]' ) _arguments -A "--*" "${arguments[@]}" '*: :_files' shell-completions/fish/play.it.fish0000644000000000000000000000257213120060140016372 0ustar rootrootcomplete -c play.it -l checksum -x -a "md5 none" -d "Archive integrity verification method" complete -c play.it -l collection-path -r -d "Limit the search for game scripts to given path" complete -c play.it -l compression -x -a "none speed size auto" -d "Generated packages compression method" complete -c play.it -l config-file -r -d "Configuration file path" complete -c play.it -l no-free-space-check -f -d "Do not check for free space" complete -c play.it -l no-icons -f -d "Do not include game icons" complete -c play.it -l no-mtree -f -d "Do not create .MTREE in Arch Linux packages" complete -c play.it -l output-dir -r -d "Output directory for generated packages" complete -c play.it -l overwrite -f -d "Replace existing packages" complete -c play.it -l package -x -a "arch deb gentoo" -d "Generated package type" complete -c play.it -l prefix -x -d "Game installation path" complete -c play.it -l tmpdir -r -d "Temporary files directory" complete -c play.it -l list-packages -f -d "Print the list of packages to build" complete -c play.it -l list-requirements -f -d "Print the list of commands required to build packages" complete -c play.it -l show-game-script -f -d "Display game script instead of running it" complete -c play.it -l list-available-scripts -f -d "Print the list of available game scripts" complete -c play.it -l list-supported-games -f -d "Print the list of supported games" src/00_header/00_license-and-version.sh0000644000000000000000000000400213120060140016551 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 bugs@dotslashplay.it ### LIBRARY_VERSION=2.34.1 ## TODO: The revision number should be appended to play.it --version output. # shellcheck disable=SC2034 library_revision=20251209.1 src/10_common/00_common.sh0000644000000000000000000000616313120060140014247 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() { local directory convmv_options find_options directory="$1" convmv_options='-f utf8 --notest --lower -r' find_options='-mindepth 1 -maxdepth 1' # shellcheck disable=SC2086 find "$directory" $find_options -exec \ convmv $convmv_options {} + >/dev/null 2>&1 } # 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() { local convmv_options find_options directory directory="$1" convmv_options='-f utf8 --notest --upper -r' find_options='-mindepth 1 -maxdepth 1' # shellcheck disable=SC2086 find "$directory" $find_options -exec \ convmv $convmv_options {} + >/dev/null 2>&1 } # 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 } # 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.sh0000644000000000000000000000644113120060140014217 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 } src/10_common/10_compatibility-level.sh0000644000000000000000000000661413120060140016737 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 compatibility_level=$(compatibility_level_legacy) else ## The bugfix version number is trimmed, to get a value respecting the expected "major.minor" format. compatibility_level="${LIBRARY_VERSION%.*}" fi # Check the validity of the compatibility level format local regexp regexp='^[1-9][0-9]*\.[0-9]\+$' if ! printf '%s' "$compatibility_level" | grep --quiet --regexp="$regexp"; then error_invalid_compatibility_level "$compatibility_level" return 1 fi printf '%s' "$compatibility_level" } # Get the compatibility level from the legacy variable # USAGE: compatibility_level_legacy # RETURN: the requested compatibility level, # displaying a warning if it is high enough that the legacy variable should no longer be used compatibility_level_legacy() { ## $target_version should be set when this function is called, ## if this is not the case it is OK to crash here because of the "nounset" shell setting local compatibility_level compatibility_level="$target_version" ## compatibility_level_is_at_least can not be used here, because it would rely on the current function local compatibility_level_major compatibility_level_minor compatibility_level_major=$(printf '%s' "$compatibility_level" | cut --delimiter='.' --fields=1) compatibility_level_minor=$(printf '%s' "$compatibility_level" | cut --delimiter='.' --fields=2) if [ "$compatibility_level_major" -eq 2 ] && [ "$compatibility_level_minor" -ge 26 ] then warning_deprecated_variable 'target_version' 'PLAYIT_COMPATIBILITY_LEVEL' fi printf '%s' "$compatibility_level" } # Check the compatibility level against a given version # USAGE: compatibility_level_is_at_least $minimum_version # RETURN: 0 if the compatibility level is equal or superior to the given level, # 1 if the compatibility level is inferior to the given level compatibility_level_is_at_least() { local minimum_version compatibility_level minimum_version="$1" if ! compatibility_level=$(compatibility_level); then ## Stop the script execution if the compatibility level is not set to a valid value. ## This needs to be done explicitly because this functions is called from tests. exit 1 fi # Compare major version fields local minimum_version_major compatibility_level_major minimum_version_major=$(printf '%s' "$minimum_version" | cut --delimiter='.' --fields=1) compatibility_level_major=$(printf '%s' "$compatibility_level" | cut --delimiter='.' --fields=1) if [ "$compatibility_level_major" -lt "$minimum_version_major" ]; then return 1 elif [ "$compatibility_level_major" -gt "$minimum_version_major" ]; then return 0 else # Compare minor version fields local minimum_version_minor compatibility_level_minor minimum_version_minor=$(printf '%s' "$minimum_version" | cut --delimiter='.' --fields=2) compatibility_level_minor=$(printf '%s' "$compatibility_level" | cut --delimiter='.' --fields=2) if [ "$compatibility_level_minor" -lt "$minimum_version_minor" ]; then return 1 elif [ "$compatibility_level_minor" -ge "$minimum_version_minor" ]; then return 0 fi fi } src/10_common/10_filters.sh0000644000000000000000000000115013120060140014417 0ustar rootroot# Clean-up the output of a command returning a list # USAGE: … | list_clean # RETURN: a list including only unique lines list_clean() { ## - remove leading and trailing spaces ## - sort the list and merge duplicate entries ## - remove empty lines, ignore errors on empty output sed 's/^\s*//;s/\s*$//' | sort --unique | grep --invert-match --regexp='^$' || true } # Clean-up the output of a command printing a snippet # USAGE: … | snippet_clean # RETURN: a snippet indented with tabulations snippet_clean() { ## - convert the series of 4 spaces to tabulations sed --regexp-extended 's/( ){4}/\t/g' } src/10_common/10_variables-manipulation.sh0000644000000000000000000000110013120060140017410 0ustar rootroot# Expand the given variable name and print its value # USAGE: get_value $variable_name # RETURN: the variable value get_value() { local variable_name variable_name="$1" eval printf -- '%s' '"${'"${variable_name}"':-}"' } # Check if the given varible is unset or set to an empty value. # USAGE: variable_is_empty $variable_name # RETURN: 0 if the variable is unset or empty, 1 if it is set to a non empty value variable_is_empty() { local variable_name variable_name="$1" local variable_value variable_value=$(get_value "$variable_name") test -z "$variable_value" } src/10_common/20_temporary-directories.sh0000644000000000000000000000651413120060140017315 0ustar rootroot# 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 } # Delete the temporary directory # USAGE: working_directory_cleanup working_directory_cleanup() { # Throw an error if the path to the temporary directory is not set yet if [ -z "${PLAYIT_WORKDIR:-}" ]; then error_working_directory_unset exit 1 fi rm --force --recursive "${PLAYIT_WORKDIR:-}" } src/10_common/90_messages.sh0000644000000000000000000003634613120060140014605 0ustar rootroot# Information - All the packages are already built, there is no need to run a new build # USAGE: info_all_packages_already_built info_all_packages_already_built() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Tous les paquets sont déjà présents, il nʼy a pas besoin de les reconstruire.\n' ;; ('en'|*) message='All the packages are already built, there is no need to run a new build.\n' ;; esac print_message 'info' "$message" } # Error - The given path is not a file # USAGE: error_not_a_file $path error_not_a_file() { local path path="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='"%s" nʼest pas un fichier valide.\n' ;; ('en'|*) message='"%s" is not a valid file.\n' ;; esac print_message 'error' "$message" \ "$path" } # Error - The available tar implementation is not supported # USAGE: error_unknown_tar_implementation error_unknown_tar_implementation() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La version de tar présente sur ce système nʼest pas reconnue.\n' message="$message"'./play.it ne peut utiliser que GNU tar ou bsdtar.\n' ;; ('en'|*) message='The tar implementation on this system wasnʼt recognized.\n' message="$message"'./play.it can only use GNU tar or bsdtar.\n' ;; esac print_message 'error' "$message" } # Error - The given path is not a directory # USAGE: error_not_a_directory $path error_not_a_directory() { local path path="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='"%s" nʼest pas un répertoire.\n' ;; ('en'|*) message='"%s" is not a directory.\n' ;; esac print_message 'error' "$message" \ "$path" } # Error - The given path is not writable # USAGE: error_not_writable $path error_not_writable() { local path path="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='"%s" nʼest pas accessible en écriture.\n' ;; ('en'|*) message='"%s" is not writable.\n' ;; esac print_message 'error' "$message" \ "$path" } # Error - A required command is missing # USAGE: error_unavailable_command $function $required_command error_unavailable_command() { local function required_command function="$1" required_command="$2" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La commande "%s" nʼest pas disponible, mais elle est requise par la fonction "%s".\n' ;; ('en'|*) message='"%s" command is not available, but it is required by function "%s".\n' ;; esac print_message 'error' "$message" \ "$required_command" \ "$function" } # Error - The calling script is not compatible with the provided library version # USAGE: error_incompatible_versions error_incompatible_versions() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Ce script nʼest pas compatible avec la version fournie de la bibliothèque ./play.it.\n' message="$message"'La bibliothèque utilisée fournit actuellement la version %s, mais le script attend une version ≥ %s et < %s.\n' ;; ('en'|*) message='This script is not compatible with the provided ./play.it library version.\n' message="$message"'The library in use currently provides version %s, but the script expects a version ≥ %s and < %s.\n' ;; esac local compatibility_level version_major_minimum compatibility_level=$(compatibility_level) version_major_minimum=$(printf '%s' "$compatibility_level" | cut --delimiter='.' --fields=1) print_message 'error' "$message" \ "$LIBRARY_VERSION" \ "$compatibility_level" \ "$((version_major_minimum + 1)).0" } # Error - The wrapper has been called with no archive argument # USAGE: error_archive_missing_from_arguments error_archive_missing_from_arguments() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Aucune archive nʼa été fournie sur la ligne de commande.\n' ;; ('en'|*) message='No archive has been provided on the command line.\n' ;; esac print_message 'error' "$message" } # Error - No game script has been found for the given archive # USAGE: error_no_script_found_for_archive $archive error_no_script_found_for_archive() { local archive archive="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Impossible de trouver un script pour le fichier %s\n' ;; ('en'|*) message='Could not find script for file %s\n' ;; esac print_message 'error' "$message" \ "$archive" } # Error - A variable is spanning multiple lines # USAGE: error_variable_multiline $variable_name error_variable_multiline() { local variable_name variable_name="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La valeur %s sʼétend sur plusieurs lignes, ce qui nʼest pas autorisé.\n' ;; ('en'|*) message='%s value is spanning multiple lines, but this is not allowed.\n' ;; esac print_message 'error' "$message" \ "$variable_name" } # Error - A type-restricted function has been called on the wrong application type # USAGE: error_application_wrong_type $function_name $application_type error_application_wrong_type() { local function_name application_type function_name="$1" application_type="$2" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='%s ne peut pas être appelée sur les applications utilisant le type "%s".\n' ;; ('en'|*) message='%s can not be called on applications using "%s" type.\n' ;; esac print_message 'error' "$message" \ "$function_name" \ "$application_type" } # Error - The directory for temporary files storage does not exist # USAGE: error_temporary_path_not_a_directory $temporary_directory_path error_temporary_path_not_a_directory() { local temporary_directory_path temporary_directory_path="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le chemin demandé pour stocker les fichiers temporaires nʼexiste pas' message="$message"', ou nʼest pas un répertoire : %s\n' message="$message"'Un chemin alternatif peut être fourni avec --tmpdir.\n' ;; ('en'|*) message='The path set for temporary files storage does not exist' message="$message"', or is not a directory: %s\n' message="$message"'An alternative path can be provided with --tmpdir.\n' ;; esac print_message 'error' "$message" \ "$temporary_directory_path" } # Error - The directory for temporary files storage has no write access # USAGE: error_temporary_path_not_writable $temporary_directory_path error_temporary_path_not_writable() { local temporary_directory_path temporary_directory_path="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le chemin demandé pour stocker les fichiers temporaires nʼest pas accessible en écriture : %s\n' message="$message"'Un chemin alternatif peut être fourni avec --tmpdir.\n' ;; ('en'|*) message='The path set for temporary files storage has no write access: %s\n' message="$message"'An alternative path can be provided with --tmpdir.\n' ;; esac print_message 'error' "$message" \ "$temporary_directory_path" } # Error - The directory for temporary files storage is not case-sensitive # USAGE: error_temporary_path_not_case_sensitive $temporary_directory_path error_temporary_path_not_case_sensitive() { local temporary_directory_path temporary_directory_path="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le chemin demandé pour stocker les fichiers temporaires est sur un système de fichiers qui nʼest pas sensible à la casse des noms de fichiers : %s\n' message="$message"'Un chemin alternatif peut être fourni avec --tmpdir.\n' ;; ('en'|*) message='The path set for temporary files storage is on a case-insensitive file system: %s\n' message="$message"'An alternative path can be provided with --tmpdir.\n' ;; esac print_message 'error' "$message" \ "$temporary_directory_path" } # Error - The directory for temporary files storage has no support for UNIX permissions # USAGE: error_temporary_path_no_unix_permissions $temporary_directory_path error_temporary_path_no_unix_permissions() { local temporary_directory_path temporary_directory_path="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le chemin demandé pour stocker les fichiers temporaires ne prend pas en charge les permissions UNIX : %s\n' message="$message"'Un chemin alternatif peut être fourni avec --tmpdir.\n' ;; ('en'|*) message='The path set for temporary files storage has no support for UNIX permissions: %s\n' message="$message"'An alternative path can be provided with --tmpdir.\n' ;; esac print_message 'error' "$message" \ "$temporary_directory_path" } # Error - The directory for temporary files storage is mounted with noexec # USAGE: error_temporary_path_noexec $temporary_directory_path error_temporary_path_noexec() { local temporary_directory_path temporary_directory_path="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le chemin demandé pour stocker les fichiers temporaires ne permet pas la création de fichiers exécutables : %s\n' message="$message"'Un chemin alternatif peut être fourni avec --tmpdir.\n' ;; ('en'|*) message='The path set for temporary files storage forbid the creation of executable files: %s\n' message="$message"'An alternative path can be provided with --tmpdir.\n' ;; esac print_message 'error' "$message" \ "$temporary_directory_path" } # Error - There is not enough free space in the directory for temporary files storage # USAGE: error_temporary_path_not_enough_space $temporary_directory_path error_temporary_path_not_enough_space() { local temporary_directory_path temporary_directory_path="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le chemin demandé pour stocker les fichiers temporaires ne dispose pas dʼassez dʼespace libre : %s\n' message="$message"'Un chemin alternatif peut être fourni avec --tmpdir.\n' message="$message"'Cette vérification de lʼespace libre peut aussi être contournée avec --no-free-space-check.\n' ;; ('en'|*) message='The path set for temporary files storage has not enough free space: %s\n' message="$message"'An alternative path can be provided with --tmpdir.\n' message="$message"'This free space check can also be disabled using --no-free-space-check.\n' ;; esac print_message 'error' "$message" \ "$temporary_directory_path" } # Error - A mandatory variable is not set # USAGE: error_missing_variable $variable_name error_missing_variable() { local variable_name variable_name="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La variable suivante est requise, mais elle nʼa pas été définie ou sa valeur est nulle : %s\n' ;; ('en'|*) message='The following variable is mandatory, but it is unset or its value is null: %s\n' ;; esac print_message 'error' "$message" \ "$variable_name" } # Error - ./play.it should not be run with the root account # USAGE: error_run_as_root error_run_as_root() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='./play.it ne doit pas être exécuté par le compte root.\n' ;; ('en'|*) message='./play.it should not be executed by the root account.\n' ;; esac print_message 'error' "$message" } # Error - The current archive identifier uses an invalid format. # USAGE: error_current_archive_format_invalid $archive error_current_archive_format_invalid() { local archive archive="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Lʼidentifiant dʼarchive "%s" ne respecte pas le format attendu: ARCHIVE_BASE_xxx\n' ;; ('en'|*) message='The archive identifier "%s" does not follow the expected format: ARCHIVE_BASE_xxx\n' ;; esac print_message 'error' "$message" \ "$archive" } # Error - The current package identifier uses an invalid format. # USAGE: error_current_package_format_invalid $package error_current_package_format_invalid() { local package package="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Lʼidentifiant de paquet "%s" ne respecte pas le format attendu: PKG_xxx\n' ;; ('en'|*) message='The package identifier "%s" does not follow the expected format: PKG_xxx\n' ;; esac print_message 'error' "$message" \ "$package" } # Error - The current package identifier is not included in the list of packages to build. # USAGE: error_current_package_not_in_list $package error_current_package_not_in_list() { local package package="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Lʼidentifiant de paquet "%s" ne fait pas partie de la liste de paquets à construire.\n' ;; ('en'|*) message='The package identifier "%s" is not included in the list of packages that should be built.\n' ;; esac print_message 'error' "$message" \ "$package" } # Error - The compatibility level of the current game script is set to an invalid value # USAGE: error_invalid_compatibility_level $compatibility_level error_invalid_compatibility_level() { local compatibility_level compatibility_level="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le niveau de compatibilité du script courant est défini à une valeur invalide : "%s"\n' message="$message"'Le format attendu est "version_majeure.version_mineure", par exemple "2.29".\n' ;; ('en'|*) message='The compatibility level of the current script is set to an invalid value: "%s"\n' message="$message"'The expected format is "major_version.minor_version", for example "2.29".\n' ;; esac print_message 'error' "$message" \ "$compatibility_level" } # Error - The working directory is not set, but its removal has been required # USAGE: error_working_directory_unset error_working_directory_unset() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le chemin vers le répertoire de travail temporaire n’est pas encore défini, mais sa suppression a déjà été demandée.\n' ;; ('en'|*) message='The path to the temporary working directory is not set yet, but its removal has already been required.\n' ;; esac print_message 'error' "$message" } src/10_context/00_environment_archives.sh0000644000000000000000000000133513120060140017377 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 error_current_archive_format_invalid "$archive" ## exit is used instead of return to ensure a failure when called from a subshell. exit 1 fi export PLAYIT_CONTEXT_ARCHIVE="$archive" } # Print the identifier of the current archive # An empty string is returned if no archive is currently set. # USAGE: current_archive current_archive() { printf '%s' "${PLAYIT_CONTEXT_ARCHIVE:-}" } src/10_context/00_environment_packages.sh0000644000000000000000000000467513120060140017363 0ustar rootroot# Set the identifier of the current package # USAGE: set_current_package $package set_current_package() { local package package="$1" # Check that the identifier uses the expected PKG_xxx format. local regexp regexp='^PKG\(_[0-9A-Z]\+\)\+$' if ! printf '%s' "$package" | grep --quiet --regexp="$regexp"; then error_current_package_format_invalid "$package" ## exit is used instead of return to ensure a failure when called from a subshell. exit 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" ## exit is used instead of return to ensure a failure when called from a subshell. exit 1 fi export PLAYIT_CONTEXT_PACKAGE="$package" } # Set the identifier of the default package # USAGE: set_default_package $package set_default_package() { local package package="$1" # Check that the identifier uses the expected PKG_xxx format. local regexp regexp='^PKG\(_[0-9A-Z]\+\)\+$' if ! printf '%s' "$package" | grep --quiet --regexp="$regexp"; then error_current_package_format_invalid "$package" return 1 fi # Check that the identifier is included in the list of packages to build. if ! package_is_included_in_packages_list "$package"; then error_current_package_not_in_list "$package" return 1 fi export PLAYIT_CONTEXT_PACKAGE_DEFAULT="$package" } # Print the identifier of the current package. # USAGE: current_package # RETURN: the current package identifier, # or an empty string if none is set current_package() { # To ensure backwards-compatibility, the legacy variable should have a higher priority than the modern one. # Otherwise the ability to set the context using $PKG from game scripts would be lost as soon as the library calls set_current_package. local package package="${PKG:-}" if [ -n "$package" ] && compatibility_level_is_at_least '2.27' then warning_context_legacy_package fi if [ -z "$package" ]; then package="${PLAYIT_CONTEXT_PACKAGE:-}" fi # If no package context is explicitly set, set it to the first package in the list of packages to build. if [ -z "$package" ]; then package=$(default_package) fi printf '%s' "$package" } # Print the identifier of the default package. # USAGE: default_package # RETURN: the default package identifier, # or an empty string if none is set default_package() { printf '%s' "${PLAYIT_CONTEXT_PACKAGE_DEFAULT:-}" } src/10_context/10_names.sh0000644000000000000000000000701513120060140014254 0ustar rootroot# Print the suffix of the identifier of the current archive. # USAGE: current_archive_suffix # RETURN: the current archive identifier suffix including the leading underscore, # or an empty string if no archive is set current_archive_suffix() { local archive archive=$(current_archive) printf '%s' "${archive#ARCHIVE_BASE}" } # Print the suffix of the identifier of the current package. # USAGE: current_package_suffix # RETURN: the current package identifier suffix including the leading underscorem # or an empty string of no current nor default package is set 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" # Try to find an archive-specific value for the given variable. local context_name_archive context_name_archive=$(context_name_archive "$variable_name") if [ -n "$context_name_archive" ]; then printf '%s' "$context_name_archive" return 0 fi # Try to find a package-specific value for the given variable. local context_name_package context_name_package=$(context_name_package "$variable_name") if [ -n "$context_name_package" ]; then printf '%s' "$context_name_package" return 0 fi # Check if the base variable value is set. if ! variable_is_empty "$variable_name"; then printf '%s' "$variable_name" return 0 fi # If no value has been found for the given variable, an empty string is returned. } # Print the name of the variable containing the archive-specific value of the given variable # USAGE: context_name_archive $variable_name # RETURN: the name of the variable containing the archive-specific value, # or an empty string context_name_archive() { local variable_name variable_name="$1" local current_archive_suffix current_archive_suffix=$(current_archive_suffix) # Return early if no archive context is set if [ -z "$current_archive_suffix" ]; then return 0 fi local current_archive_name while [ -n "$current_archive_suffix" ]; do current_archive_name="${variable_name}${current_archive_suffix}" if ! variable_is_empty "$current_archive_name"; then printf '%s' "$current_archive_name" return 0 fi current_archive_suffix="${current_archive_suffix%_*}" done # If no value has been found for the given variable, an empty string is returned. } # Print the name of the variable containing the package-specific value of the given variable # USAGE: context_name_package $variable_name # RETURN: the name of the variable containing the package-specific value, # or an empty string context_name_package() { local variable_name variable_name="$1" local current_package_suffix current_package_suffix=$(current_package_suffix) # Return early if no package context is set if [ -z "$current_package_suffix" ]; then return 0 fi local current_package_name while [ -n "$current_package_suffix" ]; do current_package_name="${variable_name}${current_package_suffix}" if ! variable_is_empty "$current_package_name"; then printf '%s' "$current_package_name" return 0 fi current_package_suffix="${current_package_suffix%_*}" done # If no value has been found for the given variable, an empty string is returned. } src/10_context/20_values.sh0000644000000000000000000000105313120060140014445 0ustar rootroot# Print the context-sensitive value for the given variable # Context priority order is the following one: # - archive-specific # - package-specific # - default # - empty # USAGE: context_value $variable_name # RETURN: the context-sensitive value of the given variable, # or an empty string context_value() { local variable_name variable_name="$1" local context_name context_name=$(context_name "$variable_name") # Return early if this variable has no set value. if [ -z "$context_name" ]; then return 0 fi get_value "$context_name" } src/10_games/10_scripts.sh0000644000000000000000000001472113120060140014252 0ustar rootroot# Print a list of directories to scan for game scripts # Each path is printed on its own line. # USAGE: games_list_sources games_list_sources() { # If an explicit path has been set, limit the search to this path local option_collection_path option_collection_path=$(option_value 'collection-path') if [ -n "$option_collection_path" ]; then printf '%s' "$option_collection_path" return 0 fi # Include the current user game scripts collections local user_collections_basedir user_collections_basedir="${XDG_DATA_HOME:=$HOME/.local/share}/play.it/collections" 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/collections" if [ -d "$system_collections_basedir" ]; then find "$system_collections_basedir" -mindepth 1 -maxdepth 1 -type d | sort fi done # Look for game scripts in the legacy "games" paths ## These have a lower priority than the "collections" paths. 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 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) # Throw an error if no source of game scripts is found if [ -z "$games_sources" ]; then error_no_collection return 1 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" # games_list_scripts_all is called on its own first, so its error code is not hidden by the pipe local scripts_list scripts_list=$(games_list_scripts_all) # grep error status in case of no game script found is ignored printf '%s' "$scripts_list" | xargs --no-run-if-empty grep \ --files-with-matches \ --regexp="^ARCHIVE_[0-9A-Z_]\\+=['\"]${archive_name}['\"]" || true } # 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") # Throw an error if no game script is found for the given archive if [ -z "$scripts_list" ]; then error_no_script_found_for_archive "$archive_name" ## Remove the empty working directory rmdir "$PLAYIT_WORKDIR" return 1 fi # If multiple scripts have been found with support for a given archive name, # run some extra tests to find the correct one if [ "$(printf '%s' "$scripts_list" | wc --lines)" -gt 1 ]; then local hash_reference archive_identifiers archive_identifier archive_hash while read -r script; do # Parse the archive identifiers from the game script # Multiple identifiers can be found if several archives of the given name are supported by the current script archive_identifiers=$(sed --silent --regexp-extended "s/^(ARCHIVE_BASE(_[A-Z0-9]+)*_[0-9]+)_NAME=['\"]?${archive_name}['\"]?$/\\1/p" "$script") # If no identifier is found, try the legacy variable without the "_NAME" suffix if [ -z "$archive_identifiers" ]; then archive_identifiers=$(sed --silent --regexp-extended "s/^(ARCHIVE_BASE(_[A-Z0-9]+)*_[0-9]+)=['\"]?${archive_name}['\"]?$/\\1/p" "$script") fi for archive_identifier in $archive_identifiers; do # Parse the archive hash from the game script ## FIXME: Fix hash parsing for archives with multiple valid hashes. archive_hash=$(sed --silent --regexp-extended "s/^${archive_identifier}_MD5=['\"]?([0-9a-f]+)['\"]?$/\\1/p" "$script") # If this is the first archive of the list, set its hash as the reference if [ -z "${hash_reference:-}" ]; then hash_reference="$archive_hash" fi # If the hash is distinct from the reference, we need to run a hash comparison to find the correct script if [ "$archive_hash" != "$hash_reference" ]; then game_find_script_by_hash "$scripts_list" return 0 fi done done <<- EOL $(printf '%s' "$scripts_list") EOL fi printf '%s' "$scripts_list" | head --lines=1 } # Discriminate between multiple game scripts using the archive hash # USAGE: game_find_script_by_hash $scripts_list # RETURN: the path to a single game script game_find_script_by_hash() { # The list of scripts that should be parsed, separated by line breaks local scripts_list scripts_list="$1" # Compute the hash of the current archive local archive_name archive_hash archive_name=$(archive_name 'SOURCE_ARCHIVE') archive_hash=$(archive_hash_md5_computed "$archive_name") local regexp regexp="^ARCHIVE_BASE(_[A-Z0-9]+)*_[0-9]+_MD5=['\"]?${archive_hash}['\"]?$" printf '%s' "$scripts_list" | xargs grep \ --files-with-matches \ --extended-regexp --regexp="$regexp" | 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.sh0000644000000000000000000000244513120060140013660 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_list) local archive game_id game_name for archive in $archives_list; do # Using a subshell to prevent the context archive setting from leaking outside of this function is not required 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. unset PLAYIT_COMPATIBILITY_LEVEL unset target_version printf '%s' "$scripts_list" | xargs --delimiter='\n' --max-args=1 --max-procs="$available_threads" sh | sort --unique } src/10_games/90_messages.sh0000644000000000000000000000334313120060140014400 0ustar rootroot# Error - $script_version does not follow the expected format # USAGE: error_invalid_version_string $version_string error_invalid_version_string() { local version_string version_string="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La valeur suivante de $script_version ne respecte pas le format attendu : %s\n' message="$message"'Le format correct est "YYYYMMDD.N", avec "YYYYMMDD" la date de dernière édition du script, et "N" un nombre incrémenté si le script est édité plusieurs fois dans la même journée.\n' ;; ('en'|*) message='The following $script_version value does not follow the expected format: %s\n' message="$message"'The correct format is "YYYYMMDD.N", with "YYYYMMDD" the date of the last edition of the script, and "N" a number incremented if the script is edited multiple times at the same date.\n' ;; esac print_message 'error' "$message" \ "$version_string" } # Error - No source of game scripts could be found # USAGE: error_no_collection error_no_collection() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Aucune collection ajoutant la prise en charge de jeux n’a été trouvée.\n' message="$message"'Des instructions pour en ajouter peuvent se trouver sur la page suivante :\n' message="$message"'%s\n' ;; ('en'|*) message='No collection adding support for games has been found.\n' message="$message"'Installation instructions for games collections can be found on the following page:\n' message="$message"'%s\n' ;; esac print_message 'error' "$message" \ 'https://git.dotslashplay.it/scripts/about/#game-scripts' } src/10_help/00_help.sh0000644000000000000000000002621313120060140013345 0ustar rootroot# display full usage instructions # USAGE: help help() { local script_name script_name=$(basename "$0") # print general usage instructions local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') if [ "$script_name" = 'play.it' ]; then message='\nUtilisation : %s ARCHIVE [OPTION]…\n\n' else message='\nUtilisation : %s [OPTION]… [ARCHIVE]\n\n' fi ;; ('en'|*) if [ "$script_name" = 'play.it' ]; then message='\nUsage: %s ARCHIVE [OPTION]…\n\n' else message='\nUsage: %s [OPTION]… [ARCHIVE]\n\n' fi ;; esac print_message 'info' "$message" \ "$script_name" # print details about options usage print_message 'info' '%s\n\n' \ 'OPTIONS' help_checksum help_compression help_prefix help_package help_icons help_overwrite help_output_dir help_no_mtree help_tmpdir help_skipfreespacecheck help_collection_path help_show_game_script 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 return 0 fi # print list of supported archives print_message 'info' '%s\n\n' \ 'ARCHIVE' messages_language=$(messages_language) case "$messages_language" in ('fr') message='Ce script reconnaît les archives suivantes :\n' ;; ('en'|*) message='This script can work on the following archives:\n' ;; esac print_message 'info' "$message" # shellcheck disable=SC2046 information_archives_list $(archives_list) return 0 } # display --checksum option usage # USAGE: help_checksum help_checksum() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tChoix de la méthode de vérification dʼintégrité de lʼarchive\n\n' message="$message"'\t%s\tvérification via md5sum\n' # md5 message="$message"'\t%s\tpas de vérification\n\n' # none ;; ('en'|*) message='\tArchive integrity verification method selection\n\n' message="$message"'\t%s\tmd5sum verification\n' # md5 message="$message"'\t%s\tno verification\n\n' # none ;; esac print_message 'info' '--%s %s\n' \ 'checksum' \ 'md5|none' print_message 'info' "$message" \ 'md5' \ 'none' } # Display --compression option usage # USAGE: help_compression help_compression() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tChoix de la méthode de compression des paquets générés\n' message="$message"'\t%s\tpas de compression\n' message="$message"'\t%s\tméthode de compression mettant lʼaccent sur la rapidité\n' message="$message"'\t%s\tméthode de compression mettant lʼaccent sur la réduction de taille\n' message="$message"'\t%s\tméthode de compression par défaut du système actuel\n\n' ;; ('en'|*) message='\tGenerated packages compression method selection\n' message="$message"'\t%s\tno compression\n' message="$message"'\t%s\tcompression method focusing on compression speed\n' message="$message"'\t%s\tcompression method focusing on size reduction\n' message="$message"'\t%s\tdefault compression method on the current system\n\n' ;; esac print_message 'info' '--%s %s\n' \ 'compression' \ 'none|speed|size|auto' print_message 'info' "$message" \ 'none' \ 'speed' \ 'size' \ 'auto' } # display --prefix option usage # USAGE: help_prefix help_prefix() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tChoix du chemin dʼinstallation du jeu\n\n' message="$message"'\tCette option accepte uniquement un chemin absolu.\n\n' ;; ('en'|*) message='\tGame installation path setting\n\n' message="$message"'\tThis option accepts an absolute path only.\n\n' ;; esac print_message 'info' '--%s %s\n' \ 'prefix' \ 'path' print_message 'info' "$message" } # display --package option usage # USAGE: help_package help_package() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tChoix du type de paquet à construire\n\n' message="$message"'\t%s\tpaquet .pkg.tar (Arch Linux)\n' message="$message"'\t%s\tpaquet .deb (Debian, Ubuntu)\n' message="$message"'\t%s\tpaquet .tbz2 (Gentoo)\n\n' ;; ('en'|*) message='\tGenerated package type selection\n\n' message="$message"'\t%s\t.pkg.tar package (Arch Linux)\n' message="$message"'\t%s\t.deb package (Debian, Ubuntu)\n' message="$message"'\t%s\t.tbz2 package (Gentoo)\n\n' ;; esac print_message 'info' '--%s %s\n' \ 'package' \ 'arch|deb|gentoo' print_message 'info' "$message" \ 'arch' \ 'deb' \ 'gentoo' } # display --no-icons option usage # USAGE: help_icons help_icons() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tNe pas inclure les icônes du jeu.\n\n' ;; ('en'|*) message='\tDo not include game icons.\n\n' ;; esac print_message 'info' '--%s\n' \ 'no-icons' print_message 'info' "$message" } # display --overwrite option usage # USAGE: help_overwrite help_overwrite() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tRemplace les paquets si ils existent déjà.\n\n' ;; ('en'|*) message='\tReplace packages if they already exist.\n\n' ;; esac print_message 'info' '--%s\n' \ 'overwrite' print_message 'info' "$message" } # display --output-dir option usage # USAGE: help_output_dir help_output_dir() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tDéfinit le répertoire de destination des paquets générés.\n\n' ;; ('en'|*) message='\tSet the output directory for generated packages.\n\n' ;; esac print_message 'info' '--%s\n' \ 'output-dir' print_message 'info' "$message" } # Display the --collection-path option usage # USAGE: help_collection_path help_collection_path() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tLimiter la recherche de scripts de prise en charge de jeux au chemin donné.\n\n' ;; ('en'|*) message='\tLimit the search for game scripts to a given path.\n\n' ;; esac print_message 'info' '--%s\n' \ 'collection-path' print_message 'info' "$message" } # display --show-game-script option usage # USAGE: help_show_game_script help_show_game_script() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tAffiche uniquement le chemin vers le script à utiliser, sans le lancer.\n\n' ;; ('en'|*) message='\tOnly displays the name of the script to use, without running it.\n\n' ;; esac print_message 'info' '--%s\n' \ 'show-game-script' print_message 'info' "$message" } # display --no-mtree option usage # USAGE: help_no_mtree help_no_mtree() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tNe crée pas de fichier .MTREE pour les paquets Arch Linux.\n\n' ;; ('en'|*) message='\tDo not make .MTREE file in Arch Linux packages\n\n' ;; esac print_message 'info' '--%s\n' \ 'no-mtree' print_message 'info' "$message" } # Display --tmpdir option usage # USAGE: help_tmpdir help_tmpdir() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tDéfinit le répertoire utilisé pour le stockage des fichiers temporaire.\n' message="$message"'\tLa valeur par défaut est : %s\n\n' ;; ('en'|*) message='\tSet the directory used for temporary files storage.\n' message="$message"'\tDefault value is: %s\n\n' ;; esac print_message 'info' '--%s\n' \ 'tmpdir' print_message 'info' "$message" \ "${TMPDIR:-/tmp}" } # Display --no-free-space-check option usage # USAGE: help_skipfreespacecheck help_skipfreespacecheck() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tNe pas tester lʼespace libre disponible.\n\n' ;; ('en'|*) message='\tDo not check for free space.\n\n' ;; esac print_message 'info' '--%s\n' \ 'no-free-space-check' print_message 'info' "$message" } # Display --config-file option usage # USAGE: help_configfile help_configfile() { local config_file_path config_file_path=$(configuration_file_default_path) local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tDéfinit le fichier de configuration à utiliser.\n' message="$message"'\tLe fichier par défaut est : %s\n\n' ;; ('en'|*) message='\tSet the configuration file to use.\n' message="$message"'\tDefault file is: %s\n\n' ;; esac print_message 'info' '--%s\n' \ 'config-file' print_message 'info' "$message" \ "$config_file_path" } # Display --list-packages option usage # USAGE: help_listpackages help_listpackages() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tAffiche la liste des paquets à construire.\n\n' ;; ('en'|*) message='\tPrint the list of packages to build.\n\n' ;; esac print_message 'info' '--%s\n' \ 'list-packages' print_message 'info' "$message" } # Display --list-requirements option usage # USAGE: help_listrequirements help_listrequirements() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tAffiche la liste des commandes nécessaire à la construction de paquets à partir de lʼarchive donnée.\n\n' ;; ('en'|*) message='\tPrint the list of commands required to build packages from the given archive.\n\n' ;; esac print_message 'info' '--%s\n' \ 'list-requirements' print_message 'info' "$message" } # Display --list-available-scripts option usage # USAGE: help_listavailablescripts help_listavailablescripts() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tAffiche la liste des scripts de prise en charge de jeux disponibles sur ce système.\n\n' ;; ('en'|*) message='\tPrint the list of game scripts available on this system.\n\n' ;; esac print_message 'info' '--%s\n' \ 'list-available-scripts' print_message 'info' "$message" } # Display --list-supported-games option usage # USAGE: help_listsupportedgames help_listsupportedgames() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\tAffiche la liste des jeux pris en charge.\n' message="$message"'\tAttention : cette opération peut prendre plusieurs minutes.\n\n' ;; ('en'|*) message='\tPrint the list of supported games.\n' message="$message"'\tWarning: this operation can take several minutes.\n\n' ;; esac print_message 'info' '--%s\n' \ 'list-supported-games' print_message 'info' "$message" } src/10_messages/00_language.sh0000644000000000000000000000060613120060140015055 0ustar rootroot# Get the language set for messages in the current environment # USAGE: messages_language # RETURN: the language code, as a two-letters code, or the fallback value "C" messages_language() { # Relying on the "locale" command prevents the need to query the values of multiple variables, # and handle the priority between them. locale | sed --silent 's/LC_MESSAGES="\?\([^_"]*\).*/\1/p' } src/10_messages/10_wrapper.sh0000644000000000000000000001264313120060140014757 0ustar rootroot# Print a localized message # USAGE: print_message $level $message $extra_values[…] print_message() { local level # Valid levels are: # - error # - warning # - warning_once # - info # - info_once # Unknown levels will be handled similar to "info". level="$1" shift 1 case "$level" in ('error') print_message_error "$@" ;; ('warning') print_message_warning "$@" ;; ('warning_once') print_message_warning_once "$@" ;; ('info_once') print_message_info_once "$@" ;; ('info'|*) print_message_info "$@" ;; esac } # Print a localized error message # USAGE: print_message_error $message $extra_values[…] print_message_error() ( local message # The message is a format string, any included "%s" will be replaced by the values passed as extra arguments. # See printf(1) for details on the sequences that can be used in the format string. message="$1" shift 1 ## Since this is called from a subshell, this should not trigger unwanted output redirections. exec 1>&2 local messages_language error_string messages_language=$(messages_language) case "$messages_language" in ('fr') error_string='Erreur :' ;; ('en'|*) error_string='Error:' ;; esac printf '\n\033[1;31m%s\033[0m\n' "$error_string" ## Silence ShellCheck false-positive ## Don't use variables in the printf format string. Use printf "..%s.." "$foo". # shellcheck disable=SC2059 printf -- "$message" "$@" ) # Print a localized warning message # USAGE: print_message_warning $message $extra_values[…] print_message_warning() { local message # The message is a format string, any included "%s" will be replaced by the values passed as extra arguments. # See printf(1) for details on the sequences that can be used in the format string. message="$1" shift 1 local messages_language warning_string messages_language=$(messages_language) case "$messages_language" in ('fr') warning_string='Avertissement :' ;; ('en'|*) warning_string='Warning:' ;; esac printf '\n\033[1;33m%s\033[0m\n' "$warning_string" ## Silence ShellCheck false-positive ## Don't use variables in the printf format string. Use printf "..%s.." "$foo". # shellcheck disable=SC2059 printf -- "$message" "$@" } # Print a localized warning message, only if it has not already been shown # USAGE: print_message_warning_once $message $extra_values[…] print_message_warning_once() { # Return early if this message has already been shown. if message_has_been_shown_already "$@"; then return 0 fi print_message_warning "$@" # Prevent this message from being shown again. messages_already_shown_add "$@" } # Print a localized information message # USAGE: print_message_info $message $extra_values[…] print_message_info() { local message # The message is a format string, any included "%s" will be replaced by the values passed as extra arguments. # See printf(1) for details on the sequences that can be used in the format string. message="$1" shift 1 ## Silence ShellCheck false-positive ## Don't use variables in the printf format string. Use printf "..%s.." "$foo". # shellcheck disable=SC2059 printf -- "$message" "$@" } # Print a localized information message, only if it has not already been shown # USAGE: print_message_info_once $message $extra_values[…] print_message_info_once() { # Return early if this message has already been shown. if message_has_been_shown_already "$@"; then return 0 fi print_message_info "$@" # Prevent this message from being shown again. messages_already_shown_add "$@" } # Compute a unique message indentifier from a message function name and its arguments # USAGE: message_identifier $message $extra_values[…] message_identifier() { local message_function message_function="$1" shift 1 printf '%s' "$message_function" if [ $# -ge 1 ]; then printf ':%s' "$@" fi } # Print the path to a file listing messages already shown # USAGE: messages_already_shown_file # RETURN: the path to the file listing the messages, # or an empty string if such a path can not be computed yet messages_already_shown_file() { # The list of shown messages can only be used when PLAYIT_WORKDIR is already set. if [ -z "${PLAYIT_WORKDIR:-}" ]; then return 0 fi printf '%s/messages-shown' "$PLAYIT_WORKDIR" } # Add a message to the list of already shown ones. # USAGE: messages_already_shown_add $message $extra_values[…] messages_already_shown_add() { local messages_list messages_list=$(messages_already_shown_file) # The list of shown messages can only be used when PLAYIT_WORKDIR is already set. if [ -z "$messages_list" ]; then return 0 fi local message_identifier message_identifier=$(message_identifier "$@") printf '%s\n' "$message_identifier" >> "$messages_list" } # Check if a message has already been shown. # USAGE: messages_has_been_shown_already $message $extra_values[…] # RETURN: 0 if the given message has already been shown, # 1 if there is no messages list yet, # 1 otherwise message_has_been_shown_already() { local messages_list messages_list=$(messages_already_shown_file) # The list of shown messages can only be used when PLAYIT_WORKDIR is already set. if [ -z "$messages_list" ]; then return 1 fi # Return early if there is no list of shown messages yet. if [ ! -e "$messages_list" ]; then return 1 fi local message_identifier message_identifier=$(message_identifier "$@") grep --quiet --fixed-strings --line-regexp --regexp="$message_identifier" "$messages_list" } src/20_configuration/00_configuration.sh0000644000000000000000000000344113120060140017202 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.sh0000644000000000000000000000726413120060140016350 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 'list-requirements' \ --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 'collection-path:' \ --longoptions 'compression:' \ --longoptions 'output-dir:' \ --longoptions 'package:' \ --longoptions 'prefix:' \ --longoptions 'tmpdir:' \ -- "$@" } # 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 ;; ( \ '--checksum' | \ '--collection-path' | \ '--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 ;; ( \ '--checksum' | \ '--collection-path' | \ '--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.sh0000644000000000000000000002632513120060140016036 0ustar rootroot# Set default values for all options # USAGE: options_init_default options_init_default() { # Guess most appropriate package format based on current system. local default_package_format default_package_format=$(options_init_default_package) # 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 \ 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_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 } # Guess most appropriate package format based on current system. # USAGE: options_init_default_package options_init_default_package() { # If a value is already set, return early. if [ -n "${PLAYIT_DEFAULT_OPTION_PACKAGE:-}" ]; then printf '%s' "$PLAYIT_DEFAULT_OPTION_PACKAGE" return 0 fi # Identify the current system. local host_system if [ -e '/etc/os-release' ]; then host_system=$( grep --regexp='^ID=' /etc/os-release | cut --delimiter='=' --fields=2 | ## This sed call is used to remove enclosing quotes. sed "s/^[\"']//;s/[\"']$//" ) 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. local default_package_format 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 # Print the default package format, falling back on "deb". printf '%s' "${default_package_format:-deb}" } # Get the name of the variable used to store the value of the given option # USAGE: option_variable $option_name # RETURN: the variable name option_variable() { local option_name option_name="$1" # The environment variable used to store an option is derived from its name: # - replace "-" with "_" # - convert to uppercase # - prepend "PLAYIT_OPTION_" # As an exemple, the value of the option "output-dir" would be stored in the following variable: # PLAYIT_OPTION_OUTPUT_DIR printf 'PLAYIT_OPTION_%s' "$( printf '%s' "$option_name" | sed 's/-/_/g' | tr '[:lower:]' '[:upper:]' )" } # Get the name of the variable used to store the default value of the given option # USAGE: option_variable_default $option_name # RETURN: the variable name option_variable_default() { local option_name option_name="$1" # The environment variable used to store an option is derived from its name: # - replace "-" with "_" # - convert to uppercase # - prepend "PLAYIT_DEFAULT_OPTION_" # As an exemple, the default value of the option "output-dir" would be stored in the following variable: # PLAYIT_DEFAULT_OPTION_OUTPUT_DIR printf 'PLAYIT_DEFAULT_OPTION_%s' "$( printf '%s' "$option_name" | sed 's/-/_/g' | tr '[:lower:]' '[:upper:]' )" } # Update the value of the given option # USAGE: option_update $option_name $option_value option_update() { local option_name option_value option_name="$1" option_value="$2" local option_variable option_variable=$(option_variable "$option_name") export $option_variable="$option_value" } # Update the default value of the given option # USAGE: option_update_default $option_name $option_value option_update_default() { local option_name option_value option_name="$1" option_value="$2" local option_variable option_variable=$(option_variable_default "$option_name") export $option_variable="$option_value" } # Get the value of the given option # USAGE: option_value $option_name # RETURN: the option value option_value() { local option_name option_name="$1" local option_variable option_value option_variable=$(option_variable "$option_name") option_value=$(get_value "$option_variable") if [ -n "$option_value" ]; then printf '%s' "$option_value" return 0 fi # If no value is explicitly set, return the default one option_variable=$(option_variable_default "$option_name") get_value "$option_variable" } # Check the validity of all options # USAGE: options_validity_check # RETURN: nothing if all option values are valid, # throw an error otherwise options_validity_check() { local option_checksum option_checksum=$(option_value 'checksum') case "$option_checksum" in ('md5'|'none') ;; (*) error_option_invalid 'checksum' "$option_checksum" return 1 ;; esac local option_compression option_compression=$(option_value 'compression') case "$option_compression" in ('none'|'speed'|'size'|'auto') ;; (*) error_option_invalid 'compression' "$option_compression" return 1 ;; esac local option_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') ;; (*) 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 local option_collection_path option_collection_path=$(option_value 'collection-path') # If collection-path is set, it should be the path to a directory if \ [ -n "$option_collection_path" ] && \ [ ! -d "$option_collection_path" ] then error_not_a_directory "$option_collection_path" return 1 fi } # 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. # Portage tries to guess that no suffix means the configured BINPKG_COMPRESS error_incompatible_options 'package' 'compression' return 1 ;; esac ;; ('auto') local option_package option_package=$(option_value 'package') case "$option_package" in ('arch'|'gentoo') # --compression auto has not been implemented for Arch Linux and Gentoo packages yet. error_incompatible_options 'package' 'compression' return 1 ;; esac ;; esac } src/20_configuration/90_messages.sh0000644000000000000000000000365713120060140016164 0ustar rootroot# Error - An invalid value has been provided for the given option # USAGE: error_option_invalid $option_name $option_value error_option_invalid() { local option_name option_value option_name="$1" option_value="$2" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='"%s" nʼest pas une valeur valide pour --%s.\n' ;; ('en'|*) message='"%s" is not a valid value for --%s.\n' ;; esac print_message 'error' "$message" \ "$option_value" \ "$option_name" } # Error - The configuration file could not be found # USAGE: error_config_file_not_found $config_file_path error_config_file_not_found() { local config_file_path config_file_path="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le fichier de configuration %s nʼa pas pu être trouvé.\n' ;; ('en'|*) message='The configuration file %s has not been found.\n' ;; esac print_message 'error' "$message" \ "$config_file_path" } # Error - Some options are currently set to incompatible values # USAGE: error_incompatible_options $option_name_1 $option_name_2 error_incompatible_options() { local option_name_1 option_name_2 option_name_1="$1" option_name_2="$2" local option_value_1 option_value_2 option_value_1=$(option_value "$option_name_1") option_value_2=$(option_value "$option_name_2") local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Les options suivantes ne sont pas compatibles :\n' message="$message"'\t--%s %s\n' message="$message"'\t--%s %s\n\n' ;; ('en'|*) message='The following options are not compatible:\n' message="$message"'\t--%s %s\n' message="$message"'\t--%s %s\n\n' ;; esac print_message 'error' "$message" \ "$option_name_1" \ "$option_value_1" \ "$option_name_2" \ "$option_value_2" } src/20_game/00_common.sh0000644000000000000000000000447313120060140013673 0ustar rootroot# Print the identifier of the current game # If a game is set the identifier is restricted to the characters set [-0-9a-z], # and can not start nor end with an hyphen (-) character. # An empty string is returned if no game is set yet. # USAGE: game_id game_id() { # The game identifier might might be archive-specific when several games are supported by a same script, # or package-specific when several games are provided by a single archive. local game_id game_id=$(context_value 'GAME_ID') # Return early if no game identifier is set yet if [ -z "$game_id" ]; then return 0 fi # Check that the identifier 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.sh0000644000000000000000000000205113120060140013637 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 Adventure Game Studio if [ -z "$game_engine" ]; then local ags_name ags_name=$(ags_name) if [ -n "$ags_name" ]; then game_engine='ags' fi fi # Try to identify games using Unity3D if [ -z "$game_engine" ]; then local unity3d_name unity3d_name=$(unity3d_name) if [ -n "$unity3d_name" ]; then game_engine='unity3d' fi fi # Try to identify games using Unreal Engine 4 if [ -z "$game_engine" ]; then local unrealengine4_name unrealengine4_name=$(unrealengine4_name) if [ -n "$unrealengine4_name" ]; then game_engine='unrealengine4' fi fi # Try to identify games using Visionaire if [ -z "$game_engine" ]; then local visionaire_name visionaire_name=$(visionaire_name) if [ -n "$visionaire_name" ]; then game_engine='visionaire' fi fi printf '%s' "$game_engine" } src/20_game/90_messages.sh0000644000000000000000000000320613120060140014214 0ustar rootroot# Error - An invalid format is used for game id # USAGE: error_game_id_invalid $game_id error_game_id_invalid() { local game_id game_id="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Lʼid de jeu fourni ne correspond pas au format attendu : "%s"\n' message="$message"'Cette valeur ne peut utiliser que des caractères du set [-a-z0-9],' message="$message"' et ne peut ni débuter ni sʼachever par un tiret.\n' ;; ('en'|*) message='The provided game id is not using the expected format: "%s"\n' message="$message"'The value should only include characters from the set [-a-z0-9],' message="$message"' and can not begin nor end with an hyphen.\n' ;; esac print_message 'error' "$message" \ "$game_id" } # Error - An invalid format is used for expansion id # USAGE: error_expansion_id_invalid $expansion_id error_expansion_id_invalid() { local expansion_id expansion_id="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Lʼid dʼextension fourni ne correspond pas au format attendu : "%s"\n' message="$message"'Cette valeur ne peut utiliser que des caractères du set [-a-z0-9],' message="$message"' et ne peut ni débuter ni sʼachever par un tiret.\n' ;; ('en'|*) message='The provided expansion id is not using the expected format: "%s"\n' message="$message"'The value should only include characters from the set [-a-z0-9],' message="$message"' and can not begin nor end with an hyphen.\n' ;; esac print_message 'error' "$message" \ "$expansion_id" } src/20_requirements/00_list.sh0000644000000000000000000001256213120060140015166 0ustar rootroot# List the requirements for the current game script # USAGE: requirements_list # RETURN: a list for required commands, one per line requirements_list() { # A variable is used to prevent error code mangling local requirements_list # List explicit requirements requirements_list=$(requirements_list_explicit) # List requirements for the current archive integrity setting requirements_list="$requirements_list $(requirements_list_checksum)" # List requirements for the current output package format setting requirements_list="$requirements_list $(requirements_list_package)" # List requirements for the current icons setting requirements_list="$requirements_list $(requirements_list_icons)" # List requirements for the current archive requirements_list="$requirements_list $(requirements_list_archive)" # If a WINE init .reg script is to be generated, # ensure that iconv is available local regedit_initial_content regedit_initial_content=$(regedit_initial_content) if [ -n "$regedit_initial_content" ]; then requirements_list="$requirements_list iconv" fi printf '%s' "$requirements_list" | list_clean } # List requirements explictly set from the game script # USAGE: requirements_list_explicit # RETURN: a list for required commands, # separated by line breaks requirements_list_explicit() { context_value 'REQUIREMENTS_LIST' } # List requirements for the current archive integrity setting # USAGE: requirements_list_checksum # RETURN: a list for required commands, one per line requirements_list_checksum() { local option_checksum requirements option_checksum=$(option_value 'checksum') case "$option_checksum" in ('md5') requirements='md5sum' ;; esac if ! variable_is_empty 'requirements'; then printf '%s\n' $requirements fi } # List requirements for the current output package format setting # USAGE: requirements_list_package # RETURN: a list for required commands, one per line requirements_list_package() { local option_package requirements option_package=$(option_value 'package') case "$option_package" in ('arch') # bsdtar and gzip are required for .MTREE requirements='bsdtar gzip' ;; ('deb') requirements='dpkg-deb' ;; ('gentoo') requirements='tar bzip2' ;; 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 # Fetch the 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 game_engine game_engine=$(game_engine) for icon in $icons_list; do ## This is allowed to fail for Unity3D games, ## because we might not know the icon path before the archive extraction. case "$game_engine" in ('unity3d') icon_path=$(icon_path "$icon" 2>/dev/null) || continue ;; (*) icon_path=$(icon_path "$icon") ;; esac case "$icon_path" in (*'.png') printf '%s\n' 'identify' ;; (*'.bmp'|*'.ico') printf '%s\n' 'identify' 'convert' ;; (*'.exe') printf '%s\n' 'identify' 'convert' 'wrestool' ;; esac done } # List requirements for the current archive # USAGE: requirements_list_archive # RETURN: a list for required commands, one per line requirements_list_archive() { local archive archive=$(current_archive) { requirements_list_archive_single "$archive" local archive_part part_index for part_index in $(seq 1 9); do archive_part="${archive}_PART${part_index}" # Stop looking at the first unset archive extra part. if variable_is_empty "$archive_part"; then break fi requirements_list_archive_single "$archive_part" done } | list_clean } # List requirements for the given archive # USAGE: requirements_list_archive_single $archive # RETURN: a list for required commands, one per line requirements_list_archive_single() { local archive archive="$1" local archive_extractor archive_extractor=$(archive_extractor "$archive") if [ -n "$archive_extractor" ]; then printf '%s\n' "$archive_extractor" return 0 fi local archive_type requirements archive_type=$(archive_type "$archive") case "$archive_type" in ('7z') requirements='7zr' ;; ('appimage') requirements='binwalk unsquashfs' ;; ('cabinet') requirements='cabextract' ;; ('debian') requirements='dpkg-deb' ;; ('innosetup') requirements='innoextract' ;; ('installshield') requirements='unshield' ;; ('iso') requirements='bsdtar' ;; ('lha') requirements='lha' ;; ('makeself') requirements=$(archive_requirements_makeself_list) ;; ('mojosetup') requirements=$(archive_requirements_mojosetup_list) ;; ('msi') requirements='msiextract' ;; ('nullsoft-installer') requirements='unar' ;; ('rar') requirements='unar' ;; ('tar') requirements='tar' ;; ('tar.bz2') requirements='tar bunzip2' ;; ('tar.gz') requirements='tar gunzip' ;; ('tar.xz') requirements='tar unxz' ;; ('zip') requirements='unzip' ;; esac if ! variable_is_empty 'requirements'; then printf '%s\n' $requirements fi } src/20_requirements/10_check.sh0000644000000000000000000000561513120060140015272 0ustar rootroot# Check the presence of the current game script requirements # USAGE: requirements_check # RETURN: 0 if all required dependencies are available, # 1 if a dependency is missing requirements_check() { # Trigger an error if fetching the requirements list failed # `return 1` is not enough when called through test, so `exit 1` is used instead local requirements_list if ! requirements_list=$(requirements_list); then exit 1 fi local requirement for requirement in $requirements_list; do if ! command -v "$requirement" >/dev/null 2>&1; then error_dependency_not_found "$requirement" return 1 fi done # Debian - Check the available version of dpkg-deb local option_package option_package=$(option_value 'package') case "$option_package" in ('deb') ## Explicitly return an error status if the version check failed. if ! requirements_check_dpkg_deb_version; then return 1 fi ;; esac } # Check the available version of dpkg-deb # USAGE: requirements_check_dpkg_deb_version # RETURN: 0 if dpkg-deb is recent enough, # 1 if dpkg-deb is too old. requirements_check_dpkg_deb_version() { local version_available version_required version_available=$( LC_ALL=C dpkg-deb --version | sed --silent "s/Debian 'dpkg-deb' package archive backend version \\([\\.0-9]\\+\\) (amd64)\\./\\1/p" ) ## Throw an explicit error if current dpkg-deb version could not be fetched. if [ -z "$version_available" ]; then error_dpkg_version_not_found return 1 fi ## The required option --root-owner-group is only available with dpkg-deb ≥ 1.19.0. version_required='1.19.0' ## If dpkg-deb is available, we can assume dpkg is available too. if ! dpkg --compare-versions "$version_available" '>=' "$version_required"; then error_requirement_too_old 'dpkg-deb' "$version_available" "$version_required" return 1 fi } # Check the presence of the required commands for icons extraction # USAGE: requirements_check_icons requirements_check_icons() { # Return early if icons inclusion has been disabled. local option_icons option_icons=$(option_value 'icons') if [ "$option_icons" -eq 0 ]; then return 0 fi local icons_requirements requirement icons_requirements=$(requirements_list_icons) for requirement in $icons_requirements; do if ! command -v "$requirement" >/dev/null 2>&1; then error_dependency_not_found "$requirement" return 1 fi done } # output what a command is provided by # USAGE: dependency_provided_by $command # CALLED BY: error_dependency_not_found dependency_provided_by() { local command provider command="$1" case "$command" in ('7zr') provider='p7zip' ;; ('bsdtar') provider='libarchive' ;; ('convert'|'identify') provider='imagemagick' ;; ('lha') provider='lhasa' ;; ('icotool'|'wrestool') provider='icoutils' ;; ('dpkg-deb') provider='dpkg' ;; (*) provider="$command" ;; esac printf '%s' "$provider" return 0 } src/20_requirements/90_messages.sh0000644000000000000000000000413013120060140016023 0ustar rootroot# Error - A required dependency is missing # USAGE: error_dependency_not_found $command_name error_dependency_not_found() { local command_name provider_package command_name="$1" provider_package=$(dependency_provided_by "$command_name") local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='%s est introuvable. Installez %s avant de lancer ce script.\n' ;; ('en'|*) message='%s not found. Install %s before running this script.\n' ;; esac print_message 'error' "$message" \ "$command_name" \ "$provider_package" } # Error - A required dependency is available, but in a build that is too old # USAGE: error_requirement_too_old $command_name $version_available $version_required error_requirement_too_old() { local command_name version_available version_required command_name="$1" version_available="$2" version_required="$3" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='%s est disponible dans la version %s, mais la version %s ou plus récente est requise.\n' ;; ('en'|*) message='%s is available in version %s, but version %s or newer is required.\n' ;; esac print_message 'error' "$message" \ "$command_name" \ "$version_available" \ "$version_required" } # Error - dpkg-deb is available, but its version could not be found. # USAGE: error_dpkg_version_not_found error_dpkg_version_not_found() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='dpkg-deb est disponible, mais sa version n’a pas pu être récupérée.' message="$message"'\nMerci de nous signaler cette erreur, en incluant la sortie complète de cette commande :' message="$message"'\n%s\n' ;; ('en'|*) message='dpkg-deb is available, but its version could not be found.' message="$message"'\nPlease report this error, including the full output of the following command:' message="$message"'\n%s\n' ;; esac print_message 'error' "$message" \ 'LC_ALL=C dpkg-deb --version' } src/30_applications/00_common.sh0000644000000000000000000002561613120060140015453 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 ## TODO: This only has support for applications using an identifier like APP_xxx ## where xxx can not include an underscore. Underscores should be allowed. if [ -z "$applications_list" ]; then local sed_expression # The following expression matches: # - APP_xxx_EXE # - APP_xxx_SCUMMID # - APP_xxx_TYPE # and the suffixed variants of these variables. sed_expression='s/^\(APP_[0-9A-Z]\+\)_\(EXE\|SCUMMID\|TYPE\)\(_[0-9A-Z]\+\)*=.*/\1/p' applications_list=$(set | sed --silent --expression="$sed_expression") fi # Fall back on the default applications list for the current game engine if [ -z "$applications_list" ]; then local game_engine game_engine=$(game_engine) case "$game_engine" in ('ags') applications_list='APP_MAIN' ;; ('gamemaker') applications_list=$(applications_list_gamemaker) ;; ('unity3d') # Unity3D games are expected to provide a single application applications_list='APP_MAIN' ;; ('visionaire') applications_list=$(visionaire_applications_list) ;; esac fi printf '%s\n' $applications_list | list_clean } # Print the type of prefix to use for the given application. # If no type is explicitely set from the game script, it defaults to "symlinks". # The supported prefix types are: # - "symlinks", the default, generate our usual symbolic links farm # - "none", no prefix is generated, the game is run from the read-only system directory # USAGE: application_prefix_type $application # RETURN: the prefix type keyword, from the supported values application_prefix_type() { # Prefix types: # - "symlinks", the default, generate our usual symbolic links farm # - "none", no prefix is generated, the game is run from the read-only system directory local application application="$1" # Set the prefix type for the current application. local prefix_type prefix_type=$(context_value "${application}_PREFIX_TYPE") # Fall back on the default prefix type for the current game. if [ -z "$prefix_type" ]; then prefix_type=$(context_value 'APPLICATIONS_PREFIX_TYPE') fi # Fall back on the default prefix type for the current application type. if [ -z "$prefix_type" ]; then local application_type application_type=$(application_type "$application") case "$application_type" in ('renpy') prefix_type='none' ;; ('scummvm') prefix_type='none' ;; (*) prefix_type='symlinks' ;; esac fi # Check that a supported prefix type has been set. case "$prefix_type" in ('symlinks'|'none') ## This is a supported type, no error to throw. ;; (*) error_unknown_prefix_type "$prefix_type" return 1 ;; esac printf '%s' "$prefix_type" } # print the id of the given application # USAGE: application_id $application # RETURN: the application id, limited to the characters set [-_0-9a-z] # the id can not start nor end with a character from the set [-_] application_id() { local application application="$1" # Get the application type from its identifier # Fall back on the game id if no value is set local application_id application_id=$(context_value "${application}_ID") if [ -z "$application_id" ]; then application_id=$(game_id) fi # Check that the id fits the format restrictions if ! printf '%s' "$application_id" | grep --quiet --regexp='^[0-9a-z][-_0-9a-z]\+[0-9a-z]$' then error_application_id_invalid "$application" "$application_id" return 1 fi printf '%s' "$application_id" } # Print the name of the binary targeted by the given application # USAGE: application_exe $application # RETURN: the binary file name, # or an empty string is none is set application_exe() { local application application="$1" local application_exe application_exe=$(context_value "${application}_EXE") # If no binary is explicitly set, fall back on the default value for the current game engine. if [ -z "$application_exe" ]; then local game_engine game_engine=$(game_engine) ## TODO: A default value could be set for Unreal Engine 4 games. ## Based on that default value, the fallback wrestool options for these games might need updating. case "$game_engine" in ('gamemaker') application_exe=$(application_exe_gamemaker) ;; ('unity3d') application_exe=$(unity3d_application_exe_default "$application") ;; ('visionaire') application_exe=$(visionaire_application_exe) ;; esac fi printf '%s' "$application_exe" } # Print the full path to the application binary # An error is thrown if the binary file could not be found anywhere # USAGE: application_exe_path $application_exe application_exe_path() { local application_exe application_exe="$1" # 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") path_game_data=$( set_current_package "$package" 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 done # Look for the application binary in the temporary path for archive content. ## Reaching this step with CONTENT_PATH_DEFAULT unset triggers an error. local content_path_default content_path_default=$(content_path_default) if [ -n "$content_path_default" ]; then local application_exe_path application_exe_path="${PLAYIT_WORKDIR}/gamedata/${content_path_default}/${application_exe}" if [ -f "$application_exe_path" ]; then printf '%s' "$application_exe_path" return 0 fi fi # Throw an error if the binary file could not be found anywhere error_application_exe_path_not_found "$application_exe" return 1 } # 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" } # Exits 1 if the give category is not an OpenDesktop Main Category as per # https://specifications.freedesktop.org/menu-spec/1.0/category-registry.html # USAGE: is_valid_category $category is_valid_category() { local category category="$1" case "$category" in 'AudioVideo') ;; 'Audio') ;; 'Video') ;; 'Development') ;; 'Education') ;; 'Game') ;; 'Graphics') ;; 'Network') ;; 'Office') ;; 'Settings') ;; 'System') ;; 'Utility') ;; *) return 1 ;; esac return 0 } # Print the menu category that should be used for the current application. # USAGE: application_category $application 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 # Check the category against XDG specification. if ! is_valid_category "$application_category"; then warning_application_category_unspecified "$application" "$application_category" fi printf '%s' "$application_category" } # Print the pre-run actions for the given application. # USAGE: application_prerun $application # RETURN: the pre-run actions, can span over multiple lines, # or an empty string if there are none application_prerun() { local application application="$1" local application_prerun application_prerun=$(context_value "${application}_PRERUN") # Run engine specific actions local game_engine game_engine=$(game_engine) case "$game_engine" in ('gamemaker') application_prerun="$(application_prerun_gamemaker) $application_prerun" ;; ('unity3d') ## Use a dedicated log file for the current game session. application_prerun="$application_prerun mkdir --parents logs" ;; esac # WARNING: This relies on a system deprecated since ./play.it 2.33.0, # LD_PRELOAD_SOURCE should be used instead. # If LD_PRELOAD hacks are provided in the current package, include them in the pre-run actions. local package hacks_list package=$(current_package) hacks_list=$(hacks_included_in_package "$package") if [ -n "$hacks_list" ]; then local hack hack_prerun for hack in $hacks_list; do hack_prerun=$(hack_application_prerun "$hack") application_prerun="$application_prerun $hack_prerun" done fi # Ensure the pre-run actions string always end with a line break. printf '%s\n' "$application_prerun" } # Print the post-run actions for the given application. # USAGE: application_postrun $application # RETURN: the post-run actions, can span over multiple lines, # or an empty string if there are none application_postrun() { local application application="$1" local application_postrun application_postrun=$(context_value "${application}_POSTRUN") # Ensure the post-run actions string always end with a line break. printf '%s\n' "$application_postrun" } # print the options string for the given application # USAGE: application_options $application # RETURN: the options string on a single line, # or an empty string if no options are set application_options() { # Get the application options string from its identifier local application application_options application="$1" application_options=$(context_value "${application}_OPTIONS") # Add engine specific options local game_engine game_engine=$(game_engine) case "$game_engine" in ('unity3d') ## Use a dedicated log file for the current game session. ## The quotes are not truly required, but they make ShellCheck happy. application_options="$application_options -logFile \"./logs/\$(date +%F-%R).log\"" ;; esac # Check that the options string does not span multiple lines local line_breaks_number line_breaks_number=$(printf '%s' "$application_options" | wc --lines) if [ "$line_breaks_number" -gt 0 ]; then error_variable_multiline "${application}_OPTIONS" return 1 fi printf '%s' "$application_options" } src/30_applications/10_type.sh0000644000000000000000000001035013120060140015132 0ustar rootroot# Print the type of the given application # USAGE: application_type $application # RETURN: the application type keyword, from the supported values: # - dosbox # - java # - mono # - native # - renpy # - scummvm # - wine # or an empty string if the type is not set and could not be guessed application_type() { local application application="$1" local application_type application_type=$(context_value "${application}_TYPE") # If no type has been explicitely set, try to guess one ## Try to detect ScummVM applications if [ -z "$application_type" ]; then local application_scummid application_scummid=$(application_scummvm_scummid "$application") if [ -n "$application_scummid" ]; then application_type='scummvm' fi fi ## Try to detect the application type based of the binary MIME type if [ -z "$application_type" ]; then if [ -n "${PLAYIT_WORKDIR:-}" ]; then application_type=$(application_type_guess_from_file "$application") fi fi # Return early if no type has been found if [ -z "$application_type" ]; then return 0 fi # Check that a supported type has been fetched case "$application_type" in ( \ 'custom' | \ 'dosbox' | \ 'java' | \ 'mono' | \ 'native' | \ 'renpy' | \ 'scummvm' | \ 'web' | \ '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. # An empty string is returned if no type could be guessed. # USAGE: application_type_guess_from_file $application application_type_guess_from_file() { local application application_type application="$1" application_type='' # Get the path to the application binary. local application_exe 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 is set for the current application. if [ -z "$application_exe" ]; then return 0 fi # Compute the full path to the application binary. local application_exe_path 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 # Guess the application type from its binary file MIME type. local file_type file_type_extended file_type=$(file_type "$application_exe_path") case "$file_type" in ( \ 'application/x-executable' | \ 'application/x-pie-executable' | \ 'application/x-sharedlib' \ ) application_type='native' ;; ('text/html') application_type='web' ;; ( \ 'application/x-dosexec' | \ 'application/vnd.microsoft.portable-executable' \ ) file_type_extended=$(env --ignore-environment file --brief --dereference "$application_exe_path") case "$file_type_extended" in ( \ 'DOS executable (COM)'* | \ 'MS-DOS executable'* \ ) application_type='dosbox' ;; (*'Mono/.Net assembly'*) application_type='mono' ;; ( \ 'PE32 executable'* | \ 'PE32+ executable'* \ ) application_type='wine' ;; esac ;; ('application/octet-stream') 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" } # Print the type of the first application. # USAGE: application_default_type application_default_type() { 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") printf '%s' "$application_type" } src/30_applications/90_messages.sh0000644000000000000000000001127413120060140015776 0ustar rootroot# Warning - An application category outside of the XDG spec is set. # USAGE: warning_application_category_unspecified warning_application_category_unspecified() { local application application_category application="$1" application_category="$2" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Catégorie inconnue par la spécification pour %s : %s\n' ;; ('en'|*) message='Unspecified application category for %s: %s\n' ;; esac print_message 'warning' "$message" \ "$application" "$application_category" } # Error - An unknown application type is used # USAGE: error_unknown_application_type $app_type error_unknown_application_type() { local application_type application_type="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le type dʼapplication "%s" est inconnu.\n' ;; ('en'|*) message='"%s" application type is unknown.\n' ;; esac print_message 'error' "$message" \ "$application_type" } # Error - No application type could be found # USAGE: error_no_application_type $application error_no_application_type() { local application application="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le type de lʼapplication "%s" nʼest pas défini, et nʼa pas pu être détecté automatiquement.\n' ;; ('en'|*) message='The type of application "%s" is not set, and could not be guessed.\n' ;; esac print_message 'error' "$message" \ "$application" } # Error - An unknown prefix type is requested # USAGE: error_unknown_prefix_type $prefix_type error_unknown_prefix_type() { local prefix_type prefix_type="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le type de préfixe "%s" est inconnu.\n' ;; ('en'|*) message='"%s" prefix type is unknown.\n' ;; esac print_message 'error' "$message" \ "$prefix_type" } # Error - An invalid format is used for the given application id # USAGE: error_application_id_invalid $application $application_id error_application_id_invalid() { local application application_id application="$1" application_id="$2" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Lʼid fourni pour lʼapplication %s ne correspond pas au format attendu : "%s"\n' message="$message"'Cette valeur ne peut utiliser que des caractères du set [-a-z0-9],' message="$message"' et ne peut ni débuter ni sʼachever par un tiret.\n' ;; ('en'|*) message='The id provided for application %s is not using the expected format: "%s"\n' message="$message"'The value should only include characters from the set [-a-z0-9],' message="$message"' and can not begin nor end with an hyphen.\n' ;; esac print_message 'error' "$message" \ "$application" \ "$application_id" } # Error - APP_xxx_EXE is unset but has been required # USAGE: error_application_exe_empty $application $function_name error_application_exe_empty() { local application function_name application="$1" function_name="$2" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='%s nʼest pas défini, mais cette valeur est requise par la fonction "%s".\n' ;; ('en'|*) message='%s is not set, but is required by the "%s" function.\n' ;; esac print_message 'error' "$message" \ "${application}_EXE" \ "$function_name" } # Error - The application binary path could not be found # USAGE: error_application_exe_path_not_found $application_exe error_application_exe_path_not_found() { local application_exe application_exe="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le fichier "%s" n’a pas été trouvé.\n' ;; ('en'|*) message='The file "%s" could not be found.\n' ;; esac print_message 'error' "$message" \ "$application_exe" } # Error - The applications list for the current game script is empty # USAGE: error_applications_list_empty error_applications_list_empty() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La liste dʼapplications à prendre en charge pour ce jeu semble vide' message="$message"', mais un traitement de cette liste a été demandé.\n' ;; ('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' ;; esac print_message 'error' "$message" } src/30_archives/00_selection.sh0000644000000000000000000002001113120060140015246 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 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}" ## Set archive version string. local archive_version archive_version=$(archive_version "$archive_candidate") export "${archive_identifier}_VERSION=${archive_version}" ## 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") # Run a MD5 hash comparison, to ensure the current archive is the expected one. ## FIXME: The hash comparison is broken for archives with multiple valid hashes. local option_checksum archive_hash_expected archive_candidate_hash_computed option_checksum=$(option_value 'checksum') case "$option_checksum" in ('md5') archive_hash_expected=$(get_value "${archive_candidate}_MD5") if [ -n "$archive_hash_expected" ]; then archive_candidate_hash_computed=$(archive_hash_md5_computed "$archive_name") if [ "$archive_candidate_hash_computed" != "$archive_hash_expected" ]; then continue fi fi ;; esac 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}" ## Set archive version string. local archive_version archive_version=$(archive_version "$archive_candidate") export "${archive_identifier}_VERSION=${archive_version}" ## 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" 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 archives_list=$(archives_used_list) local archive archive_hashes_list valid_hash_found archive_hash_expected archive_name archive_hash_computed for archive in $archives_list; do archive_hashes_list=$(archive_hashes_list_md5 "$archive") ## Skip archives with no expected hash set. if [ -z "$archive_hashes_list" ]; then continue fi valid_hash_found=0 for archive_hash_expected in $archive_hashes_list; do archive_name=$(archive_name "$archive") archive_hash_computed=$(archive_hash_md5_computed "$archive_name") if [ "$archive_hash_computed" = "$archive_hash_expected" ]; then valid_hash_found=1 break fi done if [ "$valid_hash_found" -eq 0 ]; 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 by the current game script # The archive identifiers are separated by line breaks. # USAGE: archives_list archives_list() { # Generate a list of archives based on the ARCHIVE_BASE_xxx_[0-9]+ naming scheme archives_list=$( set | sed --silent 's/^\(ARCHIVE_BASE\(_[0-9A-Z]\+\)*_[0-9]\+\)\(_NAME\)\?=.*/\1/p' | sort --reverse --version-sort ) if [ -z "$archives_list" ]; then error_no_archive_supported return 1 fi ## Errors due to empty grep output are ignored printf '%s\n' $archives_list | grep '^ARCHIVE_BASE_[0-9]\+' || true printf '%s\n' $archives_list | grep --invert-match '^ARCHIVE_BASE_[0-9]\+' || true } src/30_archives/05_selection_extra-archives-required.sh0000644000000000000000000002150113120060140022103 0ustar rootroot# Check for the presence of required extra archives # USAGE: archives_required_extra_presence_check # RETURN: 0 if no extra archive is required, or all required archives are found, # 1 if a required archive is missing archives_required_extra_presence_check() { # Check the presence of archives providing required native libraries. local libraries_required libraries_required=$(dependencies_list_native_libraries_all) while read -r library_required; do case "$library_required" in ('libcurl.so.4+CURL_OPENSSL_3') ## This function can be called through a test, ## preventing the ability to rely on set -o errexit, ## so we need to handle failures explicitly. archive_required_extra_presence_check_libcurl3 || return 1 ;; ('libFLAC.so.8') archive_required_extra_presence_check_libflac8 || return 1 ;; ('libgconf-2.so.4') archive_required_extra_presence_check_libgconf2 || return 1 ;; ('libidn.so.11') archive_required_extra_presence_check_libidn11 || return 1 ;; ('libpng12.so.0') archive_required_extra_presence_check_libpng12 || return 1 ;; ('libssl.so.1.0.0') archive_required_extra_presence_check_libssl100 || return 1 ;; ('libssl.so.1.1') archive_required_extra_presence_check_libssl11 || return 1 ;; esac done <<- EOL $(printf '%s' "$libraries_required") EOL # Check the presence of archives required to apply tweaks on the WINE prefix local wineprefix_tweaks wineprefix_tweak wineprefix_tweaks=$(wine_wineprefix_tweaks) while read -r wineprefix_tweak; do case "$wineprefix_tweak" in ('mono') # WARNING: This function can be called through a test, # preventing the ability to rely on set -o errexit if ! archive_required_extra_presence_check_mono; then return 1 fi ;; esac done <<- EOL $(printf '%s' "$wineprefix_tweaks") EOL } # Check for the presence of the extra archive providing libcurl.so.3 and libcurl.so.4 including the CURL_OPENSSL_3 symbol # USAGE: archive_required_extra_presence_check_libcurl3 # RETURN: 0 if the required archive is found, # 1 if it is missing archive_required_extra_presence_check_libcurl3() { ARCHIVE_REQUIRED_LIBCURL3_NAME='curl_7.52.1.tar.xz' ARCHIVE_REQUIRED_LIBCURL3_MD5='ae0368de97368164801618a08c70cb34' ARCHIVE_REQUIRED_LIBCURL3_URL='https://downloads.dotslashplay.it/resources/curl/' ## The archive properties will be reused later, for checking its integrity. export ARCHIVE_REQUIRED_LIBCURL3_NAME ARCHIVE_REQUIRED_LIBCURL3_MD5 ARCHIVE_REQUIRED_LIBCURL3_URL archive_initialize_required \ 'ARCHIVE_LIBCURL3' \ 'ARCHIVE_REQUIRED_LIBCURL3' } # Check for the presence of the extra archive providing libFLAC.so.8 # USAGE: archive_required_extra_presence_check_libflac8 # RETURN: 0 if the required archive is found, # 1 if it is missing archive_required_extra_presence_check_libflac8() { # On Arch Linux, this library is still provided in the packages repositories local option_package option_package=$(option_value 'package') case "$option_package" in ('arch') return 0 ;; esac ARCHIVE_REQUIRED_LIBFLAC8_NAME='libflac8.tar.xz' ARCHIVE_REQUIRED_LIBFLAC8_MD5='1f0d785f52474cb2232a2f8f8b561eda' ARCHIVE_REQUIRED_LIBFLAC8_URL='https://downloads.dotslashplay.it/resources/flac/' ## The archive properties will be reused later, for checking its integrity. export ARCHIVE_REQUIRED_LIBFLAC8_NAME ARCHIVE_REQUIRED_LIBFLAC8_MD5 ARCHIVE_REQUIRED_LIBFLAC8_URL archive_initialize_required \ 'ARCHIVE_LIBFLAC8' \ 'ARCHIVE_REQUIRED_LIBFLAC8' } # Check for the presence of the extra archive providing GConf 2 library # USAGE: archive_required_extra_presence_check_libgconf2 # RETURN: 0 if the required archive is found, # 1 if it is missing archive_required_extra_presence_check_libgconf2() { # On Arch Linux, this library is still provided in the packages repositories local option_package option_package=$(option_value 'package') case "$option_package" in ('arch') return 0 ;; esac ARCHIVE_REQUIRED_LIBGCONF2_NAME='libgconf-2-4.tar.xz' ARCHIVE_REQUIRED_LIBGCONF2_MD5='4ae540fd4114ee2ddd7bd841017aad3b' ARCHIVE_REQUIRED_LIBGCONF2_URL='https://downloads.dotslashplay.it/resources/gconf/' ## The archive properties will be reused later, for checking its integrity. export ARCHIVE_REQUIRED_LIBGCONF2_NAME ARCHIVE_REQUIRED_LIBGCONF2_MD5 ARCHIVE_REQUIRED_LIBGCONF2_URL archive_initialize_required \ 'ARCHIVE_LIBGCONF2' \ 'ARCHIVE_REQUIRED_LIBGCONF2' } # Check for the presence of the extra archive providing GNU Libidn 11 library # USAGE: archive_required_extra_presence_check_libidn11 # RETURN: 0 if the required archive is found, # 1 if it is missing archive_required_extra_presence_check_libidn11() { # On Arch Linux, this library is still provided in the packages repositories local option_package option_package=$(option_value 'package') case "$option_package" in ('arch') return 0 ;; esac ARCHIVE_REQUIRED_LIBIDN11_NAME='libidn11_1.33.tar.xz' ARCHIVE_REQUIRED_LIBIDN11_MD5='75d95702ec6a2b327c8902cc14998926' ARCHIVE_REQUIRED_LIBIDN11_URL='https://downloads.dotslashplay.it/resources/libidn/' ## The archive properties will be reused later, for checking its integrity. export ARCHIVE_REQUIRED_LIBIDN11_NAME ARCHIVE_REQUIRED_LIBIDN11_MD5 ARCHIVE_REQUIRED_LIBIDN11_URL archive_initialize_required \ 'ARCHIVE_LIBIDN11' \ 'ARCHIVE_REQUIRED_LIBIDN11' } # Check for the presence of the extra archive providing PNG 1.2 libraries # USAGE: archive_required_extra_presence_check_libpng12 # RETURN: 0 if the required archive is found, # 1 if it is missing archive_required_extra_presence_check_libpng12() { # On Arch Linux and Gentoo, this library is still provided in the packages repositories local option_package option_package=$(option_value 'package') case "$option_package" in ('arch'|'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'|'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'|'gentoo') return 0 ;; esac ARCHIVE_REQUIRED_OPENSSL11_NAME='openssl_1.1.1n.tar.xz' ARCHIVE_REQUIRED_OPENSSL11_MD5='dade7a54b213be8ac6e0bc2b571570cc' ARCHIVE_REQUIRED_OPENSSL11_URL='https://downloads.dotslashplay.it/resources/openssl/' ## The archive properties will be reused later, for checking its integrity. export ARCHIVE_REQUIRED_OPENSSL11_NAME ARCHIVE_REQUIRED_OPENSSL11_MD5 ARCHIVE_REQUIRED_OPENSSL11_URL archive_initialize_required \ 'ARCHIVE_OPENSSL11' \ 'ARCHIVE_REQUIRED_OPENSSL11' } # Check for the presence of the extra archive providing Mono, for inclusion in WINE prefixes # USAGE: archive_required_extra_presence_check_mono # RETURN: 0 if the required archive is found, # 1 if it is missing archive_required_extra_presence_check_mono() { ARCHIVE_REQUIRED_MONO_NAME='wine-mono-8.0.0-x86.msi' ARCHIVE_REQUIRED_MONO_MD5='4fe5c683fcd9634c7f6571f252b3603c' ARCHIVE_REQUIRED_MONO_URL='https://dl.winehq.org/wine/wine-mono/8.0.0/' ## The archive properties will be reused later, for checking its integrity. export ARCHIVE_REQUIRED_MONO_NAME ARCHIVE_REQUIRED_MONO_MD5 ARCHIVE_REQUIRED_MONO_URL archive_initialize_required \ 'ARCHIVE_MONO' \ 'ARCHIVE_REQUIRED_MONO' } src/30_archives/10_details.sh0000644000000000000000000002463213120060140014724 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, in kilobytes. # This is not the size of the archive file itself, # but the total size of the files it includes, after decompression. # USAGE: archive_size $archive archive_size() { ## An archive identifier local archive="$1" local archive_size archive_size=$(get_value "${archive}_SIZE") ## TODO: If the size is not explicitly set, ## try to guess it by scanning the archive contents. # 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 list of valid MD5 hashes for a given archive. # This list can be empty. # USAGE: archive_hashes_list_md5 $archive archive_hashes_list_md5() { local archive archive="$1" local hashes_list hashes_list=$(get_value "${archive}_MD5") printf '%s' "$hashes_list" | list_clean } # Get the computed MD5 hash of a given archive. # If a value is already stored in cache, this value is fetched; otherwise the hash is computed, then stored in cache. # The cache is stored in a file instead of a variable, as this function is usually called from a subshell. # USAGE: archive_hash_md5_computed $archive_name archive_hash_md5_computed() { ## A file name, found in the same directory than the main archive. local archive_name archive_name="$1" # If the hash has already been computed, use the cached value. local hash_md5_computed='' if [ -n "${PLAYIT_WORKDIR:-}" ]; then local cache_directory cache_file_hashes cache_directory="${PLAYIT_WORKDIR}/cache" cache_file_hashes="${cache_directory}/hashes" if [ -f "$cache_file_hashes" ]; then hash_md5_computed=$(sed --silent --expression="s/^${archive_name} | \([0-9a-f]\{32\}\)$/\1/p" "$cache_file_hashes") fi fi # If no cached value has been found, compute the hash. if [ -z "$hash_md5_computed" ]; then local archives_path_base archive_path archives_path_base=$(archives_path_base) archive_path="${archives_path_base}/${archive_name}" ## Print the message to the error output, to prevent messing up with the current function output. info_archive_hash_computation "$archive_name" >/dev/stderr hash_md5_computed=$(md5sum "$archive_path" | awk '{print $1}') # Cache the hash to prevent computing it again. if [ -n "${PLAYIT_WORKDIR:-}" ]; then mkdir --parents "$cache_directory" cat >> "$cache_file_hashes" <<- EOF $archive_name | $hash_md5_computed EOF fi fi printf '%s' "$hash_md5_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 (*'.AppImage') archive_type='appimage' ;; (*'.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" # 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" } # Get the game version provided by the given archive # USAGE: archive_version $archive archive_version() { local archive archive="$1" local archive_version archive_version=$(get_value "${archive}_VERSION") # Fall back on "1.0-1" if no version string is explicitly set. if [ -z "$archive_version" ]; then archive_version='1.0-1' fi # Check that the version string: # - starts with a number # - includes only digits, letters, dots and hyphens # - always includes an hyphen local version_pattern version_pattern='^[0-9][-.a-z0-9]*-[-.a-z0-9]\+$' if ! printf '%s' "$archive_version" | grep --quiet "$version_pattern" then error_archive_version_format "$archive_version" return 1 fi printf '%s' "$archive_version" } src/30_archives/20_extraction.sh0000644000000000000000000002137513120060140015461 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" # Check extraction dependencies for main archive. archive_dependencies_check_single "$archive" # 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" local archive_extractor archive_extractor=$(archive_extractor "$archive") if [ -n "$archive_extractor" ]; then archive_dependencies_check_using_extractor "$archive" else archive_dependencies_check_from_type "$archive" 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" 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" local archive_type archive_type=$(archive_type "$archive") case "$archive_type" in ('7z') archive_dependencies_check_type_7z ;; ('appimage') archive_dependencies_check_type_appimage ;; ('cabinet') archive_dependencies_check_type_cabinet ;; ('debian') archive_dependencies_check_type_debian ;; ('innosetup') archive_dependencies_check_type_innosetup ;; ('innosetup_nolowercase') archive_dependencies_check_type_innosetup ;; ('installshield') archive_dependencies_check_type_installshield ;; ('iso') archive_dependencies_check_type_iso ;; ('lha') archive_dependencies_check_type_lha ;; ('makeself') archive_requirements_makeself_check ;; ('mojosetup') archive_requirements_mojosetup_check ;; ('msi') archive_dependencies_check_type_msi ;; ('nullsoft-installer') archive_dependencies_check_type_nullsoft ;; ('rar') archive_dependencies_check_type_rar ;; ('tar') archive_dependencies_check_type_tar ;; ('tar.bz2') archive_dependencies_check_type_tarbz2 ;; ('tar.gz') archive_dependencies_check_type_targz ;; ('tar.xz') archive_dependencies_check_type_tarxz ;; ('zip') archive_dependencies_check_type_zip ;; esac } # Extract data from the current archive # USAGE: archive_extraction_default archive_extraction_default() { local archive archive=$(current_archive) if [ -z "$archive" ]; then error_archive_extraction_default_missing_archive return 1 fi archive_extraction "$archive" # Extract the contents from the extra archives providing required libraries archive_extraction_extra_libraries # Extract the contents from the extra archives providing icons if archive_is_available 'ARCHIVE_ICONS'; then archive_extraction_extra_icons fi } # Extract data from a given archive file # USAGE: archive_extraction $archive archive_extraction() { local archive archive="$1" local archive_name archive_name=$(archive_name "$archive") information_archive_data_extraction "$archive_name" local destination_directory destination_directory="${PLAYIT_WORKDIR}/gamedata" mkdir --parents "$destination_directory" # Get the path to the extraction log file local log_file log_directory log_file=$(archive_extraction_log_path) log_directory=$(dirname "$log_file") mkdir --parents "$log_directory" local archive_extractor archive_extractor=$(archive_extractor "$archive") if [ -n "$archive_extractor" ]; then archive_extraction_using_extractor "$archive" "$destination_directory" "$log_file" else archive_extraction_from_type "$archive" "$destination_directory" "$log_file" fi # Apply minimal permissions on extracted files set_standard_permissions "$destination_directory" } # extract data from the target archive, using the specified extractor # USAGE: archive_extraction_using_extractor $archive $destination_directory $log_file archive_extraction_using_extractor() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" local archive_extractor archive_extractor=$(archive_extractor "$archive") case "$archive_extractor" in ('7za') archive_extraction_using_7za "$archive" "$destination_directory" "$log_file" ;; ('7zr') archive_extraction_using_7zr "$archive" "$destination_directory" "$log_file" ;; ('bsdtar') archive_extraction_using_bsdtar "$archive" "$destination_directory" "$log_file" ;; ('cabextract') archive_extraction_using_cabextract "$archive" "$destination_directory" "$log_file" ;; ('dpkg-deb') archive_extraction_using_dpkgdeb "$archive" "$destination_directory" "$log_file" ;; ('innoextract') archive_extraction_using_innoextract "$archive" "$destination_directory" "$log_file" ;; ('lha') archive_extraction_using_lha "$archive" "$destination_directory" "$log_file" ;; ('msiextract') archive_extraction_using_msiextract "$archive" "$destination_directory" "$log_file" ;; ('tar') archive_extraction_using_tar "$archive" "$destination_directory" "$log_file" ;; ('unar') archive_extraction_using_unar "$archive" "$destination_directory" "$log_file" ;; ('unshield') archive_extraction_using_unshield "$archive" "$destination_directory" "$log_file" ;; ('unzip') archive_extraction_using_unzip "$archive" "$destination_directory" "$log_file" ;; (*) error_archive_extractor_invalid "$archive_extractor" return 1 ;; esac } # extract data from the target archive, guessing the extractor from the given type # USAGE: archive_extraction_from_type $archive $destination_directory $log_file archive_extraction_from_type() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" local archive_type archive_type=$(archive_type "$archive") if [ -z "$archive_type" ]; then error_archive_type_not_set "$archive" return 1 fi case "$archive_type" in ('7z') archive_extraction_7z "$archive" "$destination_directory" "$log_file" ;; ('appimage') archive_extraction_appimage "$archive" "$destination_directory" "$log_file" ;; ('cabinet') archive_extraction_cabinet "$archive" "$destination_directory" "$log_file" ;; ('debian') archive_extraction_debian "$archive" "$destination_directory" "$log_file" ;; ('innosetup') archive_extraction_innosetup "$archive" "$destination_directory" "$log_file" ;; ('innosetup_nolowercase') warning_archive_type_deprecated "$archive" export ${archive}_EXTRACTOR_OPTIONS='--progress=1 --silent' archive_extraction_innosetup "$archive" "$destination_directory" "$log_file" ;; ('installshield') archive_extraction_installshield "$archive" "$destination_directory" "$log_file" ;; ('iso') archive_extraction_iso "$archive" "$destination_directory" "$log_file" ;; ('lha') archive_extraction_lha "$archive" "$destination_directory" "$log_file" ;; ('makeself') archive_extraction_makeself "$archive" "$destination_directory" "$log_file" ;; ('mojosetup') archive_extraction_mojosetup "$archive" "$destination_directory" "$log_file" ;; ('msi') archive_extraction_msi "$archive" "$destination_directory" "$log_file" ;; ('nullsoft-installer') archive_extraction_nullsoft "$archive" "$destination_directory" "$log_file" ;; ('rar') archive_extraction_rar "$archive" "$destination_directory" "$log_file" ;; ('tar'|'tar.bz2'|'tar.gz'|'tar.xz') archive_extraction_tar "$archive" "$destination_directory" "$log_file" ;; ('zip') archive_extraction_zip "$archive" "$destination_directory" "$log_file" ;; (*) error_archive_type_invalid "$archive_type" return 1 ;; esac } src/30_archives/25_extraction_extra-archives.sh0000644000000000000000000000623713120060140020473 0ustar rootroot# Extract the contents from the extra archives providing required libraries # USAGE: archive_extraction_extra_libraries archive_extraction_extra_libraries() { local libraries_required library_required libraries_required=$(dependencies_list_native_libraries_all) while read -r library_required; do case "$library_required" in ('libcurl.so.4+CURL_OPENSSL_3') archive_extraction_extra_libcurl3 ;; ('libFLAC.so.8') archive_extraction_extra_libflac8 ;; ('libgconf-2.so.4') archive_extraction_extra_libgconf2 ;; ('libidn.so.11') archive_extraction_extra_libidn11 ;; ('libpng12.so.0') archive_extraction_extra_libpng12 ;; ('libssl.so.1.0.0') archive_extraction_extra_libssl100 ;; ('libssl.so.1.1') archive_extraction_extra_libssl11 ;; esac done <<- EOL $(printf '%s' "$libraries_required") EOL } # Extract libcurl.so.3 and libcurl.so.4 including the CURL_OPENSSL_3 symbol # USAGE: archive_extraction_extra_libcurl3 archive_extraction_extra_libcurl3() { archive_extraction 'ARCHIVE_LIBCURL3' } # Extract libFLAC.so.8 # USAGE: archive_extraction_extra_libflac8 archive_extraction_extra_libflac8() { # On Arch Linux, this library is still provided in the packages repositories local option_package option_package=$(option_value 'package') case "$option_package" in ('arch') return 0 ;; esac archive_extraction 'ARCHIVE_LIBFLAC8' } # Extract GConf 2 library # USAGE: archive_extraction_extra_libgconf2 archive_extraction_extra_libgconf2() { archive_extraction 'ARCHIVE_LIBGCONF2' } # Extract GNU Libidn 11 library # USAGE: archive_extraction_extra_libidn11 archive_extraction_extra_libidn11() { # On Arch Linux, this library is still provided in the packages repositories local option_package option_package=$(option_value 'package') case "$option_package" in ('arch') return 0 ;; esac archive_extraction 'ARCHIVE_LIBIDN11' } # Extract PNG 1.2 libraries # USAGE: archive_extraction_extra_libpng12 archive_extraction_extra_libpng12() { # On Arch Linux and Gentoo, this library is still provided in the packages repositories local option_package option_package=$(option_value 'package') case "$option_package" in ('arch'|'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'|'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'|'gentoo') return 0 ;; esac archive_extraction 'ARCHIVE_OPENSSL11' } # Extract the content of an icons pack archive # USAGE: archive_extraction_extra_icons archive_extraction_extra_icons() { archive_extraction 'ARCHIVE_ICONS' } src/30_archives/30_type_7z.sh0000644000000000000000000000177513120060140014705 0ustar rootroot# check the presence of required tools to handle a 7z archive # USAGE: archive_dependencies_check_type_7z archive_dependencies_check_type_7z() { local required_command for required_command in '7zr' '7za' 'unar'; do if command -v "$required_command" >/dev/null 2>&1; then return 0 fi done error_dependency_not_found '7zr' return 1 } # extract the content of a 7z archive # USAGE: archive_extraction_7z $archive $destination_directory $log_file archive_extraction_7z() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" if command -v '7zr' >/dev/null 2>&1; then archive_extraction_using_7zr "$archive" "$destination_directory" "$log_file" elif command -v '7za' >/dev/null 2>&1; then archive_extraction_using_7za "$archive" "$destination_directory" "$log_file" elif command -v 'unar' >/dev/null 2>&1; then archive_extraction_using_unar "$archive" "$destination_directory" "$log_file" else error_archive_no_extractor_found '7z' return 1 fi } src/30_archives/30_type_appimage.sh0000644000000000000000000000217113120060140016117 0ustar rootroot# Check for the availability of commands required to extract the content from AppImage archives # USAGE: archive_dependencies_check_type_appimage archive_dependencies_check_type_appimage() { local required_command for required_command in 'binwalk' 'unsquashfs'; 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 an AppImage archive # USAGE: archive_extraction_appimage $archive $destination_directory $log_file archive_extraction_appimage() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" local archive_path archive_path=$(archive_path "$archive") local archive_offset archive_offset=$(binwalk "$archive_path" | sed --silent 's/\(^[0-9]\+\).*Squashfs filesystem.*/\1/p') if ! { printf 'unsquashfs -offset "%s" -dest "%s" "%s"\n' "$archive_offset" "$destination_directory" "$archive_path" unsquashfs -offset "$archive_offset" -dest "$destination_directory" "$archive_path" } >> "$log_file" then error_archive_extraction_failure "$archive" 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.sh0000644000000000000000000000705513120060140016131 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 if ! { 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" 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" 2>&1 } >> "$log_file" 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' ) ## If no header length is fetched, this might not be a Makeself installer if [ -z "$archive_header_length" ]; then local archive_name archive_name=$(basename "$archive_path") error_makeself_fetching_header_length "$archive_name" return 1 fi 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.sh0000644000000000000000000000453213120060140016364 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_game_data="${destination_directory}/mojosetup-game-data.zip" if ! { 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" dd if="$archive_path" ibs="$archive_block_size" obs="$archive_block_size" skip="${archive_offset}B" > "$archive_game_data" } >> "$log_file" 2>> "$log_file" then error_archive_extraction_failure "$archive" return 1 fi # Extract the game data if ! { ## unzip -o: overwrite existing files without prompting. printf 'unzip -o -d "%s" "%s"\n' "$destination_directory" "$archive_game_data" unzip -o -d "$destination_directory" "$archive_game_data" 2>&1 } >> "$log_file" then error_archive_extraction_failure "$archive" return 1 fi rm "$archive_game_data" } src/30_archives/30_type_msi.sh0000644000000000000000000000134113120060140015122 0ustar rootroot# check the presence of required tools to handle a Windows Installer (.msi) # USAGE: archive_dependencies_check_type_msi archive_dependencies_check_type_msi() { if command -v 'msiextract' >/dev/null 2>&1; then return 0 fi error_dependency_not_found 'msiextract' return 1 } # extract the content of a Windows Installer (.msi) # USAGE: archive_extraction_msi $archive $destination_directory $log_file archive_extraction_msi() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" if command -v 'msiextract' >/dev/null 2>&1; then archive_extraction_using_msiextract "$archive" "$destination_directory" "$log_file" else error_archive_no_extractor_found 'msi' return 1 fi } src/30_archives/30_type_nullsoft.sh0000644000000000000000000000134013120060140016177 0ustar rootroot# check the presence of required tools to handle a NullSoft installer # USAGE: archive_dependencies_check_type_nullsoft archive_dependencies_check_type_nullsoft() { if command -v 'unar' >/dev/null 2>&1; then return 0 fi error_dependency_not_found 'unar' return 1 } # extract the content of a NullSoft installer # USAGE: archive_extraction_nullsoft $archive $destination_directory $log_file archive_extraction_nullsoft() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" if command -v 'unar' >/dev/null 2>&1; then archive_extraction_using_unar "$archive" "$destination_directory" "$log_file" else error_archive_no_extractor_found 'nullsoft-installer' return 1 fi } src/30_archives/30_type_rar.sh0000644000000000000000000000125713120060140015124 0ustar rootroot# check the presence of required tools to handle a RAR archive # USAGE: archive_dependencies_check_type_rar archive_dependencies_check_type_rar() { if command -v 'unar' >/dev/null 2>&1; then return 0 fi error_dependency_not_found 'unar' return 1 } # extract the content of a RAR archive # USAGE: archive_extraction_rar $archive $destination_directory $log_file archive_extraction_rar() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" if command -v 'unar' >/dev/null 2>&1; then archive_extraction_using_unar "$archive" "$destination_directory" "$log_file" else error_archive_no_extractor_found 'rar' return 1 fi } src/30_archives/30_type_tar.sh0000644000000000000000000000307213120060140015123 0ustar rootroot# check the presence of required tools to handle a tar archive # USAGE: archive_dependencies_check_type_tar archive_dependencies_check_type_tar() { if command -v 'tar' >/dev/null 2>&1; then return 0 fi error_dependency_not_found 'tar' return 1 } # check the presence of required tools to handle a tar.bz2 archive # USAGE: archive_dependencies_check_type_tarbz2 archive_dependencies_check_type_tarbz2() { archive_dependencies_check_type_tar if command -v 'bunzip2' >/dev/null 2>&1; then return 0 fi error_dependency_not_found 'bunzip2' return 1 } # check the presence of required tools to handle a tar.gz archive # USAGE: archive_dependencies_check_type_targz archive_dependencies_check_type_targz() { archive_dependencies_check_type_tar if command -v 'gunzip' >/dev/null 2>&1; then return 0 fi error_dependency_not_found 'gunzip' return 1 } # check the presence of required tools to handle a tar.xz archive # USAGE: archive_dependencies_check_type_tarxz archive_dependencies_check_type_tarxz() { archive_dependencies_check_type_tar if command -v 'unxz' >/dev/null 2>&1; then return 0 fi error_dependency_not_found 'unxz' return 1 } # extract the content of a .tar archive # USAGE: archive_extraction_tar $archive $destination_directory $log_file archive_extraction_tar() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" if command -v 'tar' >/dev/null 2>&1; then archive_extraction_using_tar "$archive" "$destination_directory" "$log_file" else error_archive_no_extractor_found 'tar' return 1 fi } src/30_archives/30_type_zip.sh0000644000000000000000000000126613120060140015142 0ustar rootroot# check the presence of required tools to handle a .zip archive # USAGE: archive_dependencies_check_type_zip archive_dependencies_check_type_zip() { if command -v 'unzip' >/dev/null 2>&1; then return 0 fi error_dependency_not_found 'unzip' return 1 } # extract the content of a .zip archive # USAGE: archive_extraction_zip $archive $destination_directory $log_file archive_extraction_zip() { local archive destination_directory log_file archive="$1" destination_directory="$2" log_file="$3" if command -v 'unzip' >/dev/null 2>&1; then archive_extraction_using_unzip "$archive" "$destination_directory" "$log_file" else error_archive_no_extractor_found 'zip' return 1 fi } src/30_archives/40_extractor_7za.sh0000644000000000000000000000134713120060140016074 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 extractor_options=$(archive_extractor_options "$archive") if [ -z "$extractor_options" ]; then extractor_options='-y' fi if ! { printf '7za x %s -o"%s" "%s"\n' "$extractor_options" "$destination_directory" "$archive_path" 7za x $extractor_options -o"$destination_directory" "$archive_path" 2>&1 } >> "$log_file" then error_archive_extraction_failure "$archive" return 1 fi } src/30_archives/40_extractor_7zr.sh0000644000000000000000000000134713120060140016115 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 extractor_options=$(archive_extractor_options "$archive") if [ -z "$extractor_options" ]; then extractor_options='-y' fi if ! { printf '7zr x %s -o"%s" "%s"\n' "$extractor_options" "$destination_directory" "$archive_path" 7zr x $extractor_options -o"$destination_directory" "$archive_path" 2>&1 } >> "$log_file" then error_archive_extraction_failure "$archive" return 1 fi } src/30_archives/40_extractor_bsdtar.sh0000644000000000000000000000137113120060140016647 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 extractor_options=$(archive_extractor_options "$archive") if ! { printf 'bsdtar --verbose %s --directory "%s" --extract --file "%s"\n' "$extractor_options" "$destination_directory" "$archive_path" bsdtar --verbose $extractor_options --directory "$destination_directory" --extract --file "$archive_path" 2>&1 } >> "$log_file" then error_archive_extraction_failure "$archive" return 1 fi } src/30_archives/40_extractor_cabextract.sh0000644000000000000000000000141013120060140017502 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 extractor_options=$(archive_extractor_options "$archive") if [ -z "$extractor_options" ]; then extractor_options='-L' fi if ! { printf 'cabextract %s -d "%s" "%s"\n' "$extractor_options" "$destination_directory" "$archive_path" cabextract $extractor_options -d "$destination_directory" "$archive_path" 2>&1 } >> "$log_file" then error_archive_extraction_failure "$archive" return 1 fi } src/30_archives/40_extractor_dpkgdeb.sh0000644000000000000000000000133313120060140016766 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 extractor_options=$(archive_extractor_options "$archive") if ! { printf 'dpkg-deb --verbose %s --extract "%s" "%s"\n' "$extractor_options" "$archive_path" "$destination_directory" dpkg-deb --verbose $extractor_options --extract "$archive_path" "$destination_directory" 2>&1 } >> "$log_file" then error_archive_extraction_failure "$archive" return 1 fi } src/30_archives/40_extractor_innoextract.sh0000644000000000000000000000357113120060140017732 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 extractor_options=$(archive_extractor_options "$archive") if [ -z "$extractor_options" ]; then ## TODO: Conversion of file paths to lower case should not be enforced. ## To ensure backwards compatibility, it should still be applied based on the compatibility level. extractor_options='--lowercase' fi if ! { printf 'innoextract %s --extract --output-dir "%s" "%s"\n' "$extractor_options" "$destination_directory" "$archive_path" innoextract $extractor_options --extract --output-dir "$destination_directory" "$archive_path" 2>&1 } >> "$log_file" 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.sh0000644000000000000000000000121213120060140016126 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") # Due to its unusual command syntax, lha extractor has no support for ARCHIVE_xxx_EXTRACTOR_OPTIONS if ! { printf 'lha -ew="%s" "%s"\n' "$destination_directory" "$archive_path" lha -ew="$destination_directory" "$archive_path" 2>&1 } >> "$log_file" then error_archive_extraction_failure "$archive" return 1 fi } src/30_archives/40_extractor_msiextract.sh0000644000000000000000000000147213120060140017555 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 extractor_options=$(archive_extractor_options "$archive") if ! { printf 'msiextract %s --directory "%s" "%s"\n' "$extractor_options" "$destination_directory" "$archive_path" msiextract $extractor_options --directory "$destination_directory" "$archive_path" 2>&1 } >> "$log_file" then error_archive_extraction_failure "$archive" return 1 fi ## TODO: Paths conversion to lower case should not be enforced. tolower "$destination_directory" } src/30_archives/40_extractor_tar.sh0000644000000000000000000000207413120060140016157 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 extractor_options=$(archive_extractor_options "$archive") ## Set default options based on archive type. if [ -z "$extractor_options" ]; then local archive_type archive_type=$(archive_type "$archive") case "$archive_type" in ('tar.bz2') extractor_options='--bzip2' ;; ('tar.gz') extractor_options='--gzip' ;; ('tar.xz') extractor_options='--xz' ;; esac fi if ! { printf 'tar --verbose %s --extract --file "%s" --directory "%s"\n' "$extractor_options" "$archive_path" "$destination_directory" tar --verbose $extractor_options --extract --file "$archive_path" --directory "$destination_directory" 2>&1 } >> "$log_file" then error_archive_extraction_failure "$archive" return 1 fi } src/30_archives/40_extractor_unar.sh0000644000000000000000000000144413120060140016336 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 extractor_options=$(archive_extractor_options "$archive") if [ -z "$extractor_options" ]; then extractor_options='-force-overwrite -no-directory' fi if ! { printf 'unar %s -output-directory "%s" "%s"\n' "$extractor_options" "$destination_directory" "$archive_path" unar $extractor_options -output-directory "$destination_directory" "$archive_path" 2>&1 } >> "$log_file" then error_archive_extraction_failure "$archive" return 1 fi } src/30_archives/40_extractor_unshield.sh0000644000000000000000000000140213120060140017176 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 extractor_options=$(archive_extractor_options "$archive") if [ -z "$extractor_options" ]; then extractor_options='-L' fi if ! { printf 'unshield %s -d "%s" x "%s"\n' "$extractor_options" "$destination_directory" "$archive_path" unshield $extractor_options -d "$destination_directory" x "$archive_path" 2>&1 } >> "$log_file" then error_archive_extraction_failure "$archive" return 1 fi } src/30_archives/40_extractor_unzip.sh0000644000000000000000000000145213120060140016535 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 extractor_options=$(archive_extractor_options "$archive") if [ -z "$extractor_options" ]; then ## unzip -o: overwrite existing files without prompting. extractor_options='-o' fi if ! { printf 'unzip %s -d "%s" "%s"\n' "$extractor_options" "$destination_directory" "$archive_path" unzip $extractor_options -d "$destination_directory" "$archive_path" 2>&1 } >> "$log_file" 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.sh0000644000000000000000000002632113120060140015113 0ustar rootroot# display the name of a file currently processed # USAGE: information_file_in_use $file information_file_in_use() { local file file="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Utilisation de %s\n' ;; ('en'|*) message='Using %s\n' ;; esac print_message 'info' "$message" } # print data extraction message # USAGE: information_archive_data_extraction $file information_archive_data_extraction() { local file file="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Extraction des données de %s…\n' ;; ('en'|*) message='Extracting data from %s…\n' ;; esac print_message 'info' "$message" \ "$file" } # print hash computation message # USAGE: info_archive_hash_computation $file_path info_archive_hash_computation() { local file_path file_path="$1" local file_name file_name=$(basename "$file_path") local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Calcul de la somme de contrôle de %s…\n' ;; ('en'|*) message='Computing hashsum for %s…\n' ;; esac print_message 'info' "$message" \ "$file_name" } # display a list of archives, one per line, with their download URL if one is provided # USAGE: information_archives_list $archive[…] information_archives_list() { ## TODO: The archive URL should be fetched using a dedicated function. local archive archive_name archive_url for archive in "$@"; do archive_name=$(archive_name "$archive") archive_url=$(get_value "${archive}_URL") if [ -n "$archive_url" ]; then print_message 'info' '%s — %s\n' \ "$archive_name" \ "$archive_url" else print_message 'info' '%s\n' \ "$archive_name" fi done } # Warning - An optional icons archive is supported, but not available # USAGE: warning_optional_archive_missing_icons $archive warning_optional_archive_missing_icons() { local archive archive="$1" ## TODO: The archive URL should be fetched using a dedicated function. local archive_name archive_url archive_name=$(archive_name "$archive") archive_url=$(get_value "${archive}_URL") local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Une archive proposant des icônes pour ce jeu est disponible, mais n’a pas été trouvée : %s\n' message="$message"'Elle peut être téléchargée depuis l’URL suivante : %s\n\n' ;; ('en'|*) message='An archive providing icons for this game is available, but could not be found: %s\n' message="$message"'It can be downloaded from the following URL: %s\n\n' ;; esac print_message 'warning' "$message" \ "$archive_name" \ "$archive_url" } # Error - No archive supported for the current game script error_no_archive_supported() { local game_script game_script=$(realpath "$0") local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Ce script semble ne prendre en charge aucune archive : %s\n' ;; ('en'|*) message='This script seems to support no archive: %s\n' ;; esac print_message 'error' "$message" \ "$game_script" } # Error - No extractor is available to handle the given archive # USAGE: error_archive_no_extractor_found $archive_type error_archive_no_extractor_found() { local archive_type archive_type="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Ce script a essayé dʼextraire le contenu dʼune archive de type "%s", mais aucun outil approprié nʼa été trouvé.\n' ;; ('en'|*) message='This script tried to extract the contents of a "%s" archive, but not appropriate tool could be found.\n' ;; esac print_message 'error' "$message" \ "$archive_type" } # Error - A required archive is not found # List all the archives that could fulfill the requirement, with their download URL if one is provided # USAGE: error_archive_not_found $archive[…] error_archive_not_found() { # Get the path to the directory where the current archive is found local archives_path archives_path_full archives_path=$(archives_path_base) archives_path_full=$(realpath --no-symlinks "$archives_path") local messages_language message_1 message_2 messages_language=$(messages_language) case "$messages_language" in ('fr') if [ $# -eq 1 ]; then message_1='Le fichier suivant est introuvable dans %s :\n' else message_1='Aucun des fichiers suivants nʼest présent dans %s :\n' fi message_2='Vous devez télécharger le fichier requis avant de continuer.\n' ;; ('en'|*) if [ $# -eq 1 ]; then message_1='The following file could not be found in %s:\n' else message_1='None of the following files could be found in %s:\n' fi message_2='Please download the required file before proceeding.\n' ;; esac print_message 'error' "$message_1" \ "$archives_path_full" information_archives_list "$@" > /dev/stderr print_message 'info' "$message_2" > /dev/stderr } # Error - The type of the given archive could not be guessed # USAGE: error_archive_type_not_set $archive error_archive_type_not_set() { local archive archive="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='ARCHIVE_TYPE nʼest pas défini pour %s et nʼa pas pu être détecté automatiquement.\n' ;; ('en'|*) message='ARCHIVE_TYPE is not set for %s and could not be guessed.\n' ;; esac print_message 'error' "$message" \ "$archive" } # Error - An integrity check failed # USAGE: error_hashsum_mismatch $file_path error_hashsum_mismatch() { local file_path file_path="$1" local file_name file_name=$(basename "$file_path") local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Somme de contrôle incohérente. %s nʼest pas le fichier attendu.\n' message="$message"'Utilisez --checksum=none pour forcer son utilisation.\n' ;; ('en'|*) message='Hashsum mismatch. %s is not the expected file.\n' message="$message"'Use --checksum=none to force its use.\n' ;; esac print_message 'error' "$message" \ "$file_name" } # Error - The available version of innoextract is too old # USAGE: error_innoextract_version_too_old $archive error_innoextract_version_too_old() { local archive archive="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La version de innoextract disponible sur ce système est trop ancienne pour extraire les données de lʼarchive suivante : %s\n' ;; ('en'|*) message='Available innoextract version is too old to extract data from the following archive: %s\n' ;; esac print_message 'error' "$message" \ "$archive" } # Error - Invalid value used for archive type # USAGE: error_archive_type_invalid $archive_type error_archive_type_invalid() { local archive_type archive_type="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La valeur suivante ne correspond pas à un type dʼarchive connu : "%s"\n' ;; ('en'|*) message='The following value is not a valid archive type: "%s"\n' ;; esac print_message 'error' "$message" \ "$archive_type" } # Error - Invalid value used for archive extractor # USAGE: error_archive_extractor_invalid $archive_extractor error_archive_extractor_invalid() { local archive_extractor archive_extractor="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La valeur suivante ne correspond pas à un extracteur dʼarchive connu : "%s"\n' ;; ('en'|*) message='The following value is not a valid archive extractor: "%s"\n' ;; esac print_message 'error' "$message" \ "$archive_extractor" } # Error - Archive data extraction failed # USAGE: error_archive_extraction_failure $archive error_archive_extraction_failure() { local archive archive="$1" local archive_name archive_name=$(archive_name "$archive") local log_file log_file=$(archive_extraction_log_path) local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Lʼextraction des données depuis lʼarchive suivante a échoué : %s\n' message="$message"'Vous pouvez obtenir plus de détails dans le fichier journal : %s\n' ;; ('en'|*) message='Data extraction from the following archive failed: %s\n' message="$message"'You can get more details from the following log file: %s\n' ;; esac print_message 'error' "$message" \ "$archive_name" \ "$log_file" } # Error - The header length could not be fetched from the given Makeself archive # USAGE: error_makeself_fetching_header_length $archive_name error_makeself_fetching_header_length() { local archive_name archive_name="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La récupération de la longueur de l’en-tête de l’archive a échoué.\n' message="$message"'Il s’agit peut-être d’une archive identifiée à tort comme un installateur Makeself.\n' ;; ('en'|*) message='Fetching the header length from the archive failed.\n' message="$message"'This might be an archive that has been wrongly identified as a Makeself installer.\n' ;; esac print_message 'error' "$message" \ "$archive_name" } # Error - archive_extraction_default has been called before setting the current archive # USAGE: error_archive_extraction_default_missing_archive error_archive_extraction_default_missing_archive() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La fonction archive_extraction_default a été appelée alors qu’aucune archive n’a été définie.\n' message="$message"'Une étape essentielle de l’initialisation n’a pas été effectuée correctement.\n' ;; ('en'|*) message='The archive_extraction_default function has been called but no archive has been set yet.\n' message="$message"'A required initialization step has not been run as it should.\n' ;; esac print_message 'error' "$message" } # Error - An invalid format is used for archive version # USAGE: error_archive_version_format $archive_version error_archive_version_format() { local archive_version archive_version="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le numéro de version fournie pour l’archive courante est invalide : "%s"\n' message="$message"'Cette valeur ne peut utiliser que des caractères du set [-.a-z0-9],' message="$message"' doit commencer par un chiffre,' message="$message"' et doit inclure un tiret.\n' ;; ('en'|*) message='The version string provided by the current archive is invalid: "%s"\n' message="$message"'The value should only include characters from the set [-.a-z0-9],' message="$message"' must start with a digit,' message="$message"' and must include an hyphen.\n' ;; esac print_message 'error' "$message" \ "$archive_version" } src/30_content/00_common.sh0000644000000000000000000000616213120060140014432 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_default content_path_default=$(context_value 'CONTENT_PATH_DEFAULT') if [ -z "$content_path_default" ]; then error_missing_variable 'CONTENT_PATH_DEFAULT' return 1 fi printf '%s' "$content_path_default" } # 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() { ## The content id is usually a concatenation of one of the following prefixes with a package suffix: ## - DOC ## - FONTS ## - GAME ## - LIBS ## This is not a hard requirement, any arbitrary value can be used local content_id content_id="$1" # Use the path relative to the archive root if it is set local content_path content_path=$(context_value "CONTENT_${content_id}_PATH") # Use the path relative to $CONTENT_PATH_DEFAULT if it is set # It overrides the path relative to the archive root is this one is already set local content_path_relative content_path_relative=$(context_value "CONTENT_${content_id}_RELATIVE_PATH") if [ -n "$content_path_relative" ]; then local content_path_default content_path_default=$(content_path_default) content_path="${content_path_default}/${content_path_relative}" 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 on the default content path if no explicit value is set if [ -z "$content_path" ]; then ## Do not fail if no default path is set, an empty value is returned instead ## The output redirection must be done inside the subshell, or bash --posix will ignore it 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") # 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 ('ags') content_files=$(ags_content_files_default "$content_id") ;; ('gamemaker') content_files=$(content_files_gamemaker "$content_id") ;; ('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.sh0000644000000000000000000003127113120060140016245 0ustar rootroot# Fetch icon files, convert them to the expected format, include them in the given package. # # This function is the one that should be called from game scripts, # it can take several applications as its arguments, # and default to handle all applications if none are explicitely given. # # USAGE: content_inclusion_icons [$package [$application…]] content_inclusion_icons() { # Do nothing if icons inclusion has been disabled. local option_icons option_icons=$(option_value 'icons') if [ "$option_icons" -eq 0 ]; then return 0 fi # Ensure that the commands required for icons extraction are available requirements_check_icons # Get the package that should include the icons. local package if [ $# -ge 1 ]; then package="$1" shift 1 else package=$(current_package) fi # If an optional icons archive has been provided, use that instead of the icons shipped with the game if archive_is_available 'ARCHIVE_ICONS'; then information_icons_inclusion content_inclusion_optional_icons_archive "$package" ## Return early if an optional icons archive has been used. return 0 fi # If no applications are explicitely listed, # try to fetch the icons for all applications. if [ "$#" -eq 0 ]; then applications_list=$(applications_list) ## If content_inclusion_icons has been called with no argument, the applications list should not be empty. if [ -z "$applications_list" ]; then error_applications_list_empty return 1 fi content_inclusion_icons "$package" $applications_list return 0 fi information_icons_inclusion local application for application in "$@"; do icons_inclusion_single_application "$package" "$application" done } # Fetch files from the archive, and include them into the package skeleton. # A list of default content identifiers is used. # USAGE: content_inclusion_default content_inclusion_default() { information_content_inclusion local packages_list packages_list=$(packages_list) local package unity3d_plugins for package in $packages_list; do ## TODO: content_inclusion_unity3d_plugins should always be called with Unity3D games. ## If UNITY3D_PLUGINS is empty no plugin should be included, and the Plugins directory deleted. unity3d_plugins=$(unity3d_plugins) if [ -n "$unity3d_plugins" ]; then content_inclusion_unity3d_plugins "$package" fi content_inclusion_default_libraries "$package" content_inclusion_default_fonts "$package" content_inclusion_default_game_data "$package" content_inclusion_default_documentation "$package" done # Including files used to apply tweaks to the WINE prefix should only be done in the packages including the game binaries. local package_architecture for package in $packages_list; do package_architecture=$(package_architecture "$package") case "$package_architecture" in ('64'|'32') content_inclusion_wineprefix_tweaks "$package" ;; esac done # content_inclusion_extra_libraries must be called in its own loop, at the end of the content inclusion process. # Since it can trigger an archive_extraction call leading to a set_standard_permissions call, # calling it earlier could lead to unwanted permissions reset. for package in $packages_list; do content_inclusion_extra_libraries "$package" done # Delete the remaining files extracted from archives but not included in any package ## Skip the automatic clean-up for game scripts targeting ./play.it ≤ 2.25. if ! compatibility_level_is_at_least '2.26'; then return 0 fi rm --force --recursive "${PLAYIT_WORKDIR}/gamedata" } # Fetch files from the archive, and include them into the package skeleton. # A list of default content identifiers is used, limited to native libraries for a single package. # USAGE: content_inclusion_default_libraries $package content_inclusion_default_libraries() { local package package="$1" local package_suffix files_list package_suffix="${package#PKG_}" files_list=$(context_name "CONTENT_LIBS_${package_suffix}_FILES") if [ -z "$files_list" ]; then ## Check if a default files list is set for the current engine, ## return early otherwise. local content_files content_files=$(content_files "LIBS_${package_suffix}") if [ -z "$content_files" ]; then return 0 fi fi local target_directory ## On Arch Linux and Gentoo the libraries path can change based on the package architecture. target_directory=$( set_current_package "$package" path_libraries ) content_inclusion "LIBS_${package_suffix}" "$package" "$target_directory" local index for index in $(seq 0 9); do files_list=$(context_name "CONTENT_LIBS${index}_${package_suffix}_FILES") if [ -z "$files_list" ]; then ## Stop looping at the first unset files list. return 0 fi content_inclusion "LIBS${index}_${package_suffix}" "$package" "$target_directory" done } # Fetch files from the archive, and include them into the package skeleton. # A list of default content identifiers is used, limited to TTF fonts for a single package. # USAGE: content_inclusion_default_fonts $package content_inclusion_default_fonts() { local package package="$1" local package_suffix files_list package_suffix="${package#PKG_}" files_list=$(context_name "CONTENT_FONTS_${package_suffix}_FILES") if [ -z "$files_list" ]; then ## Check if a default files list is set for the current engine, ## return early otherwise. local content_files content_files=$(content_files "FONTS_${package_suffix}") if [ -z "$content_files" ]; then return 0 fi fi local target_directory target_directory=$( set_current_package "$package" path_fonts_ttf ) content_inclusion "FONTS_${package_suffix}" "$package" "$target_directory" local index for index in $(seq 0 9); do files_list=$(context_name "CONTENT_FONTS${index}_${package_suffix}_FILES") if [ -z "$files_list" ]; then ## Stop looping at the first unset files list. return 0 fi content_inclusion "FONTS${index}_${package_suffix}" "$package" "$target_directory" done } # Fetch files from the archive, and include them into the package skeleton. # A list of default content identifiers is used, limited to game data files for a single package. # USAGE: content_inclusion_default_game_data $package content_inclusion_default_game_data() { local package package="$1" local package_suffix files_list package_suffix="${package#PKG_}" files_list=$(context_name "CONTENT_GAME_${package_suffix}_FILES") if [ -z "$files_list" ]; then ## Check if a default files list is set for the current engine, ## return early otherwise. local content_files content_files=$(content_files "GAME_${package_suffix}") if [ -z "$content_files" ]; then return 0 fi fi local target_directory target_directory=$( set_current_package "$package" path_game_data ) content_inclusion "GAME_${package_suffix}" "$package" "$target_directory" local index for index in $(seq 0 9); do files_list=$(context_name "CONTENT_GAME${index}_${package_suffix}_FILES") if [ -z "$files_list" ]; then ## Stop looping at the first unset files list. return 0 fi content_inclusion "GAME${index}_${package_suffix}" "$package" "$target_directory" done } # Fetch files from the archive, and include them into the package skeleton. # A list of default content identifiers is used, limited to documentation files for a single package. # USAGE: content_inclusion_default_documentation $package content_inclusion_default_documentation() { local package package="$1" local package_suffix files_list package_suffix="${package#PKG_}" files_list=$(context_name "CONTENT_DOC_${package_suffix}_FILES") if [ -z "$files_list" ]; then ## Check if a default files list is set for the current engine, ## return early otherwise. local content_files content_files=$(content_files "DOC_${package_suffix}") if [ -z "$content_files" ]; then return 0 fi fi local target_directory target_directory=$( set_current_package "$package" path_documentation ) content_inclusion "DOC_${package_suffix}" "$package" "$target_directory" local index for index in $(seq 0 9); do files_list=$(context_name "CONTENT_DOC${index}_${package_suffix}_FILES") if [ -z "$files_list" ]; then ## Stop looping at the first unset files list. return 0 fi content_inclusion "DOC${index}_${package_suffix}" "$package" "$target_directory" done } # Fetch files from the archive, and include them into the package skeleton. # USAGE: content_inclusion $content_id $package $target_path content_inclusion() { local content_id package target_path content_id="$1" package="$2" target_path="$3" information_content_inclusion # Check that the given package is valid if ! package_is_included_in_packages_list "$package"; then error_current_package_not_in_list "$package" return 1 fi # Return early if the content source path is not set. local content_path ## content_path relies on the package context being set, through a call to content_path_default. content_path=$( set_current_package "$package" content_path "$content_id" ) if [ -z "$content_path" ]; then return 0 fi # Return early if the content source path does not exist. content_path_full="${PLAYIT_WORKDIR}/gamedata/${content_path}" if [ ! -e "$content_path_full" ]; then return 0 fi # Debian - Handle huge files by splitting them in 9GB chunks, # and include the chunks in dedicated packages. if [ "$content_id" = "GAME_${package#PKG_}" ]; then local option_package option_package=$(option_value 'package') if [ "$option_package" = 'deb' ]; then local huge_files huge_files=$(huge_files_list "$package") if [ -n "$huge_files" ]; then content_inclusion_chunks "$package" fi fi fi # Set path to destination, # ensuring it is an absolute path. local package_path destination_path package_path=$(package_path "$package") destination_path=$(realpath --canonicalize-missing "${package_path}${target_path}") # Proceed with the actual files inclusion content_inclusion_include_paths "$content_id" "$destination_path" } # Convert a list of patterns to include into a series of find options # USAGE: content_inclusion_find_options $content_identifier # RETURN: a full find options string, using null-byte as separator content_inclusion_find_options() { local content_identifier content_identifier="$1" local content_patterns_list content_patterns_list=$(content_files "$content_identifier") local content_pattern first_path_shown printf_format first_path_shown=0 printf '.\0(\0' while read -r content_pattern; do ## Skip empty lines. if [ -z "$content_pattern" ]; then continue fi if [ $first_path_shown -eq 0 ]; then printf_format='-ipath\0./%s' first_path_shown=1 else printf_format='\0-o\0-ipath\0./%s' fi ## Silence ShellCheck false-positive ## Don't use variables in the printf format string. Use printf "..%s.." "$foo". # shellcheck disable=SC2059 printf -- "$printf_format" "${content_pattern#./}" done <<- EOL $(printf '%s' "$content_patterns_list") EOL printf '\0)\0-print0' } # Display the full list of paths to include, using a null-byte as the separator # USAGE: content_inclusion_list_paths $content_identifier # RETURN: a sorted list of paths, separated by null-bytes content_inclusion_list_paths() { local content_identifier content_identifier="$1" local content_path content_path_full content_path=$(content_path "$content_identifier") content_path_full="${PLAYIT_WORKDIR}/gamedata/${content_path}" ## TODO: env --chdir could be used instead of cd in a subshell. ( cd "$content_path_full" content_inclusion_find_options "$content_identifier" | xargs --null find ) | env --ignore-environment sort --zero-terminated } # Fetch a list of paths from the archives contents # USAGE: content_inclusion_include_paths $content_identifier $destination_path content_inclusion_include_paths() { local content_identifier destination_path content_identifier="$1" destination_path="$2" local content_path content_path_full paths_list_base64 content_path=$(content_path "$content_identifier") content_path_full="${PLAYIT_WORKDIR}/gamedata/${content_path}" mkdir --parents "$destination_path" ## TODO: env --chdir could be used instead of cd in a subshell. ( cd "$content_path_full" ## The list of paths is encoded in base64, because it uses null bytes as a separator and null bytes can not be stored in a variable. paths_list_base64=$(content_inclusion_list_paths "$content_identifier" | base64 --wrap=0) printf '%s' "$paths_list_base64" | base64 --decode | xargs --null --no-run-if-empty cp \ --force \ --link \ --recursive \ --no-dereference \ --parents \ --preserve=links \ --target-directory "$destination_path" ## Delete paths that have already been copied, so they will not end up duplicated in the packages. printf '%s' "$paths_list_base64" | base64 --decode | xargs --null --no-run-if-empty rm \ --force \ --recursive ) } src/30_content/15_files-inclusion_extra-libraries.sh0000644000000000000000000003102313120060140021422 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_libraries_list "$package") while read -r library_required; do case "$library_required" in ('libcurl.so.4+CURL_OPENSSL_3') content_inclusion_extra_libraries_libcurl3 "$package" ;; ('libFLAC.so.8') content_inclusion_extra_libraries_libflac8 "$package" ;; ('libgconf-2.so.4') content_inclusion_extra_libraries_libgconf2 "$package" ;; ('libidn.so.11') content_inclusion_extra_libraries_libidn11 "$package" ;; ('libpng12.so.0') content_inclusion_extra_libraries_libpng12 "$package" ;; ('libssl.so.1.0.0') content_inclusion_extra_libraries_libssl100 "$package" ;; ('libssl.so.1.1') content_inclusion_extra_libraries_libssl11 "$package" ;; esac done <<- EOL $(printf '%s' "$libraries_required") EOL } # Include libcurl.so.3 and libcurl.so.4 including the CURL_OPENSSL_3 symbol # USAGE: content_inclusion_extra_libraries_libcurl3 $package content_inclusion_extra_libraries_libcurl3() { local package package="$1" # Set the list of files to include from the provided archive local package_architecture CONTENT_LIBCURL3_PATH CONTENT_LIBCURL3_FILES package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') ## Silence ShellCheck false-positive ## CONTENT_LIBCURL3_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBCURL3_PATH='x86_32' ;; ('64') ## Silence ShellCheck false-positive ## CONTENT_LIBCURL3_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBCURL3_PATH='x86_64' ;; esac ## Silence ShellCheck false-positive ## CONTENT_LIBCURL3_FILES appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBCURL3_FILES=' libcrypto.so.1.0.2 libssl.so.1.0.2 libcurl.so.3 libcurl.so.4 libcurl.so.4.4.0' # Proceed with the actual files inclusion local path_libraries path_libraries=$( set_current_package "$package" path_libraries ) content_inclusion 'LIBCURL3' "$package" "$path_libraries" # Update the list of dependencies on native libraries local extra_native_libraries_required extra_native_libraries_required=' libcom_err.so.2 libc.so.6 libdl.so.2 libgssapi_krb5.so.2 libidn2.so.0 libk5crypto.so.3 libkrb5.so.3 libnghttp2.so.14 libpsl.so.5 libpthread.so.0 librtmp.so.1 libssh2.so.1 libz.so.1' dependencies_add_native_libraries "$package" "$extra_native_libraries_required" } # Include libFLAC.so.8 # USAGE: content_inclusion_extra_libraries_libflac8 $package content_inclusion_extra_libraries_libflac8() { # On Arch Linux, this library is still provided in the packages repositories local option_package option_package=$(option_value 'package') case "$option_package" in ('arch') return 0 ;; esac local package package="$1" # Set the list of files to include from the provided archive local package_architecture CONTENT_LIBFLAC8_PATH CONTENT_LIBFLAC8_FILES package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') ## Silence ShellCheck false-positive ## CONTENT_LIBFLAC8_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBFLAC8_PATH='i386-linux-gnu' ;; ('64') ## Silence ShellCheck false-positive ## CONTENT_LIBFLAC8_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBFLAC8_PATH='x86_64-linux-gnu' ;; esac ## Silence ShellCheck false-positive ## CONTENT_LIBFLAC8_FILES appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBFLAC8_FILES=' libFLAC.so.8 libFLAC.so.8.3.0' # Proceed with the actual files inclusion local path_libraries path_libraries=$( set_current_package "$package" path_libraries ) content_inclusion 'LIBFLAC8' "$package" "$path_libraries" # Update the list of dependencies on native libraries local extra_native_libraries_required extra_native_libraries_required=' libc.so.6 libm.so.6 libogg.so.0' dependencies_add_native_libraries "$package" "$extra_native_libraries_required" } # Include GConf 2 library # USAGE: content_inclusion_extra_libraries_libgconf2 $package content_inclusion_extra_libraries_libgconf2() { local package package="$1" # Set the list of files to include from the provided archive local package_architecture CONTENT_LIBGCONF2_PATH CONTENT_LIBGCONF2_FILES package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') ## Silence ShellCheck false-positive ## CONTENT_LIBGCONF2_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBGCONF2_PATH='i386-linux-gnu' ;; ('64') ## Silence ShellCheck false-positive ## CONTENT_LIBGCONF2_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBGCONF2_PATH='x86_64-linux-gnu' ;; esac ## Silence ShellCheck false-positive ## CONTENT_LIBGCONF2_FILES appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBGCONF2_FILES=' libgconf-2.so.4 libgconf-2.so.4.1.5' # Proceed with the actual files inclusion local path_libraries path_libraries=$( set_current_package "$package" path_libraries ) content_inclusion 'LIBGCONF2' "$package" "$path_libraries" # Update the list of dependencies on native libraries local extra_native_libraries_required extra_native_libraries_required=' libc.so.6 libdbus-1.so.3 libdbus-glib-1.so.2 libglib-2.0.so.0 libgmodule-2.0.so.0 libgobject-2.0.so.0' dependencies_add_native_libraries "$package" "$extra_native_libraries_required" } # Include GNU Libidn library # USAGE: content_inclusion_extra_libraries_libidn11 $package content_inclusion_extra_libraries_libidn11() { # On Arch Linux, this library is still provided in the packages repositories local option_package option_package=$(option_value 'package') case "$option_package" in ('arch') return 0 ;; esac local package package="$1" # Set the list of files to include from the provided archive local package_architecture CONTENT_LIBIDN11_PATH CONTENT_LIBIDN11_FILES package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') ## Silence ShellCheck false-positive ## CONTENT_LIBIDN11_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBIDN11_PATH='i386-linux-gnu' ;; ('64') ## Silence ShellCheck false-positive ## CONTENT_LIBIDN11_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBIDN11_PATH='x86_64-linux-gnu' ;; esac ## Silence ShellCheck false-positive ## CONTENT_LIBIDN11_FILES appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBIDN11_FILES=' libidn.so.11 libidn.so.11.6.16' # Proceed with the actual files inclusion local path_libraries path_libraries=$( set_current_package "$package" path_libraries ) content_inclusion 'LIBIDN11' "$package" "$path_libraries" # Update the list of dependencies on native libraries local extra_native_libraries_required extra_native_libraries_required=' libc.so.6' dependencies_add_native_libraries "$package" "$extra_native_libraries_required" } # Include PNG 1.2 libraries # USAGE: content_inclusion_extra_libraries_libpng12 $package content_inclusion_extra_libraries_libpng12() { # On Arch Linux and Gentoo, this library is still provided in the packages repositories local option_package option_package=$(option_value 'package') case "$option_package" in ('arch'|'gentoo') return 0 ;; esac local package package="$1" # Set the list of files to include from the provided archive local package_architecture CONTENT_LIBPNG12_PATH CONTENT_LIBPNG12_FILES package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') ## Silence ShellCheck false-positive ## CONTENT_LIBPNG12_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBPNG12_PATH='x86_32' ;; ('64') ## Silence ShellCheck false-positive ## CONTENT_LIBPNG12_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBPNG12_PATH='x86_64' ;; esac ## Silence ShellCheck false-positive ## CONTENT_LIBPNG12_FILES appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_LIBPNG12_FILES=' libpng12.so.0 libpng12.so.0.50.0' # Proceed with the actual files inclusion local path_libraries path_libraries=$( set_current_package "$package" path_libraries ) content_inclusion 'LIBPNG12' "$package" "$path_libraries" # Update the list of dependencies on native libraries local extra_native_libraries_required extra_native_libraries_required=' libc.so.6 libm.so.6 libz.so.1' dependencies_add_native_libraries "$package" "$extra_native_libraries_required" } # Include OpenSSL 1.0.0 libraries # USAGE: content_inclusion_extra_libraries_libssl100 $package content_inclusion_extra_libraries_libssl100() { # On Arch Linux and Gentoo, this library is still provided in the packages repositories local option_package option_package=$(option_value 'package') case "$option_package" in ('arch''gentoo') return 0 ;; esac local package package="$1" # Set the list of files to include from the provided archive local package_architecture CONTENT_OPENSSL100_PATH CONTENT_OPENSSL100_FILES package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') ## Silence ShellCheck false-positive ## CONTENT_OPENSSL100_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_OPENSSL100_PATH='x86_32' ;; ('64') ## Silence ShellCheck false-positive ## CONTENT_OPENSSL100_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_OPENSSL100_PATH='x86_64' ;; esac ## Silence ShellCheck false-positive ## CONTENT_OPENSSL100_FILES appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_OPENSSL100_FILES=' libcrypto.so.1.0.0 libssl.so.1.0.0' # Proceed with the actual files inclusion local path_libraries path_libraries=$( set_current_package "$package" path_libraries ) content_inclusion 'OPENSSL100' "$package" "$path_libraries" # Update the list of dependencies on native libraries local extra_native_libraries_required extra_native_libraries_required=' libc.so.6 libdl.so.2' dependencies_add_native_libraries "$package" "$extra_native_libraries_required" } # Include OpenSSL 1.1 libraries # USAGE: content_inclusion_extra_libraries_libssl11 $package content_inclusion_extra_libraries_libssl11() { # On Arch Linux and Gentoo, this library is still provided in the packages repositories local option_package option_package=$(option_value 'package') case "$option_package" in ('arch'|'gentoo') return 0 ;; esac local package package="$1" # Set the list of files to include from the provided archive local package_architecture CONTENT_OPENSSL11_PATH CONTENT_OPENSSL11_FILES package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') ## Silence ShellCheck false-positive ## CONTENT_OPENSSL11_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_OPENSSL11_PATH='i386-linux-gnu' ;; ('64') ## Silence ShellCheck false-positive ## CONTENT_OPENSSL11_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_OPENSSL11_PATH='x86_64-linux-gnu' ;; esac ## Silence ShellCheck false-positive ## CONTENT_OPENSSL11_FILES appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_OPENSSL11_FILES=' libcrypto.so.1.1 libssl.so.1.1' # Proceed with the actual files inclusion local path_libraries path_libraries=$( set_current_package "$package" path_libraries ) content_inclusion 'OPENSSL11' "$package" "$path_libraries" # Update the list of dependencies on native libraries local extra_native_libraries_required extra_native_libraries_required=' libc.so.6 libdl.so.2 libpthread.so.0' dependencies_add_native_libraries "$package" "$extra_native_libraries_required" } src/30_content/15_files-inclusion_icon-packs.sh0000644000000000000000000000050513120060140020355 0ustar rootroot# Include the icons provided by an extra archive # USAGE: content_inclusion_optional_icons_archive $package content_inclusion_optional_icons_archive() { local package package="$1" # Proceed with the actual files inclusion local path_icons path_icons=$(path_icons) content_inclusion 'ICONS' "$package" "$path_icons" } src/30_content/15_files-inclusion_wineprefix-tweaks.sh0000644000000000000000000000220113120060140021775 0ustar rootroot# Include files required to apply tweaks to the WINE prefix # USAGE: content_inclusion_wineprefix_tweaks $package content_inclusion_wineprefix_tweaks() { local package package="$1" local wineprefix_tweaks wineprefix_tweak wineprefix_tweaks=$(wine_wineprefix_tweaks) while read -r wineprefix_tweak; do case "$wineprefix_tweak" in ('mono') content_inclusion_wineprefix_tweaks_mono "$package" ;; esac done <<- EOL $(printf '%s' "$wineprefix_tweaks") EOL } # Include the Mono .msi installer # USAGE: content_inclusion_wineprefix_tweaks_mono $package content_inclusion_wineprefix_tweaks_mono() { local package package="$1" local mono_installer_source package_path path_game_data mono_installer_name mono_installer_destination mono_installer_source=$(archive_path 'ARCHIVE_MONO') package_path=$(package_path "$package") path_game_data=$( set_current_package "$package" path_game_data ) mono_installer_name=$(archive_name 'ARCHIVE_MONO') mono_installer_destination="${package_path}${path_game_data}/wineprefix-tweaks/${mono_installer_name}" install -D --mode=644 "$mono_installer_source" "$mono_installer_destination" } src/30_content/20_huge-files.sh0000644000000000000000000001326713120060140015200 0ustar rootroot# List huge files for the given package # The paths should be relative to CONTENT_PATH_DEFAULT. # USAGE: huge_files_list $package # RETURN: a list of files, # one per line huge_files_list() { local package package="$1" local huge_files huge_files=$(context_value "HUGE_FILES_${package#PKG_}") # Return early if no list is set for the given package if [ -z "$huge_files" ]; then return 0 fi printf '%s' "$huge_files" | list_clean } # Split the given file into 9GB chunks # USAGE: huge_file_split $file_path huge_file_split() { local file_path file_path="$1" information_huge_file_split "$file_path" local content_path_default content_path_default=$(content_path_default) ## TODO: Full paths could be used instead of cd in a subshell. ( cd "${PLAYIT_WORKDIR}/gamedata/${content_path_default}" 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_default content_path_default=$(content_path_default) ## TODO: env --chdir could be used instead of cd in a subshell. ( cd "${PLAYIT_WORKDIR}/gamedata/${content_path_default}" 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" "$huge_file" 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=$( set_current_package "$package" 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 $huge_file content_inclusion_chunk_single() { local package chunk_path chunks_counter package="$1" chunk_path="$2" chunks_counter="$3" huge_file="$4" # 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=$( set_current_package "$package" 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 new package to dependencies list of parent package. dependencies_siblings_add "$package" "$package_identifier" # Include the chunk into the new package local path_game path_game=$( set_current_package "$package" path_game_data ) export "CONTENT_${content_id}_FILES"="$chunk_path" content_inclusion "$content_id" "$package_identifier" "$path_game" } src/30_content/90_messages.sh0000644000000000000000000000237113120060140014760 0ustar rootroot# Information: Icons from the archives are included into packages paths # USAGE: information_icons_inclusion information_icons_inclusion() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Inclusion des icônes…\n' ;; ('en'|*) message='Including icons…\n' ;; esac print_message 'info_once' "$message" } # Information: Files from the archives are included into packages paths # USAGE: information_content_inclusion information_content_inclusion() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Inclusion des fichiers du jeu…\n' ;; ('en'|*) message='Including game files…\n' ;; esac print_message 'info_once' "$message" } # Information: A huge file is split into 9GB chunks # USAGE: information_huge_file_split $path_file information_huge_file_split() { local path_file path_file="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Découpage de %s en plusieurs fichiers…\n' ;; ('en'|*) message='Splitting %s into smaller chunks…\n' ;; esac print_message 'info' "$message" \ "$path_file" } src/30_icons/00_common.sh0000644000000000000000000001342613120060140014074 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 # Try to guess a value from the game engine. if [ -z "$icons_list" ]; then local game_engine game_engine=$(game_engine) case "$game_engine" in ('gamemaker') icons_list="${application}_ICON" ;; ('unity3d') ## It is expected that Unity3D games always come with a single icon. icons_list="${application}_ICON" ;; esac fi # Try to guess a value from the application type. if [ -z "$icons_list" ]; then local application_type ## This is allowed to fail, as the binary file might not be available yet. application_type=$(application_type "$application" 2>/dev/null) || true ## TODO: Mono binaries usually include an icon too. 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 # Try to guess a value from the binary name. if [ -z "$icons_list" ]; then local application_exe application_exe=$(application_exe "$application") case "$application_exe" in (*'.exe') ## This is most probably a WINE or Mono binary, ## 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") # Try to guess a value from the game engine. if [ -z "$icon_path" ]; then local game_engine game_engine=$(game_engine) case "$game_engine" in ('gamemaker') icon_path=$(icon_path_gamemaker) ;; ('unity3d') icon_path=$(unity3d_icon_path "$icon") ;; esac fi # Try to guess a value from the application type. if [ -z "$icon_path" ]; then local application application_type application=$(icon_application "$icon") ## This is allowed to fail, as the binary file might not be available yet. application_type=$(application_type "$application" 2>/dev/null) || true case "$application_type" in ('wine') icon_path=$(icon_wine_path "$icon") ;; esac fi # Try to guess a value from the binary name. if [ -z "$icon_path" ]; then local application_exe application_exe=$(application_exe "$application") case "$application_exe" in (*'.exe') ## This is most probably a WINE or Mono binary, ## the game binary is used as a fallback icons source. icon_path=$(icon_wine_path "$icon") ;; esac fi # Check that the path to the icon is not empty if [ -z "$icon_path" ]; then error_icon_path_empty "$icon" return 1 fi printf '%s' "$icon_path" } # Print the wrestool options string for the given .exe icon # USAGE: icon_wrestool_options $icon # RETURN: the options string to pass to wrestool icon_wrestool_options() { local icon icon="$1" # Check that the given icon is a .exe file if ! icon_path "$icon" | grep --quiet '\.exe$'; then error_icon_unexpected_type "$icon" '*.exe' return 1 fi local wrestool_options wrestool_options=$(get_value "${icon}_WRESTOOL_OPTIONS") # Fall back on a default value based on the game engine if [ -z "$wrestool_options" ]; then local game_engine game_engine=$(game_engine) case "$game_engine" in ('unrealengine4') wrestool_options=$(unrealengine4_icon_wrestool_options_default) ;; (*) wrestool_options='--type=14' ;; esac fi printf '%s' "$wrestool_options" } src/30_icons/10_conversion.sh0000644000000000000000000002050213120060140014763 0ustar rootroot# Fetch icons from the archive contents, # convert them to PNG if they are not already in a supported format, # include them in the given package. # # This function handles all icons for a given application. # # USAGE: icons_inclusion_single_application $package $application icons_inclusion_single_application() { local package application package="$1" application="$2" local application_icons_list ## application_icons_list relies on the context package being set, through a late call to application_exe. application_icons_list=$( set_current_package "$package" application_icons_list "$application" ) # Throw an error if no icon was found for the given application. if [ -z "$application_icons_list" ]; then ## Skip the error if an icons archives is supported, as a non-blocking warning has already been shown. if [ -n "${ARCHIVE_OPTIONAL_ICONS_NAME:-}" ]; then return 0 fi error_no_icon_found "$application" return 1 fi local icon for icon in $application_icons_list; do icons_inclusion_single_icon "$package" "$application" "$icon" done } # Compute the full path to the icon source file # USAGE: icon_full_path $icon # RETURN: the full path to the file that provides icons, # as an absolute path icon_full_path() { local icon icon="$1" local content_path_default icon_path content_path_default=$(content_path_default) icon_path=$(icon_path "$icon") printf '%s/gamedata/%s/%s' "$PLAYIT_WORKDIR" "$content_path_default" "$icon_path" } # Fetch icon from the archive contents, # convert it to PNG if it is not already in a supported format, # include it in the given package. # # This function handles a single icon. # # USAGE: icons_inclusion_single_icon $package $application $icon icons_inclusion_single_icon() { local package application icon package="$1" application="$2" icon="$3" # Check for the existence of the icons source file local icon_path ## TODO: icon_full_path relies on the context package being set, ## through a call to content_path_default. 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" # Check if the icon is already in a supported format. # Directly include icons in a supported format, # otherwise convert them to PNG before inclusion. local icon_type icon_type=$(file_type "$icon_path") case "$icon_type" in ( \ 'image/png' | \ 'image/svg+xml' | \ 'image/x-xpixmap' | \ 'image/x-xpmi' \ ) cp "$icon_path" "$icons_temporary_directory" ;; ( \ 'application/vnd.microsoft.portable-executable' | \ 'application/x-dosexec' | \ 'image/bmp' | \ 'image/vnd.microsoft.icon' | \ 'image/x-ms-bmp' \ ) icon_extract_png_from_file "$icon" "$icons_temporary_directory" ;; (*) error_icon_unsupported_type "$icon_path" "$icon_type" return 1 ;; esac icons_include_from_directory "$package" "$application" "$icons_temporary_directory" rmdir "$icons_temporary_directory" } # Convert the given file into .png icons # USAGE: icon_extract_png_from_file $icon $destination icon_extract_png_from_file() { local icon destination icon="$1" destination="$2" local icon_file icon_type icon_file=$(icon_full_path "$icon") icon_type=$(file_type "$icon_file") case "$icon_type" in ( \ 'application/vnd.microsoft.portable-executable' | \ 'application/x-dosexec' \ ) icon_extract_png_from_exe "$icon" "$destination" ;; ('image/vnd.microsoft.icon') icon_extract_png_from_ico "$icon" "$destination" ;; ( \ 'image/bmp' | \ 'image/x-ms-bmp' \ ) icon_convert_bmp_to_png "$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_default content_path_default=$(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_default}/${inner_icon_file_name}" mv "$inner_icon_file" "$inner_icon_file_temporary_path" export TMP_INNER_ICON="$inner_icon_file_name" icon_extract_png_from_ico 'TMP_INNER_ICON' "$destination" rm "$inner_icon_file_temporary_path" ) done } # Extract .ico file(s) from the given .exe file # USAGE: icon_extract_ico_from_exe $icon $destination icon_extract_ico_from_exe() { local icon destination icon="$1" destination="$2" local icon_file wrestool_options icon_file=$(icon_full_path "$icon") wrestool_options=$(icon_wrestool_options "$icon") ## Silence ShellCheck false-positive ## Double quote to prevent globbing and word splitting. # shellcheck disable=SC2086 wrestool $wrestool_options --extract --output="$destination" "$icon_file" 2>/dev/null # Check that at least one .ico file has been extracted, throw an error otherwise local ico_files_number ico_files_number=$(find "$destination" -name '*.ico' | wc --lines) if [ "$ico_files_number" -lt 1 ]; then error_no_ico_file_extracted "$icon" return 1 fi } # Convert the given .bmp file to .png # USAGE: icon_convert_bmp_to_png $icon $destination icon_convert_bmp_to_png() { local icon destination icon="$1" destination="$2" icon_convert_to_png "$icon" "$destination" } # Extract .png file(s) from the given .ico file # USAGE: icon_extract_png_from_ico $icon $destination icon_extract_png_from_ico() { local icon destination icon="$1" destination="$2" icon_convert_to_png "$icon" "$destination" } # Convert multiple icon formats supported by ImageMagick to .png # USAGE: icon_convert_to_png $icon $destination icon_convert_to_png() { local icon destination icon="$1" destination="$2" local icon_file file_name icon_file=$(icon_full_path "$icon") file_name=$(basename "$icon_file") convert "$icon_file" "${destination}/${file_name%.*}.png" } # Get icon files from the given directory and put them in the given package # USAGE: icons_include_from_directory $package $application $directory icons_include_from_directory() { local package application source_directory package="$1" application="$2" source_directory="$3" local package_path path_icons package_path=$(package_path "$package") path_icons=$(path_icons) local application_id application_id=$( set_current_package "$package" application_id "$application" ) # Get icons from the given source directory, then move them to the given package local source_file icon_resolution destination_directory destination_path for source_file in \ "$source_directory"/*.png \ "$source_directory"/*.svg \ "$source_directory"/*.xpm do ## Skip the current pattern if it matched no file. if [ ! -e "$source_file" ]; then continue fi icon_resolution=$(icon_get_resolution "$source_file") destination_directory="${package_path}${path_icons}/${icon_resolution}/apps" destination_path="${destination_directory}/${application_id}.${source_file##*.}" mkdir --parents "$destination_directory" mv "$source_file" "$destination_path" done } # Return the resolution of the given image file. # For bitmap images it returns its dimensions as "${width}x${height}", # for vector images it returns "scalable". # USAGE: icon_get_resolution $file icon_get_resolution() { local image_file image_file="$1" local image_type image_resolution image_type=$(file_type "$image_file") case "$image_type" in ( \ 'image/svg+xml' \ ) image_resolution='scalable' ;; ( \ 'image/png' | \ 'image/x-xpixmap' | \ 'image/x-xpmi' \ ) # `identify` should be available when handling bitmap images. 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_string=$(identify "$image_file" | sed "s;^${image_file} ;;" | cut --delimiter=' ' --fields=2) image_resolution="${image_resolution_string%+0+0}" ;; (*) error_icon_unsupported_type "$image_file" "$image_type" return 1 ;; esac printf '%s' "$image_resolution" } src/30_icons/90_messages.sh0000644000000000000000000000706213120060140014423 0ustar rootroot# Error - An icon file could not be found # USAGE: error_icon_file_not_found $file error_icon_file_not_found() { local file file="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le fichier dʼicône suivant est introuvable : %s\n' ;; ('en'|*) message='The following icon file could not be found: %s\n' ;; esac print_message 'error' "$message" \ "$file" } # Error - The path to the given icon is not set # USAGE: error_icon_path_empty $icon error_icon_path_empty() { local icon icon="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='%s nʼest pas défini, mais il y a eu une tentative de récupérer le chemin de cette icône.\n' ;; ('en'|*) message='%s is not set, but there has been a request for this icon path.\n' ;; esac print_message 'error' "$message" \ "$icon" } # Error - An icon file with an unsupported MIME type has been provided # USAGE: error_icon_unsupported_type $icon_file $icon_type error_icon_unsupported_type() { local icon_file icon_type icon_file="$1" icon_type="$2" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le fichier dʼicône suivant est du type MIME "%s", qui nʼest pas pris en charge : %s\n' ;; ('en'|*) message='The following icon file is of the "%s" MIME type, that is not supported: %s\n' ;; esac print_message 'error' "$message" \ "$icon_type" \ "$icon_file" } # Error - No application identifier could be found related to the given icon identifier # USAGE: error_icon_application_not_found $icon_identifier error_icon_application_not_found() { local icon_identifier icon_identifier="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='L’identifiant d’icône fourni ne semble pas correspondre à une des applications prises en charge : %s\n' ;; ('en'|*) message='The given icon identifier does not seem to related to any of the supported applications: %s\n' ;; esac print_message 'error' "$message" \ "$icon_identifier" } # Error - No .ico file has been extracted from the given .exe file # USAGE: error_no_ico_file_extracted $icon_identifier error_no_ico_file_extracted() { local icon_identifier icon_identifier="$1" local icon_file wrestool_options icon_file=$(icon_full_path "$icon_identifier") wrestool_options=$(icon_wrestool_options "$icon_identifier") local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Aucun fichier d’icône n’a été extrait du fichier suivant : "%s"\n' message="$message"'Les options suivantes ont été passées à wrestool : %s\n' ;; ('en'|*) message='No icon file has been extracted from the following file: "%s"\n' message="$message"'The following options have been passed to wrestool: %s\n' ;; esac print_message 'error' "$message" \ "$icon_file" \ "$wrestool_options" } # Error - No icon is set for the given application # USAGE: error_no_icon_found $application error_no_icon_found() { local application application="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Aucune icône n’est définie pour l’application suivante : %s\n' ;; ('en'|*) message='No icon is set for the following application: %s\n' ;; esac print_message 'error' "$message" \ "$application" } src/30_instructions/00_common.sh0000644000000000000000000000743013120060140015523 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 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 "$@" ;; 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 "$@" ;; esac } src/30_instructions/90_messages.sh0000644000000000000000000001053713120060140016055 0ustar rootroot# print common part of packages installation instructions # USAGE: information_installation_instructions_common $game_name information_installation_instructions_common() { local game_name game_name="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\nInstallez "%s" en lançant la série de commandes suivantes en root :\n' ;; ('en'|*) message='\nInstall "%s" by running the following commands as root:\n' ;; esac print_message 'info' "$message" \ "$game_name" } # print variant precision for packages installation instructions # USAGE: information_installation_instructions_variant $variant information_installation_instructions_variant() { local variant variant="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\nversion %s :\n' ;; ('en'|*) message='\n%s version:\n' ;; esac print_message 'info' "$message" \ "$variant" } # Display a list of unknown runtime commands from packages dependencies # USAGE: warning_dependencies_unknown_commands warning_dependencies_unknown_commands() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Certaines dépendances de ce jeu ne sont pas encore prises en charge par ./play.it' message="$message"', voici la liste de celles qui ont été ignorées :\n' ;; ('en'|*) message='Some dependencies of this game are not supported by ./play.it yet' message="$message"', here are the ones that have been skipped:\n' ;; esac print_message 'warning' "$message" local unknown_command while read -r unknown_command; do print_message 'info' '- %s\n' \ "$unknown_command" done <<- EOL $(dependencies_unknown_commands_list) EOL } # Display a list of unknown libraries from packages dependencies # USAGE: warning_dependencies_unknown_libraries warning_dependencies_unknown_libraries() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Certaines dépendances de ce jeu ne sont pas encore prises en charge par ./play.it' message="$message"', voici la liste de celles qui ont été ignorées :\n' ;; ('en'|*) message='Some dependencies of this game are not supported by ./play.it yet' message="$message"', here are the ones that have been skipped:\n' ;; esac print_message 'warning' "$message" local unkown_library while read -r unkown_library; do print_message 'info' '- %s\n' \ "$unkown_library" done <<- EOL $(dependencies_unknown_libraries_list) EOL } # Display a list of unknown Mono libraries from packages dependencies # USAGE: warning_dependencies_unknown_mono_libraries warning_dependencies_unknown_mono_libraries() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Certaines dépendances Mono de ce jeu ne sont pas encore prises en charge par ./play.it' message="$message"', voici la liste de celles qui ont été ignorées :\n' ;; ('en'|*) message='Some Mono dependencies of this game are not supported by ./play.it yet' message="$message"', here are the ones that have been skipped:\n' ;; esac print_message 'warning' "$message" 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 } # Display a list of unknown GStreamer media formats from packages dependencies # USAGE: warning_dependencies_unknown_gstreamer_media_formats warning_dependencies_unknown_gstreamer_media_formats() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Certains formats multimédia requis par ce jeu ne sont pas encore pris en charge par ./play.it' message="$message"', voici la liste de ceux qui ont été ignorés :\n' ;; ('en'|*) message='Some media formats required by this game are not supported by ./play.it yet' message="$message"', here are the ones that have been skipped:\n' ;; esac print_message 'warning' "$message" 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 } src/30_launchers/00_common.sh0000644000000000000000000002557413120060140014754 0ustar rootroot# Write launcher scripts and menu entries. # # This function can take several applications as its arguments, # and default to handle all applications if none are explicitely given. # # USAGE: launchers_generation [$package [$application…]] launchers_generation() { local package if [ $# -ge 1 ]; then package="$1" shift 1 else package=$(current_package) fi # If no applications are explicitely listed, # write launchers and menu entries for all applications. if [ "$#" -eq 0 ]; then applications_list=$(applications_list) ## If launchers_generation has been called with no argument, the applications list should not be empty. if [ -z "$applications_list" ]; then error_applications_list_empty return 1 fi launchers_generation "$package" $applications_list return 0 fi information_launchers_generation local application for application in "$@"; do launcher_generation_checks "$package" "$application" launcher_write_script "$package" "$application" launcher_write_desktop "$package" "$application" done } # Run checks to ensure all required information for the launcher generation is set # USAGE: launcher_generation_checks $package $application launcher_generation_checks() { local package application package="$1" application="$2" # Ensure that the application type is set, or can be guessed local application_type application_type=$( set_current_package "$package" application_type "$application" ) if [ -z "$application_type" ]; then # If the type could not be found despite a binary path being set, # this binary could be missing. local application_exe application_exe=$( set_current_package "$package" application_exe "$application" ) if [ -n "${application_exe:-}" ]; then if ! launcher_target_presence_check "$package" "$application"; then error_launcher_missing_binary "$application_exe" return 1 fi fi error_no_application_type "$application" return 1 fi # If the current application type relies on a game binary, ensure its path is set case "$application_type" in ('custom') ## Custom launchers might not rely on a provided binary. ;; ('renpy') ## Ren'Py games do not rely on a provided binary. ;; ('scummvm') ## ScummVM games do not rely on a provided binary, ## but they expect a ScummVM game ID to be set. local application_scummid application_scummid=$( set_current_package "$package" application_scummvm_scummid "$application" ) if [ -z "$application_scummid" ]; then error_application_scummid_invalid "$application" "$application_scummid" return 1 fi ;; (*) local application_exe application_exe=$( set_current_package "$package" application_exe "$application" ) if [ -z "$application_exe" ]; then error_application_exe_empty "$application" 'launcher_generation_checks' return 1 fi ;; esac # If the current application type relies on a game binary, ensure that it can be found if [ -n "${application_exe:-}" ]; then ## A dedicated function is used here to make it easier to override from game scripts. if ! launcher_target_presence_check "$package" "$application"; then error_launcher_missing_binary "$application_exe" return 1 fi fi } # Check that the launcher target exists # USAGE: launcher_target_presence_check $package $application # RETURN: 0 if the game binary has been found, # 1 if the game binary has not been found launcher_target_presence_check() { local package application package="$1" application="$2" local application_exe application_exe_path application_exe=$( set_current_package "$package" application_exe "$application" ) application_exe_path=$( set_current_package "$package" application_exe_path "$application_exe" ) test -f "$application_exe_path" } # Write the launcher script for the given application. # USAGE: launcher_write_script $package $application launcher_write_script() { local package application package="$1" application="$2" local launcher_path launcher_directory launcher_path=$(launcher_path "$package" "$application") launcher_directory=$(dirname "$launcher_path") mkdir --parents "$launcher_directory" touch "$launcher_path" chmod 755 "$launcher_path" ## The *_launcher functions are called on their own first, to avoid being spawned in a subshell. This prevents their return code from being lost. ## The package context is always set to ensure context-sensitive values for runtime options (and for the path to game data) are used. local application_type launcher_content application_type=$( set_current_package "$package" application_type "$application" ) case "$application_type" in ('custom') launcher_content=$( set_current_package "$package" custom_launcher "$application" ) printf '%s' "$launcher_content" | snippet_clean > "$launcher_path" ;; ('dosbox') launcher_content=$( set_current_package "$package" dosbox_launcher "$application" ) printf '%s' "$launcher_content" | snippet_clean > "$launcher_path" dependencies_add_command "$package" 'dosbox' ;; ('java') launcher_content=$( set_current_package "$package" java_launcher "$application" ) printf '%s' "$launcher_content" | snippet_clean > "$launcher_path" dependencies_add_command "$package" 'java' ;; ('mono') launcher_content=$( set_current_package "$package" mono_launcher "$application" ) printf '%s' "$launcher_content" | snippet_clean > "$launcher_path" dependencies_add_command "$package" 'mono' ;; ('native') launcher_content=$( set_current_package "$package" native_launcher "$application" ) printf '%s' "$launcher_content" | snippet_clean > "$launcher_path" # Build the .so preload shim, if one is set local ld_preload_source ld_preload_source=$(ld_preload_source) if [ -n "$ld_preload_source" ]; then ld_preload_build "$package" fi # Add execution permissions to the game binary file local application_exe application_exe_path application_exe=$( set_current_package "$package" application_exe "$application" ) application_exe_path=$( set_current_package "$package" application_exe_path "$application_exe" ) chmod +x "$application_exe_path" # Add dependencies on some system-provided libraries based on the game engine. local game_engine game_engine=$(game_engine) case "$game_engine" in ('unity3d') dependencies_add_native_libraries "$package" 'libSDL2-2.0.so.0' ;; ('visionaire') dependencies_add_native_libraries "$package" 'libSDL2-2.0.so.0' ;; esac ;; ('renpy') launcher_content=$( set_current_package "$package" renpy_launcher "$application" ) printf '%s' "$launcher_content" | snippet_clean > "$launcher_path" dependencies_add_command "$package" 'renpy' ;; ('scummvm') launcher_content=$( set_current_package "$package" scummvm_launcher "$application" ) printf '%s' "$launcher_content" | snippet_clean > "$launcher_path" dependencies_add_command "$package" 'scummvm' ;; ('web') launcher_content=$( set_current_package "$package" web_launcher "$application" ) printf '%s' "$launcher_content" | snippet_clean > "$launcher_path" dependencies_add_command "$package" 'firefox' dependencies_add_command "$package" 'python3' ;; ('wine') # Generate the init .reg script, if set from the game script wine_registry_script_write "$package" launcher_content=$( set_current_package "$package" wine_launcher "$application" ) printf '%s' "$launcher_content" | snippet_clean > "$launcher_path" dependencies_add_command "$package" 'wine' ## Add a package dependency on winetricks if the current game relies on some winetricks verbs. local winetricks_verbs winetricks_verbs=$(wine_winetricks_verbs) if [ -n "$winetricks_verbs" ]; then dependencies_add_command "$package" 'winetricks' dependencies_add_command "$package" 'terminal_wrapper' fi ## Add package dependencies on winetricks and rendering libraries if a non-default Direct3D renderer is required. local direct3d_renderer direct3d_renderer=$(wine_renderer_name) case "$direct3d_renderer" in ('wined3d/'*) dependencies_add_command "$package" 'winetricks' dependencies_add_command "$package" 'terminal_wrapper' local wined3d_backend wined3d_backend=$(printf '%s' "$direct3d_renderer" | cut --delimiter='/' --fields=2) case "$wined3d_backend" in ('gl') dependencies_add_native_libraries "$package" 'libGL.so.1' ;; ('vulkan') dependencies_add_native_libraries "$package" 'libvulkan.so.1' ;; esac ;; ('dxvk') local option_package option_package=$(option_value 'package') case "$option_package" in ('deb') dependencies_add_command "$package" 'dxvk-setup | winetricks' dependencies_add_command "$package" 'terminal_wrapper' ;; (*) dependencies_add_command "$package" 'winetricks' dependencies_add_command "$package" 'terminal_wrapper' ;; esac dependencies_add_native_libraries "$package" 'libvulkan.so.1' ;; ('vkd3d') dependencies_add_command "$package" 'winetricks' dependencies_add_command "$package" 'terminal_wrapper' dependencies_add_native_libraries "$package" 'libvulkan.so.1' ;; esac ;; esac } # Print the path to the launcher script for the given application. # USAGE: launcher_path $package $application # RETURN: The absolute path to the launcher launcher_path() { local package application package="$1" application="$2" local package_path path_binaries application_id package_path=$(package_path "$package") path_binaries=$(path_binaries) application_id=$( set_current_package "$package" application_id "$application" ) printf '%s%s/%s' "$package_path" "$path_binaries" "$application_id" } # Print the headers common to all launcher scripts # USAGE: launcher_headers launcher_headers() { cat <<- EOF #!/bin/sh # script generated by ./play.it $LIBRARY_VERSION - https://www.dotslashplay.it/ set -o errexit EOF } # Print the exit actions common to all launcher scripts # USAGE: launcher_exit launcher_exit() { cat <<- 'EOF' # Return the game exit code if [ -n "$game_exit_status" ]; then exit "$game_exit_status" else exit 0 fi EOF } # Print the line starting the game # USAGE: game_exec_line $application # RETURN: the command to execute, including its command line options game_exec_line() { local application application="$1" local application_type application_type=$(application_type "$application") case "$application_type" in ('dosbox') dosbox_exec_line "$application" ;; ('java') java_exec_line "$application" ;; ('mono') mono_exec_line "$application" ;; ('native') native_exec_line "$application" ;; ('renpy') renpy_exec_line "$application" ;; ('scummvm') scummvm_exec_line "$application" ;; ('wine') wine_exec_line "$application" ;; esac } src/30_launchers/05_paths.sh0000644000000000000000000001050613120060140014575 0ustar rootroot# Set the paths that should be available to the generated launcher # USAGE: launcher_init_paths $application launcher_init_paths() { # The application this launcher is generated for # It is used to get the application type and the prefix type local application application="$1" # Always include the path to the root of the read-only game data local path_game_data path_game_data=$(path_game_data) cat <<- EOF # Set the paths that sould be available throughout the launcher ## Set the path to the root of the read-only game data PATH_GAME_DATA='${path_game_data}' EOF # Include the path to the persistent user data, for applications relying on it local application_prefix_type application_prefix_type=$(application_prefix_type "$application") case "$application_prefix_type" in ('symlinks') launcher_path_persistent ;; esac # Include the paths that are specific to the application type # This is set after the path to the persistent user data, so a path relative to $PATH_PERSISTENT could be used local application_type application_type=$(application_type "$application") case "$application_type" in ('java') java_init_paths ;; ('mono') mono_init_paths ;; ('native') native_init_paths ;; ('wine') wine_init_paths ;; esac # Include the path to the volatile game prefix, for applications relying on it # This is set after the type-specific paths, so a path relative to $PATH_WINEPREFIX could be used case "$application_prefix_type" in ('symlinks') launcher_path_prefix ;; esac # Include the path to the fake $HOME. local application_type application_type=$(application_type "$application") case "$application_type" in ('web') launcher_path_fake_home ;; (*) local fake_home_directories fake_home_directories=$(fake_home_persistent_directories) if [ -n "$fake_home_directories" ]; then launcher_path_fake_home fi ;; esac } # Set the path to persistent user data # USAGE: launcher_path_persistent launcher_path_persistent() { local game_id game_id=$(game_id) cat <<- EOF ## Set the path to persistent user data path_persistent() { # The persistent user data path can be overriden using an environment variable if [ -n "\${PLAYIT_PATH_PERSISTENT:-}" ]; then printf '%s' "\$PLAYIT_PATH_PERSISTENT" return 0 fi # Compute the default path if none has been explicitly set printf '%s/games/${game_id}' "\${XDG_DATA_HOME:=\$HOME/.local/share}" } PATH_PERSISTENT=\$(path_persistent) EOF } # Set the path to the volatile game prefix # USAGE: launcher_path_prefix launcher_path_prefix() { local game_id game_id=$(game_id) cat <<- EOF ## Set the path to the volatile game prefix path_prefix() { # The prefix path can be overriden using an environment variable if [ -n "\${PLAYIT_PATH_PREFIX:-}" ]; then printf '%s' "\$PLAYIT_PATH_PREFIX" return 0 fi # Compute the default prefix path if none has been explicitely set printf '%s/play.it/prefixes/${game_id}' "\${XDG_CACHE_HOME:="\$HOME/.cache"}" } PATH_PREFIX=\$(path_prefix) EOF } # Set the path to the fake $HOME # USAGE: launcher_path_fake_home launcher_path_fake_home() { local game_id game_id=$(game_id) cat <<- EOF ## Set the path to the fake \$HOME path_fake_home() { # The fake \$HOME path can be overriden using an environment variable if [ -n "\${PLAYIT_PATH_FAKE_HOME:-}" ]; then printf '%s' "\$PLAYIT_PATH_FAKE_HOME" return 0 fi # Compute the default fake \$HOME path if none has been explicitely set printf '%s/play.it/home/${game_id}' "\${XDG_CACHE_HOME:="\$HOME/.cache"}" } PATH_FAKE_HOME=\$(path_fake_home) EOF } # Set the paths to native libraries # USAGE: launcher_paths_libraries launcher_paths_libraries() { local path_system game_id path_system=$(path_libraries) game_id=$(game_id) cat <<- EOF ## Set the paths to native libraries path_libraries_user() { # The path to user libraries can be overriden using an environment variable if [ -n "\${PLAYIT_PATH_LIBRARIES_USER:-}" ]; then printf '%s' "\$PLAYIT_PATH_LIBRARIES_USER" return 0 fi # Compute the path to user libraries if none has been explicitely set printf '%s/.local/lib/games/${game_id}' "\$HOME" } PATH_LIBRARIES_SYSTEM='$path_system' PATH_LIBRARIES_USER=\$(path_libraries_user) EOF } src/30_launchers/10_desktop.sh0000644000000000000000000000566313120060140015133 0ustar rootroot# Write the XDG desktop file for the given application # USAGE: launcher_write_desktop $package $application launcher_write_desktop() { local package application package="$1" application="$2" local desktop_file desktop_directory desktop_file=$(launcher_desktop_filepath "$package" "$application") desktop_directory=$(dirname "$desktop_file") mkdir --parents "$desktop_directory" ( set_current_package "$package" launcher_desktop "$application" > "$desktop_file" ) } # Print the content of the XDG desktop file for the given application # USAGE: launcher_desktop $application # RETURN: the full content of the XDG desktop file launcher_desktop() { local application application="$1" local application_name application_category desktop_field_exec desktop_field_icon application_name=$(application_name "$application") application_category=$(application_category "$application") desktop_field_exec=$(desktop_field_exec "$application") desktop_field_icon=$(desktop_field_icon "$application") cat <<- EOF [Desktop Entry] Version=1.0 Type=Application Name=$application_name Icon=$desktop_field_icon Exec=$desktop_field_exec Categories=$application_category EOF } # Print the full path to the XDG desktop file for the given application # USAGE: launcher_desktop_filepath $package $application # RETURN: an absolute file path launcher_desktop_filepath() { local package application package="$1" application="$2" local application_id package_path path_xdg_desktop application_id=$( set_current_package "$package" application_id "$application" ) package_path=$(package_path "$package") path_xdg_desktop=$(path_xdg_desktop) printf '%s/%s.desktop' \ "${package_path}${path_xdg_desktop}" \ "$application_id" } # Print the XDG desktop "Exec" field for the given application # USAGE: desktop_field_exec $application # RETURN: The "Exec" field content, including escaping if required desktop_field_exec() { local application application="$1" local option_prefix application_id exec_field option_prefix=$(option_value 'prefix') application_id=$(application_id "$application") case "$option_prefix" in ## Standard path, only the command name is required. ('/usr'|'/usr/local') exec_field="$application_id" ;; ## Non-standard path including spaces, the full path enclosed in quotes is required. (*' '*) local path_binaries path_binaries=$(path_binaries) exec_field="'${path_binaries}/${application_id}'" ;; ## Non-standard path not including spaces, the full path is required. (*) local path_binaries path_binaries=$(path_binaries) exec_field="${path_binaries}/${application_id}" ;; esac printf '%s' "$exec_field" } # Print the XDG desktop "Icon" field for the given application # USAGE: desktop_field_icon $application # RETURN: The "Icon" field content desktop_field_icon() { local application application="$1" local icon_field icon_field=$(application_id "$application") printf '%s' "$icon_field" } src/30_launchers/10_fake-home.sh0000644000000000000000000000402413120060140015304 0ustar rootroot# Enable the fake $HOME # USAGE: fake_home_enable fake_home_enable() { cat <<- 'EOF' # Enable the fake $HOME HOME_PATH_REAL="$HOME" export HOME="$PATH_FAKE_HOME" 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 ## 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 "$PATH_FAKE_HOME" "$HOME" "$xdg_path_relative" fi done << EOL ${XDG_CACHE_HOME:-${HOME}/.cache} ${XDG_CONFIG_HOME:-${HOME}/.config} ${XDG_DATA_HOME:-${HOME}/.local/share} ${XDG_STATE_HOME:-${HOME}/.local/state} EOL unset xdg_path_absolute EOF cat <<- EOF ## Divert paths specific to the current game FAKE_HOME_PERSISTENT_DIRECTORIES="$persistent_directories" EOF cat <<- 'EOF' while read -r directory; do if [ -z "$directory" ]; then continue fi persistent_path_diversion "$PATH_FAKE_HOME" "${PATH_PERSISTENT}/fake-home" "$directory" done << EOL $(printf '%s' "$FAKE_HOME_PERSISTENT_DIRECTORIES") EOL unset directory EOF } src/30_launchers/10_prefixes.sh0000644000000000000000000000252513120060140015301 0ustar rootroot# Print the code snippet used to generate a symlinks farm prefix # USAGE: prefix_symlinks_generate prefix_symlinks_generate() { ## TODO: env --chdir could be used instead of cd in subshells. ## TODO: The ability to set some volatile writable paths should be added, ## similar to user persistent paths but without the diversion to persistent storage. cat <<- 'EOF' # Generate a volatile game prefix relying on symbolic links mkdir --parents "$PATH_PREFIX" ## Remove links to game directories ( cd "$PATH_GAME_DATA" 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_DATA" "$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_DATA}/${directory}" ]; then rmdir --ignore-fail-on-non-empty "$directory" fi done unset link directory ) EOF } src/30_launchers/20_persistent-user-data.sh0000644000000000000000000002044713120060140017543 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() { context_value 'USER_PERSISTENT_DIRECTORIES' | list_clean } # List the files from the game prefix that should be diverted to a persistent path # USAGE: persistent_list_files # RETURNS: a list of paths to files, separated by line breaks # glob patterns can be included persistent_list_files() { context_value 'USER_PERSISTENT_FILES' | list_clean } # Populate the game prefix from the persistent storage # USAGE: persistent_storage_initialization persistent_storage_initialization() { ## TODO: env --chdir could be used instead of cd in a subshell. ## TODO: If $PATH_PERSISTENT does not exist yet, it should not be created here. ## Instead this while step should be skipped. cat <<- 'EOF' # Populate the prefix from persistent files mkdir --parents "$PATH_PERSISTENT" ( cd "$PATH_PERSISTENT" find -L . -type f ! -path './wine/*' | while read -r file; do persistent_file="${PATH_PERSISTENT}/${file}" prefix_file="${PATH_PREFIX}/${file}" if [ ! -e "$prefix_file" ] || [ "$(realpath "$prefix_file")" != "$(realpath "$persistent_file")" ] then mkdir --parents "$(dirname "$prefix_file")" ln --symbolic --force --no-target-directory \ "$persistent_file" \ "$prefix_file" fi done unset file persistent_file prefix_file ) EOF } # Set the common actions required for directories and files diversion to persistent storage # This is required for the diversions using one of the following variables: # - USER_PERSISTENT_DIRECTORIES # - USER_PERSISTENT_FILES # USAGE: persistent_storage_common persistent_storage_common() { local persistent_list_directories persistent_list_files persistent_list_directories=$(persistent_list_directories) persistent_list_files=$(persistent_list_files) # Return early if the current game script does not use paths diversion if [ -z "$persistent_list_directories" ] && [ -z "$persistent_list_files" ] then return 0 fi cat <<- 'EOF' # Expand a path pattern into a list of existing paths. # If the pattern can not be expanded, it is printed as-is instead. expand_path_pattern() { pattern="$1" ## Silently skip empty patterns if [ -z "$pattern" ]; then return 0 fi expanded_paths=$(find . -path "./${pattern#./}") if [ -n "$expanded_paths" ]; then printf '%s\n' "$expanded_paths" else printf '%s\n' "$pattern" fi unset pattern expanded_paths } EOF } # Set the action used to divert a given path to persistent storage # USAGE: persistent_path_diversion persistent_path_diversion() { ## TODO: env --chdir could be used instead of cd in a subshell. 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" mkdir --parents "$path_destination" cp --dereference --parents --recursive \ "$directory" \ "$path_destination" ) else mkdir --parents "${path_destination}/${directory}" fi fi ## Replace the directory in the prefix by a link to the one in persistent storage. if [ ! -h "${path_source}/${directory}" ]; then directory_parent=$(dirname "${path_source}/${directory}") rm --recursive --force "${path_source:?}/${directory}" mkdir --parents "$directory_parent" ln --symbolic "${path_destination}/${directory}" "${path_source}/${directory}" fi unset directory_parent unset path_source path_destination directory } EOF } # Update directories diversions to persistent storage # USAGE: persistent_storage_update_directories persistent_storage_update_directories() { local persistent_list_directories persistent_list_directories=$(persistent_list_directories) # Return early if the current game script does not use directories diversion if [ -z "$persistent_list_directories" ]; then return 0 fi cat <<- EOF # Update directories diversions to persistent storage USER_PERSISTENT_DIRECTORIES='${persistent_list_directories}' EOF ## TODO: env --chdir could be used instead of cd in a subshell. 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" "$PATH_PERSISTENT" "$directory" done <<- EOL $(expand_path_pattern "$directory_pattern") EOL done <<- EOL $(printf '%s' "$USER_PERSISTENT_DIRECTORIES") EOL unset directory_pattern ) EOF } # Update files diversions to persistent storage # USAGE: persistent_storage_update_files persistent_storage_update_files() { local persistent_list_files persistent_list_files=$(persistent_list_files) # Return early if the current game script does not use files diversion if [ -z "$persistent_list_files" ]; then return 0 fi cat <<- EOF # Update files diversions to persistent storage USER_PERSISTENT_FILES='${persistent_list_files}' EOF ## TODO: env --chdir could be used instead of cd in a subshell. 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 "${PATH_PERSISTENT}/${file}" ]; then if [ -e "$file" ]; then cp --dereference --parents \ "$file" \ "${PATH_PERSISTENT}" 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 "${PATH_PERSISTENT}/${file}" ]; then file_parent=$(dirname "$file") rm --force "$file" mkdir --parents "$file_parent" ln --symbolic "${PATH_PERSISTENT}/${file}" "$file" fi done <<- EOL $(expand_path_pattern "$file_pattern") EOL unset file done <<- EOL $(printf '%s' "$USER_PERSISTENT_FILES") EOL unset file_pattern ) EOF } # Update persistent storage with files from the current prefix # USAGE: persistent_storage_update_files_from_prefix persistent_storage_update_files_from_prefix() { local persistent_list_files persistent_list_files=$(persistent_list_files) # Return early if the current game script does not use files diversion if [ -z "$persistent_list_files" ]; then return 0 fi ## TODO: env --chdir could be used instead of cd in a subshell. 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" "$PATH_PERSISTENT" rm --force "$path" ln --symbolic "${PATH_PERSISTENT}/${path}" "$path" fi done <<- EOL $(expand_path_pattern "$path_pattern") EOL unset path done <<- EOL $(printf '%s' "$USER_PERSISTENT_FILES") EOL unset path_pattern ) EOF } src/30_launchers/30_wrappers.sh0000644000000000000000000000070513120060140015317 0ustar rootroot# Print a wrapper function spawning a graphical terminal # USAGE: launcher_wrapper_terminal launcher_wrapper_terminal() { cat <<- 'EOF' # Spawn a terminal with support for the -e option to run arbitrary commands terminal_wrapper() { for command in \ x-terminal-emulator \ xterm do if command -v $command >/dev/null; then printf '%s' "$command" return 0 fi done } EOF } src/30_launchers/40_tweaks.sh0000644000000000000000000000105413120060140014751 0ustar rootroot# Force the use of the system SDL library, for use with native games # USAGE: launcher_tweak_sdl_override launcher_tweak_sdl_override() { local path_libraries path_libraries=$(path_libraries_system) cat <<- EOF # Force the use of the system SDL library if [ -z "\${SDL_DYNAMIC_API:-}" ]; then export SDL_DYNAMIC_API="${path_libraries}/libSDL2-2.0.so.0" fi EOF } ## TODO: A function should be provided to force the use of the x11 SDL backend. ## Unlike the above function, it should override whatever is set in the current environment. src/30_launchers/90_messages.sh0000644000000000000000000000351013120060140015266 0ustar rootroot# Information: Launcher scripts and desktop entries are being written # USAGE: information_launchers_generation information_launchers_generation() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Génération des lanceurs…\n' ;; ('en'|*) message='Launchers generation…\n' ;; esac print_message 'info_once' "$message" } # Error - A binary file is missing # USAGE: error_launcher_missing_binary $binary # CALLS: print_error error_launcher_missing_binary() { local binary binary="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le fichier suivant est introuvable, mais la création dʼun lanceur pour celui-ci a été demandée : %s\n' ;; ('en'|*) message='The following file can not be found, but a launcher targeting it should have been created: %s\n' ;; esac print_message 'error' "$message" \ "$binary" } # Error - The requested prefix type is not compatible with the given application type # USAGE: error_launchers_prefix_type_unsupported $application error_launchers_prefix_type_unsupported() { local application application="$1" local application_type prefix_type application_type=$(application_type "$application") if [ -z "$application_type" ]; then error_no_application_type "$application" return 1 fi prefix_type=$(application_prefix_type "$application") local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le type de préfixe "%s" ne peut pas être utilisé pour une application du type "%s".\n' ;; ('en'|*) message='Prefix type "%s" can not be used with application type "%s".\n' ;; esac print_message 'error' "$message" \ "$prefix_type" \ "$application_type" } src/30_packages/00_common.sh0000644000000000000000000001375013120060140014537 0ustar rootroot# Generate packages from the given list # USAGE: packages_generation $package[…] packages_generation() { # If not explicit packages list is given, generate all packages if [ $# -eq 0 ]; then local packages_list packages_list=$(packages_list) packages_generation $packages_list return 0 fi information_packages_generation local option_package option_package=$(option_value 'package') case "$option_package" in ('arch') archlinux_packages_metadata "$@" archlinux_packages_build "$@" ;; ('deb') debian_packages_metadata "$@" debian_packages_build "$@" ;; ('gentoo') gentoo_packages_metadata "$@" gentoo_packages_build "$@" ;; 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() { local packages_list packages_list=$(context_value 'PACKAGES_LIST') # Fall back on the default packages list for the current game engine if [ -z "$packages_list" ]; then local game_engine game_engine=$(game_engine) case "$game_engine" in ('gamemaker') packages_list=$(packages_list_gamemaker) ;; ('visionaire') packages_list=$(visionaire_packages_list) ;; esac fi # Fall back of the default packages list (a single package identified by "PKG_MAIN") if [ -z "$packages_list" ]; then packages_list='PKG_MAIN' fi local package for package in $packages_list; do printf '%s\n' "$package" done } # Check if the given package is included in the list of packages that should be built # USAGE: package_is_included_in_packages_list $package # RETURN: 0 if the package is included, 1 if it is not package_is_included_in_packages_list() { local package package="$1" local packages_list packages_list=$(packages_list) printf '%s' "$packages_list" | grep --quiet --fixed-strings --word-regexp "$package" } # Print the list of the packages that would be generated from the given archive. # USAGE: packages_print_list $archive # RETURN: a list of package file names, one per line packages_print_list() { local archive archive="$1" local PLAYIT_CONTEXT_ARCHIVE packages_list package package_name set_current_archive "$archive" packages_list=$(packages_list) for package in $packages_list; do package_name=$(package_name "$package") printf '%s\n' "$package_name" done } # 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") ;; 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" 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") ;; 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 package_version archive archive_version script_version_string archive=$(current_archive) archive_version=$(archive_version "$archive") script_version_string=$(script_version) package_version="${archive_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') package_version=$(gentoo_package_version "$package_version") ;; esac printf '%s' "$package_version" } src/30_packages/10_properties.sh0000644000000000000000000001451413120060140015443 0ustar rootroot# 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") # Try to find a fallback value based on the package identifier: # current package → fallback package # PKG_BIN64 → PKG_BIN # PKG_BIN32 → PKG_BIN # PKG_LIBS64 → PKG_LIBS # PKG_LIBS32 → PKG_LIBS if [ -z "$package_id" ]; then case "$package" in ('PKG_BIN64'|'PKG_BIN32') ## Errors due to the later set_current_package call are ignored. package_id=$(package_id 'PKG_BIN' 2>/dev/null || true) ;; ('PKG_LIBS64'|'PKG_LIBS32') package_id=$(package_id 'PKG_LIBS') ;; esac fi # 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 ('gamemaker') package_id=$(package_id_gamemaker "$package") ;; ('visionaire') package_id=$(visionaire_package_id "$package") ;; esac fi # Fall back on the game id if no package id is explicitly set if [ -z "$package_id" ]; then ## We need to explicitly set the context here, ## because the value of GAME_ID might be specific to the current package. package_id=$( set_current_package "$package" game_id ) ## Include the expansion id if one is available. local expansion_id expansion_id=$(expansion_id) if [ -n "$expansion_id" ]; then package_id="${package_id}-${expansion_id}" fi fi # Check that the id fits the format restrictions. if ! printf '%s' "$package_id" | grep --quiet --regexp='^[0-9a-z][-0-9a-z]\+[0-9a-z]$' then error_package_id_invalid "$package_id" return 1 fi # Apply tweaks specific to the target package format. local option_package option_package=$(option_value 'package') case "$option_package" in ('arch') package_id=$(archlinux_package_id "$package_id") ;; ('gentoo') package_id=$(gentoo_package_id "$package_id") ;; esac printf '%s' "$package_id" } # Print the architecture of the given package # USAGE: package_architecture $package # RETURNS: the package architecture, as one of the following values: # - 32 # - 64 # - all package_architecture() { local package package="$1" local package_architecture package_architecture=$(context_value "${package}_ARCH") # If no architecture is explictly set for the given package, fall back to "all". if [ -z "$package_architecture" ]; then package_architecture='all' fi printf '%s' "$package_architecture" } # Print the desciption of the given package # USAGE: package_description $package # RETURNS: the package description, a non-empty string that should not include line breaks package_description() { local package package="$1" local package_description package_description=$(context_value "${package}_DESCRIPTION") # Try to find a fallback value based on the package identifier: # current package → fallback package # PKG_LIBS64 → PKG_LIBS # PKG_LIBS32 → PKG_LIBS if [ -z "$package_description" ]; then case "$package" in ('PKG_LIBS64'|'PKG_LIBS32') package_id=$(package_description 'PKG_LIBS') ;; esac fi # 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 ('gamemaker') package_description=$(package_description_gamemaker "$package") ;; ('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 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") # Try to find a fallback value based on the package identifier: # current package → fallback package # PKG_BIN64 → PKG_BIN # PKG_BIN32 → PKG_BIN # PKG_LIBS64 → PKG_LIBS # PKG_LIBS32 → PKG_LIBS # PKG_L10N_xxx → PKG_L10N if [ -z "$package_provides" ]; then case "$package" in ('PKG_BIN64'|'PKG_BIN32') package_provides=$(package_provides 'PKG_BIN') ;; ('PKG_LIBS64'|'PKG_LIBS32') package_provides=$(package_provides 'PKG_LIBS') ;; ('PKG_L10N_'*) package_provides=$(package_provides 'PKG_L10N') ;; esac fi # Return early if there is no package name to print if [ -z "$package_provides" ]; then return 0 fi printf '%s' "$package_provides" | list_clean } # Print the actions that should be run post-installation for the given package # USAGE: package_postinst_actions $package # RETURN: a list of actions, that can span over several lines, # the list can be empty package_postinst_actions() { local package package="$1" local postinst_actions postinst_actions=$(get_value "${package}_POSTINST_RUN") # Return early if no action is set. if [ -z "$postinst_actions" ]; then return 0 fi # Ensure the list of actions always end with a line break. printf '%s\n' "$postinst_actions" } # Print the actions that should be run pre-removal for the given package # USAGE: package_prerm_actions $package # RETURN: a list of actions, that can span over several lines, # the list can be empty package_prerm_actions() { local package package="$1" local prerm_actions prerm_actions=$(get_value "${package}_PRERM_RUN") # Return early if no action is set. if [ -z "$prerm_actions" ]; then return 0 fi # Ensure the list of actions always end with a line break. printf '%s\n' "$prerm_actions" } # Print the warning messages that should be displayed at the end of the given package installation # USAGE: package_postinst_warnings $package # RETURN: one or several messages, separated by line breaks, # the message can be empty package_postinst_warnings() { local package package="$1" get_value "${package}_POSTINST_WARNINGS" } src/30_packages/20_dependencies.sh0000644000000000000000000000213713120060140015674 0ustar rootroot# List all native packages providing dependencies of the given package. # USAGE: dependencies_full_list_packages $package dependencies_full_list_packages() { local package package="$1" local \ packages_list_legacy \ packages_list_siblings \ packages_list_commands \ packages_list_libraries \ packages_list_mono \ packages_list_gstreamer packages_list_legacy=$(dependencies_legacy_list_packages "$package") packages_list_siblings=$(dependencies_siblings_list_packages "$package") packages_list_commands=$(dependencies_commands_list_packages "$package") packages_list_libraries=$(dependencies_libraries_list_packages "$package") packages_list_mono=$(dependencies_mono_list_packages "$package") packages_list_gstreamer=$(dependencies_gstreamer_list_packages "$package") ## TODO: Some games would benefit from the ability to set dependencies on fonts. local packages_list_full packages_list_full=" $packages_list_legacy $packages_list_siblings $packages_list_commands $packages_list_libraries $packages_list_mono $packages_list_gstreamer" printf '%s' "$packages_list_full" | list_clean } src/30_packages/25_dependencies_commands.sh0000644000000000000000000000637613120060140017573 0ustar rootroot# Print commands required by given package. # USAGE: dependencies_commands_list $package dependencies_commands_list() { local package package="$1" local dependencies_commands dependencies_commands=$(context_value "${package}_DEPENDENCIES_COMMANDS") printf '%s' "$dependencies_commands" | list_clean } # Print native packages providing commands required by given package. # USAGE: dependencies_commands_list_packages $package dependencies_commands_list_packages() { local package package="$1" local dependencies_commands dependencies_commands=$(dependencies_commands_list "$package") # Return early if current package does not require any command. if [ -z "$dependencies_commands" ]; then return 0 fi local option_package dependencies_packages option_package=$(option_value 'package') case "$option_package" in ('arch') dependencies_packages=$(dependencies_commands_list_archlinux "$dependencies_commands") ;; ('deb') dependencies_packages=$( set_current_package "$package" dependencies_commands_list_debian "$dependencies_commands" ) ;; ('gentoo') dependencies_packages=$( set_current_package "$package" dependencies_commands_list_gentoo "$dependencies_commands" ) ;; esac printf '%s' "$dependencies_packages" | list_clean } # Add a command to the list of the given package. # This function is used to update the commands dependencies list. # USAGE: dependencies_add_command $package $dependency dependencies_add_command() { local package dependency package="$1" dependency="$2" local current_dependencies current_dependencies=$(dependencies_commands_list "$package") local dependencies_variable_name dependencies_variable_name=$(context_name "${package}_DEPENDENCIES_COMMANDS") if [ -z "$dependencies_variable_name" ]; then dependencies_variable_name="${package}_DEPENDENCIES_COMMANDS" fi export $dependencies_variable_name="$current_dependencies $dependency" } # Print the path to a temporary files used for unknown commands listing # USAGE: dependencies_unknown_commands_file dependencies_unknown_commands_file() { printf '%s/unknown_commands_list' "$PLAYIT_WORKDIR" } # Print a list of unknown commands # USAGE: dependencies_unknown_commands_list dependencies_unknown_commands_list() { local unknown_commands_list unknown_commands_list=$(dependencies_unknown_commands_file) # Return early if there is no unknown command if [ ! -e "$unknown_commands_list" ]; then return 0 fi list_clean < "$unknown_commands_list" } # Clear the list of unknown commands # USAGE: dependencies_unknown_commands_clear dependencies_unknown_commands_clear() { local unknown_commands_list unknown_commands_list=$(dependencies_unknown_commands_file) rm --force "$unknown_commands_list" } # Add a command to the list of unknown ones # USAGE: dependencies_unknown_command_add $unknown_command dependencies_unknown_command_add() { local unknown_command unknown_command="$1" local unknown_commands_list unknown_commands_list=$(dependencies_unknown_commands_file) # Do nothing if this command is already included in the list if [ -e "$unknown_commands_list" ] && grep --quiet --fixed-strings --word-regexp "$unknown_command" "$unknown_commands_list" then return 0 fi printf '%s\n' "$unknown_command" >> "$unknown_commands_list" } src/30_packages/25_dependencies_gstreamer.sh0000644000000000000000000000626113120060140017754 0ustar rootroot# Print GStreamer decoders required by given package. # USAGE: dependencies_gstreamer_list $package dependencies_gstreamer_list() { local package package="$1" local dependencies_gstreamer dependencies_gstreamer=$(context_value "${package}_DEPENDENCIES_GSTREAMER_PLUGINS") # Fall back on default dependencies list for current game engine. if [ -z "$dependencies_gstreamer" ]; then local game_engine game_engine=$(game_engine) case "$game_engine" in ('unrealengine4') dependencies_gstreamer=$(dependencies_gstreamer_list_unrealengine4 "$package") ;; esac fi printf '%s' "$dependencies_gstreamer" | list_clean } # Print native packages providing GStreamer decoders required by given package. # USAGE: dependencies_gstreamer_list_packages $package dependencies_gstreamer_list_packages() { local package package="$1" local dependencies_gstreamer dependencies_gstreamer=$(dependencies_gstreamer_list "$package") # Return early if current package does not require any GStreamer decoder. if [ -z "$dependencies_gstreamer" ]; then return 0 fi local option_package dependencies_packages option_package=$(option_value 'package') case "$option_package" in ('arch') dependencies_packages=$( set_current_package "$package" dependencies_gstreamer_list_archlinux "$dependencies_gstreamer" ) ;; ('deb') dependencies_packages=$(dependencies_gstreamer_list_debian "$dependencies_gstreamer") ;; ('gentoo') dependencies_packages=$( set_current_package "$package" dependencies_gstreamer_list_gentoo "$dependencies_gstreamer" ) ;; esac printf '%s' "$dependencies_packages" | list_clean } # Print the path to a temporary files used for unknown GStreamer media formats listing # USAGE: dependencies_unknown_gstreamer_media_formats_file dependencies_unknown_gstreamer_media_formats_file() { printf '%s/unknown_gstreamer_media_formats_list' "$PLAYIT_WORKDIR" } # Print a list of unknown GStreamer media formats # USAGE: dependencies_unknown_gstreamer_media_formats_list dependencies_unknown_gstreamer_media_formats_list() { local unknown_formats_list unknown_formats_list=$(dependencies_unknown_gstreamer_media_formats_file) # Return early if there is no unknown library if [ ! -e "$unknown_formats_list" ]; then return 0 fi list_clean < "$unknown_formats_list" } # Clear the list of unknown GStreamer media formats # USAGE: dependencies_unknown_gstreamer_media_formats_clear dependencies_unknown_gstreamer_media_formats_clear() { local unknown_formats_list unknown_formats_list=$(dependencies_unknown_gstreamer_media_formats_file) rm --force "$unknown_formats_list" } # Add a GStreamer media format to the list of unknown ones # USAGE: dependencies_unknown_gstreamer_media_formats_add $unknown_format dependencies_unknown_gstreamer_media_formats_add() { local unknown_format unknown_formats_list unknown_format="$1" unknown_formats_list=$(dependencies_unknown_gstreamer_media_formats_file) # Do nothing if this format is already included in the list if [ -e "$unknown_formats_list" ] && grep --quiet --fixed-strings --word-regexp "$unknown_format" "$unknown_formats_list" then return 0 fi printf '%s\n' "$unknown_format" >> "$unknown_formats_list" } src/30_packages/25_dependencies_legacy.sh0000644000000000000000000000272113120060140017224 0ustar rootroot# Print packages required by given package, set using legacy dependencies system. # USAGE: dependencies_legacy_list $package dependencies_legacy_list() { local package package="$1" local dependencies_legacy dependencies_legacy=$(context_value "${package}_DEPS") # Return early if current package does not use legacy dependencies. if [ -z "$dependencies_legacy" ]; then return 0 fi if compatibility_level_is_at_least '2.30'; then warning_deprecated_variable "${package}_DEPS" "${package}_DEPENDENCIES_xxx" fi printf '%s' "$dependencies_legacy" | sed 's/ /\n/g' | list_clean } # Print native packages required by given package, set using legacy dependencies system. # USAGE: dependencies_legacy_list_packages $package dependencies_legacy_list_packages() { local package package="$1" local dependencies_legacy dependencies_legacy=$(dependencies_legacy_list "$package") # Return early if current package does not use legacy dependencies. if [ -z "$dependencies_legacy" ]; then return 0 fi local option_package dependencies_packages option_package=$(option_value 'package') case "$option_package" in ('arch') dependencies_packages=$(dependencies_legacy_list_archlinux "$dependencies_legacy") ;; ('deb') dependencies_packages=$(dependencies_legacy_list_debian "$dependencies_legacy") ;; ('gentoo') dependencies_packages=$(dependencies_legacy_list_gentoo "$dependencies_legacy") ;; esac printf '%s' "$dependencies_packages" | list_clean } src/30_packages/25_dependencies_libraries.sh0000644000000000000000000001147713120060140017744 0ustar rootroot# Print native libraries required by given package. # USAGE: dependencies_libraries_list $package dependencies_libraries_list() { local package package="$1" local dependencies_libraries dependencies_libraries=$(context_value "${package}_DEPENDENCIES_LIBRARIES") # Try to find a fallback value based on the package identifier: # current package → fallback package # PKG_BIN64 → PKG_BIN # PKG_BIN32 → PKG_BIN # PKG_LIBS64 → PKG_LIBS # PKG_LIBS32 → PKG_LIBS if [ -z "$dependencies_libraries" ]; then case "$package" in ('PKG_BIN64'|'PKG_BIN32') dependencies_libraries=$(dependencies_libraries_list 'PKG_BIN') ;; ('PKG_LIBS64'|'PKG_LIBS32') dependencies_libraries=$(dependencies_libraries_list 'PKG_LIBS') ;; esac fi # Fall back on default dependencies list for current game engine. if [ -z "$dependencies_libraries" ]; then local game_engine game_engine=$(game_engine) case "$game_engine" in ('gamemaker') dependencies_libraries=$(dependencies_libraries_list_gamemaker "$package") ;; ('visionaire') dependencies_libraries=$(dependencies_libraries_list_visionaire "$package") ;; esac fi printf '%s' "$dependencies_libraries" | list_clean } # Print native packages providing native libraries required by given package. # USAGE: dependencies_libraries_list_packages $package dependencies_libraries_list_packages() { local package package="$1" local dependencies_libraries dependencies_libraries=$(dependencies_libraries_list "$package") # Return early if current package does not require any native library. if [ -z "$dependencies_libraries" ]; then return 0 fi local option_package dependencies_packages option_package=$(option_value 'package') case "$option_package" in ('arch') dependencies_packages=$( set_current_package "$package" dependencies_libraries_list_archlinux "$dependencies_libraries" ) ;; ('deb') dependencies_packages=$(dependencies_libraries_list_debian "$dependencies_libraries") ;; ('gentoo') dependencies_packages=$( set_current_package "$package" dependencies_libraries_list_gentoo "$dependencies_libraries" ) ;; esac printf '%s' "$dependencies_packages" | list_clean } # Print the list of native libraries required by all packages # USAGE: dependencies_list_native_libraries_all # RETURNS: a list of native library names, # one per line dependencies_list_native_libraries_all() { local packages_list package dependencies_libraries dependencies_libraries_all packages_list=$(packages_list) for package in $packages_list; do dependencies_libraries=$(dependencies_libraries_list "$package") dependencies_libraries_all="${dependencies_libraries_all:-} $dependencies_libraries" done printf '%s' "$dependencies_libraries_all" | list_clean } # Print the path to a temporary files used for unknown libraries listing # USAGE: dependencies_unknown_libraries_file dependencies_unknown_libraries_file() { printf '%s/unknown_libraries_list' "$PLAYIT_WORKDIR" } # Print a list of unknown libraries # USAGE: dependencies_unknown_libraries_list dependencies_unknown_libraries_list() { local unknown_libraries_list unknown_libraries_list=$(dependencies_unknown_libraries_file) # Return early if there is no unknown library if [ ! -e "$unknown_libraries_list" ]; then return 0 fi list_clean < "$unknown_libraries_list" } # Clear the list of unknown libraries # USAGE: dependencies_unknown_libraries_clear dependencies_unknown_libraries_clear() { local unknown_libraries_list unknown_libraries_list=$(dependencies_unknown_libraries_file) rm --force "$unknown_libraries_list" } # Add a library to the list of unknown ones # USAGE: dependencies_unknown_libraries_add $unknown_library dependencies_unknown_libraries_add() { local unknown_library unknown_libraries_list unknown_library="$1" unknown_libraries_list=$(dependencies_unknown_libraries_file) # Do nothing if this library is already included in the list if [ -e "$unknown_libraries_list" ] && grep --quiet --fixed-strings --word-regexp "$unknown_library" "$unknown_libraries_list" then return 0 fi printf '%s\n' "$unknown_library" >> "$unknown_libraries_list" } # Add a depdendency to the list of the given package. # This function is used to update the native libraries dependencies list. # USAGE: dependencies_add_native_libraries $package $dependency dependencies_add_native_libraries() { local package dependency package="$1" dependency="$2" local current_dependencies current_dependencies=$(dependencies_libraries_list "$package") local dependencies_variable_name dependencies_variable_name=$(context_name "${package}_DEPENDENCIES_LIBRARIES") if [ -z "$dependencies_variable_name" ]; then dependencies_variable_name="${package}_DEPENDENCIES_LIBRARIES" fi export $dependencies_variable_name="$current_dependencies $dependency" } src/30_packages/25_dependencies_mono.sh0000644000000000000000000000473213120060140016734 0ustar rootroot# Print Mono libraries required by given package. # USAGE: dependencies_mono_list $package dependencies_mono_list() { local package package="$1" local dependencies_mono dependencies_mono=$(context_value "${package}_DEPENDENCIES_MONO_LIBRARIES") printf '%s' "$dependencies_mono" | list_clean } # Print native packages providing Mono libraries required by given package. # USAGE: dependencies_mono_list_packages $package dependencies_mono_list_packages() { local package package="$1" local dependencies_mono dependencies_mono=$(dependencies_mono_list "$package") # Return early if current package does not require any Mono library. if [ -z "$dependencies_mono" ]; then return 0 fi local option_package option_package=$(option_value 'package') case "$option_package" in ('arch') dependencies_mono_list_archlinux "$dependencies_mono" ;; ('deb') dependencies_mono_list_debian "$dependencies_mono" ;; ('gentoo') dependencies_mono_list_gentoo "$dependencies_mono" ;; esac } # Print the path to a temporary files used for unknown Mono libraries listing # USAGE: dependencies_unknown_mono_libraries_file dependencies_unknown_mono_libraries_file() { printf '%s/unknown_mono_libraries_list' "$PLAYIT_WORKDIR" } # Print a list of unknown Mono libraries # USAGE: dependencies_unknown_mono_libraries_list dependencies_unknown_mono_libraries_list() { local unknown_library unknown_libraries_list unknown_libraries_list=$(dependencies_unknown_mono_libraries_file) # Return early if there is no unknown library if [ ! -e "$unknown_libraries_list" ]; then return 0 fi list_clean < "$unknown_libraries_list" } # Clear the list of unknown Mono libraries # USAGE: dependencies_unknown_mono_libraries_clear dependencies_unknown_mono_libraries_clear() { local unknown_library unknown_libraries_list unknown_libraries_list=$(dependencies_unknown_mono_libraries_file) rm --force "$unknown_libraries_list" } # Add a Mono library to the list of unknown ones # USAGE: dependencies_unknown_mono_libraries_add $unknown_library dependencies_unknown_mono_libraries_add() { local unknown_library unknown_libraries_list unknown_library="$1" unknown_libraries_list=$(dependencies_unknown_mono_libraries_file) # Do nothing if this library is already included in the list if [ -e "$unknown_libraries_list" ] && grep --quiet --fixed-strings --word-regexp "$unknown_library" "$unknown_libraries_list" then return 0 fi printf '%s\n' "$unknown_library" >> "$unknown_libraries_list" } src/30_packages/25_dependencies_siblings.sh0000644000000000000000000000556013120060140017576 0ustar rootroot# Print identifiers of sibling packages required by given package. # USAGE: dependencies_siblings_list $package dependencies_siblings_list() { local package package="$1" local dependencies_siblings dependencies_siblings=$(context_value "${package}_DEPENDENCIES_SIBLINGS") # Try to find a fallback value based on the package identifier: # current package → fallback package # PKG_BIN64 → PKG_BIN # PKG_BIN32 → PKG_BIN # PKG_LIBS64 → PKG_LIBS # PKG_LIBS32 → PKG_LIBS # PKG_L10N_xxx → PKG_L10N if [ -z "$dependencies_siblings" ]; then case "$package" in ('PKG_BIN64'|'PKG_BIN32') dependencies_siblings=$(dependencies_siblings_list 'PKG_BIN') ;; ('PKG_LIBS64'|'PKG_LIBS32') dependencies_siblings=$(dependencies_siblings_list 'PKG_LIBS') ;; ## TODO: The fallback value should be limited to PKG_L10N_xx identifiers, where xx is a two-letters language code. ('PKG_L10N_'*) dependencies_siblings=$(dependencies_siblings_list 'PKG_L10N') ;; esac fi # Fall back on the default list of dependencies for the current game engine if [ -z "$dependencies_siblings" ]; then local game_engine game_engine=$(game_engine) case "$game_engine" in ('gamemaker') dependencies_siblings=$(dependencies_siblings_list_gamemaker "$package") ;; ('visionaire') dependencies_siblings=$(dependencies_siblings_list_visionaire "$package") ;; esac fi printf '%s' "$dependencies_siblings" | list_clean } # Print native packages providing sibling packagesrequired by given package. # USAGE: dependencies_siblings_list_packages $package dependencies_siblings_list_packages() { local package package="$1" local dependencies_siblings dependencies_siblings=$(dependencies_siblings_list "$package") # Return early if current package does not require any Mono library. if [ -z "$dependencies_siblings" ]; then return 0 fi local option_package option_package=$(option_value 'package') case "$option_package" in ('arch') dependencies_siblings_list_archlinux "$dependencies_siblings" ;; ('deb') dependencies_siblings_list_debian "$dependencies_siblings" ;; ('gentoo') dependencies_siblings_list_gentoo "$dependencies_siblings" ;; esac } # Append packages to list of siblings required by given package. # USAGE: dependencies_siblings_add $package $sibling[…] dependencies_siblings_add() { local package package="$1" shift 1 local current_siblings_variable current_siblings_list current_siblings_variable=$(context_name "${package}_DEPENDENCIES_SIBLINGS") if [ -z "$current_siblings_variable" ]; then current_siblings_variable="${package}_DEPENDENCIES_SIBLINGS" fi current_siblings_list=$(dependencies_siblings_list "$package") local sibling for sibling in "$@"; do current_siblings_list="$current_siblings_list $sibling" done export "${current_siblings_variable}=${current_siblings_list}" } src/30_packages/90_messages.sh0000644000000000000000000000610613120060140015064 0ustar rootroot# Information: All packages are being built # USAGE: information_packages_generation information_packages_generation() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Géneration des paquets…\n' ;; ('en'|*) message='Packages generation…\n' ;; esac print_message 'info' "$message" } # display a notification when trying to build a package that already exists # USAGE: information_package_already_exists $file information_package_already_exists() { local file file="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='%s existe déjà.\n' ;; ('en'|*) message='%s already exists.\n' ;; esac print_message 'info' "$message" \ "$file" } # print package building message # USAGE: information_package_building $file information_package_building() { local file file="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Construction de %s…\n' ;; ('en'|*) message='Building %s…\n' ;; esac print_message 'info' "$message" \ "$file" } # Error - The provided package id uses an invalid format # USAGE: error_package_id_invalid $package_id error_package_id_invalid() { local package_id package_id="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Lʼid de paquet fourni ne correspond pas au format attendu : "%s"\n' message="$message"'Cette valeur ne peut utiliser que des caractères du set [-a-z0-9],' message="$message"' et ne peut ni débuter ni sʼachever par un tiret.\n' ;; ('en'|*) message='The provided package id is not using the expected format: "%s"\n' message="$message"'The value should only include characters from the set [-a-z0-9],' message="$message"' and can not begin nor end with an hyphen.\n' ;; esac print_message 'error' "$message" \ "$package_id" } # Error - The generation of the given package failed. # USAGE: error_package_generation_failed $package_name error_package_generation_failed() { local package_name package_name="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La génération du paquet suivant a échoué : %s\n' ;; ('en'|*) message='The generation of the following package failed: %s\n' ;; esac print_message 'error' "$message" \ "$package_name" } # Error - The generation of the metadata for the given package failed. # USAGE: error_package_metadata_generation_failed $package_name error_package_metadata_generation_failed() { local package_name package_name="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La génération des méta-données du paquet suivant a échoué : %s\n' ;; ('en'|*) message='The generation of the following package metadata failed: %s\n' ;; esac print_message 'error' "$message" \ "$package_name" } src/30_paths/00_common.sh0000644000000000000000000001214513120060140014075 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') local game_id package_version game_id=$(game_id) package_version=$(package_version) last_path_component="${game_id}-${package_version}" ;; esac printf '%s/share/doc/%s' "$install_prefix" "$last_path_component" } # Print install path for game files. # USAGE: path_game_data path_game_data() { local install_prefix game_id target_system path_structure install_prefix=$(option_value 'prefix') game_id=$(game_id) target_system=$(option_value 'package') case "$target_system" in ('deb') # Debian uses /usr/share/games as the default path for game-related data files. path_structure='%s/share/games/%s' ;; (*) # Non-Debian systems use /usr/share as the default path for all data files, # including game-related ones. path_structure='%s/share/%s' ;; esac ## Silence ShellCheck false-positive ## Don't use variables in the printf format string. Use printf "..%s.." "$foo". # shellcheck disable=SC2059 printf "$path_structure" "$install_prefix" "$game_id" } # Print install path for icons. # USAGE: path_icons path_icons() { # Icons are always installed under the default install prefix. # launcher_desktop (src/30_launchers/00_common.sh) expects the icon to be available under either /usr or /usr/local. printf '/usr/share/icons/hicolor' } # Print install path for native libraries. # USAGE: path_libraries path_libraries() { local install_prefix game_id install_prefix=$(option_value 'prefix') game_id=$(game_id) local path_structure target_system ## Set default value path_structure='%s/lib/games/%s' ## Override default value if required target_system=$(option_value 'package') case "$target_system" in ('arch') local package package_architecture package=$(current_package) package_architecture=$(package_architecture "$package") if [ "$package_architecture" = '32' ]; then path_structure='%s/lib32/games/%s' fi ;; ('gentoo') 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') case "$package_architecture" in ('32') path_libraries='/usr/lib' ;; ('64') path_libraries='/usr/lib64' ;; esac ;; esac if [ -z "${path_libraries:-}" ]; then error_path_libraries_system_unsupported_architecture "$current_package" return 1 fi printf '%s' "$path_libraries" } # Print install path for TTF fonts. # USAGE: path_fonts_ttf path_fonts_ttf() { local game_id game_id=$(game_id) # Fonts are always installed under the default install prefix. printf '/usr/share/fonts/truetype/%s' "$game_id" } ## TODO: The path "${PLAYIT_WORKDIR}/gamedata/$(content_path_default)" is used by many game scripts, ## it should be provided by a dedicated function. src/30_paths/90_messages.sh0000644000000000000000000000166313120060140014430 0ustar rootroot# Error - path_libraries_system has been called for a package with an unsupported architecture # USAGE: error_path_libraries_system_unsupported_architecture $package error_path_libraries_system_unsupported_architecture() { local package package="$1" local package_architecture package_architecture=$(package_architecture "$package") local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le chemin vers les bibliothèques du système a été demandé pour le paquet "%s",\n' message="$message"'mais ce paquet vise une architecture pour lequel ce chemin n’est pas connu : %s\n' ;; ('en'|*) message='The path to the system libraries has been required for the package "%s",\n' message="$message"'but this path is not known for a package using this architecture: %s\n' ;; esac print_message 'error' "$message" \ "$package" \ "$package_architecture" } src/50_engine_custom/10_launchers.sh0000644000000000000000000000034413120060140016312 0ustar rootroot# Custom launcher - Throw an error if not overriden from the game script # USAGE: custom_launcher $application custom_launcher() { local application application="$1" error_custom_launcher_not_set "$application" return 1 } src/50_engine_custom/90_messages.sh0000644000000000000000000000116213120060140016144 0ustar rootroot# Error - The "custom_launcher" function has not been overriden by the game script # USAGE: error_custom_launcher_not_set $application error_custom_launcher_not_set() { local application application="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='L’application suivante nécessite que la fonction "custom_launcher" soit définie dans le script : %s\n' ;; ('en'|*) message='The following application expects the "custom_launcher" to be set by the game script: %s\n' ;; esac print_message 'error' "$message" \ "$application" } src/50_engine_dosbox/10_applications.sh0000644000000000000000000000075013120060140017001 0ustar rootroot# Print the DOSBox pre-run actions for the given application. # These actions are run inside DOSBox. # USAGE: dosbox_prerun $application dosbox_prerun() { local application application="$1" context_value "${application}_DOSBOX_PRERUN" } # Print the DOSBox post-run actions for the given application. # These actions are run inside DOSBox. # USAGE: dosbox_postrun $application dosbox_postrun() { local application application="$1" context_value "${application}_DOSBOX_POSTRUN" } src/50_engine_dosbox/10_launchers.sh0000644000000000000000000000547413120060140016307 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 # Set the paths that should be available to the generated launcher launcher_init_paths "$application" # Generate the game prefix prefix_symlinks_generate # Set up the paths diversion to persistent storage ## TODO: Only include the snippets used by the current game. 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 - 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" local dosbox_prerun dosbox_postrun dosbox_prerun=$(dosbox_prerun "$application") dosbox_postrun=$(dosbox_postrun "$application") cat <<- EOF mount c . c: EOF # If a disk image is required, print the command mounting it dosbox_image_mount if [ -n "$dosbox_prerun" ]; then cat <<- EOF $dosbox_prerun EOF fi game_exec_line "$application" if [ -n "$dosbox_postrun" ]; then cat <<- EOF $dosbox_postrun EOF fi cat <<- EOF exit EOF } # DOSBOX - Print the line starting the game # USAGE: dosbox_exec_line $application # RETURN: the command to execute, including its command line options dosbox_exec_line() { local application application="$1" local application_exe application_options application_exe=$(application_exe "$application") application_options=$(application_options "$application") cat <<- EOF $application_exe $application_options \$@ EOF } src/50_engine_dosbox/20_disk-images.sh0000644000000000000000000000673413120060140016521 0ustar rootroot# DOSBox - Print the path to the disk image # An empty string is returned if no disk image is required for the current game. # USAGE: dosbox_disk_image_path dosbox_disk_image_path() { local disk_image_path disk_image_path=$(context_value 'DOSBOX_DISK_IMAGE_PATH') ## Check for a disk image set using the legacy variable if [ -z "$disk_image_path" ] && [ -n "${GAME_IMAGE:-}" ]; then if compatibility_level_is_at_least '2.32'; then warning_deprecated_variable 'GAME_IMAGE' 'DOSBOX_DISK_IMAGE_PATH' fi disk_image_path="$GAME_IMAGE" fi printf '%s' "$disk_image_path" } # DOSBox - Print the type of the disk image # USAGE: dosbox_disk_image_type dosbox_disk_image_type() { local disk_image_type disk_image_type=$(context_value 'DOSBOX_DISK_IMAGE_TYPE') ## Check for a disk image set using the legacy variable if [ -z "$disk_image_type" ] && [ -n "${GAME_IMAGE_TYPE:-}" ]; then if compatibility_level_is_at_least '2.32'; then warning_deprecated_variable 'GAME_IMAGE_TYPE' 'DOSBOX_DISK_IMAGE_TYPE' fi disk_image_type="$GAME_IMAGE_TYPE" fi ## Fall back on the default type if [ -z "$disk_image_type" ]; then disk_image_type='iso' fi printf '%s' "$disk_image_type" } # DOSBox - Compute the full path of the disk image # An empty string is returned if the disk image could not be found. # USAGE: disk_image_path_full disk_image_path_full() { # Look for the disk image in the current package local disk_image_path_full disk_image_path package package_path path_game_data disk_image_path=$(dosbox_disk_image_path) package=$(current_package) package_path=$(package_path "$package") path_game_data=$(path_game_data) disk_image_path_full="${package_path}${path_game_data}/${disk_image_path}" if [ -e "$disk_image_path_full" ]; then printf '%s' "$disk_image_path_full" return 0 fi # Look for the disk image in all packages local packages_list packages_list=$(packages_list) for package in $packages_list; do package_path=$(package_path "$package") disk_image_path_full="${package_path}${path_game_data}/${disk_image_path}" if [ -e "$disk_image_path_full" ]; then printf '%s' "$disk_image_path_full" return 0 fi done # Nothing is printed if the disk image could not be found. } # DOSBox - Check for the presence of the disk image # USAGE: dosbox_image_presence_check dosbox_image_presence_check() { local disk_image_path_full disk_image_path_full=$(disk_image_path_full) test -n "$disk_image_path_full" } # DOSBox - If a disk image is required, print the command mounting it # USAGE: dosbox_image_mount dosbox_image_mount() { local disk_image_path disk_image_path=$(dosbox_disk_image_path) # Return early if no disk image is required by the current game if [ -z "$disk_image_path" ]; then return 0 fi # Check for the presence of the disk image if ! dosbox_image_presence_check; then error_dosbox_disk_image_no_found "$disk_image_path" return 1 fi # Print the command used to mount the disk image, based on its type local disk_image_type disk_image_type=$(dosbox_disk_image_type) case "$disk_image_type" in ('cdrom') local disk_image_path_full disk_image_path_full=$(disk_image_path_full) if [ -d "$disk_image_path_full" ]; then printf 'mount d %s -t cdrom' "$disk_image_path" else printf 'imgmount d %s -t cdrom' "$disk_image_path" fi ;; ('iso') printf 'imgmount d %s -t iso -fs iso' "$disk_image_path" ;; (*) error_dosbox_disk_image_type_invalid "$disk_image_path" "$disk_image_type" return 1 ;; esac printf '\n' } src/50_engine_dosbox/90_messages.sh0000644000000000000000000000240513120060140016131 0ustar rootroot# Error - The DOSBox image disk was not found # USAGE: error_dosbox_disk_image_no_found $disk_image error_dosbox_disk_image_no_found() { local disk_image disk_image="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Lʼimage de disque suivante nʼa pas été trouvée : %s\n' ;; ('en'|*) message='The following disk image could not be found: %s\n' ;; esac print_message 'error' "$message" \ "$disk_image" } # Error - The DOSBox image disk type is invalid. # USAGE: error_dosbox_disk_image_type_invalid $disk_image_path $disk_image_type error_dosbox_disk_image_type_invalid() { local disk_image_path disk_image_type disk_image_path=$1 disk_image_type=$2 local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Lʼimage de disque "%s" a un type invalide : %s\n' message="$message"'Seuls les types suivants sont acceptés :\n' ;; ('en'|*) message='The disk image "%s" is assigned an invalid type: %s\n' message="$message"'Only the following types are accepted:\n' ;; esac print_message 'error' "$message" \ "$disk_image_path" \ "$disk_image_type" printf -- '- %s\n' 'cdrom' 'iso' >/dev/stderr } src/50_engine_java/05_paths.sh0000644000000000000000000000024313120060140015056 0ustar rootroot# Set the extra paths that are used by Java launchers # USAGE: java_init_paths java_init_paths() { # Set the paths to java libraries launcher_paths_libraries } 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.sh0000644000000000000000000000426313120060140015725 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 # Set the paths that should be available to the generated launcher launcher_init_paths "$application" # Generate the game prefix prefix_symlinks_generate # Set up the paths diversion to persistent storage ## TODO: Only include the snippets used by the current game. 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 - Run Java # USAGE: java_launcher_run $application java_launcher_run() { local application application="$1" cat <<- 'EOF' # Run the game cd "$PATH_PREFIX" EOF # Set the preload paths for native libraries cat <<- 'EOF' ## Set the preload paths for native libraries export LD_LIBRARY_PATH="${PATH_LIBRARIES_USER}:${PATH_LIBRARIES_SYSTEM}:${LD_LIBRARY_PATH:-}" EOF application_prerun "$application" cat <<- 'EOF' ## Do not exit on application failure, ## to ensure post-run commands are run. set +o errexit EOF game_exec_line "$application" cat <<- 'EOF' game_exit_status=$? set -o errexit EOF application_postrun "$application" } # Java - Print the line starting the game # USAGE: java_exec_line $application # RETURN: the command to execute, including its command line options java_exec_line() { local application application="$1" local application_java_options application_exe application_options application_java_options=$(application_java_options "$application") application_exe=$(application_exe "$application") application_options=$(application_options "$application") cat <<- EOF java $application_java_options -jar "${application_exe}" $application_options "\$@" EOF } src/50_engine_mono/05_paths.sh0000644000000000000000000000024313120060140015105 0ustar rootroot# Set the extra paths that are used by Mono launchers # USAGE: mono_init_paths mono_init_paths() { # Set the paths to mono libraries launcher_paths_libraries } src/50_engine_mono/10_launchers.sh0000644000000000000000000000553213120060140015754 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 # Set the paths that should be available to the generated launcher launcher_init_paths "$application" # Generate the game prefix prefix_symlinks_generate # Set up the paths diversion to persistent storage ## TODO: Only include the snippets used by the current game. 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 # Set the paths that should be available to the generated launcher launcher_init_paths "$application" mono_launcher_run "$application" launcher_exit ;; (*) error_launchers_prefix_type_unsupported "$application" return 1 ;; esac } # 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_DATA' ;; esac cat <<- EOF # Run the game cd "$execution_path" EOF # Set the preload paths for native libraries cat <<- 'EOF' ## Set the preload paths for native libraries export LD_LIBRARY_PATH="${PATH_LIBRARIES_USER}:${PATH_LIBRARIES_SYSTEM}:${LD_LIBRARY_PATH:-}" EOF application_prerun "$application" # Apply common workarounds for Mono games mono_launcher_tweaks cat <<- 'EOF' ## Do not exit on application failure, ## to ensure post-run commands are run. set +o errexit EOF game_exec_line "$application" cat <<- 'EOF' game_exit_status=$? set -o errexit EOF application_postrun "$application" } # Mono launcher - Common workarounds # USAGE: mono_launcher_tweaks mono_launcher_tweaks() { cat <<- 'EOF' ## Work around terminfo Mono bug, ## cf. https://github.com/mono/mono/issues/6752 export TERM="${TERM%-256color}" ## Work around Mono unpredictable behaviour with non-US locales export LANG=C EOF } # Mono - Print the line starting the game # USAGE: mono_exec_line $application # RETURN: the command to execute, including its command line options mono_exec_line() { local application application="$1" local application_exe application_options application_exe=$(application_exe "$application") application_options=$(application_options "$application") cat <<- EOF mono "${application_exe}" $application_options "\$@" EOF } src/50_engine_native/05_paths.sh0000644000000000000000000000025313120060140015424 0ustar rootroot# Set the extra paths that are used by native launchers # USAGE: native_init_paths native_init_paths() { # Set the paths to native libraries launcher_paths_libraries } src/50_engine_native/10_launchers.sh0000644000000000000000000001225513120060140016272 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 # Set the paths that should be available to the generated launcher launcher_init_paths "$application" # Generate the game prefix prefix_symlinks_generate # Set up the paths diversion to persistent storage ## TODO: Only include the snippets used by the current game. 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 # Set the paths that should be available to the generated launcher launcher_init_paths "$application" native_launcher_run "$application" launcher_exit ;; (*) error_launchers_prefix_type_unsupported "$application" return 1 ;; esac } # 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_DATA' ;; 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 # 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 launcher_tweak_sdl_override ;; ('visionaire') # Force the use of the system SDL library launcher_tweak_sdl_override ;; (*) # Make a hard copy of the game binary in the current prefix, # otherwise the engine might follow the link and run the game from the system path. local prefix_type prefix_type=$(application_prefix_type "$application") case "$prefix_type" in ('symlinks') native_launcher_binary_copy ;; esac ;; esac # Enable a fake $HOME path local fake_home_persistent_directories fake_home_persistent_directories=$(fake_home_persistent_directories) if [ -n "$fake_home_persistent_directories" ]; then fake_home_enable fi # Set the preload paths for native libraries cat <<- 'EOF' ## Set the preload paths for native libraries export LD_LIBRARY_PATH="${PATH_LIBRARIES_USER}:${PATH_LIBRARIES_SYSTEM}:${LD_LIBRARY_PATH:-}" EOF local ld_preload_source ld_preload_source=$(ld_preload_source) if [ -n "$ld_preload_source" ]; then local path_libraries path_libraries=$(path_libraries) cat <<- EOF export LD_PRELOAD="\${LD_PRELOAD:-}:${path_libraries}/preload-hack.so" EOF fi printf '\n' application_prerun "$application" cat <<- 'EOF' ## Do not exit on application failure, ## to ensure post-run commands are run. set +o errexit EOF game_exec_line "$application" cat <<- 'EOF' game_exit_status=$? set -o errexit EOF application_postrun "$application" # Disable the fake $HOME path if [ -n "$fake_home_persistent_directories" ]; then fake_home_disable fi case "$game_engine" in ('unity3d') # Stop pulseaudio if it has been started for this game session launcher_unity3d_pulseaudio_stop ;; esac } # Linux native launcher - Copy the game binary into the game prefix # USAGE: native_launcher_binary_copy native_launcher_binary_copy() { local application_exe exe_destination application_exe=$(application_exe "$application") exe_destination="\${PATH_PREFIX}/${application_exe}" cat <<- 'EOF' # Copy the game binary into the user prefix EOF cat <<- EOF exe_destination="${exe_destination}" EOF cat <<- 'EOF' if [ -h "$exe_destination" ]; then exe_source=$(realpath "$exe_destination") cp --remove-destination "$exe_source" "$exe_destination" fi unset exe_destination exe_source EOF } # Linux native - Print the line starting the game # USAGE: native_exec_line $application # RETURN: the command to execute, including its command line options native_exec_line() { local application application="$1" local application_exe application_options application_exe=$(application_exe "$application") application_options=$(application_options "$application") cat <<- EOF "./${application_exe}" $application_options "\$@" EOF } src/50_engine_native/40_preload-hacks.sh0000644000000000000000000000342713120060140017027 0ustar rootroot# Print the C source of the LD_PRELOAD hack that should be included in the current package # USAGE: ld_preload_source ld_preload_source() { local ld_preload_source ld_preload_source=$(context_value 'LD_PRELOAD_SOURCE') # If no value is explictly set, fall back on engine-specific ones if [ -z "$ld_preload_source" ]; then local game_engine game_engine=$(game_engine) case "$game_engine" in ('unity3d') ld_preload_source=$(unity3d_disable_map32bit) ;; esac fi printf '%s' "$ld_preload_source" } # Build the .so from the C source of an LD_PRELOAD hack # USAGE: ld_preload_build $package ld_preload_build() { local package package="$1" # Set the path to the temporary source snippet local path_source path_source="${PLAYIT_WORKDIR}/preload-hack.c" # Set the path to the packaged .so library local package_path path_libraries path_binary package_path=$(package_path "$package") path_libraries=$(path_libraries) path_binary="${package_path}${path_libraries}/preload-hack.so" # Write the C source snippet local ld_preload_source ## TODO: The context package should be set explicitly, ## as LD_PRELOAD_SOURCE can be context-specific. ld_preload_source=$(ld_preload_source) printf '%s' "$ld_preload_source" > "$path_source" # Prepare the compiler options string local gcc_options package_architecture gcc_options='-shared -Wall -fPIC -ldl' package_architecture=$(package_architecture "$package") if [ "$package_architecture" = '32' ]; then gcc_options="$gcc_options -m32" fi # Build the .so library local binary_directory binary_directory=$(dirname "$path_binary") mkdir --parents "$binary_directory" if ! gcc $gcc_options "$path_source" -o "$path_binary"; then error_hack_build_failure return 1 fi # Remove the source snippet rm "$path_source" } src/50_engine_native/90_messages.sh0000644000000000000000000000124413120060140016121 0ustar rootroot# Error - The build of a preload shim failed # USAGE: error_hack_build_failure error_hack_build_failure() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La construction d’une bibliothèque depuis ses sources a échoué.\n' message="$message"'Ce message devrait être précédé d’une erreur indiquant des en-têtes de compilation manquantes.\n' ;; ('en'|*) message='The building of a library from its sources failed.\n' message="$message"'This message should be preceded with an error indicating the missing compilation headers.\n' ;; esac print_message 'error' "$message" } src/50_engine_renpy/10_launchers.sh0000644000000000000000000000376313120060140016145 0ustar rootroot# Ren'Py - Print the launcher script content # USAGE: renpy_launcher $application renpy_launcher() { local application application="$1" local prefix_type prefix_type=$(application_prefix_type "$application") case "$prefix_type" in ('symlinks') launcher_headers # Set the paths that should be available to the generated launcher launcher_init_paths "$application" # Generate the game prefix prefix_symlinks_generate # Set up the paths diversion to persistent storage ## TODO: Only include the snippets used by the current game. persistent_storage_initialization persistent_storage_common persistent_path_diversion persistent_storage_update_directories persistent_storage_update_files renpy_launcher_run "$application" launcher_exit ;; ('none') launcher_headers # Set the paths that should be available to the generated launcher launcher_init_paths "$application" renpy_launcher_run "$application" launcher_exit ;; (*) error_launchers_prefix_type_unsupported "$application" return 1 ;; esac } # Ren'Py - Print the commands running the game, either from the read-only system path or from a symlinks farm prefix # USAGE: renpy_launcher_run $application renpy_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_DATA' ;; esac cat <<- EOF # Run the game cd "$execution_path" EOF application_prerun "$application" game_exec_line "$application" application_postrun "$application" } # Ren'Py - Print the line starting the game # USAGE: renpy_exec_line $application # RETURN: the command to execute renpy_exec_line() { ## The application identifier is not actually used for Ren'Py games, ## it is only passed for consistency with other *_exec_line functions. local application application="$1" cat <<- 'EOF' renpy . EOF } src/50_engine_scummvm/10_applications.sh0000644000000000000000000000225213120060140017171 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") # If no explicit ScummVM id is set, try to guess one from the game engine if [ -z "$application_scummid" ]; then local game_engine game_engine=$(game_engine) case "$game_engine" in ('ags') local ags_name ags_name=$(ags_name) application_scummid="ags:${ags_name}" ;; esac fi # Return early if no ScummVM id is set if [ -z "$application_scummid" ]; then return 0 fi # Check that the id fits the ScummVM id format # Allowed formats are: # - "engine:game" # - "game" # The game field is allowed to include hyphen-minus, like in: "ags:gobliiins5-1" if ! printf '%s' "$application_scummid" | grep --quiet --regexp='^\([0-9a-z]\+:\)\?[-0-9a-z]\+$' then error_application_scummid_invalid "$application" "$application_scummid" return 1 fi printf '%s' "$application_scummid" } src/50_engine_scummvm/10_launchers.sh0000644000000000000000000000233513120060140016471 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 # Set the paths that should be available to the generated launcher launcher_init_paths "$application" scummvm_launcher_run launcher_exit ;; (*) error_launchers_prefix_type_unsupported "$application" return 1 ;; esac } # ScummVM launcher - Run ScummVM # USAGE: scummvm_launcher_run scummvm_launcher_run() { cat <<- 'EOF' # Run the game EOF application_prerun "$application" game_exec_line "$application" application_postrun "$application" } # ScummVM - Print the line starting the game # USAGE: scummvm_exec_line $application # RETURN: the command to execute, including its command line options scummvm_exec_line() { local application application="$1" local application_options application_scummid application_options=$(application_options "$application") application_scummid=$(application_scummvm_scummid "$application") cat <<- EOF scummvm --path="\$PATH_GAME_DATA" $application_options "\$@" "${application_scummid}" EOF } src/50_engine_scummvm/90_messages.sh0000644000000000000000000000157213120060140016326 0ustar rootroot# Error - The provided ScummVM id uses an invalid format # USAGE: error_application_scummid_invalid $application $application_scummid error_application_scummid_invalid() { local application application_scummid application="$1" application_scummid="$2" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Lʼid ScummVM fourni pour lʼapplication %s ne semble pas correct : "%s"\n' message="$message"'Une liste de valeurs acceptées peut se trouver sur le site Web de ScummVM : \n%s\n' ;; ('en'|*) message='The ScummVM id provided for application %s does not seem correct: "%s"\n' message="$message"'A list of valid values can be found on ScummVM website: \n%s\n' ;; esac print_message 'error' "$message" \ "$application" \ "$application_scummid" \ 'https://www.scummvm.org/compatibility/' } src/50_engine_web/10_launchers.sh0000644000000000000000000000622613120060140015562 0ustar rootroot# Web launcher - Print the script content # USAGE: web_launcher $application web_launcher() { local application application="$1" local prefix_type prefix_type=$(application_prefix_type "$application") case "$prefix_type" in ('symlinks') launcher_headers # Set the paths that should be available to the generated launcher. launcher_init_paths "$application" ## TODO: This can probably be dropped, cf. web_launcher_environment below. web_launcher_environment "$application" # Generate the game prefix prefix_generate_links_farm launcher_prefix_symlinks_build # A fake $HOME path is used to not mess up with the user real Firefox profiles. persistent_path_diversion fake_home_persistent ## TODO: This can probably be dropped, cf. web_firefox_profile_path below. web_firefox_profile_path web_launcher_run "$application" launcher_exit ;; (*) error_launchers_prefix_type_unsupported "$application" return 1 ;; esac } # Web launcher - Set the environment # USAGE: web_launcher_environment $application web_launcher_environment() { local application application="$1" local game_id path_game web_path game_id=$(game_id) path_game=$(path_game_data) web_path=$(application_exe "$application") ## TODO: GAME_ID can be dropped by updating web_firefox_profile_path. ## TODO: WEB_PATH can be dropped by updating web_launcher_run. cat <<- EOF # Set the environment GAME_ID='${game_id}' WEB_PATH='/${web_path}' EOF } # Web launcher - Set the path to the Firefox profile # USAGE: web_firefox_profile_path ## TODO: This path should be set from launcher_init_paths. web_firefox_profile_path() { cat <<- 'EOF' # Compute the path to the Firefox profile for the current session firefox_profile_path() { # Firefox profile path can be explicitely set using an environment variable if [ -n "$PLAYIT_FIREFOX_PROFILE_PATH" ]; then printf '%s' "$PLAYIT_FIREFOX_PROFILE_PATH" return 0 fi # Compute the default Firefox profile path if none has been explicitely set printf '%s/play.it/firefox/%s' \ "${XDG_CACHE_HOME:="$HOME/.cache"}" \ "$GAME_ID" } EOF } # Web launcher - Run the Python 3 Web server and open Firefox # USAGE: web_launcher_run web_launcher_run() { cat <<- 'EOF' cd "$PATH_PREFIX" # Start local Web server WEB_HOST='localhost' WEB_PORT='8000' python3 -m http.server \ --directory "$PWD" \ --bind "$WEB_HOST" "$WEB_PORT" & WEB_PID=$! # Set the path to the dedicated Firefox profile and the local URL of the game FIREFOX_PROFILE_PATH=$(firefox_profile_path) WEB_URL="http://${WEB_HOST}:${WEB_PORT}${WEB_PATH}" EOF # Enable a fake $HOME path, to prevent messing up with the user regular Firefox profiles fake_home_enable cat <<- 'EOF' # Run Firefox, using a dedicated profile firefox -CreateProfile "./play.it $FIREFOX_PROFILE_PATH" ## Ignore firefox failure exit code in case of crash, so post-run actions are not skipped. firefox -safe-mode -kiosk -no-remote -profile "$FIREFOX_PROFILE_PATH" "$@" "$WEB_URL" || true EOF # Disable the fake $HOME path fake_home_disable cat <<- 'EOF' # Stop local Web server kill -s TERM $WEB_PID EOF } src/50_engine_wine/00_common.sh0000644000000000000000000000127513120060140015251 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" } src/50_engine_wine/05_paths.sh0000644000000000000000000000163313120060140015103 0ustar rootroot# Set the extra paths that are used by WINE launchers # USAGE: wine_init_paths wine_init_paths() { # Set the path to the WINE prefix local game_id game_id=$(game_id) cat <<- EOF ## Set the path to the WINE prefix path_wineprefix() { # The prefix path can be overriden 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}" } PATH_WINEPREFIX=\$(path_wineprefix) EOF # Set the path to store registry dumps if [ -n "${WINE_REGEDIT_PERSISTENT_KEYS:-}" ]; then cat <<- 'EOF' ## Set the path to store registry dumps path_wine_registry() { printf '%s/wine/regedit' "$PATH_PERSISTENT" } PATH_WINE_REGISTRY=$(path_wine_registry) EOF fi } src/50_engine_wine/10_icons.sh0000644000000000000000000000051213120060140015066 0ustar rootroot# Compute the icon path from the APP_xxx_EXE value # USAGE: icon_wine_path $icon_identifier icon_wine_path() { local icon_identifier icon_identifier="$1" local application application_exe application=$(icon_application "$icon_identifier") application_exe=$(application_exe "$application") printf '%s' "$application_exe" } src/50_engine_wine/10_launchers.sh0000644000000000000000000000753213120060140015750 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 # Set the paths that should be available to the generated launcher launcher_init_paths "$application" # Include the wrappers that are useful for the current game wine_launcher_wrappers # Generate the game prefix prefix_symlinks_generate # Set up the paths diversion to persistent storage ## TODO: Only include the snippets used by the current game. 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 } # Include the wrappers that are useful for the current WINE game # USAGE: wine_launcher_wrappers wine_launcher_wrappers() { cat <<- 'EOF' # Set the wrappers ## 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 # Include the path to the `regedit` command only if it is going to be used local regedit_initial_content regedit_initial_content=$(regedit_initial_content) if [ -n "$regedit_initial_content" ] || [ -n "${WINE_REGEDIT_PERSISTENT_KEYS:-}" ] then cat <<- 'EOF' regedit_command() { wine_command | sed 's#/wine$#/regedit#' } EOF fi # Include the terminal and winetricks wrappers local winetricks_verbs renderer_name winetricks_verbs=$(wine_winetricks_verbs) renderer_name=$(wine_renderer_name) if [ -n "$winetricks_verbs" ] || [ "$renderer_name" != 'default' ] then launcher_wrapper_terminal wine_winetricks_wrapper fi # Include the screen resolution detection function if it is required to set a virtual desktop local virtual_desktop virtual_desktop=$(wine_virtual_desktop) if [ "$virtual_desktop" = 'auto' ]; then wine_snippet_screen_resolution fi } # WINE - Print the snippet handling the actual run of the game # USAGE: wine_launcher_run $application wine_launcher_run() { local application application="$1" local game_id game_id=$(game_id) cat <<- EOF # Run the game cd "\${WINEPREFIX}/drive_c/${game_id}" EOF local game_engine game_engine=$(game_engine) case "$game_engine" in ('visionaire') # Prevent the use of wayland SDL video driver visionaire_tweak_sdl_wine ;; esac application_prerun "$application" cat <<- 'EOF' ## Do not exit on application failure, ## to ensure post-run commands are run. set +o errexit EOF game_exec_line "$application" cat <<- 'EOF' game_exit_status=$? set -o errexit EOF application_postrun "$application" } # WINE - Print the line starting the game # USAGE: wine_exec_line $application # RETURN: the command to execute, including its command line options wine_exec_line() { local application application="$1" local application_exe application_options application_exe=$(application_exe "$application") application_options=$(application_options "$application") cat <<- EOF \$(wine_command) "${application_exe}" $application_options "\$@" EOF } src/50_engine_wine/20_regedit.sh0000644000000000000000000001157313120060140015410 0ustar rootroot# Load init .reg script during WINEPREFIX initialisation. # USAGE: regedit_initial regedit_initial() { local script_content script_content=$(regedit_initial_content) # Return early if there is no script to load during prefix initilisation if [ -z "$script_content" ]; then return 0 fi local game_id game_id=$(game_id) cat <<- EOF # Load initial registry keys. printf 'Loading initial registry keys…\\n' ( ## regedit fails to find the script if called with env --chdir. cd "\${WINEPREFIX}/drive_c/${game_id}" \$(regedit_command) registry-scripts/init.reg ) EOF } # Print the content of the init .reg file. # USAGE: regedit_initial_content regedit_initial_content() { local script_content script_content=$(context_value 'WINE_REGISTRY_INIT') # Return early if there is no initial registry key to load. if [ -z "$script_content" ]; then return 0 fi cat <<- EOF Windows Registry Editor Version 5.00 $script_content EOF } # Write the init .reg script setting the initial keys for the given package. # USAGE: wine_registry_script_write $package ## TODO: This should be renamed to regedit_initial_write, ## but that can only be done in sync with game scripts overriding this function. ## Maybe this can be handled with a temporary copy of the function, ## and one or the other called based on the compatibility level. wine_registry_script_write() { ## Identifier of the package that should include that .reg script local package="$1" local script_content script_content=$(regedit_initial_content) ## Return early if there is no script to write. if [ -z "$script_content" ]; then return 0 fi local package_path path_game_data script_directory script_path package_path=$(package_path "$package") path_game_data=$(path_game_data) script_directory="${package_path}${path_game_data}/registry-scripts" script_path="${script_directory}/init.reg" mkdir --parents "$script_directory" printf '%s' "$script_content" | iconv --from-code=UTF-8 --to-code=UTF-16 --output="$script_path" } # 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 local game_id game_id=$(game_id) cat <<- EOF # Set environment for registry keys persistent storage REGEDIT_DUMPS_WINEPREFIX_PATH="\${WINEPREFIX}/drive_c/${game_id}/wine/regedit" 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##*\\}" | tr '[:upper:]' '[:lower:]' } EOF } # WINE launcher - Store registry keys in a persistent path # USAGE: wine_launcher_regedit_store wine_launcher_regedit_store() { # Return early if no persistent registry keys are listed if [ -z "${WINE_REGEDIT_PERSISTENT_KEYS:-}" ]; then return 0 fi ## TODO: env --chdir could be used instead of cd in a subshell. 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 "$PATH_WINE_REGISTRY" ( cd "$REGEDIT_DUMPS_WINEPREFIX_PATH" find . -type f \ -exec cp --force --parents --target-directory="$PATH_WINE_REGISTRY" {} + ) EOF } # WINE launcher - Load registry keys from persistent dumps # USAGE: wine_launcher_regedit_load wine_launcher_regedit_load() { # Return early if no persistent registry keys are listed if [ -z "${WINE_REGEDIT_PERSISTENT_KEYS:-}" ]; then return 0 fi ## TODO: env --chdir could be used instead of cd in a subshell. cat <<- 'EOF' # Load registry keys from persistent dumps if [ -e "$PATH_WINE_REGISTRY" ]; then mkdir --parents "$REGEDIT_DUMPS_WINEPREFIX_PATH" ( cd "$PATH_WINE_REGISTRY" find . -type f \ -exec cp --force --parents --target-directory="$REGEDIT_DUMPS_WINEPREFIX_PATH" {} + ) fi while read -r registry_key; do if [ -z "$registry_key" ]; then continue fi registry_dump="${REGEDIT_DUMPS_WINEPREFIX_PATH}/$(regedit_convert_key_to_path "$registry_key")" if [ -e "$registry_dump" ]; then printf 'Loading registry key from "%s".\n' "$registry_dump" $(regedit_command) "$registry_dump" fi done << EOL $(printf '%s' "$REGEDIT_PERSISTENT_KEYS") EOL unset registry_key registry_dump EOF } src/50_engine_wine/20_renderer.sh0000644000000000000000000001145613120060140015573 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 } # Use WineD3D with a specific backend for Direct3D rendering. # USAGE: wine_launcher_renderer_wined3d $wined3d_backend ## TODO: Check if the forced 1s waiting time is still useful. 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 } # Use DXVK for Direct3D rendering. # USAGE: wine_launcher_renderer_dxvk ## TODO: Check if the forced 1s waiting time is still useful. wine_launcher_renderer_dxvk() { cat <<- 'EOF' # Use DXVK for Direct3D rendering. if command -v dxvk-setup >/dev/null 2>&1; then ## Compute the correct dxvk-setup options ## based on the current WINE origin wine_path_full=$(realpath "$(wine_command)") case "$wine_path_full" in ('/usr/bin/wine-stable') dxvk_options='--stable' ;; ('/usr/bin/wine-development') dxvk_options='--development' ;; ('/opt/wine-devel/bin/wine') dxvk_options='--upstream' ;; ('/opt/wine-staging/bin/wine') dxvk_options='--upstream-staging' ;; (*) dxvk_options='--stable' ;; esac ## Run dxvk-setup, spawning a terminal if required ## to ensure it is not silently running in the background. if [ -t 0 ]; then dxvk-setup install $dxvk_options else $(terminal_wrapper) -e \ env \ WINEARCH="$WINEARCH" \ WINEDEBUG="$WINEDEBUG" \ WINEPREFIX="$WINEPREFIX" \ dxvk-setup install $dxvk_options 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 } # Use vkd3d for Direct3D rendering. # USAGE: wine_launcher_renderer_vkd3d ## TODO: Check if the forced 1s waiting time is still useful. wine_launcher_renderer_vkd3d() { cat <<- 'EOF' # Use vkd3d for Direct3D rendering. if command -v winetricks >/dev/null 2>&1; then winetricks_wrapper vkd3d else message="\\033[1;33mWarning:\\033[0m\\n" message="${message}vkd3d patches could not be installed in the WINE prefix.\\n" message="${message}The game might run with display or performance issues.\\n" printf "\\n${message}\\n" fi # Wait a bit to ensure there is no lingering wine process. sleep 1s EOF } src/50_engine_wine/20_virtual-desktop.sh0000644000000000000000000000570513120060140017122 0ustar rootroot# WINE - Get the WINE virtual desktop setting expected by the current game. # # The value should be one of: # - none (default if no value is set) # - auto (use the current screen resolution when the game is launched for the first time) # - some specific resolution (example: "1280x1024") # # USAGE: wine_virtual_desktop wine_virtual_desktop() { local virtual_desktop ## TODO: Some game scripts would benefit from support for contextual values. virtual_desktop=${WINE_VIRTUAL_DESKTOP:-} # Fall back on the default setting for the current game engine. if [ -z "$virtual_desktop" ]; then local game_engine game_engine=$(game_engine) case "$game_engine" in ('unrealengine3') virtual_desktop=$(wine_virtual_desktop_unrealengine3) ;; (*) virtual_desktop='none' ;; esac fi case "$virtual_desktop" in ('none'|'auto') ;; (*[0-9]x[0-9]*) if ! printf '%s' "$virtual_desktop" | grep --silent '^[0-9]\+x[0-9]\+$'; then error_wine_virtual_desktop_invalid "$virtual_desktop" fi ;; (*) error_wine_virtual_desktop_invalid "$virtual_desktop" ;; esac printf '%s' "$virtual_desktop" } # WINE - Get the display resolution of the current screen. # USAGE: wine_snippet_screen_resolution wine_snippet_screen_resolution() { cat <<- 'EOF' # Return the type of the current display server. # Supported return values: # - wayland/sway # - xorg # If no supported server is running, nothing is returned. display_server() { if [ -n "${WAYLAND_DISPLAY:-}" ]; then if [ -n "${SWAYSOCK:-}" ]; then display_server='wayland/sway' fi elif [ -n "${DISPLAY:-}" ]; then display_server='xorg' fi printf '%s' "${display_server:-}" unset display_server } # Return the resolution of the current screen. # If no supported server is running, a fallback value is returned. screen_resolution() { display_server=$(display_server) case "$display_server" in ('wayland/sway') current_screen=$( LANG=C swaymsg --pretty --type get_workspaces | grep --after 1 '(focused)' | sed --silent --expression='s/\s*Output: \(.*\)$/\1/p' ) screen_resolution=$( LANG=C swaymsg --pretty --type get_outputs | grep --after 1 "$current_screen" | sed --silent --expression='s/.*Current mode: \([0-9]\+x[0-9]\+\) @.*$/\1/p' ) unset current_screen ;; ('xorg') screen_resolution=$( LANG=C xrandr | sed --regexp-extended --silent --expression='s/.*primary.* ([0-9]+x[0-9]+).*/\1/p' ) ;; (*) ## Fall back on a default resolution if an unsupported display server is in use. screen_resolution='1024x768' ;; esac printf '%s' "${screen_resolution:-}" unset display_server screen_resolution } EOF } src/50_engine_wine/20_wineprefix.sh0000644000000000000000000001550013120060140016137 0ustar rootroot# WINE - Set default value for WINEDLLOVERRIDES # USAGE: wine_dlloverrides_default # RETURN: the default value to use for WINEDLLOVERRIDES, # if it is not set in the user environment. wine_dlloverrides_default() { # A default value can be set from the game script, using the variable WINE_DLLOVERRIDES_DEFAULT local wine_dlloverrides wine_dlloverrides=$(context_value 'WINE_DLLOVERRIDES_DEFAULT') # Fall back on a default value if [ -z "$wine_dlloverrides" ]; then local wineprefix_tweaks wineprefix_tweaks=$(wine_wineprefix_tweaks) if printf '%s' "$wineprefix_tweaks" | grep --quiet --fixed-strings --line-regexp --regexp='mono'; then ## If Mono is going to be used in the WINE prefix, "mscoree" should not be disabled. wine_dlloverrides='winemenubuilder.exe,mshtml=' else wine_dlloverrides='winemenubuilder.exe,mscoree,mshtml=' fi fi printf '%s' "$wine_dlloverrides" } # WINE launcher - Set the WINE prefix environment # USAGE: wine_launcher_wineprefix_environment wine_launcher_wineprefix_environment() { # Set compatibility links to legacy paths ## TODO: env --chdir could be used instead of cd in a subshell. cat <<- 'EOF' # Set compatibility link to a legacy user path wineprefix_legacy_link() { path_current="$1" path_legacy="$2" user_directory="${WINEPREFIX}/drive_c/users/${USER}" ( cd "$user_directory" if [ ! -e "$path_current" ]; then path_current_parent=$(dirname "$path_current") mkdir --parents "$path_current_parent" mv "$path_legacy" "$path_current" fi path_legacy_parent=$(dirname "$path_legacy") mkdir --parents "$path_legacy_parent" ## Warning: the use of a link to an absolute path means that the WINE prefix can not be moved around without breaking the link. ## Using a link to a relative path would be better, but harder to implement when $path_legacy includes several path components (like "Local Settings/Application Data"). ln --symbolic "${user_directory}/${path_current}" "$path_legacy" ) unset user_directory path_current_parent unset path_current path_legacy } EOF # Compute WINE prefix architecture local option_package wine_architecture option_package=$(option_value 'package') case "$option_package" in ('arch') ## Arch Linux WINE is built in wow64 mode, ## in that mode win32 prefixes can not be created. wine_architecture='win64' ;; (*) local package package_architecture package=$(current_package) package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') wine_architecture='win32' ;; ('64') wine_architecture='win64' ;; esac ;; 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 export WINEARCH='$wine_architecture' export WINEDEBUG="\${WINEDEBUG:=-all}" export WINEPREFIX="\$PATH_WINEPREFIX" ## Disable some WINE anti-features ## - creation of desktop entries ## - installation of Mono ## - installation of Gecko export 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 export FREETYPE_PROPERTIES='truetype:interpreter-version=35' EOF } # Print the actions that should be run when initialising the WINE prefix. # USAGE: wine_wineprefix_init_actions wine_wineprefix_init_actions() { local game_id game_id=$(game_id) cat <<- EOF # Link the game prefix into the WINE prefix. ln --symbolic \\ "\$PATH_PREFIX" \\ "\${WINEPREFIX}/drive_c/${game_id}" EOF cat <<- 'EOF' # 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 symbolic links to the 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 # Include custom actions set from the game script. wineprefix_init_custom } # Set custom actions to run on WINE prefix initialisation. # USAGE: wineprefix_init_custom wineprefix_init_custom() { # This function prints nothing by default, # it should be overridden from game scripts to have any effect. return 0 } # Generate the WINE prefix. # USAGE: wine_launcher_wineprefix_generate wine_launcher_wineprefix_generate() { cat <<- 'EOF' # Generate the WINE prefix. if ! [ -e "$WINEPREFIX" ]; then EOF ## An extra level of indentation is added to all text output by the following group of commands. { cat <<- 'EOF' 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 EOF # Print the actions that should be run when initialising the WINE prefix. wine_wineprefix_init_actions local game_engine game_engine=$(game_engine) case "$game_engine" in ('unity3d') wine_wineprefix_init_actions_unity3d ;; esac # If required, install Mono in the prefix. local wineprefix_tweaks wineprefix_tweaks=$(wine_wineprefix_tweaks) if printf '%s' "$wineprefix_tweaks" | grep --quiet --fixed-strings --line-regexp --regexp='mono'; then wine_wineprefix_tweak_mono_install fi # Run initial winetricks call. local winetricks_verbs winetricks_verbs=$(wine_winetricks_verbs) if [ -n "$winetricks_verbs" ]; then cat <<- EOF # Run the initial winetricks call. winetricks_wrapper ${winetricks_verbs} EOF fi # Load registry scripts. regedit_initial # Set Direct3D renderer. wine_launcher_renderer } | sed 's/^/\t/;s/^\s*$//' 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" "${PATH_PERSISTENT}/wineprefix" "$directory" done <<- EOL $(printf '%s' "$WINE_PERSISTENT_DIRECTORIES") EOL unset directory EOF return 0 fi } src/50_engine_wine/20_winetricks.sh0000644000000000000000000000431613120060140016144 0ustar rootroot# WINE - Print the list of winetricks verbs that should be applied during the WINE prefix initialization. # USAGE: wine_winetricks_verbs # RETURN: A list of winetricks verbs, # the list can be empty. wine_winetricks_verbs() { local winetricks_verbs winetricks_verbs=$(context_value 'WINE_WINETRICKS_VERBS') # Fall back on the 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 ('unrealengine3') winetricks_verbs=$(wine_winetricks_verbs_unrealengine3) ;; ('unrealengine4') winetricks_verbs=$(unrealengine4_wine_winetricks_verbs_default) ;; esac fi # Append the verb used to set a virtual desktop, if required local virtual_desktop virtual_desktop=$(wine_virtual_desktop) case "$virtual_desktop" in ('none') ;; ('auto') winetricks_verbs="$winetricks_verbs vd=\"\$(screen_resolution)\"" ;; (*) winetricks_verbs="$winetricks_verbs vd=$virtual_desktop" ;; esac printf '%s' "$winetricks_verbs" } # WINE - Setup the winetricks wrapper # USAGE: wine_winetricks_wrapper wine_winetricks_wrapper() { cat <<- 'EOF' ## The `wineserver` command is only used by winetricks wineserver_command() { wine_command | sed 's#/wine$#/wineserver#' } ## 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 "$@" else $(terminal_wrapper) -e \ env \ WINE="$WINE" \ WINESERVER="$WINESERVER" \ WINEBOOT="$WINEBOOT" \ WINEARCH="$WINEARCH" \ WINEDEBUG="$WINEDEBUG" \ WINEPREFIX="$WINEPREFIX" \ winetricks "$@" fi ## Wait a bit for lingering WINE processes to terminate sleep 1s } EOF } src/50_engine_wine/25_wineprefix-tweaks.sh0000644000000000000000000000125713120060140017444 0ustar rootroot# WINE - List the tweaks that should be applied to the WINE prefix # USAGE: wine_wineprefix_tweaks # RETURN: the list of tweaks, one per line wine_wineprefix_tweaks() { local tweaks_list tweaks_list=$(context_value 'WINE_WINEPREFIX_TWEAKS') printf '%s' "$tweaks_list" | list_clean } # Print the snippet installing Mono in the WINE prefix. # USAGE: wine_wineprefix_tweak_mono_install wine_wineprefix_tweak_mono_install() { local game_id mono_installer_name game_id=$(game_id) mono_installer_name=$(archive_name 'ARCHIVE_MONO') cat <<- EOF # Install Mono in the WINE prefix. \$(wine_command) "\${WINEPREFIX}/drive_c/${game_id}/wineprefix-tweaks/${mono_installer_name}" EOF } src/50_engine_wine/90_messages.sh0000644000000000000000000000216313120060140015576 0ustar rootroot# Error - The provided value for the WINE Direct3D renderer is invalid # USAGE: error_unknown_wine_renderer $unknown_value error_unknown_wine_renderer() { local unknown_value unknown_value="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La valeur suivante est invalide pour WINE_DIRECT3D_RENDERER : %s\n' ;; ('en'|*) message='The following value is not supported for WINE_DIRECT3D_RENDERER: %s\n' ;; esac print_message 'error' "$message" \ "$unknown_value" } # Error - The provided setting for the WINE virtual desktop is invalid. # USAGE: error_wine_virtual_desktop_invalid $invalid_setting error_wine_virtual_desktop_invalid() { local invalid_setting invalid_setting="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La valeur suivante est invalide pour WINE_VIRTUAL_DESKTOP : "%s"\n' ;; ('en'|*) message='The following value is not supported for WINE_VIRTUAL_DESKTOP: "%s"\n' ;; esac print_message 'error' "$message" \ "$invalid_setting" } src/55_engine-variant_ags/30_content.sh0000644000000000000000000000131113120060140016704 0ustar rootroot# Print the Adventure Game Studio name for the current game # This function should not fail if no AGS name is set for the current game, # so it can be used to automatically detect games using the "ags" type variant. # USAGE: ags_name ags_name() { context_value 'AGS_NAME' } # Adventure Game Studio - Print the list of files to include from the archive for a given identifier # USAGE: ags_content_files_default $content_id ags_content_files_default() { local content_id content_id="$1" local ags_name ags_name=$(ags_name) local content_files case "$content_id" in ('GAME_MAIN') content_files=' acsetup.cfg *.0?? *.ags *.exe *.vox *.tra' esac printf '%s' "${content_files:-}" } src/55_engine-variant_gamemaker/30_applications.sh0000644000000000000000000000140213120060140021100 0ustar rootroot# Set default applications list for GameMaker games. # USAGE: applications_list_gamemaker applications_list_gamemaker() { printf '%s\n' \ 'APP_MAIN' } # Set default binary name for GameMaker games. # USAGE: application_exe_gamemaker application_exe_gamemaker() { printf '%s' 'runner' } # Set pre-run actions common to GameMaker games. # This is not overridden by setting APP_xxx_PRERUN from game scripts, # the value of APP_xxx_PRERUN is appended to this list of actions. # USAGE: application_prerun_gamemaker application_prerun_gamemaker() { cat <<- EOF # Work around a crash on Mesa. # cf. https://gitlab.freedesktop.org/mesa/mesa/issues/1310 export radeonsi_sync_compile=true # Work around broken support for non-US locales. export LC_NUMERIC=C EOF } src/55_engine-variant_gamemaker/30_content.sh0000644000000000000000000000054613120060140020074 0ustar rootroot# Set default list of paths to include for GameMaker games. # USAGE: content_files_gamemaker $content_id content_files_gamemaker() { local content_id content_id="$1" local content_files case "$content_id" in ('GAME_BIN') content_files=' runner' ;; ('GAME_DATA') content_files=' assets' ;; esac printf '%s' "${content_files:-}" } src/55_engine-variant_gamemaker/30_icons.sh0000644000000000000000000000020413120060140017524 0ustar rootroot# Set default icon path for GameMaker games. # USAGE: icon_path_gamemaker icon_path_gamemaker() { printf '%s' 'assets/icon.png' } src/55_engine-variant_gamemaker/30_packages.sh0000644000000000000000000000330513120060140020174 0ustar rootroot# Set default packages list for GameMaker games. # USAGE: packages_list_gamemaker packages_list_gamemaker() { printf '%s\n' \ 'PKG_BIN' \ 'PKG_DATA' } # Set default package id for GameMaker games. # USAGE: package_id_gamemaker $package package_id_gamemaker() { 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:-}" } # Set default package description for GameMaker games. # USAGE: package_description_gamemaker $package package_description_gamemaker() { local package package="$1" local package_description case "$package" in ('PKG_DATA') package_description='data' ;; esac printf '%s' "${package_description:-}" } # Set default list of dependencies on sibling packages for GameMaker games. # USAGE: dependencies_siblings_list_gamemaker $package dependencies_siblings_list_gamemaker() { local package package="$1" local package_dependencies case "$package" in ('PKG_BIN') package_dependencies=' PKG_DATA' ;; esac printf '%s' "${package_dependencies:-}" } # Set default list of dependencies on native libraries for GameMaker games. # USAGE: dependencies_libraries_list_gamemaker $package dependencies_libraries_list_gamemaker() { 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 libGLU.so.1 libm.so.6 libopenal.so.1 libpthread.so.0 librt.so.1 libssl.so.1.0.0 libstdc++.so.6 libX11.so.6 libXext.so.6 libXrandr.so.2 libXxf86vm.so.1 libz.so.1' ;; esac printf '%s' "${package_dependencies:-}" } src/55_engine-variant_unity3d/00_common.sh0000644000000000000000000000443013120060140017351 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_default engine_info_file_path content_path_default=$(content_path_default) engine_info_file_path="${PLAYIT_WORKDIR}/gamedata/${content_path_default}/${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") path_game_data=$( set_current_package "$package" 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 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/20_wineprefix.sh0000644000000000000000000000063213120060140020243 0ustar rootroot# Print the WINE prefix initialisation actions specific to Unity3D games. # USAGE: wine_wineprefix_init_actions_unity3d wine_wineprefix_init_actions_unity3d() { cat <<- 'EOF' # Work around a loss of input on loss of focus with Unity3D games. # cf. https://bugs.winehq.org/show_bug.cgi?id=48121 $(wine_command) reg add 'HKEY_CURRENT_USER\Software\Wine\X11 Driver' /t REG_SZ /v UseTakeFocus /d N /f EOF } src/55_engine-variant_unity3d/30_applications.sh0000644000000000000000000000347013120060140020555 0ustar rootroot# Compute the file name of the game binary from the UNITY3D_NAME value # USAGE: unity3d_application_exe_default application # RETURN: the application file name, # or an empty string unity3d_application_exe_default() { local application application="$1" # We can not rely on the application type here, # to avoid a loop between application_exe and application_type. local unity3d_name unity3d_name=$(unity3d_name) local package package_architecture package=$(current_package) package_architecture=$(package_architecture "$package") local filename_candidates_list filename_candidate filename_path filename_found case "$package_architecture" in ('32') filename_candidates_list=" ${unity3d_name}.x86 ${unity3d_name}.exe ${unity3d_name}" ;; ('64') filename_candidates_list=" ${unity3d_name}.x86_64 ${unity3d_name}.exe ${unity3d_name}" ;; (*) filename_candidates_list=" ${unity3d_name}.x86 ${unity3d_name}.x86_64 ${unity3d_name}.exe ${unity3d_name}" ;; esac ## Use a while loop to avoid breaking on spaces in file name. while read -r filename_candidate; do ## Skip empty lines. if [ -z "$filename_candidate" ]; then continue fi ## Do not throw an error if no path is found for the current candidate. ## The output redirection must be done inside the subshell, or bash --posix will ignore it. filename_path=$(application_exe_path "$filename_candidate" 2>/dev/null || true) 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.sh0000644000000000000000000001303513120060140017537 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 ## TODO: This function should be expanded to handle plugins of Windows builds. content_inclusion_unity3d_plugins() { local package package="$1" # Set the plugins source path # Warning: content_path should not be used as a variable name here, # to prevent it from leaking into the content_inclusion call below local unity3d_name content_path_default plugins_directory plugins_path unity3d_name=$(unity3d_name) content_path_default=$(content_path_default) plugins_directory="${content_path_default}/${unity3d_name}_Data/Plugins" plugins_path="${PLAYIT_WORKDIR}/gamedata/${plugins_directory}" local CONTENT_UNITY3D_PLUGINS_FILES target_directory ## Silence ShellCheck false-positive ## CONTENT_UNITY3D_PLUGINS_FILES appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_UNITY3D_PLUGINS_FILES=$(unity3d_plugins) target_directory=$( set_current_package "$package" path_libraries ) # Proceed with the actual files inclusion. local package_architecture architecture_string package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') architecture_string='x86' ;; ('64') architecture_string='x86_64' ;; ('all') # Return early if the current package should not include binaries. return 0 ;; esac if [ -d "${plugins_path}/${architecture_string}" ]; then local CONTENT_UNITY3D_PLUGINS_PATH ## Silence ShellCheck false-positive ## CONTENT_UNITY3D_PLUGINS_PATH appears unused. Verify use (or export if used externally). # shellcheck disable=SC2034 CONTENT_UNITY3D_PLUGINS_PATH="${plugins_directory}/${architecture_string}" content_inclusion 'UNITY3D_PLUGINS' "$package" "$target_directory" # Delete the remaining plugins for the current architecture, # to prevent them from being included later. find "${plugins_path}/${architecture_string}" -mindepth 1 -type f -delete find "${plugins_path}/${architecture_string}" -mindepth 1 -type d -empty -delete 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" find "$plugins_path" -mindepth 1 -type f -delete find "$plugins_path" -mindepth 1 -type d -empty -delete 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 application_type application_type=$(application_default_type) local content_files case "$content_id" in ('GAME_BIN') case "$application_type" in ('native') ## TODO: An explicit list of .so files should be set, to prevent the inclusion of unwanted libraries. content_files=" MonoBleedingEdge ${unity3d_name}_Data/Mono ${unity3d_name}_Data/MonoBleedingEdge ${unity3d_name}.x86_64 ${unity3d_name}.x86 ${unity3d_name} *.so" ;; ('wine') content_files=" Mono MonoBleedingEdge ${unity3d_name}_Data/Mono ${unity3d_name}_Data/MonoBleedingEdge ${unity3d_name}_Data/Plugins ${unity3d_name}.exe baselib.dll GameAssembly.dll UnityPlayer.dll" ## Game scripts targeting ./play.it < 2.28 might rely on more .dll files being included. if ! compatibility_level_is_at_least '2.28'; then content_files="$content_files *.dll" fi ;; esac ;; ('GAME_BIN32') case "$application_type" in ('native') content_files=" MonoBleedingEdge/x86 ${unity3d_name}_Data/Mono/x86 ${unity3d_name}_Data/MonoBleedingEdge/x86 ${unity3d_name}.x86 ${unity3d_name}" ;; esac ;; ('GAME_BIN64') case "$application_type" in ('native') content_files=" MonoBleedingEdge/x86_64 ${unity3d_name}_Data/Mono/x86_64 ${unity3d_name}_Data/MonoBleedingEdge/x86_64 ${unity3d_name}.x86_64 ${unity3d_name}" ;; esac ;; ('GAME_DATA') case "$application_type" in ('native') content_files=" ${unity3d_name}_Data" ;; ('wine') content_files=" ${unity3d_name}_Data" ;; 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.sh0000644000000000000000000000551613120060140020056 0ustar rootroot# Print the snippet starting pulseaudio if it is available # USAGE: launcher_unity3d_pulseaudio_start launcher_unity3d_pulseaudio_start() { cat <<- 'EOF' # Start pulseaudio if it is available pulseaudio_is_available() { command -v pulseaudio >/dev/null 2>&1 } if pulseaudio_is_available; then if ! pulseaudio --check; then touch .stop_pulseaudio_on_exit fi pulseaudio --start fi EOF } # Print the snippet stopping pulseaudio if it has been started for this game session # USAGE: launcher_unity3d_pulseaudio_stop launcher_unity3d_pulseaudio_stop() { cat <<- 'EOF' # Stop pulseaudio if it has been started for this game session if [ -e .stop_pulseaudio_on_exit ]; then pulseaudio --kill rm .stop_pulseaudio_on_exit fi EOF } # Print the snippet hiding libpulse-simple.so.0 if pulseaudio is not available # USAGE: launcher_unity3d_pulseaudio_hide_libpulse launcher_unity3d_pulseaudio_hide_libpulse() { cat <<- 'EOF' # Work around crash on launch related to libpulse # Some Unity3D games crash on launch if libpulse-simple.so.0 is available but pulseaudio is not running libpulse_null_link="${PATH_LIBRARIES_USER}/libpulse-simple.so.0" if pulseaudio_is_available; then rm --force "$libpulse_null_link" else mkdir --parents "$PATH_LIBRARIES_USER" ln --force --symbolic /dev/null "$libpulse_null_link" fi unset libpulse_null_link EOF } # Print the snippet setting forcing the use of a US-like locale # USAGE: launcher_unity3d_force_locale # RETURN: the code snippet, a multi-lines string launcher_unity3d_force_locale() { cat <<- 'EOF' # Work around Unity3D poor support for non-US locales export LANG=C EOF } # 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 unity3d_disable_map32bit() { 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" = '32' ]; then # This Unity3D build is not affected by the MAP_32BIT flag. return 0 fi ;; (*) # This Unity3D build is not affected by the MAP_32BIT flag. return 0 ;; esac cat <<- 'EOF' #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); } EOF } src/55_engine-variant_unity3d/90_messages.sh0000644000000000000000000000126013120060140017677 0ustar rootroot# Error - Unity3D - No binary has been found # USAGE: error_unity3d_binary_not_found error_unity3d_binary_not_found() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Aucun exécutable nʼa été trouvé pour ce jeu Unity3D.\n' message="$message"'Cette erreur est probablement due à une valeur incorrecte assignée à CONTENT_PATH_DEFAULT ou UNITY3D_NAME.\n' ;; ('en'|*) message='No binary has been found for the current Unity3D game.\n' message="$message"'This is probably due to an incorrect value set for CONTENT_PATH_DEFAULT or UNITY3D_NAME.\n' ;; esac print_message 'error' "$message" } src/55_engine-variant_unrealengine3/50_wine.sh0000644000000000000000000000105513120060140020170 0ustar rootroot# Print the list of winetricks verbs installed by default for Unreal Engine 3 games. # USAGE: wine_winetricks_verbs_unrealengine3 wine_winetricks_verbs_unrealengine3() { # Cursor is prevented from leaving the game window, avoiding problems with mouse look. # (WINE 10.0) printf '%s' 'grabfullscreen=y' } # Set the default virtual desktop setting for Unreal Engine 3 games. # USAGE: wine_virtual_desktop_unrealengine3 wine_virtual_desktop_unrealengine3() { # Without a virtual desktop, keyboard inputs are ignored. # (WINE 10.0) printf '%s' 'auto' } src/55_engine-variant_unrealengine4/30_content.sh0000644000000000000000000000323013120060140020674 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 application_type application_type=$(application_default_type) 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.sh0000644000000000000000000000136513120060140021007 0ustar rootroot# Print GStreamer decoders required by Unreal Engine 4 games. # USAGE: dependencies_gstreamer_list_unrealengine4 $package dependencies_gstreamer_list_unrealengine4() { local package package="$1" # Return early if current package should not depend on GStreamer decoders. local package_architecture package_architecture=$(package_architecture "$package") if [ "$package_architecture" = 'all' ]; then return 0 fi # Unreal Engine 4 games require specific GStreamer decoders when running through WINE. local application_type gstreamer_decoders application_type=$(application_default_type) case "$application_type" in ('wine') gstreamer_decoders=' video/quicktime, variant=(string)iso' ;; esac printf '%s' "${gstreamer_decoders:-}" } src/55_engine-variant_unrealengine4/50_wine.sh0000644000000000000000000000227213120060140020173 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() { ## TODO: This should no longer be applied for games targeting the current compatibility level. ## With WINE ≥ 10.0 (maybe earlier too?) Unreal Engine 4 games work without requiring Visual C++ Runtime, ## having it installed actually prevent many (all?) of them from starting. printf '%s' 'vcrun2019' } src/55_engine-variant_visionaire/30_applications.sh0000644000000000000000000000243513120060140021326 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 ## Do not throw an error if no path is found for the current candidate. ## The output redirection must be done inside the subshell, or bash --posix will ignore it. filename_path=$(application_exe_path "$filename_candidate" 2>/dev/null || true) 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.sh0000644000000000000000000000617113120060140020313 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 application_type application_type=$(application_default_type) 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 application_type application_type=$(application_default_type) 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.sh0000644000000000000000000000045413120060140020623 0ustar rootroot# Visionaire - Prevent the use of wayland SDL video driver, for use with WINE games # USAGE: visionaire_tweak_sdl_wine visionaire_tweak_sdl_wine() { cat <<- 'EOF' # Prevent the use of wayland SDL video driver if [ "${SDL_VIDEODRIVER:-}" = 'wayland' ]; then unset SDL_VIDEODRIVER fi EOF } src/55_engine-variant_visionaire/30_packages.sh0000644000000000000000000000365113120060140020417 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:-}" } # Print identifiers of sibling packages required by Visionaire games. # USAGE: dependencies_siblings_list_visionaire $package dependencies_siblings_list_visionaire() { local package package="$1" local package_dependencies case "$package" in ('PKG_BIN') package_dependencies=' PKG_DATA' ;; esac printf '%s' "${package_dependencies:-}" } # Print native libraries required by Visionaire games. # USAGE: dependencies_libraries_list_visionaire $package dependencies_libraries_list_visionaire() { 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 libSDL2-2.0.so.0 libstdc++.so.6' ;; esac printf '%s' "${package_dependencies:-}" } 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.sh0000644000000000000000000001645313120060140016657 0ustar rootroot# Arch Linux - Write the metadata for the listed packages # USAGE: archlinux_packages_metadata $package[…] archlinux_packages_metadata() { local package for package in "$@"; do archlinux_package_metadata_single "$package" done } # Arch Linux - Write the metadata for the given package # USAGE: archlinux_package_metadata_single $package archlinux_package_metadata_single() { local package package="$1" local package_path target package_path=$(package_path "$package") target="${package_path}/.PKGINFO" mkdir --parents "$(dirname "$target")" local \ archlinux_field_pkgname \ archlinux_field_pkgver \ archlinux_field_packager \ archlinux_field_builddate \ archlinux_field_size \ archlinux_field_arch \ archlinux_field_pkgdesc \ archlinux_field_depend \ archlinux_field_provides archlinux_field_pkgname=$(archlinux_field_pkgname "$package") archlinux_field_pkgver=$(archlinux_field_pkgver "$package") archlinux_field_packager=$(archlinux_field_packager "$package") archlinux_field_builddate=$(archlinux_field_builddate "$package") archlinux_field_size=$(archlinux_field_size "$package") archlinux_field_arch=$(archlinux_field_arch "$package") archlinux_field_pkgdesc=$(archlinux_field_pkgdesc "$package") archlinux_field_depend=$(archlinux_field_depend "$package") archlinux_field_provides=$(archlinux_field_provides "$package") cat > "$target" <<- EOF # Generated by ./play.it $LIBRARY_VERSION pkgname = $archlinux_field_pkgname pkgver = $archlinux_field_pkgver packager = $archlinux_field_packager builddate = $archlinux_field_builddate size = $archlinux_field_size arch = $archlinux_field_arch pkgdesc = $archlinux_field_pkgdesc EOF if [ -n "$archlinux_field_depend" ]; then local field_depend_single while read -r field_depend_single; do cat >> "$target" <<- EOF depend = $field_depend_single EOF done <<- EOL $(printf '%s' "$archlinux_field_depend") EOL fi if [ -n "$archlinux_field_provides" ]; then local field_provides_single while read -r field_provides_single; do cat >> "$target" <<- EOF conflict = $field_provides_single provides = $field_provides_single EOF done <<- EOL $(printf '%s' "$archlinux_field_provides") EOL fi # Write the .INSTALL metadata file local install_contents install_contents=$(archlinux_script_install "$package") if [ -n "$install_contents" ]; then printf '%s' "$install_contents" > "${package_path}/.INSTALL" fi # Creates .MTREE local option_mtree option_mtree=$(option_value 'mtree') if [ "$option_mtree" -eq 1 ]; then package_archlinux_create_mtree "$package" fi } # Arch Linux - Build a list of packages # USAGE: archlinux_packages_build $package[…] archlinux_packages_build() { local package for package in "$@"; do archlinux_package_build_single "$package" done } # Arch Linux - Build a single package # USAGE: archlinux_package_build_single $package archlinux_package_build_single() { local package package="$1" local package_path package_path=$(package_path "$package") local option_output_dir package_name generated_package_path option_output_dir=$(option_value 'output-dir') package_name=$(package_name "$package") ## The path to the generated package must be an absolute path, ## because we do not run the tar call from the current directory. generated_package_path=$(realpath "${option_output_dir}/${package_name}") # Skip packages already existing, # unless called with --overwrite. local option_overwrite option_overwrite=$(option_value 'overwrite') if [ "$option_overwrite" -eq 0 ] && [ -e "$generated_package_path" ] then information_package_already_exists "$package_name" return 0 fi # Set basic tar options. local tar_version tar_options tar_version=$(tar --version | head --lines 1) case "$tar_version" in (*'GNU tar'*) tar_options='--create --group=root --owner=root' ;; (*'libarchive'*) tar_options='--create --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_contents package_contents='.PKGINFO *' if [ -e "${package_path}/.INSTALL" ]; then package_contents=".INSTALL $package_contents" fi if [ -e "${package_path}/.MTREE" ]; then package_contents=".MTREE $package_contents" fi information_package_building "$package_name" ## TODO: env --chdir could be used instead of cd in a subshell. if ! ( cd "$package_path" if [ -n "$tar_compress_program" ]; then tar $tar_options --use-compress-program="$tar_compress_program" --file "$generated_package_path" $package_contents else tar $tar_options --file "$generated_package_path" $package_contents fi ) 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" ## TODO: env --chdir could be used instead of cd in a subshell. ( cd "$package_path" # shellcheck disable=SC2094 env --ignore-environment find . -print0 | env --ignore-environment bsdtar \ --create \ --file - \ --files-from - \ --format=mtree \ --no-recursion \ --null \ --options='!all,use-set,type,uid,gid,mode,time,size,md5,sha256,link' \ --exclude .MTREE \ | env --ignore-environment gzip \ --force \ --no-name \ --to-stdout \ > .MTREE ) } # Print the file name of the given package # USAGE: package_name_archlinux $package # RETURNS: the file name, as a string package_name_archlinux() { local package package="$1" local package_id package_version package_architecture package_name package_id=$(package_id "$package") package_version=$(package_version) package_architecture=$(archlinux_field_arch "$package") package_name="${package_id}_${package_version}_${package_architecture}.pkg.tar" local option_compression option_compression=$(option_value 'compression') case $option_compression in ('speed') package_name="${package_name}.zst" ;; ('size') package_name="${package_name}.zst" ;; esac printf '%s' "$package_name" } # Get the path to the directory where the given package is prepared, # relative to the directory where all packages are stored # USAGE: package_path_archlinux $package # RETURNS: relative path to a directory, as a string package_path_archlinux() { local package package="$1" local package_name package_path package_name=$(package_name "$package") package_path="${package_name%.pkg.tar*}" printf '%s' "$package_path" } # Tweak the given package id to follow Arch Linux standards # USAGE: archlinux_package_id $package_id # RETURNS: the package id, as a non-empty string archlinux_package_id() { local package_id package_id="$1" # Prepend "lib32-" to the ID of 32-bit packages. local package_architecture package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') package_id="lib32-${package_id}" ;; esac printf '%s' "$package_id" } src/60_system_archlinux/15_metadata-fields.sh0000644000000000000000000000717613120060140020134 0ustar rootroot# Arch Linux - Print the value of the "pkgname" field # USAGE: archlinux_field_pkgname $package # RETURN: the field value archlinux_field_pkgname() { local package package="$1" package_id "$package" } # Arch Linux - Print the value of the "pkgver" field # USAGE: archlinux_field_pkgver $package # RETURN: the field value archlinux_field_pkgver() { ## The package value is not actually used by this function, ## but it is still passed for consistency with other archlinux_field_* functions. local package package="$1" package_version } # Arch Linux - Print the value of the "packager" field # USAGE: archlinux_field_packager $package # RETURN: the field value archlinux_field_packager() { ## The package value is not actually used by this function, ## but it is still passed for consistency with other archlinux_field_* functions. local package package="$1" package_maintainer } # Arch Linux - Print the value of the "builddate" field # USAGE: archlinux_field_builddate $package # RETURN: the field value archlinux_field_builddate() { ## The package value is not actually used by this function, ## but it is still passed for consistency with other archlinux_field_* functions. local package package="$1" date '+%s' } # Arch Linux - Print the value of the "size" field # USAGE: archlinux_field_size $package # RETURN: the field value archlinux_field_size() { local package package="$1" local package_path package_path=$(package_path "$package") du --total --block-size=1 --summarize "$package_path" | tail --lines=1 | cut --fields=1 } # Arch Linux - Print the value of the "arch" field # USAGE: archlinux_field_arch $package # RETURN: the field value archlinux_field_arch() { local package package="$1" local package_architecture package_architecture_string package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32'|'64') package_architecture_string='x86_64' ;; ('all') package_architecture_string='any' ;; esac printf '%s' "$package_architecture_string" } # Arch Linux - Print the value of the "pkgdesc" field # USAGE: archlinux_field_pkgdesc $package # RETURN: the field value archlinux_field_pkgdesc() { local package package="$1" local game_name package_description script_version_string game_name=$(game_name) package_description=$(package_description "$package") script_version_string=$(script_version) printf '%s' "$game_name" if [ -n "$package_description" ]; then printf -- ' - %s' "$package_description" fi printf -- ' - ./play.it script version %s' "$script_version_string" } # Arch Linux - Print the value of the "depend" fields # Each package name is displayed on its own line. # USAGE: archlinux_field_depend $package # RETURN: the field value archlinux_field_depend() { local package package="$1" dependencies_full_list_packages "$package" } # Arch Linux - Print the value of the "provides" fields # Each package name is displayed on its own line. # These values are used for the "conflict" fields too. # USAGE: archlinux_field_provides $package # RETURN: the field value archlinux_field_provides() { local package package="$1" local package_provides package_provides=$(package_provides "$package") local package_architecture package_architecture=$(package_architecture "$package") if [ "$package_architecture" = '32' ]; then local package_id package_name_32 package_id=$(package_id "$package") package_name_32=$(printf '%s' "$package_id" | sed 's/^lib32-//') package_provides="$package_provides $package_name_32" fi # Return early if there is no package name provided if [ -z "$package_provides" ]; then return 0 fi printf '%s\n' $package_provides } src/60_system_archlinux/15_package-scripts.sh0000644000000000000000000000241113120060140020153 0ustar rootroot# Arch Linux - Print the contents of the .INSTALL script # USAGE: archlinux_script_install $package # RETURN: the contents of the .INSTALL file, # spanning over several lines archlinux_script_install() { local package package="$1" # Print the definitions of post_install and post_upgrade. local postinst_actions postinst_warnings postinst_actions=$(package_postinst_actions "$package") postinst_warnings=$(package_postinst_warnings "$package") if [ -n "$postinst_actions" ] || [ -n "$postinst_warnings" ]; then cat <<- EOF post_install() { EOF ## Include actions that should be run. if [ -n "$postinst_actions" ]; then printf '%s\n' "$postinst_actions" fi ## Include warnings that should be displayed. if [ -n "$postinst_warnings" ]; then local warning_line while read -r warning_line; do printf 'printf "Warning: %%s\\n" "%s"\n' "$warning_line" done <<- EOL $(printf '%s' "$postinst_warnings") EOL fi cat <<- EOF } post_upgrade() { post_install } EOF fi # Print the definitions of pre_remove and pre_upgrade. local prerm_actions prerm_actions=$(package_prerm_actions "$package") if [ -n "$prerm_actions" ]; then cat <<- EOF pre_remove() { $prerm_actions } pre_upgrade() { pre_remove } EOF fi } src/60_system_archlinux/25_dependencies_commands.sh0000644000000000000000000000414713120060140021413 0ustar rootroot# Print Arch Linux packages providing given commands list. # USAGE: dependencies_commands_list_archlinux $dependencies_commands dependencies_commands_list_archlinux() { local dependencies_commands dependencies_commands="$1" local dependency_command while read -r dependency_command; do dependencies_command_single_archlinux "$dependency_command" done <<- EOL $(printf '%s' "$dependencies_commands") EOL } # Print Arch Linux packages providing given single command. # USAGE: dependencies_command_single_archlinux $dependency_command dependencies_command_single_archlinux() { local dependency_command dependency_command="$1" local package_names case "$dependency_command" in ('7za') package_names=' extra/p7zip' ;; ('corsix-th') package_names=' corsix-th' ;; ('dos2unix') package_names=' dos2unix' ;; ('dosbox') package_names=' dosbox' ;; ('godot3-runner') # The Godot 3 runtime is not provided by Arch Linux, not even in the AUR return 0 ;; ('firefox') package_names=' firefox' ;; ('java') package_names=' jre8-openjdk' ;; ('julius') package_names=' julius' ;; ('mono') package_names=' mono' ;; ('mpv') package_names=' mpv' ;; ('openmw-iniimporter') package_names=' openmw' ;; ('openmw-launcher') package_names=' openmw' ;; ('pidwait') package_names=' procps-ng' ;; ('pulseaudio') package_names=' pulseaudio' ;; ('python3') package_names=' python' ;; ('renpy') package_names=' renpy' ;; ('scummvm') package_names=' scummvm' ;; ('sed') package_names=' sed' ;; ('setxkbmap') package_names=' xorg-setxkbmap' ;; ('terminal_wrapper') package_names=' xterm' ;; ('vcmilauncher') package_names=' vcmi' ;; ('wine') package_names=' wine' ;; ('winetricks') package_names=' winetricks' ;; ('xgamma') package_names=' xorg-xgamma' ;; ('xrandr') package_names=' xorg-xrandr' ;; (*) dependencies_unknown_command_add "$dependency_command" return 0 ;; esac printf '%s\n' "$package_names" } src/60_system_archlinux/25_dependencies_gstreamer.sh0000644000000000000000000000720613120060140021602 0ustar rootroot# Print Arch Linux packages providing given GStreamer decoders list. # USAGE: dependencies_gstreamer_list_archlinux $dependencies_gstreamer dependencies_gstreamer_list_archlinux() { local dependencies_gstreamer dependencies_gstreamer="$1" local package package_architecture dependency_gstreamer package=$(current_package) package_architecture=$(package_architecture "$package") while read -r dependency_gstreamer; do case "$package_architecture" in ('32') dependencies_gstreamer_single_archlinux_32bit "$dependency_gstreamer" ;; (*) dependencies_gstreamer_single_archlinux "$dependency_gstreamer" ;; esac done <<- EOL $(printf '%s' "$dependencies_gstreamer") EOL } # Print Arch Linux packages providing given single GStreamer decoder. # USAGE: dependencies_gstreamer_single_archlinux $dependency_gstreamer dependencies_gstreamer_single_archlinux() { local dependency_gstreamer dependency_gstreamer="$1" local package_names case "$dependency_gstreamer" in ('audioconvert') package_names=' gst-plugins-base' ;; ('avidemux') package_names=' gst-plugins-good' ;; ('decodebin') package_names=' gst-plugins-base' ;; ('deinterlace') package_names=' gst-plugins-good' ;; ('application/x-id3') package_names=' gst-plugins-good' ;; ('audio/mpeg, mpegversion=(int)1, layer=(int)3') package_names=' gst-plugins-good' ;; ( \ 'audio/x-wma' | \ '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' | \ 'video/x-wmv, wmvversion=(int)1' \ ) package_names=' gst-libav' ;; (*) dependencies_unknown_gstreamer_media_formats_add "$dependency_gstreamer" return 0 ;; esac printf '%s\n' "$package_names" } # Print 32-bit Arch Linux packages providing given single GStreamer decoder. # USAGE: dependencies_gstreamer_single_archlinux_32bit $dependency_gstreamer dependencies_gstreamer_single_archlinux_32bit() { local dependency_gstreamer dependency_gstreamer="$1" local package_names case "$dependency_gstreamer" in ('audioconvert') package_names=' lib32-gst-plugins-base' ;; ('avidemux') package_names=' lib32-gst-plugins-good' ;; ('decodebin') package_names=' lib32-gst-plugins-base' ;; ('deinterlace') package_names=' lib32-gst-plugins-good' ;; ('application/x-id3') package_names=' lib32-gst-plugins-good' ;; ('audio/mpeg, mpegversion=(int)1, layer=(int)3') package_names=' lib32-gst-plugins-good' ;; ( \ 'audio/x-wma' | \ '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' | \ 'video/x-wmv, wmvversion=(int)1' \ ) package_names=' lib32-gst-libav' ;; (*) dependencies_unknown_gstreamer_media_formats_add "$dependency_gstreamer" return 0 ;; esac printf '%s\n' "$package_names" } src/60_system_archlinux/25_dependencies_legacy.sh0000644000000000000000000000055213120060140021052 0ustar rootroot# Print Arch Linux packages providing given packages list, set using legacy dependencies system. # USAGE: dependencies_legacy_list_archlinux $dependencies_legacy dependencies_legacy_list_archlinux() { local dependencies_legacy dependencies_legacy="$1" ## FIXME: A "lib32-" prefix might be required for 32-bit packages. printf '%s\n' $dependencies_legacy } src/60_system_archlinux/25_dependencies_libraries.sh0000644000000000000000000005570513120060140021574 0ustar rootroot# Print Arch Linux packages providing given native libraries list. # USAGE: dependencies_libraries_list_archlinux $dependencies_libraries dependencies_libraries_list_archlinux() { local dependencies_libraries dependencies_libraries="$1" local package package_architecture dependency_library package=$(current_package) package_architecture=$(package_architecture "$package") while read -r dependency_library; do case "$package_architecture" in ('32') dependencies_library_single_archlinux_32bit "$dependency_library" ;; (*) dependencies_library_single_archlinux "$dependency_library" ;; esac done <<- EOL $(printf '%s' "$dependencies_libraries") EOL } # Print Arch Linux packages providing given single native library. # USAGE: dependencies_library_single_archlinux $dependency_library dependencies_library_single_archlinux() { local dependency_library dependency_library="$1" local package_name case "$dependency_library" in ('alleg-alsadigi.so') package_name='allegro4' ;; ('alleg-alsamidi.so') package_name='allegro4' ;; ('ld-linux.so.2') package_name='glibc' ;; ('ld-linux-x86-64.so.2') package_name='glibc' ;; ('libaldmb.so.1') # This library is not provided for Arch Linux unset package_name ;; ('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' ;; ('libatk-bridge-2.0.so.0') package_name='at-spi2-core' ;; ('libatspi.so.0') package_name='at-spi2-core' ;; ('libatk-1.0.so.0') package_name='atk' ;; ('libaudio.so.2') package_name='nas' ;; ('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' ;; ('libcrypto.so.3') package_name='openssl' ;; ('libcups.so.2') package_name='libcups' ;; ('libcurl.so.4') package_name='curl' ;; ('libcurl.so.4+CURL_OPENSSL_3') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/curl/ return 0 ;; ('libcurl-gnutls.so.4') package_name='libcurl-gnutls' ;; ('libdbus-1.so.3') package_name='dbus' ;; ('libdbus-glib-1.so.2') package_name='dbus-glib' ;; ('libdl.so.2') package_name='glibc' ;; ('libdrm.so.2') package_name='libdrm' ;; ('libdumb.so.1') # This library is not provided for Arch Linux unset package_name ;; ('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' ;; ('libgbm.so.1') package_name='mesa' ;; ('libgcc_s.so.1') package_name='gcc-libs' ;; ('libgconf-2.so.4') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/gconf/ return 0 ;; ('libgcrypt.so.11') package_name='libgcrypt15' ;; ('libgdiplus.so') package_name='libgdiplus' ;; ('libgdk-3.so.0') package_name='gtk3' ;; ('libgdk_pixbuf-2.0.so.0') package_name='gdk-pixbuf2' ;; ('libgdk-x11-2.0.so.0') package_name='gtk2' ;; ('libgio-2.0.so.0') package_name='glib2' ;; ('libGL.so.1') package_name='libgl' ;; ('libGLEW.so.2.2') package_name='glew' ;; ('libglfw.so.3') package_name='glfw' ;; ('libglib-2.0.so.0') package_name='glib2' ;; ('libGLU.so.1') package_name='glu' ;; ('libGLX.so.0') package_name='libglvnd' ;; ('libgmodule-2.0.so.0') package_name='glib2' ;; ('libgobject-2.0.so.0') package_name='glib2' ;; ('libgomp.so.1') package_name='gcc-libs' ;; ('libgpg-error.so.0') package_name='libgpg-error' ;; ('libgssapi_krb5.so.2') package_name='krb5' ;; ('libgthread-2.0.so.0') package_name='glib2' ;; ('libgtk-x11-2.0.so.0') package_name='gtk2' ;; ('libgtk-3.so.0') package_name='gtk3' ;; ('libICE.so.6') package_name='libice' ;; ('libidn.so.11') package_name='libidn11' ;; ('libidn2.so.0') package_name='libidn2' ;; ('libIL.so.1') package_name='devil' ;; ('libjpeg.so.62') package_name='libjpeg6-turbo' ;; ('libk5crypto.so.3') package_name='krb5' ;; ('libkrb5.so.3') package_name='krb5' ;; ('liblcms2.so.2') package_name='lcms2' ;; ('liblua5.3.so.0') package_name='lua' ;; ('libluajit-5.1.so.2') package_name='luajit' ;; ('liblz4.so.1') package_name='lz4' ;; ('libm.so.6') package_name='glibc' ;; ('libmbedtls.so.12') package_name='mbedtls' ;; ('libminizip.so.1') package_name='minizip' ;; ('libmodplug.so.1') package_name='libmodplug' ;; ('libmpg123.so.0') package_name='mpg123' ;; ('libnghttp2.so.14') package_name='libnghttp2' ;; ('libnotify.so.4') package_name='libnotify' ;; ('libnspr4.so') package_name='nspr' ;; ('libnss3.so') package_name='nss' ;; ('libnssutil3.so') package_name='nss' ;; ('libogg.so.0') package_name='libogg' ;; ('libopenal.so.1') package_name='openal' ;; ('libOpenGL.so.0') package_name='libglvnd' ;; ('libopenmpt.so.0') package_name='libopenmpt' ;; ('libpango-1.0.so.0') package_name='pango' ;; ('libpangocairo-1.0.so.0') package_name='pango' ;; ('libpangoft2-1.0.so.0') package_name='pango' ;; ('libpcre.so.3') # This library is not provided for Arch Linux unset package_name ;; ('libphysfs.so.1') package_name='physfs' ;; ('libpixman-1.so.0') package_name='pixman' ;; ('libplc4.so') package_name='nspr' ;; ('libplds4.so') package_name='nspr' ;; ('libpng12.so.0') package_name='libpng12' ;; ('libpng16.so.16') package_name='libpng' ;; ('libpsl.so.5') package_name='libpsl' ;; ('libpthread.so.0') package_name='glibc' ;; ('libpulse.so.0') package_name='libpulse' ;; ('libpulse-simple.so.0') package_name='libpulse' ;; ( \ 'libQt5Core.so.5' | \ 'libQt5Gui.so.5' | \ 'libQt5Widgets.so.5' \ ) package_name='qt5-base' ;; ('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_net-2.0.so.0') package_name='sdl2_net' ;; ('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.3') package_name='openssl' ;; ('libssl.so.1.1') package_name='openssl-1.1' ;; ('libssl3.so') package_name='nss' ;; ('libstdc++.so.5') package_name='libstdc++5' ;; ('libstdc++.so.6') package_name='gcc-libs' ;; ('libtcmalloc_minimal.so.4') package_name='gperftools' ;; ('libtheora.so.0') package_name='libtheora' ;; ('libtheoradec.so.1') package_name='libtheora' ;; ('libtheoraenc.so.1') package_name='libtheora' ;; ('libthread_db.so.1') package_name='glibc' ;; ('libtiff.so.6') package_name='libtiff' ;; ('libturbojpeg.so.0') package_name='libjpeg-turbo' ;; ('libudev.so.0') package_name='libudev0-shim' ;; ('libudev.so.1') package_name='libudev.so=1-64' ;; ('libutil.so.1') package_name='glibc' ;; ('libuuid.so.1') package_name='util-linux-libs' ;; ('libuv.so.1') package_name='libuv' ;; ('libvorbis.so.0') package_name='libvorbis' ;; ('libvorbisenc.so.2') package_name='libvorbis' ;; ('libvorbisfile.so.3') package_name='libvorbis' ;; ('libvulkan.so.1') package_name=' vulkan-icd-loader vulkan-driver' ;; ('libwayland-client.so.0') package_name='wayland' ;; ('libX11.so.6') package_name='libx11' ;; ('libX11-xcb.so.1') package_name='libx11' ;; ('libxcb.so.1') package_name='libxcb' ;; ('libxcb-randr.so.0') package_name='libxcb' ;; ('libXcomposite.so.1') package_name='libxcomposite' ;; ('libXcursor.so.1') package_name='libxcursor' ;; ('libXdamage.so.1') package_name='libxdamage' ;; ('libXext.so.6') package_name='libxext' ;; ('libXfixes.so.3') package_name='libxfixes' ;; ('libXft.so.2') package_name='libxft' ;; ('libXi.so.6') package_name='libxi' ;; ('libXinerama.so.1') package_name='libxinerama' ;; ('libxkbcommon.so.0') package_name='libxkbcommon' ;; ('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\n' "$package_name" return 0 fi dependencies_unknown_libraries_add "$dependency_library" } # Print 32-bit Arch Linux packages providing given single native library. # USAGE: dependencies_library_single_archlinux_32bit $dependency_library dependencies_library_single_archlinux_32bit() { local dependency_library dependency_library="$1" local package_name case "$dependency_library" in ('alleg-alsadigi.so') # This library is not provided in a 32-bit build for Arch Linux unset package_name ;; ('alleg-alsamidi.so') # This library is not provided in a 32-bit build for Arch Linux unset package_name ;; ('ld-linux.so.2') package_name='lib32-glibc' ;; ('ld-linux-x86-64.so.2') package_name='lib32-glibc' ;; ('libaldmb.so.1') # This library is not provided for Arch Linux unset package_name ;; ('liballeg.so.4.4') # This library is not provided in a 32-bit build for Arch Linux unset package_name ;; ('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' ;; ('libatk-bridge-2.0.so.0') package_name='lib32-at-spi2-core' ;; ('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 ;; ('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' ;; ('libcrypto.so.3') package_name='lib32-openssl' ;; ('libcups.so.2') package_name='lib32-libcups' ;; ('libcurl.so.4') package_name='lib32-curl' ;; ('libcurl.so.4+CURL_OPENSSL_3') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/curl/ return 0 ;; ('libcurl-gnutls.so.4') package_name='lib32-libcurl-gnutls' ;; ('libdbus-1.so.3') package_name='lib32-dbus' ;; ('libdbus-glib-1.so.2') package_name='lib32-dbus-glib' ;; ('libdl.so.2') package_name='lib32-glibc' ;; ('libdrm.so.2') package_name='lib32-libdrm' ;; ('libdumb.so.1') # This library is not provided for Arch Linux unset package_name ;; ('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' ;; ('libgbm.so.1') package_name='lib32-mesa' ;; ('libgcc_s.so.1') package_name='lib32-gcc-libs' ;; ('libgconf-2.so.4') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/gconf/ return 0 ;; ('libgcrypt.so.11') package_name='lib32-libgcrypt15' ;; ('libgdiplus.so') # This library is not provided in a 32-bit build for Arch Linux unset package_name ;; ('libgdk-3.so.0') package_name='lib32-gtk3' ;; ('libgdk_pixbuf-2.0.so.0') package_name='lib32-gdk-pixbuf2' ;; ('libgdk-x11-2.0.so.0') package_name='lib32-gtk2' ;; ('libgio-2.0.so.0') package_name='lib32-glib2' ;; ('libGL.so.1') package_name='lib32-libgl' ;; ('libGLEW.so.2.2') package_name='lib32-glew' ;; ('libglfw.so.3') package_name='lib32-glfw' ;; ('libglib-2.0.so.0') package_name='lib32-glib2' ;; ('libGLU.so.1') package_name='lib32-glu' ;; ('libGLX.so.0') package_name='lib32-libglvnd' ;; ('libgmodule-2.0.so.0') package_name='lib32-glib2' ;; ('libgobject-2.0.so.0') package_name='lib32-glib2' ;; ('libgomp.so.1') package_name='lib32-gcc-libs' ;; ('libgpg-error.so.0') package_name='lib32-libgpg-error' ;; ('libgssapi_krb5.so.2') package_name='lib32-krb5' ;; ('libgthread-2.0.so.0') package_name='lib32-glib2' ;; ('libgtk-x11-2.0.so.0') package_name='lib32-gtk2' ;; ('libgtk-3.so.0') package_name='lib32-gtk3' ;; ('libICE.so.6') package_name='lib32-libice' ;; ('libidn.so.11') package_name='lib32-libidn11' ;; ('libidn2.so.0') package_name='lib32-libidn2' ;; ('libIL.so.1') # This library is not provided in a 32-bit build for Arch Linux unset package_name ;; ('libjpeg.so.62') package_name='lib32-libjpeg6-turbo' ;; ('libk5crypto.so.3') package_name='lib32-krb5' ;; ('libkrb5.so.3') package_name='lib32-krb5' ;; ('liblcms2.so.2') package_name='lib32-lcms2' ;; ('liblua5.3.so.0') package_name='lib32-lua' ;; ('libluajit-5.1.so.2') package_name='lib32-luajit' ;; ('liblz4.so.1') package_name='lib32-lz4' ;; ('libm.so.6') package_name='lib32-glibc' ;; ('libmbedtls.so.12') # This library is not provided in a 32-bit build for Arch Linux unset package_name ;; ('libminizip.so.1') package_name='lib32-minizip' ;; ('libmodplug.so.1') package_name='lib32-libmodplug' ;; ('libmpg123.so.0') package_name='lib32-mpg123' ;; ('libnghttp2.so.14') package_name='lib32-libnghttp2' ;; ('libnotify.so.4') # This library is not provided in a 32-bit build for Arch Linux unset package_name ;; ('libnspr4.so') package_name='lib32-nspr' ;; ('libnss3.so') package_name='lib32-nss' ;; ('libnssutil3.so') package_name='lib32-nss' ;; ('libogg.so.0') package_name='lib32-libogg' ;; ('libopenal.so.1') package_name='lib32-openal' ;; ('libOpenGL.so.0') package_name='lib32-libglvnd' ;; ('libopenmpt.so.0') # This library is not provided in a 32-bit build for Arch Linux unset package_name ;; ('libpango-1.0.so.0') package_name='lib32-pango' ;; ('libpangocairo-1.0.so.0') package_name='lib32-pango' ;; ('libpangoft2-1.0.so.0') package_name='lib32-pango' ;; ('libpcre.so.3') # This library is not provided for Arch Linux unset package_name ;; ('libphysfs.so.1') package_name='lib32-physfs' ;; ('libpixman-1.so.0') package_name='lib32-pixman' ;; ('libplc4.so') package_name='lib32-nspr' ;; ('libplds4.so') package_name='lib32-nspr' ;; ('libpng12.so.0') package_name='lib32-libpng12' ;; ('libpng16.so.16') package_name='lib32-libpng' ;; ('libpsl.so.5') package_name='lib32-libpsl' ;; ('libpthread.so.0') package_name='lib32-glibc' ;; ('libpulse.so.0') package_name='lib32-libpulse' ;; ('libpulse-simple.so.0') package_name='lib32-libpulse' ;; ( \ 'libQt5Core.so.5' | \ 'libQt5Gui.so.5' | \ 'libQt5Widgets.so.5' \ ) # These libraries are not provided in 32-bit builds for Arch Linux. unset package_name ;; ('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_net-2.0.so.0') # This library is not provided in a 32-bit build for Arch Linux. unset package_name ;; ('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' ;; ('libssl.so.3') package_name='lib32-openssl' ;; ('libssl3.so') package_name='lib32-nss' ;; ('libstdc++.so.5') package_name='lib32-libstdc++5' ;; ('libstdc++.so.6') package_name='lib32-gcc-libs' ;; ('libtcmalloc_minimal.so.4') package_name='lib32-gperftools' ;; ('libtheora.so.0') package_name='lib32-libtheora' ;; ('libtheoradec.so.1') package_name='lib32-libtheora' ;; ('libtheoraenc.so.1') package_name='lib32-libtheora' ;; ('libthread_db.so.1') package_name='lib32-glibc' ;; ('libtiff.so.6') package_name='lib32-libtiff' ;; ('libturbojpeg.so.0') package_name='lib32-libjpeg-turbo' ;; ('libudev.so.0') package_name='lib32-libudev0-shim' ;; ('libudev.so.1') package_name='lib32-systemd' ;; ('libutil.so.1') package_name='lib32-glibc' ;; ('libuuid.so.1') package_name='lib32-util-linux' ;; ('libuv.so.1') # This library is not provided in a 32-bit build for Arch Linux unset package_name ;; ('libvorbis.so.0') package_name='lib32-libvorbis' ;; ('libvorbisenc.so.2') package_name='lib32-libvorbis' ;; ('libvorbisfile.so.3') package_name='lib32-libvorbis' ;; ('libvulkan.so.1') package_name=' lib32-vulkan-icd-loader lib32-vulkan-driver' ;; ('libwayland-client.so.0') package_name='lib32-wayland' ;; ('libX11.so.6') package_name='lib32-libx11' ;; ('libX11-xcb.so.1') package_name='lib32-libx11' ;; ('libxcb.so.1') package_name='lib32-libxcb' ;; ('libxcb-randr.so.0') package_name='lib32-libxcb' ;; ('libXcomposite.so.1') package_name='lib32-libxcomposite' ;; ('libXcursor.so.1') package_name='lib32-libxcursor' ;; ('libXdamage.so.1') package_name='lib32-libxdamage' ;; ('libXext.so.6') package_name='lib32-libxext' ;; ('libXfixes.so.3') package_name='lib32-libxfixes' ;; ('libXft.so.2') package_name='lib32-libxft' ;; ('libXi.so.6') package_name='lib32-libxi' ;; ('libXinerama.so.1') package_name='lib32-libxinerama' ;; ('libxkbcommon.so.0') package_name='lib32-libxkbcommon' ;; ('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\n' "$package_name" return 0 fi dependencies_unknown_libraries_add "$dependency_library" } src/60_system_archlinux/25_dependencies_mono.sh0000644000000000000000000000040213120060140020550 0ustar rootroot# Print Arch Linux packages providing given Mono libraries list. # USAGE: dependencies_mono_list_archlinux $dependencies_mono dependencies_mono_list_archlinux() { # Arch Linux provides all Mono libraries in a single "mono" package. printf '%s\n' 'mono' } src/60_system_archlinux/25_dependencies_siblings.sh0000644000000000000000000000135613120060140021423 0ustar rootroot# Print Arch Linux packages providing given sibling packages list. # USAGE: dependencies_siblings_list_archlinux $dependencies_siblings dependencies_siblings_list_archlinux() { local dependencies_siblings dependencies_siblings="$1" local dependency_sibling while read -r dependency_sibling; do dependencies_sibling_single_archlinux "$dependency_sibling" done <<- EOL $(printf '%s' "$dependencies_siblings") EOL } # Print Arch Linux package providing given single sibling package. # USAGE: dependencies_sibling_single_archlinux $dependency_sibling dependencies_sibling_single_archlinux() { local dependency_sibling dependency_sibling="$1" local package_id package_id=$(package_id "$dependency_sibling") printf '%s\n' "$package_id" } src/60_system_archlinux/90_messages.sh0000644000000000000000000000077013120060140016713 0ustar rootroot# print mtree computation message # USAGE: info_package_mtree_computation $package info_package_mtree_computation() { local package package="$1" local package_path package_path=$(package_path "$package") local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Création du fichier .MTREE pour %s…\n' ;; ('en'|*) message='Creating .MTREE file for %s…\n' ;; esac print_message 'info' "$message" \ "$package_path" } src/60_system_debian/10_instructions.sh0000644000000000000000000000465313120060140017071 0ustar rootroot# Debian - Print installation instructions # USAGE: debian_install_instructions $package[…] debian_install_instructions() { if [ "${PLAYIT_DEBIAN_OLD_DEB_FORMAT:-0}" -eq 1 ]; then debian_install_instructions_dpkg "$@" else debian_install_instructions_apt "$@" fi } # Debian - Print installation instructions, using apt # USAGE: debian_install_instructions_apt $package[…] debian_install_instructions_apt() { local option_output_dir string_format option_output_dir=$(option_value 'output-dir') if printf '%s' "$option_output_dir" | grep --quiet --fixed-strings ' '; then string_format=' "%s"' else string_format=' %s' fi printf 'apt install' local package package_name package_output string_format for package in "$@"; do package_name=$(package_name "$package") package_output=$(realpath "${option_output_dir}/${package_name}") ## Silence ShellCheck false-positive ## Don't use variables in the printf format string. Use printf "..%s.." "$foo". # shellcheck disable=SC2059 printf "$string_format" "$package_output" done printf '\n' } # Debian - Print installation instructions, using dpkg # USAGE: debian_install_instructions_dpkg $package[…] debian_install_instructions_dpkg() { local option_output_dir string_format option_output_dir=$(option_value 'output-dir') if printf '%s' "$option_output_dir" | grep --quiet --fixed-strings ' '; then string_format=' "%s"' else string_format=' %s' fi printf 'dpkg --install' local package package_name package_output string_format for package in "$@"; do package_name=$(package_name "$package") package_output=$(realpath "${option_output_dir}/${package_name}") ## Silence ShellCheck false-positive ## Don't use variables in the printf format string. Use printf "..%s.." "$foo". # shellcheck disable=SC2059 printf "$string_format" "$package_output" done printf '\n' printf 'apt-get install --fix-broken\n\n' local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Les éventuelles erreurs de dépendances suite à la commande dpkg peuvent être ignorées,' message="$message"' elles seront corrigées par la commande apt-get à entrer ensuite.\n' ;; ('en'|*) message='Potential errors related to dependencies after the dpkg command can be ignored,' message="$message"' they will be fixed by the command apt-get to run afterwards.\n' ;; esac print_message 'info' "$message" } src/60_system_debian/10_packages.sh0000644000000000000000000001446213120060140016102 0ustar rootroot# Debian - Write the metadata for the listed packages # USAGE: debian_packages_metadata $package[…] debian_packages_metadata() { local package for package in "$@"; do debian_package_metadata_single "$package" done } # Debian - Write the metadata for the given package # USAGE: debian_package_metadata_single $package debian_package_metadata_single() { local package package="$1" # Create metadata directory. local package_path control_directory package_path=$(package_path "$package") control_directory="${package_path}/DEBIAN" mkdir --parents "$control_directory" # Write main metadata file (DEBIAN/control). local control_file control_file="${control_directory}/control" debian_package_metadata_control "$package" > "$control_file" # Write postinst/prerm scripts, enforce correct permissions. local postinst_actions postinst_warnings postinst_actions=$(package_postinst_actions "$package") postinst_warnings=$(package_postinst_warnings "$package") if [ -n "$postinst_actions" ] || [ -n "$postinst_warnings" ]; then debian_script_postinst "$package" > "${control_directory}/postinst" chmod 755 "${control_directory}/postinst" fi local prerm_actions prerm_actions=$(package_prerm_actions "$package") if [ -n "$prerm_actions" ]; then debian_script_prerm "$package" > "${control_directory}/prerm" chmod 755 "${control_directory}/prerm" fi } # Print the content of the DEBIAN/control metadata file for the given package # USAGE: debian_package_metadata_control $package # RETURN: the contents of the DEBIAN/control file, # spanning over multiple lines debian_package_metadata_control() { local package package="$1" local \ debian_field_package \ debian_field_source \ debian_field_version \ debian_field_architecture \ debian_field_maintainer \ debian_field_installedsize \ debian_field_provides \ debian_field_depends \ debian_field_description debian_field_package=$(debian_field_package "$package") debian_field_source=$(debian_field_source "$package") debian_field_version=$(debian_field_version "$package") debian_field_architecture=$(debian_field_architecture "$package") debian_field_maintainer=$(debian_field_maintainer "$package") debian_field_installedsize=$(debian_field_installedsize "$package") debian_field_provides=$(debian_field_provides "$package") debian_field_depends=$(debian_field_depends "$package") debian_field_description=$(debian_field_description "$package") cat <<- EOF Package: $debian_field_package Source: $debian_field_source Version: $debian_field_version Architecture: $debian_field_architecture Multi-Arch: foreign Maintainer: $debian_field_maintainer Installed-Size: $debian_field_installedsize Section: non-free/games EOF if [ -n "$debian_field_provides" ]; then cat <<- EOF Conflicts: $debian_field_provides Provides: $debian_field_provides Replaces: $debian_field_provides EOF fi if [ -n "$debian_field_depends" ]; then cat <<- EOF Depends: $debian_field_depends EOF fi cat <<- EOF Description: $debian_field_description EOF } # Debian - Build a list of packages # USAGE: debian_packages_build $package[…] debian_packages_build() { local package for package in "$@"; do debian_package_build_single "$package" done } # Debian - Build a single package # USAGE: debian_package_build_single $package debian_package_build_single() { local package package="$1" local package_path package_path=$(package_path "$package") local option_output_dir package_name generated_package_path option_output_dir=$(option_value 'output-dir') package_name=$(package_name "$package") generated_package_path="${option_output_dir}/${package_name}" # Skip packages already existing, # unless called with --overwrite. local option_overwrite option_overwrite=$(option_value 'overwrite') if [ "$option_overwrite" -eq 0 ] && [ -e "$generated_package_path" ] then information_package_already_exists "$package_name" return 0 fi # Set the common dpkg-deb options local dpkg_options ## Create all files and directories with owner:group = root:root. dpkg_options='--root-owner-group' # Use old .deb format if the package is going over the size limit for the modern format local package_size package_size=$(debian_field_installedsize "$package") if [ "$package_size" -gt 9700000 ]; then warning_debian_size_limit "$package" export PLAYIT_DEBIAN_OLD_DEB_FORMAT=1 dpkg_options="${dpkg_options:-} --deb-format=0.939000" fi # Set compression setting local option_compression option_compression=$(option_value 'compression') case "$option_compression" in ('none') dpkg_options="${dpkg_options:-} -Znone" ;; ('speed') dpkg_options="${dpkg_options:-} -Zgzip" ;; ('size') if [ "$package_size" -gt 9700000 ]; 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 [ "$package_size" -gt 9700000 ]; 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 information_package_building "$package_name" ## We need to explicitly export TMPDIR, or dpkg-deb will not pick it up. export TMPDIR="$PLAYIT_WORKDIR" if ! dpkg-deb ${dpkg_options:-} --build "$package_path" "$generated_package_path" 1>/dev/null then error_package_generation_failed "$package_name" return 1 fi } # Print the file name of the given package # USAGE: package_name_debian $package # RETURNS: the file name, as a string package_name_debian() { local package package="$1" local package_id package_version package_architecture package_name package_id=$(package_id "$package") package_version=$(package_version) package_architecture=$(debian_field_architecture "$package") package_name="${package_id}_${package_version}_${package_architecture}.deb" printf '%s' "$package_name" } # Get the path to the directory where the given package is prepared, # relative to the directory where all packages are stored # USAGE: package_path_debian $package # RETURNS: relative path to a directory, as a string package_path_debian() { local package package="$1" local package_name package_path package_name=$(package_name "$package") package_path="${package_name%.deb}" printf '%s' "$package_path" } src/60_system_debian/15_metadata-fields.sh0000644000000000000000000001022013120060140017341 0ustar rootroot# Debian - Print the content of the "Package" field # USAGE: debian_field_package $package # RETURN: the field content debian_field_package() { local package package="$1" package_id "$package" } # Debian - Print the content of the "Source" field # USAGE: debian_field_source $package # RETURN: the field content debian_field_source() { ## The package argument is not actually used, ## but is passed for consistency with other debian_field_* functions. local package package="$1" ## Including the version should make upgrades easier when using `apt install --with-source (…)`. local game_id package_version game_id=$(game_id) package_version=$(package_version) printf '%s (%s)' "$game_id" "$package_version" } # Debian - Print the content of the "Version" field # USAGE: debian_field_version $package # RETURN: the field content debian_field_version() { ## The package argument is not actually used, ## but is passed for consistency with other debian_field_* functions. local package package="$1" package_version } # Debian - Print the content of the "Architecture" field # USAGE: debian_field_architecture $package # RETURN: the field content debian_field_architecture() { local package package="$1" local package_architecture package_architecture_string package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') package_architecture_string='i386' ;; ('64') package_architecture_string='amd64' ;; ('all') package_architecture_string='all' ;; esac printf '%s' "$package_architecture_string" } # Debian - Print the content of the "Maintainer" field # USAGE: debian_field_maintainer $package # RETURN: the field content debian_field_maintainer() { ## The package argument is not actually used, ## but is passed for consistency with other debian_field_* functions. local package package="$1" package_maintainer } # Debian - Print the content of the "Installed-Size" field # USAGE: debian_field_installedsize $package # RETURN: the field content debian_field_installedsize() { local package package="$1" # Compute the package size, in kilobytes local package_path package_path=$(package_path "$package") du --total --block-size=1K --summarize "$package_path" | tail --lines=1 | cut --fields=1 } # Debian - Print the content of the "Provides" field # This value is used for the fields "Conflicts" and "Replaces" too. # USAGE: debian_field_provides $package # RETURN: the field content debian_field_provides() { local package package="$1" local package_provides package_provides=$(package_provides "$package") # Return early if there is no package name provided if [ -z "$package_provides" ]; then return 0 fi local provides_list package_name while read -r package_name; do if [ -z "${provides_list:-}" ]; then provides_list="$package_name" else provides_list="$provides_list, $package_name" fi done <<- EOL $(printf '%s' "$package_provides") EOL printf '%s' "$provides_list" } # Debian - Print the content of the "Depends" field # USAGE: debian_field_depends $package # RETURN: the field content debian_field_depends() { local package package="$1" local dependencies_list first_item_displayed dependency_string dependencies_list=$(dependencies_full_list_packages "$package") first_item_displayed=0 while read -r dependency_string; do if [ -z "$dependency_string" ]; then continue fi if [ "$first_item_displayed" -eq 0 ]; then printf '%s' "$dependency_string" first_item_displayed=1 else printf ', %s' "$dependency_string" fi done <<- EOF $(printf '%s' "$dependencies_list") EOF } # Debian - Print the content of the "Description" field # USAGE: debian_field_description $package # RETURN: the field content, # spanning over multiple lines debian_field_description() { local package package="$1" local game_name package_description script_version_string game_name=$(game_name) package_description=$(package_description "$package") script_version_string=$(script_version) printf '%s' "$game_name" if [ -n "$package_description" ]; then printf -- ' - %s' "$package_description" fi printf '\n ./play.it script version %s' "$script_version_string" } src/60_system_debian/15_package-scripts.sh0000644000000000000000000000222513120060140017403 0ustar rootroot# Debian - Print the content of the DEBIAN/postinst script for the given package # USAGE: debian_script_postinst $package # RETURN: the contents of the DEBIAN/postinst file, # spanning over multiple lines debian_script_postinst() { local package package="$1" cat <<- EOF #!/bin/sh set -o errexit EOF ## Include actions that should be run. package_postinst_actions "$package" ## Include warnings that should be displayed. local warning_messages warning_messages=$(package_postinst_warnings "$package") if [ -n "$warning_messages" ]; then local warning_line while read -r warning_line; do printf 'printf "Warning: %%s\\n" "%s"\n' "$warning_line" done <<- EOL $(printf '%s' "$warning_messages") EOL fi cat <<- EOF exit 0 EOF } # Debian - Print the content of the DEBIAN/prerm script for the given package # USAGE: debian_script_prerm $package # RETURN: the contents of the DEBIAN/prerm file, # spanning over multiple lines debian_script_prerm() { local package package="$1" cat <<- EOF #!/bin/sh set -o errexit EOF ## Include actions that should be run. package_prerm_actions "$package" cat <<- EOF exit 0 EOF } src/60_system_debian/25_dependencies_commands.sh0000644000000000000000000000664713120060140020647 0ustar rootroot# Print Debian packages providing given commands list. # USAGE: dependencies_commands_list_debian $dependencies_commands dependencies_commands_list_debian() { local dependencies_commands dependencies_commands="$1" local dependency_command while read -r dependency_command; do dependencies_command_single_debian "$dependency_command" done <<- EOL $(printf '%s' "$dependencies_commands") EOL } # Print Debian packages providing given single command. # USAGE: dependencies_command_single_debian $dependency_command dependencies_command_single_debian() { local dependency_command dependency_command="$1" local package_names case "$dependency_command" in ('7za') package_names=' 7zip | p7zip-full' ;; ('corsix-th') package_names=' corsix-th' ;; ('dos2unix') package_names=' dos2unix' ;; ('dosbox') package_names=' dosbox' ;; ('dxvk-setup | winetricks') local package package_architecture package=$(current_package) package_architecture=$(package_architecture "$package") case "$package_architecture" in ('64') package_names=' dxvk-wine64 | winetricks dxvk | winetricks' ;; ('32') package_names=' dxvk-wine32 | winetricks dxvk | winetricks' ;; esac ;; ('godot3-runner') package_names=' godot3-runner' ;; ('firefox') package_names=' firefox-esr | firefox' ;; ('java') package_names=' default-jre | java-runtime' ;; ('julius') package_names=' julius' ;; ('mono') package_names=' mono-runtime' ;; ('mpv') package_names=' mpv:amd64 | mpv' ;; ('openmw-iniimporter') package_names=' openmw-launcher' ;; ('openmw-launcher') package_names=' openmw-launcher' ;; ('pidwait') package_names=' procps:amd64 | procps' ;; ('pulseaudio') package_names=' pulseaudio:amd64 | pulseaudio' ;; ('python3') package_names=' python3' ;; ('renpy') package_names=' renpy' ;; ('scummvm') package_names=' scummvm' ;; ('sed') # The Debian policy advises against adding dependencies on packages that are part of the required set. package_names='' ;; ('setxkbmap') package_names=' x11-xkb-utils' ;; ('terminal_wrapper') local package package_architecture package=$(current_package) package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') package_names=' xterm:amd64 | xterm | x-terminal-emulator:amd64 | x-terminal-emulator' ;; ('64') package_names=' xterm | x-terminal-emulator' ;; esac ;; ('vcmilauncher') package_names=' vcmi' ;; ('wine') local package package_architecture package=$(current_package) 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' ;; ('xgamma') package_names=' x11-xserver-utils:amd64 | x11-xserver-utils' ;; ('xrandr') package_names=' x11-xserver-utils:amd64 | x11-xserver-utils' ;; (*) dependencies_unknown_command_add "$dependency_command" return 0 ;; esac printf '%s\n' "$package_names" } src/60_system_debian/25_dependencies_gstreamer.sh0000644000000000000000000000374513120060140021033 0ustar rootroot# Print Debian packages providing given GStreamer decoders list. # USAGE: dependencies_gstreamer_list_debian $dependencies_gstreamer dependencies_gstreamer_list_debian() { local dependencies_gstreamer dependencies_gstreamer="$1" local dependency_gstreamer while read -r dependency_gstreamer; do dependencies_gstreamer_single_debian "$dependency_gstreamer" done <<- EOL $(printf '%s' "$dependencies_gstreamer") EOL } # Print Debian packages providing given single GStreamer decoder. # USAGE: debian_dependency_providing_gstreamer_plugin $dependency_gstreamer dependencies_gstreamer_single_debian() { local dependency_gstreamer dependency_gstreamer="$1" local package_names case "$dependency_gstreamer" in ('audioconvert') package_names=' gstreamer1.0-plugins-base' ;; ('avidemux') package_names=' gstreamer1.0-plugins-good' ;; ('decodebin') package_names=' gstreamer1.0-plugins-base' ;; ('deinterlace') package_names=' gstreamer1.0-plugins-good' ;; ('application/x-id3') package_names=' gstreamer1.0-plugins-good' ;; ('audio/mpeg, mpegversion=(int)1, layer=(int)3') package_names=' gstreamer1.0-plugins-good' ;; ( \ 'audio/x-wma' | \ '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' | \ 'video/x-wmv, wmvversion=(int)1' \ ) package_names=' gstreamer1.0-libav' ;; (*) dependencies_unknown_gstreamer_media_formats_add "$dependency_gstreamer" return 0 ;; esac printf '%s\n' "$package_names" } src/60_system_debian/25_dependencies_legacy.sh0000644000000000000000000000043413120060140020276 0ustar rootroot# Print Debian packages providing given packages list, set using legacy dependencies system. # USAGE: dependencies_legacy_list_debian $dependencies_legacy dependencies_legacy_list_debian() { local dependencies_legacy dependencies_legacy="$1" printf '%s\n' $dependencies_legacy } src/60_system_debian/25_dependencies_libraries.sh0000644000000000000000000002727713120060140021024 0ustar rootroot# Print Debian packages providing given native libraries list. # USAGE: dependencies_libraries_list_debian $dependencies_libraries dependencies_libraries_list_debian() { local dependencies_libraries dependencies_libraries="$1" local dependency_library while read -r dependency_library; do dependencies_library_single_debian "$dependency_library" done <<- EOL $(printf '%s' "$dependencies_libraries") EOL } # Print Debian packages providing given single native library. # USAGE: dependencies_library_single_debian $dependency_library dependencies_library_single_debian() { local dependency_library dependency_library="$1" local package_name case "$dependency_library" in ('alleg-alsadigi.so') package_name='liballegro4.4' ;; ('alleg-alsamidi.so') package_name='liballegro4.4' ;; ('ld-linux.so.2') package_name='libc6' ;; ('ld-linux-x86-64.so.2') package_name='libc6' ;; ('libaldmb.so.1') package_name='libaldmb1' ;; ('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' ;; ('libatk-bridge-2.0.so.0') package_name='libatk-bridge2.0-0' ;; ('libatspi.so.0') package_name='libatspi2.0-0' ;; ('libatk-1.0.so.0') package_name='libatk1.0-0' ;; ('libaudio.so.2') package_name='libaudio2' ;; ('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' ;; ('libcrypto.so.3') package_name='libssl3' ;; ('libcups.so.2') package_name='libcups2' ;; ('libcurl.so.4') package_name='libcurl4' ;; ('libcurl.so.4+CURL_OPENSSL_3') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/curl/ return 0 ;; ('libcurl-gnutls.so.4') package_name='libcurl3-gnutls' ;; ('libdbus-1.so.3') package_name='libdbus-1-3' ;; ('libdbus-glib-1.so.2') package_name='libdbus-glib-1-2' ;; ('libdl.so.2') package_name='libc6' ;; ('libdrm.so.2') package_name='libdrm2' ;; ('libdumb.so.1') package_name='libdumb1' ;; ('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' ;; ('libgbm.so.1') package_name='libgbm1' ;; ('libgcc_s.so.1') package_name='libgcc-s1' ;; ('libgconf-2.so.4') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/gconf/ return 0 ;; ('libgcrypt.so.11') # This old library is no longer available from Debian. unset package_name ;; ('libgdiplus.so') package_name='libgdiplus' ;; ('libgdk-3.so.0') package_name='libgtk-3-0' ;; ('libgdk_pixbuf-2.0.so.0') package_name='libgdk-pixbuf-2.0-0 | libgdk-pixbuf2.0-0' ;; ('libgdk-x11-2.0.so.0') package_name='libgtk2.0-0' ;; ('libgio-2.0.so.0') package_name='libglib2.0-0' ;; ('libGL.so.1') package_name=' libgl1 | libgl1-mesa-glx libglx-mesa0 | libglx-vendor | libgl1-mesa-glx' ;; ('libGLEW.so.2.2') package_name='libglew2.2' ;; ('libglfw.so.3') package_name='libglfw3 | libglfw3-wayland' ;; ('libglib-2.0.so.0') package_name='libglib2.0-0' ;; ('libGLU.so.1') package_name='libglu1-mesa | libglu1' ;; ('libGLX.so.0') package_name='libglx0' ;; ('libgmodule-2.0.so.0') package_name='libglib2.0-0' ;; ('libgobject-2.0.so.0') package_name='libglib2.0-0' ;; ('libgomp.so.1') package_name='libgomp1' ;; ('libgpg-error.so.0') package_name='libgpg-error0' ;; ('libgssapi_krb5.so.2') package_name='libgssapi-krb5-2' ;; ('libgthread-2.0.so.0') package_name='libglib2.0-0' ;; ('libgtk-x11-2.0.so.0') package_name='libgtk2.0-0' ;; ('libgtk-3.so.0') package_name='libgtk-3-0' ;; ('libICE.so.6') package_name='libice6' ;; ('libidn.so.11') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/libidn/ return 0 ;; ('libidn2.so.0') package_name='libidn2-0' ;; ('libIL.so.1') package_name='libdevil1c2' ;; ('libjpeg.so.62') package_name='libjpeg62-turbo | libjpeg62' ;; ('libk5crypto.so.3') package_name='libk5crypto3' ;; ('libkrb5.so.3') package_name='libkrb5-3' ;; ('liblcms2.so.2') package_name='liblcms2-2' ;; ('liblua5.3.so.0') package_name='liblua5.3-0' ;; ('libluajit-5.1.so.2') package_name='libluajit-5.1-2' ;; ('liblz4.so.1') package_name='liblz4-1' ;; ('libm.so.6') package_name='libc6' ;; ('libmbedtls.so.12') package_name='libmbedtls12' ;; ('libminizip.so.1') package_name='libminizip1' ;; ('libmodplug.so.1') package_name='libmodplug1' ;; ('libmpg123.so.0') package_name='libmpg123-0' ;; ('libnghttp2.so.14') package_name='libnghttp2-14' ;; ('libnotify.so.4') package_name='libnotify4' ;; ('libnspr4.so') package_name='libnspr4' ;; ('libnss3.so') package_name='libnss3' ;; ('libnssutil3.so') package_name='libnss3' ;; ('libogg.so.0') package_name='libogg0' ;; ('libopenal.so.1') package_name='libopenal1' ;; ('libOpenGL.so.0') package_name='libopengl0' ;; ('libopenmpt.so.0') package_name='libopenmpt0' ;; ('libpango-1.0.so.0') package_name='libpango-1.0-0' ;; ('libpangocairo-1.0.so.0') package_name='libpangocairo-1.0-0' ;; ('libpangoft2-1.0.so.0') package_name='libpangoft2-1.0-0' ;; ('libpcre.so.3') package_name='libpcre3' ;; ('libphysfs.so.1') package_name='libphysfs1' ;; ('libpixman-1.so.0') package_name='libpixman-1-0' ;; ('libplc4.so') package_name='libnspr4' ;; ('libplds4.so') package_name='libnspr4' ;; ('libpng12.so.0') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/libpng/ return 0 ;; ('libpng16.so.16') package_name='libpng16-16' ;; ('libpsl.so.5') package_name='libpsl5' ;; ('libpthread.so.0') package_name='libc6' ;; ('libpulse.so.0') package_name='libpulse0' ;; ('libpulse-simple.so.0') package_name='libpulse0' ;; ( \ 'libQt5Core.so.5' | \ 'libQt5Gui.so.5' \ ) package_name='libqt5core5a' ;; ('libQt5Widgets.so.5') package_name='libqt5widgets5' ;; ('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_net-2.0.so.0') package_name='libsdl2-net-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 ;; ('libssl.so.3') package_name='libssl3' ;; ('libssl3.so') package_name='libnss3' ;; ('libstdc++.so.5') package_name='libstdc++5' ;; ('libstdc++.so.6') package_name='libstdc++6' ;; ('libtcmalloc_minimal.so.4') package_name='libtcmalloc-minimal4' ;; ('libtheora.so.0') package_name='libtheora0' ;; ('libtheoradec.so.1') package_name='libtheora0' ;; ('libtheoraenc.so.1') package_name='libtheora0' ;; ('libthread_db.so.1') package_name='libc6' ;; ('libtiff.so.6') package_name='libtiff6' ;; ('libturbojpeg.so.0') package_name='libturbojpeg0' ;; ('libudev.so.0') package_name='libudev0' ;; ('libudev.so.1') package_name='libudev1' ;; ('libutil.so.1') package_name='libc6' ;; ('libuuid.so.1') package_name='libuuid1' ;; ('libuv.so.1') package_name='libuv1' ;; ('libvorbis.so.0') package_name='libvorbis0a' ;; ('libvorbisenc.so.2') package_name='libvorbisenc2' ;; ('libvorbisfile.so.3') package_name='libvorbisfile3' ;; ('libvulkan.so.1') package_name=' libvulkan1 mesa-vulkan-drivers | vulkan-icd' ;; ('libwayland-client.so.0') package_name='libwayland-client0' ;; ('libX11.so.6') package_name='libx11-6' ;; ('libX11-xcb.so.1') package_name='libx11-xcb1' ;; ('libxcb.so.1') package_name='libxcb1' ;; ('libxcb-randr.so.0') package_name='libxcb-randr0' ;; ('libXcomposite.so.1') package_name='libxcomposite1' ;; ('libXcursor.so.1') package_name='libxcursor1' ;; ('libXdamage.so.1') package_name='libxdamage1' ;; ('libXext.so.6') package_name='libxext6' ;; ('libXfixes.so.3') package_name='libxfixes3' ;; ('libXft.so.2') package_name='libxft2' ;; ('libXi.so.6') package_name='libxi6' ;; ('libXinerama.so.1') package_name='libxinerama1' ;; ('libxkbcommon.so.0') package_name='libxkbcommon0' ;; ('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\n' "$package_name" return 0 fi dependencies_unknown_libraries_add "$dependency_library" } src/60_system_debian/25_dependencies_mono.sh0000644000000000000000000000674513120060140020015 0ustar rootroot# Print Debian packages providing given Mono libraries list. # USAGE: dependencies_mono_list_debian $dependencies_mono dependencies_mono_list_debian() { local dependencies_mono dependencies_mono="$1" local dependency_mono while read -r dependency_mono; do dependencies_mono_single_debian "$dependency_mono" done <<- EOL $(printf '%s' "$dependencies_mono") EOL } # Print Debian packages providing given Mono library. # USAGE: dependencies_mono_single_debian $dependency_mono dependencies_mono_single_debian() { local dependency_mono dependency_mono="$1" local package_name case "$dependency_mono" in ('mscorlib.dll') package_name='libmono-corlib4.5-cil' ;; ('I18N.dll') package_name='libmono-i18n4.0-cil' ;; ('I18N.West.dll') package_name='libmono-i18n-west4.0-cil' ;; ('Microsoft.CSharp.dll') package_name='libmono-microsoft-csharp4.0-cil' ;; ('Mono.CSharp.dll') package_name='libmono-csharp4.0c-cil' ;; ('Mono.Posix.dll') package_name='libmono-posix4.0-cil' ;; ('Mono.Security.dll') package_name='libmono-security4.0-cil' ;; ('OpenTK.dll') package_name='libopentk1.1-cil' ;; ('OpenTK.Compatibility.dll') package_name='libopentk1.1-cil' ;; ('OpenTK.GLControl.dll') package_name='libopentk1.1-cil' ;; ('System.dll') package_name='libmono-system4.0-cil' ;; ('System.ComponentModel.DataAnnotations.dll') package_name='libmono-system-componentmodel-dataannotations4.0-cil' ;; ('System.Configuration.dll') package_name='libmono-system-configuration4.0-cil' ;; ('System.Configuration.Install.dll') package_name='libmono-system-configuration-install4.0-cil' ;; ('System.Core.dll') package_name='libmono-system-core4.0-cil' ;; ('System.Data.dll') package_name='libmono-system-data4.0-cil' ;; ('System.Design.dll') package_name='libmono-system-design4.0-cil' ;; ('System.Drawing.dll') package_name='libmono-system-drawing4.0-cil' ;; ('System.IO.Compression.dll') package_name='libmono-system-io-compression4.0-cil' ;; ('System.IO.Compression.FileSystem.dll') package_name='libmono-system-io-compression-filesystem4.0-cil' ;; ('System.Management.dll') package_name='libmono-system-management4.0-cil' ;; ('System.Net.dll') package_name='libmono-system-net4.0-cil' ;; ('System.Net.Http.dll') package_name='libmono-system-net-http4.0-cil' ;; ('System.Numerics.dll') package_name='libmono-system-numerics4.0-cil' ;; ('System.Runtime.Serialization.dll') package_name='libmono-system-runtime-serialization4.0-cil' ;; ('System.Security.dll') package_name='libmono-system-security4.0-cil' ;; ('System.Transactions.dll') package_name='libmono-system-transactions4.0-cil' ;; ('System.Web.dll') package_name='libmono-system-web4.0-cil' ;; ('System.Web.Extensions.dll') package_name='libmono-system-web-extensions4.0-cil' ;; ('System.Web.Http.dll') package_name='libmono-system-web-http4.0-cil' ;; ('System.Web.Services.dll') package_name='libmono-system-web-services4.0-cil' ;; ('System.Windows.Forms.dll') package_name='libmono-system-windows-forms4.0-cil' ;; ('System.Xml.dll') package_name='libmono-system-xml4.0-cil' ;; ('System.Xml.Linq.dll') package_name='libmono-system-xml-linq4.0-cil' ;; ('WindowsBase.dll') package_name='libmono-windowsbase4.0-cil' ;; esac if [ -n "${package_name:-}" ]; then printf '%s\n' "$package_name" return 0 fi dependencies_unknown_mono_libraries_add "$dependency_mono" } src/60_system_debian/25_dependencies_siblings.sh0000644000000000000000000000132113120060140020640 0ustar rootroot# Print Debian packages providing sibling packages list. # USAGE: dependencies_siblings_list_debian $dependencies_siblings dependencies_siblings_list_debian() { local dependencies_siblings dependencies_siblings="$1" local dependency_sibling while read -r dependency_sibling; do dependencies_sibling_single_debian "$dependency_sibling" done <<- EOL $(printf '%s' "$dependencies_siblings") EOL } # Print Debian package providing given single sibling package. # USAGE: dependencies_sibling_single_debian $dependency_sibling dependencies_sibling_single_debian() { local dependency_sibling dependency_sibling="$1" local package_id package_id=$(package_id "$dependency_sibling") printf '%s\n' "$package_id" } src/60_system_debian/90_messages.sh0000644000000000000000000000077713120060140016147 0ustar rootroot# Warning: A .deb package is going over the 9GB size limit # USAGE: warning_debian_size_limit $package warning_debian_size_limit() { local package package="$1" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le paquet suivant est trop gros pour le format .deb moderne : %s\n' ;; ('en'|*) message='The following package is too big for .deb modern format: %s\n' ;; esac print_message 'warning' "$message" \ "$package" } src/60_system_gentoo/10_instructions.sh0000644000000000000000000000157713120060140017144 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://git.dotslashplay.it/array/quickunpkg/plain/quickunpkg ' information_installation_instructions_gentoo_comment printf '\n' } src/60_system_gentoo/10_packages.sh0000644000000000000000000002136313120060140016151 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 package_name package_path package_prep_path package_id=$(package_id "$package") package_name=$(package_name "$package") package_path=$(package_path "$package") package_prep_path="$(realpath --canonicalize-existing "${package_path}/..")" mkdir --parents "$package_path" mkdir --parents "${package_prep_path}/metadata" touch "${package_prep_path}/gpkg-1" # The CHOST field can be used to limit installation to some triples, # but it can be omitted from the metadata with no issues. gentoo_field_rdepend "$package" > "${package_prep_path}/metadata/RDEPEND" printf '%s' "x-gentoo-overlay" > "${package_prep_path}/metadata/repository" printf '%s' "8" > "${package_prep_path}/metadata/EAPI" gentoo_field_description "$package" > "${package_prep_path}/metadata/DESCRIPTION" gentoo_field_keywords "$package" > "${package_prep_path}/metadata/KEYWORDS" printf '%s' "0" > "${package_prep_path}/metadata/SLOT" printf '%s' "${package_name%.gpkg.tar}" > "${package_prep_path}/metadata/PF" printf '%s' "games-playit" > "${package_prep_path}/metadata/CATEGORY" (gentoo_script_postinst "$package"; gentoo_script_prerm "$package") | bzip2 > "${package_prep_path}/metadata/environment.bz2" truncate --size=0 "${package_prep_path}/metadata/${package_name%.gpkg.tar}.ebuild" } # 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" local package_path package_prep_path package_path=$(package_path "$package") package_prep_path="$(realpath --canonicalize-existing "${package_path}/..")" # Set the path where the package should be generated. local option_output_dir package_name generated_package_path option_output_dir=$(option_value 'output-dir') package_name=$(package_name "$package") generated_package_path="${option_output_dir}/${package_name}" # Skip packages already existing, # unless called with --overwrite. local option_overwrite option_overwrite=$(option_value 'overwrite') if [ "$option_overwrite" -eq 0 ] && [ -e "$generated_package_path" ] then information_package_already_exists "$package_name" return 0 fi # Set basic tar options. local tar_version tar_options tar_version=$(tar --version | head --lines 1) case "$tar_version" in (*'GNU tar'*) tar_options='--create --group=root --owner=root' ;; (*'libarchive'*) tar_options='--create --gname=root --uname=root' ;; (*) error_unknown_tar_implementation return 1 ;; esac # Set compression setting local option_compression tar_compress_program compression_suffix # For compression suffixes, see # https://www.gentoo.org/glep/glep-0074.html#compressed-file-formats option_compression=$(option_value 'compression') case "$option_compression" in ('none') tar_compress_program='' compression_suffix='' ;; ('speed') tar_compress_program='gzip' compression_suffix='.gz' ;; ('size') tar_compress_program='xz' tar_compress_program='.xz' ;; esac # Run the actual package generation, using tar ## I think it only has to be approximate, I’m not even sure if it’s used or not printf '%s' "$(( $(du --apparent-size --block-size=1 --summarize | cut --fields=1)*2 ))" > "${package_prep_path}/metadata/SIZE" ## Create image tar if [ -n "$tar_compress_program" ]; then env --chdir="$package_prep_path" tar $tar_options --use-compress-program="$tar_compress_program" --file "image.tar${compression_suffix}" image else env --chdir="$package_prep_path" tar $tar_options --file "image.tar${compression_suffix}" image fi rm --recursive "${package_prep_path}/image" ## Create metadata tar if ! ( cd "$package_prep_path" if [ -n "$tar_compress_program" ]; then tar $tar_options --use-compress-program="$tar_compress_program" --file "metadata.tar${compression_suffix}" metadata/* else tar $tar_options --file "metadata.tar${compression_suffix}" metadata/* fi ) then error_package_generation_failed "$package_name" return 1 fi rm --recursive "${package_prep_path}/metadata" ## Create Manifest truncate --size=0 "${package_prep_path}/Manifest" for file in gpkg-1 "metadata.tar${compression_suffix}" "image.tar${compression_suffix}"; do local path="${package_prep_path}/${file}" #printf 'DATA %s %d SHA512 %s\n' >> "${package_prep_path}/Manifest" "$file" "$(stat --format='%s' "$path")" "$(sha512sum "$path" | cut --delimiter=' ' --fields=1)" printf 'DATA %s %d SHA512 %s BLAKE2B %s\n' >> "${package_prep_path}/Manifest" "$file" "$(stat --format='%s' "$path")" "$(sha512sum "$path" | cut --delimiter=' ' --fields=1)" "$(b2sum "$path" | cut --delimiter=' ' --fields=1)" done ## Create combined package local package_prep_path_parent package_prep_path_name package_prep_path_parent=$(dirname "$package_prep_path") package_prep_path_name=$(basename "$package_prep_path") env --chdir="$package_prep_path_parent" tar $tar_options --file "$generated_package_path" "${package_prep_path_name}/gpkg-1" "${package_prep_path_name}/metadata.tar${compression_suffix}" "${package_prep_path_name}/image.tar${compression_suffix}" "${package_prep_path_name}/Manifest" } # 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}.gpkg.tar" # Avoid paths collisions when building multiple architecture variants for a same package local packages_list current_package current_package_id packages_list=$(packages_list) for current_package in $packages_list; do current_package_id=$(package_id "$current_package") if [ "$current_package" != "$package" ] && [ "$current_package_id" = "$package_id" ] then local package_architecture package_architecture=$(gentoo_package_architecture_string "$package") package_name="${package_architecture}/${package_name}" break fi done printf '%s' "$package_name" } # Get the path to the directory where the given package image 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%.gpkg.tar}" printf '%s/image' "$package_path" } # Tweak the given package version string to ensure it is compatible with portage # USAGE: gentoo_package_version $package_version # RETURNS: the package version, as a non-empty string gentoo_package_version() { local package_version package_version="$1" set +o errexit package_version=$( printf '%s' "$package_version" | grep --extended-regexp --only-matching '^([0-9]{1,18})(\.[0-9]{1,18})*[a-z]?' ) set -o errexit if [ -z "$package_version" ]; then package_version='1.0' fi local script_version_string script_version_string=$(script_version) printf '%s_p%s' "$package_version" "$(printf '%s' "$script_version_string" | sed 's/\.//g')" } # Print the architecture string of the given package, in the format expected by portage # USAGE: gentoo_package_architecture_string $package # RETURNS: the package architecture, as one of the following values: # - x86 # - amd64 # - data (dummy value) gentoo_package_architecture_string() { local package package="$1" local package_architecture package_architecture_string package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') package_architecture_string='x86' ;; ('64') package_architecture_string='amd64' ;; ('all') # We could put anything here, it should not be used for package metadata. package_architecture_string='data' ;; esac printf '%s' "$package_architecture_string" } # Tweak the given package id to ensure compatibility with portage # USAGE: gentoo_package_id $package_id # RETURNS: the package id, as a non-empty string gentoo_package_id() { local package_id package_id="$1" # Avoid mixups between numbers in package id and version number. printf '%s' "$package_id" | sed 's/-/_/g' } src/60_system_gentoo/15_metadata-fields.sh0000644000000000000000000000641713120060140017427 0ustar rootroot# Gentoo - Print the content of the "KEYWORDS" field # USAGE: gentoo_field_keywords $package # RETURN: the field value gentoo_field_keywords() { local package package="$1" local package_architecture ebuild_keywords package_architecture=$(package_architecture "$package") case "$package_architecture" in ('32') ebuild_keywords='-* x86 amd64' ;; ('64') ebuild_keywords='-* amd64' ;; (*) ebuild_keywords='x86 amd64' # data packages ;; esac printf '%s' "$ebuild_keywords" } # Gentoo - Print the content of the "DESCRIPTION" field # USAGE: gentoo_field_description $package # RETURN: the field value gentoo_field_description() { local package package="$1" local game_name package_description script_version_string game_name=$(game_name) package_description=$(package_description "$package") script_version_string=$(script_version) printf '%s' "$game_name" if [ -n "$package_description" ]; then printf -- ' - %s' "$package_description" fi printf -- ' - ./play.it script version %s' "$script_version_string" } # Gentoo - Print the content of the "RDEPEND" field # USAGE: gentoo_field_rdepend $package # RETURN: the field value gentoo_field_rdepend() { local package package="$1" local dependencies_list first_item_displayed dependency_string dependencies_list=$(dependencies_full_list_packages "$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 local package_conflicts package_conflict package_conflicts=$(package_provides "$package") # Return early if the current package has no "provides" field. if [ -z "$package_conflicts" ]; then return 0 fi # Gentoo has no notion of "provided" package, # so we need to loop over all supported archives # to get the name of all packages providing a given package id. local archives_list packages_list archives_list=$(archives_list) packages_list=$( for archive in $archives_list; do set_current_archive "$archive" packages_list done | list_clean ) # For each conflict of the current package, # find all potential packages that would provide this package id. local package_current package_current_id package_current_provides package_current_provide for package_conflict in $package_conflicts; do for package_current in $packages_list; do # Skip the package we are writing metadata for, # so it does not end up conflicting with itself. if [ "$package_current" = "$package" ]; then continue fi package_current_provides=$(package_provides "$package_current") for package_current_provide in $package_current_provides; do if [ "$package_current_provide" = "$package_conflict" ]; then package_current_id=$(package_id "$package_current") if [ "$first_item_displayed" -eq 0 ]; then printf '!games-play.it/%s' "$package_current_id" first_item_displayed=1 else printf '\n\t!games-play.it/%s' "$package_current_id" fi fi done done done } src/60_system_gentoo/15_package-scripts.sh0000644000000000000000000000241613120060140017456 0ustar rootroot# Gentoo - Print the pkg_postinst function # USAGE: gentoo_script_postinst $package # RETURN: the defintion of the pkg_postinst function, # spanning over several lines gentoo_script_postinst() { local package package="$1" local postinst_actions postinst_warnings postinst_actions=$(package_postinst_actions "$package") postinst_warnings=$(package_postinst_warnings "$package") if [ -n "$postinst_actions" ] || [ -n "$postinst_warnings" ]; then cat <<- EOF pkg_postinst() { EOF # Include actions that should be run. if [ -n "$postinst_actions" ]; then printf '%s\n' "$postinst_actions" fi # Include warnings that should be displayed. if [ -n "$postinst_warnings" ]; then local warning_line while read -r warning_line; do printf 'ewarn "%s"\n' "$warning_line" done <<- EOL $(printf '%s' "$postinst_warnings") EOL fi cat <<- EOF } EOF fi } # Gentoo - Print the pkg_prerm function # USAGE: gentoo_script_prerm $package # RETURN: the defintion of the pkg_prerm function, # spanning over several lines gentoo_script_prerm() { local package package="$1" local prerm_actions prerm_actions=$(package_prerm_actions "$package") if [ -n "$prerm_actions" ]; then cat <<- EOF pkg_prerm() { $prerm_actions } EOF fi } src/60_system_gentoo/20_dependencies.sh0000644000000000000000000000360413120060140017020 0ustar rootroot# Gentoo - Print the path to a temporary file used for additional overlays listing # USAGE: dependency_gentoo_overlays_file dependency_gentoo_overlays_file() { printf '%s/overlays' "$PLAYIT_WORKDIR" } # Gentoo - Add an overlay to the list of additional overlays # USAGE: dependency_gentoo_overlays_add $overlay dependency_gentoo_overlays_add() { local overlay overlays_file overlay="$1" overlays_file="$(dependency_gentoo_overlays_file)" # Do nothing if this overlay is already included in the list if test -e "$overlays_file" \ && grep --quiet --fixed-strings --word-regexp "$overlay" < "$overlays_file" then return 0 fi printf '%s\n' "$overlay" >> "$overlays_file" } # Gentoo - Print gentoo libdir name # USAGE: dependency_gentoo_libdir $arch_string # Note: This prints the name (ie. “lib”) not the path (ie. “/usr/lib”) dependency_gentoo_libdir() { local arch_string="$1" if command -v portageq >/dev/null 2>&1; then print '%s' "$(portageq envvar "LIBDIR_${arch_string}")" else case "$arch_string" in ('amd64') printf '%s' 'lib64' ;; ('x86') printf '%s' 'lib' ;; ('x32') printf '%s' 'libx32' ;; (*) error_unknown_gentoo_architecture_string "$arch_string" 'dependency_gentoo_libdir' return 1 ;; esac fi return 0 } # Gentoo - Link library installed in non-standard libdir to the game’s libdir # USAGE: dependencies_gentoo_link $libname $libdir $package # Note: $libdir is the library’s directory, not the game’s one! dependencies_gentoo_link() { local libname libdir package game_libdir libname="$1" libdir="$2" package="$3" game_libdir=$( set_current_package "$package" path_libraries ) local package_path library_destination package_path=$(package_path "$package") library_destination="${package_path}${game_libdir}" mkdir --parents "$library_destination" ln -sft "$library_destination" "${libdir}/${libname}" } src/60_system_gentoo/25_dependencies_commands.sh0000644000000000000000000000547113120060140020712 0ustar rootroot# Print Gentoo packages providing given commands list. # USAGE: dependencies_commands_list_gentoo $dependencies_commands dependencies_commands_list_gentoo() { local dependencies_commands dependencies_commands="$1" local dependency_command while read -r dependency_command; do dependencies_command_single_gentoo "$dependency_command" done <<- EOL $(printf '%s' "$dependencies_commands") EOL } # Print Gentoo packages providing given single command. # USAGE: dependencies_command_single_gentoo $dependency_command dependencies_command_single_gentoo() { local dependency_command dependency_command="$1" local package_names case "$dependency_command" in ('7za') package_names=' app-arch/p7zip' ;; ('corsix-th') package_names=' games-simulation/corsix-th' ;; ('dos2unix') package_names=' app-text/dos2unix' ;; ('dosbox') package_names=' games-emulation/dosbox' ;; ('godot3-runner') ## TODO: Some version constraint might be required package_names=' dev-games/godot' ;; ('firefox') package_names=' || ( www-client/firefox www-client/firefox-bin )' ;; ('java') package_names=' virtual/jre' ;; ('julius') ## Warning: The command provided on Gentoo is "julius-game", not "julius". package_names=' games-strategy/julius' ;; ('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' ;; ('pidwait') package_names=' sys-process/procps' ;; ('pulseaudio') package_names=' media-sound/pulseaudio' ;; ('python3') package_names=' dev-lang/python' ;; ('renpy') package_names=' games-engines/renpy' ;; ('scummvm') package_names=' games-engines/scummvm[mp3,truetype,opengl,vorbis,theora]' ;; ('sed') package_names=' sys-apps/sed' ;; ('setxkbmap') package_names=' x11-apps/setxkbmap' ;; ('terminal_wrapper') package_names=' x11-terms/xterm' ;; ('vcmilauncher') dependency_gentoo_overlays_add 'https://cgit.gentoo.org/proj/gamerlay.git' package_names=' games-strategy/vcmi' ;; ('wine') local package package_architecture package=$(current_package) 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') package_names=' app-emulation/winetricks' ;; ('xgamma') package_names=' x11-apps/xgamma' ;; ('xrandr') package_names=' x11-apps/xrandr' ;; (*) dependencies_unknown_command_add "$dependency_command" return 0 ;; esac printf '%s\n' "$package_names" } src/60_system_gentoo/25_dependencies_gstreamer.sh0000644000000000000000000001022113120060140021067 0ustar rootroot# Print Gentoo packages providing given GStreamer decoders list. # USAGE: dependencies_gstreamer_list_gentoo $dependencies_gstreamer dependencies_gstreamer_list_gentoo() { local dependencies_gstreamer dependencies_gstreamer="$1" local package package_architecture dependency_gstreamer package=$(current_package) package_architecture=$(package_architecture "$package") while read -r dependency_gstreamer; do case "$package_architecture" in ('32') dependencies_gstreamer_single_gentoo_32bit "$dependency_gstreamer" ;; (*) dependencies_gstreamer_single_gentoo "$dependency_gstreamer" ;; esac done <<- EOL $(printf '%s' "$dependencies_gstreamer") EOL } # Print Gentoo packages providing given single GStreamer decoder. # USAGE: dependencies_gstreamer_single_gentoo $dependency_gstreamer dependencies_gstreamer_single_gentoo() { local dependency_gstreamer dependency_gstreamer="$1" local package_names case "$dependency_gstreamer" in ('audioconvert') package_names=' media-libs/gst-plugins-base' ;; ('avidemux') package_names=' media-libs/gst-plugins-good' ;; ('decodebin') package_names=' media-libs/gst-plugins-base' ;; ('deinterlace') package_names=' media-libs/gst-plugins-good' ;; ('application/x-id3') package_names=' media-libs/gst-plugins-good' ;; ('audio/mpeg, mpegversion=(int)1, layer=(int)3') package_names=' media-libs/gst-plugins-good' ;; ( \ 'audio/x-wma' | \ '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' | \ 'video/x-wmv, wmvversion=(int)1' \ ) package_names=' media-plugins/gst-plugins-libav' ;; (*) dependencies_unknown_gstreamer_media_formats_add "$dependency_gstreamer" return 0 ;; esac printf '%s\n' "$package_names" } # Print 32-bit Gentoo packages providing given single GStreamer decoder. # USAGE: dependencies_gstreamer_single_gentoo_32bit $dependency_gstreamer dependencies_gstreamer_single_gentoo_32bit() { local dependency_gstreamer dependency_gstreamer="$1" local package_names case "$dependency_gstreamer" in ('audioconvert') package_names=' media-libs/gst-plugins-base[abi_x86_32]' ;; ('avidemux') package_names=' media-libs/gst-plugins-good[abi_x86_32]' ;; ('decodebin') package_names=' media-libs/gst-plugins-base[abi_x86_32]' ;; ('deinterlace') package_names=' media-libs/gst-plugins-good[abi_x86_32]' ;; ('application/x-id3') package_names=' media-libs/gst-plugins-good[abi_x86_32]' ;; ('audio/mpeg, mpegversion=(int)1, layer=(int)3') package_names=' media-libs/gst-plugins-good[abi_x86_32]' ;; ( \ 'audio/x-wma' | \ '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' | \ 'video/x-wmv, wmvversion=(int)1' \ ) package_names=' media-plugins/gst-plugins-libav[abi_x86_32]' ;; (*) dependencies_unknown_gstreamer_media_formats_add "$dependency_gstreamer" return 0 ;; esac printf '%s\n' "$package_names" } src/60_system_gentoo/25_dependencies_legacy.sh0000644000000000000000000000056013120060140020347 0ustar rootroot# Print Gentoo packages providing given packages list, set using legacy dependencies system. # USAGE: dependencies_legacy_list_gentoo $dependencies_legacy dependencies_legacy_list_gentoo() { local dependencies_legacy dependencies_legacy="$1" # This now only supports sibling dependencies for non-updated scripts printf 'games-playit/%s\n' $dependencies_legacy } src/60_system_gentoo/25_dependencies_libraries.sh0000644000000000000000000007051713120060140021070 0ustar rootroot# Print Gentoo packages providing given native libraries. # USAGE: dependencies_libraries_list_gentoo $dependencies_libraries dependencies_libraries_list_gentoo() { local dependencies_libraries dependencies_libraries="$1" local package package_architecture library package=$(current_package) package_architecture=$(package_architecture "$package") while read -r library; do case "$package_architecture" in ('32') dependencies_library_single_gentoo_32bit "$library" ;; (*) dependencies_library_single_gentoo "$library" ;; esac done <<- EOL $(printf '%s' "$dependencies_libraries") EOL } # Gentoo - Print the package name providing the given native library # USAGE: dependencies_library_single_gentoo $dependency_library dependencies_library_single_gentoo() { local dependency_library dependency_library="$1" local package_name case "$dependency_library" in ('alleg-alsadigi.so') package_name='media-libs/allegro' ;; ('alleg-alsamidi.so') package_name='media-libs/allegro' ;; ('ld-linux.so.2') package_name='sys-libs/glibc' ;; ('ld-linux-x86-64.so.2') package_name='sys-libs/glibc' ;; ('libaldmb.so.1') # This library is not provided for Gentoo unset package_name ;; ('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' ;; ('libatk-bridge-2.0.so.0') package_name='app-accessibility/at-spi2-core' ;; ('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' ;; ('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' ;; ('libcrypto.so.3') package_name='dev-libs/openssl' ;; ('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' local pkg_overlay package pkg_overlay='steam-overlay' package=$(current_package) dependencies_gentoo_link 'libcurl-gnutls.so.4' "/usr/$(dependency_gentoo_libdir 'amd64')/debiancompat" "$package" ;; ('libdbus-1.so.3') package_name='sys-apps/dbus' ;; ('libdbus-glib-1.so.2') package_name='dev-libs/dbus-glib' ;; ('libdl.so.2') package_name='sys-libs/glibc' ;; ('libdrm.so.2') package_name='x11-libs/libdrm' ;; ('libdumb.so.1') # This library is not provided for Gentoo unset package_name ;; ('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' ;; ('libgbm.so.1') package_name='media-libs/mesa' ;; ('libgcc_s.so.1') package_name='sys-devel/gcc' ;; ('libgconf-2.so.4') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/gconf/ return 0 ;; ('libgcrypt.so.11') package_name='dev-libs/libgcrypt-compat' ;; ('libgdiplus.so') package_name='dev-dotnet/libgdiplus' ;; ('libgdk-3.so.0') package_name='x11-libs/gtk+:3' ;; ('libgdk_pixbuf-2.0.so.0') package_name='x11-libs/gdk-pixbuf:2' ;; ('libgdk-x11-2.0.so.0') package_name='x11-libs/gtk+:2' ;; ('libgio-2.0.so.0') package_name='dev-libs/glib:2' ;; ('libGL.so.1') package_name='virtual/opengl' ;; ('libGLEW.so.2.2') package_name='media-libs/glew' ;; ('libglfw.so.3') package_name='media-libs/glfw' ;; ('libglib-2.0.so.0') package_name='dev-libs/glib:2' ;; ('libGLU.so.1') package_name='virtual/glu' ;; ('libGLX.so.0') package_name='media-libs/libglvnd' ;; ('libgmodule-2.0.so.0') package_name='dev-libs/glib:2' ;; ('libgobject-2.0.so.0') package_name='dev-libs/glib:2' ;; ('libgomp.so.1') package_name='sys-devel/gcc' ;; ('libgpg-error.so.0') package_name='dev-libs/libgpg-error' ;; ('libgssapi_krb5.so.2') package_name='app-crypt/mit-krb5' ;; ('libgthread-2.0.so.0') package_name='dev-libs/glib:2' ;; ('libgtk-x11-2.0.so.0') package_name='x11-libs/gtk+:2' ;; ('libgtk-3.so.0') package_name='x11-libs/gtk+:3' ;; ('libICE.so.6') package_name='x11-libs/libICE' ;; ('libidn.so.11') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/libidn/ return 0 ;; ('libidn2.so.0') package_name='net-dns/libidn2' ;; ('libIL.so.1') package_name='media-libs/devil' ;; ('libjpeg.so.62') package_name='media-libs/libjpeg-turbo' ;; ('libk5crypto.so.3') package_name='app-crypt/mit-krb5' ;; ('libkrb5.so.3') package_name='app-crypt/mit-krb5' ;; ('liblcms2.so.2') package_name='media-libs/lcms' ;; ('liblua5.3.so.0') package_name='dev-lang/lua' ;; ('libluajit-5.1.so.2') package_name='dev-lang/luajit' ;; ('liblz4.so.1') package_name='app-arch/lz4' ;; ('libm.so.6') package_name='sys-libs/glibc' ;; ('libmbedtls.so.12') package_name='net-libs/mbedtls:0/12' ;; ('libminizip.so.1') package_name='sys-libs/zlib' ;; ('libmodplug.so.1') package_name='media-libs/libmodplug' ;; ('libmpg123.so.0') package_name='media-sound/mpg123' ;; ('libnghttp2.so.14') package_name='net-libs/nghttp2' ;; ('libnotify.so.4') package_name='x11-libs/libnotify' ;; ('libnspr4.so') package_name='dev-libs/nspr' ;; ('libnss3.so') package_name='dev-libs/nss' ;; ('libnssutil3.so') package_name='dev-libs/nss' ;; ('libogg.so.0') package_name='media-libs/libogg' ;; ('libopenal.so.1') package_name='media-libs/openal' ;; ('libOpenGL.so.0') package_name='media-libs/libglvnd' ;; ('libopenmpt.so.0') package_name='media-libs/libopenmpt' ;; ('libpango-1.0.so.0') package_name='x11-libs/pango' ;; ('libpangocairo-1.0.so.0') package_name='x11-libs/pango' ;; ('libpangoft2-1.0.so.0') package_name='x11-libs/pango' ;; ('libpcre.so.3') package_name='dev-libs/libpcre-debian' ;; ('libphysfs.so.1') package_name='dev-games/physfs' ;; ('libpixman-1.so.0') package_name='x11-libs/pixman' ;; ('libplc4.so') package_name='dev-libs/nspr' ;; ('libplds4.so') package_name='dev-libs/nspr' ;; ('libpng12.so.0') package_name='media-libs/libpng-compat:1.2' ;; ('libpng16.so.16') package_name='media-libs/libpng:0/16' ;; ('libpsl.so.5') package_name='net-libs/libpsl' ;; ('libpthread.so.0') package_name='sys-libs/glibc' ;; ('libpulse.so.0') package_name='media-sound/pulseaudio' ;; ('libpulse-simple.so.0') package_name='media-sound/pulseaudio' ;; ('libQt5Core.so.5') package_name='dev-qt/qtcore' ;; ('libQt5Gui.so.5') package_name='dev-qt/qtgui' ;; ('libQt5Widgets.so.5') package_name='dev-qt/qtwidgets' ;; ('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_net-2.0.so.0') package_name='media-libs/sdl2-net' ;; ('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' ;; ('libssl.so.3') package_name='dev-libs/openssl' ;; ('libssl3.so') package_name='dev-libs/nss' ;; ('libstdc++.so.5') package_name='sys-libs/libstdc++-v3' ;; ('libstdc++.so.6') package_name='sys-devel/gcc' ;; ('libtcmalloc_minimal.so.4') package_name='dev-util/google-perftools' ;; ('libtheora.so.0') package_name='media-libs/libtheora' ;; ('libtheoradec.so.1') package_name='media-libs/libtheora' ;; ('libtheoraenc.so.1') package_name='media-libs/libtheora' ;; ('libthread_db.so.1') package_name='sys-libs/glibc' ;; ('libtiff.so.6') package_name='media-libs/tiff' ;; ('libturbojpeg.so.0') package_name='media-libs/libjpeg-turbo' ;; ('libudev.so.0') package_name='sys-libs/libudev-compat' ;; ('libudev.so.1') package_name='virtual/libudev' ;; ('libutil.so.1') package_name='sys-libs/glibc' ;; ('libuuid.so.1') package_name='sys-apps/util-linux' ;; ('libuv.so.1') package_name='dev-libs/libuv:0/1' ;; ('libvorbis.so.0') package_name='media-libs/libvorbis' ;; ('libvorbisenc.so.2') package_name='media-libs/libvorbis' ;; ('libvorbisfile.so.3') package_name='media-libs/libvorbis' ;; ('libvulkan.so.1') package_name='media-libs/vulkan-loader' ;; ('libwayland-client.so.0') package_name='dev-libs/wayland' ;; ('libX11.so.6') package_name='x11-libs/libX11' ;; ('libX11-xcb.so.1') package_name='x11-libs/libX11' ;; ('libxcb.so.1') package_name='x11-libs/libxcb' ;; ('libxcb-randr.so.0') package_name='x11-libs/libxcb' ;; ('libXcomposite.so.1') package_name='x11-libs/libXcomposite' ;; ('libXcursor.so.1') package_name='x11-libs/libXcursor' ;; ('libXdamage.so.1') package_name='x11-libs/libXdamage' ;; ('libXext.so.6') package_name='x11-libs/libXext' ;; ('libXfixes.so.3') package_name='x11-libs/libXfixes' ;; ('libXft.so.2') package_name='x11-libs/libXft' ;; ('libXi.so.6') package_name='x11-libs/libXi' ;; ('libXinerama.so.1') package_name='x11-libs/libXinerama' ;; ('libxkbcommon.so.0') package_name='x11-libs/libxkbcommon' ;; ('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\n' "$package_name" if [ -n "${pkg_overlay:-}" ]; then dependency_gentoo_overlays_add "$pkg_overlay" fi return 0 fi dependencies_unknown_libraries_add "$dependency_library" } # Print 32-bit Gentoo packages providing given single native library. # USAGE: dependencies_library_single_gentoo_32bit $dependency_library dependencies_library_single_gentoo_32bit() { local dependency_library dependency_library="$1" local package_name case "$dependency_library" in ('alleg-alsadigi.so') package_name='media-libs/allegro[abi_x86_32]' ;; ('alleg-alsamidi.so') package_name='media-libs/allegro[abi_x86_32]' ;; ('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] )' ;; ('libaldmb.so.1') # This library is not provided for Gentoo unset package_name ;; ('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]' ;; ('libatk-bridge-2.0.so.0') package_name='app-accessibility/at-spi2-core[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]' ;; ('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]' ;; ('libcrypto.so.3') package_name='dev-libs/openssl[abi_x86_32]' ;; ('libcups.so.2') package_name='net-print/cups[abi_x86_32]' ;; ('libcurl.so.4') package_name='net-misc/curl[abi_x86_32]' ;; ('libcurl.so.4+CURL_OPENSSL_3') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/curl/ return 0 ;; ('libcurl-gnutls.so.4') package_name='net-libs/libcurl-debian[abi_x86_32]' pkg_overlay='steam-overlay' dependencies_gentoo_link 'libcurl-gnutls.so.4' "/usr/$(dependency_gentoo_libdir 'x86')/debiancompat" "$package" ;; ('libdbus-1.so.3') package_name='sys-apps/dbus[abi_x86_32]' ;; ('libdbus-glib-1.so.2') package_name='dev-libs/dbus-glib[abi_x86_32]' ;; ('libdl.so.2') package_name='sys-libs/glibc amd64? ( sys-libs/glibc[multilib] )' ;; ('libdrm.so.2') package_name='x11-libs/libdrm[abi_x86_32]' ;; ('libdumb.so.1') # This library is not provided for Gentoo unset package_name ;; ('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]' ;; ('libgbm.so.1') package_name='media-libs/mesa[abi_x86_32]' ;; ('libgcc_s.so.1') package_name='sys-devel/gcc[abi_x86_32]' ;; ('libgconf-2.so.4') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/gconf/ return 0 ;; ('libgcrypt.so.11') package_name='dev-libs/libgcrypt-compat[abi_x86_32]' ;; ('libgdiplus.so') package_name='dev-dotnet/libgdiplus[abi_x86_32]' ;; ('libgdk-3.so.0') package_name='x11-libs/gtk+:3[abi_x86_32]' ;; ('libgdk_pixbuf-2.0.so.0') package_name='x11-libs/gdk-pixbuf:2[abi_x86_32]' ;; ('libgdk-x11-2.0.so.0') package_name='x11-libs/gtk+:2[abi_x86_32]' ;; ('libgio-2.0.so.0') package_name='dev-libs/glib:2[abi_x86_32]' ;; ('libGL.so.1') package_name='virtual/opengl[abi_x86_32]' ;; ('libGLEW.so.2.2') package_name='media-libs/glew[abi_x86_32]' ;; ('libglfw.so.3') package_name='media-libs/glfw[abi_x86_32]' ;; ('libglib-2.0.so.0') package_name='dev-libs/glib:2[abi_x86_32]' ;; ('libGLU.so.1') package_name='virtual/glu[abi_x86_32]' ;; ('libGLX.so.0') package_name='media-libs/libglvnd[abi_x86_32]' ;; ('libgmodule-2.0.so.0') package_name='dev-libs/glib:2[abi_x86_32]' ;; ('libgobject-2.0.so.0') package_name='dev-libs/glib:2[abi_x86_32]' ;; ('libgomp.so.1') package_name='sys-devel/gcc[abi_x86_32]' ;; ('libgpg-error.so.0') package_name='dev-libs/libgpg-error[abi_x86_32]' ;; ('libgssapi_krb5.so.2') package_name='app-crypt/mit-krb5[abi_x86_32]' ;; ('libgthread-2.0.so.0') package_name='dev-libs/glib:2[abi_x86_32]' ;; ('libgtk-x11-2.0.so.0') package_name='x11-libs/gtk+:2[abi_x86_32]' ;; ('libgtk-3.so.0') package_name='x11-libs/gtk+:3[abi_x86_32]' ;; ('libICE.so.6') package_name='x11-libs/libICE[abi_x86_32]' ;; ('libidn.so.11') # This native library is provided by an extra archive: # https://downloads.dotslashplay.it/resources/libidn/ return 0 ;; ('libidn2.so.0') package_name='net-dns/libidn2[abi_x86_32]' ;; ('libIL.so.1') package_name='media-libs/devil[abi_x86_32]' ;; ('libjpeg.so.62') package_name='media-libs/libjpeg-turbo[abi_x86_32]' ;; ('libk5crypto.so.3') package_name='app-crypt/mit-krb5[abi_x86_32]' ;; ('libkrb5.so.3') package_name='app-crypt/mit-krb5[abi_x86_32]' ;; ('liblcms2.so.2') package_name='media-libs/lcms[abi_x86_32]' ;; ('liblua5.3.so.0') package_name='dev-lang/lua[abi_x86_32]' ;; ('libluajit-5.1.so.2') package_name='dev-lang/luajit[abi_x86_32]' ;; ('liblz4.so.1') package_name='app-arch/lz4[abi_x86_32]' ;; ('libm.so.6') package_name='sys-libs/glibc amd64? ( sys-libs/glibc[multilib] )' ;; ('libmbedtls.so.12') package_name='net-libs/mbedtls:0/12[abi_x86_32]' ;; ('libminizip.so.1') package_name='sys-libs/zlib[abi_x86_32]' ;; ('libmodplug.so.1') package_name='media-libs/libmodplug[abi_x86_32]' ;; ('libmpg123.so.0') package_name='media-sound/mpg123[abi_x86_32]' ;; ('libnghttp2.so.14') package_name='net-libs/nghttp2[abi_x86_32]' ;; ('libnotify.so.4') package_name='x11-libs/libnotify[abi_x86_32]' ;; ('libnspr4.so') package_name='dev-libs/nspr[abi_x86_32]' ;; ('libnss3.so') package_name='dev-libs/nss[abi_x86_32]' ;; ('libnssutil3.so') package_name='dev-libs/nss[abi_x86_32]' ;; ('libogg.so.0') package_name='media-libs/libogg[abi_x86_32]' ;; ('libopenal.so.1') package_name='media-libs/openal[abi_x86_32]' ;; ('libOpenGL.so.0') package_name='media-libs/libglvnd[abi_x86_32]' ;; ('libopenmpt.so.0') package_name='media-libs/libopenmpt[abi_x86_32]' ;; ('libpango-1.0.so.0') package_name='x11-libs/pango[abi_x86_32]' ;; ('libpangocairo-1.0.so.0') package_name='x11-libs/pango[abi_x86_32]' ;; ('libpangoft2-1.0.so.0') package_name='x11-libs/pango[abi_x86_32]' ;; ('libpcre.so.3') package_name='dev-libs/libpcre-debian[abi_x86_32]' ;; ('libphysfs.so.1') package_name='dev-games/physfs[abi_x86_32]' ;; ('libpixman-1.so.0') package_name='x11-libs/pixman[abi_x86_32]' ;; ('libplc4.so') package_name='dev-libs/nspr[abi_x86_32]' ;; ('libplds4.so') package_name='dev-libs/nspr[abi_x86_32]' ;; ('libpng12.so.0') package_name='media-libs/libpng-compat:1.2[abi_x86_32]' ;; ('libpng16.so.16') package_name='media-libs/libpng:0/16[abi_x86_32]' ;; ('libpsl.so.5') package_name='net-libs/libpsl[abi_x86_32]' ;; ('libpthread.so.0') package_name='sys-libs/glibc amd64? ( sys-libs/glibc[multilib] )' ;; ('libpulse.so.0') package_name='media-sound/pulseaudio[abi_x86_32]' ;; ('libpulse-simple.so.0') package_name='media-sound/pulseaudio[abi_x86_32]' ;; ('libQt5Core.so.5') package_name='dev-qt/qtcore[abi_x86_32]' ;; ('libQt5Gui.so.5') package_name='dev-qt/qtgui[abi_x86_32]' ;; ('libQt5Widgets.so.5') package_name='dev-qt/qtwidgets[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_net-2.0.so.0') package_name='media-libs/sdl2-net[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]' ;; ('libssl.so.3') package_name='dev-libs/openssl[abi_x86_32]' ;; ('libssl3.so') package_name='dev-libs/nss[abi_x86_32]' ;; ('libstdc++.so.5') package_name='sys-libs/libstdc++-v3[abi_x86_32]' ;; ('libstdc++.so.6') package_name='sys-devel/gcc amd64? ( sys-devel/gcc[multilib] )' ;; ('libtcmalloc_minimal.so.4') package_name='dev-util/google-perftools[abi_x86_32]' ;; ('libtheora.so.0') package_name='media-libs/libtheora[abi_x86_32]' ;; ('libtheoradec.so.1') package_name='media-libs/libtheora[abi_x86_32]' ;; ('libtheoraenc.so.1') package_name='media-libs/libtheora[abi_x86_32]' ;; ('libthread_db.so.1') package_name='sys-libs/glibc amd64? ( sys-libs/glibc[multilib] )' ;; ('libtiff.so.6') package_name='media-libs/tiff[abi_x86_32]' ;; ('libturbojpeg.so.0') package_name='media-libs/libjpeg-turbo[abi_x86_32]' ;; ('libudev.so.0') package_name='sys-libs/libudev-compat[abi_x86_32]' ;; ('libudev.so.1') package_name='virtual/libudev[abi_x86_32]' ;; ('libutil.so.1') package_name='sys-libs/glibc amd64? ( sys-libs/glibc[multilib] )' ;; ('libuuid.so.1') package_name='sys-apps/util-linux[abi_x86_32]' ;; ('libuv.so.1') package_name='dev-libs/libuv:0/1[abi_x86_32]' ;; ('libvorbis.so.0') package_name='media-libs/libvorbis[abi_x86_32]' ;; ('libvorbisenc.so.2') package_name='media-libs/libvorbis[abi_x86_32]' ;; ('libvorbisfile.so.3') package_name='media-libs/libvorbis[abi_x86_32]' ;; ('libvulkan.so.1') package_name='media-libs/vulkan-loader[abi_x86_32]' ;; ('libwayland-client.so.0') package_name='dev-libs/wayland[abi_x86_32]' ;; ('libX11.so.6') package_name='x11-libs/libX11[abi_x86_32]' ;; ('libX11-xcb.so.1') package_name='x11-libs/libX11[abi_x86_32]' ;; ('libxcb.so.1') package_name='x11-libs/libxcb[abi_x86_32]' ;; ('libxcb-randr.so.0') package_name='x11-libs/libxcb[abi_x86_32]' ;; ('libXcomposite.so.1') package_name='x11-libs/libXcomposite[abi_x86_32]' ;; ('libXcursor.so.1') package_name='x11-libs/libXcursor[abi_x86_32]' ;; ('libXdamage.so.1') package_name='x11-libs/libXdamage[abi_x86_32]' ;; ('libXext.so.6') package_name='x11-libs/libXext[abi_x86_32]' ;; ('libXfixes.so.3') package_name='x11-libs/libXfixes[abi_x86_32]' ;; ('libXft.so.2') package_name='x11-libs/libXft[abi_x86_32]' ;; ('libXi.so.6') package_name='x11-libs/libXi[abi_x86_32]' ;; ('libXinerama.so.1') package_name='x11-libs/libXinerama[abi_x86_32]' ;; ('libxkbcommon.so.0') package_name='x11-libs/libxkbcommon[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\n' "$package_name" if [ -n "${pkg_overlay:-}" ]; then dependency_gentoo_overlays_add "$pkg_overlay" fi return 0 fi dependencies_unknown_libraries_add "$dependency_library" } src/60_system_gentoo/25_dependencies_mono.sh0000644000000000000000000000040613120060140020052 0ustar rootroot# Print Gentoo packages providing given Mono libraries list. # USAGE: dependencies_mono_list_gentoo $dependencies_mono dependencies_mono_list_gentoo() { # Gentoo provides all Mono libraries in a single "dev-lang/mono" package. printf '%s\n' 'dev-lang/mono' } src/60_system_gentoo/25_dependencies_siblings.sh0000644000000000000000000000133613120060140020717 0ustar rootroot# Print Gentoo packages providing sibling packages list. # USAGE: dependencies_siblings_list_gentoo $dependencies_siblings dependencies_siblings_list_gentoo() { local dependencies_siblings dependencies_siblings="$1" local dependency_sibling while read -r dependency_sibling; do dependencies_sibling_single_gentoo "$dependency_sibling" done <<- EOL $(printf '%s' "$dependencies_siblings") EOL } # Print Gentoo package providing given single sibling package. # USAGE: dependencies_sibling_single_gentoo $dependency_sibling dependencies_sibling_single_gentoo() { local dependency_sibling dependency_sibling="$1" local package_id package_id=$(package_id "$dependency_sibling") printf 'games-playit/%s\n' "$package_id" } src/60_system_gentoo/90_messages.sh0000644000000000000000000000506313120060140016211 0ustar rootroot# print notification about required overlays when building Gentoo packages # USAGE: information_required_gentoo_overlays $overlays information_required_gentoo_overlays() { local overlays_file overlays_file=$(dependency_gentoo_overlays_file) local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Vous pourriez avoir besoin des overlays suivants pour installer ces paquets :' ;; ('en'|*) message='You may need the following overlays to install these packages:' ;; esac print_message 'info' '\n%s\n' \ "$message" print_message 'info' '\t%s\n' \ "$(cat "$overlays_file")" } # add comment to packages installation instructions on Gentoo # USAGE: information_installation_instructions_gentoo_comment information_installation_instructions_gentoo_comment() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='ou mettez les paquets dans un PKGDIR (dans un dossier nommé games-playit) et emergez-les avec FEATURES="-pkgdir-index-trusted -binpkg-request-signature"' ;; ('en'|*) message='or put the packages in a PKGDIR (in a folder named games-playit) and emerge them with FEATURES="-pkgdir-index-trusted -binpkg-request-signature"' ;; esac print_message 'info' "$message" } # inform the need of a local overlay on gentoo for ebuilds # USAGE: info_local_overlay_gentoo info_local_overlay_gentoo() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='\nUn overlay local est nécessaire pour utiliser les ebuilds générés par ./play.it\n' message="$message"'Dans la suite OVERLAY_PATH correspond au chemin de votre overlay local\n' ;; ('en'|*) message='\nA local overlay is needed to use the ebuilds generated by ./play.it\n' message="$message"'In what comes next, OVERLAY_PATH is the path to your local overlay\n' ;; esac print_message 'info' "$message" } # Error - An unknown architecture string is used # USAGE: error_unknown_gentoo_architecture_string $arch_string $caller error_unknown_gentoo_architecture_string() { local arch_string caller arch_string="$1" caller="$2" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Lʼarchitecture « %s », utilisée dans %s, est inconnue sous Gentoo.\n' ;; ('en'|*) message='“%s” architecture, used in %s, is unknown on Gentoo.\n' ;; esac print_message 'error' "$message" \ "$arch_string" \ "$caller" } src/70_deprecation/90_messages.sh0000644000000000000000000001023213120060140015602 0ustar rootroot# Warning: A deprecated function has been called. # USAGE: warning_deprecated_function $old_function $new_function warning_deprecated_function() { local old_function new_function old_function="$1" new_function="$2" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La fonction suivante est dépréciée : %s\n' message="$message"'Cette nouvelle fonction devrait être utilisée à sa place : %s\n\n' ;; ('en'|*) message='The following function is deprecated: %s\n' message="$message"'This new function should be used instead: %s\n\n' ;; esac # Print the message on the standard error output, # to avoid messing up the regular output of the function that triggered this warning. print_message 'warning_once' "$message" \ "$old_function" \ "$new_function" \ > /dev/stderr } # Warning: A deprecated variable is set. # USAGE: warning_deprecated_variable $old_variable $new_variable warning_deprecated_variable() { local old_variable new_variable old_variable="$1" new_variable="$2" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La variable suivante est dépréciée : %s\n' message="$message"'Cette nouvelle variable devrait être utilisée à sa place : %s\n\n' ;; ('en'|*) message='The following variable is deprecated: %s\n' message="$message"'This new variable should be used instead: %s\n\n' ;; esac # Print the message on the standard error output, # to avoid messing up the regular output of the variable that triggered this warning. print_message 'warning_once' "$message" \ "$old_variable" \ "$new_variable" \ > /dev/stderr } # Warning: An option is set to a deprecated value. # USAGE: warning_option_value_deprecated $option_name $option_value warning_option_value_deprecated() { local option_name option_value option_name="$1" option_value="$2" local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La valeur suivante est dépréciée pour lʼoption "%s", et ne sera plus acceptée dans une future version : "%s"\n\n' ;; ('en'|*) message='The following value is deprecated for option "%s", and will no longer be supported with some future update: "%s"\n\n' ;; esac # Print the message on the standard error output, # to avoid messing up the regular output of the variable that triggered this warning. print_message 'warning_once' "$message" \ "$option_name" \ "$option_value" \ > /dev/stderr } # Warning: An archive has a deprecated type set. # USAGE: warning_archive_type_deprecated $archive warning_archive_type_deprecated() { local archive archive="$1" local archive_type game_name archive_type=$(archive_type "$archive") game_name=$(game_name) local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='La prise en charge de "%s" utilise un type dʼarchive déprécié : %s\n\n' ;; ('en'|*) message='Support for "%s" is using an obsolete archive type: %s\n\n' ;; esac # Print the message on the standard error output, # to avoid messing up the regular output of the variable that triggered this warning. print_message 'warning_once' "$message" \ "$game_name" \ "$archive_type" \ > /dev/stderr } # Warning - The legacy variable has been use to set the current package. # USAGE: warning_context_legacy_package warning_context_legacy_package() { local messages_language message messages_language=$(messages_language) case "$messages_language" in ('fr') message='Le paquet actuel a été défini en utilisant la variable dépréciée $PKG.\n' message="$message"'La fonction set_current_package devrait être utilisée à sa place.\n' ;; ('en'|*) message='The current package has been set using the deprecated variable $PKG.\n' message="$message"'The function set_current_package should be used instead.\n' ;; esac # Print the message on the standard error output, # to avoid messing up the regular output of the function that triggered this warning. print_message 'warning_once' "$message" > /dev/stderr } src/90_compatibility/80_2.32-and-older.sh0000644000000000000000000001220113120060140016575 0ustar rootroot# Keep compatibility with 2.32 and older hacks_list() { local hacks_list hacks_list=$(context_value 'PRELOAD_HACKS_LIST') if [ -z "$hacks_list" ]; then return 0 fi if compatibility_level_is_at_least '2.33'; then warning_deprecated_variable 'PRELOAD_HACKS_LIST' 'LD_PRELOAD_SOURCE' fi local hack for hack in $hacks_list; do printf '%s\n' "$hack" done } 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" } 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" } 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" } 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" } hack_build() { local hack hack="$1" # Write the source file local hack_path_source hack_path_source=$(hack_path_source "$hack") mkdir --parents "${PLAYIT_WORKDIR}/hacks" hack_source "$hack" > "$hack_path_source" # Prepare the compiler options string local gcc_options gcc_options='-shared -Wall -fPIC -ldl' local hack_package hack_package_architecture hack_package=$(hack_package "$hack") hack_package_architecture=$(package_architecture "$hack_package") if [ "$hack_package_architecture" = '32' ]; then gcc_options="$gcc_options -m32" fi # Build the .so library local hack_path_library hack_build_status hack_path_library=$(hack_path_library "$hack") { gcc $gcc_options "$hack_path_source" -o "$hack_path_library" hack_build_status=$? } || true if [ "$hack_build_status" -ne 0 ]; then error_hack_build_failure return 1 fi rm "$hack_path_source" } hack_path_source() { local hack hack="$1" local hack_name hack_name=$(hack_name "$hack") printf '%s/hacks/%s.c' "$PLAYIT_WORKDIR" "$hack_name" } hack_path_library() { local hack hack="$1" local hack_name hack_name=$(hack_name "$hack") printf '%s/hacks/%s.so' "$PLAYIT_WORKDIR" "$hack_name" } hacks_inclusion_default() { local packages_list package hacks_list hack packages_list=$(packages_list) for package in $packages_list; do hacks_list=$(hacks_included_in_package "$package") if [ -z "$hacks_list" ]; then continue fi for hack in $hacks_list; do hack_build "$hack" ( set_current_package "$package" hack_inclusion "$hack" ) done done } hack_inclusion() { local hack hack="$1" local hack_path_library hack_path_library=$(hack_path_library "$hack") if [ ! -f "$hack_path_library" ]; then 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" } 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 } 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 } prefix_generate_links_farm() { if compatibility_level_is_at_least '3.33'; then warning_deprecated_function 'prefix_generate_links_farm' 'prefix_symlinks_generate' fi prefix_symlinks_generate } launcher_prefix_symlinks_build() { if compatibility_level_is_at_least '3.33'; then warning_deprecated_function 'launcher_prefix_symlinks_build' 'prefix_symlinks_generate' fi # This compatibility wrapper does nothing, as it is always called alongside prefix_generate_links_farm. } dependencies_list_native_libraries() { if compatibility_level_is_at_least '2.32'; then warning_deprecated_function 'dependencies_list_native_libraries' 'dependencies_libraries_list' fi dependencies_libraries_list "$@" } src/90_compatibility/82_2.27-and-older.sh0000644000000000000000000000077213120060140016615 0ustar rootroot# Keep compatibility with 2.27 and older icons_inclusion() { if compatibility_level_is_at_least '2.28'; then warning_deprecated_function 'icons_inclusion' 'content_inclusion_icons' fi local package package=$(current_package) content_inclusion_icons "$package" "$@" } launchers_write() { if compatibility_level_is_at_least '2.28'; then warning_deprecated_function 'launchers_write' 'launchers_generation' fi local package package=$(current_package) launchers_generation "$package" "$@" } src/90_compatibility/85_2.23-and-older.sh0000644000000000000000000000222313120060140016605 0ustar rootroot# Keep compatibility with 2.23 and older 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 "$@" ;; 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 "$@" ;; esac } src/99_init/00_compatibility-level.sh0000644000000000000000000000177013120060140016430 0ustar rootroot# Check the expected compatibility level against the current library version # USAGE: init_compatibility_level_check init_compatibility_level_check() { local compatibility_level compatibility_level_major library_version_major 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 local compatibility_level_minor library_version_minor 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 } src/99_init/00_environment-variables.sh0000644000000000000000000000042713120060140016762 0ustar rootroot# Set/unset some environment variables # USAGE: init_environment init_environment() { # Unset variables that we do not want to import from the user environment ## TODO: This should be handled by the functions initializing these variables instead. unset SOURCE_ARCHIVE_PATH } src/99_init/00_root-account-check.sh0000644000000000000000000000115013120060140016132 0ustar rootroot# 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 } # Throw an error is the current process has been called by the root account # USAGE: init_fail_as_root init_fail_as_root() { ## 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 } src/99_init/00_shell-settings.sh0000644000000000000000000000075613120060140015422 0ustar rootroot# Set the options for the current shell # USAGE: init_shell_options init_shell_options() { # Exit immediately on error set -o errexit # Error out (and exit) when trying to expand an unset variable ## Only for game scripts targeting ./play.it ≥ 2.23 if compatibility_level_is_at_least '2.23'; then set -o nounset fi # Set input field separator to default value (space, tab, newline) unset IFS # Force umask to ensure all paths are created with correct permissions umask 0022 } src/99_init/10_options.sh0000644000000000000000000000104013120060140014134 0ustar rootroot# Set the default and current options # USAGE: init_options $command_line_option[…] init_options() { # Set hardcoded defaults options_init_default # Load defaults from the configuration file local config_file_path config_file_path=$(find_configuration_file "$@") load_configuration_file "$config_file_path" # Set options from the command-line parse_arguments "$@" } # Check the validity of current options # USAGE: init_options_validity_check init_options_validity_check() { options_validity_check options_compatibility_check } src/99_init/20_directories.sh0000644000000000000000000000241113120060140014761 0ustar rootroot# Set the path to the temporary directory # This path is exported as $PLAYIT_WORKDIR # USAGE: init_working_directory init_working_directory() { # Do nothing if PLAYIT_WORKDIR is already set if [ -n "${PLAYIT_WORKDIR:-}" ]; then return 0 fi # Get the path to the temporary directory local temporary_directory_path temporary_directory_path=$(option_value 'tmpdir') # Generate a directory with a unique name for the current instance local game_id game_id=$(game_id) ## "play.it" is used instead of the game identifier if we are too early for a game to be set yet. PLAYIT_WORKDIR=$(mktemp --directory --tmpdir="$temporary_directory_path" "${game_id:-play.it}.XXXXX") ## Ensure that we are always using an absolute path for PLAYIT_WORKDIR, ## to avoid problems when a relative path has been used with the ./play.it --tmpdir option. PLAYIT_WORKDIR=$(realpath "$PLAYIT_WORKDIR") export PLAYIT_WORKDIR } # Check the validity of the temporary directory # USAGE: init_working_directory_checks init_working_directory_checks() { # Get the path to the temporary directory local temporary_directory_path temporary_directory_path=$(option_value 'tmpdir') # Check that the given path is valid for temporary files storage temporary_directory_checks "$temporary_directory_path" } src/99_init/20_early-actions.sh0000644000000000000000000001024613120060140015224 0ustar rootroot# If some early action is set, run it then exit # The following switches trigger an early action: # - --help # - --version # - --list-available-scripts # - --list-supported-games # USAGE: init_early_actions init_early_actions() { init_help init_version init_scripts_list init_games_list } # Display the list of available game scripts then exit, if called with --list-available-scripts # USAGE: init_scripts_list init_scripts_list() { local option_list_available_scripts option_list_available_scripts=$(option_value 'list-available-scripts') if [ "${option_list_available_scripts:-0}" -eq 0 ]; then return 0 fi games_list_scripts_all exit 0 } # List supported games then exit, if called with --list-supported-games # USAGE: init_games_list init_games_list() { ## Return early if ./play.it has not been called in games listing mode local option_list_supported_games option_list_supported_games=$(option_value 'list-supported-games') ## If called before the options parsing step, the previous function might have returned an empty string. if [ "${option_list_supported_games:-0}" -eq 0 ]; then return 0 fi ## List the games supported by the current game script, or all games if called from the "play.it" wrapper. local script_name script_name=$(basename "$0") if [ "$script_name" = 'play.it' ]; then games_list_supported_all else games_list_supported fi ## Exit after printing the games listing, a no further action should be taken exit 0 } # Display the help message then exit, if called with --help # USAGE: init_help init_help() { local option_help option_help=$(option_value 'help') if [ "$option_help" -eq 1 ]; then help exit 0 fi } # Display the version string then exit, if called with --version # USAGE: init_version init_version() { local option_version option_version=$(option_value 'version') if [ "$option_version" -eq 1 ]; then ## TODO: The revision number should be appended to play.it --version output. printf '%s\n' "$LIBRARY_VERSION" exit 0 fi } # If some no-op action is set, run it then exit # The following switches trigger a no-op action: # - --list-packages # - --list-requirements # - --show-game-script # USAGE: init_noop_actions init_noop_actions() { init_list_packages init_list_requirements init_show_game_script } # Print the list of packages that would be generated from the current archive then exit, if called with --list-packages # USAGE: init_list_packages init_list_packages() { local option_list_packages option_list_packages=$(option_value 'list-packages') if [ "$option_list_packages" -eq 0 ]; then return 0 fi local archive archive=$(current_archive) packages_print_list "$archive" # Delete temporary files working_directory_cleanup exit 0 } # Print the list of commands required to run the current game script then exit, if called with --list-requirements # USAGE: init_list_requirements init_list_requirements() { local option_list_requirements option_list_requirements=$(option_value 'list-requirements') if [ "$option_list_requirements" -eq 0 ]; then return 0 fi requirements_list # Delete temporary files working_directory_cleanup exit 0 } # Display the path to the game script then exit, if called with --show-game-script # USAGE: init_show_game_script init_show_game_script() { local option_show_game_script option_show_game_script=$(option_value 'show-game-script') if [ "$option_show_game_script" -eq 0 ]; then return 0 fi realpath "$0" # Delete temporary files working_directory_cleanup exit 0 } # Exit early if all packages are already built # USAGE: init_packages_already_built init_packages_already_built() { local option_overwrite option_overwrite=$(option_value 'overwrite') if [ "$option_overwrite" -eq 1 ]; then return 0 fi local option_output_dir archive generated_packages_list option_output_dir=$(option_value 'output-dir') archive=$(current_archive) generated_packages_list=$(packages_print_list "$archive") while read -r generated_package; do if [ ! -e "${option_output_dir}/${generated_package}" ]; then return 0 fi done <<- EOL $(printf '%s' "$generated_packages_list") EOL info_all_packages_already_built # Delete temporary files working_directory_cleanup exit 0 } src/99_init/30_archive.sh0000644000000000000000000001253013120060140014072 0ustar rootroot# Set up the base archive # USAGE: init_archive # RETURN: 0 if the archive is found, # 1 if it is missing init_archive() { 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 # Filter the list of supported archives to keep only the ones with the same name than the provided one local archives_list archive archive_name archive_candidates_list archives_list=$(archives_list) for archive in $archives_list; do archive_name=$(archive_name "$archive") if [ "$archive_name" = "$archive_name_expected" ]; then archive_candidates_list="${archive_candidates_list:-} $archive" fi done # Drop empty lines archive_candidates_list=$(printf '%s' "${archive_candidates_list:-}" | list_clean) # Throw an error if no archive candidate has been found if [ -z "$archive_candidates_list" ]; then error_archive_not_found $archives_list return 1 fi # If there is only one archive candidate, set it as the current archive local archive_candidates_number archive_candidates_number=$(printf '%s\n' "$archive_candidates_list" | wc --lines) if [ "$archive_candidates_number" -eq 1 ]; then init_archive_set_properties "$archive_identifier" "$archive_candidates_list" return 0 fi # If there are multiple archive candidates, discriminate between them using the archive hash local archive_name archive_hash archive_name=$(archive_name "$archive_identifier") archive_hash=$(archive_hash_md5_computed "$archive_name") local archive archive_hashes_list archive_candidate_hash for archive in $archive_candidates_list; do archive_hashes_list=$(archive_hashes_list_md5 "$archive") for archive_candidate_hash in $archive_hashes_list; do if [ "$archive_candidate_hash" = "$archive_hash" ]; then init_archive_set_properties "$archive_identifier" "$archive" return 0 fi done done # Throw an error if no archive candidate has the expected hash error_archive_not_found $archives_list return 1 } # Set the properties of the base archive # USAGE: init_archive_set_properties $archive_identifier $archive_candidate init_archive_set_properties() { local archive_identifier archive_candidate archive_identifier="$1" archive_candidate="$2" local archive_name archive_path archive_name=$(archive_name "$archive_candidate") archive_path=$(archive_path "$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}" # 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 might not be set yet, # and it is required to cache computed hashes. archives_integrity_check must be called later. } # Check for the presence of extra required archives # USAGE: init_extra_archives_required init_extra_archives_required() { if ! archives_required_extra_presence_check; then # Delete temporary files working_directory_cleanup exit 1 fi } # Check for the presence of extra optional archives # USAGE: init_extra_archives_optional init_extra_archives_optional() { # Check for the presence of an optional icons pack local icons_pack_name icons_pack_name=$(context_value 'ARCHIVE_OPTIONAL_ICONS_NAME') if [ -n "$icons_pack_name" ]; then ## Set contextual values, for game scripts with support for multiple games. ARCHIVE_OPTIONAL_ICONS_NAME=$(context_value 'ARCHIVE_OPTIONAL_ICONS_NAME') ARCHIVE_OPTIONAL_ICONS_MD5=$(context_value 'ARCHIVE_OPTIONAL_ICONS_MD5') ARCHIVE_OPTIONAL_ICONS_URL=$(context_value 'ARCHIVE_OPTIONAL_ICONS_URL') export ARCHIVE_OPTIONAL_ICONS_NAME ARCHIVE_OPTIONAL_ICONS_MD5 ARCHIVE_OPTIONAL_ICONS_URL archive_initialize_optional \ 'ARCHIVE_ICONS' \ 'ARCHIVE_OPTIONAL_ICONS' if ! archive_is_available 'ARCHIVE_ICONS'; then warning_optional_archive_missing_icons 'ARCHIVE_OPTIONAL_ICONS' fi fi } # Check the archives integrity # USAGE: init_archives_integrity_check init_archives_integrity_check() { if ! archives_integrity_check; then # Delete temporary files working_directory_cleanup exit 1 fi } src/99_init/30_package.sh0000644000000000000000000000046713120060140014052 0ustar rootroot# Set the default package, used by the context system when no explicit package context is set # USAGE: init_package init_package() { local packages_list default_package packages_list=$(packages_list) default_package=$(printf '%s' "$packages_list" | head --lines=1) set_default_package "$default_package" } src/99_init/50_requirements.sh0000644000000000000000000000056213120060140015200 0ustar rootroot# Check for the presence of required tools # USAGE: init_requirements_check init_requirements_check() { if ! requirements_check; then # Delete temporary files working_directory_cleanup exit 1 fi local archive archive=$(current_archive) if ! archive_dependencies_check "$archive"; then # Delete temporary files working_directory_cleanup exit 1 fi } src/99_init/99_init.sh0000644000000000000000000000376013120060140013440 0ustar rootroot# Run the common initialization process # USAGE: initialization_default $command_line_argument[…] initialization_default() { # Exit if the current process has been spawned by the root user init_fail_as_root # Set the options for the current shell init_shell_options # Check early if ./play.it has been called in games listing mode, since most of the initialization steps are not required in this mode. # This is called before options parsing because the games listing mode might have been set by a parent ./play.it process. init_games_list # Set/unset some environment variables init_environment # Check the expected compatibility level against the current library version init_compatibility_level_check # Set the default and current options init_options "$@" # If some early action is set, run it then exit init_early_actions # Check the validity of current options init_options_validity_check # Set the path to the temporary directory init_working_directory # Set up the base archive init_archive # Check the validity of the temporary directory init_working_directory_checks # Set the default package, used by the context system when no explicit package context is set init_package # If some no-op action is set, run it then exit ## TODO: This could probably be run earlier, as soon as the base archive is set init_noop_actions # Exit early if all packages are already built init_packages_already_built # Check for the presence of required tools init_requirements_check # Check for the presence of extra required archives init_extra_archives_required # Check for the presence of extra optional archives init_extra_archives_optional # Check the archives integrity init_archives_integrity_check } # Automatically run the initialization process when sourcing the library from an old game script # This can be bypassed by setting $LIB_ONLY to any non-null value if \ [ -z "${LIB_ONLY:-}" ] && \ ! compatibility_level_is_at_least '2.31' then initialization_default "$@" fi tests/shunit2/10_common/00_common.sh0000644000000000000000000000500613120060140016211 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . 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_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_file_type() { local file_type file_type=$(file_type 'play.it') assertEquals 'text/x-shellscript' "$file_type" } tests/shunit2/10_common/10_compatibility-level.sh0000644000000000000000000000251213120060140020677 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . lib/libplayit2.sh } test_compatibility_level() { local compatibility_level PLAYIT_COMPATIBILITY_LEVEL target_version LIBRARY_VERSION PLAYIT_COMPATIBILITY_LEVEL='2.22' compatibility_level=$(compatibility_level) assertEquals '2.22' "$compatibility_level" unset PLAYIT_COMPATIBILITY_LEVEL target_version='2.21' compatibility_level=$(compatibility_level) assertEquals '2.21' "$compatibility_level" unset target_version ## The LIBRARY_VERSION value is used as a fallback if no compatibility level is set from the game script. LIBRARY_VERSION='1.42.0' compatibility_level=$(compatibility_level) assertEquals '1.42' "$compatibility_level" unset LIBRARY_VERSION ## Check that errors are thrown on invalid values. PLAYIT_COMPATIBILITY_LEVEL='2' assertFalse 'compatibility_level' PLAYIT_COMPATIBILITY_LEVEL='2.29.0' assertFalse 'compatibility_level' unset PLAYIT_COMPATIBILITY_LEVEL } test_compatibility_level_is_at_least() { local PLAYIT_COMPATIBILITY_LEVEL PLAYIT_COMPATIBILITY_LEVEL='2.21' assertTrue 'compatibility_level_is_at_least "1.0"' assertTrue 'compatibility_level_is_at_least "2.0"' assertTrue 'compatibility_level_is_at_least "2.21"' assertFalse 'compatibility_level_is_at_least "2.42"' assertFalse 'compatibility_level_is_at_least "3.0"' } tests/shunit2/10_common/10_context.sh0000644000000000000000000001477413120060140016422 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . lib/libplayit2.sh } test_set_current_archive() { local PLAYIT_CONTEXT_ARCHIVE set_current_archive 'ARCHIVE_BASE_0' assertEquals 'ARCHIVE_BASE_0' "$PLAYIT_CONTEXT_ARCHIVE" # Check that the format ARCHIVE_BASE_xxx is enforced assertFalse 'set_current_archive "ARCHIVE_GOG_OLD0"' } test_set_current_package() { local PLAYIT_CONTEXT_PACKAGE PACKAGES_LIST set_current_package 'PKG_MAIN' assertEquals 'PKG_MAIN' "$PLAYIT_CONTEXT_PACKAGE" # Check that the format PKG_xxx is enforced assertFalse 'set_current_package "PACKAGE_MAIN"' # An error should be thrown when trying to set a package that is not included in the list of packages to build. PACKAGES_LIST=' PKG_BIN PKG_DATA' assertFalse 'set_current_package "PKG_BIN32"' } test_set_default_package() { local PLAYIT_CONTEXT_PACKAGE_DEFAULT PACKAGES_LIST set_default_package 'PKG_MAIN' assertEquals 'PKG_MAIN' "$PLAYIT_CONTEXT_PACKAGE_DEFAULT" # Check that the format PKG_xxx is enforced assertFalse 'set_default_package "PACKAGE_MAIN"' # An error should be thrown when trying to set a package that is not included in the list of packages to build. PACKAGES_LIST=' PKG_BIN PKG_DATA' assertFalse 'set_default_package "PKG_BIN32"' } test_current_archive() { local current_archive PLAYIT_CONTEXT_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 } test_current_package() { local PACKAGES_LIST PLAYIT_CONTEXT_PACKAGE_DEFAULT PLAYIT_CONTEXT_PACKAGE PLAYIT_COMPATIBILITY_LEVEL PKG assertNull "$(current_package)" PACKAGES_LIST=' PKG_BIN32 PKG_BIN64 PKG_L10N PKG_DATA' set_default_package 'PKG_BIN32' assertEquals 'PKG_BIN32' "$(current_package)" set_current_package 'PKG_BIN64' assertEquals 'PKG_BIN64' "$(current_package)" # Backwards compatibility with compatibility level ≤ 2.25 PLAYIT_COMPATIBILITY_LEVEL='2.25' ## Set the current package using the legacy global variable PKG='PKG_L10N' assertEquals 'PKG_L10N' "$(current_package)" ## Check the priority order between the current context system and the legacy one PKG='PKG_DATA' current_package=$(current_package) assertEquals \ 'current_package ignored the value of $PKG because set_current_package has been used. This will break compatibility with game scripts relying on the ability to set the context using $PKG.' \ 'PKG_DATA' "$(current_package)" } test_default_package() { local PLAYIT_CONTEXT_PACKAGE_DEFAULT assertNull "$(default_package)" set_default_package 'PKG_MAIN' assertEquals 'PKG_MAIN' "$(default_package)" } test_current_archive_suffix() { local PLAYIT_CONTEXT_ARCHIVE set_current_archive 'ARCHIVE_BASE_MULTIARCH_0' assertEquals '_MULTIARCH_0' "$(current_archive_suffix)" } test_current_package_suffix() { local current_package_suffix PLAYIT_CONTEXT_PACKAGE PACKAGES_LIST PACKAGES_LIST=' PKG_BIN32 PKG_BIN64 PKG_DATA' set_current_package 'PKG_BIN32' current_package_suffix=$(current_package_suffix) assertEquals '_BIN32' "$current_package_suffix" } test_context_name() { local \ context_name \ PLAYIT_CONTEXT_ARCHIVE PLAYIT_CONTEXT_PACKAGE PACKAGES_LIST \ SOME_VARIABLE SOME_VARIABLE_BIN32 SOME_VARIABLE_MULTIARCH SOME_VARIABLE_MULTIARCH_0 set_current_archive 'ARCHIVE_BASE_MULTIARCH_0' PACKAGES_LIST=' PKG_BIN32 PKG_BIN64 PKG_DATA' set_current_package 'PKG_BIN32' context_name=$(context_name 'SOME_VARIABLE') assertNull "$context_name" SOME_VARIABLE='some value' context_name=$(context_name 'SOME_VARIABLE') assertEquals 'SOME_VARIABLE' "$context_name" SOME_VARIABLE_BIN32='some value' context_name=$(context_name 'SOME_VARIABLE') assertEquals 'SOME_VARIABLE_BIN32' "$context_name" SOME_VARIABLE_MULTIARCH='some value' context_name=$(context_name 'SOME_VARIABLE') assertEquals 'SOME_VARIABLE_MULTIARCH' "$context_name" SOME_VARIABLE_MULTIARCH_0='some value' context_name=$(context_name 'SOME_VARIABLE') assertEquals 'SOME_VARIABLE_MULTIARCH_0' "$context_name" } test_context_name_archive() { local \ context_name_archive \ PLAYIT_CONTEXT_ARCHIVE \ SOME_VARIABLE SOME_VARIABLE_MULTIARCH SOME_VARIABLE_MULTIARCH_0 set_current_archive 'ARCHIVE_BASE_MULTIARCH_0' context_name_archive=$(context_name_archive 'SOME_VARIABLE') assertNull "$context_name_archive" SOME_VARIABLE='some value' context_name_archive=$(context_name_archive 'SOME_VARIABLE') assertNull "$context_name_archive" SOME_VARIABLE_MULTIARCH='some value' context_name_archive=$(context_name_archive 'SOME_VARIABLE') assertEquals 'SOME_VARIABLE_MULTIARCH' "$context_name_archive" SOME_VARIABLE_MULTIARCH_0='some value' context_name_archive=$(context_name_archive 'SOME_VARIABLE') assertEquals 'SOME_VARIABLE_MULTIARCH_0' "$context_name_archive" } test_context_name_package() { local \ context_name_package \ PLAYIT_CONTEXT_PACKAGE PACKAGES_LIST \ SOME_VARIABLE SOME_VARIABLE_BIN32 PACKAGES_LIST=' PKG_BIN32 PKG_BIN64 PKG_DATA' set_current_package 'PKG_BIN32' context_name_package=$(context_name_package 'SOME_VARIABLE') assertNull "$context_name_package" SOME_VARIABLE='some value' context_name_package=$(context_name_package 'SOME_VARIABLE') assertNull "$context_name_package" SOME_VARIABLE_BIN32='some value' context_name_package=$(context_name_package 'SOME_VARIABLE') assertEquals 'SOME_VARIABLE_BIN32' "$context_name_package" } test_context_value() { local \ context_value \ PLAYIT_CONTEXT_ARCHIVE PLAYIT_CONTEXT_PACKAGE PACKAGES_LIST \ SOME_VARIABLE SOME_VARIABLE_BIN32 SOME_VARIABLE_MULTIARCH SOME_VARIABLE_MULTIARCH_0 set_current_archive 'ARCHIVE_BASE_MULTIARCH_0' PACKAGES_LIST=' PKG_BIN32 PKG_BIN64 PKG_DATA' set_current_package 'PKG_BIN32' context_value=$(context_value 'SOME_VARIABLE') assertNull "$context_value" SOME_VARIABLE='some value' context_value=$(context_value 'SOME_VARIABLE') assertEquals 'some value' "$context_value" SOME_VARIABLE_BIN32='some value specific to PKG_BIN32' context_value=$(context_value 'SOME_VARIABLE') assertEquals 'some value specific to PKG_BIN32' "$context_value" SOME_VARIABLE_MULTIARCH='some value specific to ARCHIVE_BASE_MULTIARCH_*' context_value=$(context_value 'SOME_VARIABLE') assertEquals 'some value specific to ARCHIVE_BASE_MULTIARCH_*' "$context_value" SOME_VARIABLE_MULTIARCH_0='some value specific to ARCHIVE_BASE_MULTIARCH_0' context_value=$(context_value 'SOME_VARIABLE') assertEquals 'some value specific to ARCHIVE_BASE_MULTIARCH_0' "$context_value" } tests/shunit2/10_common/10_lists.sh0000644000000000000000000000052413120060140016060 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . lib/libplayit2.sh } test_list_clean() { local list_unsorted list_sorted list_expected list_unsorted='aaa ccc bbb aaa ' list_expected='aaa bbb ccc' list_sorted=$(printf '%s' "$list_unsorted" | list_clean) assertEquals "$list_expected" "$list_sorted" } tests/shunit2/10_common/10_variables-manipulation.sh0000644000000000000000000000067413120060140021376 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . 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_games/10_scripts.sh0000644000000000000000000000664413120060140016226 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . 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}/10_another-scripts-collection" \ "${TEST_TEMP_DIR}/99_old-scripts-collection" cat > "${TEST_TEMP_DIR}/00_scripts-collection/play-some-game.sh" <<- EOF ARCHIVE_BASE_0_NAME='some_game_archive.tar.gz' ARCHIVE_BASE_0_MD5='d41d8cd98f00b204e9800998ecf8427e' ARCHIVE_BASE_1_NAME='some_game_archive.tar.gz' ARCHIVE_BASE_1_MD5='d401431ff9c1c7526c3104194409bd6e' EOF cat > "${TEST_TEMP_DIR}/00_scripts-collection/play-some-other-game.sh" <<- EOF ARCHIVE_BASE_0_NAME='another_game_archive.zip' EOF cat > "${TEST_TEMP_DIR}/10_another-scripts-collection/play-some-game.sh" <<- EOF ARCHIVE_BASE_0_NAME='some_game_archive.tar.gz' ARCHIVE_BASE_0_MD5='d41d8cd98f00b204e9800998ecf8427e' EOF cat > "${TEST_TEMP_DIR}/10_another-scripts-collection/play-yet-another-game.sh" <<- EOF ARCHIVE_BASE_0_NAME='some_game_archive.tar.gz' ARCHIVE_BASE_0_MD5='d401431ff9c1c7526c3104194409bd6e' EOF export PLAYIT_OPTION_COLLECTION_PATH="$TEST_TEMP_DIR" } oneTimeTearDown() { rm --force --recursive "$TEST_TEMP_DIR" } test_games_list_scripts_all() { local game_scripts_list game_scripts_list_expected 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}/10_another-scripts-collection/play-some-game.sh ${TEST_TEMP_DIR}/10_another-scripts-collection/play-yet-another-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 game_scripts_expected="${TEST_TEMP_DIR}/00_scripts-collection/play-some-game.sh ${TEST_TEMP_DIR}/10_another-scripts-collection/play-some-game.sh ${TEST_TEMP_DIR}/10_another-scripts-collection/play-yet-another-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 SOURCE_ARCHIVE_NAME game_script_expected="${TEST_TEMP_DIR}/00_scripts-collection/play-some-other-game.sh" game_script=$(games_find_script_for_archive 'another_game_archive.zip') assertEquals "$game_script_expected" "$game_script" local PLAYIT_ARCHIVES_PATH_BASE PLAYIT_ARCHIVES_PATH_BASE="$TEST_TEMP_DIR" SOURCE_ARCHIVE_NAME='some_game_archive.tar.gz' SOURCE_ARCHIVE_PATH="${TEST_TEMP_DIR}/some_game_archive.tar.gz" touch "$SOURCE_ARCHIVE_PATH" 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' 2>/dev/null) assertEquals 'Failed to select a single game script after a MD5-based archive identification.' \ "$game_script_expected" "$game_script" rm "$SOURCE_ARCHIVE_PATH" } 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.sh0000644000000000000000000000222113120060140021144 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . 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.sh0000644000000000000000000000410713120060140020310 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . 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) ## getopt output walways include a leading space, that is used to separate options from each other. 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.sh0000644000000000000000000000371013120060140017776 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . lib/libplayit2.sh } setUp() { # Set a temporary directory to mess with real files TEST_TEMP_DIR=$(mktemp --directory) export TEST_TEMP_DIR } tearDown() { rm --force --recursive "$TEST_TEMP_DIR" } test_options_init_default() { local option_prefix options_init_default option_prefix=$(option_value 'prefix') assertEquals '/usr' "$option_prefix" } test_option_variable() { local option_variable option_variable=$(option_variable 'free-space-check') assertEquals 'PLAYIT_OPTION_FREE_SPACE_CHECK' "$option_variable" } test_option_variable_default() { local option_variable option_variable=$(option_variable_default 'free-space-check') assertEquals 'PLAYIT_DEFAULT_OPTION_FREE_SPACE_CHECK' "$option_variable" } test_option_update() { local PLAYIT_COMPATIBILITY_LEVEL option_value option_update 'prefix' '/usr/local' option_value=$(option_value 'prefix') assertEquals '/usr/local' "$option_value" } test_option_update_default() { local option_value option_update_default 'prefix' '/usr/local' option_value=$(option_value 'prefix') assertEquals '/usr/local' "$option_value" } test_option_value() { local option_value option_update 'prefix' '/usr/local' option_value=$(option_value 'prefix') assertEquals '/usr/local' "$option_value" } test_options_validity_check() { options_init_default # Create paths expected to be existing and writable option_update 'tmpdir' "${TEST_TEMP_DIR}/tmpdir" option_update 'output-dir' "${TEST_TEMP_DIR}output-dir" mkdir "${TEST_TEMP_DIR}/tmpdir" "${TEST_TEMP_DIR}output-dir" option_update 'checksum' 'none' assertTrue 'options_validity_check' option_update 'checksum' 'invalid' assertFalse 'options_validity_check' } test_options_compatibility_check() { option_update 'package' 'arch' option_update 'compression' 'size' assertTrue 'options_compatibility_check' option_update 'compression' 'auto' assertFalse 'options_compatibility_check' } tests/shunit2/20_game/00_common.sh0000644000000000000000000000142013120060140015627 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . 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.sh0000644000000000000000000000113213120060140015605 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . 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.sh0000644000000000000000000000510413120060140020700 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . lib/libplayit2.sh # Set some required options PLAYIT_OPTION_ICONS=1 } test_requirements_list() { local \ requirements_list requirements_list_expected \ REQUIREMENTS_LIST \ PLAYIT_CONTEXT_ARCHIVE ARCHIVE_BASE_0_EXTRACTOR REQUIREMENTS_LIST=' command1 command2 command3' ARCHIVE_BASE_0_EXTRACTOR='unzip' set_current_archive 'ARCHIVE_BASE_0' requirements_list=$(requirements_list) requirements_list_expected='command1 command2 command3 unzip' assertEquals "$requirements_list_expected" "$requirements_list" } test_requirements_list_checksum() { local requirements_list option_update 'checksum' 'md5' requirements_list=$(requirements_list_checksum) assertEquals 'md5sum' "$requirements_list" } test_requirements_list_package() { local requirements_list requirements_list_expected option_update 'package' 'deb' requirements_list=$(requirements_list_package) requirements_list_expected='dpkg-deb' assertEquals "$requirements_list_expected" "$requirements_list" } test_requirements_list_icons() { local APPLICATIONS_LIST APP_MAIN_ICONS_LIST APP_MAIN_ICON requirements_list requirements_list_expected APPLICATIONS_LIST='APP_MAIN' APP_MAIN_ICONS_LIST='APP_MAIN_ICON' APP_MAIN_ICON='icon.exe' requirements_list=$(requirements_list_icons) requirements_list_expected='identify convert wrestool' assertEquals "$requirements_list_expected" "$requirements_list" } test_requirements_list_archive() { local \ requirements_list requirements_list_expected \ PLAYIT_CONTEXT_ARCHIVE ARCHIVE_BASE_0_TYPE ARCHIVE_BASE_0_PART1 ARCHIVE_BASE_0_PART1_TYPE ARCHIVE_BASE_0_TYPE='innosetup' ARCHIVE_BASE_0_PART1='setup_something-1.bin' ARCHIVE_BASE_0_PART1_TYPE='rar' set_current_archive 'ARCHIVE_BASE_0' requirements_list_expected='innoextract unar' requirements_list=$(requirements_list_archive) assertEquals "$requirements_list_expected" "$requirements_list" } test_requirements_list_archive_single() { local ARCHIVE_BASE_0_TYPE ARCHIVE_BASE_0_EXTRACTOR requirements_list requirements_list_expected ARCHIVE_BASE_0_TYPE='mojosetup' requirements_list=$(requirements_list_archive_single 'ARCHIVE_BASE_0') requirements_list_expected='head sed wc tr gzip tar unzip' assertEquals "$requirements_list_expected" "$requirements_list" ARCHIVE_BASE_0_EXTRACTOR='innosetup' requirements_list=$(requirements_list_archive_single 'ARCHIVE_BASE_0') assertEquals 'innosetup' "$requirements_list" } test_dependency_provided_by() { local provider provider=$(dependency_provided_by 'bsdtar') assertEquals 'libarchive' "$provider" } tests/shunit2/30_applications/00_common.sh0000644000000000000000000001603413120060140017414 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . 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_path() { local \ PLAYIT_OPTION_TMPDIR GAME_ID CONTENT_PATH_DEFAULT APP_MAIN_EXE \ PACKAGES_LIST PKG_BIN32_ID PKG_BIN64_ID PKG_DATA_ID \ PLAYIT_WORKDIR \ application_exe_path_gamedata application_exe_path_bin32 application_exe_path_bin64 application_exe_path_data \ PLAYIT_CONTEXT_PACKAGE_DEFAULT PLAYIT_CONTEXT_PACKAGE # Create a couple files in the temporary directory, and set required variables. PLAYIT_OPTION_TMPDIR="$TEST_TEMP_DIR" GAME_ID='some-game' CONTENT_PATH_DEFAULT='.' APP_MAIN_EXE='SomeGame.exe' PACKAGES_LIST=' PKG_BIN32 PKG_BIN64 PKG_DATA' PKG_BIN32_ID='some-game-bin32' PKG_BIN64_ID='some-game-bin64' PKG_DATA_ID='some-game-data' set_default_package 'PKG_BIN32' init_working_directory application_exe_path_gamedata="${PLAYIT_WORKDIR}/gamedata/${CONTENT_PATH_DEFAULT}/${APP_MAIN_EXE}" application_exe_path_bin32="${PLAYIT_WORKDIR}/packages/${PKG_BIN32_ID}_1.0-1+19700101.1_all/usr/share/games/${GAME_ID}/${APP_MAIN_EXE}" application_exe_path_bin64="${PLAYIT_WORKDIR}/packages/${PKG_BIN64_ID}_1.0-1+19700101.1_all/usr/share/games/${GAME_ID}/${APP_MAIN_EXE}" application_exe_path_data="${PLAYIT_WORKDIR}/packages/${PKG_DATA_ID}_1.0-1+19700101.1_all/usr/share/games/${GAME_ID}/${APP_MAIN_EXE}" mkdir --parents \ "$(dirname "$application_exe_path_gamedata")" \ "$(dirname "$application_exe_path_bin32")" \ "$(dirname "$application_exe_path_bin64")" \ "$(dirname "$application_exe_path_data")" touch \ "$application_exe_path_gamedata" \ "$application_exe_path_bin32" \ "$application_exe_path_bin64" \ "$application_exe_path_data" # Look in the current package set_current_package 'PKG_BIN64' assertEquals "$application_exe_path_bin64" "$(application_exe_path "$APP_MAIN_EXE")" rm "$application_exe_path_bin32" "$application_exe_path_bin64" # Look in all packages assertEquals "$application_exe_path_data" "$(application_exe_path "$APP_MAIN_EXE")" rm "$application_exe_path_data" # Look in archive contents assertEquals "$application_exe_path_gamedata" "$(application_exe_path "$APP_MAIN_EXE")" rm "$application_exe_path_gamedata" } 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" } test_application_postrun() { local APP_MAIN_POSTRUN APP_MAIN_TYPE PLAYIT_COMPATIBILITY_LEVEL application_postrun APP_MAIN_TYPE='native' APP_MAIN_POSTRUN='some commands' application_postrun=$(application_postrun 'APP_MAIN') assertEquals 'some commands' "$application_postrun" } test_application_options() { local APP_MAIN_OPTIONS application_options application_options=$(application_options 'APP_MAIN') assertNull "$application_options" APP_MAIN_OPTIONS='--some-option --some-other-option' application_options=$(application_options 'APP_MAIN') assertEquals '--some-option --some-other-option' "$application_options" # Ensure that line breaks are not allowed APP_MAIN_OPTIONS='--some-option --some-other-option' assertFalse 'application_options failed to correctly prevent a multi-lines options string.' 'application_options "APP_MAIN"' return 0 } tests/shunit2/30_applications/10_type.sh0000644000000000000000000000102413120060140017077 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . 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.sh0000644000000000000000000000412313120060140017223 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . 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_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_list() { local ARCHIVE_BASE_0 ARCHIVE_BASE_1 ARCHIVE_NOT_EXPECTED_FORMAT archives_list_expected ARCHIVE_BASE_0='some_game_archive.tar.gz' ARCHIVE_BASE_1='some_other_game_archive.tar.gz' ARCHIVE_BASE_OTHER_0='yet_another_game_archive.tar.gz' ARCHIVE_NOT_EXPECTED_FORMAT='some_game_archive_that_should_not_be_included.tar.gz' archives_list_expected='ARCHIVE_BASE_1 ARCHIVE_BASE_0 ARCHIVE_BASE_OTHER_0' assertEquals "$archives_list_expected" "$(archives_list)" } tests/shunit2/30_archives/10_details.sh0000644000000000000000000001125613120060140016671 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . 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_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.sh0000644000000000000000000000047113120060140016211 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . 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.sh0000644000000000000000000000366513120060140016406 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . lib/libplayit2.sh } test_content_path_default() { local \ content_path_default \ CONTENT_PATH_DEFAULT \ PLAYIT_CONTEXT_ARCHIVE CONTENT_PATH_DEFAULT_0 CONTENT_PATH_DEFAULT='some/path' content_path_default=$(content_path_default) assertEquals \ 'content_path_default failed to get an explicitly set value from CONTENT_PATH_DEFAULT.' \ 'some/path' "$content_path_default" unset CONTENT_PATH_DEFAULT set_current_archive 'ARCHIVE_BASE_0' CONTENT_PATH_DEFAULT_0='some/other/path' content_path_default=$(content_path_default) assertEquals \ 'content_path_default failed to get a contextual value for CONTENT_PATH_DEFAULT.' \ 'some/other/path' "$content_path_default" unset ARCHIVE CONTENT_PATH_DEFAULT_0 assertFalse \ 'content_path_default did not fail, despite no value being set for CONTENT_PATH_DEFAULT.' \ 'content_path_default' } test_content_path() { local \ CONTENT_PATH_DEFAULT CONTENT_GAME_DATA_PATH CONTENT_GAME_DATA_PATH_0 \ PLAYIT_CONTEXT_ARCHIVE CONTENT_PATH_DEFAULT='default/path' assertEquals "$CONTENT_PATH_DEFAULT" "$(content_path 'GAME_DATA')" CONTENT_GAME_DATA_PATH='specific/path' assertEquals "$CONTENT_GAME_DATA_PATH" "$(content_path 'GAME_DATA')" set_current_archive 'ARCHIVE_BASE_0' CONTENT_GAME_DATA_PATH_0='more/specific/path' assertEquals "$CONTENT_GAME_DATA_PATH_0" "$(content_path 'GAME_DATA')" } test_content_files() { local \ CONTENT_GAME_DATA_FILES CONTENT_GAME_DATA_FILES_0 \ PLAYIT_CONTEXT_ARCHIVE assertNull "$(content_files 'GAME_DATA')" CONTENT_GAME_DATA_FILES='some list of files' assertEquals "$CONTENT_GAME_DATA_FILES" "$(content_files 'GAME_DATA')" set_current_archive 'ARCHIVE_BASE_0' CONTENT_GAME_DATA_FILES_0='specific list of files' assertEquals "$CONTENT_GAME_DATA_FILES_0" "$(content_files 'GAME_DATA')" # Fallback lists of files for Unity3D and Unreal Engine 4 games are not tested here. } tests/shunit2/30_content/10_files-inclusion.sh0000644000000000000000000000311413120060140020207 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . lib/libplayit2.sh } setUp() { # Set a temporary directory to mess with real files TEST_TEMP_DIR=$(mktemp --directory) export TEST_TEMP_DIR } tearDown() { rm --force --recursive "$TEST_TEMP_DIR" } test_content_inclusion() { local PLAYIT_WORKDIR CONTENT_GAME_MAIN_PATH CONTENT_GAME_DATA_FILES # Create source files mkdir --parents "${TEST_TEMP_DIR}/gamedata" mkdir \ "${TEST_TEMP_DIR}/gamedata/directory to include" \ "${TEST_TEMP_DIR}/gamedata/directory to exclude" touch \ "${TEST_TEMP_DIR}/gamedata/file to include" \ "${TEST_TEMP_DIR}/gamedata/file to exclude" # Use a hardcoded destination package_path() { printf '%s/packages/destination-package' "$TEST_TEMP_DIR" } PLAYIT_WORKDIR="$TEST_TEMP_DIR" CONTENT_GAME_MAIN_PATH='.' CONTENT_GAME_MAIN_FILES='directory to include file to include missing file' content_inclusion 'GAME_MAIN' 'PKG_MAIN' '/' paths_included=$(find "${TEST_TEMP_DIR}/packages/destination-package" | env --ignore-environment sort) paths_included_expected="${TEST_TEMP_DIR}/packages/destination-package ${TEST_TEMP_DIR}/packages/destination-package/directory to include ${TEST_TEMP_DIR}/packages/destination-package/file to include" assertEquals "$paths_included_expected" "$paths_included" paths_excluded=$(find "${TEST_TEMP_DIR}/gamedata" | env --ignore-environment sort) paths_excluded_expected="${TEST_TEMP_DIR}/gamedata ${TEST_TEMP_DIR}/gamedata/directory to exclude ${TEST_TEMP_DIR}/gamedata/file to exclude" assertEquals "$paths_excluded_expected" "$paths_excluded" } tests/shunit2/30_content/20_huge-files.sh0000644000000000000000000000343613120060140017144 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . 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' 'huge-file.pak.1' '1' 'huge-file.pak' 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 'huge-file.pak.1' "$content_files" } tests/shunit2/30_icons/00_common.sh0000644000000000000000000000720113120060140016035 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . lib/libplayit2.sh } test_icons_list_all() { local APPLICATIONS_LIST APP_MAIN_ICONS_LIST APP_EXTRA_ICONS_LIST icons_list icons_list_expected APPLICATIONS_LIST='APP_MAIN APP_EXTRA' APP_MAIN_ICONS_LIST='APP_MAIN_ICON' APP_EXTRA_ICONS_LIST='APP_EXTRA_ICON1 APP_EXTRA_ICON2' icons_list=$(icons_list_all) icons_list_expected='APP_EXTRA_ICON1 APP_EXTRA_ICON2 APP_MAIN_ICON' assertEquals "$icons_list_expected" "$icons_list" } test_application_icons_list() { local APP_MAIN_ICONS_LIST APP_MAIN_ICON APP_MAIN_TYPE GAME_ENGINE icons_list APP_MAIN_ICONS_LIST='APP_MAIN_ICON1 APP_MAIN_ICON_2' icons_list=$(application_icons_list 'APP_MAIN') assertEquals 'APP_MAIN_ICON1 APP_MAIN_ICON_2' "$icons_list" unset APP_MAIN_ICONS_LIST APP_MAIN_ICON='something.ico' icons_list=$(application_icons_list 'APP_MAIN') assertEquals 'APP_MAIN_ICON' "$icons_list" unset APP_MAIN_ICON APP_MAIN_TYPE='wine' icons_list=$(application_icons_list 'APP_MAIN') assertEquals 'APP_MAIN_ICON' "$icons_list" unset APP_MAIN_TYPE GAME_ENGINE='unity3d' icons_list=$(application_icons_list 'APP_MAIN') assertEquals 'APP_MAIN_ICON' "$icons_list" unset GAME_ENGINE icons_list=$(application_icons_list 'APP_MAIN') assertNull "$icons_list" } test_icon_application() { local APPLICATIONS_LIST application APPLICATIONS_LIST='APP_MAIN APP_EXTRA' application=$(icon_application 'APP_MAIN_ICON') assertEquals 'APP_MAIN' "$application" application=$(icon_application 'APP_EXTRA_ICON1') assertEquals 'APP_EXTRA' "$application" # Check that icon_application does not get confused between applications with similar names. APPLICATIONS_LIST='APP_HOF APP_HOFEDIT' APP_HOF_ID='heroes-of-might-and-magic-5-hammers-of-fate' APP_HOFEDIT_ID='heroes-of-might-and-magic-5-hammers-of-fate-map-editor' application=$(icon_application 'APP_HOFEDIT_ID') assertEquals 'icon_application linked an icon to the wrong application.' 'APP_HOFEDIT' "$application" } test_icon_path() { local \ APP_MAIN_ICON icon_path \ APPLICATIONS_LIST APP_MAIN_TYPE APP_MAIN_EXE \ GAME_ENGINE UNITY3D_NAME APP_MAIN_ICON='something.ico' icon_path=$(icon_path 'APP_MAIN_ICON') assertEquals 'something.ico' "$APP_MAIN_ICON" unset APP_MAIN_ICON APPLICATIONS_LIST='APP_MAIN' APP_MAIN_TYPE='wine' APP_MAIN_EXE='something.exe' icon_path=$(icon_path 'APP_MAIN_ICON') assertEquals 'something.exe' "$icon_path" unset APPLICATIONS_LIST APP_MAIN_TYPE APP_MAIN_EXE GAME_ENGINE='unity3d' UNITY3D_NAME='Something' APP_MAIN_TYPE='native' icon_path=$(icon_path 'APP_MAIN_ICON') assertEquals 'Something_Data/Resources/UnityPlayer.png' "$icon_path" APP_MAIN_TYPE='wine' icon_path=$(icon_path 'APP_MAIN_ICON') assertEquals 'Something.exe' "$icon_path" unset GAME_ENGINE UNITY3D_NAME APP_MAIN_TYPE } test_icon_wrestool_options() { local \ APP_MAIN_ICON APP_MAIN_ICON_WRESTOOL_OPTIONS wrestool_options \ APPLICATIONS_LIST GAME_ENGINE \ APP_MAIN_ICON_ID # Check that an error is triggered if the icon is not using the expected file extension APP_MAIN_ICON='something.ico' assertFalse "wrestool_options 'APP_MAIN_ICON'" APP_MAIN_ICON='something.exe' APP_MAIN_ICON_WRESTOOL_OPTIONS='--type=14 --name=221' wrestool_options=$(icon_wrestool_options 'APP_MAIN_ICON') assertEquals '--type=14 --name=221' "$wrestool_options" unset APP_MAIN_ICON_WRESTOOL_OPTIONS APPLICATIONS_LIST='APP_MAIN' GAME_ENGINE='unrealengine4' wrestool_options=$(icon_wrestool_options 'APP_MAIN_ICON') assertEquals '--type=14 --name=101' "$wrestool_options" unset GAME_ENGINE wrestool_options=$(icon_wrestool_options 'APP_MAIN_ICON') assertEquals '--type=14' "$wrestool_options" } tests/shunit2/30_icons/10_conversion.sh0000644000000000000000000000243613120060140016740 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . lib/libplayit2.sh } test_icons_inclusion_single_application() { ## Prevent actions requiring files manipulation. icons_inclusion_single_icon() { return 0; } # Ensure that an error is thrown when trying to extract icons from an application with no icon set assertFalse \ 'icons_inclusion_single_application failed to notice it has been asked to handle an application with no icon set.' \ 'icons_inclusion_single_application "PKG_MAIN" "APP_MAIN"' # Check that the previous error is not thrown if an optional icons archive is supported. local ARCHIVE_OPTIONAL_ICONS_NAME ARCHIVE_OPTIONAL_ICONS_NAME='alpha-centauri-icons.2022-10-04.tar.gz' assertTrue \ 'icons_inclusion_single_application failed to notice that an optional icons archive is supported.' \ 'icons_inclusion_single_application "PKG_MAIN" "APP_MAIN"' unset -f icons_inclusion_single_icon } test_icon_full_path() { local icon_full_path PLAYIT_WORKDIR CONTENT_PATH_DEFAULT APP_MAIN_ICON PLAYIT_WORKDIR='/var/tmp/play.it/jazz-jackrabbit-2.t3nm8' CONTENT_PATH_DEFAULT='app' APP_MAIN_ICON='jazz2.exe' icon_full_path=$(icon_full_path 'APP_MAIN_ICON') assertEquals '/var/tmp/play.it/jazz-jackrabbit-2.t3nm8/gamedata/app/jazz2.exe' "$icon_full_path" } tests/shunit2/30_launchers/00_common.sh0000644000000000000000000000173313120060140016712 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . lib/libplayit2.sh } test_launcher_generation_checks() { local APP_MAIN_TYPE APP_MAIN_EXE APP_MAIN_SCUMMID # Disable launcher_target_presence_check, # it should be tested in its own function. launcher_target_presence_check() { return 0; } # Ensure that an error is triggered on missing application type. unset APP_MAIN_TYPE assertFalse 'launcher_generation_checks "PKG_MAIN" "APP_MAIN"' # Ensure that an error is triggered when no binary path is set, # but one is expected by the current application type. unset APP_MAIN_EXE for APP_MAIN_TYPE in \ 'dosbox' \ 'java' \ 'mono' \ 'native' \ 'wine' do assertFalse 'launcher_generation_checks "PKG_MAIN" "APP_MAIN"' done # Ensure that an error is triggered when no ScummVM game ID is set, # but one is expected by the current application type. APP_MAIN_TYPE='scummvm' assertFalse 'launcher_generation_checks "PKG_MAIN" "APP_MAIN"' } tests/shunit2/30_launchers/10_fake-home.sh0000644000000000000000000000224113120060140017252 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . 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.sh0000644000000000000000000000216613120060140021510 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . 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 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 that empty values do not trigger an error assertTrue 'persistent_list_files' } tests/shunit2/30_packages/00_common.sh0000644000000000000000000000632513120060140016506 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . lib/libplayit2.sh } test_packages_generation() { # Check that packages_generation called without an explicit list of packages does no end up running twice local PLAYIT_OPTION_PACKAGE function_calls_counter PLAYIT_OPTION_PACKAGE='deb' function_calls_counter=0 debian_packages_metadata() { function_calls_counter=$((function_calls_counter + 1)) export function_calls_counter } debian_packages_build() { return 0; } ## Prevent the console message from being mixed with shunit2 output. packages_generation >/dev/null assertEquals \ 'packages_generation called without an argument ended up running multiple times instead of only once.' \ '1' "$function_calls_counter" unset -f debian_packages_metadata debian_packages_build } test_package_description() { local package_description PKG_MAIN_DESCRIPTION PKG_MAIN_DESCRIPTION='French localization' package_description=$(package_description 'PKG_MAIN') assertEquals 'French localization' "$package_description" # Line breaks should trigger an error PKG_MAIN_DESCRIPTION='French localization Includes text and voices' assertFalse 'package_description "PKG_MAIN"' } test_package_postinst_actions() { local postinst_actions postinst_actions_expected PKG_MAIN_POSTINST_RUN PKG_MAIN_POSTINST_RUN="# Link common files shared by the games series ln --symbolic '/usr/share/games/heroes-chronicles/data' '/usr/share/games/heroes-chronicles-3' ln --symbolic '/usr/share/games/heroes-chronicles/mp3' '/usr/share/games/heroes-chronicles-3'" postinst_actions=$(package_postinst_actions 'PKG_MAIN') postinst_actions_expected="# Link common files shared by the games series ln --symbolic '/usr/share/games/heroes-chronicles/data' '/usr/share/games/heroes-chronicles-3' ln --symbolic '/usr/share/games/heroes-chronicles/mp3' '/usr/share/games/heroes-chronicles-3'" assertEquals "$postinst_actions_expected" "$PKG_MAIN_POSTINST_RUN" } test_package_prerm_actions() { local prerm_actions prerm_actions_expected PKG_MAIN_PRERM_RUN PKG_MAIN_PRERM_RUN="# Delete links to common files shared by the games series rm '/usr/share/games/heroes-chronicles-3/mp3' rm '/usr/share/games/heroes-chronicles-3/data'" prerm_actions=$(package_prerm_actions 'PKG_MAIN') prerm_actions_expected="# Delete links to common files shared by the games series rm '/usr/share/games/heroes-chronicles-3/mp3' rm '/usr/share/games/heroes-chronicles-3/data'" assertEquals "$prerm_actions_expected" "$PKG_MAIN_PRERM_RUN" } test_package_postinst_warnings() { local postinst_warnings postinst_warnings_expected PKG_MAIN_POSTINST_WARNINGS PKG_MAIN_POSTINST_WARNINGS='You may need to generate the ja_JP.UTF-8 locale for the configuration program to run You need a MIDI synthetiser for music to play in the game (you can use timidity++ or fluidsynth if you don’t have a hardware synthetiser)' postinst_warnings=$(package_postinst_warnings 'PKG_MAIN') postinst_warnings_expected='You may need to generate the ja_JP.UTF-8 locale for the configuration program to run You need a MIDI synthetiser for music to play in the game (you can use timidity++ or fluidsynth if you don’t have a hardware synthetiser)' assertEquals "$postinst_warnings_expected" "$postinst_warnings" } tests/shunit2/50_engine_dosbox/10_launchers.sh0000644000000000000000000000046613120060140020252 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . lib/libplayit2.sh } test_dosbox_launcher() { local APP_MAIN_PREFIX_TYPE APP_MAIN_PREFIX_TYPE='symlinks' assertTrue \ 'Failed to generate a launcher for a DOSBox game using a symlinks prefix.' \ 'dosbox_launcher "APP_MAIN"' } tests/shunit2/50_engine_java/10_launchers.sh0000644000000000000000000000046013120060140017667 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . lib/libplayit2.sh } test_java_launcher() { local APP_MAIN_PREFIX_TYPE APP_MAIN_PREFIX_TYPE='symlinks' assertTrue \ 'Failed to generate a launcher for a Java game using a symlinks prefix.' \ 'java_launcher "APP_MAIN"' } tests/shunit2/50_engine_mono/10_launchers.sh0000644000000000000000000000070113120060140017714 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . lib/libplayit2.sh } test_mono_launcher() { local APP_MAIN_PREFIX_TYPE APP_MAIN_PREFIX_TYPE='symlinks' assertTrue \ 'Failed to generate a launcher for a Mono game using a symlinks prefix.' \ 'mono_launcher "APP_MAIN"' APP_MAIN_PREFIX_TYPE='none' assertTrue \ 'Failed to generate a launcher for a Mono game not using a prefix.' \ 'mono_launcher "APP_MAIN"' } tests/shunit2/50_engine_native/10_launchers.sh0000644000000000000000000000072713120060140020242 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . lib/libplayit2.sh } test_native_launcher() { local APP_MAIN_PREFIX_TYPE APP_MAIN_PREFIX_TYPE='symlinks' assertTrue \ 'Failed to generate a launcher for a native Linux game using a symlinks prefix.' \ 'native_launcher "APP_MAIN"' APP_MAIN_PREFIX_TYPE='none' assertTrue \ 'Failed to generate a launcher for a native Linux game not using a prefix.' \ 'native_launcher "APP_MAIN"' } tests/shunit2/50_engine_renpy/10_launchers.sh0000644000000000000000000000071013120060140020101 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . lib/libplayit2.sh } test_renpy_launcher() { local APP_MAIN_PREFIX_TYPE APP_MAIN_PREFIX_TYPE='symlinks' assertTrue \ "Failed to generate a launcher for a Ren'Py game using a symlinks prefix." \ 'renpy_launcher "APP_MAIN"' APP_MAIN_PREFIX_TYPE='none' assertTrue \ "Failed to generate a launcher for a Ren'Py game not using a prefix." \ 'renpy_launcher "APP_MAIN"' } tests/shunit2/50_engine_scummvm/10_launchers.sh0000644000000000000000000000046013120060140020435 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . lib/libplayit2.sh } test_scummvm_launcher() { local APP_MAIN_PREFIX_TYPE APP_MAIN_PREFIX_TYPE='none' assertTrue \ 'Failed to generate a launcher for a ScummVM game not using a prefix.' \ 'scummvm_launcher "APP_MAIN"' } tests/shunit2/50_engine_wine/10_launchers.sh0000644000000000000000000000142713120060140017714 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . lib/libplayit2.sh } test_wine_launcher() { local \ PLAYIT_CONTEXT_PACKAGE='PKG_MAIN' \ PKG_MAIN_ARCH='32' local APP_MAIN_PREFIX_TYPE='symlinks' assertTrue \ 'Failed to generate a launcher for a WINE game using a symlinks prefix.' \ 'wine_launcher APP_MAIN' local UNREALENGINE4_NAME='VotV' ## Override the function returning a default list of winetricks verbs, ## so only the non-default Direct3D rendering would require the winetricks wrapper. unrealengine4_wine_winetricks_verbs_default() { return 0; } assertTrue \ 'Failed to include the required winetricks wrapper in the generated launcher for an Unreal Engine 4 game.' \ 'wine_launcher APP_MAIN | grep --silent "winetricks_wrapper()"' } tests/shunit2/55_engine-variant_unity3d/30_applications.sh0000644000000000000000000000165413120060140022526 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . lib/libplayit2.sh } setUp() { # Set a temporary directory to mess with real files TEST_TEMP_DIR=$(mktemp --directory) export TEST_TEMP_DIR } tearDown() { rm --force --recursive "$TEST_TEMP_DIR" } test_unity3d_application_exe_default() { # Check that the correct Unity3D game binary is picked up local CONTENT_PATH_DEFAULT PLAYIT_WORKDIR UNITY3D_NAME CONTENT_PATH_DEFAULT='.' PLAYIT_WORKDIR="$TEST_TEMP_DIR" UNITY3D_NAME='Dungeons2' mkdir --parents "${PLAYIT_WORKDIR}/gamedata/${CONTENT_PATH_DEFAULT}" touch \ "${PLAYIT_WORKDIR}/gamedata/${CONTENT_PATH_DEFAULT}/${UNITY3D_NAME}" \ "${PLAYIT_WORKDIR}/gamedata/${CONTENT_PATH_DEFAULT}/${UNITY3D_NAME}.x86" assertEquals \ 'unity3d_application_exe_default picked up the wrong file as the main game binary.' \ "${UNITY3D_NAME}.x86" \ "$(unity3d_application_exe_default 'APP_MAIN')" return 0 } tests/shunit2/60_system_archlinux/15_metadata-fields.sh0000644000000000000000000000116613120060140022074 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . lib/libplayit2.sh } test_archlinux_field_pkgdesc() { local field_pkgdesc script_version GAME_NAME PKG_MAIN_DESCRIPTION script_version='19700101.1' GAME_NAME='Alpha Centauri' PKG_MAIN_DESCRIPTION='French localization' field_pkgdesc=$(archlinux_field_pkgdesc 'PKG_MAIN') assertEquals 'Alpha Centauri - French localization - ./play.it script version 19700101.1' "$field_pkgdesc" unset PKG_MAIN_DESCRIPTION field_pkgdesc=$(archlinux_field_pkgdesc 'PKG_MAIN') assertEquals 'Alpha Centauri - ./play.it script version 19700101.1' "$field_pkgdesc" } tests/shunit2/60_system_archlinux/15_package-scripts.sh0000644000000000000000000000405613120060140022131 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . lib/libplayit2.sh } test_archlinux_script_install() { local \ script_install script_install_expected \ PKG_MAIN_POSTINST_RUN PKG_MAIN_PRERM_RUN \ PKG_MAIN_POSTINST_WARNINGS PKG_MAIN_POSTINST_RUN="# Link common files shared by the games series ln --symbolic '/usr/share/games/heroes-chronicles/data' '/usr/share/games/heroes-chronicles-3' ln --symbolic '/usr/share/games/heroes-chronicles/mp3' '/usr/share/games/heroes-chronicles-3'" PKG_MAIN_PRERM_RUN="# Delete links to common files shared by the games series rm '/usr/share/games/heroes-chronicles-3/mp3' rm '/usr/share/games/heroes-chronicles-3/data'" script_install=$(archlinux_script_install 'PKG_MAIN') script_install_expected="post_install() { # Link common files shared by the games series ln --symbolic '/usr/share/games/heroes-chronicles/data' '/usr/share/games/heroes-chronicles-3' ln --symbolic '/usr/share/games/heroes-chronicles/mp3' '/usr/share/games/heroes-chronicles-3' } post_upgrade() { post_install } pre_remove() { # Delete links to common files shared by the games series rm '/usr/share/games/heroes-chronicles-3/mp3' rm '/usr/share/games/heroes-chronicles-3/data' } pre_upgrade() { pre_remove }" assertEquals "$script_install_expected" "$script_install" unset PKG_MAIN_POSTINST_RUN PKG_MAIN_PRERM_RUN PKG_MAIN_POSTINST_WARNINGS='You may need to generate the ja_JP.UTF-8 locale for the configuration program to run You need a MIDI synthetiser for music to play in the game (you can use timidity++ or fluidsynth if you don’t have a hardware synthetiser)' script_install=$(archlinux_script_install 'PKG_MAIN') script_install_expected='post_install() { printf "Warning: %s\n" "You may need to generate the ja_JP.UTF-8 locale for the configuration program to run" printf "Warning: %s\n" "You need a MIDI synthetiser for music to play in the game (you can use timidity++ or fluidsynth if you don’t have a hardware synthetiser)" } post_upgrade() { post_install }' assertEquals "$script_install_expected" "$script_install" } tests/shunit2/60_system_debian/15_metadata-fields.sh0000644000000000000000000000143213120060140021315 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . lib/libplayit2.sh } test_debian_field_description() { local field_description field_description_expected script_version GAME_NAME PKG_MAIN_DESCRIPTION script_version='19700101.1' GAME_NAME='Alpha Centauri' PKG_MAIN_DESCRIPTION='French localization' field_description=$(debian_field_description 'PKG_MAIN') field_description_expected='Alpha Centauri - French localization ./play.it script version 19700101.1' assertEquals "$field_description_expected" "$field_description" unset PKG_MAIN_DESCRIPTION field_description=$(debian_field_description 'PKG_MAIN') field_description_expected='Alpha Centauri ./play.it script version 19700101.1' assertEquals "$field_description_expected" "$field_description" } tests/shunit2/60_system_debian/15_package-scripts.sh0000644000000000000000000000422413120060140021353 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . lib/libplayit2.sh } test_debian_script_postinst() { local script_postinst script_postinst_expected PKG_MAIN_POSTINST_RUN PKG_MAIN_POSTINST_WARNINGS PKG_MAIN_POSTINST_RUN='# Link common files shared by the games series ln --symbolic /usr/share/games/heroes-chronicles/data /usr/share/games/heroes-chronicles-3 ln --symbolic /usr/share/games/heroes-chronicles/mp3 /usr/share/games/heroes-chronicles-3' script_postinst=$(debian_script_postinst 'PKG_MAIN') script_postinst_expected='#!/bin/sh set -o errexit # Link common files shared by the games series ln --symbolic /usr/share/games/heroes-chronicles/data /usr/share/games/heroes-chronicles-3 ln --symbolic /usr/share/games/heroes-chronicles/mp3 /usr/share/games/heroes-chronicles-3 exit 0' assertEquals "$script_postinst_expected" "$script_postinst" unset PKG_MAIN_POSTINST_RUN PKG_MAIN_POSTINST_WARNINGS='You may need to generate the ja_JP.UTF-8 locale for the configuration program to run You need a MIDI synthetiser for music to play in the game (you can use timidity++ or fluidsynth if you don’t have a hardware synthetiser)' script_postinst=$(debian_script_postinst 'PKG_MAIN') script_postinst_expected='#!/bin/sh set -o errexit printf "Warning: %s\n" "You may need to generate the ja_JP.UTF-8 locale for the configuration program to run" printf "Warning: %s\n" "You need a MIDI synthetiser for music to play in the game (you can use timidity++ or fluidsynth if you don’t have a hardware synthetiser)" exit 0' assertEquals "$script_postinst_expected" "$script_postinst" } test_debian_script_prerm() { local script_prerm script_prerm_expected PKG_MAIN_PRERM_RUN PKG_MAIN_PRERM_RUN='# Delete links to common files shared by the games series rm /usr/share/games/heroes-chronicles-3/mp3 rm /usr/share/games/heroes-chronicles-3/data' script_prerm=$(debian_script_prerm 'PKG_MAIN') script_prerm_expected='#!/bin/sh set -o errexit # Delete links to common files shared by the games series rm /usr/share/games/heroes-chronicles-3/mp3 rm /usr/share/games/heroes-chronicles-3/data exit 0' assertEquals "$script_prerm_expected" "$script_prerm" } tests/shunit2/60_system_debian/25_dependencies_mono-libraries.sh0000644000000000000000000000103113120060140023715 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . lib/libplayit2.sh } test_debian_dependency_providing_mono_library() { ## Prevent actions requiring the creation/modification of files dependencies_unknown_mono_libraries_add() { return 0; } # Check that setting a dependency on an unkown Mono library is allowed assertTrue \ 'dependencies_mono_single_debian choked on an unkwown Mono library.' \ 'dependencies_mono_single_debian "Mono.unknown.dll"' unset -f dependencies_unknown_mono_libraries_add } tests/shunit2/60_system_gentoo/15_metadata-fields_gentoo-variant.sh0000644000000000000000000000121513120060140024402 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . 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/99_init/40_directories.sh0000644000000000000000000000111413120060140016731 0ustar rootroot#!/bin/sh set -o nounset oneTimeSetUp() { # Load the ./play.it library . 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_init_working_directory() { local GAME_ID PLAYIT_WORKDIR GAME_ID='some-game' init_working_directory assertTrue "test -n '$PLAYIT_WORKDIR'" assertTrue "test -d '$PLAYIT_WORKDIR'" }