From a46776838aa7cbe8306bafbda12cb222c36cbf16 Mon Sep 17 00:00:00 2001 From: v4n <105587619+v4n00@users.noreply.github.com> Date: Thu, 16 Jan 2025 11:22:19 +0200 Subject: [PATCH] feat: enable/disable functionality --- README.md | 29 ++++-- h2mm | 304 +++++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 241 insertions(+), 92 deletions(-) diff --git a/README.md b/README.md index f76a025..a579672 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ - [Basic usage](#basic-usage) - [Install a mod](#install-a-mod) - [Uninstall a mod](#uninstall-a-mod) + - [Enable/disable mods](#enabledisable-mods) - [List installed mods](#list-installed-mods) - [Advanced usage](#advanced-usage) - [Shortcuts](#shortcuts) @@ -57,7 +58,7 @@ h2mm install -n "Example mod" mod.patch_0 mod.patch_0.stream # -n is mandatory w h2mm install -n "Example mod" mod* # using a wildcard to include all files ``` -Important: If the mod has more than 1 variant, you need to install the one you want by unarchiving it separately. +> Currently, if the mod has more than 1 variant, you need to install the one you want by unarchiving it separately. #### Uninstall a mod @@ -66,6 +67,16 @@ h2mm uninstall "Example mod" h2mm uninstall -i 1 # uninstall mod with index 1 ``` + +#### Enable/disable mods + +```bash +h2mm enable "Example mod" +h2mm enable -i 1 # enable mod with index 1 +h2mm disable "Example mod" +h2mm disable -i 1 # disable mod with index 1 +``` + #### List installed mods ```bash @@ -76,10 +87,12 @@ h2mm list ### Shortcuts -You can use the short form of the commands to save some time. The shortcuts are: +You can use the short form of commands to save some time. The shortcuts are: - `i` for `install` - `u` for `uninstall` +- `e` for `enable` +- `d` for `disable` - `l` for `list` - `ex` for `export` - `im` for `import` @@ -113,8 +126,10 @@ Feel free to contribute to this project by creating a pull request or opening an ## Planned features - [x] Check for mod updates -- [x] Change to `.tar.gz` for exporting and importing -- [ ] ! Enable/disable mods -- [ ] ! Provide fixes for breaking updates -- [ ] !! Easier way to change mod presets -- [ ] !!! Find a way to make use of `manifest.json` and simplify installing variants +- [x] Enable/disable mods +- [ ] Easier way to change mod presets +- [ ] Find a way to make use of `manifest.json` and simplify installing variants +- [x] [DEV] Change to `.tar.gz` for exporting and importing +- [ ] [DEV] Provide fixes for breaking updates +- [ ] [DEV] Optimize code - throw errors in 1 line +- [ ] [DEV] Rewrite some code to be more readable diff --git a/h2mm b/h2mm index 01c7971..6e0ffee 100755 --- a/h2mm +++ b/h2mm @@ -27,6 +27,31 @@ function get_basename() { echo $(get_filename_without_path "$1" | sed -E 's/\.+.*//') } +function get_extension() { + echo $(get_filename_without_path "$1" | sed -E 's/.*patch_[0-9]+//') +} + +function get_files_by_entry_from_db() { + echo $(echo "$1" | cut -d',' -f4- | tr ',' ' ' | head -1) +} + +function get_mod_name_and_index() { + if [[ -n "$mod_index" ]]; then # if mod index exists + entry=$(grep "^${mod_index}," "$DB_FILE") + mod_name=$(echo "$entry" | awk -F, '{print $3}') + elif [[ -n "$mod_name" ]]; then # if mod name exists + entry=$(grep -i ",$mod_name," "$DB_FILE") + mod_index=$(echo "$entry" | awk -F, '{print $1}' | head -1) + fi + + if [[ -z "$entry" || -z "$mod_index" || -z "$mod_name" ]]; then + echo -e "${RED}Error${NC}: Mod not found." >&2 + exit 1 + fi + + status=$(echo "$entry" | awk -F, '{print $2}') +} + function find_game_directory() { local search_dir="${HOME}" local target_dir="Steam/steamapps/common/Helldivers\ 2/data" @@ -35,7 +60,6 @@ function find_game_directory() { if [[ -f "$H2PATH" ]]; then saved_dir=$(cat "$H2PATH") if [[ -d "$saved_dir" ]]; then - echo "Using saved game directory \$MODS_DIR: $saved_dir" >&2 echo "$saved_dir" return else @@ -77,7 +101,7 @@ function initialize_directories() { if [[ ! -f "$DB_FILE" ]]; then touch "$DB_FILE" if [[ $? -eq 0 ]]; then - echo "Database file created at: $DB_FILE" + echo -e "Database file ${GREEN}created${NC}: $DB_FILE" else echo -e "${RED}Error${NC}: Could not create database file." >&2 exit 1 @@ -131,6 +155,26 @@ function display_uninstall_help() { echo " h2mm uninstall -i 1 # uninstall mod with index 1" } +function display_enable_help() { + echo "Usage: h2mm enable [options] \"\"" + echo "Short form: h2mm e" + echo "Options:" + echo " -i Index of the mod to enable." + echo "Usage:" + echo " h2mm enable \"Example mod\"" + echo " h2mm enable -i 1 # enable mod with index 1" +} + +function display_disable_help() { + echo "Usage: h2mm disable [options] \"\"" + echo "Short form: h2mm d" + echo "Options:" + echo " -i Index of the mod to disable." + echo "Usage:" + echo " h2mm disable \"Example mod\"" + echo " h2mm disable -i 1 # disable mod with index 1" +} + function display_list_help() { echo "Usage: h2mm list" echo "Short form: h2mm l" @@ -190,6 +234,119 @@ function check_for_updates() { # Mod management +function mod_disable() { + local mod_name="" + local mod_index="" + + [[ $# -eq 0 ]] && { display_disable_help; exit 0; } + + # parse arguments + while [[ $# -gt 0 ]]; do + case "$1" in + -i) + mod_index="$2"; shift 2 + ;; + --help|-h) + display_disable_help; exit 0 + ;; + *) + mod_name="$1"; shift 1 + ;; + esac + done + + if [[ -z "$mod_name" && -z "$mod_index" ]]; then + echo -e "${RED}Error${NC}: Mod name or index is required to disable." >&2 + exit 1 + fi + + # find mod files + get_mod_name_and_index "$mod_name" "$mod_index" + + if [[ "$status" == "DISABLED" ]]; then + echo -e "${RED}Error${NC}: Mod $mod_name is already disabled." >&2 + exit 1 + fi + + # disable each mod file by adding disabled_ to the start of the filename + files=$(get_files_by_entry_from_db "$entry") + for file in $files; do + if [[ ! -f "$MODS_DIR/$file" ]]; then + echo -e "${RED}Error${NC}: Mod file $file does not exist." >&2 + exit 1 + else + disabled_file="disabled_$file" + mv "$MODS_DIR/$file" "$MODS_DIR/$disabled_file" + echo -e "Disabled ${ORANGE}$file${NC} (changed to ${GREEN}\$MODS_DIR/$disabled_file${NC})." >&2 + fi + done + + # update the database + sed -i "/^$mod_index,/s/ENABLED/DISABLED/" "$DB_FILE" + + if [[ $? -eq 0 ]]; then + echo -e "Mod $mod_name ${ORANGE}disabled${NC} successfully." >&2 + else + echo -e "${RED}Error${NC}: Failed to disable mod." >&2 + exit 1 + fi +} + +function mod_enable() { + local mod_name="" + local mod_index="" + + [[ $# -eq 0 ]] && { display_enable_help; exit 0; } + + # parse arguments + while [[ $# -gt 0 ]]; do + case "$1" in + -i) + mod_index="$2"; shift 2 + ;; + --help|-h) + display_enable_help; exit 0 + ;; + *) + mod_name="$1"; shift 1 + ;; + esac + done + + [[ -z "$mod_name" && -z "$mod_index" ]] && { echo -e "${RED}Error${NC}: Mod name or index is required to enable." >&2; exit 1; } + + # find mod files + get_mod_name_and_index "$mod_name" "$mod_index" + + [[ "$status" == "ENABLED" ]] && { echo -e "${RED}Error${NC}: Mod $mod_name is already enabled." >&2; exit 1; } + + files=$(get_files_by_entry_from_db "$entry") + + # enable each mod file by removing disabled_ from the start of the filename + for file in $files; do + disabled_file="disabled_$file" + + # check if the files exists + [[ -f "$MODS_DIR/$disabled_file" ]] || { echo -e "${RED}Error${NC}: Mod file $file does not exist." >&2; exit 1; } + + mv "$MODS_DIR/$disabled_file" "$MODS_DIR/$file" + + # check if the file was moved successfully + [[ $? -ne 0 ]] && { echo -e "${RED}Error${NC}: Could not enable mod file $disabled_file." >&2; exit 1; } + echo -e "Enabled ${ORANGE}$disabled_file${NC} (changed to ${GREEN}\$MODS_DIR/$file${NC})." >&2 + done + + # update the database + sed -i "/^$mod_index,/s/DISABLED/ENABLED/" "$DB_FILE" + + if [[ $? -eq 0 ]]; then + echo -e "Mod $mod_name ${GREEN}enabled${NC} successfully." >&2 + else + echo -e "${RED}Error${NC}: Failed to enable mod." >&2 + exit 1 + fi +} + function mod_reset() { if [[ "$1" == "--help" || "$1" == "-h" ]]; then display_reset_help @@ -210,24 +367,19 @@ function mod_reset() { function mod_install() { local mod_name="" - local mod_files=() local mod_dir="" + local mod_files=() - if [[ $# -eq 0 ]]; then - display_install_help - exit 0 - fi + [[ $# -eq 0 ]] && { display_install_help; exit 0; } # parse arguments while [[ $# -gt 0 ]]; do case "$1" in -n) - mod_name="$2" - shift 2 + mod_name="$2"; shift 2 ;; --help|-h) - display_install_help - exit 0 + display_install_help; exit 0 ;; *) if [[ -f "$1" && "$1" == *.zip ]]; then @@ -239,20 +391,14 @@ function mod_install() { fi shift ;; - esac + esac done # zip file containing mod files if [[ -n "$mod_zip" ]]; then - if ! command -v unzip &> /dev/null; then - echo -e "${RED}Error${NC}: unzip is not installed, please install the package and try again." >&2 - exit 1 - fi + command -v unzip &> /dev/null || { echo -e "${RED}Error${NC}: unzip package is not installed." >&2; exit 1; } - if [[ ! -f "$mod_zip" ]]; then - echo -e "${RED}Error${NC}: Zip file $mod_zip does not exist." >&2 - exit 1 - fi + [[ ! -f "$mod_zip" ]] && { echo -e "${RED}Error${NC}: Zip file $mod_zip does not exist." >&2; exit 1; } # check if mod name was provided, otherwise use the zip file name, get rid of .zip and version numbers if [[ -z "$mod_name" ]]; then @@ -265,10 +411,8 @@ function mod_install() { # directory containing mod files if [[ -n "$mod_dir" ]]; then - if [[ ! -d "$mod_dir" ]]; then - echo -e "${RED}Error${NC}: Directory $mod_dir does not exist." >&2 - exit 1 - fi + # verify directory exists + [[ ! -d "$mod_dir" ]] && { echo -e "${RED}Error${NC}: Directory $mod_dir does not exist." >&2; exit 1; } readarray -d '' mod_files < <(find "$mod_dir" -type f -name "*.patch_*" -print0) if [[ -z "$mod_name" ]]; then @@ -277,20 +421,15 @@ function mod_install() { fi # verify minimum information required - if [[ -z "$mod_name" || ${#mod_files[@]} -eq 0 ]]; then - echo -e "${RED}Error${NC}: Mod name and files are required." >&2 - exit 1 - fi + [[ -z "$mod_name" || ${#mod_files[@]} -eq 0 ]] && { echo -e "${RED}Error${NC}: Mod name and files are required." >&2; exit 1; } # verify mod files exist and is not directory for file in "${mod_files[@]}"; do if [[ ! -f "$file" ]]; then - if [[ ! -d "$file" ]]; then - echo -e "${RED}Error${NC}: File $file does not exist." >&2 - exit 1 - else - mod_files=(${mod_files[@]/$file}) - fi + # if it isn't a file, check if it's a directory + [[ ! -d "$file" ]] && { echo -e "${RED}Error${NC}: File $file does not exist." >&2; exit 1; } + + mod_files=(${mod_files[@]/$file}) fi done @@ -313,7 +452,7 @@ function mod_install() { patch_count["$base_name"]=$count # if the file has an extension, look for the last patch number and use that - extension=$(echo "$file" | sed -E 's/.*patch_[0-9]+//') + extension=$(get_extension "$file") if [[ -n "$extension" ]]; then target_file="${base_name}.patch_$((patch_count[$base_name] - 1))${extension}" else @@ -321,60 +460,36 @@ function mod_install() { fi target_files+=($target_file) - cp "$file" "$MODS_DIR/$target_file" - if [[ $? -eq 0 ]]; then - echo -e "Mod file ${ORANGE}$file${NC} installed at ${GREEN}\$MODS_DIR/$target_file${NC}." >&2 - else - echo -e "${RED}Error${NC}: Could not install mod file $file." >&2 - exit 1 - fi + + # verify installation worked + [[ $? -ne 0 ]] && { echo -e "${RED}Error${NC}: Could not install mod file $file." >&2; exit 1; } + echo -e "Mod file ${ORANGE}$file${NC} installed at ${GREEN}\$MODS_DIR/$target_file${NC}." >&2 done # add entry to database next_id=$(awk -F, 'END {print $1 + 1}' "$DB_FILE") - echo "$next_id,$mod_name,${target_files[*]}" >> "$DB_FILE" - echo -e "Mod $mod_name ($base_name) ${GREEN}installed successfully${NC}." >&2 -} - -function mod_list() { - if [[ "$1" == "--help" || "$1" == "-h" ]]; then - display_list_help - exit 0 - fi - - if [[ ! -s "$DB_FILE" ]]; then - echo "No mods installed." - return - fi - - echo "Installed mods:" >&2 - awk -F, '{ if (length($3) > 150) $3 = substr($3, 1, 147) "..."; printf "%2s. %s (%s)\n", $1, $2, $3 }' "$DB_FILE" + echo "$next_id,ENABLED,$mod_name,${target_files[*]}" >> "$DB_FILE" + echo -e "Mod $mod_name ($base_name) ${GREEN}installed${NC} successfully." >&2 } function mod_uninstall() { local mod_name="" local mod_index="" - if [[ $# -eq 0 ]]; then - display_uninstall_help - exit 0 - fi + [[ $# -eq 0 ]] && { display_uninstall_help; exit 0; } # parse arguments while [[ $# -gt 0 ]]; do case "$1" in -i) - mod_index="$2" - shift 2 + mod_index="$2"; shift 2 ;; --help|-h) - display_uninstall_help - exit 0 + display_uninstall_help; exit 0 ;; *) - mod_name="$1" - shift 1 + mod_name="$1"; shift 1 ;; esac done @@ -385,21 +500,11 @@ function mod_uninstall() { fi # find mod files - if [[ -n "$mod_index" ]]; then - entry=$(grep "^${mod_index}," "$DB_FILE") - mod_name=$(echo "$entry" | awk -F, '{print $2}') - elif [[ -n "$mod_name" ]]; then - entry=$(grep -i ",$mod_name," "$DB_FILE") - mod_index=$(echo "$entry" | awk -F, '{print $1}' | head -1) - fi - - if [[ -z "$entry" ]]; then - echo -e "${RED}Error${NC}: Mod not found." >&2 - exit 1 - fi + get_mod_name_and_index "$mod_name" "$mod_index" # delete mod files - files=$(echo "$entry" | cut -d',' -f3- | tr ',' ' ' | head -1) + files=$(get_files_by_entry_from_db "$entry") + echo "$files" declare -A downgrades for file in $files; do if [[ ! -f "$MODS_DIR/$file" ]]; then @@ -407,7 +512,12 @@ function mod_uninstall() { exit 1 else echo -e "Removing ${ORANGE}\$MODS_DIR/$file${NC}." >&2 - rm -f "$MODS_DIR/$file" + rm "$MODS_DIR/$file" + + if [[ $? -ne 0 ]]; then + echo -e "${RED}Error${NC}: Could not remove mod file $file." >&2 + exit 1 + fi base_name=$(get_basename "$file") current_version=$(echo $file | grep -oP '(?<=patch_)\d+') @@ -426,7 +536,7 @@ function mod_uninstall() { patch_version=$(echo $patch | grep -oP '(?<=patch_)\d+') if [[ $patch_version -gt ${downgrades[$file]} ]]; then new_version=$((patch_version - downgrades[$base_name] - 1)) - extension=$(echo "$patch" | sed -E 's/.*patch_[0-9]+//') + extension=$(get_extension "$path") new_patch="${base_name}.patch_${new_version}${extension}" mv "$MODS_DIR/$patch" "$MODS_DIR/$new_patch" @@ -441,7 +551,25 @@ function mod_uninstall() { # remove entry from database sed -i "/^$mod_index/d" "$DB_FILE" - echo -e "Mod ${GREEN}uninstalled successfully${NC}." >&2 + echo -e "Mod $mod_name ${ORANGE}uninstalled${NC} successfully." >&2 +} + +function mod_list() { + if [[ "$1" == "--help" || "$1" == "-h" ]]; then + display_list_help + exit 0 + fi + + if [[ ! -s "$DB_FILE" ]]; then + echo "No mods installed." + return + fi + + echo "Installed mods:" >&2 + awk -v GREEN="$GREEN" -v RED="$RED" -v NC="$NC" -F, '{ + color = ($2 == "DISABLED") ? RED : GREEN; + if (length($3) > 150) $3 = substr($3, 1, 147) "..."; + printf "%2s. [%s%s%s] %s (%s)\n", $1, color, $2, NC, $3, $4}' "$DB_FILE" } function mod_export() { @@ -542,6 +670,12 @@ function main() { uninstall|u) mod_uninstall "$@" ;; + enable|e) + mod_enable "$@" + ;; + disable|d) + mod_disable "$@" + ;; export|ex) mod_export "$@" ;;