Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 90 additions & 4 deletions scripts/Extras.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1375,6 +1375,75 @@ option_five() {
read -n 1 -s -r -p " Press any key to return to the menu..." </dev/tty
}

option_six() {
echo "########################################################################################################" >> "${LOG_FILE}"
echo "PS2 VMC Groups:" >> "${LOG_FILE}"
EXTRAS_SPLASH

clean_up
MOUNT_OPL || return 1

if [[ "$(awk -F' *= *' '$1=="VMC_GROUPS"{print $2}' "${OPL}/version.txt" 2>/dev/null)" == "enabled" ]]; then
current_status="ENABLED"
else
current_status="DISABLED"
fi

cat <<EOF
PS2 VMC Groups for Neutrino
---------------------------
Current status: $current_status

When enabled, the Game Installer creates an 8MB virtual memory
card (VMC) image file for every PS2 game installed with Neutrino.

Games belonging to a cross-game save group (as documented at
https://github.com/sync-on-luma/xebplus-neutrino-loader-plugin/wiki)
share a single VMC so that cross-game save features work correctly.
Multi-disc games also share a single VMC across all discs.

All other PS2 games each receive their own unique 8MB VMC.
VMC files and CFG entries that already exist are never overwritten.

Note: VMC files are created in games/VMC/ and are synced to the
PS2 drive on the next game installation run.

EOF

if [[ "$current_status" == "ENABLED" ]]; then
read -rp " Disable PS2 VMC Groups? (y/n): " answer </dev/tty
if [[ "${answer,,}" == "y" ]]; then
if grep -q '^VMC_GROUPS =' "${OPL}/version.txt"; then
sed -i 's/^VMC_GROUPS =.*/VMC_GROUPS = disabled/' "${OPL}/version.txt"
else
echo 'VMC_GROUPS = disabled' >> "${OPL}/version.txt"
fi
VMC_STATUS="disabled"
echo
echo " [✓] PS2 VMC Groups disabled."
fi
else
read -rp " Enable PS2 VMC Groups? (y/n): " answer </dev/tty
if [[ "${answer,,}" == "y" ]]; then
if grep -q '^VMC_GROUPS =' "${OPL}/version.txt"; then
sed -i 's/^VMC_GROUPS =.*/VMC_GROUPS = enabled/' "${OPL}/version.txt"
else
echo 'VMC_GROUPS = enabled' >> "${OPL}/version.txt"
fi
VMC_STATUS="enabled"
echo
echo " [✓] PS2 VMC Groups enabled."
echo " VMC files will be created on the next game installation."
fi
fi

UNMOUNT_OPL

echo
read -n 1 -s -r -p " Press any key to return to the menu..." </dev/tty
echo
}

EXTRAS_SPLASH() {
clear
cat << "EOF"
Expand All @@ -1392,16 +1461,24 @@ EOF
# Function to display the menu
display_menu() {
EXTRAS_SPLASH
cat << "EOF"

if [[ "$VMC_STATUS" == "enabled" ]]; then
vmc_status="[ON] "
else
vmc_status="[OFF]"
fi

cat <<EOF
1) Install PS2 Linux
2) Reassign Cross and Circle Buttons
3) Change Language
4) Change Screen Settings
5) Clear Art & Icon Cache

b) Back to Main Menu

EOF
printf " 6) PS2 VMC Groups for Neutrino %s\n" "$vmc_status"
echo
echo " b) Back to Main Menu"
echo
}

clear
Expand Down Expand Up @@ -1436,6 +1513,12 @@ if ! sudo rm -rf "${STORAGE_DIR}"; then
error_msg "Failed to remove $STORAGE_DIR folder."
fi

# Read VMC_GROUPS from version.txt to show [ON]/[OFF] in the menu without
# needing to mount OPL on every display_menu call.
MOUNT_OPL
VMC_STATUS=$(awk -F' *= *' '$1=="VMC_GROUPS"{print $2}' "${OPL}/version.txt" 2>/dev/null)
UNMOUNT_OPL

# Main loop

while true; do
Expand All @@ -1458,6 +1541,9 @@ while true; do
5)
option_five
;;
6)
option_six
;;
b|B)
break
;;
Expand Down
91 changes: 90 additions & 1 deletion scripts/Game-Installer.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ MISSING_ART="${LOGS_DIR}/missing-art.log"
MISSING_APP_ART="${LOGS_DIR}/missing-app-art.log"
MISSING_ICON="${LOGS_DIR}/missing-icon.log"
MISSING_VMC="${LOGS_DIR}/missing-vmc.log"
PS2_VMC_GROUPS="${HELPER_DIR}/ps2_vmc_groups.list"
GAMES_PATH="${TOOLKIT_PATH}/games"
CONFIG_FILE="${SCRIPTS_DIR}/gamepath.cfg"
STORAGE_DIR="${SCRIPTS_DIR}/storage"
Expand Down Expand Up @@ -549,6 +550,85 @@ CREATE_VMC() {
cp -rf "${POPS_DIR}/${VMC_FOLDER}/"* "${STORAGE_DIR}/__common/POPS"
}

