10 Commits

Author SHA1 Message Date
v4n df3c780270 fix: version number url 2025-06-20 15:09:27 +03:00
v4n 1c1235c85e fix: remove nexus upgrade delay (#63) 2025-06-20 15:07:11 +03:00
v4n 7cedfcb451 feat: added rename command (#62) 2025-06-20 11:10:41 +03:00
v4n 0fdc5a2306 fix: nexus integration does not quit prematurely 2025-05-17 14:23:44 +03:00
v4n 13dc822fd9 fix: nexus integration on Steam Deck (#55) 2025-05-16 16:59:14 +03:00
F. St. 35302aa7c7 fix: print parent folder of mod variants (#52)
* Print parent folder of mod variants

Because some mods have multiple variants with the same name.
As is, it is impossible to tell them apart in the selection.
This commit changes the selection to also print the parent folder of the
variants.

* remove the name of the mod zip/dir itself because it not necessary

---------

Co-authored-by: v4n <105587619+v4n00@users.noreply.github.com>
2025-04-19 14:57:20 +03:00
v4n ebadc049e7 fix: confirm after auto-finding HD2 path (#51) 2025-04-17 17:41:56 +03:00
v4n 649404c2d4 fix: sanitize file name of commas (fixes #48) (#49) 2025-04-16 14:54:59 +03:00
v4n 737fa01c11 feat: 7z file format support (#47) 2025-04-15 18:06:54 +03:00
v4n 0ac711085b fix: date invalid issue (fixes #46) 2025-04-15 17:56:06 +03:00
4 changed files with 243 additions and 230 deletions
+8 -19
View File
@@ -2,7 +2,7 @@
Helldivers 2 Mod Manager CLI is a command line interface for managing Helldivers 2 mods. Since there is no Linux mod manager available and I like being a nerd by using CLI tools instead of GUIs, this project was born. Helldivers 2 Mod Manager CLI is a command line interface for managing Helldivers 2 mods. Since there is no Linux mod manager available and I like being a nerd by using CLI tools instead of GUIs, this project was born.
This script is complete, the version will always [remain at 0.x.x](https://0ver.org/) This mod manager is complete, the version will always [remain at 0.x.x](https://0ver.org/). Version 0.6.x will be the last version of this mod manager.
## Installation ## Installation
@@ -11,8 +11,6 @@ Pre-requisites:
- You must have the `unzip` package installed for zip archives; - You must have the `unzip` package installed for zip archives;
- You might want to have the `unarchiver` package installed for rar archives. - You might want to have the `unarchiver` package installed for rar archives.
> The `unzip` package comes pre-installed on most Linux distributions. If you do not know how to install packages, search for your Linux distro and package manager.
To install Helldivers 2 Mod Manager CLI run the following command in your terminal: To install Helldivers 2 Mod Manager CLI run the following command in your terminal:
```bash ```bash
@@ -20,20 +18,11 @@ bash -c "$(curl -fsSL https://raw.githubusercontent.com/v4n00/h2mm-cli/refs/head
``` ```
> [!CAUTION] > [!CAUTION]
> Running this script will require sudo permissions. **DO NOT TRUST** random scripts from the internet. If you want to review the script before running it, check out the mod repository for yourself. > Running this script will require sudo permissions. **DO NOT TRUST** random scripts from the internet. If you want to review the script before running it, check out the repository for yourself.
If for some reason, the installation command doesn't work you can:
1. Go to <https://raw.githubusercontent.com/v4n00/h2mm-cli/refs/heads/master/install.sh>
1. Right click -> Save page as...
1. Open your terminal
1. Go to your downloads folders with `cd ~/Downloads`
1. Give the script execution permissions `chmod +x install.sh`
1. Run the script `./install.sh`
## Usage ## Usage
The script gets added to `/usr/local/bin/h2mm` and can be used by running `h2mm` in your shell, which will show the help message explaining how to use the script. The script gets added to `/usr/local/bin/h2mm` (or `$HOME/.local/bin` on Steam Deck) and can be used by running `h2mm` in your shell, which will show the help message explaining how to use the script.
```bash ```bash
h2mm h2mm
@@ -46,6 +35,7 @@ h2mm
- `list` or `l` - List all installed mods; - `list` or `l` - List all installed mods;
- `enable` or `e` - Enable a mod; - `enable` or `e` - Enable a mod;
- `disable` or `d` - Disable a mod; - `disable` or `d` - Disable a mod;
- `rename` or `r` - Rename a mod;
- `order` or `o` - Change load order for a mod; - `order` or `o` - Change load order for a mod;
- `export` or `ex` - Export installed mods to a zip file; - `export` or `ex` - Export installed mods to a zip file;
- `import` or `im` - Import mods from a zip file; - `import` or `im` - Import mods from a zip file;
@@ -56,21 +46,20 @@ h2mm
- `modpack-overwrite` or `mo` - Overwrite a modpack; - `modpack-overwrite` or `mo` - Overwrite a modpack;
- `modpack-reset` or `mr` - Reset all installed modpacks; - `modpack-reset` or `mr` - Reset all installed modpacks;
- `nexus-setup` or `ns` - Setup Nexus Mods integration; - `nexus-setup` or `ns` - Setup Nexus Mods integration;
- `nexus-update` or `nu` - Start Nexus mods upgrade process;
- `update` or `up` - Update h2mm to latest version; - `update` or `up` - Update h2mm to latest version;
- `reset` or `r` - Reset all installed mods; - `reset` or `rs` - Reset all installed mods;
- `help` or `h` - Display this help message. - `help` or `h` - Display this help message.
### Usage ### Examples
To find out how to use a command, you can run `h2mm <COMMAND> --help`. To find out how to use a command, you can run `h2mm <COMMAND> --help`.
#### Install mod(s) #### Install mod(s)
```bash ```bash
h2mm install /path/to/mod.zip h2mm install mod.zip
h2mm install /path/to/mod/directory/ h2mm install /path/to/mod/directory/
h2mm install /path/to/mod.zip /path/to/mod2.zip /path/to/mod/files h2mm install /path/to/mod.zip /path/to/mod2.zip /path/to/mod/files # mix and match hoewever you want
h2mm install -n "Example mod" mod.patch_0 mod.patch_0.stream # -n is mandatory when using files h2mm install -n "Example mod" mod.patch_0 mod.patch_0.stream # -n is mandatory when using files
``` ```
+195 -178
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
VERSION="0.5.0"
VERSION="0.6.0"
# --- Globals --- # --- Globals ---
@@ -18,7 +19,6 @@ MODPACKS_DB_FILE=""
H2CONFIG_DIR="${HOME}/.config/h2mm" H2CONFIG_DIR="${HOME}/.config/h2mm"
H2PATH_FILE="${H2CONFIG_DIR}/h2path" H2PATH_FILE="${H2CONFIG_DIR}/h2path"
API_KEY_FILE="${H2CONFIG_DIR}/api_key" API_KEY_FILE="${H2CONFIG_DIR}/api_key"
UPDATES_AVAILABLE_FILE="${H2CONFIG_DIR}/updates_available"
LAST_CHECKED_UPDATE_FILE="${H2CONFIG_DIR}/last_update" LAST_CHECKED_UPDATE_FILE="${H2CONFIG_DIR}/last_update"
BACKUPS_DIR="${H2CONFIG_DIR}/backups" BACKUPS_DIR="${H2CONFIG_DIR}/backups"
@@ -29,6 +29,7 @@ breaking_changes_patches=(
["3"]='sed -i "1 i\\3" "$1/mods.csv"' ["3"]='sed -i "1 i\\3" "$1/mods.csv"'
["4"]='tmp_file=$(mktemp) && awk '\''BEGIN {FS=OFS=","} NR==1 {print 4; next} {print NR-1, $2, $3, $4, $5}'\'' "$1/mods.csv" > "$tmp_file" && tee "$1/mods.csv" < "$tmp_file" > /dev/null && rm "$tmp_file"' ["4"]='tmp_file=$(mktemp) && awk '\''BEGIN {FS=OFS=","} NR==1 {print 4; next} {print NR-1, $2, $3, $4, $5}'\'' "$1/mods.csv" > "$tmp_file" && tee "$1/mods.csv" < "$tmp_file" > /dev/null && rm "$tmp_file"'
["5"]='sed -i "s/^\([0-9]\+\),\(.*\),\(.*\),\(.*\)/\1,\2,\3,,,,\4/" "$1/mods.csv"; sed -i "1 s/4/5/" "$1/mods.csv"' ["5"]='sed -i "s/^\([0-9]\+\),\(.*\),\(.*\),\(.*\)/\1,\2,\3,,,,\4/" "$1/mods.csv"; sed -i "1 s/4/5/" "$1/mods.csv"'
["6"]='sed -i "s/^\([0-9]\+\),\(.*\),\(.*\),\(.*\),\(.*\),\(.*\),\(.*\)/\1,\2,\3,\4,\6,\7/" "$1/mods.csv"; sed -i "1 s/5/6/" "$1/mods.csv"'
) )
# --- Utility Functions --- # --- Utility Functions ---
@@ -38,7 +39,7 @@ function substitute_home() {
} }
function get_version_major() { function get_version_major() {
echo "$1" | awk -F. '{print $2}' echo "$VERSION" | awk -F. '{print $2}'
} }
function get_filename_without_path() { function get_filename_without_path() {
@@ -57,28 +58,24 @@ function get_extension() {
get_filename_without_path "$1" | sed -E 's/.*patch_[0-9]+//' get_filename_without_path "$1" | sed -E 's/.*patch_[0-9]+//'
} }
function get_files_by_entry_from_db() { function get_index_by_entry_from_db() {
echo "$1" | cut -d',' -f7- | tr ',' ' ' | head -1 echo "$1" | awk -F, '{print $1}'
} }
function get_name_by_entry_from_db() { function get_name_by_entry_from_db() {
echo "$1" | awk -F, '{print $3}' echo "$1" | awk -F, '{print $3}'
} }
function get_index_by_entry_from_db() {
echo "$1" | awk -F, '{print $1}'
}
function get_nexus_mod_id_by_entry_from_db() { function get_nexus_mod_id_by_entry_from_db() {
echo "$1" | awk -F, '{print $4}' echo "$1" | awk -F, '{print $4}'
} }
function get_nexus_mod_file_id_by_entry_from_db() { function get_nexus_mod_version_by_entry_from_db() {
echo "$1" | awk -F, '{print $5}' echo "$1" | awk -F, '{print $5}'
} }
function get_nexus_mod_version_by_entry_from_db() { function get_files_by_entry_from_db() {
echo "$1" | awk -F, '{print $6}' echo "$1" | cut -d',' -f6- | tr ',' ' ' | head -1
} }
function disable_all_modpacks() { function disable_all_modpacks() {
@@ -125,16 +122,6 @@ function log() {
# --- Functions --- # --- Functions ---
function check_nexus_mod_arguments() {
if [[ -n "$nexus_mod_id" || -n "$nexus_mod_file_id" || -n "$nexus_mod_version" ]]; then
if [[ -z "$nexus_mod_id" || -z "$nexus_mod_file_id" || -z "$nexus_mod_version" ]]; then
log ERROR "Nexus mod ID, file ID and version are required when specifing at least one of them."
exit 1
fi
has_nexus_mod_arguments=true
fi
}
function get_nexus_api_key() { function get_nexus_api_key() {
[[ ! -f "$API_KEY_FILE" ]] && { log ERROR "Nexus API key file not found. Please run h2mm nexus-setup."; exit 1; } [[ ! -f "$API_KEY_FILE" ]] && { log ERROR "Nexus API key file not found. Please run h2mm nexus-setup."; exit 1; }
api_key=$(cat "$API_KEY_FILE") api_key=$(cat "$API_KEY_FILE")
@@ -142,11 +129,11 @@ function get_nexus_api_key() {
} }
function remove_disabled_prefix() { function remove_disabled_prefix() {
local disabledFile="$1" local disabled_file="$1"
while [[ "$disabledFile" == disabled_* ]]; do while [[ "$disabled_file" == disabled_* ]]; do
disabledFile=$(echo "$disabledFile" | sed 's/^disabled_//') disabled_file=$(echo "$disabled_file" | sed 's/^disabled_//')
done done
echo "$disabledFile" echo "$disabled_file"
} }
function get_mod_name_and_index() { function get_mod_name_and_index() {
@@ -194,13 +181,29 @@ function find_game_directory() {
game_dir=$(timeout 10 find "$search_dir" -type d -path "*/$target_dir" 2>/dev/null | head -n 1) game_dir=$(timeout 10 find "$search_dir" -type d -path "*/$target_dir" 2>/dev/null | head -n 1)
if [[ -z "$game_dir" ]]; then if [[ -z "$game_dir" ]]; then
# if not found, ask user for the directory
log INFO "Could not find the Helldivers 2 data directory automatically." log INFO "Could not find the Helldivers 2 data directory automatically."
log PROMPT "Please enter the ABSOLUTE path to the Helldivers 2 data directory: "
IFS= read -e game_dir; unset IFS
game_dir="$(substitute_home "$game_dir")" # replace ~ with $HOME
game_dir="${game_dir%/}" # remove last / if it exists
[[ ! -d "$game_dir" ]] && { log ERROR "Provided path is not a valid directory."; exit 1; }
else
# confirm with the user that the directory is ok
log INFO "Found Helldivers 2 data directory: $game_dir"
log PROMPT "Is this the correct directory? (Y/n): "
read confirm
if [[ "$confirm" != "y" && "$confirm" != "Y" && "$confirm" != "" ]]; then
log PROMPT "Please enter the path to the Helldivers 2 data directory: " log PROMPT "Please enter the path to the Helldivers 2 data directory: "
IFS= read -e game_dir IFS= read -e game_dir; unset IFS
game_dir="$(substitute_home "$game_dir")" game_dir="$(substitute_home "$game_dir")"
[[ ! -d "$game_dir" ]] && { log ERROR "Provided path is not a valid directory."; exit 1; } [[ ! -d "$game_dir" ]] && { log ERROR "Provided path is not a valid directory."; exit 1; }
fi fi
fi
# save path # save path
echo "$game_dir" > "$H2PATH_FILE" echo "$game_dir" > "$H2PATH_FILE"
@@ -228,11 +231,6 @@ function initialize_config_directory() {
touch "$LAST_CHECKED_UPDATE_FILE" touch "$LAST_CHECKED_UPDATE_FILE"
[[ $? -ne 0 ]] && { log ERROR "Could not create last checked update file."; exit 1; } [[ $? -ne 0 ]] && { log ERROR "Could not create last checked update file."; exit 1; }
fi fi
if [[ ! -f "$UPDATES_AVAILABLE_FILE" ]]; then
touch "$UPDATES_AVAILABLE_FILE"
[[ $? -ne 0 ]] && { log ERROR "Could not create updates available file."; exit 1; }
fi
} }
function initialize_directories() { function initialize_directories() {
@@ -245,7 +243,7 @@ function initialize_directories() {
[[ $? -ne 0 ]] && { log ERROR "Could not create database file."; exit 1; } [[ $? -ne 0 ]] && { log ERROR "Could not create database file."; exit 1; }
echo "$VERSION" | awk -F. '{print $2}' > "$DB_FILE" echo "$(get_version_major)" > "$DB_FILE"
log INFO "Database file ${GREEN}created${NC}: $DB_FILE" log INFO "Database file ${GREEN}created${NC}: $DB_FILE"
fi fi
} }
@@ -261,7 +259,7 @@ function initialize_modpack_directories() {
touch "$MODPACKS_DB_FILE" touch "$MODPACKS_DB_FILE"
[[ $? -ne 0 ]] && { log ERROR "Could not create modpacks database file."; exit 1; } [[ $? -ne 0 ]] && { log ERROR "Could not create modpacks database file."; exit 1; }
echo "$VERSION" | awk -F. '{print $2}' > "$MODPACKS_DB_FILE" echo "$(get_version_major)" > "$MODPACKS_DB_FILE"
log INFO "Modpacks directory and file ${GREEN}created${NC}: $MODPACKS_DB_FILE" log INFO "Modpacks directory and file ${GREEN}created${NC}: $MODPACKS_DB_FILE"
fi fi
} }
@@ -278,6 +276,7 @@ Commands:
l, list List all installed mods. l, list List all installed mods.
e, enable Enable a mod. e, enable Enable a mod.
d, disable Disable a mod. d, disable Disable a mod.
r, rename Rename a mod.
o, order Change load order of a mod. o, order Change load order of a mod.
ex, export Export installed mods to a zip file. ex, export Export installed mods to a zip file.
im, import Import mods from a zip file. im, import Import mods from a zip file.
@@ -290,10 +289,10 @@ Commands:
ns, nexus-setup Setup Nexus Mods integration. ns, nexus-setup Setup Nexus Mods integration.
nu, nexus-update Start Nexus mods update process. nu, nexus-update Start Nexus mods update process.
up, update Update h2mm to the latest version. up, update Update h2mm to the latest version.
r, reset Reset all installed mods. rs, reset Reset all installed mods.
help Display this help message. help Display this help message.
For more information on usage, use h2mm <COMMAND> --help. For more information on usage, use h2mm <COMMAND> --help.
Usage: Example:
h2mm install /path/to/mod.zip h2mm install /path/to/mod.zip
h2mm uninstall -n "Example mod" h2mm uninstall -n "Example mod"
h2mm list h2mm list
@@ -310,10 +309,10 @@ Options:
MOD_FILES Mod file(s), accepts wildcards. MOD_FILES Mod file(s), accepts wildcards.
MOD_DIRECTORIES Directory/directories containing mod files. MOD_DIRECTORIES Directory/directories containing mod files.
Example: Example:
h2mm install /path/to/mod.zip h2mm install mod.zip
h2mm install /path/to/mod/files h2mm install /path/to/mod/directory/
h2mm install /path/to/mod.zip /path/to/mod2.zip /path/to/mod/files h2mm install /path/to/mod.zip /path/to/mod2.zip /path/to/mod/files # mix and match hoewever you want
h2mm install mod.patch_0 mod.patch_0.stream -n "Example mod" h2mm install -n "Example mod" mod.patch_0 mod.patch_0.stream # -n is mandatory when using files
EOF EOF
} }
@@ -324,7 +323,7 @@ Uninstall a mod by name or index.
Options: Options:
-n "MOD_NAME" Name of the mod to uninstall. -n "MOD_NAME" Name of the mod to uninstall.
-i MOD_INDEX Index of the mod to uninstall. -i MOD_INDEX Index of the mod to uninstall.
Usage: Example:
h2mm uninstall -n "Example mod" h2mm uninstall -n "Example mod"
h2mm uninstall -i 3 h2mm uninstall -i 3
EOF EOF
@@ -337,7 +336,7 @@ Enable a mod by name or index.
Options: Options:
-n "MOD_NAME" Name of the mod to enable. -n "MOD_NAME" Name of the mod to enable.
-i MOD_INDEX Index of the mod to enable. -i MOD_INDEX Index of the mod to enable.
Usage: Example:
h2mm enable -n "Example mod" h2mm enable -n "Example mod"
h2mm enable -i 3 h2mm enable -i 3
EOF EOF
@@ -350,7 +349,7 @@ Disable a mod by name or index.
Options: Options:
-n "MOD_NAME" Name of the mod to disable. -n "MOD_NAME" Name of the mod to disable.
-i MOD_INDEX Index of the mod to disable. -i MOD_INDEX Index of the mod to disable.
Usage: Example:
h2mm disable -n "Example mod" h2mm disable -n "Example mod"
h2mm disable -i 3 h2mm disable -i 3
EOF EOF
@@ -396,12 +395,25 @@ Change order of a mod by name or index.
Options: Options:
-i index Index of the mod to order. -i index Index of the mod to order.
-n "MOD_NAME" Name of the mod to order. -n "MOD_NAME" Name of the mod to order.
Usage: Example:
h2mm order -n "Example mod" 6 h2mm order -n "Example mod" 6
h2mm order -i 3 6 h2mm order -i 3 6
EOF EOF
} }
function display_help_rename() {
cat << EOF
Usage: h2mm rename [OPTIONS] <"MOD_NAME"|MOD_INDEX> <NEW_NAME>
Rename a mod by name or index.
Options:
-n "MOD_NAME" Name of the mod to rename.
-i MOD_INDEX Index of the mod to rename.
Example:
h2mm rename -n "Example mod" "New mod name"
h2mm rename -i 3 "New mod name"
EOF
}
function display_help_modpack_list() { function display_help_modpack_list() {
cat << EOF cat << EOF
Usage: h2mm modpack-list Usage: h2mm modpack-list
@@ -469,14 +481,6 @@ Run this again in case you change the API key or want to change the desktop entr
EOF EOF
} }
function display_help_nexus_update() {
cat << EOF
Usage: h2mm nexus-update
Display a list of Nexus mods that have updates available.
Due to Nexus API limitations, this will prompt the user to access the Nexus website to download the updates.
EOF
}
# --- Main Functions --- # --- Main Functions ---
# Upgrade/downgrade logic # Upgrade/downgrade logic
@@ -728,9 +732,9 @@ function mod_install() {
local mod_files=() local mod_files=()
local mod_zip=() local mod_zip=()
local nexus_mod_version="" local nexus_mod_version=""
local nexus_mod_file_id=""
local nexus_mod_id="" local nexus_mod_id=""
local is_rar=false local is_not_zip=false
local has_nexus_mod_arguments=false
# parse arguments # parse arguments
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
@@ -743,18 +747,14 @@ function mod_install() {
[[ -z "$2" ]] && { log ERROR "Nexus mod version is required."; exit 1; } [[ -z "$2" ]] && { log ERROR "Nexus mod version is required."; exit 1; }
nexus_mod_version="$2"; shift 2 nexus_mod_version="$2"; shift 2
;; ;;
"--file-id")
[[ -z "$2" ]] && { log ERROR "Nexus mod file ID is required."; exit 1; }
nexus_mod_file_id="$2"; shift 2
;;
"--mod-id") "--mod-id")
[[ -z "$2" ]] && { log ERROR "Nexus mod ID is required."; exit 1; } [[ -z "$2" ]] && { log ERROR "Nexus mod ID is required."; exit 1; }
nexus_mod_id="$2"; shift 2 nexus_mod_id="$2"; shift 2
;; ;;
*) *)
if [[ "$1" == *.zip || "$1" == *.rar ]]; then if [[ "$1" == *.zip || "$1" == *.rar || "$1" == *.7z ]]; then
mod_zip+=("$1") mod_zip+=("$1")
[[ "$1" == *.rar ]] && is_rar=true [[ "$1" != *.zip ]] && is_not_zip=true
elif [[ -d "$1" ]]; then elif [[ -d "$1" ]]; then
mod_dir+=("$1") mod_dir+=("$1")
else else
@@ -765,7 +765,14 @@ function mod_install() {
esac esac
done done
check_nexus_mod_arguments # check if nexus mod arguments are provided
if [[ -n "$nexus_mod_id" || -n "$nexus_mod_version" ]]; then
if [[ -z "$nexus_mod_id" || -z "$nexus_mod_version" ]]; then
log ERROR "Nexus mod ID, file ID and version are required when specifing at least one of them."
exit 1
fi
has_nexus_mod_arguments=true
fi
# edge case when there is a combination of mod zips and directories, call function for all zips and dirs # edge case when there is a combination of mod zips and directories, call function for all zips and dirs
if [[ ${mod_zip} && ${mod_dir} ]]; then if [[ ${mod_zip} && ${mod_dir} ]]; then
@@ -788,10 +795,10 @@ function mod_install() {
# if zip, 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 if [[ -n "$mod_zip" ]]; then
if [[ $is_rar == true ]]; then if [[ $is_not_zip == true ]]; then
command -v unar &> /dev/null || { log ERROR "Rar archive could not be extracted because package \"unarchiver\" is not installed."; exit 1; } command -v unar &> /dev/null || { log ERROR "Archive in 7z/rar format could not be extracted because package \"unarchiver\" is not installed."; exit 1; }
else else
command -v unzip &> /dev/null || { log ERROR "Zip archive could not be extracted because package \"unzip\" is not installed."; exit 1; } command -v unzip &> /dev/null || { log ERROR "Archive in zip format could not be extracted because package \"unzip\" is not installed."; exit 1; }
fi fi
if [[ ! -f "$mod_zip" ]]; then if [[ ! -f "$mod_zip" ]]; then
@@ -809,7 +816,7 @@ function mod_install() {
# mod_dir as a temporary directory # mod_dir as a temporary directory
mod_dir+=$(mktemp -d) mod_dir+=$(mktemp -d)
if [[ $is_rar == true ]]; then if [[ $is_not_zip == true ]]; then
unar -q "$mod_zip" -o "$mod_dir" unar -q "$mod_zip" -o "$mod_dir"
else else
unzip -qq "$mod_zip" -d "$mod_dir" unzip -qq "$mod_zip" -d "$mod_dir"
@@ -849,8 +856,21 @@ function mod_install() {
if [[ ${#filtered_dirs[@]} -gt 1 ]]; then if [[ ${#filtered_dirs[@]} -gt 1 ]]; then
log INFO "Multiple mod variants found for mod ${mod_name}." log INFO "Multiple mod variants found for mod ${mod_name}."
# print the variant name by display all the directories (and how they're nested)
# first, take the basename of the directory/zip we're installing from
# then, make the variant name by removing the tmp dir name from the path (if it's a zip)
# finally, take out this name, if it exists
if [[ -n "$mod_zip" ]]; then
mod_file_name="$(get_basename "$mod_zip")"
else
mod_file_name="*"
fi
for i in "${!filtered_dirs[@]}"; do for i in "${!filtered_dirs[@]}"; do
log INFO "$((i + 1)). $(basename "${filtered_dirs[$i]}")" variant_name="${filtered_dirs[$i]#$mod_dir/}" # remote temp name in case its a zip
log INFO "$((i + 1)). ${variant_name#$mod_file_name/}" # remote the name of the zip/dir if it exists
done done
# prompt user to choose # prompt user to choose
@@ -886,6 +906,9 @@ function mod_install() {
[[ ! -f "$file" ]] && { log ERROR "Mod file $file does not exist."; exit 1; } [[ ! -f "$file" ]] && { log ERROR "Mod file $file does not exist."; exit 1; }
done done
# sanitize mod name so it doesn't contain commas or underscores
mod_name=$(echo "$mod_name" | sed 's/,//g' | sed 's/_/ /g')
# verify duplicate mod names # verify duplicate mod names
if [[ $has_nexus_mod_arguments == false ]]; then if [[ $has_nexus_mod_arguments == false ]]; then
get_mod_name_and_index --do-not-exit get_mod_name_and_index --do-not-exit
@@ -922,7 +945,7 @@ function mod_install() {
# add entry to database # add entry to database
next_id=$(awk -F, 'NR > 1 {last_id = $1} END {print last_id + 1}' "$DB_FILE") next_id=$(awk -F, 'NR > 1 {last_id = $1} END {print last_id + 1}' "$DB_FILE")
entry="$next_id,ENABLED,$mod_name,$nexus_mod_id,$nexus_mod_file_id,$nexus_mod_version,${target_files[*]}" entry="$next_id,ENABLED,$mod_name,$nexus_mod_id,$nexus_mod_version,${target_files[*]}"
echo "$entry" >> "$DB_FILE" echo "$entry" >> "$DB_FILE"
log INFO "Mod ${GREEN}successfully${NC} installed: $mod_name." log INFO "Mod ${GREEN}successfully${NC} installed: $mod_name."
@@ -1012,9 +1035,8 @@ function mod_list() {
mod_status = $2; mod_status = $2;
mod_name = $3; mod_name = $3;
mod_id = $4; mod_id = $4;
mod_file_id = $5; mod_version = $5;
mod_version = $6; mod_files = $6;
mod_files = $7;
mod_details = ""; mod_details = "";
STATUS_COLOR = (mod_status == "DISABLED") ? RED : GREEN; STATUS_COLOR = (mod_status == "DISABLED") ? RED : GREEN;
@@ -1031,7 +1053,7 @@ function mod_list() {
printf "%2s. [%s%s%s/%s%s%s] %s %s\n", mod_index, STATUS_COLOR, mod_status, NC, MOD_TYPE_COLOR, mod_type, NC, mod_name, mod_version; printf "%2s. [%s%s%s/%s%s%s] %s %s\n", mod_index, STATUS_COLOR, mod_status, NC, MOD_TYPE_COLOR, mod_type, NC, mod_name, mod_version;
if (verbose == "true") { if (verbose == "true") {
gsub(/ /,"\n -> ", mod_files); gsub(/ /,"\n -> ", mod_files);
if (mod_id != "") printf " => Nexus mod ID: %s / Nexus file ID: %s\n", mod_id, mod_file_id; if (mod_id != "") printf " => Nexus mod ID: %s / Nexus file ID: %s\n", mod_id;
printf " -> %s\n", mod_files; printf " -> %s\n", mod_files;
} }
}' "$DB_FILE" }' "$DB_FILE"
@@ -1100,7 +1122,7 @@ function mod_import() {
[[ ! -f "$1" ]] && { log ERROR "File $1 does not exist."; exit 1; } [[ ! -f "$1" ]] && { log ERROR "File $1 does not exist."; exit 1; }
# reset mods before importing # reset mods before importing
[[ $modpack == false ]] && log INFO "Importing mods will ${RED}reset${NC} your mods." [[ $modpack == false ]] && log INFO "Importing will ${RED}reset${NC} your mods."
mod_reset --no-path-reset mod_reset --no-path-reset
# extract in temp directory # extract in temp directory
@@ -1114,7 +1136,7 @@ function mod_import() {
# fix breaking changes if the version number (from the first line) is different # fix breaking changes if the version number (from the first line) is different
db_version=$(head -n 1 "$MODS_EXPORT_DIR/mods.csv") db_version=$(head -n 1 "$MODS_EXPORT_DIR/mods.csv")
current_version=$(echo "$VERSION" | awk -F. '{print $2}') current_version=$(get_version_major)
[[ -z "$db_version" || ! "$db_version" =~ ^[0-9]+$ ]] && { log ERROR "Invalid version number inside mods.csv from imported archive."; exit 1; } [[ -z "$db_version" || ! "$db_version" =~ ^[0-9]+$ ]] && { log ERROR "Invalid version number inside mods.csv from imported archive."; exit 1; }
@@ -1315,6 +1337,48 @@ function mod_order {
log INFO "Mod ${GREEN}successfully${NC} reindexed: \"$mod_name\" went from $mod_index to $new_index." log INFO "Mod ${GREEN}successfully${NC} reindexed: \"$mod_name\" went from $mod_index to $new_index."
} }
function mod_rename() {
parse_help_has_arguments display_help_rename "$@"
local mod_name=""
local new_mod_name=""
local mod_index=""
# parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
"-i")
[[ -z "$2" || ! "$2" =~ ^[0-9]+$ ]] && { log ERROR "Invalid mod index."; exit 1; }
mod_index="$2"; shift 2
;;
"-n")
[[ -z "$2" ]] && { log ERROR "Mod name is required."; exit 1; }
mod_name="$2"; shift 2
;;
*)
new_mod_name="$1"; shift 1
;;
esac
done
[[ -z "$mod_name" && -z "$mod_index" ]] && { log ERROR "Mod name or index is required to rename."; exit 1; }
# find mod files
get_mod_name_and_index
# verify new mod name is not empty, does not contain commas and trim it
new_mod_name=$(echo "$new_mod_name" | sed 's/,//g' | sed 's/^[[:space:]]*//g' | sed 's/[[:space:]]*$//g')
[[ -z "$new_mod_name" ]] && { log ERROR "New mod name is required."; exit 1; }
# verify if new mod name already exists in the database
grep -q ",$new_mod_name," "$DB_FILE"
[[ $? -eq 0 ]] && { log ERROR "Mod with name \"$new_mod_name\" already exists."; exit 1; }
sed -i "s/^$mod_index,ENABLED,$mod_name,/$mod_index,ENABLED,$new_mod_name,/" "$DB_FILE"
sed -i "s/^$mod_index,DISABLED,$mod_name,/$mod_index,DISABLED,$new_mod_name,/" "$DB_FILE"
log INFO "Mod ${GREEN}successfully${NC} renamed: ${ORANGE}$mod_name${NC} -> ${GREEN}$new_mod_name${NC}."
}
# --- Modpack management --- # --- Modpack management ---
function modpack_list() { function modpack_list() {
@@ -1400,7 +1464,7 @@ function modpack_create() {
OLD_MODS_DIR="$MODS_DIR" OLD_MODS_DIR="$MODS_DIR"
MODS_DIR="$(mktemp -d)" MODS_DIR="$(mktemp -d)"
DB_FILE="$MODS_DIR/mods.csv" DB_FILE="$MODS_DIR/mods.csv"
echo "$(get_version_major "$VERSION")" > "$DB_FILE" echo "$(get_version_major)" > "$DB_FILE"
# install selected mods to temp directory # install selected mods to temp directory
for entry in "${mod_entries[@]}"; do for entry in "${mod_entries[@]}"; do
@@ -1601,7 +1665,7 @@ function nexus_setup() {
# test API key # test API key
response=$(curl -sSIH "apikey: $nexus_api_key" -X GET "https://api.nexusmods.com/v1/users/validate.json") response=$(curl -sSIH "apikey: $nexus_api_key" -X GET "https://api.nexusmods.com/v1/users/validate.json")
[[ $? -ne 0 ]] && { log ERROR "curl failed."; exit 1; } [[ $? -eq 8 ]] && log INFO "Above error is common for Steam Deck, everything is fine."
[[ ! "$response" =~ "HTTP/2 200" ]] && { log ERROR "Invalid Nexus Mods API key."; exit 1; } [[ ! "$response" =~ "HTTP/2 200" ]] && { log ERROR "Invalid Nexus Mods API key."; exit 1; }
log INFO "Nexus Mods API key ${GREEN}successfully${NC} tested and saved." log INFO "Nexus Mods API key ${GREEN}successfully${NC} tested and saved."
@@ -1645,7 +1709,7 @@ function nexus_setup() {
[[ "$terminal_command" != *"<COMMAND>"* ]] && { log ERROR "Terminal command is invalid. You did not include <COMMAND>"; exit 1; } [[ "$terminal_command" != *"<COMMAND>"* ]] && { log ERROR "Terminal command is invalid. You did not include <COMMAND>"; exit 1; }
# test terminal -e command # test terminal -e command
wait_command="read -p 'Press any key to validate terminal...'" wait_command="read -p 'Press Enter to validate terminal...'"
test_command=$(echo "$terminal_command" | sed "s/<COMMAND>/$wait_command/") test_command=$(echo "$terminal_command" | sed "s/<COMMAND>/$wait_command/")
log INFO "Using test command: $test_command" log INFO "Using test command: $test_command"
@@ -1655,7 +1719,7 @@ function nexus_setup() {
esac esac
# build terminal command # build terminal command
terminal_command=$(echo "$terminal_command" | sed "s/<COMMAND>/h2mm nexus/") terminal_command=$(echo "$terminal_command" | sed "s|<COMMAND>|$(which h2mm) nexus \%u|")
# build the desktop entry # build the desktop entry
local desktop_entry="[Desktop Entry] local desktop_entry="[Desktop Entry]
@@ -1667,7 +1731,10 @@ Type=Application
Categories=Game; Categories=Game;
MimeType=x-scheme-handler/nxm;" MimeType=x-scheme-handler/nxm;"
local desktop_file="$HOME/.local/share/applications/h2mm.desktop" local desktop_folder="$HOME/.local/share/applications"
[[ ! -d "$desktop_folder" ]] && mkdir -p "$desktop_folder"
local desktop_file="$desktop_folder/h2mm.desktop"
# create the desktop entry file # create the desktop entry file
echo "$desktop_entry" > "$desktop_file" echo "$desktop_entry" > "$desktop_file"
@@ -1675,89 +1742,21 @@ MimeType=x-scheme-handler/nxm;"
log INFO "" log INFO ""
log INFO "Desktop entry ${GREEN}successfully${NC} created." log INFO "Desktop entry ${GREEN}successfully${NC} created."
log INFO "Your system needs to refresh the desktop database to use the new entry."
log INFO "This requires sudo permissions. Please enter your password."
sudo update-desktop-database update-desktop-database "$desktop_folder"
[[ $? -ne 0 ]] && { log ERROR "Could not refresh desktop database."; exit 1; }
# test xdg mime command exists
if command -v xdg-mime >/dev/null 2>&1; then
xdg-mime default "$desktop_file" x-scheme-handler/nxm
fi
log INFO "You can now use the Nexus Mods integration." log INFO "You can now use the Nexus Mods integration."
log INFO "To use it, go to a mod page and click on the \"Vortex\" or \"Download with Manager\" button." log INFO "To use it, go to a mod page and click on the \"Vortex\" or \"Download with Manager\" button."
log INFO "Your browser will ask you to open the link with h2mm." log INFO "Your browser will ask you to open the link with h2mm."
} }
function nexus_get_mod_info() {
local mod_id="$1"
local mod_file_id="$2"
[[ -z "$mod_id" || -z "$mod_file_id" ]] && { log ERROR "Mod ID and file ID are required."; exit 1; }
# api_key
get_nexus_api_key
api_url="https://api.nexusmods.com/v1/games/helldivers2/mods/$mod_id/files/$mod_file_id.json"
response=$(curl -sSw " http:%{http_code}" -H "apikey: $api_key" "$api_url")
[[ $? -ne 0 ]] && { log ERROR "curl failed."; exit 1; }
# checks
status="${response: -3}"
[[ "$status" != "200" ]] && { log ERROR "Invalid response from Nexus Mods API."; exit 1; }
# extract info
mod_name=$(echo "$response" | grep -oP '"name":"\K[^"]+')
nexus_mod_version=$(echo "$response" | grep -oP '"version":"\K[^"]+')
[[ -z "$mod_name" || -z "$nexus_mod_version" ]] && { log ERROR "Could not extract mod name and version."; exit 1; }
}
function nexus_check_for_updates() {
# get list of entries that have a nexus mod_id
IFS=$'\n' nexus_mods=($(awk -F, 'NR > 1 && $4 != "" {print $0}' "$DB_FILE")); unset IFS
[[ -z "$nexus_mods" ]] && return
local update_count=0
[[ -f "$UPDATES_AVAILABLE_FILE" ]] && rm -f "$UPDATES_AVAILABLE_FILE"
# call the API for each mod_id and get the latest version
for entry in "${nexus_mods[@]}"; do
nexus_mod_id=$(get_nexus_mod_id_by_entry_from_db "$entry")
nexus_mod_file_id=$(get_nexus_mod_file_id_by_entry_from_db "$entry")
local_mod_version=$(get_nexus_mod_version_by_entry_from_db "$entry")
nexus_get_mod_info "$nexus_mod_id" "$nexus_mod_file_id"
# compare the latest version with the installed version
if [[ "$nexus_mod_version" != "$local_mod_version" ]]; then
mod_name=$(get_name_by_entry_from_db "$entry")
# store in a temp file (inside config dir) the mods that have updates
echo "$mod_name,$local_mod_version,$nexus_mod_version,$nexus_mod_id,$nexus_mod_file_id" >> "$UPDATES_AVAILABLE_FILE"
update_count=$((update_count + 1))
fi
done
[[ $update_count -eq 0 ]] && return 0;
log WARNING "Updates available for ${ORANGE}$update_count${NC} Nexus mods => run 'h2mm nexus-update'."
}
function nexus_update_mods() {
parse_help_no_arguments display_help_nexus_update "$@"
[[ $(wc -l < "$UPDATES_AVAILABLE_FILE") -eq 0 ]] && { log INFO "No updates available."; return 0; }
log INFO "Due to Nexus Mods API limitations, the update process is not automated."
log INFO "Please visit the direct download links to install the latest version of your mods:"
awk -v GREEN="$GREEN" -v ORANGE="$ORANGE" -v NC="$NC" -F, '{
printf "%2s. %s\n", NR, $1
printf " => Update from %s%s%s -> %s%s%s\n", ORANGE, $2, NC, GREEN, $3, NC
printf " -> Direct install: https://www.nexusmods.com/helldivers2/mods/%s?tab=files&file_id=%s&nmm=1\n", $4, $5
printf " -> Mod page: https://www.nexusmods.com/helldivers2/mods/%s\n", $4
}' "$UPDATES_AVAILABLE_FILE"
}
function nexus() { function nexus() {
trap 'read -p "Press any key to continue..."' EXIT trap 'read -p "Press Enter to continue..."' EXIT
local mod_nxm_link="$1" local mod_nxm_link="$1"
[[ -z "$mod_nxm_link" ]] && { log ERROR "Nexus Mods nxm link is required."; exit 1; } [[ -z "$mod_nxm_link" ]] && { log ERROR "Nexus Mods nxm link is required."; exit 1; }
@@ -1774,6 +1773,8 @@ function nexus() {
key=$(echo "$mod_nxm_link" | awk -F= '{print $2}' | cut -d\& -f1) key=$(echo "$mod_nxm_link" | awk -F= '{print $2}' | cut -d\& -f1)
expires=$(echo "$mod_nxm_link" | awk -F= '{print $3}' | cut -d\& -f1) expires=$(echo "$mod_nxm_link" | awk -F= '{print $3}' | cut -d\& -f1)
[[ -z "$nexus_mod_id" || -z "$nexus_mod_file_id" || -z "$key" || -z "$expires" ]] && { log ERROR "Could not extract Nexus Mods mod ID, file ID, key or expiry."; exit 1; }
api_url="https://api.nexusmods.com/v1/games/helldivers2/mods/$nexus_mod_id/files/$nexus_mod_file_id/download_link.json?key=$key&expires=$expires" api_url="https://api.nexusmods.com/v1/games/helldivers2/mods/$nexus_mod_id/files/$nexus_mod_file_id/download_link.json?key=$key&expires=$expires"
log INFO "Using API URL: $api_url" log INFO "Using API URL: $api_url"
@@ -1803,13 +1804,24 @@ function nexus() {
log INFO "" log INFO ""
# get mod info # get mod info
nexus_get_mod_info "$nexus_mod_id" "$nexus_mod_file_id" api_url="https://api.nexusmods.com/v1/games/helldivers2/mods/$nexus_mod_id/files/$nexus_mod_file_id.json"
response=$(curl -sSw " http:%{http_code}" -H "apikey: $api_key" "$api_url")
[[ $? -ne 0 ]] && { log ERROR "curl failed."; exit 1; }
# checks
status="${response: -3}"
[[ "$status" != "200" ]] && { log ERROR "Invalid response from Nexus Mods API."; exit 1; }
# extract info
mod_name=$(echo "$response" | grep -oP '"name":"\K[^"]+')
nexus_mod_version=$(echo "$response" | grep -oP '"version":"\K[^"]+')
[[ -z "$mod_name" || -z "$nexus_mod_version" ]] && { log ERROR "Could not extract mod name and version."; exit 1; }
# check if the mod is already installed, if yes, update it, otherwise install it # check if the mod is already installed, if yes, update it, otherwise install it
existing_mod_entry=$(get_entry_from_db_by_nexus_mod_id "$nexus_mod_id") existing_mod_entry=$(get_entry_from_db_by_nexus_mod_id "$nexus_mod_id")
if [[ -z "$existing_mod_entry" ]]; then if [[ -z "$existing_mod_entry" ]]; then
# install the mod if it doesn't exist # install the mod if it doesn't exist
mod_install "$output_file" -n "$mod_name" --mod-id "$nexus_mod_id" --file-id "$nexus_mod_file_id" --version "$nexus_mod_version" mod_install "$output_file" -n "$mod_name" --mod-id "$nexus_mod_id" --version "$nexus_mod_version"
else else
# replace the mod if it exists # replace the mod if it exists
existing_mod_index=$(get_index_by_entry_from_db "$existing_mod_entry") existing_mod_index=$(get_index_by_entry_from_db "$existing_mod_entry")
@@ -1817,46 +1829,51 @@ function nexus() {
mod_uninstall -i "$mod_index" mod_uninstall -i "$mod_index"
mod_install "$output_file" -n "$mod_name" --mod-id "$nexus_mod_id" --file-id "$nexus_mod_file_id" --version "$nexus_mod_version" mod_install "$output_file" -n "$mod_name" --mod-id "$nexus_mod_id" --version "$nexus_mod_version"
# get the current mod index from what we just installed, last line of the db # get the current mod index from what we just installed, last line of the db
new_mod_index=$(get_index_by_entry_from_db "$(awk -F, 'END {print $1}' "$DB_FILE")") new_mod_index=$(get_index_by_entry_from_db "$(awk -F, 'END {print $1}' "$DB_FILE")")
[[ -z "$new_mod_index" ]] && { log ERROR "Could not get current mod index."; exit 1; } [[ -z "$new_mod_index" ]] && { log ERROR "Could not get current mod index."; exit 1; }
[[ $mod_index -ne $old_mod_index ]] && mod_order -i "$new_mod_index" "$mod_index" [[ $mod_index -ne $new_mod_index ]] && mod_order -i "$new_mod_index" "$mod_index"
log INFO "Mod ${GREEN}successfully${NC} updated: $mod_name." log INFO "Mod ${GREEN}successfully${NC} updated: $mod_name."
disable_all_modpacks
fi fi
disable_all_modpacks
} }
# --- Update Check --- # --- Update Check ---
function check_h2mm_update() { function check_for_h2mm_update() {
last_update=$(cat "$LAST_CHECKED_UPDATE_FILE")
# if last_update is not in unix time format, reset it (for version <= 0.5.0)
if [[ ! "$last_update" =~ ^[0-9]+$ ]]; then
rm -f "$LAST_CHECKED_UPDATE_FILE"
echo "$(date +%s)" > "$LAST_CHECKED_UPDATE_FILE"
last_update=$(cat "$LAST_CHECKED_UPDATE_FILE")
fi
# check for updates by comparing the last update time + 3 hours with the current time
if [[ -n "$last_update" && "$(date +%s)" -lt $(("$last_update" + 7200)) ]]; then
return
fi
log INFO "Checking for updates..."
latest_version=$(curl -sS "$VERSION_URL") latest_version=$(curl -sS "$VERSION_URL")
if [[ $? -ne 0 ]]; then if [[ $? -ne 0 ]]; then
log ERROR "Could not check for updates." log ERROR "Could not check for updates (curl failed)."
return return
fi fi
if [[ "$latest_version" != "$VERSION" ]]; then if [[ "$latest_version" != "$VERSION" ]]; then
log WARNING "A new version of h2mm is available: ${ORANGE}$VERSION${NC} -> ${GREEN}$latest_version${NC} => run 'h2mm update'." log WARNING "A new version of h2mm is available: ${ORANGE}$VERSION${NC} -> ${GREEN}$latest_version${NC} => run 'h2mm update'."
fi fi
}
function check_for_updates() { echo "$(date +%s)" > "$LAST_CHECKED_UPDATE_FILE"
last_update=$(cat "$LAST_CHECKED_UPDATE_FILE")
if [[ -n "$last_update" && "$(date +%s)" -lt $(("$(date +%s -d "$last_update")" + 7200)) ]]; then
return
fi
log INFO "Checking for updates..."
check_h2mm_update
nexus_check_for_updates
echo "$(date)" > "$LAST_CHECKED_UPDATE_FILE"
} }
# --- Main --- # --- Main ---
@@ -1868,7 +1885,7 @@ function main() {
initialize_directories initialize_directories
initialize_modpack_directories initialize_modpack_directories
check_for_updates check_for_h2mm_update
case "$command" in case "$command" in
"install"|"i") "install"|"i")
@@ -1895,6 +1912,9 @@ function main() {
"order"|"o") "order"|"o")
mod_order "$@" mod_order "$@"
;; ;;
"rename"|"r")
mod_rename "$@"
;;
"modpack-list"|"ml") "modpack-list"|"ml")
modpack_list "$@" modpack_list "$@"
;; ;;
@@ -1916,13 +1936,10 @@ function main() {
"nexus-setup"|"ns") "nexus-setup"|"ns")
nexus_setup "$@" nexus_setup "$@"
;; ;;
"nexus-update"|"nu")
nexus_update_mods "$@"
;;
"nexus") "nexus")
nexus "$@" nexus "$@"
;; ;;
"reset"|"r") "reset"|"rs")
mod_reset "$@" mod_reset "$@"
;; ;;
"version"|"v"|"-v"|"--version") "version"|"v"|"-v"|"--version")
+37 -30
View File
@@ -7,7 +7,6 @@ ORANGE='\033[0;33m'
NC='\033[0m' NC='\033[0m'
DESTINATION_PATH="/usr/local/bin" DESTINATION_PATH="/usr/local/bin"
SCRIPT_NAME="h2mm"
REPO_URL="https://raw.githubusercontent.com/v4n00/h2mm-cli/refs/heads/master" REPO_URL="https://raw.githubusercontent.com/v4n00/h2mm-cli/refs/heads/master"
function log() { function log() {
@@ -30,15 +29,15 @@ function log() {
# warning # warning
log INFO "${RED}!!! WARNING !!!${NC}"
cat << EOF cat << EOF
!!! WARNING !!! This script will install Helldivers 2 Mod Manager CLI for Linux to $DESTINATION_PATH/h2mm.
This script will install Helldivers 2 Mod Manager CLI for Linux to $DESTINATION_PATH/$SCRIPT_NAME.
Running this script will require sudo permissions. DO NOT TRUST random scripts from the internet. Running this script will require sudo permissions. DO NOT TRUST random scripts from the internet.
If you want to review the script before running it, check out the mod repository for yourself: If you want to review the script before running it, check out the repository for yourself:
https://github.com/v4n00/h2mm-cli https://github.com/v4n00/h2mm-cli
!!! WARNING !!!
EOF EOF
log INFO "${RED}!!! WARNING !!!${NC}"
log INFO ""
# breaking changes hash table # breaking changes hash table
breaking_changes_patches=( breaking_changes_patches=(
@@ -46,19 +45,21 @@ breaking_changes_patches=(
["3"]='sed -i "1 i\\3" "$1/mods.csv"' ["3"]='sed -i "1 i\\3" "$1/mods.csv"'
["4"]='tmp_file=$(mktemp) && awk '\''BEGIN {FS=OFS=","} NR==1 {print 4; next} {print NR-1, $2, $3, $4, $5}'\'' "$1/mods.csv" > "$tmp_file" && tee "$1/mods.csv" < "$tmp_file" > /dev/null && rm "$tmp_file"' ["4"]='tmp_file=$(mktemp) && awk '\''BEGIN {FS=OFS=","} NR==1 {print 4; next} {print NR-1, $2, $3, $4, $5}'\'' "$1/mods.csv" > "$tmp_file" && tee "$1/mods.csv" < "$tmp_file" > /dev/null && rm "$tmp_file"'
["5"]='sed -i "s/^\([0-9]\+\),\(.*\),\(.*\),\(.*\)/\1,\2,\3,,,,\4/" "$1/mods.csv"; sed -i "1 s/4/5/" "$1/mods.csv"' ["5"]='sed -i "s/^\([0-9]\+\),\(.*\),\(.*\),\(.*\)/\1,\2,\3,,,,\4/" "$1/mods.csv"; sed -i "1 s/4/5/" "$1/mods.csv"'
["6"]='sed -i "s/^\([0-9]\+\),\(.*\),\(.*\),\(.*\),\(.*\),\(.*\),\(.*\)/\1,\2,\3,\4,\6,\7/" "$1/mods.csv"; sed -i "1 s/5/6/" "$1/mods.csv"'
) )
# notify if update is happening # notify if update is happening
installed_version="" installed_version=""
latest_version="" latest_version=""
if [[ -x "$(command -v $SCRIPT_NAME)" ]]; then if [[ -x "$(command -v h2mm)" ]]; then
installed_version=$($SCRIPT_NAME --version) installed_version=$(h2mm --version)
# if installed version isn't x.x.x crash # if installed version isn't x.x.x crash
if [[ ! "$installed_version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then if [[ ! "$installed_version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
log ERROR "Installed version is not in the correct format." log ERROR "Installed version is not in the correct format."
log ERROR "h2mm is installed here -> $(which h2mm)" log ERROR "h2mm is installed here -> $(which h2mm)"
log ERROR "Delete the script file and retry the install script, any mods installed will not be lost." log ERROR "Delete the script file and retry the install script, any mods installed will not be lost."
log INFO 'Download using the command: bash -c "$(curl -fsSL https://raw.githubusercontent.com/v4n00/h2mm-cli/refs/heads/master/install.sh)"'
exit 1 exit 1
fi fi
@@ -92,7 +93,7 @@ if [[ "$response_sd" == "y" || "$response_sd" == "Y" ]]; then
[[ $? -ne 0 ]] && { log ERROR "Failed to add $DESTINATION_PATH to \$PATH in ~/.bashrc." ; exit 1; } [[ $? -ne 0 ]] && { log ERROR "Failed to add $DESTINATION_PATH to \$PATH in ~/.bashrc." ; exit 1; }
source "$HOME/.bashrc" source "$HOME/.bashrc"
[[ $? -ne 0 ]] && { log ERROR "Failed to source ~/.bashrc." ; exit 1; } export PATH="$HOME/.local/bin:$PATH" # fallback kinda in case sourcing fails
log INFO "Added $DESTINATION_PATH to your \$PATH in ~/.bashrc." log INFO "Added $DESTINATION_PATH to your \$PATH in ~/.bashrc."
fi fi
@@ -115,37 +116,42 @@ latest_major=$(echo "$latest_version" | awk -F. '{print $2}')
if [[ $latest_major -gt $installed_major ]]; then if [[ $latest_major -gt $installed_major ]]; then
log INFO "" log INFO ""
log INFO "Major version upgrade detected." log INFO "${GREEN}IMPORTANT${NC}: Major version upgrade detected. Check out the changelogs here -> https://github.com/v4n00/h2mm-cli/releases"
log INFO "Check out the changelogs here -> https://github.com/v4n00/h2mm-cli/releases" log INFO "The script will proceed to upgrade the database file. Creating a backup in case anything goes wrong."
log INFO "The script will proceed to upgrade the database file to avoid breaking changes."
# find hd2 path
search_dir="${HOME}"
target_dir="Steam/steamapps/common/Helldivers\ 2/data"
# make backup # make backup
log INFO "Creating a backup in case anything goes wrong."
h2mm export h2mm export
# check if game directory is in ~/.config/h2mm/h2path # check if game directory is in ~/.config/h2mm/h2path
if [[ -f "$HOME/.config/h2mm/h2path" ]]; then if [[ -f "$HOME/.config/h2mm/h2path" ]]; then
game_dir=$(cat "$HOME/.config/h2mm/h2path") 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; } [[ ! -d "$game_dir" ]] && { log ERROR "Helldivers 2 data directory is not valid: $game_dir." ; exit 1; }
log INFO "Helldivers 2 data directory found: $game_dir."
else else
log INFO "Searching for the Helldivers 2 data directory... (10 seconds timeout)" log INFO "Searching for the Helldivers 2 data directory... (10 seconds timeout)"
game_dir=$(timeout 10 find "$search_dir" -type d -path "*/$target_dir" 2>/dev/null | head -n 1) game_dir=$(timeout 10 find "$HOME" -type d -path "*/Steam/steamapps/common/Helldivers\ 2/data" 2>/dev/null | head -n 1)
fi fi
# if not found, prompt user # if not found, prompt user
if [[ -z "$game_dir" ]]; then if [[ -z "$game_dir" ]]; then
# if not found, ask user for the directory
log INFO "Could not find the Helldivers 2 data directory automatically." log INFO "Could not find the Helldivers 2 data directory automatically."
log PROMPT "Please enter the path to the Helldivers 2 data directory: " log PROMPT "Please enter the path to the Helldivers 2 data directory: "
IFS= read -e game_dir IFS= read -e game_dir; unset IFS
if [[ ! -d "$game_dir" ]]; then game_dir="$(substitute_home "$game_dir")"
log ERROR "Provided path is not a valid directory."
exit 1 [[ ! -d "$game_dir" ]] && { log ERROR "Provided path is not a valid directory."; exit 1; }
else
# confirm with the user that the directory is ok
log INFO "Found Helldivers 2 data directory: $game_dir"
log PROMPT "Is this the correct directory? (Y/n): "
read confirm
if [[ "$confirm" != "y" && "$confirm" != "Y" && "$confirm" != "" ]]; then
log PROMPT "Please enter the path to the Helldivers 2 data directory: "
IFS= read -e game_dir; unset IFS
game_dir="$(substitute_home "$game_dir")"
[[ ! -d "$game_dir" ]] && { log ERROR "Provided path is not a valid directory."; exit 1; }
fi fi
fi fi
@@ -174,12 +180,12 @@ if [[ $latest_major -gt $installed_major ]]; then
fi fi
# install # install
log INFO "Installing $SCRIPT_NAME to $DESTINATION_PATH." log INFO "Installing h2mm to $DESTINATION_PATH."
sudo curl "$REPO_URL"/h2mm --output "$DESTINATION_PATH/$SCRIPT_NAME" sudo curl "$REPO_URL"/h2mm --output "$DESTINATION_PATH/h2mm"
sudo chmod +x "$DESTINATION_PATH/$SCRIPT_NAME" sudo chmod +x "$DESTINATION_PATH/h2mm"
log INFO "" log INFO ""
[[ ! -x "$(command -v $SCRIPT_NAME)" ]] && { log ERROR "Installation failed. Mod manager was not found in \$PATH." ; exit 1; } [[ ! -x "$(command -v h2mm)" ]] && { log ERROR "Installation failed. Mod manager was not found in \$PATH." ; exit 1; }
log INFO "Helldivers 2 Mod Manager CLI ${GREEN}successfully${NC} installed." log INFO "Helldivers 2 Mod Manager CLI ${GREEN}successfully${NC} installed."
log INFO "${GREEN}IMPORTANT${NC}: To install mods, you need to have installed:" log INFO "${GREEN}IMPORTANT${NC}: To install mods, you need to have installed:"
@@ -187,5 +193,6 @@ log INFO " -> \"unzip\" package for .zip archives"
log INFO " -> \"unarchiver\" package for .rar archives" log INFO " -> \"unarchiver\" package for .rar archives"
log INFO "If you do not know how to install these packages, please search for your linux distro on how to install packages." log INFO "If you do not know how to install these packages, please search for your linux distro on how to install packages."
log INFO "" log INFO ""
log INFO "Use the mod manager by running '$SCRIPT_NAME' in your terminal." log INFO "Use the mod manager by running 'h2mm' in your terminal."
log INFO "Check out the Nexus Mods integration by running 'h2mm nexus-setup'."
log INFO "Made with love <3 by v4n and contributors." log INFO "Made with love <3 by v4n and contributors."
+1 -1
View File
@@ -1 +1 @@
0.5.0 0.6.0