From c281b94d6a5941170771f0de3c3d2d90e31f58ec Mon Sep 17 00:00:00 2001 From: mord0d Date: Thu, 11 Jan 2024 22:55:42 +0500 Subject: [PATCH] init --- .gitignore | 3 + LICENSE | 22 ++ README.md | 123 +++++++ gamelib | 975 ++++++++++++++++++++++++++++++++++++++++++++++++++++ gamelib.gui | 70 ++++ gamelib.new | 128 +++++++ 6 files changed, 1321 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100755 gamelib create mode 100755 gamelib.gui create mode 100755 gamelib.new diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1a09b40 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +_drafts +run +TODO diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ee895cc --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2019-2021, mord0d and their slaves +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 OWNER 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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d8fc7c6 --- /dev/null +++ b/README.md @@ -0,0 +1,123 @@ +gamelib +======= + +`gamelib` is a shell script for gaming on [FreeBSD](https://www.freebsd.org). + + + +## Supported emulators: + +- amd64 + - dolphin + - dosbox + - gzdoom + - pcsxr + - renpy + - wine + - wine32 (lib32 required) +- i386 **\[UNTESTED\]** + - dolphin + - dosbox + - gzdoom + - pcsxr + - renpy + - wine + + + +## Pre-requirements: + +- Running X11. +- `pkg` (any version with `fetch` subcommand but **not** 1.13.1, it's broken!) + installed; +- Binary repo(s) with supported emulators/engines enabled; +- No need to install any packages (except `pkg` itself), they'll never be used; +- **\[OPTIONAL\]** amd64 with lib32 support (used by `i386-wine`); + + + +## Setup games: + +### The structure of the directory with games: + +- `$GAMELIB_GAMES_DIR` + - `gamename/` (without spaces) + - `gamelib.conf` (see below) + - `game/` (place here game files) + - `prefix/` (do **not** create it manually!) + +### The structure of `gamelib.conf`: + +| Function | Description | +| :---: | --- | +| `prerun()` | Function that triggered any time before game run | +| `prerun_first()` | Function that triggered after prefix was created | + +These functions available for all supported emulators/engines, even if +`gamelib.conf` does not required. + +#### dolphin + +| Variable | Description | +| :---: | --- | +| `dolphin_file=` | GameCube/Wii disk image file (relative from the `game/`, see above) | + +#### dosbox + +| Variable | Description | +| :---: | --- | +| `dosbox_file=` | MS-DOS executable (relative from the `game/`, see above) | + +#### gzdoom + +| Variable | Description | +| :---: | --- | +| `gzdoom_file=` | WAD file (relative from the `game/`, see above) | + +#### pcsxr + +| Variable | Description | +| :---: | --- | +| `pcsxr_file=` | Sony PlayStation (PSX) image file (relative from the `game/`, see above) | +| `pcsxr_plugins_dir=` | Absolute path to the directory with plugins, they will be symlinked to the prefix | +| `pcsxr_bios_dir=` | Absolute path to the directory with bios, they will be symlinked to the prefix | + +#### renpy + +No `gamelib.conf` required. + +#### wine (any arch) + +| Variable | Description | +| :---: | --- | +| `wine_file=` | Microsoft Windows executable (PE32/PE32+) (relative from the `game/`, see above) | +| `wine_args=` | Additional arguments for the executable | +| `wine_dlls=` | Space-separated dlls that will be installed by winetricks | +| `wine_settings=` | Space-separated settings that will be configured by winetricks | + + + +## Pre-run: + +``` +export GAMELIB_GAMES_DIR="${HOME}/games" +``` + +Optional, you can set `GAMELIB_DEBUG` to enable stdout/stderr (in any case, +everything will be logged to `$GAMELIB_GAMES_DIR/.gamelib/gamelib.log`): + +``` +export GAMELIB_DEBUG=1 +``` + +Then run gamelib: + +``` +/path/to/gamelib/gamelib help +``` + + + +## License: + +This project is licensed under the terms of [2-Clause BSD](./LICENSE). diff --git a/gamelib b/gamelib new file mode 100755 index 0000000..8660817 --- /dev/null +++ b/gamelib @@ -0,0 +1,975 @@ +#!/bin/sh + +# +# Copyright (c) 2019-2021, mord0d and their slaves +# 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 OWNER 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. +# + +VERSION="0.5.3" +PROGNAME="gamelib" + +arch="$(uname -m)" +euid="$(id -u)" +uid="$(id -ur)" +user="$(id -urn)" +group="$(id -grn)" + +orig_HOME="${HOME}" +orig_XDG_CONFIG_HOME="${XDG_CONFIG_HOME}" +orig_XDG_CACHE_HOME="${XDG_CACHE_HOME}" +orig_XDG_DATA_HOME="${XDG_DATA_HOME}" + +orig_PATH="${PATH}" +orig_LD_LIBRARY_PATH="${LD_LIBRARY_PATH}" +orig_LD_32_LIBRARY_PATH_RPATH="${LD_32_LIBRARY_PATH_RPATH}" +orig_LD_32_LIBRARY_PATH="${LD_32_LIBRARY_PATH}" +orig_PYTHONPATH="${PYTHONPATH}" + +MY_LOCKFILE="${TMPDIR:-/tmp}/.${PROGNAME}-$uid.lock" + +MY_DATEFORMAT="${GAMELIB_DATE_FORMAT:-%Y-%m-%d %H:%M:%S}" +MY_DEBUG="${GAMELIB_DEBUG:-0}" + +MY_TEMPDIR="${TMPDIR:-/tmp}/.${PROGNAME}}" + + +# cmd:iscmd {{{ +cmd:iscmd() { + command -v "${1}" >/dev/null 2>&1 +} +# }}} + +# cmd:ls {{{ +cmd:ls() { + ls --color=never -1 "${@}" 2>/dev/null +} +# }}} + +# cmd:ln {{{ +cmd:ln() { + ln -s "${1}" "${2}" >/dev/null 2>&1 + return ${?} +} +# }}} + +# cmd:mkdir {{{ +cmd:mkdir() { + mkdir -p "${1}" >/dev/null 2>&1 + return ${?} +} +# }}} + + +# hlpr:startup {{{ +hlpr:startup() { + export MY_GAMESDIR="${GAMELIB_GAMES_DIR}" + if [ -n "${MY_GAMESDIR}" -a -d "${MY_GAMESDIR}" -a -r "${MY_GAMESDIR}" -a -w "${MY_GAMESDIR}" -a -x "${MY_GAMESDIR}" ]; then + export MY_DIR="${MY_GAMESDIR}/.gamelib" + else + hlpr:die "\$GAMELIB_GAMES_DIR is not set or it is not a directory or have wrong permissions" + fi + cmd:mkdir "${MY_DIR}" + [ -n "${MY_DIR}" -a -d "${MY_DIR}" -a -r "${MY_DIR}" -a -w "${MY_DIR}" -a -x "${MY_DIR}" ] || hlpr:die "\$GAMELIB_GAMES_DIR has wrong permissions" + export MY_LIBDIR="${MY_DIR}/lib" + export MY_BINDIR="${MY_DIR}/bin" + export MY_TEMPDIR="${MY_DIR}/tmp" + export MY_LOGFILE="${MY_DIR}/gamelib.log" + for dir in "${MY_LIBDIR}" "${MY_BINDIR}"; do + cmd:mkdir "${dir}" + done +} +# }}} + +# hlpr:shutdown {{{ +hlpr:shutdown() { + if cmd:iscmd "xrandr"; then + for out in $(xrandr -q | awk '/ connected /{print $1}'); do + xrandr --output "${out}" --auto --gamma 1.0 + done + fi + unset LIBGL_DRIVERS_PATH LC_ALL TZ + unset bin path + if [ -n "${orig_PATH}" ]; then + export PATH="${orig_PATH}" + else + unset PATH + fi + if [ -n "${orig_HOME}" ]; then + export HOME="${orig_HOME}" + else + unset HOME + fi + if [ -n "${orig_XDG_CONFIG_HOME}" ]; then + export XDG_CONFIG_HOME="${orig_XDG_CONFIG_HOME}" + else + unset XDG_CONFIG_HOME + fi + if [ -n "${orig_XDG_CACHE_HOME}" ]; then + export XDG_CACHE_HOME="${orig_XDG_CACHE_HOME}" + else + unset XDG_CACHE_HOME + fi + if [ -n "${orig_XDG_DATA_HOME}" ]; then + export XDG_DATA_HOME="${orig_XDG_DATA_HOME}" + else + unset XDG_DATA_HOME + fi + if [ -n "${orig_LD_LIBRARY_PATH}" ]; then + export LD_LIBRARY_PATH="${orig_LD_LIBRARY_PATH}" + else + unset LD_LIBRARY_PATH + fi + if [ -n "${orig_LD_32_LIBRARY_PATH}" ]; then + export LD_32_LIBRARY_PATH="${orig_LD_32_LIBRARY_PATH}" + else + unset LD_32_LIBRARY_PATH + fi + if [ -n "${orig_LD_32_LIBRARY_PATH_RPATH}" ]; then + export LD_32_LIBRARY_PATH_RPATH="${orig_LD_32_LIBRARY_PATH_RPATH}" + else + unset LD_32_LIBRARY_PATH_RPATH + fi + if [ -n "${orig_PYTHONPATH}" ]; then + export PYTHONPATH="${orig_PYTHONPATH}" + else + unset PYTHONPATH + fi + rm -R "${MY_TEMPDIR}" >/dev/null 2>&1 + rm "${MY_LOCKFILE}" >/dev/null 2>&1 +} +# }}} + +# hlpr:msg_log {{{ +hlpr:msg_log() { + c_reset="\033[0m" + c_bold="\033[1m" + c_black="\033[30m" + c_red="\033[31m" + c_green="\033[32m" + c_yellow="\033[33m" + c_blue="\033[34m" + c_magenta="\033[35m" + c_cyan="\033[36m" + b_black="\033[30;1m" + b_red="\033[31;1m" + b_green="\033[32;1m" + b_yellow="\033[33;1m" + b_blue="\033[34;1m" + b_magenta="\033[35;1m" + b_cyan="\033[36;1m" + d="$(date "+${MY_DATEFORMAT}")" + lvl="${1}" + shift + case "${lvl}" in + (INF) clr="${b_green}" ;; + (WRN) clr="${b_yellow}" ;; + (ERR) clr="${b_red}" ;; + (FTL) clr="${b_red}" ;; + esac + if [ -z "${MY_LOGFILE}" -o "${MY_DEBUG}" != "0" ]; then + echo -e "[${clr}${lvl}${c_reset}] ${c_cyan}${d}${c_reset} ${c_bold}${*}${c_reset}" >&2 + fi + echo "[${lvl}:${$}] ${d} ${*}" >>"${MY_LOGFILE:-/dev/null}" +} +# }}} + +# hlpr:err {{{ +hlpr:err() { + hlpr:msg_log "ERR" "${@}" +} +# }}} + +# hlpr:wrn {{{ +hlpr:wrn() { + hlpr:msg_log "WRN" "${@}" +} +# }}} + +# hlpr:inf {{{ +hlpr:inf() { + hlpr:msg_log "INF" "${@}" +} +# }}} + +# hlpr:die {{{ +hlpr:die() { + hlpr:msg_log "FTL" "${@}" + exit 1 +} +# }}} + +# hlpr:ask {{{ +hlpr:ask() { + while true; do + read -p "${1}? [y/n] " answer + case "${answer}" in + ([Yy]) return 0 ;; + ([Nn]) return 1 ;; + (*) MY_DEBUG=1 hlpr:err "incorrect input; only \"y\" or \"n\"" ;; + esac + done +} +# }}} + +# hlpr:pkgline {{{ +hlpr:pkgline() { + case "${1}" in + (dolphin) pkg_line="^dolphin-emu-[0-9]" ;; + (dosbox) pkg_line="^dosbox-[0-9]" ;; + (gzdoom) pkg_line="^gzdoom-[0-9]" ;; + (pcsxr) pkg_line="^pcsxr-[0-9]" ;; + (renpy) pkg_line="^renpy-[0-9]" ;; + (renpy6) pkg_line="^renpy[0-9]-[0-9]" ;; + (wine) pkg_line="^wine-devel-[0-9]" ;; # TODO:BROKEN + # (wine) pkg_line="^wine-[0-9]" ;; + (wine32) pkg_line="^i386-wine-devel-[0-9]" ;; + (*) return 1 ;; + esac + echo "${pkg_line}" +} +# }}} + + +# get:pkgname {{{ +get:pkgname() { + pkg search "$(hlpr:pkgline "${1}")" 2>/dev/null | awk '{print $1}' | sort -V | tail -n1 + return ${?} +} +# }}} + +# get:gametype {{{ +get:gametype() { + unset wine_file wine_args wine_dlls wine_settings + unset -f prerun prerun_first + unset dolphin_file + unset gzdoom_file + unset dosbox_file + unset ft sv pv + if [ -d "${MY_GAMESDIR}/${1}/game" ]; then + if [ -f "${MY_GAMESDIR}/${1}/gamelib.conf" ]; then + . "${MY_GAMESDIR}/${1}/gamelib.conf" + if [ -n "${dolphin_file}" -a -f "${MY_GAMESDIR}/${1}/game/${dolphin_file}" ]; then + ft="$(file -Lbr "${MY_GAMESDIR}/${1}/game/${dolphin_file}")" + if echo "${ft}" | grep -qE '^Nintendo (Wii|GameCube) disc image \(GCZ format\)$'; then + echo "dolphin" + else + hlpr:err "\"${dolphin_file}\" is not a dolphin image file" + return 1 + fi + elif [ -n "${dosbox_file}" -a -f "${MY_GAMESDIR}/${1}/game/${dosbox_file}" ]; then + ft="$(file -Lbr "${MY_GAMESDIR}/${1}/game/${dosbox_file}")" + # MS-DOS executable, LE executable for MS-DOS, DOS4GW DOS extender + # MS-DOS executable + if echo "${ft}" | grep -qE '^MS-DOS executable.*$'; then + echo "dosbox" + else + hlpr:err "\"${dosbox_file}\" is not a dosbox executable file" + return 1 + fi + elif [ -n "${gzdoom_file}" -a -f "${MY_GAMESDIR}/${1}/game/${gzdoom_file}" ]; then + ft="$(file -Lbr "${MY_GAMESDIR}/${1}/game/${gzdoom_file}")" + if echo "${ft}" | grep -qE '^doom main IWAD data containing [0-9]+ lumps$'; then + echo "gzdoom" + else + hlpr:err "\"${gzdoom_file}\" is not a gzdoom WAD file" + return 1 + fi + elif [ -n "${pcsxr_file}" -a -f "${MY_GAMESDIR}/${1}/game/${pcsxr_file}" ]; then + if run:pcsxr:disktype "${MY_GAMESDIR}/${1}/game/${pcsxr_file}" | grep -q '^ Application "PLAYSTATION"$'; then + echo "pcsxr" + else + hlpr:err "\"${pcsxr_file}\" is not a PlayStation (PSX) disk image" + return 1 + fi + elif [ -n "${wine_file}" -a -f "${MY_GAMESDIR}/${1}/game/${wine_file}" ]; then + ft="$(file -Lbr "${MY_GAMESDIR}/${1}/game/${wine_file}" 2>/dev/null)" + # PE32 executable (GUI) Intel 80386, for MS Windows + # PE32 executable (GUI) Intel 80386 (stripped to external PDB), for MS Windows + # PE32 executable (GUI) Intel 80386, for MS Windows, Nullsoft Installer self-extracting archive + # PE32+ executable (GUI) x86-64, for MS Windows + # PE32+ executable (GUI) x86-64 (stripped to external PDB), for MS Windows + # MS-DOS executable PE32 executable (GUI) Intel 80386, for MS Windows, MZ for MS-DOS + if echo "${ft}" | grep -qE '^PE32 executable.*Intel 80386.*for MS Windows' \ + || echo "${ft}" | grep -qE '^MS-DOS executable.*Intel 80386.*for MS Windows'; then + if [ "${arch}" = "amd64" ]; then + if [ -f "/libexec/ld-elf32.so.1" ]; then + echo "wine32" + else + hlpr:err "you can not run win32 executables on ${arch} without lib32 support" + return 1 + fi + elif [ "${arch}" = "i386" ]; then + echo "wine" + fi + elif echo "${ft}" | grep -qE '^PE32\+ executable.*x86-64.*for MS Windows$'; then + if [ "${arch}" = "amd64" ]; then + echo "wine" + else + hlpr:err "you can not run win64 executables on ${arch}" + return 1 + fi + fi + else + hlpr:err "\"${1}\": unknown game type" + return 1 + fi + else + if [ -f "${MY_GAMESDIR}/${1}/game/archive.rpa" ]; then + sv="$(grep -o '[0-9]' "${MY_GAMESDIR}/${1}/game/script_version.txt" 2>/dev/null | head -n1)" + pv="$(pkg rquery '%v' 'renpy' 2>/dev/null | sed 's/\..*//')" + if [ -n "${sv}" -a -n "${pv}" -a "${sv}" = "${pv}" ]; then + echo "renpy" + else + hlpr:wrn "old versions of RenPy not supported (yet)" + echo "renpy6" + fi + else + hlpr:err "\"${1}\": unknown game type" + return 1 + fi + fi + else + return 1 + fi +} +# }}} + + +# prnt:license {{{ +prnt:license() { + sed -n 's/^# //; s/^#$//; 4,25p' "${0}" +} +# }}} + +# prnt:version {{{ +prnt:version() { + echo "${PROGNAME} ${VERSION}" +} +# }}} + +# prnt:usage {{{ +prnt:usage() { + cat <<-__EOF__ | column -ts'|' + run [args]|run game + clean|available in: renpy + cfg, cmd, reg|available in: wine + reinit|available in: dosbox, wine + gui|available in: dolphin, pcsxr + list|show games list + help|show this help + version|print ${PROGNAME} version + upgrade|upgrade existing libs (emulators) + license|print license + __EOF__ +} +# }}} + +# prnt:list {{{ +prnt:list() { + orig_MY_DEBUG="${MY_DEBUG}" + MY_DEBUG=0 + for i in $(cmd:ls "${MY_GAMESDIR}"); do + gametype="$(get:gametype "${i}")" + if [ -n "${gametype}" ]; then + echo "${i}|${gametype}" + fi + done | column -ts'|' + MY_DEBUG="${orig_MY_DEBUG}" +} +# }}} + +# prnt:list_libs {{{ +prnt:list_libs() { + for lib in $(cmd:ls "${MY_LIBDIR}"); do + gameline="$(hlpr:pkgline "${lib#*.}")" + if [ ${?} -eq 0 ]; then + ver="$(grep "${gameline}" "${MY_LIBDIR}/${lib}/.packages.${PROGNAME}" | grep -oE '\-[0-9].*' | sed 's/^-//')" + echo -n "${lib#*.}|${ver}" + if [ -d "${MY_LIBDIR}/${lib}.old" ]; then + oldver="$(grep "${gameline}" "${MY_LIBDIR}/${lib}.old/.packages.${PROGNAME}" | grep -oE '\-[0-9].*' | sed 's/^-//')" + echo "|${oldver}" + else + echo "" + fi + fi + done | column -ts'|' +} +# }}} + + +# gamelib:add {{{ +gamelib:add() { + pkg="$(get:pkgname "${1}")" + pkg_tmp="${MY_TEMPDIR}/.${PROGNAME}-${uid}-${1}" + pkg_unpack="${pkg_tmp}/Unpack" + pkg_workdir="${pkg_tmp}/Workdir" + cmd:mkdir "${pkg_tmp}" + cmd:mkdir "${pkg_unpack}" + # cmd:mkdir "${pkg_workdir}" + if ! pkg fetch -q -U -d -o "${pkg_tmp}" -y "${pkg}" >/dev/null 2>&1; then + cd "${MY_DIR}" + rm -fR "${pkg_tmp}" >/dev/null 2>&1 + hlpr:die "fetch packages for \"${1}\" failed" + fi + for p in $(find "${pkg_tmp}/All" -type f -print 2>/dev/null); do + if ! tar -x -C "${pkg_unpack}" -f "${p}" >/dev/null 2>&1; then + cd "${MY_DIR}" + rm -fR "${pkg_tmp}" >/dev/null 2>&1 + hlpr:die "cannot unpack \"${p##*/}\" for \"${1}\"" + fi + done + chflags -R 0 "${pkg_unpack}" >/dev/null 2>&1 + chown -R ${user}:${group} "${pkg_unpack}" >/dev/null 2>&1 + chmod -R u+rw,go-w "${pkg_unpack}" >/dev/null 2>&1 + mv "${pkg_unpack}/usr/local" "${pkg_workdir}" >/dev/null 2>&1 + rm -R "${pkg_unpack}" >/dev/null 2>&1 + cd "${pkg_workdir}" + for i in "include" "libdata/pkgconfig" "man" "var" "share/aclocal" \ + "share/applications" "share/certs" "share/cmake" "share/bash-completion" \ + "share/doc" "share/emacs" "share/examples" "share/ffmpeg/examples" \ + "share/gtk-doc" "share/info" "share/licenses" "share/locale" "share/pciids" \ + "share/readline"; do + [ -d "${pkg_workdir}/${i}" ] && rm -R "${pkg_workdir}/${i}" >/dev/null 2>&1 + done + cmd:ls "${pkg_tmp}/All" | sed 's/\.[a-z]*$//' > "${pkg_workdir}/.packages.${PROGNAME}" + mv "${pkg_workdir}" "${MY_LIBDIR}/${arch}.${1}" >/dev/null 2>&1 + rm -R "${pkg_tmp}" >/dev/null 2>&1 + return ${?} +} +# }}} + +# gamelib:upgrade {{{ +gamelib:upgrade() { + for lib in $(cmd:ls "${MY_LIBDIR}"); do + gameline="$(hlpr:pkgline "${lib#*.}")" + if [ ${?} -eq 0 ]; then + i="$(grep "${gameline}" "${MY_LIBDIR}/${lib}/.packages.${PROGNAME}")" + p="$(get:pkgname "${lib#*.}")" + if [ "${i}" != "${p}" ]; then + if hlpr:ask "upgrade ${lib#*.}"; then + hlpr:inf "upgrade ${lib}..." + if [ -d "${MY_LIBDIR}/${lib}.old" ]; then + rm -R "${MY_LIBDIR}/${lib}.old" + fi + mv "${MY_LIBDIR}/${lib}" "${MY_LIBDIR}/${lib}.old" + gamelib:add "${lib#*.}" + fi + fi + fi + done + return ${?} +} +# }}} + +# gamelib:rollback {{{ +gamelib:rollback() { + if [ -d "${MY_LIBDIR}/${arch}.${1}.old" ]; then + if [ -d "${MY_LIBDIR}/${arch}.${1}" ]; then + rm -R "${MY_LIBDIR}/${arch}.${1}" >/dev/null 2>&1 + fi + mv "${MY_LIBDIR}/${arch}.${1}.old" "${MY_LIBDIR}/${arch}.${1}" + else + hlpr:die "\"${1}\" can't be rolled back" + fi +} +# }}} + + +# run:gamelib:configure {{{ +run:gamelib:configure() { + # export LC_ALL="C" + # export LC_ALL="ru_RU.UTF-8" + export TZ="UTC" + export HOME="${MY_GAMESDIR}/${1}/prefix" + export XDG_CONFIG_HOME="${HOME}/.config" + export XDG_CACHE_HOME="${HOME}/.cache" + export XDG_DATA_HOME="${HOME}/.local/share" + for dir in "${XDG_CONFIG_HOME}" "${XDG_CACHE_HOME}" "${XDG_DATA_HOME}"; do + cmd:mkdir "${dir}" || hlpr:die "can't create \"${dir}\"" + done +} +# }}} + +# run:dolphin {{{ +run:dolphin() { + export path="${MY_LIBDIR}/${arch}.dolphin" + export bin="${path}/bin/dolphin-emu" + export PATH="${path}/bin:${PATH}" + export LD_LIBRARY_PATH="${path}/lib:${path}/lib/pulseaudio:${LD_LIBRARY_PATH}" + cmd:mkdir "${PREFIX_DIR}" + run:gamelib:configure "${1}" + cd "${GAME_DIR}" + . ../gamelib.conf + [ -f "${GAME_DIR}/${dolphin_file}" ] || hlpr:die "\"${dolphin_file}\" not found" + case "${2}" in + (gui) "${bin}" >>"${PREFIX_DIR}/gamelib.log" 2>&1; return ;; + ('') ;; + (*) hlpr:wrn "unknown argument \"${2}\""; return ;; + esac + if [ -f "${HOME}/.config/dolphin-emu/Dolphin.ini" ]; then + "${bin}-nogui" "${dolphin_file}" >>"${PREFIX_DIR}/gamelib.log" 2>&1 + else + "${bin}" >>"${PREFIX_DIR}/gamelib.log" 2>&1 + fi + return ${?} +} +# }}} + +# run:dosbox:configure {{{ +run:dosbox:configure() { + cat > "${GAME_DIR}/dosbox.conf" <<-__EOF__ + [sdl] + fullscreen=false + fulldouble=false + fullresolution=desktop + windowresolution=original + output=opengl + autolock=true + sensitivity=100 + waitonerror=true + priority=higher,normal + mapperfile=mapper-0.74-3.map + usescancodes=false + + [dosbox] + language= + machine=vesa_nolfb + captures=capture + memsize=64 + + [render] + frameskip=0 + aspect=true + scaler=none + + [cpu] + core=auto + cputype=auto + cycles=auto + cycleup=10 + cycledown=20 + + [mixer] + nosound=false + rate=44100 + blocksize=1024 + prebuffer=512 + + [midi] + mpu401=intelligent + mididevice=default + midiconfig= + + [sblaster] + sbtype=sb16 + sbbase=220 + irq=7 + dma=1 + hdma=5 + sbmixer=true + oplmode=auto + oplemu=default + oplrate=44100 + + [gus] + gus=false + gusrate=44100 + gusbase=240 + gusirq=5 + gusdma=3 + ultradir=C:\ULTRASND + + [speaker] + pcspeaker=true + pcrate=44100 + tandy=auto + tandyrate=44100 + disney=false + + [joystick] + joysticktype=auto + timed=false + autofire=false + swap34=false + buttonwrap=false + + [serial] + serial1=dummy + serial2=disabled + serial3=disabled + serial4=disabled + + [dos] + xms=true + ems=true + umb=true + keyboardlayout=none + + [ipx] + ipx=false + + [autoexec] + echo off + cls + MOUNT C: ${1} + C: + ${2} + EXIT + __EOF__ + return ${?} +} +# }}} + +# run:dosbox {{{ +run:dosbox() { + export path="${MY_LIBDIR}/${arch}.dosbox" + export bin="${path}/bin/dosbox" + export PATH="${path}/bin:${PATH}" + export LD_LIBRARY_PATH="${path}/lib:${LD_LIBRARY_PATH}" + cmd:mkdir "${PREFIX_DIR}" + run:gamelib:configure "${1}" + cd "${GAME_DIR}" + . ../gamelib.conf + [ -f "${GAME_DIR}/${dosbox_file}" ] || hlpr:die "\"${dosbox_file}\" not found" + if [ ! -f "${GAME_DIR}/dosbox.conf" ]; then + run:dosbox:configure "$(pwd)" "${dosbox_file}" + fi + case "${2}" in + (reinit) run:dosbox:configure "$(pwd)" "${dosbox_file}"; return ;; + ('') ;; + (*) hlpr:wrn "unknown argument \"${2}\""; return ;; + esac + ${bin} "${GAME_DIR}/${dosbox_file}" >>"${PREFIX_DIR}/gamelib.log" 2>&1 + return ${?} +} +# }}} + +# run:gzdoom {{{ +run:gzdoom() { + export path="${MY_LIBDIR}/${arch}.gzdoom" + export bin="${path}/bin/gzdoom" + export PATH="${path}/bin:${PATH}" + export LD_LIBRARY_PATH="${path}/lib:${LD_LIBRARY_PATH}" + cmd:mkdir "${PREFIX_DIR}" + run:gamelib:configure "${1}" + cd "${GAME_DIR}" + . ../gamelib.conf + [ -f "${GAME_DIR}/${gzdoom_file}" ] || hlpr:die "\"${gzdoom_file}\" not found" + if [ ! -f "${XDG_CONFIG_HOME}/gzdoom/gzdoom.pk3" ]; then + ${bin} -iwad "/dev/null" >/dev/null 2>&1 + cmd:mkdir "${XDG_CONFIG_HOME}/gzdoom" + cmd:ln "${path}/share/doom/gzdoom/gzdoom.pk3" "${XDG_CONFIG_HOME}/gzdoom/gzdoom.pk3" + fi + ${bin} -iwad "${GAME_DIR}/${gzdoom_file}" >>"${PREFIX_DIR}/gamelib.log" 2>&1 + return ${?} +} +# }}} + +# run:pcsxr:disktype {{{ +run:pcsxr:disktype() { + if [ ! -x "${MY_BINDIR}/disktype.${arch}" ]; then + pkg_tmp="${MY_TEMPDIR}/.${PROGNAME}-${uid}-disktype" + pkg fetch -q -U -d -o "${pkg_tmp}" -y "disktype" >/dev/null 2>&1 + cd "${pkg_tmp}" \ + tar -x -C "${pkg_tmp}" -f "${pkg_tmp}"/All/disktype* >/dev/null 2>&1 + chflags -R 0 "${pkg_tmp}" >/dev/null 2>&1 + chown -R ${user}:${group} "${pkg_tmp}" >/dev/null 2>&1 + chmod -R u+rw,go-w "${pkg_tmp}" >/dev/null 2>&1 + mv "${pkg_tmp}/usr/local/bin/disktype" "${MY_BINDIR}/disktype.${arch}" >/dev/null 2>&1 + cd "${MY_DIR}" + rm -R "${pkg_tmp}" >/dev/null 2>&1 + fi + "${MY_BINDIR}/disktype.${arch}" "${1}" 2>/dev/null + return ${?} +} +# }}} + +# run:pcsxr:configure {{{ +run:pcsxr:configure() { + cmd:mkdir "${HOME}/.pcsxr/bios" + cmd:mkdir "${HOME}/.pcsxr/plugins" + for plugin in $(cmd:ls "${path}/lib/pcsxr"); do + if [ ! -f "${HOME}/.pcsxr/plugins/${plugin}" ]; then + cmd:ln "${path}/lib/pcsxr/${plugin}" "${HOME}/.pcsxr/plugins/${plugin}" + fi + done + return ${?} +} +# }}} + +# run:pcsxr {{{ +run:pcsxr() { + local first=0 + export path="${MY_LIBDIR}/${arch}.pcsxr" + export bin="${path}/bin/pcsxr" + export PATH="${path}/bin:${PATH}" + export LD_LIBRARY_PATH="${path}/lib:${LD_LIBRARY_PATH}" + cmd:mkdir "${PREFIX_DIR}" + run:gamelib:configure "${1}" + if [ ! -d "${HOME}/.pcsxr" ]; then + run:pcsxr:configure + first=1 + fi + cd "${GAME_DIR}" + . ../gamelib.conf + if [ -n "${pcsxr_plugins_dir}" -a -d "${pcsxr_plugins_dir}" ]; then + cmd:mkdir "${HOME}/.pcsxr/plugins" + IFS=$'\n' + for plugin in $(cmd:ls "${pcsxr_plugins_dir}"); do + if [ ! -f "${HOME}/.pcsxr/plugins/${plugin}" ]; then + cmd:ln "${pcsxr_plugins_dir}/${plugin}" "${HOME}/.pcsxr/plugins/${plugin}" + fi + done + fi + if [ -n "${pcsxr_bios_dir}" -a -d "${pcsxr_bios_dir}" ]; then + cmd:mkdir "${HOME}/.pcsxr/bios" + IFS=$'\n' + for bios in $(cmd:ls "${pcsxr_bios_dir}"); do + if [ ! -f "${HOME}/.pcsxr/plugins/${bios}" ]; then + cmd:ln "${pcsxr_bios_dir}/${bios}" "${HOME}/.pcsxr/bios/${bios}" + fi + done + fi + case "${2}" in + (gui) ${bin} >>"${PREFIX_DIR}/gamelib.log" 2>&1; return ;; + (reinit) run:pcsxr:configure; return ;; + ('') ;; + (*) hlpr:wrn "unknown argument \"${2}\""; return ;; + esac + if [ ${first} -eq 1 ]; then + prerun_first >/dev/null 2>&1 + else + prerun >/dev/null 2>&1 + fi + if [ -f "${HOME}/.pcsxr/pcsxr.cfg" ]; then + ${bin} -nogui -cdfile "${GAME_DIR}/${pcsxr_file}" >>"${PREFIX_DIR}/gamelib.log" 2>&1 + else + ${bin} >>"${PREFIX_DIR}/gamelib.log" 2>&1 + fi + return ${?} +} +# }}} + +# run:renpy:common {{{ +run:renpy:common() { + # if [ "x${path}" != "x" ]; then + # return 1 + # fi + export bin="${path}/bin/renpy" + export PATH="${path}/bin:${PATH}" + export LD_LIBRARY_PATH="${path}/lib:${LD_LIBRARY_PATH}" + if head -n1 "${bin}" | grep -q '^#!/usr/local/bin'; then + sed -i '' 's,^#!/usr/local/bin/,#!/usr/bin/env ,' "$(readlink -f -- "${bin}")" + fi + p="$(head -n1 "${bin}" | awk '{print $2}')" + export PYTHONPATH="${path}/lib:${path}/lib/${p}/site-packages:${PYTHONPATH}" + cmd:mkdir "${PREFIX_DIR}" + run:gamelib:configure "${1}" + cd "${GAME_DIR}" + case "${2}" in + (clean) + read -p "Are you REALLY sure? (type \"yes\" if so) " answer + if [ "${answer}" = "yes" ]; then + [ -f "${GAME_DIR}/traceback.txt" ] && rm "${GAME_DIR}/traceback.txt" >/dev/null 2>&1 + [ -f "${GAME_DIR}/log.txt" ] && rm "${GAME_DIR}/log.txt" >/dev/null 2>&1 + [ -d "${GAME_DIR}/cache" ] && rm -R "${GAME_DIR}/cache" >/dev/null 2>&1 + # [ -d "${GAME_DIR}/saves" ] && rm -R "${GAME_DIR}/saves" >/dev/null 2>&1 + fi + return + ;; + ('') ;; + (*) hlpr:wrn "unknown argument \"${2}\""; return ;; + esac + ${bin} "${GAME_DIR}" >>"${PREFIX_DIR}/gamelib.log" 2>&1 + return ${?} +} +# }}} + +run:renpy() { + export path="${MY_LIBDIR}/${arch}.renpy" + run:renpy:common "${@}" +} + +run:renpy6() { + export path="${MY_LIBDIR}/${arch}.renpy6" + run:renpy:common "${@}" +} + +# run:wine:winetricks {{{ +run:wine:winetricks() { + if [ ! -x "${MY_BINDIR}/winetricks" ]; then + url="https://raw.githubusercontent.com/Winetricks/winetricks/master/src/winetricks" + fetch -qo "${MY_BINDIR}/winetricks" "${url}" >/dev/null 2>&1 + chmod ug+x go-w "${MY_BINDIR}/winetricks" >/dev/null 2>&1 + else + "${MY_BINDIR}/winetricks" --self-update + fi + export WINE="${bin}" + "${MY_BINDIR}/winetricks" ${@} >>"${PREFIX_DIR}/gamelib.log" 2>&1 + return ${?} +} +# }}} + +# run:wine:configure {{{ +run:wine:configure() { + ${bin} wineboot.exe -i >/dev/null 2>&1 + if [ ! -h "${HOME}/.wine/dosdevices/e:" -a ! -d "${HOME}/.wine/dosdevices/e:" ]; then + cmd:ln "${GAME_DIR}" "${HOME}/.wine/dosdevices/e:" + fi + run:wine:winetricks settings \ + "autostart_winedbg=disabled" "csmt=on" "fontfix" "fontsmooth=gray" \ + "glsl=enabled" "grabfullscreen=y" "hidewineexports=enable" "isolate_home" \ + "mimeassoc=off" "multisampling=enabled" "nocrashdialog" "remove_mono" \ + "sandbox" "sound=oss" "vd=off" "videomemorysize=default" "win7" \ + "windowmanagerdecorated=y" "windowmanagermanaged=y" >/dev/null 2>&1 + run:wine:winetricks corefonts + for i in $(cmd:ls "${HOME}/.wine/dosdevices/"); do + if [ "${i}" != "c:" -a "${i}" != "e:" ]; then + rm "${HOME}/.wine/dosdevices/${i}" >/dev/null 2>&1 + fi + done + ${bin} winecfg.exe >/dev/null 2>&1 + return ${?} +} +# }}} + +# run:wine:common {{{ +run:wine:common() { + local first=0 + export PATH="${MY_BINDIR}:${PATH}" + run:gamelib:configure "${1}" + cmd:mkdir "${PREFIX_DIR}" + if [ ! -d "${HOME}/.wine" ]; then + run:wine:configure "${1}" + first=1 + fi + cd "${GAME_DIR}" + . ../gamelib.conf + [ -f "${GAME_DIR}/${wine_file}" ] || hlpr:die "\"${wine_file}\" not found" + if [ -n "${wine_dlls}" ]; then + # run:wine:winetricks dlls ${wine_dlls} >>"${PREFIX_DIR}/gamelib.log" 2>&1 + run:wine:winetricks ${wine_dlls} >>"${PREFIX_DIR}/gamelib.log" 2>&1 + fi + if [ -n "${wine_settings}" ]; then + run:wine:winetricks settings ${wine_settings} >>"${PREFIX_DIR}/gamelib.log" 2>&1 + fi + case "${2}" in + (cfg) ${bin} winecfg.exe >>"${PREFIX_DIR}/gamelib.log" 2>&1; return ;; + (cmd) ${bin} cmd.exe >>"${PREFIX_DIR}/gamelib.log" 2>&1; return ;; + (reg) ${bin} regedit.exe >>"${PREFIX_DIR}/gamelib.log" 2>&1; return ;; + (reinit) + run:wine:configure "${1}" + prerun_first >/dev/null 2>&1 + return + ;; + (sh) sh; return ;; + ('') ;; + (*) hlpr:wrn "unknown argument \"${2}\""; return ;; + esac + if [ ${first} -eq 1 ]; then + prerun_first >/dev/null 2>&1 + else + prerun >/dev/null 2>&1 + fi + ${bin} "${GAME_DIR}/${wine_file}" ${wine_args} >>"${PREFIX_DIR}/gamelib.log" 2>&1 + return ${?} +} +# }}} + +# run:wine32 {{{ +run:wine32() { + export path="${MY_LIBDIR}/${arch}.wine32" + export bin="${path}/bin32/wine" + # export bin="${path}/bin32/wine" + export PATH="${path}/bin:${path}/bin32:${PATH}" + export LIBGL_DRIVERS_PATH="${path}/lib32/.libGL/dri" + # export LD_32_LIBRARY_PATH="${path}/lib32:${path}/lib32/wine:${path}/lib32/.libGL:${path}/lib32/.libGL/dri/:${LD_32_LIBRARY_PATH}" + export LD_32_LIBRARY_PATH="${path}/lib32:${path}/lib32/wine/i386-unix:${path}/lib32/wine/i386-windows:${path}/lib32/.libGL:${path}/lib32/.libGL/dri/:${LD_32_LIBRARY_PATH}" + export LD_32_LIBRARY_PATH_RPATH=y + run:wine:common "${@}" + return ${?} +} +# }}} + +# run:wine {{{ +run:wine() { + export path="${MY_LIBDIR}/${arch}.wine" + [ "${arch}" = "amd64" ] && suf="64" + export bin="${path}/bin/wine${suf}" + export PATH="${path}/bin:${PATH}" + export LD_LIBRARY_PATH="${path}/lib:${path}/lib/wine:${path}/lib32/wine/i386-unix:${path}/lib32/wine/i386-windows:${LD_LIBRARY_PATH}" + run:wine:common "${@}" + return ${?} +} +# }}} + +# run:gamelib {{{ +run:gamelib() { + [ -n "${DISPLAY}" ] || hlpr:die "graphical session needed" + gamelib="$(get:gametype "${1}")" + [ ${?} -eq 0 ] || return 1 + if [ -f "${MY_LOCKFILE}" ]; then + trap '' EXIT INT ABRT TERM + hlpr:die "another instance of ${PROGNAME} is running with PID $(cat "${MY_LOCKFILE}")" + else + trap hlpr:shutdown EXIT INT ABRT TERM + echo ${$} > "${MY_LOCKFILE}" + fi + if [ ! -d "${MY_LIBDIR}/${arch}.${gamelib}" ]; then + hlpr:inf "prepare ${gamelib}..." + gamelib:add "${gamelib}" + fi + export GAME_DIR="${MY_GAMESDIR}/${1}/game" + export PREFIX_DIR="${MY_GAMESDIR}/${1}/prefix" + run:${gamelib} "${@}" + return ${?} +} +# }}} + + +[ ${euid} -ne 0 ] || hlpr:die "do not run this program as root!" +[ ${euid} -eq ${uid} ] || hlpr:die "EUID and UID does not match" + +case "${arch}" in + (amd64) ;; + (i386) hlpr:wrn "\"${arch}\" is not tested" ;; + (*) hlpr:die "unsupported architecture \"${arch}\"" ;; +esac + +hlpr:startup + +case "${1}" in + (run) shift; run:gamelib "${@}" ;; + (list) prnt:list ;; + (help) prnt:usage ;; + (version) prnt:version ;; + (license) prnt:license ;; + (list-libs) prnt:list_libs ;; + (upgrade) gamelib:upgrade ;; + (rollback) shift; gamelib:rollback "${@}" ;; + (*) MY_DEBUG=1 hlpr:die "unknown action \"${1}\", try \"help\"" ;; +esac + +exit ${?} + +# vim: foldlevel=0 diff --git a/gamelib.gui b/gamelib.gui new file mode 100755 index 0000000..268c51a --- /dev/null +++ b/gamelib.gui @@ -0,0 +1,70 @@ +#!/bin/sh + +VERSION="0.2" + +PROGNAME="gamelib.gui" + +GAMELIB="$(dirname -- "$(readlink -f -- "${0}")")/gamelib" + +hlpr:die() { + echo "${@}" >&2 + exit 1 +} + +[ $(${GAMELIB} list | wc -l) -ne 0 ] || hlpr:die "you have no games" + +orig_IFS="${IFS}" +IFS=$'\n' +for game in $(${GAMELIB} list); do + img="${GAMELIB_GAMES_DIR}/${game%% *}/gamelib_gui.png" + if [ -r "${img}" ]; then + image="${img}" + else + image="applications-games" + fi + conf="${GAMELIB_GAMES_DIR}/${game%% *}/gamelib_gui.conf" + if [ -f "${conf}" ]; then + . "${conf}" + [ -n "${name}" ] || name="$(echo "${game%% *}" | sed 's/-/ /g')" + [ -n "${description}" ] || description="${game##* }" + else + name="$(echo "${game%% *}" | sed 's/-/ /g')" + description="${game##* }" + fi + games="${games} +${game%% *} ${game##* } '${image}' '${name}' ' ${description} '" +done +games="$(echo "${games}" | sed '/^$/d')" +gnum="$(echo "${games}" | wc -l | sed 's/^[[:space:]]*//')" + +# game: 96×32 + +game="$(eval yad \ + --width 600 --height 600 --center \ + --class "'${PROGNAME%.*}'" \ + --title "'${PROGNAME}'" \ + --text "'$(${GAMELIB} version) (gui: ${VERSION})\n${gnum} games found:'" \ + --image "'applications-games'" --image-on-top \ + --buttons-layout "'center'" \ + --button "'_cancel:1'" \ + --button "'_run:0'" \ + --list --no-headers \ + --print-column 1 \ + --search-column 0 \ + --expand-column 4 \ + --tooltip-column 5 \ + --ellipsize-cols 4,5 \ + --separator "''" \ + --column game:HD --column type:HD --column icon:IMG --column name:TEXT --column description:HD \ + ${games})" +exitcode=${?} + + +case ${exitcode} in + (0) ${GAMELIB} run "${game}" ;; + (1) echo "${PROGNAME}: canceled" >&2 ;; + (252) echo "${PROGNAME}: escaped" >&2 ;; + (*) echo "${PROGNAME}: exited with ${exitcode}" >&2 ;; +esac + +exit ${exitcode} diff --git a/gamelib.new b/gamelib.new new file mode 100755 index 0000000..64cc0ac --- /dev/null +++ b/gamelib.new @@ -0,0 +1,128 @@ +#!/bin/sh + +# +# Copyright (c) 2019-2021, mord0d and their slaves +# 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 OWNER 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. +# + +VERSION="0.6" +PROGNAME="gamelib" + +arch="$(uname -m)" +uid="$(id -u)" +gid="$(id -g)" + +: ${TMPDIR:=/tmp} +: ${GAMELIB_DEBUG:=0} + +LOCKFILE="${TMPDIR}/.${PROGNAME}-${uid}.lock" +TEMPDIR="${TMPDIR}/.${PROGNAME}-${uid}" + +# gl_log {{{ +gl_log() { + local lvl="${1}" + shift + if [ "${lvl}" = "FTL" ] || [ "${GAMELIB_DEBUG}" -ne 0 ] || [ -z "${LOGFILE}" -o ! -w "${LOGFILE}" ]; then + echo "[${lvl}] ${*}" >&2 + fi + echo "[${lvl}:${$}] $(date -j '+%Y-%m-%d %H:%M:%S') ${*}" >> "${LOGFILE:=/dev/null}" +} +# }}} + +gl_run() { +} +gl_edit() { +} +gl_upgrade() { +} +gl_rollback() { +} +gl_usage() { +} + +# gl_version {{{ +gl_version() { + echo "${PROGNAME} ${VERSION}" +} +# }}} + +# gl_license {{{ +gl_license() { + sed -n 's/^# //; s/^#$//; 4,25p' "${0}" +} +# }}} + +gl_list() { +} + +game='' +subcommands='' +upgrade=0 +rollback=0 +edit=0 + +while :; do + case "${1}" in + ('') break ;; + (-h|--help) gl_usage; exit 0 ;; + (-V|--version) gl_version; exit 0 ;; + (-L|--license) gl_license; exit 0 ;; + (-l|--list) gl_list; exit 0 ;; + (-v|--verbose) GAMELIB_DEBUG=1; shift ;; + (-u|--upgrade) upgrade=1; rollback=0; shift ;; + (-b|--rollback) rollback=1; upgrade=0; shift ;; + (-r|--run) shift; subcommands="${subcommands} ${1}"; shift ;; + (-e|--edit) edit=1; shift ;; + (-w|--workdir) shift; GAMELIB_GAMES_DIR="${1}"; shift ;; + (-*) gl_log ERR "Unknown option \"${1}\"!"; exit 1 ;; + (*) game="${1}"; shift ;; + esac +done + +if [ -z "${GAMELIB_GAMES_DIR}" ]; then + gl_log FTL "\"\$GAMELIB_GAMES_DIR\" is not defined!" + exit 1 +fi +if [ ! -d "${GAMELIB_GAMES_DIR}" ]; then + gl_log FTL "\"\$GAMELIB_GAMES_DIR\" is not a directory" + exit 1 +fi + +if [ -z "${game}" ]; then + gl_log FTL "No game specified!" + gl_list + exit 1 +fi + +if [ ${edit} -eq 1 ]; then + gl_edit "${game}" + exit ${?} +fi + +if [ ${upgrade} -eq 1 ]; then + gl_upgrade "${game}" +elif [ ${rollback} -eq 1 ]; then + gl_rollback "${game}" +else + gl_run "${game}" "${subcommands}" +fi