From 26031c0381fbb624ab6d1df722156926363b9264 Mon Sep 17 00:00:00 2001 From: v4n <105587619+v4n00@users.noreply.github.com> Date: Wed, 19 Mar 2025 20:20:12 +0200 Subject: [PATCH] version 0.4.0 release --- README.md | 38 ++--- h2mm | 434 ++++++++++++++++++++++++++++------------------------- install.sh | 26 +++- version | 2 +- 4 files changed, 268 insertions(+), 232 deletions(-) diff --git a/README.md b/README.md index 4343925..029d2a9 100644 --- a/README.md +++ b/README.md @@ -34,17 +34,18 @@ h2mm ### Available commands - `install` - Install a mod by the file provided (directory, zip, patch). -- `uninstall` - Uninstall a mod by name (or index). +- `uninstall` - Uninstall a mod. - `list` - List all installed mods. -- `enable` - Enable a mod by name (or index). -- `disable` - Disable a mod by name (or index). +- `enable` - Enable a mod. +- `disable` - Disable a mod. - `export` - Export installed mods to a zip file. - `import` - Import mods from a zip file. +- `order` - Change load order for a mod. - `modpack-create` - Create a modpack from the currently installed mods. -- `modpack-switch` - Switch to a modpack by name (or index). +- `modpack-switch` - Switch to a modpack. - `modpack-list` - List all installed modpacks. -- `modpack-delete` - Delete a modpack by name (or index). -- `modpack-overwrite` - Overwrite a modpack by name (or index). +- `modpack-delete` - Delete a modpack. +- `modpack-overwrite` - Overwrite a modpack. - `modpack-reset` - Reset all installed modpacks. - `update` - Update h2mm to latest version. - `reset` - Reset all installed mods. @@ -52,6 +53,8 @@ h2mm ### Basic usage +To find out how to use a command, you can run `h2mm --help`. + #### Install mod(s) ```bash @@ -65,17 +68,17 @@ h2mm install -n "Example mod" mod* # using a wildcard to include all files #### Uninstall a mod ```bash -h2mm uninstall "Example mod" -h2mm uninstall -i 1 # uninstall mod with index 1 +h2mm uninstall -n "Example mod" +h2mm uninstall -i 3 ``` #### 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 +h2mm enable -n "Example mod" +h2mm enable -i 3 +h2mm disable -n "Example mod" +h2mm disable -i 3 ``` #### List installed mods @@ -115,6 +118,7 @@ You can use the short form of commands to save some time. The shortcuts are: - `l` for `list` - `ex` for `export` - `im` for `import` +- `o` for `order` - `mc` for `modpack-create` - `ms` for `modpack-switch` - `ml` for `modpack-list` @@ -129,10 +133,10 @@ You can use the short form of commands to save some time. The shortcuts are: You can set up modpacks by using the `modpack-*` commands. This allows you to quickly change between a set of mods. For more information, check the help message. ```bash -h2mm modpack-create "Modpack 1" +h2mm modpack-create -n "Modpack 1" # install, enable, disable other mods... -h2mm modpack-create "Modpack 2" -h2mm modpack-switch "Modpack 1" +h2mm modpack-create -n "Modpack 2" +h2mm modpack-switch -n "Modpack 1" ``` ### Exporting and importing @@ -140,8 +144,8 @@ h2mm modpack-switch "Modpack 1" You can export all installed mods to a zip file and import mods from the same file. This can be useful for sharing mods with others or for backing up your mods. The archive file (`.tar.gz`) will be saved in the current directory. ```bash -h2mm export modpack1.zip -h2mm import modpack2.zip +h2mm export +h2mm import /path/to/mods.tar.gz ``` ### Resetting all installed mods diff --git a/h2mm b/h2mm index 96ddbca..4721021 100755 --- a/h2mm +++ b/h2mm @@ -1,6 +1,6 @@ #!/usr/bin/env bash -VERSION="0.3.15" +VERSION="0.4.0" # --- Globals --- @@ -18,6 +18,12 @@ H2PATH="${HOME}/.config/h2mm/h2path" LAST_CHECKED_UPDATE_FILE="${HOME}/.config/h2mm/last_update" VERSION_URL="https://raw.githubusercontent.com/v4n00/h2mm-cli/refs/heads/master/version" +breaking_changes_patches=( + ["2"]='sed -i "s/^\([0-9]\+\),/\1,ENABLED,/" "$1/mods.csv"' + ["3"]='sed -i "1 i\\3" "$1/mods.csv"' + ["4"]='awk '\''BEGIN {FS=OFS=","} NR==1 {print 4; next} {print NR-1, $2, $3, $4, $5}'\'' "$1/mods.csv" | tee "$1/mods.csv" > /dev/null' +) + # --- Utility Functions --- function get_version_major() { @@ -52,6 +58,16 @@ function disable_all_modpacks() { sed -i 's/ENABLED/DISABLED/' "$MODPACKS_DB_FILE" } +function parse_help_no_arguments() { + display_help="$1" + [[ "$2" == "--help" || "$2" == "-h" ]] && { $display_help; exit 0; } +} + +function parse_help_has_arguments() { + display_help="$1" + [[ $# -eq 1 || "$2" == "--help" || "$2" == "-h" ]] && { $display_help; exit 0; } +} + function log() { local type="$1" shift @@ -189,89 +205,93 @@ function initialize_modpack_directories() { # --- Help Functions --- -function display_help() { +function display_help_main() { cat << EOF Helldivers 2 Mod Manager v${VERSION} Usage: h2mm [OPTION] COMMAND Commands: i, install Install a mod by the file provided (directory, zip, patch). - u, uninstall Uninstall a mod by name (or index). + u, uninstall Uninstall a mod. l, list List all installed mods. - e, enable Enable a mod by name (or index). - d, disable Disable a mod by name (or index). + e, enable Enable a mod. + d, disable Disable a mod. ex, export Export installed mods to a zip file. im, import Import mods from a zip file. + o, order Change load order of a mod. mc, modpack-create Create a modpack from the currently installed mods. - ms, modpack-switch Switch to a modpack by name (or index). + ms, modpack-switch Switch to a modpack. ml, modpack-list List all installed modpacks. - mc, modpack-delete Delete a modpack by name (or index). - mo, modpack-overwrite Overwrite a modpack by name (or index). + mc, modpack-delete Delete a modpack. + mo, modpack-overwrite Overwrite a modpack. mr, modpack-reset Reset all installed modpacks. up, update Update h2mm to the latest version. r, reset Reset all installed mods. help Display this help message. -For more information on usage, use h2mm [COMMAND] --help. +For more information on usage, use h2mm --help. Usage: h2mm install /path/to/mod.zip - h2mm install /path/to/mod/files - h2mm uninstall \"Example mod\" + h2mm uninstall -n "Example mod" + h2mm list EOF } -function display_install_help() { +function display_help_install() { cat << EOF -Usage: h2mm install [OPTIONS] MOD_FILES|MOD_DIRECTORIES|MOD_ZIPS +Usage: h2mm install [OPTIONS] Install a mod with any combination of mod files, directories, and zip files. Options: - -n "" Name the mod yourself, inside double quotes. - Multiple mod files, accepts wildcards. - Directory/directories containing mod files. - Zip file(s) containing mod files. + -n "MOD_NAME" Name of the mod. + MOD_ZIPS Zip file(s) containing mod files. + MOD_FILES Mod file(s), accepts wildcards. + MOD_DIRECTORIES Directory/directories containing mod files. Example: h2mm install /path/to/mod.zip h2mm install /path/to/mod/files h2mm install /path/to/mod.zip /path/to/mod2.zip /path/to/mod/files - h2mm install -n "Example mod" mod.patch_0 mod.patch_0.stream + h2mm install mod.patch_0 mod.patch_0.stream -n "Example mod" EOF } -function display_uninstall_help() { +function display_help_uninstall() { cat << EOF -Usage: h2mm uninstall [OPTIONS] "MOD_NAME" +Usage: h2mm uninstall [OPTIONS] <"MOD_NAME"|MOD_INDEX> Uninstall a mod by name or index. Options: - -i Index of the mod to uninstall. + -n "MOD_NAME" Name of the mod to uninstall. + -i MOD_INDEX Index of the mod to uninstall. Usage: - h2mm uninstall "Example mod" - h2mm uninstall -i 1 # uninstall mod with index 1 + h2mm uninstall -n "Example mod" + h2mm uninstall -i 3 EOF } -function display_enable_help() { +function display_help_enable() { cat << EOF -Usage: h2mm enable [OPTIONS] "MOD_NAME" +Usage: h2mm enable [OPTIONS] <"MOD_NAME"|MOD_INDEX> Enable a mod by name or index. Options: - -i Index of the mod to enable. + -n "MOD_NAME" Name of the mod to enable. + -i MOD_INDEX Index of the mod to enable. Usage: - h2mm enable "Example mod" - h2mm enable -i 1 # enable mod with index 1 + h2mm enable -n "Example mod" + h2mm enable -i 3 EOF } -function display_disable_help() { +function display_help_disable() { cat << EOF -Usage: h2mm disable [OPTIONS] "MOD_NAME" +Usage: h2mm disable [OPTIONS] <"MOD_NAME"|MOD_INDEX> Disable a mod by name or index. Options: - -i Index of the mod to disable. + -n "MOD_NAME" Name of the mod to disable. + -i MOD_INDEX Index of the mod to disable. Usage: - h2mm disable "Example mod" - h2mm disable -i 1 # disable mod with index 1 + h2mm disable -n "Example mod" + h2mm disable -i 3 EOF } -function display_list_help() { +function display_help_list() { cat << EOF Usage: h2mm list Database of mods is stored in Steam/steamapps/common/Helldivers\ 2/data/mods.csv @@ -281,43 +301,43 @@ Options: EOF } -function display_reset_help() { +function display_help_reset() { cat << EOF Usage: h2mm reset Reset all installed mods. Deletes all installed mods/modpacks and the database file. -Database of mods is stored in Steam/steamapps/common/Helldivers\ 2/data/mods.csv, along with the mods. +Database of mods is stored in Steam/steamapps/common/Helldivers 2/data/mods.csv, along with the mods. EOF } -function display_export_help() { +function display_help_export() { cat << EOF Usage: h2mm export -Export installed mods and database to a zip file (in h2mm format, archive with csv). +Export installed mods, modpacks and database to a zip file (in h2mm format - archive with csv) in the current working directory. EOF } -function display_import_help() { +function display_help_import() { cat << EOF -Usage: h2mm import -Import mods and database from an archive file (coming from h2mm). +Usage: h2mm import +Import mods, modpacks and database from an archive file (coming from h2mm). EOF } -function display_order_help() { +function display_help_order() { cat << EOF -Usage: h2mm order [OPTIONS] [MOD_NAME|MOD_INDEX] +Usage: h2mm order [OPTIONS] <"MOD_NAME"|MOD_INDEX> Change order of a mod by name or index. Options: - -i Index of the mod to order. - -n New index of the mod. + -i index Index of the mod to order. + -n "MOD_NAME" Name of the mod to order. Usage: - h2mm order -n "Example mod" 1 - h2mm order -i 1 2 + h2mm order -n "Example mod" 6 + h2mm order -i 3 6 EOF } -function display_modpack_list_help() { +function display_help_modpack_list() { cat << EOF Usage: h2mm modpack-list List all installed modpacks. @@ -326,24 +346,24 @@ You can rename, delete, or edit this file to manage modpacks manually. EOF } -function display_modpack_create_help() { +function display_help_modpack_create() { cat << EOF -Usage: h2mm modpack-create "MODPACK_NAME" +Usage: h2mm modpack-create -n "MODPACK_NAME" Create a modpack from a range of mods specified after command is called. EOF } -function display_modpack_switch_help() { +function display_help_modpack_switch() { cat << EOF -Usage: h2mm modpack-switch [OPTIONS] "MODPACK_NAME" +Usage: h2mm modpack-switch [OPTIONS] <"MODPACK_NAME"|MODPACK_INDEX> Switch to a modpack by name or index. Options: - -i Index of the modpack to switch to. -Switch to a modpack by name or index. + -n "MODPACK_NAME" Name of the modpack to switch to. + -i index Index of the modpack to switch to. EOF } -function display_modpack_reset_help() { +function display_help_modpack_reset() { cat << EOF Usage: h2mm modpack-reset Reset all installed modpacks. @@ -352,21 +372,23 @@ Database of modpacks is stored in Steam/steamapps/common/Helldivers\ 2/data/modp EOF } -function display_modpack_delete_help() { +function display_help_modpack_delete() { cat << EOF -Usage: h2mm modpack-delete [OPTIONS] "MODPACK_NAME" -Options: - -i Index of the modpack to delete. +Usage: h2mm modpack-delete [OPTIONS] <"MODPACK_NAME"|MODPACK_INDEX> Delete a modpack by name or index. +Options: + -n "MODPACK_NAME" Name of the modpack to delete. + -i index Index of the modpack to delete. EOF } -function display_modpack_overwrite_help() { +function display_help_modpack_overwrite() { cat << EOF -Usage: h2mm modpack-overwrite [OPTIONS] "MODPACK_NAME" -Options: - -i Index of the modpack to overwrite. +Usage: h2mm modpack-overwrite [OPTIONS] <"MODPACK_NAME"|MODPACK_INDEX> Overwrite a modpack (the mods that it uses) by name or index. +Options: + -n "MODPACK_NAME" Name of the modpack to overwrite. + -i index Index of the modpack to overwrite. EOF } @@ -498,22 +520,23 @@ function upgrade_mods() { # Mod management function mod_disable() { + parse_help_has_arguments display_help_disable "$@" local mod_name="" local mod_index="" - [[ $# -eq 0 ]] && { display_disable_help; exit 0; } - # parse arguments while [[ $# -gt 0 ]]; do case "$1" in - -i) + "-i") + [[ -z "$2" || ! "$2" =~ ^[0-9]+$ ]] && { log ERROR "Invalid mod index."; exit 1; } mod_index="$2"; shift 2 ;; - --help|-h) - display_disable_help; exit 0 + "-n") + [[ -z "$2" ]] && { log ERROR "Mod name is required."; exit 1; } + mod_name="$2"; shift 2 ;; *) - mod_name="$1"; shift 1 + $display_help; exit 0 ;; esac done @@ -557,26 +580,29 @@ function mod_disable() { sed -i "/^$mod_index,/s/ENABLED/DISABLED/" "$DB_FILE" [[ $? -ne 0 ]] && { log ERROR "Could not disable mod."; exit 1; } - log INFO "Mod $mod_name ${GREEN}successfully${NC} disabled." + log INFO "Mod ${GREEN}successfully${NC} disabled: $mod_name." + + disable_all_modpacks } function mod_enable() { + parse_help_has_arguments display_help_enable "$@" local mod_name="" local mod_index="" - [[ $# -eq 0 ]] && { display_enable_help; exit 0; } - # parse arguments while [[ $# -gt 0 ]]; do case "$1" in - -i) + "-i") + [[ -z "$2" || ! "$2" =~ ^[0-9]+$ ]] && { log ERROR "Invalid mod index."; exit 1; } mod_index="$2"; shift 2 ;; - --help|-h) - display_enable_help; exit 0 + "-n") + [[ -z "$2" ]] && { log ERROR "Mod name is required."; exit 1; } + mod_name="$2"; shift 2 ;; *) - mod_name="$1"; shift 1 + $display_help; exit 0 ;; esac done @@ -614,24 +640,22 @@ function mod_enable() { sed -i "/^$mod_index,/s/DISABLED/ENABLED/" "$DB_FILE" [[ $? -ne 0 ]] && { log ERROR "Could not enable mod."; exit 1; } - log INFO "Mod $mod_name ${GREEN}successfully${NC} enabled." + log INFO "Mod ${GREEN}successfully${NC} enabled: $mod_name." + + disable_all_modpacks } function mod_reset() { - if [[ "$1" == "--help" || "$1" == "-h" ]]; then - display_reset_help - exit 0 - fi - - local modpack=false - [[ "$1" == "--modpack" ]] && modpack=true + parse_help_no_arguments display_help_reset "$@" + local no_path_reset=false; [[ "$1" == "--no-path-reset" ]] && no_path_reset=true log PROMPT "Are you sure you want to ${RED}reset${NC} all installed mods? (Y/n): " read confirm + if [[ "$confirm" == "y" || "$confirm" == "Y" || "$confirm" = "" ]]; then rm -f "$MODS_DIR"/*.patch_* rm -f "$DB_FILE" - [[ $modpack == false ]] && rm -f "$H2PATH" + [[ $no_path_reset == false ]] && rm -f "$H2PATH" log INFO "Mods ${GREEN}successfully${NC} reset." else log INFO "Reset cancelled." @@ -640,22 +664,19 @@ function mod_reset() { } function mod_install() { + parse_help_has_arguments display_help_install "$@" local mod_name="" local mod_dir=() local mod_files=() local mod_zip=() - [[ $# -eq 0 ]] && { display_install_help; exit 0; } - # parse arguments while [[ $# -gt 0 ]]; do case "$1" in - -n) + "-n") + [[ -z "$2" ]] && { log ERROR "Mod name is required."; exit 1; } mod_name="$2"; shift 2 ;; - --help|-h) - display_install_help; exit 0 - ;; *) if [[ -f "$1" && "$1" == *.zip ]]; then mod_zip+=("$1") @@ -688,7 +709,7 @@ function mod_install() { mod_zip=("${mod_zip[@]:1}") done - # extract the zip file and pass it to mod dirs + # if zip, extract the zip file and pass it to mod dirs if [[ -n "$mod_zip" ]]; then command -v unzip &> /dev/null || { log ERROR "unzip package is not installed."; exit 1; } @@ -777,8 +798,6 @@ function mod_install() { [[ ! -f "$file" ]] && { log ERROR "Mod file $file does not exist."; exit 1; } done - # hash table - in case multiple named files are needed for 1 mod install, store the patch count - declare -A patch_count # store the target files so we can put them in the database later target_files=() # sort the mod files because with the below logic, the .stream and .gpu_resources files need to come after their respective patch files @@ -786,23 +805,18 @@ function mod_install() { for file in "${mod_files[@]}"; do base_name=$(get_basename "$file") - patch_prefix="$MODS_DIR/${base_name}.patch_" - # count already installed patches - count=$(ls "${patch_prefix}"* 2>/dev/null | grep -E '([0-9]+$)' 2>/dev/null | wc -l) - - # set patch count for file name if it doesn't exist yet - if [[ -z "${patch_count[$file]+unset}" ]]; then - patch_count["$file"]=$count - fi - # if the file has an extension (e.g. .stream, .gpu_resources), set the last patch number for the next step - patch_count["$base_name"]=$count - - # if the file has an extension, look for the last patch number and use that, otherwise, the count will be wrong extension=$(get_extension "$file") + + + # count already installed patches + count=$(ls "$MODS_DIR/${base_name}.patch_"* 2>/dev/null | grep -E '([0-9]+$)' 2>/dev/null | wc -l) + + # if the file has an extension, look for the last patch number and subtract 1, otherwise, the count will be wrong + target_file="${base_name}.patch_" if [[ -n "$extension" ]]; then - target_file="${base_name}.patch_$((patch_count[$base_name] - 1))${extension}" + target_file="${target_file}$(($count - 1))${extension}" else - target_file="${base_name}.patch_${patch_count[$file]}" + target_file="${target_file}${count}" fi target_files+=($target_file) @@ -823,22 +837,23 @@ function mod_install() { } function mod_uninstall() { + parse_help_has_arguments display_help_uninstall "$@" local mod_name="" local mod_index="" - [[ $# -eq 0 ]] && { display_uninstall_help; exit 0; } - # parse arguments while [[ $# -gt 0 ]]; do case "$1" in - -i) + "-i") + [[ -z "$2" || ! "$2" =~ ^[0-9]+$ ]] && { log ERROR "Invalid mod index."; exit 1; } mod_index="$2"; shift 2 ;; - --help|-h) - display_uninstall_help; exit 0 + "-n") + [[ -z "$2" ]] && { log ERROR "Mod name is required."; exit 1; } + mod_name="$2"; shift 2 ;; *) - mod_name="$1"; shift 1 + $display_help; exit 0 ;; esac done @@ -879,26 +894,14 @@ function mod_uninstall() { } function mod_list() { + parse_help_no_arguments display_help_list "$@" local verbose=false # parse arguments - while [[ $# -gt 0 ]]; do - case "$1" in - --help|-h) - display_list_help - exit 0 - ;; - --verbose|-v) - verbose=true - shift - ;; - *) - shift - ;; - esac - done + [[ "$1" == "--verbose" || "$1" == "-v" ]] && verbose=true - [[ $(wc -l < "$DB_FILE") -le 1 ]] && { log INFO "No mods installed."; return; } + # quit if no mods are installed + [[ $(wc -l < "$DB_FILE") -le 1 ]] && { log INFO "No mods installed."; exit 0; } log INFO "Installed mods:" @@ -915,8 +918,7 @@ function mod_list() { } function mod_export() { - [[ "$1" == "--help" || "$1" == "-h" ]] && { display_export_help; exit 0; } - + parse_help_no_arguments display_help_export "$@" local save_dir=$(pwd) local archive_name="Helldivers_2_Mods_$(date +%Y-%m-%d_%H-%M-%S)" local modpack_export=false @@ -928,7 +930,7 @@ function mod_export() { archive_name="$3" fi - [[ $(wc -l < "$DB_FILE") -le 1 ]] && { log INFO "No mods installed."; exit 1; } + [[ $(wc -l < "$DB_FILE") -le 1 ]] && { log ERROR "No mods installed."; exit 1; } if [[ $modpack_export == false ]]; then log PROMPT "Archive file will be saved to ${save_dir}/${archive_name}. Make? (Y/n): " @@ -966,32 +968,57 @@ function mod_export() { } function mod_import() { - [[ "$1" == "--help" || "$1" == "-h" ]] && { display_import_help; exit 0; } - - local modpack=false - [[ "$1" == "--modpack" ]] && { modpack=true; shift 1; } + parse_help_has_arguments display_help_import "$@" + local modpack=false; [[ "$1" == "--modpack" ]] && { modpack=true; shift 1; } + # check if the file exists [[ ! -f "$1" ]] && { log ERROR "File $1 does not exist."; exit 1; } # reset mods before importing - if [[ $modpack == false ]]; then - log INFO "Importing mods will ${RED}reset${NC} your mods." - mod_reset - else - mod_reset --modpack - fi + [[ $modpack == false ]] && log INFO "Importing mods will ${RED}reset${NC} your mods." + mod_reset --no-path-reset # extract in temp directory OUT_DIR=$(mktemp -d) tar -xzf "$1" -C "$OUT_DIR" - [[ $? -ne 0 ]] && { log ERROR "Could not import mods. Possibly because the archive is invalid."; exit 1; } + # get the mods directory MODS_EXPORT_DIR="$OUT_DIR/Helldivers 2 Mods" [[ ! -d "$MODS_EXPORT_DIR" ]] && { log ERROR "Could not import mods. Possibly because the archive is invalid."; exit 1; } - # copy mods - cp "$MODS_EXPORT_DIR"/* "$MODS_DIR" + # fix breaking changes if the version number (from the first line) is different + db_version=$(head -n 1 "$MODS_EXPORT_DIR/mods.csv") + current_version=$(echo "$VERSION" | awk -F. '{print $2}') + + [[ -z "$db_version" || ! "$db_version" =~ ^[0-9]+$ ]] && { log ERROR "Invalid version number inside mods.csv from imported archive."; exit 1; } + + if [[ "$db_version" != "$current_version" ]]; then + log INFO "Import detected version 0.$db_version.x, current version is 0.$current_version.x." + + # iterate from installed major number to latest major number + for ((i = db_version + 1; i <= current_version; i++)); do + if [[ -n "${breaking_changes_patches[$i]}" ]]; then + # apply breaking changes patch + eval $(echo "${breaking_changes_patches[$i]}" | sed "s:\$1:$MODS_EXPORT_DIR:g") + else + log INFO "No breaking changes for version $i." + continue + fi + + if [[ $? -ne 0 ]]; then + log ERROR "Failed to apply breaking changes patch for version $i. Do you want to continue? (Y/n): " + read -er response + + [[ "$response" != "y" && "$response" != "Y" && -n "$response" ]] && { log INFO "Exiting." ; exit 1; } + else + log INFO "Version upgrade fix ${GREEN}successfully${NC} applied for version $i." + fi + done + fi + + # copy everything in + cp -r "$MODS_EXPORT_DIR"/* "$MODS_DIR" [[ $? -ne 0 ]] && { log ERROR "Failed to import mods."; exit 1; } @@ -999,8 +1026,7 @@ function mod_import() { } function mod_order { - [[ "$1" == "--help" || "$1" == "-h" ]] && { display_order_help; exit 0; } - + parse_help_has_arguments display_help_order "$@" local mod_name="" local mod_index="" local new_index="" @@ -1008,15 +1034,14 @@ function mod_order { # parse arguments while [[ $# -gt 0 ]]; do case "$1" in - -i) + "-i") + [[ -z "$2" || ! "$2" =~ ^[0-9]+$ ]] && { log ERROR "Invalid mod index."; exit 1; } mod_index="$2"; shift 2 ;; - -n) + "-n") + [[ -z "$2" ]] && { log ERROR "Mod name is required."; exit 1; } mod_name="$2"; shift 2 ;; - --help|-h) - display_order_help; exit 0 - ;; *) new_index="$1"; shift 1 ;; @@ -1054,23 +1079,23 @@ function mod_order { declare -A replace_count for file in $current_mod_files; do extension=$(get_extension "$file") - basename=$(get_basename "$file") + base_name=$(get_basename "$file") # if the file has no extension, add the basename to the hash table or increment the count if [[ -z "$extension" ]]; then - replace_count["$basename"]=$(( ${replace_count["$basename"]:-0} + 1 )) + replace_count["$base_name"]=$(( ${replace_count["$base_name"]:-0} + 1 )) fi done # step 2.1 - iterate over the basenames, find and store the files with the same basenames as the ones we want to upgrade/downgrade declare -A files_to_replace - for basename in "${!replace_count[@]}"; do - IFS= files_to_replace["$basename"]=$(echo "$entries" | awk -F, '{print $4}' | grep -o "$basename\.patch_[^ ]*"); unset IFS + for base_name in "${!replace_count[@]}"; do + IFS= files_to_replace["$base_name"]=$(echo "$entries" | awk -F, '{print $4}' | grep -o "$base_name\.patch_[^ ]*"); unset IFS done # step 2.2 - reverse sort the files_to_replace if we are in descending order - [[ $ascending_order == false ]] && for basename in "${!files_to_replace[@]}"; do - files_to_replace["$basename"]=$(echo "${files_to_replace["$basename"]}" | sort -rV) + [[ $ascending_order == false ]] && for base_name in "${!files_to_replace[@]}"; do + files_to_replace["$base_name"]=$(echo "${files_to_replace["$base_name"]}" | sort -rV) done # move current mod files to "t_$file" so we can move the new files to the correct index @@ -1080,17 +1105,17 @@ function mod_order { done # step 3 - for every basename in files_to_replace, add replace_count to each file's patch number and move - for basename in "${!files_to_replace[@]}"; do - files=$(echo "${files_to_replace["$basename"]}" | tr ' ' '\n') + for base_name in "${!files_to_replace[@]}"; do + files=$(echo "${files_to_replace["$base_name"]}" | tr ' ' '\n') for file in $files; do - basename=$(get_basename "$file") + base_name=$(get_basename "$file") patch_number=$(get_patch_number "$file") extension=$(get_extension "$file") # if ascending order, subtract replace_count to the patch number, otherwise add operation="-"; [[ $ascending_order == false ]] && operation="+" - new_file="${basename}.patch_$(($patch_number $operation ${replace_count["$basename"]}))${extension}" + new_file="${base_name}.patch_$(($patch_number $operation ${replace_count["$base_name"]}))${extension}" mv "$MODS_DIR/$file" "$MODS_DIR/$new_file" [[ $? -ne 0 ]] && { log ERROR "Could not move mod file $file."; exit 1; } @@ -1104,12 +1129,12 @@ function mod_order { # step 4 - for every file in current_mod_files, subtract the basename's array size of files_to_replace from the patch number for file in $current_mod_files; do - basename=$(get_basename "$file") + base_name=$(get_basename "$file") patch_number=$(get_patch_number "$file") extension=$(get_extension "$file") # get size of only files without an extension - size=$(echo "${files_to_replace["$basename"]}" | grep -E "${basename}.patch_[0-9]+$" | tr ' ' '\n' | wc -l) + size=$(echo "${files_to_replace["$base_name"]}" | grep -E "${base_name}.patch_[0-9]+$" | tr ' ' '\n' | wc -l) # in case size is 0, move t_file back to file later new_file=${file} @@ -1117,7 +1142,7 @@ function mod_order { if [[ $size -gt 0 ]]; then # if ascending order, add size from the patch number, otherwise subtract operation="+"; [[ $ascending_order == false ]] && operation="-" - new_file="${basename}.patch_$(($patch_number $operation $size))${extension}" + new_file="${base_name}.patch_$(($patch_number $operation $size))${extension}" log INFO "Reindexing ${ORANGE}$file${NC} to ${GREEN}$new_file${NC}." @@ -1162,15 +1187,16 @@ function mod_order { sed -i "$((new_index + 1))i $entry" "$DB_FILE" fi - log INFO "Mod ${GREEN}successfully${NC} reindexed: $mod_name from $mod_index to $new_index." + log INFO "Mod ${GREEN}successfully${NC} reindexed: \"$mod_name\" went from $mod_index to $new_index." } -# --- Modpacks management --- +# --- Modpack management --- function modpack_list() { - [[ "$1" == "--help" || "$1" == "-h" ]] && { display_modpack_list_help; exit 0; } + parse_help_no_arguments display_help_modpack_list "$@" - [[ $(wc -l < "$MODPACKS_DB_FILE") -le 1 ]] && { log INFO "No modpacks saved."; return; } + # quit if no modpacks are saved + [[ $(wc -l < "$MODPACKS_DB_FILE") -le 1 ]] && { log INFO "No modpacks saved."; exit 0; } log INFO "Saved modpacks:" @@ -1180,10 +1206,9 @@ function modpack_list() { } function modpack_create() { + parse_help_no_arguments display_help_modpack_create "$@" local modpack_name="" - [[ $# -eq 0 ]] && { display_modpack_create_help; exit 0; } - # if no mods are installed, exit [[ $(wc -l < "$DB_FILE") -le 1 ]] && { log ERROR "No mods installed."; exit 1; } @@ -1234,7 +1259,7 @@ function modpack_create() { mod_export --modpack "$MODPACKS_FOLDER" "$modpack_name" log INFO "Modpack ${GREEN}successfully${NC} created: \$MODPACKS_FOLDER/$modpack_name.tar.gz" - log INFO "Switch to the modpack with 'h2mm modpack-switch $modpack_name'." + log INFO "Switch to the modpack with 'h2mm modpack-switch -n \"$modpack_name\"'." # warn user to create a modpack with his main mods [[ $(wc -l < "$MODPACKS_DB_FILE") -le 1 ]] && log INFO "If this is your first modpack, it is ${ORANGE}recommended${NC} to create a separate modpack with your main mods." @@ -1245,22 +1270,23 @@ function modpack_create() { } function modpack_switch() { + parse_help_has_arguments display_help_modpack_switch "$@" local modpack_name="" local modpack_index="" - [[ $# -eq 0 ]] && { display_modpack_switch_help; exit 0; } - # parse arguments while [[ $# -gt 0 ]]; do case "$1" in - -i) + "-i") + [[ -z "$2" || ! "$2" =~ ^[0-9]+$ ]] && { log ERROR "Invalid modpack index."; exit 1; } modpack_index="$2"; shift 2 ;; - --help|-h) - display_modpack_switch_help; exit 0 + "-n") + [[ -z "$2" ]] && { log ERROR "Modpack name is required."; exit 1; } + modpack_name="$2"; shift 2 ;; *) - modpack_name="$1"; shift 1 + $display_help; exit 0 ;; esac done @@ -1271,7 +1297,9 @@ function modpack_switch() { get_modpack_name_and_index "$modpack_name" "$modpack_index" log INFO "Switching modpacks mods will ${RED}reset${NC} your mods." + silent=true mod_import --modpack "$MODPACKS_FOLDER/$modpack_name.tar.gz" + silent=false log INFO "Modpack ${GREEN}successfully${NC} switched: $modpack_name." @@ -1281,10 +1309,7 @@ function modpack_switch() { } function modpack_reset() { - if [[ "$1" == "--help" || "$1" == "-h" ]]; then - display_modpack_reset_help - exit 0 - fi + parse_help_no_arguments display_help_modpack_reset "$@" log PROMPT "Are you sure you want to ${RED}reset${NC} all installed modpacks? (Y/n): " read confirm @@ -1300,22 +1325,23 @@ function modpack_reset() { } function modpack_delete() { + parse_help_has_arguments display_help_modpack_delete "$@" local modpack_name="" local modpack_index="" - [[ $# -eq 0 ]] && { display_modpack_delete_help; exit 0; } - # parse arguments while [[ $# -gt 0 ]]; do case "$1" in - -i) + "-i") + [[ -z "$2" || ! "$2" =~ ^[0-9]+$ ]] && { log ERROR "Invalid modpack index."; exit 1; } modpack_index="$2"; shift 2 ;; - --help|-h) - display_modpack_delete_help; exit 0 + "-n") + [[ -z "$2" ]] && { log ERROR "Modpack name is required."; exit 1; } + modpack_name="$2"; shift 2 ;; *) - modpack_name="$1"; shift 1 + $display_help; exit 0 ;; esac done @@ -1334,42 +1360,41 @@ function modpack_delete() { } function modpack_overwrite() { - [[ $# -eq 0 ]] && { display_modpack_overwrite_help; exit 0; } - + parse_help_has_arguments display_help_modpack_overwrite "$@" local modpack_name="" local modpack_index="" # parse arguments while [[ $# -gt 0 ]]; do case "$1" in - -i) + "-i") + [[ -z "$2" || ! "$2" =~ ^[0-9]+$ ]] && { log ERROR "Invalid modpack index."; exit 1; } modpack_index="$2"; shift 2 ;; - --help|-h) - display_modpack_save_help; exit 0 + "-n") + [[ -z "$2" ]] && { log ERROR "Modpack name is required."; exit 1; } + modpack_name="$2"; shift 2 ;; *) - modpack_name="$1"; shift 1 + $display_help; exit 0 ;; esac done - [[ -z "$modpack_name" && -z "$modpack_index" ]] && { log ERROR "Modpack name or index is required to save."; exit 1; } + [[ -z "$modpack_name" && -z "$modpack_index" ]] && { log ERROR "Modpack name or index is required to overwrite."; exit 1; } get_modpack_name_and_index "$modpack_name" "$modpack_index" # if the modpack doesn't exist, exit [[ ! -f "$MODPACKS_FOLDER/$modpack_name.tar.gz" ]] && { log ERROR "Modpack $modpack_name does not exist."; exit 1; } - rm -f "$MODPACKS_FOLDER/$modpack_name.tar.gz" - [[ $? -ne 0 ]] && { log ERROR "Could not delete modpack."; exit 1; } # use built-in export function mod_export --modpack "$MODPACKS_FOLDER" "$modpack_name" - log INFO "Modpack ${GREEN}successfully${NC} saved: \$MODPACKS_FOLDER/$modpack_name.tar.gz" + log INFO "Modpack ${GREEN}successfully${NC} overwritten: \$MODPACKS_FOLDER/$modpack_name.tar.gz" sed -i "/^$modpack_index,/s/DISABLED/ENABLED/" "$MODPACKS_DB_FILE" } @@ -1391,10 +1416,10 @@ function self_update() { # --- Main --- function main() { - [[ $# -lt 1 ]] && { display_help; exit 1; } + parse_help_has_arguments display_help_main "$@" + + command="$1"; shift - command="$1" - shift initialize_directories initialize_modpack_directories check_for_updates @@ -1451,11 +1476,8 @@ function main() { "update"|"up") self_update ;; - "help"|"--help"|"-h"|"h") - display_help - ;; *) - display_help + $display_help ;; esac } diff --git a/install.sh b/install.sh index 4add219..03b34a1 100755 --- a/install.sh +++ b/install.sh @@ -44,6 +44,7 @@ EOF breaking_changes_patches=( ["2"]='sed -i "s/^\([0-9]\+\),/\1,ENABLED,/" "$1/mods.csv"' ["3"]='sed -i "1 i\\3" "$1/mods.csv"' + ["4"]='awk '\''BEGIN {FS=OFS=","} NR==1 {print 4; next} {print NR-1, $2, $3, $4, $5}'\'' "$1/mods.csv" | tee "$1/mods.csv" > /dev/null' ) # notify if update is happening @@ -105,10 +106,8 @@ else fi # handle breaking changes -installed_major="" -latest_major="" -IFS='.' read -r _1 installed_major _2 <<< "$installed_version" -IFS='.' read -r _1 latest_major _2 <<< "$latest_version" +installed_major=$(echo "$installed_version" | awk -F. '{print $2}') +latest_major=$(echo "$latest_version" | awk -F. '{print $2}') if [[ $latest_major -gt $installed_major ]]; then log INFO "" @@ -121,7 +120,15 @@ if [[ $latest_major -gt $installed_major ]]; then target_dir="Steam/steamapps/common/Helldivers\ 2/data" log INFO "Searching for the Helldivers 2 data directory... (20 seconds timeout)" - game_dir=$(timeout 20 find "$search_dir" -type d -path "*/$target_dir" 2>/dev/null | head -n 1) + # check if game directory is in ~/.config/h2mm/h2path + if [[ -f "$HOME/.config/h2mm/h2path" ]]; then + game_dir=$(cat "$HOME/.config/h2mm/h2path") + [[ ! -d "$game_dir" ]] && { log ERROR "Helldivers 2 data directory in ~/.config/h2mm/h2path is not a valid directory." ; exit 1; } + else + game_dir=$(timeout 20 find "$search_dir" -type d -path "*/$target_dir" 2>/dev/null | head -n 1) + fi + + # if not found, prompt user if [[ -z "$game_dir" ]]; then log INFO "Could not find the Helldivers 2 data directory automatically." log PROMPT "Please enter the path to the Helldivers 2 data directory: " @@ -137,17 +144,20 @@ if [[ $latest_major -gt $installed_major ]]; then # iterate from installed major number to latest major number for ((i = installed_major + 1; i <= latest_major; i++)); do if [[ -n "${breaking_changes_patches[$i]}" ]]; then - eval $(echo "${breaking_changes_patches[$i]}" | sed "s:\$1:$game_dir:") + # apply breaking changes patch + eval $(echo "${breaking_changes_patches[$i]}" | sed "s:\$1:$game_dir:g") else log INFO "No breaking changes for version $i." + continue fi + if [[ $? -ne 0 ]]; then log ERROR "Failed to apply breaking changes patch for version $i. Do you want to continue? (Y/n): " read -er response - [[ "$response" != "y" && "$response" != "Y" && -n "$response" ]] && { log INFO "Exiting. Uninstall the script, then retry the install script." ; exit 1; } + [[ "$response" != "y" && "$response" != "Y" && -n "$response" ]] && { log INFO "Exiting." ; exit 1; } else - log INFO "Successfully applied breaking changes patch for version $i." + log INFO "Version upgrade fix ${GREEN}successfully${NC} applied for version $i." fi done log INFO "" diff --git a/version b/version index 9e29e10..1d0ba9e 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.3.15 +0.4.0