Skip to content

Commit

Permalink
FEAT: multidisc playlist (.m3u) generator (#1301)
Browse files Browse the repository at this point in the history
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`
...
  • Loading branch information
schmurtzm authored Dec 11, 2023
1 parent bdb0853 commit 428f2f2
Show file tree
Hide file tree
Showing 7 changed files with 254 additions and 8 deletions.
2 changes: 2 additions & 0 deletions src/tweaks/appstate.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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();
Expand Down
27 changes: 27 additions & 0 deletions src/tweaks/menus.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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",
Expand Down
47 changes: 44 additions & 3 deletions src/tweaks/tools.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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};

Expand Down
4 changes: 3 additions & 1 deletion src/tweaks/tools_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"};

Expand Down
11 changes: 8 additions & 3 deletions static/build/.tmp_update/script/cue_gen.sh
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 {} \;
169 changes: 169 additions & 0 deletions static/build/.tmp_update/script/m3u_gen.sh
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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 = ""
Expand Down

0 comments on commit 428f2f2

Please sign in to comment.