Skip to content

Commit 9dd3945

Browse files
committed
.m3u playlist support
Initial stab - just parses the list and loads the first disk. Compiles, but I'm doing it in a docker image with no video, so I need to move it over to a real computer to test.
1 parent c648313 commit 9dd3945

File tree

3 files changed

+58
-6
lines changed

3 files changed

+58
-6
lines changed

pcsx2-qt/MainWindow.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
#endif
6464

6565
const char* MainWindow::OPEN_FILE_FILTER =
66-
QT_TRANSLATE_NOOP("MainWindow", "All File Types (*.bin *.iso *.cue *.mdf *.chd *.cso *.zso *.gz *.elf *.irx *.gs *.gs.xz *.gs.zst *.dump);;"
66+
QT_TRANSLATE_NOOP("MainWindow", "All File Types (*.bin *.iso *.cue *.mdf *.chd *.cso *.zso *.gz *.elf *.irx *.gs *.gs.xz *.gs.zst *.dump *.m3u);;"
6767
"Single-Track Raw Images (*.bin *.iso);;"
6868
"Cue Sheets (*.cue);;"
6969
"Media Descriptor File (*.mdf);;"
@@ -74,17 +74,19 @@ const char* MainWindow::OPEN_FILE_FILTER =
7474
"ELF Executables (*.elf);;"
7575
"IRX Executables (*.irx);;"
7676
"GS Dumps (*.gs *.gs.xz *.gs.zst);;"
77-
"Block Dumps (*.dump)");
77+
"Block Dumps (*.dump);;"
78+
"M3U Playlists (*.m3u)");
7879

79-
const char* MainWindow::DISC_IMAGE_FILTER = QT_TRANSLATE_NOOP("MainWindow", "All File Types (*.bin *.iso *.cue *.mdf *.chd *.cso *.zso *.gz *.dump);;"
80+
const char* MainWindow::DISC_IMAGE_FILTER = QT_TRANSLATE_NOOP("MainWindow", "All File Types (*.bin *.iso *.cue *.mdf *.chd *.cso *.zso *.gz *.dump *.m3u);;"
8081
"Single-Track Raw Images (*.bin *.iso);;"
8182
"Cue Sheets (*.cue);;"
8283
"Media Descriptor File (*.mdf);;"
8384
"MAME CHD Images (*.chd);;"
8485
"CSO Images (*.cso);;"
8586
"ZSO Images (*.zso);;"
8687
"GZ Images (*.gz);;"
87-
"Block Dumps (*.dump)");
88+
"Block Dumps (*.dump);;"
89+
"M3U Playlists (*.m3u)");
8890

8991
MainWindow* g_main_window = nullptr;
9092

pcsx2/ImGui/FullscreenUI.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -746,7 +746,7 @@ ImGuiFullscreen::FileSelectorFilters FullscreenUI::GetOpenFileFilters()
746746

747747
ImGuiFullscreen::FileSelectorFilters FullscreenUI::GetDiscImageFilters()
748748
{
749-
return {"*.bin", "*.iso", "*.cue", "*.mdf", "*.chd", "*.cso", "*.zso", "*.gz"};
749+
return {"*.bin", "*.iso", "*.cue", "*.mdf", "*.chd", "*.cso", "*.zso", "*.gz", "*.m3u"};
750750
}
751751

752752
ImGuiFullscreen::FileSelectorFilters FullscreenUI::GetAudioFileFilters()

pcsx2/VMManager.cpp

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include "common/Console.h"
4747
#include "common/Error.h"
4848
#include "common/FileSystem.h"
49+
#include "common/Path.h"
4950
#include "common/FPControl.h"
5051
#include "common/ScopedGuard.h"
5152
#include "common/SettingsWrapper.h"
@@ -1235,6 +1236,41 @@ bool VMManager::HasBootedELF()
12351236
return s_current_crc != 0 && s_elf_executed;
12361237
}
12371238

1239+
static std::vector<std::string> ParseM3UPlaylist(const std::string& m3u_path)
1240+
{
1241+
std::vector<std::string> disc_paths;
1242+
1243+
const std::optional<std::string> content = FileSystem::ReadFileToString(m3u_path.c_str());
1244+
if (!content.has_value())
1245+
return disc_paths;
1246+
1247+
const std::string_view m3u_dir = Path::GetDirectory(m3u_path);
1248+
1249+
const std::vector<std::string_view> lines = StringUtil::SplitString(*content, '\n', false);
1250+
for (const std::string_view line : lines)
1251+
{
1252+
// Skip empty lines and comments
1253+
const std::string_view trimmed = StringUtil::StripWhitespace(line);
1254+
if (trimmed.empty() || trimmed[0] == '#')
1255+
continue;
1256+
1257+
// Resolve relative paths
1258+
std::string disc_path;
1259+
if (Path::IsAbsolute(trimmed))
1260+
{
1261+
disc_path = std::string(trimmed);
1262+
}
1263+
else
1264+
{
1265+
disc_path = Path::Combine(m3u_dir, trimmed);
1266+
}
1267+
1268+
disc_paths.push_back(std::move(disc_path));
1269+
}
1270+
1271+
return disc_paths;
1272+
}
1273+
12381274
bool VMManager::AutoDetectSource(const std::string& filename, Error* error)
12391275
{
12401276
if (!filename.empty())
@@ -1268,6 +1304,20 @@ bool VMManager::AutoDetectSource(const std::string& filename, Error* error)
12681304
s_elf_override = filename;
12691305
return true;
12701306
}
1307+
else if (StringUtil::EndsWithNoCase(filename, ".m3u"))
1308+
{
1309+
const std::vector<std::string> disc_paths = ParseM3UPlaylist(filename);
1310+
if (disc_paths.empty())
1311+
{
1312+
Error::SetStringFmt(error, TRANSLATE_FS("VMManager", "M3U playlist '{}' does not contain any valid disc paths."), filename);
1313+
return false;
1314+
}
1315+
1316+
// For now, just load the first disc
1317+
CDVDsys_SetFile(CDVD_SourceType::Iso, disc_paths[0]);
1318+
CDVDsys_ChangeSource(CDVD_SourceType::Iso);
1319+
return true;
1320+
}
12711321
else
12721322
{
12731323
// TODO: Maybe we should check if it's a valid iso here...
@@ -2424,7 +2474,7 @@ bool VMManager::IsSaveStateFileName(const std::string_view path)
24242474

24252475
bool VMManager::IsDiscFileName(const std::string_view path)
24262476
{
2427-
static const char* extensions[] = {".iso", ".bin", ".img", ".mdf", ".gz", ".cso", ".zso", ".chd"};
2477+
static const char* extensions[] = {".iso", ".bin", ".img", ".mdf", ".gz", ".cso", ".zso", ".chd", ".m3u"};
24282478

24292479
for (const char* test_extension : extensions)
24302480
{

0 commit comments

Comments
 (0)