diff --git a/src/core/fullscreenui_settings.cpp b/src/core/fullscreenui_settings.cpp index b1590d98d..5417486ec 100644 --- a/src/core/fullscreenui_settings.cpp +++ b/src/core/fullscreenui_settings.cpp @@ -4374,6 +4374,13 @@ void FullscreenUI::DrawGraphicsSettingsPage() DrawIntRangeSetting(bsi, FSUI_ICONVSTR(ICON_FA_CAMERA_RETRO, "Screenshot Quality"), FSUI_VSTR("Selects the quality at which screenshots will be compressed."), "Display", "ScreenshotQuality", Settings::DEFAULT_DISPLAY_SCREENSHOT_QUALITY, 1, 100, "%d%%"); + DrawEnumSetting(bsi, FSUI_ICONVSTR(ICON_FA_FILE, "Screenshot File Name Format"), + FSUI_VSTR("Determines the format of the screenshot file names."), "Display", + "ScreenshotFileNameFormat", Settings::DEFAULT_DISPLAY_SCREENSHOT_FILENAME_FORMAT, + &Settings::ParseCaptureFileNameFormat, &Settings::GetCaptureFileNameFormatName, + &Settings::GetCaptureFileNameFormatDisplayName, CaptureFileNameFormat::Count); + DrawFolderSetting(bsi, FSUI_ICONVSTR(ICON_FA_FOLDER, "Screenshot Directory"), "Folders", "Screenshots", + EmuFolders::Screenshots); MenuHeading(FSUI_VSTR("Texture Replacements")); diff --git a/src/core/fullscreenui_strings.h b/src/core/fullscreenui_strings.h index 5ba30b6f0..9054dd1de 100644 --- a/src/core/fullscreenui_strings.h +++ b/src/core/fullscreenui_strings.h @@ -265,6 +265,7 @@ TRANSLATE_NOOP("FullscreenUI", "Determines that field that the game list will be TRANSLATE_NOOP("FullscreenUI", "Determines the amount of audio buffered before being pulled by the host API."); TRANSLATE_NOOP("FullscreenUI", "Determines the area of the overlay image that the display will be drawn within."); TRANSLATE_NOOP("FullscreenUI", "Determines the emulated hardware type."); +TRANSLATE_NOOP("FullscreenUI", "Determines the format of the screenshot file names."); TRANSLATE_NOOP("FullscreenUI", "Determines the format that screenshots will be saved/compressed with."); TRANSLATE_NOOP("FullscreenUI", "Determines the frequency at which the macro will toggle the buttons on and off (aka auto fire)."); TRANSLATE_NOOP("FullscreenUI", "Determines the margin between the edge of the screen and on-screen messages."); @@ -675,6 +676,8 @@ TRANSLATE_NOOP("FullscreenUI", "Scanning Subdirectories"); TRANSLATE_NOOP("FullscreenUI", "Screen Margins"); TRANSLATE_NOOP("FullscreenUI", "Screen Position"); TRANSLATE_NOOP("FullscreenUI", "Screen Rotation"); +TRANSLATE_NOOP("FullscreenUI", "Screenshot Directory"); +TRANSLATE_NOOP("FullscreenUI", "Screenshot File Name Format"); TRANSLATE_NOOP("FullscreenUI", "Screenshot Format"); TRANSLATE_NOOP("FullscreenUI", "Screenshot Quality"); TRANSLATE_NOOP("FullscreenUI", "Screenshot Size"); diff --git a/src/core/settings.cpp b/src/core/settings.cpp index da44b346c..d1cea7830 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -403,6 +403,11 @@ void Settings::Load(const SettingsInterface& si, const SettingsInterface& contro si.GetStringViewValue("Display", "ScreenshotFormat", GetDisplayScreenshotFormatName(DEFAULT_DISPLAY_SCREENSHOT_FORMAT))) .value_or(DEFAULT_DISPLAY_SCREENSHOT_FORMAT); + display_screenshot_filename_format = + ParseCaptureFileNameFormat( + si.GetStringViewValue("Display", "ScreenshotFileNameFormat", + GetCaptureFileNameFormatName(DEFAULT_DISPLAY_SCREENSHOT_FILENAME_FORMAT))) + .value_or(DEFAULT_DISPLAY_SCREENSHOT_FILENAME_FORMAT); display_screenshot_quality = static_cast( std::clamp(si.GetUIntValue("Display", "ScreenshotQuality", DEFAULT_DISPLAY_SCREENSHOT_QUALITY), 1, 100)); display_optimal_frame_pacing = si.GetBoolValue("Display", "OptimalFramePacing", DEFAULT_OPTIMAL_FRAME_PACING); @@ -772,6 +777,8 @@ void Settings::Save(SettingsInterface& si, bool ignore_base) const GetDisplayExclusiveFullscreenControlName(display_exclusive_fullscreen_control)); si.SetStringValue("Display", "ScreenshotMode", GetDisplayScreenshotModeName(display_screenshot_mode)); si.SetStringValue("Display", "ScreenshotFormat", GetDisplayScreenshotFormatName(display_screenshot_format)); + si.SetStringValue("Display", "ScreenshotFileNameFormat", + GetCaptureFileNameFormatName(display_screenshot_filename_format)); si.SetUIntValue("Display", "ScreenshotQuality", display_screenshot_quality); if (!ignore_base) { @@ -2358,6 +2365,46 @@ std::optional Settings::GetDisplayScreenshotFormatFromF return std::nullopt; } +static constexpr const std::array s_capture_file_name_format_names = { + "Timestamp", + "TitleAndTimestamp", + "TimestampInFolder", + "TitleAndTimestampInFolder", +}; +static_assert(s_capture_file_name_format_names.size() == static_cast(CaptureFileNameFormat::Count)); +static constexpr const std::array s_capture_file_name_format_display_names = { + TRANSLATE_DISAMBIG_NOOP("Settings", "Timestamp", "CaptureFileNameFormat"), + TRANSLATE_DISAMBIG_NOOP("Settings", "Game and Timestamp", "CaptureFileNameFormat"), + TRANSLATE_DISAMBIG_NOOP("Settings", "Timestamp in Game Folder", "CaptureFileNameFormat"), + TRANSLATE_DISAMBIG_NOOP("Settings", "Game and Timestamp in Game Folder", "CaptureFileNameFormat"), +}; +static_assert(s_capture_file_name_format_names.size() == static_cast(CaptureFileNameFormat::Count)); + +std::optional Settings::ParseCaptureFileNameFormat(std::string_view str) +{ + int index = 0; + for (const char* name : s_capture_file_name_format_names) + { + if (str == name) + return static_cast(index); + + index++; + } + + return std::nullopt; +} + +const char* Settings::GetCaptureFileNameFormatName(CaptureFileNameFormat format) +{ + return s_capture_file_name_format_names[static_cast(format)]; +} + +const char* Settings::GetCaptureFileNameFormatDisplayName(CaptureFileNameFormat format) +{ + return Host::TranslateToCString("Settings", s_capture_file_name_format_display_names[static_cast(format)], + "CaptureFileNameFormat"); +} + static constexpr const std::array s_display_osd_message_type_names = { "Error", "Warning", "Info", "Quick", "Persistent", }; diff --git a/src/core/settings.h b/src/core/settings.h index 77c4bec66..740e58281 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -51,8 +51,6 @@ struct GPUSettings DisplayScalingMode display_scaling_24bit = DEFAULT_DISPLAY_SCALING; DisplayExclusiveFullscreenControl display_exclusive_fullscreen_control = DEFAULT_DISPLAY_EXCLUSIVE_FULLSCREEN_CONTROL; DisplayScreenshotMode display_screenshot_mode = DEFAULT_DISPLAY_SCREENSHOT_MODE; - DisplayScreenshotFormat display_screenshot_format = DEFAULT_DISPLAY_SCREENSHOT_FORMAT; - u8 display_screenshot_quality = DEFAULT_DISPLAY_SCREENSHOT_QUALITY; u8 gpu_max_queued_frames = DEFAULT_GPU_MAX_QUEUED_FRAMES; s16 display_active_start_offset = 0; s16 display_active_end_offset = 0; @@ -241,8 +239,6 @@ struct GPUSettings static constexpr DisplayExclusiveFullscreenControl DEFAULT_DISPLAY_EXCLUSIVE_FULLSCREEN_CONTROL = DisplayExclusiveFullscreenControl::Automatic; static constexpr DisplayScreenshotMode DEFAULT_DISPLAY_SCREENSHOT_MODE = DisplayScreenshotMode::ScreenResolution; - static constexpr DisplayScreenshotFormat DEFAULT_DISPLAY_SCREENSHOT_FORMAT = DisplayScreenshotFormat::PNG; - static constexpr u8 DEFAULT_DISPLAY_SCREENSHOT_QUALITY = 85; static constexpr float DEFAULT_DISPLAY_PRE_FRAME_SLEEP_BUFFER = 2.0f; static constexpr float DEFAULT_OSD_SCALE = 100.0f; static constexpr NotificationLocation DEFAULT_OSD_MESSAGE_LOCATION = NotificationLocation::TopLeft; @@ -342,6 +338,10 @@ struct Settings : public GPUSettings u32 cdrom_max_seek_speedup_cycles = DEFAULT_CDROM_MAX_SEEK_SPEEDUP_CYCLES; u32 cdrom_max_read_speedup_cycles = DEFAULT_CDROM_MAX_READ_SPEEDUP_CYCLES; + DisplayScreenshotFormat display_screenshot_format = DEFAULT_DISPLAY_SCREENSHOT_FORMAT; + u8 display_screenshot_quality = DEFAULT_DISPLAY_SCREENSHOT_QUALITY; + CaptureFileNameFormat display_screenshot_filename_format = DEFAULT_DISPLAY_SCREENSHOT_FILENAME_FORMAT; + u8 audio_output_volume = 100; u8 audio_fast_forward_volume = 100; @@ -585,6 +585,10 @@ struct Settings : public GPUSettings static const char* GetDisplayScreenshotFormatExtension(DisplayScreenshotFormat mode); static std::optional GetDisplayScreenshotFormatFromFileName(const std::string_view filename); + static std::optional ParseCaptureFileNameFormat(std::string_view str); + static const char* GetCaptureFileNameFormatName(CaptureFileNameFormat format); + static const char* GetCaptureFileNameFormatDisplayName(CaptureFileNameFormat format); + static const char* GetDisplayOSDMessageTypeName(OSDMessageType type); static std::optional ParseMemoryCardTypeName(std::string_view str); @@ -631,6 +635,11 @@ struct Settings : public GPUSettings static constexpr u32 DEFAULT_CDROM_MAX_READ_SPEEDUP_CYCLES = 30000; static constexpr CDROMMechaconVersion DEFAULT_CDROM_MECHACON_VERSION = CDROMMechaconVersion::VC1A; + static constexpr DisplayScreenshotFormat DEFAULT_DISPLAY_SCREENSHOT_FORMAT = DisplayScreenshotFormat::PNG; + static constexpr u8 DEFAULT_DISPLAY_SCREENSHOT_QUALITY = 85; + static constexpr CaptureFileNameFormat DEFAULT_DISPLAY_SCREENSHOT_FILENAME_FORMAT = + CaptureFileNameFormat::TitleAndTimestamp; + static constexpr ControllerType DEFAULT_CONTROLLER_1_TYPE = ControllerType::AnalogController; static constexpr ControllerType DEFAULT_CONTROLLER_2_TYPE = ControllerType::None; static constexpr MemoryCardType DEFAULT_MEMORY_CARD_1_TYPE = MemoryCardType::PerGameTitle; @@ -649,6 +658,8 @@ struct Settings : public GPUSettings static constexpr u32 DEFAULT_MEDIA_CAPTURE_VIDEO_HEIGHT = 480; static constexpr u32 DEFAULT_MEDIA_CAPTURE_VIDEO_BITRATE = 6000; static constexpr u32 DEFAULT_MEDIA_CAPTURE_AUDIO_BITRATE = 128; + static constexpr CaptureFileNameFormat DEFAULT_MEDIA_CAPTURE_FILENAME_FORMAT = + CaptureFileNameFormat::TitleAndTimestamp; // Android doesn't create settings until they're first opened, so we have to override the defaults here. #ifndef __ANDROID__ diff --git a/src/core/system.cpp b/src/core/system.cpp index 4f302bd8c..719463642 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -181,7 +181,6 @@ static void ClearRunningGame(); static void DestroySystem(); static void RecreateGPU(GPURenderer new_renderer); -static std::string GetScreenshotPath(std::string_view extension); static bool StartMediaCapture(std::string path, bool capture_video, bool capture_audio, u32 video_width, u32 video_height); static void StopMediaCapture(std::unique_ptr cap); @@ -357,11 +356,6 @@ static StateVars s_state; } // namespace System -static TinyString GetTimestampStringForFileName() -{ - return TinyString::from_format("{:%Y-%m-%d-%H-%M-%S}", Common::LocalTime(std::time(nullptr)).value_or(std::tm{})); -} - bool System::PerformEarlyHardwareChecks(Error* error) { // This shouldn't fail... if it does, just hope for the best. @@ -1428,6 +1422,8 @@ void System::SetDefaultSettings(SettingsInterface& si) #ifndef __ANDROID__ si.SetStringValue("MediaCapture", "Backend", MediaCapture::GetBackendName(Settings::DEFAULT_MEDIA_CAPTURE_BACKEND)); si.SetStringValue("MediaCapture", "Container", Settings::DEFAULT_MEDIA_CAPTURE_CONTAINER); + si.SetStringValue("MediaCapture", "FileNameFormat", + Settings::GetCaptureFileNameFormatName(Settings::DEFAULT_MEDIA_CAPTURE_FILENAME_FORMAT)); si.SetBoolValue("MediaCapture", "VideoCapture", true); si.SetUIntValue("MediaCapture", "VideoWidth", Settings::DEFAULT_MEDIA_CAPTURE_VIDEO_WIDTH); si.SetUIntValue("MediaCapture", "VideoHeight", Settings::DEFAULT_MEDIA_CAPTURE_VIDEO_HEIGHT); @@ -4836,8 +4832,6 @@ void System::CheckForSettingsChanges(const Settings& old_settings) g_settings.display_show_enhancements != old_settings.display_show_enhancements || g_settings.display_auto_resize_window != old_settings.display_auto_resize_window || g_settings.display_screenshot_mode != old_settings.display_screenshot_mode || - g_settings.display_screenshot_format != old_settings.display_screenshot_format || - g_settings.display_screenshot_quality != old_settings.display_screenshot_quality || g_settings.display_osd_scale != old_settings.display_osd_scale || g_settings.display_osd_margin != old_settings.display_osd_margin || g_settings.display_osd_message_duration != old_settings.display_osd_message_duration || @@ -5624,29 +5618,84 @@ void System::UpdateVolume() SPU::GetOutputStream().SetOutputVolume(GetAudioOutputVolume()); } -std::string System::GetScreenshotPath(std::string_view extension) +std::string System::GetNewCapturePath(const std::string& directory, const std::string_view title, + CaptureFileNameFormat format, std::string_view extension) { - const std::string sanitized_name = Path::SanitizeFileName(System::GetGameTitle()); - std::string basename; - if (sanitized_name.empty()) - basename = fmt::format("{}", GetTimestampStringForFileName()); + // don't calculate if unnecessary + std::string sanitized_title; + if (!title.empty() && + (format == CaptureFileNameFormat::TitleAndTimestamp || format >= CaptureFileNameFormat::TimestampInFolder)) + { + sanitized_title = Path::SanitizeFileName(title); + } + + std::string path; + if (format >= CaptureFileNameFormat::TimestampInFolder && !sanitized_title.empty()) + { + path = Path::Combine(directory, sanitized_title); + + if (Error error; !FileSystem::EnsureDirectoryExists(path.c_str(), false, &error)) [[unlikely]] + { + WARNING_LOG("Failed to create capture subdirectory '{}': {}", path, error.GetDescription()); + path = directory; + } + } else - basename = fmt::format("{} {}", sanitized_name, GetTimestampStringForFileName()); + { + path = directory; + } + + path += FS_OSPATH_SEPARATOR_CHARACTER; + + if ((format == CaptureFileNameFormat::TitleAndTimestamp || + format == CaptureFileNameFormat::TitleAndTimestampInFolder) && + !sanitized_title.empty()) + { + path += sanitized_title; + path += ' '; + } - std::string path = fmt::format("{}" FS_OSPATH_SEPARATOR_STR "{}.{}", EmuFolders::Screenshots, basename, extension); + fmt::format_to(std::back_inserter(path), "{:%Y-%m-%d-%H-%M-%S}", + Common::LocalTime(std::time(nullptr)).value_or(std::tm{})); + + if (!extension.empty()) + { + path += '.'; + path += extension; + } // handle quick screenshots to the same filename u32 next_suffix = 1; while (FileSystem::FileExists(path.c_str())) { - path = - fmt::format("{}" FS_OSPATH_SEPARATOR_STR "{} ({}).{}", EmuFolders::Screenshots, basename, next_suffix, extension); + // remove and re-add the extension + if (!extension.empty()) + path.erase(path.rfind('.')); + + // and the suffix if this isn't the first + if (next_suffix > 1) + path.erase(path.rfind(' ')); + + if (!extension.empty()) + fmt::format_to(std::back_inserter(path), " ({}).{}", next_suffix, extension); + else + fmt::format_to(std::back_inserter(path), " ({})", next_suffix); + next_suffix++; } return path; } +std::string System::GetNewMediaCapturePath(const std::string_view title, const std::string_view container) +{ + const CaptureFileNameFormat format = + Settings::ParseCaptureFileNameFormat(Core::GetBaseTinyStringSettingValue("MediaCapture", "FilenameFormat")) + .value_or(Settings::DEFAULT_MEDIA_CAPTURE_FILENAME_FORMAT); + + return GetNewCapturePath(EmuFolders::Videos, title, format, container); +} + void System::SaveScreenshot(const char* path, DisplayScreenshotMode mode, DisplayScreenshotFormat format, u8 quality) { if (!IsValid()) @@ -5654,7 +5703,12 @@ void System::SaveScreenshot(const char* path, DisplayScreenshotMode mode, Displa std::string auto_path; if (!path || path[0] == '\0') - path = (auto_path = GetScreenshotPath(Settings::GetDisplayScreenshotFormatExtension(format))).c_str(); + { + path = (auto_path = GetNewCapturePath(EmuFolders::Screenshots, s_state.running_game_title, + g_settings.display_screenshot_filename_format, + Settings::GetDisplayScreenshotFormatExtension(format))) + .c_str(); + } GPUBackend::RenderScreenshotToFile(path, mode, quality, true); } @@ -5666,7 +5720,11 @@ bool System::StartRecordingGPUDump(const char* path /*= nullptr*/, u32 num_frame std::string auto_path; if (!path) - path = (auto_path = GetScreenshotPath("psxgpu")).c_str(); + { + path = (auto_path = GetNewCapturePath(EmuFolders::Screenshots, s_state.running_game_title, + g_settings.display_screenshot_filename_format, "psxgpu")) + .c_str(); + } return g_gpu.StartRecordingGPUDump(path, num_frames); } @@ -5691,23 +5749,6 @@ MediaCapture* System::GetMediaCapture() return s_state.media_capture.get(); } -std::string System::GetNewMediaCapturePath(const std::string_view title, const std::string_view container) -{ - const std::string sanitized_name = Path::SanitizeFileName(title); - std::string path; - if (sanitized_name.empty()) - { - path = Path::Combine(EmuFolders::Videos, fmt::format("{}.{}", GetTimestampStringForFileName(), container)); - } - else - { - path = Path::Combine(EmuFolders::Videos, - fmt::format("{} {}.{}", sanitized_name, GetTimestampStringForFileName(), container)); - } - - return path; -} - bool System::StartMediaCapture(std::string path) { const bool capture_video = Core::GetBoolSettingValue("MediaCapture", "VideoCapture", true); @@ -5764,9 +5805,9 @@ bool System::StartMediaCapture(std::string path, bool capture_video, bool captur if (path.empty()) { - path = - GetNewMediaCapturePath(GetGameTitle(), Core::GetStringSettingValue("MediaCapture", "Container", - Settings::DEFAULT_MEDIA_CAPTURE_CONTAINER)); + path = GetNewMediaCapturePath( + s_state.running_game_title, + Core::GetTinyStringSettingValue("MediaCapture", "Container", Settings::DEFAULT_MEDIA_CAPTURE_CONTAINER)); } const MediaCaptureBackend backend = diff --git a/src/core/system.h b/src/core/system.h index 205d2b5a6..26668451e 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -427,6 +427,8 @@ bool StartRecordingGPUDump(const char* path = nullptr, u32 num_frames = 1); void StopRecordingGPUDump(); /// Returns the path that a new media capture would be saved to by default. Safe to call from any thread. +std::string GetNewCapturePath(const std::string& directory, const std::string_view title, CaptureFileNameFormat format, + std::string_view extension); std::string GetNewMediaCapturePath(const std::string_view title, const std::string_view container); /// Current media capture (if active). diff --git a/src/core/types.h b/src/core/types.h index 9db7520d5..70558db32 100644 --- a/src/core/types.h +++ b/src/core/types.h @@ -226,6 +226,15 @@ enum class DisplayScreenshotFormat : u8 Count }; +enum class CaptureFileNameFormat : u8 +{ + Timestamp, + TitleAndTimestamp, + TimestampInFolder, + TitleAndTimestampInFolder, + Count +}; + enum class PresentSkipMode : u8 { Disabled, diff --git a/src/duckstation-qt/capturesettingswidget.cpp b/src/duckstation-qt/capturesettingswidget.cpp index 3bcf5f0a6..6f2228ac5 100644 --- a/src/duckstation-qt/capturesettingswidget.cpp +++ b/src/duckstation-qt/capturesettingswidget.cpp @@ -33,6 +33,10 @@ CaptureSettingsWidget::CaptureSettingsWidget(SettingsWindow* dialog, QWidget* pa tr("Select Screenshots Directory"), m_ui.screenshotsDirectoryOpen, m_ui.screenshotsDirectoryReset, "Folders", "Screenshots", Path::Combine(EmuFolders::DataRoot, "screenshots")); + SettingWidgetBinder::BindWidgetToEnumSetting( + sif, m_ui.screenshotSaveName, "Display", "ScreenshotFileNameFormat", &Settings::ParseCaptureFileNameFormat, + &Settings::GetCaptureFileNameFormatName, &Settings::GetCaptureFileNameFormatDisplayName, + Settings::DEFAULT_DISPLAY_SCREENSHOT_FILENAME_FORMAT, CaptureFileNameFormat::Count); SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.mediaCaptureBackend, "MediaCapture", "Backend", &MediaCapture::ParseBackendName, &MediaCapture::GetBackendName, @@ -42,6 +46,10 @@ CaptureSettingsWidget::CaptureSettingsWidget(SettingsWindow* dialog, QWidget* pa tr("Select Media Capture Directory"), m_ui.videosDirectoryOpen, m_ui.videosDirectoryReset, "Folders", "Videos", Path::Combine(EmuFolders::DataRoot, "videos")); + SettingWidgetBinder::BindWidgetToEnumSetting( + sif, m_ui.mediaCaptureSaveName, "MediaCapture", "FilenameFormat", &Settings::ParseCaptureFileNameFormat, + &Settings::GetCaptureFileNameFormatName, &Settings::GetCaptureFileNameFormatDisplayName, + Settings::DEFAULT_DISPLAY_SCREENSHOT_FILENAME_FORMAT, CaptureFileNameFormat::Count); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableVideoCapture, "MediaCapture", "VideoCapture", true); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.videoCaptureWidth, "MediaCapture", "VideoWidth", Settings::DEFAULT_MEDIA_CAPTURE_VIDEO_WIDTH); @@ -80,6 +88,11 @@ CaptureSettingsWidget::CaptureSettingsWidget(SettingsWindow* dialog, QWidget* pa "preserve more detail at the cost of file size.")); dialog->registerWidgetHelp(m_ui.screenshotsDirectory, tr("Save Location"), tr("Default"), tr("Specifies the directory where screenshots will be saved.")); + dialog->registerWidgetHelp( + m_ui.screenshotSaveName, tr("Save Name"), + Settings::GetCaptureFileNameFormatDisplayName(Settings::DEFAULT_DISPLAY_SCREENSHOT_FILENAME_FORMAT), + tr("Determines the format of the filename used when saving screenshots, and whether a subdirectory is created " + "per-game.")); dialog->registerWidgetHelp( m_ui.screenshotFormat, tr("Screenshot Format"), tr("PNG"), tr("Selects the format which will be used to save screenshots. JPEG produces smaller files, but loses detail.")); @@ -95,6 +108,11 @@ CaptureSettingsWidget::CaptureSettingsWidget(SettingsWindow* dialog, QWidget* pa tr("Determines the file format used to contain the captured audio/video.")); dialog->registerWidgetHelp(m_ui.videosDirectory, tr("Save Location"), tr("Default"), tr("Specifies the directory where media capture (video/audio) will be saved.")); + dialog->registerWidgetHelp( + m_ui.mediaCaptureSaveName, tr("Save Name"), + Settings::GetCaptureFileNameFormatDisplayName(Settings::DEFAULT_MEDIA_CAPTURE_FILENAME_FORMAT), + tr("Determines the format of the filename used when saving screenshots, and whether a subdirectory is created " + "per-game.")); dialog->registerWidgetHelp(m_ui.enableVideoCapture, tr("Capture Video"), tr("Checked"), tr("Captures video to the chosen file when media capture is started. If unchecked, the " "file will only contain audio.")); diff --git a/src/duckstation-qt/capturesettingswidget.ui b/src/duckstation-qt/capturesettingswidget.ui index 9c5754a54..e55b2a424 100644 --- a/src/duckstation-qt/capturesettingswidget.ui +++ b/src/duckstation-qt/capturesettingswidget.ui @@ -70,13 +70,23 @@ + + + Save Name: + + + + + + + Save Location: - + @@ -143,13 +153,23 @@ + + + Save Name: + + + + + + + Save Location: - + @@ -186,7 +206,7 @@ - + 20