CREATE_PS2_VMC() {
# Only run when Neutrino is selected and VMC_GROUPS is enabled in version.txt
if [[ "$VMC_GROUPS" != "enabled" ]]; then
return 0
fi

if [[ "$LAUNCHER" != "NEUTRINO" ]]; then
return 0
fi

if [[ ! -s "${PS2_LIST}" ]]; then
return 0
fi

echo | tee -a "${LOG_FILE}"
echo -n "Creating PS2 VMC files for Neutrino..." | tee -a "${LOG_FILE}"

# Build hash map: game_id -> XEBP group ID
declare -A ps2_vmc_by_id
local current_group=""

while IFS= read -r line; do
line="${line%%$'\r'}" # strip CR
[[ -z "$line" || "$line" == \#* ]] && continue

if [[ "$line" =~ ^XEBP_ ]]; then
current_group="$line"
elif [[ -n "$current_group" ]]; then
local gid="${line%%|*}"
ps2_vmc_by_id["$gid"]="$current_group"
fi
done < "${PS2_VMC_GROUPS}"

mkdir -p "${GAMES_PATH}/VMC" 2>/dev/null
mkdir -p "${GAMES_PATH}/nhddl" 2>/dev/null

echo | tee -a "${LOG_FILE}"

while IFS='|' read -r title game_id publisher disc_type file_name jpn_title; do
# Determine VMC name: group ID if in a group, otherwise per-title ID
local vmc_name
if [[ -n "${ps2_vmc_by_id[$game_id]}" ]]; then
vmc_name="${ps2_vmc_by_id[$game_id]}"
else
vmc_name="$game_id"
fi

# Create 8MB blank VMC image if it does not already exist
local vmc_file="${GAMES_PATH}/VMC/${vmc_name}.bin"
if [[ ! -f "$vmc_file" ]]; then
echo -n "Creating ${vmc_name}.bin for $game_id..." | tee -a "${LOG_FILE}"
if dd if=/dev/zero of="$vmc_file" bs=1M count=8 >> "${LOG_FILE}" 2>&1; then
echo " [✓]" | tee -a "${LOG_FILE}"
else
echo " [X] Failed to create VMC" | tee -a "${LOG_FILE}"
fi
else
echo "VMC ${vmc_name}.bin already exists for $game_id, skipping." | tee -a "${LOG_FILE}"
fi

# Create or update the NHDDL YAML file for this game.
# NHDDL matches nhddl/<ISO name without extension>.yaml to the ISO at launch.
local yaml_base="${file_name%.*}"
local yaml_file="${GAMES_PATH}/nhddl/${yaml_base}.yaml"
if [[ ! -f "$yaml_file" ]]; then
printf 'mc0: /VMC/%s.bin\n' "$vmc_name" > "$yaml_file"
echo "Created NHDDL YAML: ${yaml_base}.yaml -> mc0: /VMC/${vmc_name}.bin" | tee -a "${LOG_FILE}"
elif ! grep -q '^mc0:' "$yaml_file"; then
printf 'mc0: /VMC/%s.bin\n' "$vmc_name" >> "$yaml_file"
echo "Updated NHDDL YAML: ${yaml_base}.yaml -> mc0: /VMC/${vmc_name}.bin" | tee -a "${LOG_FILE}"
else
echo "NHDDL YAML ${yaml_base}.yaml already has mc0, skipping." | tee -a "${LOG_FILE}"
fi
done < "${PS2_LIST}"

echo | tee -a "${LOG_FILE}"
echo "[✓] PS2 VMC files created." | tee -a "${LOG_FILE}"
}

POPS_SIZE_CKECK() {

if [ "$INSTALL_TYPE" = "sync" ]; then
Expand Down Expand Up @@ -1471,7 +1551,9 @@ fi

APA_SIZE=$(awk -F' *= *' '$1=="APA_SIZE"{print $2}' "${OPL}/version.txt")
LANG=$(awk -F' *= *' '$1=="LANG"{print $2}' "${OPL}/version.txt")
VMC_GROUPS=$(awk -F' *= *' '$1=="VMC_GROUPS"{print $2}' "${OPL}/version.txt")
echo "Language: $LANG" >> "${LOG_FILE}"
echo "VMC_GROUPS: ${VMC_GROUPS:-disabled}" >> "${LOG_FILE}"

if [ -z "$APA_SIZE" ] || [ -z "$LANG" ]; then
error_msg "Error" "Missing required value(s) in ${OPL}/version.txt"
Expand Down Expand Up @@ -2689,6 +2771,11 @@ else
echo "No games to process." | tee -a "${LOG_FILE}"
fi

# Create PS2 VMC images and NHDDL YAML files before syncing to exFAT
if [ -s "${PS2_LIST}" ]; then
CREATE_PS2_VMC
fi

# Copy OPL files
dirs=(
"${GAMES_PATH}/ART"
Expand All @@ -2697,6 +2784,7 @@ dirs=(
"${GAMES_PATH}/LNG"
"${GAMES_PATH}/THM"
"${GAMES_PATH}/VMC"
"${GAMES_PATH}/nhddl"
)

# Flag to track if any files exist
Expand All @@ -2709,9 +2797,10 @@ for dir in "${dirs[@]}"; do
# Create the subdirectory in the destination path using the directory name
folder_name=$(basename "$dir")
dest_dir="${OPL}/$folder_name"
mkdir -p "$dest_dir"

# Copy non-hidden files to the corresponding destination subdirectory
if [ "$folder_name" == "CFG" ] || [ "$folder_name" == "VMC" ]; then
if [ "$folder_name" == "CFG" ] || [ "$folder_name" == "VMC" ] || [ "$folder_name" == "nhddl" ]; then
echo "Copying OPL $folder_name files..." | tee -a "${LOG_FILE}"
find "$dir" -type f ! -name '.*' -exec cp --update=none {} "$dest_dir" \; >> "${LOG_FILE}" 2>&1
else
Expand Down
Loading