From 428f2f27ce24c57a435b62d2a7d37812d071dfe9 Mon Sep 17 00:00:00 2001 From: Schmurtz Date: Mon, 11 Dec 2023 22:30:35 +0100 Subject: [PATCH] FEAT: multidisc playlist (.m3u) generator (#1301) Create playlists automatically from multidisk roms New script m3u_gen.sh (easy to adapt to GLO if necessary) Available from `Tweaks` -> `Tools` section. Additional folders added to cue generator. By default the script is systematically applied to `PS, SEGACD, NEOCD, PCE, PCFX, AMIGA` rom folders. The supported multidisc file names are numerous (not key sensitive and support "disc" or "disk"), for example : `MyGame (Disc 1).chd` or `MyGame (disc 1).chd` or `MyGame (DISC 1).chd` or `MyGame (Disk 1).chd` `MyGame (USA) (Disc 1).bin` + `MyGame (USA) (Disc 1).cue` `MyGame (USA) (Disc 1) (Rev 1).chd` `MyGame (Disc 1 of .2).chd` ... --- src/tweaks/appstate.h | 2 + src/tweaks/menus.h | 27 +++ src/tweaks/tools.h | 47 ++++- src/tweaks/tools_defs.h | 4 +- static/build/.tmp_update/script/cue_gen.sh | 11 +- static/build/.tmp_update/script/m3u_gen.sh | 169 ++++++++++++++++++ .../cores/mednafen_pcfx_libretro.info | 2 +- 7 files changed, 254 insertions(+), 8 deletions(-) create mode 100644 static/build/.tmp_update/script/m3u_gen.sh diff --git a/src/tweaks/appstate.h b/src/tweaks/appstate.h index cfab203dda..853b0c8dea 100644 --- a/src/tweaks/appstate.h +++ b/src/tweaks/appstate.h @@ -67,6 +67,7 @@ static List _menu_battery_percentage; static List _menu_advanced; static List _menu_reset_settings; static List _menu_tools; +static List _menu_tools_m3uGenerator; static List _menu_diagnostics; void menu_free_all(void) @@ -85,6 +86,7 @@ void menu_free_all(void) list_free(&_menu_advanced); list_free(&_menu_reset_settings); list_free(&_menu_tools); + list_free(&_menu_tools_m3uGenerator); list_free(&_menu_diagnostics); menu_icons_free_all(); diff --git a/src/tweaks/menus.h b/src/tweaks/menus.h index 6fce5cf7cd..4966829081 100644 --- a/src/tweaks/menus.h +++ b/src/tweaks/menus.h @@ -662,6 +662,25 @@ void menu_advanced(void *_) header_changed = true; } +void menu_tools_m3uGenerator(void *_) +{ + if (!_menu_tools_m3uGenerator._created) { + _menu_tools_m3uGenerator = list_createWithTitle(2, LIST_SMALL, "m3u Generator"); + list_addItemWithInfoNote(&_menu_tools_m3uGenerator, + (ListItem){ + .label = "Multiple directories (.Game_Name)", + .action = tool_generateM3uFiles_md}, + "One directory for each game \".Game_Name\""); + list_addItemWithInfoNote(&_menu_tools_m3uGenerator, + (ListItem){ + .label = "Single directory (.multi-disc)", + .action = tool_generateM3uFiles_sd}, + "One single directory \".multi-disc\"\nwill contains all multi-disc files"); + } + menu_stack[++menu_level] = &_menu_tools_m3uGenerator; + header_changed = true; +} + void menu_tools(void *_) { if (!_menu_tools._created) { @@ -674,6 +693,14 @@ void menu_tools(void *_) "PSX roms in '.bin' format needs a\n" "matching '.cue' file. Use this tool\n" "to automatically generate them."); + list_addItemWithInfoNote(&_menu_tools, + (ListItem){ + .label = "Generate M3U files for PSX games...", + .action = menu_tools_m3uGenerator}, + "PSX multidisc roms require to create\n" + "a playslist file (.m3u). It allows to \n" + "have only one entry for each multidisc\n" + "game and one unique save file for each game"); list_addItemWithInfoNote(&_menu_tools, (ListItem){ .label = "Generate game list for short name roms", diff --git a/src/tweaks/tools.h b/src/tweaks/tools.h index a782be3131..98b67e5be1 100644 --- a/src/tweaks/tools.h +++ b/src/tweaks/tools.h @@ -84,7 +84,7 @@ void _runCommandPopup(const char *tool_name, const char *_cmd) _toolDialog(full_title, thread_success ? "Done" : "Tool failed", false); if (video != NULL) - msleep(300); + msleep(1200); SDL_FreeSurface(_tool_bg_cache); _tool_bg_cache = NULL; @@ -93,23 +93,64 @@ void _runCommandPopup(const char *tool_name, const char *_cmd) all_changed = true; } +void _displayM3uTotal() +{ + FILE *file = fopen("/tmp/count_m3u", "r"); + if (file == NULL) { + perror("Error opening file"); + return; + } + int value; + if (fscanf(file, "%d", &value) != 1) { + perror("Error reading from file"); + fclose(file); + return; + } + fclose(file); + + if (remove("/tmp/count_m3u") != 0) { + perror("Error deleting file"); + } + + char message[28]; + snprintf(message, sizeof(message), "%d playlist files created.", value); + + _toolDialog("M3u Generator", message, false); + if (video != NULL) + msleep(1200); +} + void tool_generateCueFiles(void *pt) { _runCommandPopup(tools_short_names[0], "/mnt/SDCARD/.tmp_update/script/cue_gen.sh"); } +void tool_generateM3uFiles_sd(void *pt) +{ + _runCommandPopup(tools_short_names[1], "/mnt/SDCARD/.tmp_update/script/m3u_gen.sh -sd"); + _displayM3uTotal(); +} + +void tool_generateM3uFiles_md(void *pt) +{ + _runCommandPopup(tools_short_names[2], "/mnt/SDCARD/.tmp_update/script/m3u_gen.sh -md"); + _displayM3uTotal(); +} + void tool_buildShortRomGameList(void *pt) { - _runCommandPopup(tools_short_names[1], "./bin/gameNameList /mnt/SDCARD /mnt/SDCARD/BIOS/arcade_lists"); + _runCommandPopup(tools_short_names[3], "./bin/gameNameList /mnt/SDCARD /mnt/SDCARD/BIOS/arcade_lists"); } void tool_generateMiyoogamelists(void *pt) { - _runCommandPopup(tools_short_names[2], "/mnt/SDCARD/.tmp_update/script/miyoogamelist_gen.sh"); + _runCommandPopup(tools_short_names[4], "/mnt/SDCARD/.tmp_update/script/miyoogamelist_gen.sh"); } static void (*tools_pt[NUM_TOOLS])(void *) = { tool_generateCueFiles, + tool_generateM3uFiles_sd, + tool_generateM3uFiles_md, tool_buildShortRomGameList, tool_generateMiyoogamelists}; diff --git a/src/tweaks/tools_defs.h b/src/tweaks/tools_defs.h index 9a306995c8..81a20aa248 100644 --- a/src/tweaks/tools_defs.h +++ b/src/tweaks/tools_defs.h @@ -3,10 +3,12 @@ #include "utils/str.h" -#define NUM_TOOLS 3 +#define NUM_TOOLS 5 static char tools_short_names[NUM_TOOLS][STR_MAX] = { "cue_gen", + "m3u_gen_sd", + "m3u_gen_md", "build_short_rom_game_list", "miyoogamelist_gen"}; diff --git a/static/build/.tmp_update/script/cue_gen.sh b/static/build/.tmp_update/script/cue_gen.sh index 432344c7f6..91d4b78c7e 100755 --- a/static/build/.tmp_update/script/cue_gen.sh +++ b/static/build/.tmp_update/script/cue_gen.sh @@ -1,10 +1,15 @@ #!/bin/sh rootdir="/mnt/SDCARD/Roms" -targets=PS SEGACD NEOCD + +if [ $# -gt 0 ]; then + targets="$1" +else + targets="PS SEGACD NEOCD PCE PCFX AMIGA" +fi cd "$rootdir" -find $targets -name *.bin -type f | ( +find $targets -maxdepth 3 -name *.bin -type f | ( count=0 while read target ; do @@ -31,4 +36,4 @@ find $targets -name *.bin -type f | ( echo "$count cue $([ $count -eq 1 ] && (echo "file") || (echo "files")) created" ) -find $targets -type f -name "*_cache6.db" -exec rm -f {} \; +find $targets -maxdepth 1 -type f -name "*_cache6.db" -exec rm -f {} \; diff --git a/static/build/.tmp_update/script/m3u_gen.sh b/static/build/.tmp_update/script/m3u_gen.sh new file mode 100644 index 0000000000..ee95bd6a2c --- /dev/null +++ b/static/build/.tmp_update/script/m3u_gen.sh @@ -0,0 +1,169 @@ +# Credits: gotbletu (@gmail|twitter|youtube|github|lbry) +# adapted by Onion Team (https://github.com/OnionUI/Onion) + +count_m3u="/tmp/count_m3u" + +# target files extension +EXT_INT="cue|gdi|chd|pbp|iso|dsk" + +helpmsg() { + echo "desc: create m3u playlist for multi disc games [$EXT_INT]" + echo " e.g Playstation, Sega CD/Mega CD, NeoGeo CD," + echo " PC Engine CD/PC-FX, Amiga" + echo "" + echo "usage: ${0##*/} [options] [folder name]" + echo "" + echo "options:" + echo " -md create multiple directories : one directory and one m3u playlist for each game" + echo " from" + echo " Novastorm (USA) (Disc 1).bin" + echo " Novastorm (USA) (Disc 1).cue" + echo " Novastorm (USA) (Disc 2).bin" + echo " Novastorm (USA) (Disc 2).cue" + echo " Metal Gear Solid (USA) (Disc 1) (Rev 1).chd" + echo " Metal Gear Solid (USA) (Disc 2) (Rev 1).chd" + echo " to" + echo " Novastorm (USA).m3u" + echo " Metal Gear Solid (USA) (Rev 1).m3u" + echo " /.Novastorm (USA)/Novastorm (USA) (Disc 1).bin" + echo " /.Novastorm (USA)/Novastorm (USA) (Disc 1).cue" + echo " /.Novastorm (USA)/Novastorm (USA) (Disc 2).bin" + echo " /.Novastorm (USA)/Novastorm (USA) (Disc 2).cue" + echo " /.Metal Gear Solid (USA) (Rev 1)/Metal Gear Solid (USA) (Disc 1) (Rev 1).chd" + echo " /.Metal Gear Solid (USA) (Rev 1)/Metal Gear Solid (USA) (Disc 2) (Rev 1).chd" + echo "" + echo " -sd create a single \"multi-disc\" directory for all multidisk files" + echo " from" + echo " Heart of Darkness (USA) (Disc 1).bin" + echo " Heart of Darkness (USA) (Disc 1).cue" + echo " Heart of Darkness (USA) (Disc 2).bin" + echo " Heart of Darkness (USA) (Disc 2).cue" + echo " Lunar 2 - Eternal Blue Complete (USA) (Disc 1).chd" + echo " Lunar 2 - Eternal Blue Complete (USA) (Disc 2).chd" + echo " Lunar 2 - Eternal Blue Complete (USA) (Disc 3).chd" + echo " to" + echo " Heart of Darkness (USA).m3u" + echo " Lunar 2 - Eternal Blue Complete (USA).m3u" + echo " .multi-disc/Heart of Darkness (USA) (Disc 1).bin" + echo " .multi-disc/Heart of Darkness (USA) (Disc 1).cue" + echo " .multi-disc/Heart of Darkness (USA) (Disc 2).bin" + echo " .multi-disc/Heart of Darkness (USA) (Disc 2).cue" + echo " .multi-disc/Lunar 2 - Eternal Blue Complete (USA) (Disc 1).chd" + echo " .multi-disc/Lunar 2 - Eternal Blue Complete (USA) (Disc 2).chd" + echo " .multi-disc/Lunar 2 - Eternal Blue Complete (USA) (Disc 3).chd" + echo "" + echo " -h, --help display this help message" + echo "" + echo "Swap Disc: Retroarch > [Load Your Game] > Quick Menu > Disc Control" +} + +if [ "$1" = -h ] || [ "$1" = --help ]; then + helpmsg + exit 0 +elif [ "$1" = -md ]; then + TARGETFOLDER="-md" +elif [ "$1" = -sd ]; then + TARGETFOLDER="-sd" +else # case for eventual GLO menu + LD_PRELOAD=/mnt/SDCARD/miyoo/lib/libpadsp.so prompt -t "Multi-disc - Target Directory" -m "Choose the sub folder name where\noriginal disc files will be stored." \ + "Single directory (\".multi-disc\")" \ + "Multiple directories (\".Game_Name\")" + retcode=$? + + if [ $retcode -eq 0 ]; then + TARGETFOLDER="-sd" + elif [ $retcode -eq 1 ]; then + TARGETFOLDER="-md" + elif [ $retcode -eq 255 ]; then + exit + fi +fi + +echo 0 >"$count_m3u" +# create missing cue files. +"/mnt/SDCARD/.tmp_update/script/cue_gen.sh" + +if [ "$TARGETFOLDER" = -sd ]; then + DIR_NAME=".multi-disc" +fi + +DIR_LIST="PS SEGACD NEOCD PCE PCFX AMIGA" +for dir in $DIR_LIST; do + full_path="/mnt/SDCARD/Roms/$dir" + echo "============================= $full_path =============================" + if [ -d "$full_path" ]; then + cd "$full_path" + ### TYPE 1: TitleOfGame (USA) (Disc 1).chd + #--------------------------------------------------------------------------------- + find . -maxdepth 1 ! -iname '*.m3u' -type f -iname "*([Dd][Ii][Ss][KkCc] 1).*[$EXT_INT]" | while read line; do + # searching for a "Disc 1" file. + FILE_NAME="$(echo "${line%.*}" | sed 's@./@@g' | sed 's@ ([Dd][Ii][Ss][KkCc] 1)@@g')" + if [ "$TARGETFOLDER" = -md ]; then + DIR_NAME=".$(echo "${line%.*}" | sed 's@./@@g' | sed 's@ ([Dd][Ii][Ss][KkCc] 1)@@g')" + DIR_NAME="${DIR_NAME%"${DIR_NAME##*[![:space:]]}"}" # remove spaces at the end + fi + SEARCH_NAME="$(echo "${line%.*}" | sed 's@./@@g' | sed 's@([Dd][Ii][Ss][KkCc] 1)@([Dd][Ii][Ss][KkCc] ?)@g')" + mkdir -p "$DIR_NAME" + echo -e "****************\nType 1: ${line}\n**** SEARCH_NAME : $SEARCH_NAME \n**** DIR_NAME : $DIR_NAME\n**** FILE_NAME : $FILE_NAME" + count=$(cat "$count_m3u") + count=$((count + 1)) + echo "$count" >"$count_m3u" + sync + # move matching files to directory + find . -maxdepth 1 ! -iname '*.m3u' -type f -iname "$SEARCH_NAME.*" -exec mv -n -- '{}' "$DIR_NAME" \; + # Create m3u playslist file + find "$DIR_NAME" ! -iname '*.m3u' -type f -iname "$SEARCH_NAME*.*[$EXT_INT]" | sed -e 's/^//' | sort >"$FILE_NAME".m3u + done + + ### TYPE 2: AnotherTitleOfGame (USA) (Disc 1) (Rev 2).chd + #--------------------------------------------------------------------------------- + find . -maxdepth 1 ! -iname '*.m3u' -type f -iname "*([Dd][Ii][Ss][KkCc] 1) *.*[$EXT_INT]" | while read line; do + # searching for a "Disc 1" file. + FILE_NAME="$(echo "${line%.*}" | sed 's@./@@g' | sed 's@ ([Dd][Ii][Ss][KkCc] 1)@@g')" + if [ "$TARGETFOLDER" = -md ]; then + DIR_NAME=".$(echo "${line%.*}" | sed 's@./@@g' | sed 's@ ([Dd][Ii][Ss][KkCc] 1)@@g')" + DIR_NAME="${DIR_NAME%"${DIR_NAME##*[![:space:]]}"}" # remove spaces at the end + fi + SEARCH_NAME="$(echo "${line%.*}" | sed 's@./@@g' | sed 's@([Dd][Ii][Ss][KkCc] 1)@([Dd][Ii][Ss][KkCc] ?)@g')" + echo -e "****************\nType 2: ${line}\n**** SEARCH_NAME : $SEARCH_NAME \n**** DIR_NAME : $DIR_NAME\n**** FILE_NAME : $FILE_NAME" + count=$(cat "$count_m3u") + count=$((count + 1)) + echo "$count" >"$count_m3u" + sync + mkdir -p "$DIR_NAME" + # move matching files to directory + find . -maxdepth 1 ! -iname '*.m3u' -type f -iname "$SEARCH_NAME.*" -exec mv -n -- '{}' "$DIR_NAME" \; + # Create m3u playslist file + find "$DIR_NAME" ! -iname '*.m3u' -type f -iname "$SEARCH_NAME*.*[$EXT_INT]" | sed -e 's/^//' | sort >"$FILE_NAME".m3u + done + + ### TYPE 3: AmstradMSXTitleOfGame (19xx)(Developer)(Disc 1 of 3).dsk + #--------------------------------------------------------------------------------- + find . -maxdepth 1 ! -iname '*.m3u' -type f -iname "*([Dd][Ii][Ss][KkCc] 1 of ?).*[$EXT_INT]" | while read line; do + # searching for a "Disc 1" file. + FILE_NAME="$(echo "${line%.*}" | sed 's@./@@g' | sed 's@([Dd][Ii][Ss][KkCc] 1 of .*)@@g')" + if [ "$TARGETFOLDER" = -md ]; then + DIR_NAME=".$(echo "${line%.*}" | sed 's@./@@g' | sed 's@([Dd][Ii][Ss][KkCc] 1 of .*)@@g')" + DIR_NAME="${DIR_NAME%"${DIR_NAME##*[![:space:]]}"}" # remove spaces at the end + fi + SEARCH_NAME="$(echo "${line%.*}" | sed 's@./@@g' | sed 's@([Dd][Ii][Ss][KkCc] 1 of@([Dd][Ii][Ss][KkCc] ? of@g')" + # SEARCH_NAME="$(echo "${line%.*}" | sed 's@./@@g' | sed 's@([Dd][Ii][Ss][KkCc] 1)@([Dd][Ii][Ss][KkCc] ?)@g')" + echo -e "****************\nType 3: ${line}\n**** SEARCH_NAME : $SEARCH_NAME \n**** DIR_NAME : $DIR_NAME\n**** FILE_NAME : $FILE_NAME" + count=$(cat "$count_m3u") + count=$((count + 1)) + echo "$count" >"$count_m3u" + sync + mkdir -p "$DIR_NAME" + # move matching files to directory + find . -maxdepth 1 ! -iname '*.m3u' -type f -iname "$SEARCH_NAME*.*" -exec mv -n -- '{}' "$DIR_NAME" \; + # Create m3u playslist file + find "$DIR_NAME" ! -iname '*.m3u' -type f -iname "$SEARCH_NAME*.*[$EXT_INT]" | sed -e 's/^//' | sort >"$FILE_NAME".m3u + done + else + echo "Directory $full_path does not exist." + fi +done + +if [ "$#" -eq 0 ]; then # when launched by GLO menu, without args. + infoPanel --title "Multi-disc Creator" --message "$(cat "$count_m3u") playlist files created." --auto +fi diff --git a/static/build/RetroArch/.retroarch/cores/mednafen_pcfx_libretro.info b/static/build/RetroArch/.retroarch/cores/mednafen_pcfx_libretro.info index 3b8f3cbd6d..9967391c9b 100644 --- a/static/build/RetroArch/.retroarch/cores/mednafen_pcfx_libretro.info +++ b/static/build/RetroArch/.retroarch/cores/mednafen_pcfx_libretro.info @@ -1,7 +1,7 @@ # Software Information display_name = "NEC - PC-FX (Beetle PC-FX)" authors = "Mednafen Team" -supported_extensions = "cue|ccd|toc|chd" +supported_extensions = "cue|ccd|toc|chd|m3u" corename = "Beetle PC-FX" license = "GPLv2" permissions = ""